]> O.S.I.I.S - jp/crow.git/commitdiff
new binding system - Groupin in test case members of same target
authorjpbruyere <jp.bruyere@hotmail.com>
Thu, 8 Oct 2015 07:42:37 +0000 (09:42 +0200)
committerjpbruyere <jp.bruyere@hotmail.com>
Thu, 8 Oct 2015 07:42:37 +0000 (09:42 +0200)
- bound created delegate to instance of source, prevent need of
reference table.

Tests/GOLIBTests.cs
Tests/Tests.csproj
src/CompilerServices/CompilerServices.cs
src/GraphicObjects/GraphicObject.cs
src/GraphicObjects/Group.cs
src/GraphicObjects/PrivateContainer.cs
src/Interface.cs

index 2825246c35b4af5436361533871691b915f5d701..8ad93184d209c69a0ed9f5240b7ff46e86397719 100644 (file)
@@ -34,12 +34,11 @@ namespace test
                int frameCpt = 0;\r
                int idx = 0;\r
                string[] testFiles = {\r
-                       "test4.goml",\r
+                       "fps.goml",\r
                        "testContainer.goml",\r
                        "testBorder.goml",\r
                        "testLabel.goml",\r
                        "testCheckbox.goml",\r
-                       "fps.goml",\r
                        "testRadioButton.goml",\r
                        "testSpinner.goml",\r
                        "testPopper.goml",\r
@@ -49,7 +48,7 @@ namespace test
                        "testWindow.goml",\r
                        "testMsgBox.goml",\r
                        "testGrid.goml",\r
-\r
+                       "test4.goml",\r
                        "testMeter.goml",\r
                };\r
 \r
@@ -100,12 +99,12 @@ namespace test
                protected override void OnLoad (EventArgs e)\r
                {\r
                        base.OnLoad (e);\r
-                       this.AddWidget(new test4());\r
-                       //LoadInterface("Interfaces/" + testFiles[idx]);\r
+                       //this.AddWidget(new test4());\r
+                       LoadInterface("Interfaces/" + testFiles[idx]);\r
                }\r
                protected override void OnUpdateFrame (FrameEventArgs e)\r
                {\r
-                       if (frameCpt % 8 == 0)\r
+                       //if (frameCpt % 8 == 0)\r
                                base.OnUpdateFrame (e);\r
                        \r
                        fps = (int)RenderFrequency;\r
index 1c88e56087277c5c5336ba7d0e8826804f57cf94..725c9e80f12d3a7f32ef46d62f0fe758042517f5 100644 (file)
@@ -8,7 +8,7 @@
     <OutputType>Exe</OutputType>
     <RootNamespace>Tests</RootNamespace>
     <AssemblyName>Tests</AssemblyName>
-    <StartupObject>test.GOLIBTest_4</StartupObject>
+    <StartupObject>test.GOLIBTests</StartupObject>
     <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
     <OutputPath>..\bin\$(configuration)</OutputPath>
     <IntermediateOutputPath>obj\$(configuration)</IntermediateOutputPath>
index 4b3a4448bb533b6b074e47e0de1732af550b7980..44b930e06b15893ce885bc5c7d7fd8244bf106f3 100644 (file)
@@ -17,9 +17,99 @@ namespace go
                PropertyBinding
        }
        public class Binding{
-               public BindingType BindingType;
-               public string MemberName;
-               public string BindingExpression;
+               public PropertyInfo Property;
+               public string Expression;
+
+               public Binding(){
+               }
+               public Binding(PropertyInfo _property, string _expression)
+               {
+                       Property = _property;
+                       Expression = _expression;
+               }
+       }
+
+       public class ResolvedBinding
+       {
+               public object Target;
+               public MemberInfo Member;
+
+               public Binding Binding;
+
+               public ResolvedBinding(){
+               }
+               public ResolvedBinding(Binding _binding){
+                       Binding = _binding;
+               }
+
+               public bool FindTarget(GraphicObject source){
+                       string member = null;
+
+                       if (string.IsNullOrEmpty (Binding.Expression)) {
+                               Target = source;
+                               Member = null;
+                               return true;
+                       }
+
+                       string[] bindingExp = Binding.Expression.Split ('/');
+
+                       if (bindingExp.Length == 1) {
+                               //datasource binding
+                               Target = source.DataSource;
+                               member = bindingExp [0];
+                       } else {
+                               int ptr = 0;
+                               ILayoutable tmp = source;
+                               if (string.IsNullOrEmpty (bindingExp [0])) {
+                                       //if exp start with '/' => Graphic tree parsing start at top container
+                                       tmp = source.TopContainer as ILayoutable;
+                                       ptr++;
+                               }
+                               while (ptr < bindingExp.Length - 1) {
+                                       if (tmp == null)
+                                               return false;
+                                       if (bindingExp [ptr] == "..")
+                                               tmp = tmp.Parent as GraphicObject;
+                                       else if (bindingExp [ptr] == ".") {
+                                               if (ptr > 0)
+                                                       throw new Exception ("Syntax error in binding, './' may only appear in first position");                                                
+                                               tmp = source;
+                                       }else
+                                               tmp = (tmp as GraphicObject).FindByName (bindingExp [ptr]);
+                                       ptr++;
+                               }
+
+                               string[] bindTrg = bindingExp [ptr].Split ('.');
+
+                               if (bindTrg.Length == 2) {
+                                       tmp = (tmp as GraphicObject).FindByName (bindTrg [0]);
+                                       member = bindTrg [1];
+                               } else
+                                       throw new Exception ("Syntax error in binding, expected 'go dot member'");
+                               Target = tmp;
+                       }
+                       if (Target == null) {
+                               Debug.WriteLine ("Binding Source is null: " + Binding.Expression);
+                               return false;
+                       }
+                       Type targetType = Target.GetType ();
+                       Member = targetType.GetMember (member).FirstOrDefault ();
+
+                       #region search for extensions methods if member not found in type
+                       if (Member == null && !string.IsNullOrEmpty(member))
+                       {
+                               Assembly a = Assembly.GetExecutingAssembly();
+                               Member =  CompilerServices.GetExtensionMethods(a, targetType).FirstOrDefault();                                 
+                       }
+                       #endregion
+
+                       if (member == null) {
+                               Debug.WriteLine ("Binding member not found: " + member);
+                               return false;
+                       }
+
+                       return true;
+               }
        }
 
        public static class CompilerServices2
@@ -153,6 +243,10 @@ namespace go
        }
        public static class CompilerServices
        {
+               public static void CreateBindingHandler()
+               {
+               }
+
                static int dynHandleCpt = 0;
 
                /// <summary>
index 0617e927e0649102798032f969b87302d4e3e3d6..7aba98c83bb45c6687d7653ea13177c3354dbe9d 100644 (file)
@@ -16,12 +16,15 @@ using System.ComponentModel;
 using System.IO;\r
 //using System.Xml;\r
 using System.Xml;\r
+using System.Runtime.CompilerServices;\r
+using System.Reflection.Emit;\r
 \r
 namespace go\r
 {              \r
        public class GraphicObject : IXmlSerializable, ILayoutable, IValueChange\r
        {\r
                internal List<int> DynamicMethodIds = new List<int> ();\r
+               internal List<Binding> Bindings = new List<Binding> ();\r
 \r
                #region IValueChange implementation\r
                public event EventHandler<ValueChangeEventArgs> ValueChanged;\r
@@ -329,7 +332,16 @@ namespace go
 \r
                [XmlIgnore]public object DataSource {\r
                        set {\r
+                               if (dataSource == value)\r
+                                       return;\r
+\r
+                               if (dataSource != null)\r
+                                       this.ClearBinding ();\r
+                               \r
                                dataSource = value;\r
+\r
+                               if (dataSource != null)\r
+                                       this.ResolveBindings();\r
                        }\r
                        get {                           \r
                                return dataSource == null ? \r
@@ -775,7 +787,161 @@ namespace go
                                tmp = Parent.ToString () + tmp;\r
                        return Name == "unamed" ? tmp + "." + this.GetType ().Name : tmp + "." + Name;\r
                }\r
-                       \r
+\r
+               public virtual void ResolveBindings()\r
+               {\r
+                       List<ResolvedBinding> resolved = new List<ResolvedBinding> ();\r
+                       foreach (Binding b in Bindings) {\r
+                               ResolvedBinding rb = new ResolvedBinding (b);\r
+                               if (!rb.FindTarget (this))\r
+                                       continue;\r
+                               resolved.Add (rb);                              \r
+                       }\r
+                       IEnumerable<ResolvedBinding[]> groupedByTarget = resolved.GroupBy (g => g.Target, g => g, (k, g) => g.ToArray ());\r
+                       foreach (ResolvedBinding[] grouped in groupedByTarget) {\r
+                               int i = 0;\r
+                               object targetValue = grouped [0].Target;//empty binding exp=> bound to target object by default\r
+                               Type targetType = targetValue.GetType();\r
+                               Type sourceType = this.GetType();\r
+\r
+                               DynamicMethod dm = null;\r
+                               ILGenerator il = null;\r
+\r
+                               MethodInfo stringEquals = typeof(string).GetMethod\r
+                                       ("op_Equality", new Type[2] {typeof(string), typeof(string)});\r
+\r
+\r
+                               System.Reflection.Emit.Label[] jumpTable = null;\r
+                               System.Reflection.Emit.Label endMethod = new System.Reflection.Emit.Label();\r
+\r
+                               LocalBuilder lbMemberName = null;\r
+                               LocalBuilder lbValue = null;\r
+\r
+                               #region Retrieve EventHandler parameter type\r
+                               EventInfo ei = targetType.GetEvent ("ValueChanged");\r
+                               //no dynamic update if ValueChanged interface is not implemented\r
+                               if (ei != null){\r
+                                       MethodInfo evtInvoke = ei.EventHandlerType.GetMethod ("Invoke");\r
+                                       ParameterInfo[] evtParams = evtInvoke.GetParameters ();\r
+                                       Type handlerArgsType = evtParams [1].ParameterType;\r
+\r
+                                       Type[] args = {typeof(object), typeof(object),handlerArgsType};\r
+                                       dm = new DynamicMethod("dynHandle",\r
+                                               typeof(void), \r
+                                               args,\r
+                                               sourceType);\r
+                                       \r
+\r
+                                       il = dm.GetILGenerator(256);\r
+\r
+\r
+                                       endMethod = il.DefineLabel();\r
+                                       jumpTable = new System.Reflection.Emit.Label[grouped.Length];\r
+                                       for (i = 0; i < grouped.Length; i++)\r
+                                               jumpTable [i] = il.DefineLabel ();\r
+                                       lbMemberName = il.DeclareLocal(typeof(string));\r
+                                       lbValue  = il.DeclareLocal(typeof(object));\r
+\r
+                                       il.Emit(OpCodes.Nop);\r
+                                       il.Emit(OpCodes.Ldarg_0);\r
+                                       //il.Emit(OpCodes.Isinst, sourceType);\r
+                                       //push new value onto stack\r
+                                       il.Emit(OpCodes.Ldarg_2);\r
+                                       FieldInfo fiNewValue = typeof(ValueChangeEventArgs).GetField("NewValue");\r
+                                       il.Emit(OpCodes.Ldfld, fiNewValue);\r
+                                       il.Emit(OpCodes.Stloc, lbValue);\r
+                                       //push name \r
+                                       il.Emit(OpCodes.Ldarg_2);\r
+                                       FieldInfo fiMbName = typeof(ValueChangeEventArgs).GetField("MemberName");\r
+                                       il.Emit(OpCodes.Ldfld, fiMbName);\r
+                                       il.Emit(OpCodes.Stloc, lbMemberName);\r
+                                       il.Emit(OpCodes.Ldloc, lbMemberName);\r
+                                       il.Emit(OpCodes.Brfalse, endMethod);\r
+                               }\r
+                               #endregion\r
+\r
+                               i = 0;\r
+                               foreach (ResolvedBinding rb in grouped) {\r
+                                       #region initialize target with actual value\r
+                                       if (rb.Member != null){\r
+                                               if (rb.Member.MemberType == MemberTypes.Property)\r
+                                                       targetValue = (rb.Member as PropertyInfo).GetGetMethod ().Invoke (rb.Target, null);\r
+                                               else if (rb.Member.MemberType == MemberTypes.Field)\r
+                                                       targetValue = (rb.Member as FieldInfo).GetValue (rb.Target);\r
+                                               else if (rb.Member.MemberType == MemberTypes.Method){\r
+                                                       MethodInfo mthSrc = rb.Member as MethodInfo;\r
+                                                       if (mthSrc.IsDefined(typeof(ExtensionAttribute), false))\r
+                                                               targetValue = mthSrc.Invoke(null, new object[] {rb.Target});\r
+                                                       else\r
+                                                               targetValue = mthSrc.Invoke(rb.Target, null);\r
+                                               }else\r
+                                                       throw new Exception ("unandled source member type for binding");                                                \r
+                                       }\r
+                                       //TODO: handle other dest type conversions\r
+                                       if (rb.Binding.Property.PropertyType == typeof(string)){\r
+                                               if (targetValue != null)\r
+                                                       targetValue = targetValue.ToString ();\r
+                                       }\r
+                                       rb.Binding.Property.GetSetMethod ().Invoke (this, new object[] { targetValue });\r
+                                       #endregion\r
+\r
+                                       //if no dyn update, skip jump table\r
+                                       if (il == null)\r
+                                               continue;\r
+\r
+                                       il.Emit (OpCodes.Ldloc, lbMemberName);\r
+                                       il.Emit (OpCodes.Ldstr, rb.Member.Name);\r
+                                       il.Emit (OpCodes.Callvirt, stringEquals);\r
+                                       il.Emit (OpCodes.Brtrue, jumpTable[i]);\r
+                                       i++;\r
+                               }\r
+\r
+                               if (il == null)\r
+                                       continue;\r
+                               \r
+                               il.Emit (OpCodes.Br, endMethod);\r
+\r
+                               i = 0;\r
+                               foreach (ResolvedBinding rb in grouped) {\r
+\r
+                                       il.MarkLabel (jumpTable [i]);\r
+                                       il.Emit(OpCodes.Ldloc, lbValue);\r
+\r
+                                       //by default, target value type is deducted from source member type to allow\r
+                                       //memberless binding, if targetMember exists, it will be used to determine target\r
+                                       //value type for conversion\r
+                                       Type targetValueType = rb.Binding.Property.PropertyType;\r
+                                       if (rb.Member != null) {\r
+                                               if (rb.Member.MemberType == MemberTypes.Property)\r
+                                                       targetValueType = (rb.Member as PropertyInfo).PropertyType;\r
+                                               else if (rb.Member.MemberType == MemberTypes.Field)\r
+                                                       targetValueType = (rb.Member as FieldInfo).FieldType;\r
+                                               else\r
+                                                       throw new Exception ("unhandle target member type in binding");\r
+                                       }\r
+                                       \r
+                                       if (!targetValueType.IsValueType)\r
+                                               il.Emit(OpCodes.Castclass, targetValueType);\r
+                                       else if (rb.Binding.Property.PropertyType != targetValueType)\r
+                                               il.Emit(OpCodes.Callvirt, CompilerServices.GetConvertMethod( rb.Binding.Property.PropertyType ));\r
+                                       else\r
+                                               il.Emit(OpCodes.Unbox_Any, rb.Binding.Property.PropertyType);\r
+\r
+                                       il.Emit(OpCodes.Callvirt, rb.Binding.Property.GetSetMethod());\r
+                                       il.Emit (OpCodes.Br, endMethod);\r
+                                       i++;\r
+\r
+                               }\r
+                               il.MarkLabel(endMethod);\r
+                               il.Emit(OpCodes.Pop);\r
+                               il.Emit(OpCodes.Ret);\r
+\r
+                               Delegate del = dm.CreateDelegate(ei.EventHandlerType, this);\r
+                               MethodInfo addHandler = ei.GetAddMethod ();\r
+                               addHandler.Invoke(grouped [0].Target, new object[] {del});\r
+                       }\r
+               }\r
+\r
                #region IXmlSerializable\r
                public virtual System.Xml.Schema.XmlSchema GetSchema ()\r
                {\r
@@ -848,18 +1014,11 @@ namespace go
 //                                     } else {\r
 \r
                                        if (attValue.StartsWith("{")) {\r
-                                               if (Interface.DontResoveGOML)\r
-                                                       continue;\r
                                                //binding\r
                                                if (!attValue.EndsWith("}"))\r
                                                        throw new Exception (string.Format("GOML:Malformed binding: {0}", attValue));\r
 \r
-                                               string strBinding = attValue.Substring (1, attValue.Length - 2);\r
-                                               Interface.GOMLResolver.Add (new DynAttribute () {\r
-                                                       Source = this,\r
-                                                       MemberName = attName,\r
-                                                       Value = strBinding\r
-                                               });\r
+                                               this.Bindings.Add (new Binding (pi, attValue.Substring (1, attValue.Length - 2)));\r
                                                continue;\r
                                        }\r
 \r
@@ -976,26 +1135,26 @@ namespace go
                ///  and delete ref of this in Shared interface refs\r
                /// </summary>\r
                public virtual void ClearBinding(){\r
-                       object ds = this.DataSource;\r
-                       if (ds != null) {\r
-                               Type dataSourceType = ds.GetType ();\r
-                               EventInfo evtInfo = dataSourceType.GetEvent ("ValueChanged");\r
-                               if (evtInfo != null) {\r
-                                       FieldInfo evtFi = CompilerServices.GetEventHandlerField (dataSourceType, "ValueChanged");\r
-                                       MulticastDelegate multicastDelegate = evtFi.GetValue (ds) as MulticastDelegate;\r
-                                       if (multicastDelegate != null) {                                \r
-                                               foreach (Delegate d in multicastDelegate.GetInvocationList()) {\r
-                                                       string dn = d.Method.Name;\r
-                                                       if (!dn.StartsWith ("dynHandle_"))\r
-                                                               continue;\r
-                                                       int did = int.Parse (dn.Substring (10));\r
-                                                       if (this.DynamicMethodIds.Contains (did))\r
-                                                               evtInfo.RemoveEventHandler (ds, d);\r
-                                               }\r
-                                       }\r
-                               }\r
-                       }\r
-                       Interface.Unreference (this);\r
+//                     object ds = this.DataSource;\r
+//                     if (ds != null) {\r
+//                             Type dataSourceType = ds.GetType ();\r
+//                             EventInfo evtInfo = dataSourceType.GetEvent ("ValueChanged");\r
+//                             if (evtInfo != null) {\r
+//                                     FieldInfo evtFi = CompilerServices.GetEventHandlerField (dataSourceType, "ValueChanged");\r
+//                                     MulticastDelegate multicastDelegate = evtFi.GetValue (ds) as MulticastDelegate;\r
+//                                     if (multicastDelegate != null) {                                \r
+//                                             foreach (Delegate d in multicastDelegate.GetInvocationList()) {\r
+//                                                     string dn = d.Method.Name;\r
+//                                                     if (!dn.StartsWith ("dynHandle_"))\r
+//                                                             continue;\r
+//                                                     int did = int.Parse (dn.Substring (10));\r
+//                                                     if (this.DynamicMethodIds.Contains (did))\r
+//                                                             evtInfo.RemoveEventHandler (ds, d);\r
+//                                             }\r
+//                                     }\r
+//                             }\r
+//                     }\r
+//                     Interface.Unreference (this);\r
 \r
                }\r
        }\r
index f711bb88b15195b1100aae21f4ab108d094b2f1c..5c2b7df27bcdbc08ab677d448d52ea812ef1dcf4 100644 (file)
@@ -83,7 +83,12 @@ namespace go
                                return true;\r
                        }\r
                }\r
-\r
+               public override void ResolveBindings ()\r
+               {\r
+                       base.ResolveBindings ();\r
+                       foreach (GraphicObject w in Children)\r
+                               w.ResolveBindings ();\r
+               }\r
                public override GraphicObject FindByName (string nameToFind)\r
                {\r
                        if (Name == nameToFind)\r
index 3d29177d1897e67f998b45bb422dec221c910be5..e6f826dc725593e4b6e9748177ef8782b08435f4 100644 (file)
@@ -67,6 +67,12 @@ namespace go
                }
 
                #region GraphicObject Overrides
+               public override void ResolveBindings ()
+               {
+                       base.ResolveBindings ();
+                       if (child != null)
+                               child.ResolveBindings ();
+               }
                public override GraphicObject FindByName (string nameToFind)
                {
                        if (Name == nameToFind)
index 9dccd969ba82059f8073b0b58dd17a60263799ad..304e48b634a8a513cfd5699c3996fc45bb0cabd7 100644 (file)
@@ -438,22 +438,10 @@ namespace go
 
                        XmlSerializerNamespaces xn = new XmlSerializerNamespaces ();
                        xn.Add ("", "");
-                       //prevent unused ref in References created by xmlSerializer
-                       Interface.DontResoveGOML = true;
                        XmlSerializer xs = new XmlSerializer (type);
-                       Interface.DontResoveGOML = false;
-                       GOMLResolutionStack.Push (new List<DynAttribute> ());
 
                        result = (GraphicObject)xs.Deserialize (stream);
-                       //result.DataSource = hostClass;
-
-                       if (hostClass == null) {
-                               GOMLResolutionStack.Pop ();
-                               return result;
-                       }
-                               
-                       if (resolve)
-                               resolveGOML (hostClass);
+                       result.DataSource = hostClass;
 
                        #if DEBUG_LOAD_TIME
                        loadingTime.Stop ();