:Revert "fix line numbering issues with vala generator - hopefully fixes completion...
[roobuilder] / src / JsRender / Node.vala
index 90ce379..de6cba3 100644 (file)
 
 
 
-public class JsRender.Node : Object {
+public class JsRender.Node : GLib.Object {
        
 
        public static int uid_count = 0;
        
+       public int oid { get; private set; }
        public Node parent;
-       public Gee.ArrayList<Node> items; // child items..
-       
-       public Gee.HashMap<string,string> props; // the properties..
-       public Gee.HashMap<string,string> listeners; // the listeners..
+       private Gee.ArrayList<Node> items; // child items..
+       public GLib.ListStore  childstore; // must be kept in sync with items
+       public GLib.ListStore?  propstore; // must be kept in sync with items
        public string  xvala_cls;
        public string xvala_xcls; // 'Xcls_' + id;
        public string xvala_id; // item id or ""
@@ -107,16 +107,32 @@ public class JsRender.Node : Object {
        public int line_start;
        public int line_end;
        public Gee.ArrayList<int> lines;
-       public Gee.HashMap<int,string> line_map; // store of l:xxx or p:....
-       public Gee.ArrayList<int> node_lines;
+       public Gee.HashMap<int,string> line_map; // store of l:xxx or p:....  // fixme - not needed as we can store line numbers in props now.
+       public Gee.ArrayList<int> node_lines; 
        public Gee.HashMap<int,Node> node_lines_map; // store of l:xxx or p:....
        
+       private int _updated_count = 0;
+       public int updated_count { 
+               get {
+                       return this._updated_count; 
+               }
+               set  {
+                       this.nodeTitleProp = ""; // ?? should trigger set?
+                       this.iconFilename = "";
+                       this._updated_count = value;
+                       if (this.parent != null) {
+                               this.parent.updated_count++;
+                       }
+               }
+       } // changes to this trigger updates on the tree..
 
        public Node()
        {
                this.items = new Gee.ArrayList<Node>();
-               this.props = new Gee.HashMap<string,string>();
-               this.listeners = new Gee.HashMap<string,string>();
+               //this._props = new Gee.HashMap<string,NodeProp>();
+               //this._listeners = new Gee.HashMap<string,NodeProp>(); // Nodeprop can include line numbers..
+               this.propstore = new GLib.ListStore(typeof(NodeProp)); // Nodeprop can include line numbers..
                this.xvala_cls = "";
                this.xvala_xcls = "";
                this.xvala_id = "";
@@ -127,9 +143,16 @@ public class JsRender.Node : Object {
                this.line_map = new Gee.HashMap<int,string>();
                this.node_lines = new Gee.ArrayList<int>();
                this.node_lines_map = new Gee.HashMap<int,Node>();
+               this.childstore = new GLib.ListStore( typeof(Node));
+               this.oid = uid_count++;
                
        }
        
+       public  Gee.ArrayList<Node> readItems()
+       {
+               return this.items; // note should not modify add/remove from this directly..
+               
+       }
        public void setNodeLine(int line, Node node) {
                //print("Add node @ %d\n", line);
                if (this.node_lines_map.has_key(line)) {
@@ -140,7 +163,9 @@ public class JsRender.Node : Object {
                
        }
        
-       public void setLine(int line, string type, string prop) {
+       public void setLine(int line, string type, string prop) 
+       {
+               //GLib.debug("set prop %s (%s) to line %d", prop, type, line);
                if (this.line_map.has_key(line)) {
                        if  (this.line_map.get(line) != "e:"  ) {
                                return;
@@ -149,7 +174,42 @@ public class JsRender.Node : Object {
                        this.lines.add(line);
                }
                this.line_map.set(line, type + ":" + prop);
-               GLib.debug("setLine %d, %s", line, type + ":" + prop);
+               if (type == "e" || type == "p" ) {
+               
+                       if (prop == "" || !this.props.has_key(prop)) {
+                               ///GLib.debug("cant find prop '%s'", prop);
+                               return;
+                       }
+                       
+                       var prope = this.props.get(prop);
+                       if (prope != null && type =="p") { 
+                               prope.start_line = line;
+                       }
+                       if (prope != null && type =="e") { 
+                               prope.end_line = line;
+                       }       
+                       
+               }
+               if (type == "l" || type =="x") {
+                       if (prop == "" || !this.listeners.has_key(prop)) {
+                               //GLib.debug("cant find listener '%s'", prop);
+                               return;
+                       }
+                       
+                       var prope = this.listeners.get(prop);
+                       if (prope != null && type =="l") { 
+                               prope.start_line = line;
+                       }
+                       if (prope != null && type =="x") { 
+                               prope.end_line = line;
+                       }       
+                       
+               
+               }
+               
+               
+               
+               //GLib.debug("setLine %d, %s", line, type + ":" + prop);
        }
        public void sortLines() {
                //print("sortLines\n");
@@ -205,45 +265,30 @@ public class JsRender.Node : Object {
                return null;
                
        }
-       public string lineToProp(int line)
+       
+       
+       public NodeProp? lineToProp(int line)
        {
-               // assume lineToNode called first...
-               var l = -1;
-               //foreach(int el in this.lines) {
-               //      //print("all lines %d\n", el);
-               //
-               
                
-               foreach(int el in this.lines) {
-                       //print("?match %d\n", el);
-                       if (el < line) {
-                               
-                               l = el;
-                               //print("LESS\n");
+               for(var i= 0; i < this.propstore.get_n_items();i++) {
+                       var p = (NodeProp) this.propstore.get_item(i);
+                       GLib.debug("prop %s lines %d -> %d", p.name, p.start_line, p.end_line);
+                       if (p.start_line > line) {
                                continue;
                        }
-                       if (el == line) {
-                               //print("SAME\n");
-                               l = el;
-                               break;
-                       }
-                       if (l > -1) {
-                               //print("RETURNING NODE ON LINE %d", l);
-                               return this.line_map.get(l);
+                       if (line > p.end_line) {
+                               continue;
                        }
-                       return null;
-                       
-               }
-               if (l > -1) {
-                       //print("RETURNING NODE ON LINE %d", l);
-                       return this.line_map.get(l);
+                       return p;
                }
                return null;
-       
        }
+               
+                
        
        public bool getPropertyRange(string prop, out int start, out int end)
        {
+               end = 0;
                start = -1;
                foreach(int el in this.lines) {
                        if (start < 0) {
@@ -277,10 +322,9 @@ public class JsRender.Node : Object {
        public string uid()
        {
                if (this.props.get("id") == null) {
-                       uid_count++;
-                       return "uid-%d".printf(uid_count);
+                       return "uid-%d".printf(this.oid);
                }
-               return this.props.get("id");
+               return this.props.get("id").val;
        }
        
        
@@ -290,145 +334,80 @@ public class JsRender.Node : Object {
        }
        public bool hasXnsType()
        {
-               if (this.props.get("xns") != null && this.props.get("xtype") != null) {
+               if (this.props.get("xns") != null && this.props.get("xtype") != null) {
                        return true;
                        
                }
                return false;
        }
+       
+       public string FQN { // for sorting
+               owned get { return this.fqn(); }
+               private set  {}
+       }
+       
        public string fqn()
        {
                if (!this.hasXnsType ()) {
                        return "";
                }
-               return this.props.get("$ xns") + "." + this.props.get("xtype")
+               return this.props.get("xns").val + "." + this.props.get("xtype").val
 
        }
        public void setFqn(string name)
        {
                var ar = name.split(".");
-               this.props.set("xtype", ar[ar.length-1]);
                var l = name.length - (ar[ar.length-1].length +1);
-               this.props.set("$ xns", name.substring(0, l));
+               
+
+               
+               if (this.props.has_key("xtype")) {
+                       this.props.get("xtype").val = ar[ar.length-1];
+               } else {
+                       this.add_prop(new NodeProp.prop("xtype", "",  ar[ar.length-1]));                
+               }       
+               if (this.props.has_key("xns")) {
+                       this.props.get("xns").val = name.substring(0, l);
+               } else {
+                       this.add_prop(new NodeProp.raw("xns", "", name.substring(0, l)));               
+               }       
+               
+               
                //print("setFQN %s to %s\n", name , this.fqn());
                               
 
        }
        // wrapper around get props that returns empty string if not found.
-       public string get(string key)
+       //overrides Glib.object.get (hence new)
+       public new string get(string key)
        {
-               var k = this.props.get(key);
-               if (k != null) {
-                       return k;
-               }
-               
-               k = this.props.get("$ " + key);
-               if (k != null) {
-                       return k;
-               }
-               
-               var iter = this.props.map_iterator();
-               while (iter.next()) {
-                       var kk = iter.get_key().split(" ");
-                       if (kk[kk.length-1] == key) {
-                               return iter.get_value();
-                       }
-               }
                
-               
-               return "";
-               
-       }
-       
-       public string get_key(string key)
+               var v = this.props.get(key);
+               return v == null ? "" : v.val;
+       }       
+                
+       public  NodeProp? get_prop(string key)
        {
-               var k = this.props.get(key);
-               if (k != null) {
-                       return key;
-               }
-               
-               k = this.props.get("$ " + key);
-               if (k != null) {
-                       return "$ " + key;
-               }
                
-               var iter = this.props.map_iterator();
-               while (iter.next()) {
-                       var kk = iter.get_key().split(" ");
-                       if (kk[kk.length-1] == key) {
-                               return iter.get_key();
-                       }
-               }
-               
-               
-               return "";
-               
-       }
-       public void normalize_key(string key, out string kname, out string kflag, out string ktype)
-       {
-               // key formats : XXXX
-               // XXX - plain
-               // string XXX - with type
-               // $ XXX - with flag (no type)
-               // $ string XXX - with flag
-               kname = "";
-               ktype = ""; // these used to contain '-' ???
-               kflag = ""; // these used to contain '-' ???
-               var kkv = key.strip().split(" ");
-               string[] kk = {};
-               for (var i = 0; i < kkv.length; i++) {
-                       if (kkv[i].length > 0 ) {
-                               kk+= kkv[i];
-                       }
-               }
-               //print("normalize %s => %s\n", key,string.joinv("=:=",kk));
+               return this.props.get(key);
                
-               switch(kk.length) {
-                       case 1: 
-                               kname = kk[0];
-                               return;
-                       case 2: 
-                               kname = kk[1];
-                               if (kk[0].length > 1) {
-                                       ktype = kk[0];
-                               } else {
-                                       kflag = kk[0];
-                               }
-                               return;
-                       case 3:
-                               kname = kk[2];
-                               kflag = kk[0];
-                               ktype = kk[1];
-                               return;
-               }
-               // everything blank otherwise...
        }
-       public void set(string key, string value) {
-               this.props.set(key,value);
-       }
-        public bool has(string key)
+       
+       
+
+
+       public bool has(string key)
        {
-               var k = this.props.get(key);
-               if (k != null) {
-                       return true;
-               }
-               var iter = this.props.map_iterator();
-               while (iter.next()) {
-                       var kk = iter.get_key().strip().split(" ");
-                       if (kk[kk.length-1] == key) {
-                               return true;
-                       }
-               }
-               
-               return false;
-               
+               return this.props.has_key(key);
+                
+        
        }
 
        public void  remove()
        {
                if (this.parent == null) {
-                       
-                       
+                       GLib.debug("remove - parent is null?");
                        return;
                }
                var nlist = new Gee.ArrayList<Node>();
@@ -438,6 +417,11 @@ public class JsRender.Node : Object {
                        }
                        nlist.add(this.parent.items.get(i));
                }
+               uint pos;
+               if ( this.parent.childstore.find(this, out pos)) {
+                       this.parent.childstore.remove(pos);
+               } 
+               
                this.parent.items = nlist;
                this.parent = null;
 
@@ -447,7 +431,7 @@ public class JsRender.Node : Object {
        public Node? findProp(string n) {
                for(var i=0;i< this.items.size;i++) {
                        var p = this.items.get(i).get("* prop");
-                       if (this.items.get(i).get("* prop").length < 1) {
+                       if (p  == null) {
                                continue;
                        }
                        if (p == n) {
@@ -475,7 +459,25 @@ public class JsRender.Node : Object {
                return  Node.gen.to_data (null);   
        }
 
+       public void loadFromJsonString(string str, int ver)
+       {
+               var pa = new Json.Parser();
+               try {
+                       pa.load_from_data(str);
+               } catch (GLib.Error e) {
+                       GLib.debug("Error loading string?");
+                       return;
+               }
+               var new_node = pa.get_root();
+               var obj = new_node.get_object ();
+                    
+               this.loadFromJson(obj, ver);
+       }
+       
+
        public void loadFromJson(Json.Object obj, int version) {
+                
                obj.foreach_member((o , key, value) => {
                        //print(key+"\n");
                        if (key == "items") {
@@ -485,34 +487,64 @@ public class JsRender.Node : Object {
                                        node.parent = this;
                                        node.loadFromJson(el.get_object(), version);
                                        this.items.add(node);
+                                       this.childstore.append(node);
                                });
                                return;
                        }
                        if (key == "listeners") {
                                var li = value.get_object();
                                li.foreach_member((lio , li_key, li_value) => {
-                                       this.listeners.set(li_key, li_value.get_string());
-
+                                       this.add_prop(new NodeProp.listener(li_key, this.jsonNodeAsString(li_value)));
+                                       //this.listeners.set(li_key,  new NodeProp.listener(li_key, this.jsonNodeAsString(li_value)));
                                });
                                return;
                        }
-                       var v = value.get_value();
-                       var sv =  Value (typeof (string));
-                       v.transform(ref sv);
+                       
 
                        var rkey = key;
+                       var sval = this.jsonNodeAsString(value);
+               
                        if (version == 1) {
-                               rkey = this.upgradeKey(key, (string)sv);
+                               rkey = this.upgradeKey(key, sval);
                        }
+                       var n =  new NodeProp.from_json(rkey, sval);
+                               
+                       this.add_prop(n );
+
 
-                       
-                       this.props.set(rkey,  (string)sv);
                });
                
+               
+               
+
 
 
+       }
+       
+       // converts the array into a string with line breaks.
+       public string jsonNodeAsString(Json.Node node)
+       {
+               
+               if (node.get_node_type() == Json.NodeType.ARRAY) {
+                       var  buffer = new GLib.StringBuilder();
+                       var ar = node.get_array();
+                       for (var i = 0; i < ar.get_length(); i++) {
+                               if (i >0 ) {
+                                       buffer.append_c('\n');
+                               }
+                               buffer.append(ar.get_string_element(i));
+                       }
+                       return buffer.str;
+               }
+       // hopeflyu only type value..           
+               var sv =  Value (typeof (string));                      
+               var v = node.get_value();
+               v.transform(ref sv);
+               return (string)sv;
 
        }
+       
+       // really old files...
 
        public string upgradeKey(string key, string val)
        {
@@ -585,6 +617,20 @@ public class JsRender.Node : Object {
                return  Node.gen.to_data (null);   
        }
        
+       public void jsonObjectAddStringValue(Json.Object obj, string key, string v)
+       {
+               if (v.index_of_char('\n',0) < 0) {
+                       obj.set_string_member(key,v);
+                       return;
+               }
+               var aro = new Json.Array();
+               var ar = v.split("\n");
+               for(var i =0;i < ar.length;i++) {
+                       aro.add_string_element(ar[i]);
+               }
+               obj.set_array_member(key,aro);
+       }
+       
        public Json.Object toJsonObject()
        {
                var ret = new Json.Object();
@@ -595,14 +641,14 @@ public class JsRender.Node : Object {
                        ret.set_object_member("listeners", li);
                        var liter = this.listeners.map_iterator();
                        while (liter.next()) {
-                               li.set_string_member(liter.get_key(), liter.get_value());
+                               this.jsonObjectAddStringValue(li, liter.get_value().to_json_key(), liter.get_value().val);
                        }
                }
                //props
                if (this.props.size > 0 ) {
                        var iter = this.props.map_iterator();
                        while (iter.next()) {
-                               this.jsonObjectsetMember(ret, iter.get_key(), iter.get_value());
+                               this.jsonObjectsetMember(ret, iter.get_value().to_json_key(), iter.get_value().val);
                        }
                }
                if (this.items.size > 0) {
@@ -638,52 +684,110 @@ public class JsRender.Node : Object {
                        return;
                }
                ///print( "ADD " + key + "=" + val + " as a string?\n");
-               o.set_string_member(key,val);
+               this.jsonObjectAddStringValue(o,key,val);
+               //o.set_string_member(key,val);
                
        }
+       
+       
+       public string nodeTipProp { 
+               set {
+                       // NOOp ??? should 
+               }
+               owned get {
+                        return  this.nodeTip();
+               } 
+       }
+       // fixme this needs to better handle 'user defined types etc..
        public string nodeTip()
        {
                var ret = this.nodeTitle(true);
+               var spec = "";
                var funcs = "";
                var props = "";
                var listen = "";
-               var iter = this.props.map_iterator();
-               while (iter.next()) {
-                       var i =  iter.get_key().strip();
-                       var val = iter.get_value().strip();
-                       if (val == null || val.length < 1) {
-                               continue;
-                       }
-                       if ( i[0] != '|') {
-                               props += "\n\t<b>" + 
-                                       GLib.Markup.escape_text(i) +"</b> : " + 
-                                       GLib.Markup.escape_text(val.split("\n")[0]);
-                                
-                               continue;
-                       }
+               var uprops = "";
+               // sort?
+               
+               var keys = new  Gee.ArrayList<string>();
+               foreach(var k in this.props.keys) {
+                       keys.add(k);
+               }
+               keys.sort((a,b) => {
+                        return Posix.strcmp(a, b);
+               
+               });
                
-                       //if (i == "* init") { 
-                       //      continue;
-                       //}
+               
+               foreach(var pk in keys) {
+                        
+                       var prop = this.props.get(pk);
+                       var i = prop.name.strip();
+                       
+                       var val = prop.val;
+                       val = val == null ? "" : val;
+                       
+                       switch(prop.ptype) {
+                               case PROP: 
+                               case RAW: // should they be the same?
+                                
+                                       props += "\n\t" + GLib.Markup.escape_text(prop.rtype) +
+                                               " <b>" + GLib.Markup.escape_text(i) +"</b> : " + 
+                                               GLib.Markup.escape_text(val == "" ? "" : val.split("\n")[0]);
+                                               
+                                       break;
+                                       
+                       
+                               
+                               case METHOD :
+                                        
+                                       funcs += "\n\t" + GLib.Markup.escape_text(prop.rtype) +
+                                               " <b>" + GLib.Markup.escape_text(i) +"</b> : "  +
+                                               GLib.Markup.escape_text(val == "" ? "" : val.split("\n")[0]);
+                                       break;
+                                       
+                                
+                               case USER : // user defined.
+                                        
+                                       uprops += "\n\t<b>" + 
+                                               GLib.Markup.escape_text(i) +"</b> : " + 
+                                               GLib.Markup.escape_text(val == "" ? "" : val.split("\n")[0]);
+                                       break;
+                                       
+                               case SPECIAL : // * prop| args | ctor | init
+                                        
+                                               
+                                       spec += "\n\t<b>" + 
+                                               GLib.Markup.escape_text(i) +"</b> : " + 
+                                               GLib.Markup.escape_text(val == "" ? "" : val.split("\n")[0]);
+                                       break;
+                                       
+                               case LISTENER : return  "";  // always raw...
+                               // not used
+                               default:
+                                       break;;
                        
-                       if (Regex.match_simple("^\\s*function", val)) { 
-                               funcs += "\n\t<b>" + 
-                                       GLib.Markup.escape_text(i.substring(1)).strip() +"</b> : " + 
-                                       GLib.Markup.escape_text(val.split("\n")[0]);
-                               continue;
-                       }
-                       if (Regex.match_simple("^\\s*\\(", val)) {
-                               funcs += "\n\t<b>" + GLib.Markup.escape_text(i.substring(1)).strip() +
-                                       "</b> : " + 
-                                       GLib.Markup.escape_text(val.split("\n")[0]);
-                               continue;
                        }
+                        
                        
                }
-               iter = this.listeners.map_iterator();
-               while (iter.next()) {
-                       var i =  iter.get_key().strip();
-                       var val = iter.get_value().strip();
+               
+               keys = new  Gee.ArrayList<string>();
+               foreach(var k in this.listeners.keys) {
+                       keys.add(k);
+               }
+               keys.sort((a,b) => {
+                        return Posix.strcmp(a, b);
+               
+               });
+               
+               foreach(var pk in keys) {
+                        
+                       var prop = this.listeners.get(pk);
+                       var i =  prop.name.strip();
+                       
+                       var val = prop.val.strip();
                        if (val == null || val.length < 1) {
                                continue;
                        }
@@ -696,17 +800,42 @@ public class JsRender.Node : Object {
                
                if (props.length > 0) {
                        ret+="\n\nProperties:" + props;
+               }
+               if (uprops.length > 0) {
+                       ret+="\n\nUser defined Properties:" + uprops;
                } 
+               
+               
                if (funcs.length > 0) {
                        ret+="\n\nMethods:" + funcs;
                } 
                if (listen.length > 0) {
                        ret+="\n\nListeners:" + listen;
                } 
+               if (spec.length > 0) {
+                       ret+="\n\nSpecial:" + spec;
+               } 
+               
                return ret;
 
        }
-       public string nodeTitle(bool for_tip = false) {
+       
+       public string nodeTitleProp { 
+               set {
+                       // NOOp ??? should 
+               }
+               owned get {
+                        return  this.nodeTitle();
+               } 
+       }
+       
+       
+       
+       
+       
+       
+       public string nodeTitle(bool for_tip = false) 
+       {
                string[] txt = {};
 
                //var sr = (typeof(c['+buildershow']) != 'undefined') &&  !c['+buildershow'] ? true : false;
@@ -767,5 +896,196 @@ public class JsRender.Node : Object {
                //if (sr) txt.push('</s>');
                return (txt.length == 0) ? "Element" : string.joinv(" ", txt);
        }
+       // used by trees to display icons?
+       // needs more thought?!?
+       public string iconFilename { 
+               set {
+                       // NOOp ??? should 
+               }
+               owned get {
+                       var clsname = this.fqn();
+    
+                       var clsb = clsname.split(".");
+                   var sub = clsb.length > 1 ? clsb[1].down()  : "";
+                       var fn = "/usr/share/glade/pixmaps/hicolor/16x16/actions/widget-gtk-" + sub + ".png";
+                       //if (FileUtils.test (fn, FileTest.IS_REGULAR)) {
+                               return fn;
+                       //}
+                       //return "/dev/null"; //???
+               } 
+       }
+       
+        
+       
+       public void insertAfter(Node child, Node after) 
+       {
+               this.insertChild(this.items.index_of(after) + 1, child);
+       }
+       public void insertBefore(Node child, Node before)       
+       {
+               this.insertChild(this.items.index_of(before), child);
+       }
+       
+       public void insertChild(int pos, Node child)
+       {
+               this.items.insert(pos, child);
+               this.childstore.insert(pos, child);
+               child.parent = this;
+       }
+       public void appendChild(Node child)
+       {
+               this.items.add( child);
+               this.childstore.append(child);
+               child.parent = this;
+       }
+       
+       
+       /**
+       
+       properties
+               previous we had listeners / and props
+               
+               we really need to store this as flat array - keep it simple!?
+               
+               getValue(key)
+               update(key, value)
+               
+               
+       
+       */
+       
+
+       
+       
+       public void loadProps(GLib.ListStore model) 
+       {
+       
+               // fixme sorting?? - no need to loop twice .. just use sorting.!
+               var oldstore = this.propstore;
+               this.propstore = model;
+               for(var i =  0; i < oldstore.n_items; i++ ) {
+                       var it = (NodeProp) oldstore.get_item(i);
+                   model.append(it);
+                       
+               }
+               this.sortProps();
+          
+   }
+   // used to replace propstore, so it does not get wiped by editing a node
+   public void dupeProps()
+   {
+               GLib.debug("dupeProps START");
+               var oldstore = this.propstore;
+               this.propstore = new GLib.ListStore(typeof(NodeProp));;
+               for(var i =  0; i < oldstore.n_items; i++ ) {
+                       var it = (NodeProp) oldstore.get_item(i);
+                       this.propstore.append(it);
+               }
+               GLib.debug("dupeProps END");
+       }
+       
+   
+   public void remove_prop(NodeProp prop)
+       {
+               uint pos;
+               if (!this.propstore.find(prop, out pos)) {
+                       return;
+               }
+               this.propstore.remove(pos);
+               this.updated_count++;
+               
+       }   
+   
+       public bool has_prop_key(NodeProp prop) 
+       {
+               for(var i =  0; i < this.propstore.n_items; i++ ) {
+                       var it = (NodeProp) this.propstore.get_item(i);
+                       if (it.ptype == prop.ptype && it.to_index_key() == prop.to_index_key()) {
+                               return true;
+                       }
+                       
+               }
+               return false;
+          
+       }
+       
+        
+       
+       
+       public void add_prop(NodeProp prop)
+       {
+               if (this.has_prop_key(prop) && !prop.to_index_key().has_suffix("[]")) {
+                       GLib.warning("duplicate key' %s'- can not add - call has_prop_key first", prop.to_index_key());
+                       return;
+               }
+               prop.parent = this;
+               this.propstore.append(prop);
+               this.sortProps();
+               
+               this.updated_count++;
+               
+               
+       }
+       
+       int props_updated_count = -1;
+       Gee.HashMap<string,NodeProp> props_cache;
+       
+       public Gee.HashMap<string,NodeProp> props {
+               owned get {
+                       if (this.updated_count == this.props_updated_count) {
+                               return this.props_cache;
+                       }
+                        this.props_cache = new Gee.HashMap<string,NodeProp>(); // the properties..
 
+                       for(var i =  0; i < this.propstore.n_items; i++ ) {
+                               var it = (NodeProp) this.propstore.get_item(i);
+                               if (it.ptype != NodePropType.LISTENER) {
+                               //      GLib.debug("props add key %s", it.to_index_key());
+                                       this.props_cache.set( it.to_index_key() , it);
+                               }
+                       }
+                       this.props_updated_count = this.updated_count;
+                       return this.props_cache;
+               }
+               private set {
+                       GLib.error("do not set listerners direclty");
+               }
+       }
+       
+       int listeners_updated_count = -1;
+       Gee.HashMap<string,NodeProp> listeners_cache;
+       
+       //private Gee.HashMap<string,NodeProp> _listeners; // the listeners..
+       public Gee.HashMap<string,NodeProp> listeners {
+               owned get {
+                       if (this.updated_count == this.listeners_updated_count) {
+                               return this.listeners_cache;
+                       }
+                       
+                       this.listeners_cache = new Gee.HashMap<string,NodeProp>(); // the properties..
+
+                       for(var i =  0; i < this.propstore.n_items; i++ ) {
+                               var it = (NodeProp) this.propstore.get_item(i);
+                               if (it.ptype == NodePropType.LISTENER) {
+                                       this.listeners_cache.set( it.to_index_key() , it);
+                               }
+                       }
+                       this.listeners_updated_count = this.updated_count;
+                       return this.listeners_cache;;
+               }
+               private set {
+                       GLib.error("do not set listerners direclty");
+               }
+       }
+       private void sortProps ()
+       {
+       
+               this.propstore.sort( (a, b) => {
+
+                       return Posix.strcmp( ((NodeProp)a).to_sort_key(),  ((NodeProp)b).to_sort_key());
+                       
+               });
+        
+       
+       }
 }