diff options
| author | jpbruyere <jp.bruyere@hotmail.com> | 2016-08-08 19:22:52 +0200 |
|---|---|---|
| committer | jpbruyere <jp.bruyere@hotmail.com> | 2016-08-08 19:22:52 +0200 |
| commit | cc4c62ab5d809ff451ca475a7949adc4fdfd806b (patch) | |
| tree | 3b39b3eeffbadbc930941e37c1ef3b5026339da3 /src | |
| parent | 4d4a9116ea5c68b27f0b7c43db9682ab57738ae7 (diff) | |
IMLInstantiator implementation
Diffstat (limited to 'src')
| -rw-r--r-- | src/CompilerServices/Bindings.cs | 13 | ||||
| -rw-r--r-- | src/CompilerServices/CompilerServices.cs | 246 | ||||
| -rw-r--r-- | src/GraphicObjects/Container.cs | 6 | ||||
| -rw-r--r-- | src/GraphicObjects/GraphicObject.cs | 359 | ||||
| -rw-r--r-- | src/GraphicObjects/Grid.cs | 5 | ||||
| -rw-r--r-- | src/GraphicObjects/Group.cs | 11 | ||||
| -rw-r--r-- | src/GraphicObjects/ListBox.cs | 4 | ||||
| -rw-r--r-- | src/GraphicObjects/PrivateContainer.cs | 5 | ||||
| -rw-r--r-- | src/GraphicObjects/Scroller.cs | 4 | ||||
| -rw-r--r-- | src/GraphicObjects/TabView.cs | 4 | ||||
| -rw-r--r-- | src/IMLInstantiatorBuilder.cs | 100 | ||||
| -rw-r--r-- | src/IMLStream.cs | 10 | ||||
| -rw-r--r-- | src/Instantiator.cs (renamed from src/GraphicObjects/IBindable.cs) | 21 | ||||
| -rw-r--r-- | src/Interface.cs | 108 |
14 files changed, 593 insertions, 303 deletions
diff --git a/src/CompilerServices/Bindings.cs b/src/CompilerServices/Bindings.cs index 8ece856c..0ce8da6e 100644 --- a/src/CompilerServices/Bindings.cs +++ b/src/CompilerServices/Bindings.cs @@ -41,6 +41,11 @@ namespace Crow public string DynMethodId { get { return dynMethodId; } } + public Type SourceType { + get { return Source == null ? null + : Source.Instance == null ? null + : Source.Instance.GetType();} + } public bool Resolved { get { return resolved; } @@ -185,12 +190,8 @@ namespace Crow } if (Target.TryFindMember (memberName)) { - if (TwoWayBinding) { - IBindable source = Target.Instance as IBindable; - if (source == null) - throw new Exception (Source.Instance + " does not implement IBindable for 2 way bindings"); - source.Bindings.Add (new Binding (Target, Source)); - } + if (TwoWayBinding) + Interface.RegisterBinding (new Binding (Target, Source)); } #if DEBUG_BINDING else diff --git a/src/CompilerServices/CompilerServices.cs b/src/CompilerServices/CompilerServices.cs index 0b69f970..137ce626 100644 --- a/src/CompilerServices/CompilerServices.cs +++ b/src/CompilerServices/CompilerServices.cs @@ -5,12 +5,258 @@ using System.Diagnostics; using System.Linq; using System.Collections.Generic; using System.Runtime.CompilerServices; +using System.Xml; namespace Crow { public static class CompilerServices { + public static void BuildInstanciator(IMLInstantiatorBuilder builder, Type crowType){ + string tmpXml = builder.ReadOuterXml (); + + builder.il.Emit (OpCodes.Ldloc_0);//save current go onto the stack if child has to be added + + if (typeof(TemplatedControl).IsAssignableFrom (crowType)) { + //if its a template, first read template elements + using (IMLInstantiatorBuilder reader = new IMLInstantiatorBuilder (builder.il, tmpXml)) { + + string template = reader.GetAttribute ("Template"); + + bool inlineTemplate = false; + if (string.IsNullOrEmpty (template)) { + reader.Read (); + + while (reader.Read ()) { + if (!reader.IsStartElement ()) + continue; + if (reader.Name == "Template") { + inlineTemplate = true; + reader.Read (); + + readChildren (reader, crowType); + continue; + } + } + if (!inlineTemplate) { + DefaultTemplate dt = (DefaultTemplate)crowType.GetCustomAttributes (typeof(DefaultTemplate), true).FirstOrDefault(); + template = dt.Path; + } + } + if (!inlineTemplate) { + reader.il.Emit (OpCodes.Ldloc_0);//Load this templateControl ref + + reader.il.Emit (OpCodes.Ldstr, template); //Load template path string + reader.il.Emit (OpCodes.Callvirt,//call Interface.Load(path) + typeof(Interface).GetMethod ("Load", BindingFlags.Static | BindingFlags.Public)); + reader.il.Emit (OpCodes.Callvirt,//add child + typeof(PrivateContainer).GetMethod ("SetChild", BindingFlags.Instance | BindingFlags.NonPublic)); + } + } + } + + using (IMLInstantiatorBuilder reader = new IMLInstantiatorBuilder(builder.il,tmpXml)){ + reader.Read (); + + if (reader.HasAttributes) { + string style = reader.GetAttribute ("Style"); + if (!string.IsNullOrEmpty (style)) { + PropertyInfo pi = crowType.GetProperty ("Style"); + CompilerServices.EmitSetValue (reader.il, pi, style); + } + } + if (reader.HasAttributes) { + reader.il.Emit (OpCodes.Ldloc_0); + reader.il.Emit (OpCodes.Callvirt, typeof(GraphicObject).GetMethod ("loadDefaultValues")); + + while (reader.MoveToNextAttribute ()) { + if (reader.Name == "Style") + continue; + + PropertyInfo pi = crowType.GetProperty (reader.Name); + + if (pi == null) + throw new Exception ("Member '" + reader.Name + "' not found in " + crowType.Name); + + CompilerServices.EmitSetValue (reader.il, pi, reader.Value); + + } + reader.MoveToElement (); + } + + if (reader.IsEmptyElement) { + reader.il.Emit (OpCodes.Pop);//pop saved ref to current object + return; + } + + readChildren (reader, crowType); + } + builder.il.Emit (OpCodes.Pop);//pop saved ref to current object + } + static void readChildren(IMLInstantiatorBuilder reader, Type crowType){ + MethodInfo miAddChild = null; + bool endTagReached = false; + while (reader.Read()){ + switch (reader.NodeType) { + case XmlNodeType.EndElement: + endTagReached = true; + break; + case XmlNodeType.Element: + //Templates + + + if (miAddChild == null) { + if (typeof(Group).IsAssignableFrom (crowType)) + miAddChild = typeof(Group).GetMethod ("AddChild"); + else if (typeof(Container).IsAssignableFrom (crowType)) + miAddChild = typeof(Container).GetMethod ("SetChild"); + else if (typeof(PrivateContainer).IsAssignableFrom (crowType)) + miAddChild = typeof(PrivateContainer).GetMethod ("SetChild", + BindingFlags.Instance | BindingFlags.NonPublic); + } + + //push current instance on stack for parenting + //loc_0 will be used for child + reader.il.Emit (OpCodes.Ldloc_0); + + Type t = Type.GetType ("Crow." + reader.Name); + if (t == null) { + Assembly a = Assembly.GetEntryAssembly (); + foreach (Type expT in a.GetExportedTypes ()) { + if (expT.Name == reader.Name) + t = expT; + } + } + if (t == null) + throw new Exception (reader.Name + " type not found"); + + reader.il.Emit(OpCodes.Newobj, t.GetConstructors () [0]); + reader.il.Emit (OpCodes.Stloc_0);//child is now loc_0 + + BuildInstanciator(reader, t); + + reader.il.Emit (OpCodes.Ldloc_0);//load child on stack for parenting + reader.il.Emit (OpCodes.Callvirt, miAddChild); + reader.il.Emit (OpCodes.Stloc_0); //reset local to current go + reader.il.Emit (OpCodes.Ldloc_0);//save current go onto the stack if child has to be added + break; + } + if (endTagReached) + break; + } + } + public static void EmitSetValue(ILGenerator il, PropertyInfo pi, object val){ + il.Emit (OpCodes.Ldloc_0); + + if (val == null) { + il.Emit (OpCodes.Ldnull); + il.Emit (OpCodes.Callvirt, pi.GetSetMethod ()); + return; + } + Type dvType = val.GetType (); + + if (dvType.IsValueType) { + if (pi.PropertyType.IsValueType) { + if (pi.PropertyType.IsEnum) { + if (pi.PropertyType != dvType) + throw new Exception ("Enum mismatch in default values: " + pi.PropertyType.FullName); + il.Emit (OpCodes.Ldc_I4, Convert.ToInt32 (val)); + } else { + switch (Type.GetTypeCode (dvType)) { + case TypeCode.Boolean: + if ((bool)val == true) + il.Emit (OpCodes.Ldc_I4_1); + else + il.Emit (OpCodes.Ldc_I4_0); + break; + // case TypeCode.Empty: + // break; + // case TypeCode.Object: + // break; + // case TypeCode.DBNull: + // break; + // case TypeCode.SByte: + // break; + // case TypeCode.Decimal: + // break; + // case TypeCode.DateTime: + // break; + case TypeCode.Char: + il.Emit (OpCodes.Ldc_I4, Convert.ToChar (val)); + break; + case TypeCode.Byte: + case TypeCode.Int16: + case TypeCode.Int32: + il.Emit (OpCodes.Ldc_I4, Convert.ToInt32 (val)); + break; + case TypeCode.UInt16: + case TypeCode.UInt32: + il.Emit (OpCodes.Ldc_I4, Convert.ToUInt32 (val)); + break; + case TypeCode.Int64: + il.Emit (OpCodes.Ldc_I8, Convert.ToInt64 (val)); + break; + case TypeCode.UInt64: + il.Emit (OpCodes.Ldc_I8, Convert.ToUInt64 (val)); + break; + case TypeCode.Single: + il.Emit (OpCodes.Ldc_R4, Convert.ToSingle (val)); + break; + case TypeCode.Double: + il.Emit (OpCodes.Ldc_R8, Convert.ToDouble (val)); + break; + case TypeCode.String: + il.Emit (OpCodes.Ldstr, Convert.ToString (val)); + break; + default: + il.Emit (OpCodes.Pop); + return; + } + } + } else + throw new Exception ("Expecting valuetype in default values for: " + pi.Name); + }else{ + //surely a class or struct + if (dvType != typeof(string)) + throw new Exception ("Expecting String in default values for: " + pi.Name); + if (pi.PropertyType == typeof(string)) + il.Emit (OpCodes.Ldstr, Convert.ToString (val)); + else if (pi.PropertyType.IsEnum) { + MethodInfo miParse = typeof(Enum).GetMethod + ("Parse", BindingFlags.Static | BindingFlags.Public, + Type.DefaultBinder, new Type [] {typeof (Type), typeof (string), typeof (bool)}, null); + + if (miParse == null) + throw new Exception ("Enum Parse method not found"); + + //load type of enum + il.Emit(OpCodes.Ldtoken, pi.PropertyType); + il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", new + Type[1]{typeof(RuntimeTypeHandle)})); + //load enum value name + il.Emit (OpCodes.Ldstr, Convert.ToString (val));//TODO:is this convert required? + //load false + il.Emit (OpCodes.Ldc_I4_0); + il.Emit (OpCodes.Callvirt, miParse); + + if (miParse.ReturnType != pi.PropertyType) + il.Emit (OpCodes.Unbox_Any, pi.PropertyType); + } else { + MethodInfo miParse = pi.PropertyType.GetMethod + ("Parse", BindingFlags.Static | BindingFlags.Public, + Type.DefaultBinder, new Type [] {typeof (string)},null); + if (miParse == null) + throw new Exception ("no Parse method found for: " + pi.PropertyType.FullName); + + il.Emit (OpCodes.Ldstr, Convert.ToString (val));//TODO:is this convert required? + il.Emit (OpCodes.Callvirt, miParse); + + if (miParse.ReturnType != pi.PropertyType) + il.Emit (OpCodes.Unbox_Any, pi.PropertyType); + } + } + il.Emit (OpCodes.Callvirt, pi.GetSetMethod ()); + } public static void ResolveBindings (List<Binding> Bindings) { if (Bindings == null) diff --git a/src/GraphicObjects/Container.cs b/src/GraphicObjects/Container.cs index 69e3a3b4..9acbf03a 100644 --- a/src/GraphicObjects/Container.cs +++ b/src/GraphicObjects/Container.cs @@ -4,6 +4,7 @@ using System.Reflection; using System.ComponentModel; using System.Linq; using System.Threading; +using System.Diagnostics; namespace Crow { @@ -25,9 +26,10 @@ namespace Crow get { return child; } set { child = value; } } - public virtual T SetChild<T> (T _child) + public virtual void SetChild(GraphicObject _child) { - return base.SetChild (_child); + Debug.WriteLine ("container setChild"); + base.SetChild (_child); } #region IXmlSerializable diff --git a/src/GraphicObjects/GraphicObject.cs b/src/GraphicObjects/GraphicObject.cs index 8966acf6..fb9f592d 100644 --- a/src/GraphicObjects/GraphicObject.cs +++ b/src/GraphicObjects/GraphicObject.cs @@ -12,16 +12,8 @@ using System.IO; namespace Crow { - public class GraphicObject : IXmlSerializable, ILayoutable, IValueChange, ICloneable, IBindable + public class GraphicObject : IXmlSerializable, ILayoutable, IValueChange, ICloneable { - #region IBindable implementation - List<Binding> bindings = new List<Binding> (); - public List<Binding> Bindings { - get { return bindings; } - } - - #endregion - internal static ulong currentUid = 0; internal ulong uid = 0; @@ -513,7 +505,7 @@ namespace Crow #endregion /// <summary> Loads the default values from XML attributes default </summary> - protected virtual void loadDefaultValues() + public void loadDefaultValues() { #if DEBUG_LOAD Debug.WriteLine ("LoadDefValues for " + this.ToString ()); @@ -581,151 +573,70 @@ namespace Crow typeof(void),new Type[] {typeof(object)},thisType,true); il = dm.GetILGenerator(256); - + il.DeclareLocal(typeof(GraphicObject)); il.Emit(OpCodes.Nop); + //set local GraphicObject to root object passed as 1st argument + il.Emit (OpCodes.Ldarg_0); + il.Emit (OpCodes.Stloc_0); - foreach (PropertyInfo pi in thisType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { - string name = ""; - object defaultValue = null; - - #region retrieve custom attributes + foreach (PropertyInfo pi in thisType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { if (pi.GetSetMethod () == null) continue; - - XmlIgnoreAttribute xia = (XmlIgnoreAttribute)pi.GetCustomAttribute (typeof(XmlIgnoreAttribute)); - if (xia != null) - continue; - XmlAttributeAttribute xaa = (XmlAttributeAttribute)pi.GetCustomAttribute (typeof(XmlAttributeAttribute)); - if (xaa != null) { - if (string.IsNullOrEmpty (xaa.AttributeName)) - name = pi.Name; - else - name = xaa.AttributeName; - } - - int styleIndex = -1; - if (styling.Count > 0){ - for (int i = 0; i < styling.Count; i++) { - if (styling[i].ContainsKey (name)){ - styleIndex = i; - break; - } - } - } - if (styleIndex >= 0){ - if (pi.PropertyType.IsEnum)//maybe should be in parser.. - defaultValue = Enum.Parse(pi.PropertyType, (string)styling[styleIndex] [name], true); - else - defaultValue = styling[styleIndex] [name]; - }else { - DefaultValueAttribute dv = (DefaultValueAttribute)pi.GetCustomAttribute (typeof (DefaultValueAttribute)); - if (dv == null) - continue; - defaultValue = dv.Value; - } - #endregion - - il.Emit (OpCodes.Ldarg_0); - - if (defaultValue == null) { - il.Emit (OpCodes.Ldnull); - il.Emit (OpCodes.Callvirt, pi.GetSetMethod ()); + object defaultValue; + if (!getDefaultValue (pi, styling, out defaultValue)) continue; - } - Type dvType = defaultValue.GetType (); - if (dvType.IsValueType) { - if (pi.PropertyType.IsValueType) { - if (pi.PropertyType.IsEnum) { - if (pi.PropertyType != dvType) - throw new Exception ("Enum mismatch in default values: " + pi.PropertyType.FullName); - il.Emit (OpCodes.Ldc_I4, Convert.ToInt32 (defaultValue)); - } else { - switch (Type.GetTypeCode (dvType)) { - case TypeCode.Boolean: - if ((bool)defaultValue == true) - il.Emit (OpCodes.Ldc_I4_1); - else - il.Emit (OpCodes.Ldc_I4_0); - break; -// case TypeCode.Empty: -// break; -// case TypeCode.Object: -// break; -// case TypeCode.DBNull: -// break; -// case TypeCode.SByte: -// break; -// case TypeCode.Decimal: -// break; -// case TypeCode.DateTime: -// break; - case TypeCode.Char: - il.Emit (OpCodes.Ldc_I4, Convert.ToChar (defaultValue)); - break; - case TypeCode.Byte: - case TypeCode.Int16: - case TypeCode.Int32: - il.Emit (OpCodes.Ldc_I4, Convert.ToInt32 (defaultValue)); - break; - case TypeCode.UInt16: - case TypeCode.UInt32: - il.Emit (OpCodes.Ldc_I4, Convert.ToUInt32 (defaultValue)); - break; - case TypeCode.Int64: - il.Emit (OpCodes.Ldc_I8, Convert.ToInt64 (defaultValue)); - break; - case TypeCode.UInt64: - il.Emit (OpCodes.Ldc_I8, Convert.ToUInt64 (defaultValue)); - break; - case TypeCode.Single: - il.Emit (OpCodes.Ldc_R4, Convert.ToSingle (defaultValue)); - break; - case TypeCode.Double: - il.Emit (OpCodes.Ldc_R8, Convert.ToDouble (defaultValue)); - break; - case TypeCode.String: - il.Emit (OpCodes.Ldstr, Convert.ToString (defaultValue)); - break; - default: - il.Emit (OpCodes.Pop); - continue; - } - } - } else - throw new Exception ("Expecting valuetype in default values for: " + pi.Name); - }else{ - //surely a class or struct - if (dvType != typeof(string)) - throw new Exception ("Expecting String in default values for: " + pi.Name); - if (pi.PropertyType == typeof (string)) - il.Emit (OpCodes.Ldstr, Convert.ToString (defaultValue)); - else { - MethodInfo miParse = pi.PropertyType.GetMethod - ("Parse", BindingFlags.Static | BindingFlags.Public, - Type.DefaultBinder, new Type [] {typeof (string)},null); - if (miParse == null) - throw new Exception ("no Parse method found for: " + pi.PropertyType.FullName); - - il.Emit (OpCodes.Ldstr, Convert.ToString (defaultValue)); - il.Emit (OpCodes.Callvirt, miParse); - - if (miParse.ReturnType != pi.PropertyType) - il.Emit (OpCodes.Unbox_Any, pi.PropertyType); - } - } - il.Emit (OpCodes.Callvirt, pi.GetSetMethod ()); + CompilerServices.EmitSetValue (il, pi, defaultValue); } il.Emit(OpCodes.Ret); #endregion try { - Interface.DefaultValuesLoader[styleKey] = (Interface.loadDefaultInvoker)dm.CreateDelegate(typeof(Interface.loadDefaultInvoker)); + Interface.DefaultValuesLoader[styleKey] = (Interface.LoaderInvoker)dm.CreateDelegate(typeof(Interface.LoaderInvoker)); Interface.DefaultValuesLoader[styleKey] (this); } catch (Exception ex) { throw new Exception ("Error applying style <" + styleKey + ">:", ex); } } + bool getDefaultValue(PropertyInfo pi, List<Dictionary<string, object>> styling, + out object defaultValue){ + defaultValue = null; + string name = ""; + + XmlIgnoreAttribute xia = (XmlIgnoreAttribute)pi.GetCustomAttribute (typeof(XmlIgnoreAttribute)); + if (xia != null) + return false; + XmlAttributeAttribute xaa = (XmlAttributeAttribute)pi.GetCustomAttribute (typeof(XmlAttributeAttribute)); + if (xaa != null) { + if (string.IsNullOrEmpty (xaa.AttributeName)) + name = pi.Name; + else + name = xaa.AttributeName; + } + + int styleIndex = -1; + if (styling.Count > 0){ + for (int i = 0; i < styling.Count; i++) { + if (styling[i].ContainsKey (name)){ + styleIndex = i; + break; + } + } + } + if (styleIndex >= 0){ + if (pi.PropertyType.IsEnum)//maybe should be in parser.. + defaultValue = Enum.Parse(pi.PropertyType, (string)styling[styleIndex] [name], true); + else + defaultValue = styling[styleIndex] [name]; + }else { + DefaultValueAttribute dv = (DefaultValueAttribute)pi.GetCustomAttribute (typeof (DefaultValueAttribute)); + if (dv == null) + return false; + defaultValue = dv.Value; + } + return true; + } + public virtual GraphicObject FindByName(string nameToFind){ return string.Equals(nameToFind, _name, StringComparison.Ordinal) ? this : null; @@ -747,7 +658,7 @@ namespace Crow if (Width == Measure.Fit || Height == Measure.Fit) RegisterForLayouting (LayoutingType.Sizing); else if (RegisteredLayoutings == LayoutingType.None) - Interface.CurrentInterface.EnqueueForRepaint (this); + CurrentInterface.EnqueueForRepaint (this); } /// <summary> query an update of the content, a redraw </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -755,8 +666,20 @@ namespace Crow { bmp = null; if (RegisteredLayoutings == LayoutingType.None) - Interface.CurrentInterface.EnqueueForRepaint (this); + CurrentInterface.EnqueueForRepaint (this); } + public Interface CurrentInterface { + get { + ILayoutable tmp = this.Parent; + while (tmp != null) { + if (tmp is Interface) + return tmp as Interface; + tmp = tmp.Parent; + } + return null; + } + } + #region Layouting #if DEBUG_LAYOUTING @@ -1200,18 +1123,15 @@ namespace Crow } #region Binding - public void BindMember(string _member, string _expression){ - Bindings.Add(new Binding (this, _member, _expression)); - } public virtual void ResolveBindings() { - if (Bindings.Count == 0) - return; - #if DEBUG_BINDING - Debug.WriteLine ("Resolve Bindings => " + this.ToString ()); - #endif - - CompilerServices.ResolveBindings (Bindings); +// if (Bindings.Count == 0) +// return; +// #if DEBUG_BINDING +// Debug.WriteLine ("Resolve Bindings => " + this.ToString ()); +// #endif +// +// CompilerServices.ResolveBindings (Bindings); } /// <summary> @@ -1220,53 +1140,53 @@ namespace Crow /// </summary> public virtual void ClearBinding(){ //dont clear binding if dataSource is not null, - foreach (Binding b in Bindings) { - try { - if (!b.Resolved) - continue; - //cancel compiled events - if (b.Target == null){ - continue; - #if DEBUG_BINDING - Debug.WriteLine("Clear binding canceled for => " + b.ToString()); - #endif - } - if (b.Target.Instance != DataSource){ - #if DEBUG_BINDING - Debug.WriteLine("Clear binding canceled for => " + b.ToString()); - #endif - continue; - } - #if DEBUG_BINDING - Debug.WriteLine("ClearBinding => " + b.ToString()); - #endif - if (string.IsNullOrEmpty (b.DynMethodId)) { - b.Resolved = false; - if (b.Source.Member.MemberType == MemberTypes.Event) - removeEventHandler (b); - //TODO:check if full reset is necessary - continue; - } - MemberReference mr = null; - if (b.Target == null) - mr = b.Source; - else - mr = b.Target; - Type dataSourceType = mr.Instance.GetType(); - EventInfo evtInfo = dataSourceType.GetEvent ("ValueChanged"); - FieldInfo evtFi = CompilerServices.GetEventHandlerField (dataSourceType, "ValueChanged"); - MulticastDelegate multicastDelegate = evtFi.GetValue (mr.Instance) as MulticastDelegate; - if (multicastDelegate != null) { - foreach (Delegate d in multicastDelegate.GetInvocationList()) { - if (d.Method.Name == b.DynMethodId) - evtInfo.RemoveEventHandler (mr.Instance, d); - } - } - b.Reset (); - } catch (Exception ex) { - Debug.WriteLine("\t Error: " + ex.ToString()); - } - } +// foreach (Binding b in Bindings) { +// try { +// if (!b.Resolved) +// continue; +// //cancel compiled events +// if (b.Target == null){ +// continue; +// #if DEBUG_BINDING +// Debug.WriteLine("Clear binding canceled for => " + b.ToString()); +// #endif +// } +// if (b.Target.Instance != DataSource){ +// #if DEBUG_BINDING +// Debug.WriteLine("Clear binding canceled for => " + b.ToString()); +// #endif +// continue; +// } +// #if DEBUG_BINDING +// Debug.WriteLine("ClearBinding => " + b.ToString()); +// #endif +// if (string.IsNullOrEmpty (b.DynMethodId)) { +// b.Resolved = false; +// if (b.Source.Member.MemberType == MemberTypes.Event) +// removeEventHandler (b); +// //TODO:check if full reset is necessary +// continue; +// } +// MemberReference mr = null; +// if (b.Target == null) +// mr = b.Source; +// else +// mr = b.Target; +// Type dataSourceType = mr.Instance.GetType(); +// EventInfo evtInfo = dataSourceType.GetEvent ("ValueChanged"); +// FieldInfo evtFi = CompilerServices.GetEventHandlerField (dataSourceType, "ValueChanged"); +// MulticastDelegate multicastDelegate = evtFi.GetValue (mr.Instance) as MulticastDelegate; +// if (multicastDelegate != null) { +// foreach (Delegate d in multicastDelegate.GetInvocationList()) { +// if (d.Method.Name == b.DynMethodId) +// evtInfo.RemoveEventHandler (mr.Instance, d); +// } +// } +// b.Reset (); +// } catch (Exception ex) { +// Debug.WriteLine("\t Error: " + ex.ToString()); +// } +// } } void removeEventHandler(Binding b){ FieldInfo fiEvt = CompilerServices.GetEventHandlerField (b.Source.Instance.GetType(), b.Source.Member.Name); @@ -1297,7 +1217,7 @@ namespace Crow return; } if (mi.MemberType == MemberTypes.Event) { - this.Bindings.Add (new Binding (new MemberReference(this, mi), value)); + Interface.RegisterBinding (new Binding (new MemberReference(this, mi), value)); return; } if (mi.MemberType == MemberTypes.Property) { @@ -1318,7 +1238,7 @@ namespace Crow if (!value.EndsWith("}", StringComparison.Ordinal)) throw new Exception (string.Format("XML:Malformed binding: {0}", value)); - this.Bindings.Add (new Binding (new MemberReference(this, pi), value.Substring (1, value.Length - 2))); + Interface.RegisterBinding (new Binding (new MemberReference(this, pi), value.Substring (1, value.Length - 2))); return; } if (pi.GetCustomAttribute (typeof(XmlIgnoreAttribute)) != null) @@ -1341,21 +1261,34 @@ namespace Crow } public virtual void ReadXml (System.Xml.XmlReader reader) { - if (reader.HasAttributes) { - - style = reader.GetAttribute ("Style"); + IMLInstantiatorBuilder ir = reader as IMLInstantiatorBuilder; + Type thisType = this.GetType (); - loadDefaultValues (); + if (ir.HasAttributes) { + style = ir.GetAttribute ("Style"); + if (!string.IsNullOrEmpty (style)) { + PropertyInfo pi = thisType.GetProperty ("Style"); + CompilerServices.EmitSetValue (ir.il, pi, style); + } + } + if (ir.HasAttributes) { + ir.il.Emit (OpCodes.Ldarg_0); + ir.il.Emit (OpCodes.Callvirt, typeof(GraphicObject).GetMethod ("loadDefaultValues")); - while (reader.MoveToNextAttribute ()) { - if (reader.Name == "Style") + while (ir.MoveToNextAttribute ()) { + if (ir.Name == "Style") continue; - affectMember (reader.Name, reader.Value); + PropertyInfo pi = thisType.GetProperty (ir.Name); + + if (pi == null) + throw new Exception ("Member '" + ir.Name + "' not found in " + thisType.Name); + + CompilerServices.EmitSetValue (ir.il, pi, ir.Value); + } - reader.MoveToElement (); - }else - loadDefaultValues (); + ir.MoveToElement (); + } } public virtual void WriteXml (System.Xml.XmlWriter writer) { @@ -1445,6 +1378,8 @@ namespace Crow } #endregion + + #region ICloneable implementation public object Clone () { @@ -1470,8 +1405,8 @@ namespace Crow /// </summary> public virtual GraphicObject DeepClone(){ GraphicObject tmp = Clone () as GraphicObject; - foreach (Binding b in this.bindings) - tmp.Bindings.Add (new Binding (new MemberReference (tmp, b.Source.Member), b.Expression)); +// foreach (Binding b in this.bindings) +// tmp.Bindings.Add (new Binding (new MemberReference (tmp, b.Source.Member), b.Expression)); return tmp; } } diff --git a/src/GraphicObjects/Grid.cs b/src/GraphicObjects/Grid.cs index 525827e0..ed0cd986 100644 --- a/src/GraphicObjects/Grid.cs +++ b/src/GraphicObjects/Grid.cs @@ -29,11 +29,10 @@ namespace Crow int _rowCount; #endregion - public override T AddChild<T> (T child) + public override void AddChild (GraphicObject child) { - T tmp = base.AddChild (child); + base.AddChild (child); this.RegisterForLayouting (LayoutingType.ArrangeChildren); - return tmp; } public override void RemoveChild (GraphicObject child) { diff --git a/src/GraphicObjects/Group.cs b/src/GraphicObjects/Group.cs index a6ca5758..4c8d0537 100644 --- a/src/GraphicObjects/Group.cs +++ b/src/GraphicObjects/Group.cs @@ -38,19 +38,14 @@ namespace Crow get { return _multiSelect; } set { _multiSelect = value; } } - - - public virtual T AddChild<T>(T child) - { - GraphicObject g = child as GraphicObject; + public virtual void AddChild(GraphicObject g){ Children.Add(g); - g.Parent = this as GraphicObject; + g.Parent = this; g.ResolveBindings (); g.RegisteredLayoutings = LayoutingType.None; g.RegisterForLayouting (LayoutingType.Sizing | LayoutingType.ArrangeChildren); g.LayoutChanged += OnChildLayoutChanges; - return (T)child; - } + } public virtual void RemoveChild(GraphicObject child) { child.LayoutChanged -= OnChildLayoutChanges; diff --git a/src/GraphicObjects/ListBox.cs b/src/GraphicObjects/ListBox.cs index 58d41e0b..397ef53d 100644 --- a/src/GraphicObjects/ListBox.cs +++ b/src/GraphicObjects/ListBox.cs @@ -151,10 +151,10 @@ namespace Crow //because _list total size is forced to approx size if (_gsList.Orientation == Orientation.Horizontal) { page.Width = Measure.Fit; - page.BindMember ("Height", "../HeightPolicy"); + Interface.Bindings.Add(new Binding (this, "Height", "../HeightPolicy")); } else { page.Height = Measure.Fit; - page.BindMember ("Width", "../WidthPolicy"); + Interface.Bindings.Add(new Binding (this, "Width", "../WidthPolicy")); } for (int i = (pageNum - 1) * itemPerPage; i < pageNum * itemPerPage; i++) { diff --git a/src/GraphicObjects/PrivateContainer.cs b/src/GraphicObjects/PrivateContainer.cs index 8793ba45..51d76604 100644 --- a/src/GraphicObjects/PrivateContainer.cs +++ b/src/GraphicObjects/PrivateContainer.cs @@ -22,6 +22,7 @@ using System; using System.Xml.Serialization; using System.ComponentModel; using Cairo; +using System.Diagnostics; namespace Crow { @@ -47,7 +48,7 @@ namespace Crow protected GraphicObject child; - protected virtual T SetChild<T>(T _child) + internal virtual void SetChild(GraphicObject _child) { if (child != null) { @@ -67,8 +68,8 @@ namespace Crow child.RegisteredLayoutings = LayoutingType.None; child.RegisterForLayouting (LayoutingType.Sizing); } + Debug.WriteLine ("privateContainer setChild"); - return (T)_child; } #region GraphicObject Overrides diff --git a/src/GraphicObjects/Scroller.cs b/src/GraphicObjects/Scroller.cs index 3eaff5f4..c248595d 100644 --- a/src/GraphicObjects/Scroller.cs +++ b/src/GraphicObjects/Scroller.cs @@ -148,7 +148,7 @@ namespace Crow ScrollY = 0; ScrollX = 0; } - public override T SetChild<T> (T _child) + public override void SetChild (GraphicObject _child) { GraphicObject c = child as GraphicObject; Group g = child as Group; @@ -164,7 +164,7 @@ namespace Crow if (g != null) g.ChildrenCleared += onChildListCleared; } - return base.SetChild (_child); + base.SetChild (_child); } #endregion diff --git a/src/GraphicObjects/TabView.cs b/src/GraphicObjects/TabView.cs index d616ffc6..015f5f4f 100644 --- a/src/GraphicObjects/TabView.cs +++ b/src/GraphicObjects/TabView.cs @@ -100,7 +100,7 @@ namespace Crow set { } } - public override T AddChild<T> (T child) + public override void AddChild (GraphicObject child) { TabItem ti = child as TabItem; if (ti == null) @@ -113,7 +113,7 @@ namespace Crow SelectedTab = 0; } - return base.AddChild (child); + base.AddChild (child); } public override void RemoveChild (GraphicObject child) { diff --git a/src/IMLInstantiatorBuilder.cs b/src/IMLInstantiatorBuilder.cs new file mode 100644 index 00000000..a9c078d4 --- /dev/null +++ b/src/IMLInstantiatorBuilder.cs @@ -0,0 +1,100 @@ +// +// IMLReader.cs +// +// Author: +// Jean-Philippe Bruyère <jp.bruyere@hotmail.com> +// +// Copyright (c) 2016 jp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +using System; +using System.Xml; +using System.IO; +using System.Reflection; +using System.Reflection.Emit; + +namespace Crow +{ + public class IMLInstantiatorBuilder : XmlTextReader + { + public Stream ImlStream; + public Type RootType = null; + + DynamicMethod dm = null; + public ILGenerator il = null; + + public IMLInstantiatorBuilder (Stream stream) + : base(stream) + { + ImlStream = stream; + } + public IMLInstantiatorBuilder (ILGenerator ilGen, string xmlFragment) + : base(xmlFragment, XmlNodeType.Element,null){ + il = ilGen; + } + /// <summary> + /// Inits il generator, RootType must have been read first + /// </summary> + public void InitEmitter(){ + + dm = new DynamicMethod("dyn_instantiator", + MethodAttributes.Family | MethodAttributes.FamANDAssem | MethodAttributes.NewSlot, + CallingConventions.Standard, + typeof(void),new Type[] {typeof(object)}, RootType, true); + + il = dm.GetILGenerator(256); + il.DeclareLocal(typeof(GraphicObject)); + il.Emit(OpCodes.Nop); + //set local GraphicObject to root object passed as 1st argument + il.Emit (OpCodes.Ldarg_0); + il.Emit (OpCodes.Stloc_0); + } + + /// <summary> + /// read first node to set GraphicObject class for loading + /// and let reader position on that node + /// </summary> + public Type ReadRootType () + { + string root = "Object"; + while (Read()) { + if (NodeType == XmlNodeType.Element) { + root = this.Name; + break; + } + } + + Type t = Type.GetType ("Crow." + root); + if (t == null) { + Assembly a = Assembly.GetEntryAssembly (); + foreach (Type expT in a.GetExportedTypes ()) { + if (expT.Name == root) + t = expT; + } + } + RootType = t; + return t; + } + /// <summary> + /// Finalize instatiator MSIL and return LoaderInvoker delegate + /// </summary> + public Instantiator GetInstanciator(){ + il.Emit(OpCodes.Ret); + + return new Instantiator (RootType, + (Interface.LoaderInvoker)dm.CreateDelegate (typeof(Interface.LoaderInvoker))); + } + } +} + diff --git a/src/IMLStream.cs b/src/IMLStream.cs index 542797a1..290c5355 100644 --- a/src/IMLStream.cs +++ b/src/IMLStream.cs @@ -23,6 +23,10 @@ using System.IO; using System.Reflection; using System.Reflection.Emit; using System.Threading; +using System.Xml.Serialization; +using System.Xml; +using System.Collections.Generic; +using System.Diagnostics; namespace Crow { @@ -33,10 +37,10 @@ namespace Crow Path = path; using (Stream stream = Interface.GetStreamFromPath (path)) stream.CopyTo (this); - RootType = Interface.GetTopContainerOfXMLStream (this); + //RootType = Interface.GetTopContainerOfXMLStream (this); } public IMLStream(Byte[] b) : base (b){ - RootType = Interface.GetTopContainerOfXMLStream (this); + //RootType = Interface.GetTopContainerOfXMLStream (this); } /// <summary> /// Create a graphicObject instance from the this XML stream. @@ -47,7 +51,7 @@ namespace Crow Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture; Seek (0, SeekOrigin.Begin); - GraphicObject tmp = Interface.Load (this, this.RootType); + GraphicObject tmp = null;//Interface.Load (this, this.RootType); Thread.CurrentThread.CurrentCulture = savedCulture; return tmp; diff --git a/src/GraphicObjects/IBindable.cs b/src/Instantiator.cs index 45ff5864..e54e744e 100644 --- a/src/GraphicObjects/IBindable.cs +++ b/src/Instantiator.cs @@ -1,10 +1,10 @@ // -// IValueChange.cs +// Instantiator.cs // // Author: // Jean-Philippe Bruyère <jp.bruyere@hotmail.com> // -// Copyright (c) 2015 jp +// Copyright (c) 2016 jp // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -19,14 +19,23 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. using System; -using System.Collections.Generic; namespace Crow { - public interface IBindable + public class Instantiator { - List<Binding> Bindings { get; } - object DataSource { get; set; } + Type RootType; + Interface.LoaderInvoker Loader; + public Instantiator (Type _root, Interface.LoaderInvoker loader) + { + RootType = _root; + Loader = loader; + } + public GraphicObject CreateInstance(){ + GraphicObject tmp = (GraphicObject)Activator.CreateInstance(RootType); + Loader (tmp); + return tmp; + } } } diff --git a/src/Interface.cs b/src/Interface.cs index 7b324de7..4712a54c 100644 --- a/src/Interface.cs +++ b/src/Interface.cs @@ -52,6 +52,10 @@ namespace Crow FontRenderingOptions.HintMetrics = HintMetrics.On; FontRenderingOptions.HintStyle = HintStyle.Medium; FontRenderingOptions.SubpixelOrder = SubpixelOrder.Rgb; + + Thread tBindings = new Thread (BindingThread); + tBindings.IsBackground = true; + tBindings.Start (); } public Interface(){ Interface.CurrentInterface = this; @@ -107,14 +111,16 @@ namespace Crow g.IsQueueForRedraw = true; } } + //fast compiled IML instantiators + public static Dictionary<String, Instantiator> Instantiators = new Dictionary<string, Instantiator>(); #region Default values and Style loading /// Default values of properties from GraphicObjects are retrieve from XML Attributes. /// The reflexion process used to retrieve those values being very slow, it is compiled in MSIL /// and injected as a dynamic method referenced in the DefaultValuesLoader Dictionnary. /// The compilation is done on the first object instancing, and is also done for custom widgets - public delegate void loadDefaultInvoker(object instance); - public static Dictionary<String, loadDefaultInvoker> DefaultValuesLoader = new Dictionary<string, loadDefaultInvoker>(); + public delegate void LoaderInvoker(object instance); + public static Dictionary<String, LoaderInvoker> DefaultValuesLoader = new Dictionary<string, LoaderInvoker>(); public static Dictionary<string, Dictionary<string, object>> Styling; /// <summary> parse all styling data's and build global Styling Dictionary </summary> static void LoadStyling() { @@ -176,36 +182,6 @@ namespace Crow return stream; } - /// <summary> - /// Pre-read first node to set GraphicObject class for loading - /// and reset stream position to 0 - /// </summary> - public static Type GetTopContainerOfXMLStream (Stream stream) - { - string root = "Object"; - stream.Seek (0, SeekOrigin.Begin); - using (XmlReader reader = XmlReader.Create (stream)) { - while (reader.Read ()) { - // first element is the root element - if (reader.NodeType == XmlNodeType.Element) { - root = reader.Name; - break; - } - } - } - - Type t = Type.GetType ("Crow." + root); - if (t == null) { - Assembly a = Assembly.GetEntryAssembly (); - foreach (Type expT in a.GetExportedTypes ()) { - if (expT.Name == root) - t = expT; - } - } - - stream.Seek (0, SeekOrigin.Begin); - return t; - } public static void Save<T> (string file, T graphicObject) { @@ -225,9 +201,10 @@ namespace Crow GraphicObject tmp = null; try { - using (Stream stream = GetStreamFromPath (path)) { - tmp = Load(stream, GetTopContainerOfXMLStream(stream)); - } + if (!Instantiators.ContainsKey(path)) + BuildInstaciator(path); + tmp = Instantiators [path].CreateInstance (); + } catch (Exception ex) { throw new Exception ("Error loading <" + path + ">:", ex); } @@ -236,37 +213,37 @@ namespace Crow return tmp; } - internal static GraphicObject Load (Stream stream, Type type) + public static void BuildInstaciator (string path) { + System.Globalization.CultureInfo savedCulture = Thread.CurrentThread.CurrentCulture; + Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture; + #if DEBUG_LOAD Stopwatch loadingTime = new Stopwatch (); loadingTime.Start (); #endif - GraphicObject result; - - CurrentInterface.XmlLoading = true; - - XmlSerializerNamespaces xn = new XmlSerializerNamespaces (); - xn.Add ("", ""); - - XmlSerializer xs = new XmlSerializer (type); - - result = (GraphicObject)xs.Deserialize (stream); - CurrentInterface.XmlLoading = false; + try { + using (Stream stream = GetStreamFromPath (path)) { + using (IMLInstantiatorBuilder itr = new IMLInstantiatorBuilder (stream)){ + itr.ReadRootType(); + itr.InitEmitter(); + CompilerServices.BuildInstanciator(itr, itr.RootType); + itr.Read();//close tag + Instantiators[path] = itr.GetInstanciator(); + } + } + } catch (Exception ex) { + throw new Exception ("Error loading <" + path + ">:", ex); + } #if DEBUG_LOAD - FileStream fs = stream as FileStream; - if (fs!=null) - CurrentGOMLPath = fs.Name; loadingTime.Stop (); - Debug.WriteLine ("GOML Loading ({2}->{3}): {0} ticks, {1} ms", - loadingTime.ElapsedTicks, - loadingTime.ElapsedMilliseconds, - CurrentGOMLPath, result.ToString()); + Debug.WriteLine ("IML Loading '{2}' : {0} ticks, {1} ms", + loadingTime.ElapsedTicks, loadingTime.ElapsedMilliseconds, path); #endif - return result; + Thread.CurrentThread.CurrentCulture = savedCulture; } public GraphicObject LoadInterface (string path) @@ -289,6 +266,27 @@ namespace Crow public List<GraphicObject> GraphicTree = new List<GraphicObject>(); + public static List<Binding> Bindings = new List<Binding>(); + public static void RegisterBinding (Binding b){ + lock (Bindings) + Bindings.Add (b); + } + public static void BindingThread(){ + while(true){ + Binding[] unresolved; + lock (Bindings){ + unresolved = Bindings.Where (b => !b.Resolved).ToArray (); + } + foreach (Binding ub in unresolved) { + if (ub.TryFindTarget ()) { + ub.Resolved = true; + Debug.WriteLine ("RESOLVED: " + ub); + } + } + Thread.Sleep (5); + } + } + public static Interface CurrentInterface; Rectangles _redrawClip = new Rectangles(); |