aboutsummaryrefslogtreecommitdiffstatsci
path: root/src/IML
diff options
context:
space:
mode:
authorjpbruyere <jp.bruyere@hotmail.com>2017-01-24 12:46:43 +0100
committerjpbruyere <jp.bruyere@hotmail.com>2017-01-24 12:46:43 +0100
commitd8acaa909ce73e150827db9846cd8690fb93d68e (patch)
treec22aab77a5851aad79505a49dc6ac91ca4b0a106 /src/IML
parente39ac936765e88382d858ee6922293315a18b60d (diff)
parent1a4fe8bd37ed4a3e3324a134df4f90c14b315918 (diff)
Merge branch 'master' into CrowIDE
Diffstat (limited to 'src/IML')
-rw-r--r--src/IML/BindingMember.cs160
-rw-r--r--src/IML/Node.cs12
-rw-r--r--src/IML/NodeAddress.cs76
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 {