diff options
Diffstat (limited to 'src/IML')
| -rw-r--r-- | src/IML/BindingMember.cs | 160 | ||||
| -rw-r--r-- | src/IML/Node.cs | 12 | ||||
| -rw-r--r-- | src/IML/NodeAddress.cs | 76 |
3 files changed, 240 insertions, 8 deletions
diff --git a/src/IML/BindingMember.cs b/src/IML/BindingMember.cs new file mode 100644 index 00000000..f643d10b --- /dev/null +++ b/src/IML/BindingMember.cs @@ -0,0 +1,160 @@ +// +// BindingMember.cs +// +// Author: +// Jean-Philippe Bruyère <jp.bruyere@hotmail.com> +// +// Copyright (c) 2017 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.Reflection.Emit; + +namespace Crow +{ + /// <summary> + /// Expression token, a variable, a string constant or a parsable constant (having a static Parse method) + /// '../' => 1 level up in graphic tree + /// './' or '/' => template root level + /// '.Name1.Name2' current level properties + /// 'name.prop' named descendant in graphic tree, search with 'FindByName' method of GraphicObject + /// </summary> + public class BindingMember + { + /// <summary> + /// true if expression was enclosed in ' + /// </summary> + public bool IsStringConstant = false; + /// <summary> + /// Nb level to go up, '-1' for template root + /// </summary> + public int LevelsUp; + /// <summary> + /// Remaining string after '/' split, splitted on '.' + /// </summary> + public string[] Tokens; + + /// <summary> + /// Target the template's root node, expression was in the form './name[.name[...]]' or '/name[.name[...]]' + /// </summary> + public bool IsTemplateBinding { get { return LevelsUp < 0; }} + + /// <summary> + /// No level change and expression was '.name' + /// </summary> + /// <value><c>true</c> if this instance is current node property; otherwise, <c>false</c>.</value> + public bool IsCurrentNodeProperty { + get { + return LevelsUp == 0 && ( Tokens.Length == 2 && string.IsNullOrEmpty(Tokens[0])); + } + } + /// <summary> + /// no level change, and only a single name in Tokens[], that's dataSource member if property binding + /// </summary> + public bool IsSingleName { get { return LevelsUp == 0 && Tokens.Length == 1; }} + + #region CTOR + public BindingMember (){} + public BindingMember (string expression){ + if (string.IsNullOrEmpty (expression)) + return; + + string[] splitedExp = expression.Trim().Split ('/'); + + int ptr = 0; + if (splitedExp.Length == 1) { + if (splitedExp [0].StartsWith ("\'")) { + if (!splitedExp [0].EndsWith ("\'")) + throw new Exception (string.Format + ("IML:malformed string constant in binding expression: {0}", splitedExp [0])); + Tokens = new string[] { splitedExp [0].Substring (1, splitedExp [0].Length - 2) }; + IsStringConstant = true; + return; + } + } else { + if (string.IsNullOrEmpty (splitedExp [0]) || splitedExp [0] == ".") {//template root + LevelsUp = -1; + ptr++; + } else { + while (splitedExp [ptr] == "..") + ptr++; + LevelsUp = ptr; + } + } + if (ptr != splitedExp.Length - 1) + throw new Exception ("invalid expresion: " + expression); + Tokens = splitedExp [ptr].Split ('.'); + } + #endregion + + public void emitGetTarget(ILGenerator il, System.Reflection.Emit.Label cancel){ + + if (IsTemplateBinding) { + System.Reflection.Emit.Label nextLogicParent = il.DefineLabel (); + il.MarkLabel (nextLogicParent); + il.Emit (OpCodes.Callvirt, CompilerServices.miGetLogicalParent); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Brfalse, cancel); + il.Emit (OpCodes.Isinst, typeof(TemplatedControl)); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Brfalse, nextLogicParent); + } else if (LevelsUp > 0) {//go upward in logical tree + il.Emit (OpCodes.Ldc_I4, LevelsUp);//push arg 2 of goUpLevels + il.Emit (OpCodes.Callvirt, CompilerServices.miGoUpLevels); + //test if null + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Brfalse, cancel); + } + + if (!string.IsNullOrEmpty (Tokens [0])) {//find by name + il.Emit (OpCodes.Ldstr, Tokens [0]); + il.Emit (OpCodes.Callvirt, CompilerServices.miFindByName); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Brfalse, cancel); + } + + for (int i = 1; i < Tokens.Length -1; i++) { + System.Reflection.Emit.Label miOK = il.DefineLabel (); + il.Emit (OpCodes.Dup);//duplicate instance + il.Emit (OpCodes.Ldstr, Tokens [i]);//load member name + il.Emit (OpCodes.Call, CompilerServices.miGetMembIinfoWithRefx); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Brtrue, miOK); + il.Emit (OpCodes.Pop);//pop dup instance + il.Emit (OpCodes.Br, cancel); + il.MarkLabel (miOK); + il.Emit (OpCodes.Call, CompilerServices.miGetValWithRefx); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Brfalse, cancel); + } + } + public void emitGetProperty(ILGenerator il, System.Reflection.Emit.Label cancel) { + System.Reflection.Emit.Label miOK = il.DefineLabel (); + il.Emit (OpCodes.Dup);//duplicate instance + il.Emit (OpCodes.Ldstr, Tokens [Tokens.Length -1]);//load member name + il.Emit (OpCodes.Call, CompilerServices.miGetMembIinfoWithRefx); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Brtrue, miOK); + il.Emit (OpCodes.Pop);//pop dup instance + il.Emit (OpCodes.Br, cancel); + il.MarkLabel (miOK); + il.Emit (OpCodes.Call, CompilerServices.miGetValWithRefx); + } + public void emitSetProperty(ILGenerator il) { + il.Emit (OpCodes.Ldstr, Tokens [Tokens.Length -1]);//load member name + il.Emit (OpCodes.Call, CompilerServices.miSetValWithRefx); + } + } +} + diff --git a/src/IML/Node.cs b/src/IML/Node.cs index 4ba61c03..29f204f5 100644 --- a/src/IML/Node.cs +++ b/src/IML/Node.cs @@ -30,16 +30,18 @@ namespace Crow.IML /// </summary> public struct Node { - /// <summary> Current node type</summary> - public Type CrowType; - /// <summary> Index in parent, -1 for template</summary> - public int Index; - + #region CTOR public Node (Type crowType, int _index = 0) { CrowType = crowType; Index = _index; } + #endregion + + /// <summary> Current node type</summary> + public Type CrowType; + /// <summary> Index in parent, -1 for template</summary> + public int Index; public MethodInfo GetAddMethod(int childIdx){ if (typeof (Group).IsAssignableFrom (CrowType)) diff --git a/src/IML/NodeAddress.cs b/src/IML/NodeAddress.cs index 460998da..c81a7f66 100644 --- a/src/IML/NodeAddress.cs +++ b/src/IML/NodeAddress.cs @@ -26,13 +26,83 @@ using System.Reflection.Emit; namespace Crow.IML { + /// <summary> + /// Node address is a list of nodes from root to leaf defining a unique node + /// </summary> public class NodeAddress : List<Node> { - public NodeAddress (Node[] nodes) : base(nodes) { - } + #region CTOR + public NodeAddress (Node[] nodes) : base(nodes) {} + #endregion public Type NodeType { get { return Count == 0 ? null : this[this.Count -1].CrowType; }} + /// <summary> + /// Gets the node adress from binding expression starting from this node + /// and return in expression remaining part + /// </summary> + public NodeAddress ResolveExpression (ref string expression){ + int ptr = this.Count - 1; + string[] splitedExp = expression.Split ('/'); + + if (splitedExp.Length < 2)//dataSource binding + return null; + + if (string.IsNullOrEmpty (splitedExp [0]) || splitedExp [0] == ".") {//search template root + ptr--; + while (ptr >= 0) { + if (typeof(TemplatedControl).IsAssignableFrom (this [ptr].CrowType)) + break; + ptr--; + } + } else if (splitedExp [0] == "..") { //search starting at current node + int levelUp = splitedExp.Length - 1; + if (levelUp > ptr + 1) + throw new Exception ("Binding error: try to bind outside IML source"); + ptr -= levelUp; + } + expression = splitedExp [splitedExp.Length - 1]; + //TODO:change Template special address identified with Nodecount = 0 to something not using array count to 0, + //here linq is working without limits checking in compile option + //but defining a 0 capacity array with limits cheking enabled, cause 'out of memory' error + return new NodeAddress (this.Take(ptr+1).ToArray());//[ptr+1]; + //Array.Copy (sourceAddr.ToArray (), targetNode, ptr + 1); + //return new NodeAddress (targetNode); + } + /// <summary> + /// get BindingDefinition from binding expression + /// </summary> + public BindingDefinition GetBindingDef(string sourceMember, string expression){ + BindingDefinition bindingDef = new BindingDefinition(this, sourceMember); + if (string.IsNullOrEmpty (expression)) { + return bindingDef; + } else { + if (expression.StartsWith ("²")) { + bindingDef.TwoWay = true; + expression = expression.Substring (1); + } + + string exp = expression; + bindingDef.TargetNA = this.ResolveExpression (ref exp); + + string [] bindTrg = exp.Split ('.'); + + if (bindTrg.Length == 0) + throw new Exception ("invalid binding expression: " + expression); + if (bindTrg.Length == 1) + bindingDef.TargetMember = bindTrg [0]; + else { + if (!string.IsNullOrEmpty(bindTrg[0]))//searchByName + bindingDef.TargetName = bindTrg[0]; + + bindingDef.TargetMember = exp.Substring (bindTrg[0].Length + 1); + } + } + + return bindingDef; + } + + #region Object overrides public override bool Equals (object obj) { if (obj == null) @@ -50,7 +120,6 @@ namespace Crow.IML return hash; } } - public override string ToString () { string tmp = ""; @@ -58,6 +127,7 @@ namespace Crow.IML tmp += string.Format ("{0};", n.Index); return tmp; } + #endregion } public class NamedNodeAddress : NodeAddress { |