Mercurial > pub > bltoolkit
diff Source/Aspects/Builders/MixinAspectBuilder.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Aspects/Builders/MixinAspectBuilder.cs Thu Mar 27 21:46:09 2014 +0400 @@ -0,0 +1,210 @@ +using System; +using System.Reflection; +using System.Reflection.Emit; + +using BLToolkit.TypeBuilder.Builders; +using BLToolkit.Reflection.Emit; +using BLToolkit.TypeBuilder; + +namespace BLToolkit.Aspects.Builders +{ + public class MixinAspectBuilder : AbstractTypeBuilderBase + { + public MixinAspectBuilder( + Type targetInterface, string memberName, bool throwExceptionIfNull, string exceptionMessage) + { + _targetInterface = targetInterface; + _memberName = memberName; + _throwExceptionIfNull = throwExceptionIfNull; + _exceptionMessage = exceptionMessage; + } + + private readonly Type _targetInterface; + private readonly string _memberName; + private readonly bool _throwExceptionIfNull; + private readonly string _exceptionMessage; + + public override bool IsApplied(BuildContext context, AbstractTypeBuilderList builders) + { + return context.BuildElement == BuildElement.InterfaceMethod; + } + + public override Type[] GetInterfaces() + { + return new Type[] { _targetInterface }; + } + + public override void Build(BuildContext context) + { + Context = context; + + if (CheckOverrideAttribute()) + return; + + EmitHelper emit = Context.MethodBuilder.Emitter; + MethodInfo method = Context.MethodBuilder.OverriddenMethod; + ParameterInfo[] ps = method.GetParameters(); + Type memberType; + + FieldInfo field = Context.Type.GetField(_memberName); + + if (field != null) + { + if (field.IsPrivate) + throw new TypeBuilderException(string.Format( + "Field '{0}.{1}' must be protected or public.", + Context.Type.Name, _memberName)); + + memberType = field.FieldType; + + emit + .ldarg_0 + .ldfld (field) + ; + + CheckNull(emit); + + emit + .ldarg_0 + .ldfld (field) + ; + } + else + { + PropertyInfo prop = Context.Type.GetProperty(_memberName); + + if (prop != null) + { + MethodInfo mi = prop.GetGetMethod(true); + + if (mi == null) + throw new TypeBuilderException(string.Format( + "Property '{0}.{1}' getter not found.", + Context.Type.Name, _memberName)); + + memberType = prop.PropertyType; + + if (mi.IsPrivate) + throw new TypeBuilderException(string.Format( + "Property '{0}.{1}' getter must be protected or public.", + Context.Type.Name, _memberName)); + + emit + .ldarg_0 + .callvirt (mi) + ; + + CheckNull(emit); + + emit + .ldarg_0 + .callvirt (mi) + ; + } + else + { + throw new TypeBuilderException(string.Format( + "Member '{0}.{1}' not found.", + Context.Type.Name, _memberName)); + } + } + + emit.CastIfNecessary(_targetInterface, memberType); + + for (int i = 0; i < ps.Length; i++) + emit.ldarg(i + 1); + + emit.callvirt(method); + + if (Context.ReturnValue != null) + emit.stloc(Context.ReturnValue); + } + + private void CheckNull(EmitHelper emit) + { + if (_throwExceptionIfNull == false && string.IsNullOrEmpty(_exceptionMessage)) + { + emit + .brfalse (Context.ReturnLabel) + ; + } + else + { + string message = string.Format( + string.IsNullOrEmpty(_exceptionMessage)? + "'{0}.{1}' is not initialized." : _exceptionMessage, + _targetInterface.Name, _memberName, _targetInterface.FullName); + + Label label = emit.DefineLabel(); + + emit + .brtrue (label) + .ldstr (message) + .newobj (typeof(InvalidOperationException), typeof(string)) + .@throw + .MarkLabel (label) + ; + } + } + + private bool CheckOverrideAttribute() + { + MethodInfo method = Context.MethodBuilder.OverriddenMethod; + ParameterInfo[] ps = method.GetParameters(); + + MethodInfo[] methods = Context.Type.GetMethods( + BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + + foreach (MethodInfo mi in methods) + { + if (mi.IsPrivate) + continue; + + object[] attrs = mi.GetCustomAttributes(typeof(MixinOverrideAttribute), true); + + if (attrs == null || attrs.Length == 0) + continue; + + foreach (MixinOverrideAttribute attr in attrs) + { + if (attr.TargetInterface != null && + attr.TargetInterface != Context.CurrentInterface.Type) + continue; + + string name = string.IsNullOrEmpty(attr.MethodName)? + mi.Name: attr.MethodName; + + if (name != method.Name || mi.ReturnType != method.ReturnType) + continue; + + ParameterInfo[] mips = mi.GetParameters(); + + if (mips.Length != ps.Length) + continue; + + bool equal = true; + + for (int i = 0; equal && i < ps.Length; i++) + equal = ps[i].ParameterType == mips[i].ParameterType; + + if (equal) + { + EmitHelper emit = Context.MethodBuilder.Emitter; + + for (int i = -1; i < ps.Length; i++) + emit.ldarg(i + 1); + + emit.callvirt(mi); + + if (Context.ReturnValue != null) + emit.stloc(Context.ReturnValue); + + return true; + } + } + } + + return false; + } + } +}