src/JsRender/Node.vala
[app.Builder.js] / src / JsRender / Node.vala
1
2 // test..
3 // valac gitlive/app.Builder.js/JsRender/Lang.vala gitlive/app.Builder.js/JsRender/Node.vala --pkg gee-1.0 --pkg=json-glib-1.0 -o /tmp/Lang ;/tmp/Lang
4
5
6 /*
7  * 
8  * props:
9  * 
10  * key value view of properties.
11  * 
12  * Old standard..
13  * XXXXX : YYYYY  -- standard - should be rendered as XXXX : "YYYY" usually.
14  * |XXXXX : YYYYY  -- standard - should be rendered as XXXX : YYYY usually.
15  * |init  -- the initialization...
16  * *prop : a property which is actually an object definition... 
17  * *args : contructor args
18  * .ctor : Full contruct line...  
19  * 
20  * Newer code
21  * ".Gee.ArrayList<Xcls_fileitem>:fileitems" ==> # type  name 
22  * ".signal:void:open": "(JsRender.JsRender file)" ==> @ type name
23  *  "|void:clearFiles": "() .... some code...."  | type name
24  *
25  * 
26  * 
27  * 
28  * 
29  * Standardize this crap...
30  * 
31  * standard properties (use to set)
32  *          If they are long values show the dialog..
33  * 
34  * bool is_xxx  :: can show a pulldown.. (true/false)
35  * string html  
36  * $ string html  = string with value interpolated eg. baseURL + ".." 
37  *  Clutter.ActorAlign x_align  (typed)  -- shows pulldowns if type is ENUM? 
38  * $ untypedvalue = javascript untyped value... 
39  * 
40  * object properties (not part of the GOjbect being wrapped?
41  * # Gee.ArrayList<Xcls_fileitem> fileitems
42  * 
43  * signals
44  * @ void open 
45  * 
46  * methods -- always text editor..
47  * | void clearFiles
48  * | someJSmethod
49  * 
50  * specials
51  * * prop -- string
52  * * args  -- string
53  * * ctor -- string
54  * * init -- big string?
55  * 
56  * event handlers (listeners)
57  *   just shown 
58  * 
59  * -----------------
60  * special ID values
61  *  +XXXX -- indicates it's a instance property / not glob...
62  *  *XXXX -- skip writing glob property (used as classes that can be created...)
63  *  _XXXX -- (string) a translatable string.
64  * 
65  * 
66  *  FORMATING?
67 .method {
68          color : green;
69          font-weight: bold;      
70 }
71 .prop {
72         color : #333;
73 }
74 .prop-code {
75     font-style: italic;
76  }
77 .listener {
78     color: #600;
79     font-weight: bold;   
80 }
81 .special { 
82   color : #00c;    font-weight: bold;    
83
84
85 */
86
87
88
89
90
91
92 public class JsRender.Node : Object {
93         
94
95         public static int uid_count = 0;
96         
97         public Node parent;
98         public Gee.ArrayList<Node> items; // child items..
99         
100         public Gee.HashMap<string,string> props; // the properties..
101         public Gee.HashMap<string,string> listeners; // the listeners..
102         public string  xvala_cls;
103         public string xvala_xcls; // 'Xcls_' + id;
104         public string xvala_id; // item id or ""
105         public int line_start;
106         public int line_end;
107         public Gee.ArrayList<int> lines;
108         public Gee.HashMap<int,string> line_map; // store of l:xxx or p:....
109         
110
111         public Node()
112         {
113                 this.items = new Gee.ArrayList<Node>();
114                 this.props = new Gee.HashMap<string,string>();
115                 this.listeners = new Gee.HashMap<string,string>();
116                 this.xvala_cls = "";
117                 this.xvala_xcls = "";
118                 this.xvala_id = "";
119                 this.parent = null;
120                 this.line_start = -1;
121                 this.line_end = -1;             
122                 this.lines = new Gee.ArrayList<int>();
123                 this.line_map = new Gee.HashMap<int,string>();
124                 
125         }
126
127         public void setLine(int line, string type, string prop) {
128                 this.lines.add(line);
129                 this.line_map.set(line, type +":" + prop);
130         }
131         public void sortLines() {
132                 this.lines.sort((a,b) => {   
133                         return b-a;
134                         /*if (a == b) {
135                                 return 0;
136                         }
137                         return a < b ? -1 : 1;
138                         */
139                 });
140         }
141         
142         public string uid()
143         {
144                 if (this.props.get("id") == null) {
145                         uid_count++;
146                         return "uid-%d".printf(uid_count);
147                 }
148                 return this.props.get("id");
149         }
150         
151         
152         public bool hasChildren()
153         {
154                 return this.items.size > 0;
155         }
156         public bool hasXnsType()
157         {
158                 if (this.props.get("$ xns") != null && this.props.get("xtype") != null) {
159                         return true;
160                         
161                 }
162                 return false;
163         }
164         public string fqn()
165         {
166                 if (!this.hasXnsType ()) {
167                         return "";
168                 }
169                 return this.props.get("$ xns") + "." + this.props.get("xtype"); 
170
171         }
172         public void setFqn(string name)
173         {
174                 var ar = name.split(".");
175                 this.props.set("xtype", ar[ar.length-1]);
176                 var l = name.length - (ar[ar.length-1].length +1);
177                 this.props.set("$ xns", name.substring(0, l));
178                 print("setFQN %s to %s\n", name , this.fqn());
179                                
180
181         }
182         // wrapper around get props that returns empty string if not found.
183         public string get(string key)
184         {
185                 var k = this.props.get(key);
186                 if (k != null) {
187                         return k;
188                 }
189                 
190                 k = this.props.get("$ " + key);
191                 if (k != null) {
192                         return k;
193                 }
194                 
195                 var iter = this.props.map_iterator();
196                 while (iter.next()) {
197                         var kk = iter.get_key().split(" ");
198                         if (kk[kk.length-1] == key) {
199                                 return iter.get_value();
200                         }
201                 }
202                 
203                 
204                 return "";
205                 
206         }
207         
208         public string get_key(string key)
209         {
210                 var k = this.props.get(key);
211                 if (k != null) {
212                         return key;
213                 }
214                 
215                 k = this.props.get("$ " + key);
216                 if (k != null) {
217                         return "$ " + key;
218                 }
219                 
220                 var iter = this.props.map_iterator();
221                 while (iter.next()) {
222                         var kk = iter.get_key().split(" ");
223                         if (kk[kk.length-1] == key) {
224                                 return iter.get_key();
225                         }
226                 }
227                 
228                 
229                 return "";
230                 
231         }
232         public void normalize_key(string key, out string kname, out string kflag, out string ktype)
233         {
234                 // key formats : XXXX
235                 // XXX - plain
236                 // string XXX - with type
237                 // $ XXX - with flag (no type)
238                 // $ string XXX - with flag
239                 kname = "";
240                 ktype = "-";
241                 kflag = "-";
242                 var kk = key.strip().split(" ");
243                 switch(kk.length) {
244                         case 1: 
245                                 kname = kk[0];
246                                 return;
247                         case 2: 
248                                 kname = kk[1];
249                                 if (kk[0].length > 1) {
250                                         ktype = kk[0];
251                                 } else {
252                                         kflag = kk[0];
253                                 }
254                                 return;
255                         case 3:
256                                 kname = kk[2];
257                                 kflag = kk[0];
258                                 ktype = kk[1];
259                                 return;
260                 }
261                 // everything blank otherwise...
262         }
263         public void set(string key, string value) {
264                 this.props.set(key,value);
265         }
266          public bool has(string key)
267         {
268                 var k = this.props.get(key);
269                 if (k != null) {
270                         return true;
271                 }
272                 var iter = this.props.map_iterator();
273                 while (iter.next()) {
274                         var kk = iter.get_key().strip().split(" ");
275                         if (kk[kk.length-1] == key) {
276                                 return true;
277                         }
278                 }
279                 
280                 return false;
281                 
282         }
283
284         public void  remove()
285         {
286                 if (this.parent == null) {
287                         
288                         
289                         return;
290                 }
291                 var nlist = new Gee.ArrayList<Node>();
292                 for (var i =0;i < this.parent.items.size; i++) {
293                         if (this.parent.items.get(i) == this) {
294                                 continue;
295                         }
296                         nlist.add(this.parent.items.get(i));
297                 }
298                 this.parent.items = nlist;
299                 this.parent = null;
300
301         }
302          
303         /* creates javascript based on the rules */
304         public Node? findProp(string n) {
305                 for(var i=0;i< this.items.size;i++) {
306                         var p = this.items.get(i).get("* prop");
307                         if (this.items.get(i).get("* prop").length < 1) {
308                                 continue;
309                         }
310                         if (p == n) {
311                                 return this.items.get(i);
312                         }
313                 }
314                 return null;
315
316         }
317
318         
319         
320          
321         static Json.Generator gen = null;
322         
323         public string quoteString(string str)
324         {
325                 if (Node.gen == null) {
326                         Node.gen = new Json.Generator();
327                 }
328                  var n = new Json.Node(Json.NodeType.VALUE);
329                 n.set_string(str);
330  
331                 Node.gen.set_root (n);
332                 return  Node.gen.to_data (null);   
333         }
334
335         public void loadFromJson(Json.Object obj, int version) {
336                 obj.foreach_member((o , key, value) => {
337                         //print(key+"\n");
338                         if (key == "items") {
339                                 var ar = value.get_array();
340                                 ar.foreach_element( (are, ix, el) => {
341                                         var node = new Node();
342                                         node.parent = this;
343                                         node.loadFromJson(el.get_object(), version);
344                                         this.items.add(node);
345                                 });
346                                 return;
347                         }
348                         if (key == "listeners") {
349                                 var li = value.get_object();
350                                 li.foreach_member((lio , li_key, li_value) => {
351                                         this.listeners.set(li_key, li_value.get_string());
352
353                                 });
354                                 return;
355                         }
356                         var v = value.get_value();
357                         var sv =  Value (typeof (string));
358                         v.transform(ref sv);
359
360                         var rkey = key;
361                         if (version == 1) {
362                                 rkey = this.upgradeKey(key, (string)sv);
363                         }
364
365                         
366                         this.props.set(rkey,  (string)sv);
367                 });
368                 
369
370
371
372         }
373
374         public string upgradeKey(string key, string val)
375         {
376                 // convert V1 to V2
377                 if (key.length < 1) {
378                         return key;
379                 }
380                 switch(key) {
381                         case "*prop":
382                         case "*args":
383                         case ".ctor":
384                         case "|init":
385                                 return "* " + key.substring(1);
386                                 
387                         case "pack":
388                                 return "* " + key;
389                 }
390                 if (key[0] == '.') { // v2 does not start with '.' ?
391                         var bits = key.substring(1).split(":");
392                         if (bits[0] == "signal") {
393                                 return "@" + string.joinv(" ", bits).substring(bits[0].length);
394                         }
395                         return "# " + string.joinv(" ", bits);                  
396                 }
397                 if (key[0] != '|' || key[1] == ' ') { // might be a v2 file..
398                         return key;
399                 }
400                 var bits = key.substring(1).split(":");
401                 // two types '$' or '|' << for methods..
402                 // javascript 
403                 if  (Regex.match_simple ("^function\\s*(", val.strip())) {
404                         return "| " + key.substring(1);
405                 }
406                 // vala function..
407                 
408                 if  (Regex.match_simple ("^\\(", val.strip())) {
409                 
410                         return "| " + string.joinv(" ", bits);
411                 }
412                 
413                 // guessing it's a property..
414                 return "$ " + string.joinv(" ", bits);
415                 
416                 
417
418         }
419
420
421
422
423
424         
425         public Node  deepClone()
426         {
427                 var n = new Node();
428                 n.loadFromJson(this.toJsonObject(), 2);
429                 return n;
430
431         }
432         public string toJsonString()
433         {
434                 if (Node.gen == null) {
435                         Node.gen = new Json.Generator();
436                         gen.pretty =  true;
437                         gen.indent = 1;
438                 }
439                 var n = new Json.Node(Json.NodeType.OBJECT);
440                 n.set_object(this.toJsonObject () );
441                 Node.gen.set_root (n);
442                 return  Node.gen.to_data (null);   
443         }
444         
445         public Json.Object toJsonObject()
446         {
447                 var ret = new Json.Object();
448
449                 // listeners...
450                 if (this.listeners.size > 0) {
451                         var li = new Json.Object();
452                         ret.set_object_member("listeners", li);
453                         var liter = this.listeners.map_iterator();
454                         while (liter.next()) {
455                                 li.set_string_member(liter.get_key(), liter.get_value());
456                         }
457                 }
458                 //props
459                 if (this.props.size > 0 ) {
460                         var iter = this.props.map_iterator();
461                         while (iter.next()) {
462                                 this.jsonObjectsetMember(ret, iter.get_key(), iter.get_value());
463                         }
464                 }
465                 if (this.items.size > 0) {
466                         var ar = new Json.Array();
467                         ret.set_array_member("items", ar);
468                 
469                         // children..
470                         for(var i =0;i < this.items.size;i++) {
471                                 ar.add_object_element(this.items.get(i).toJsonObject());
472                         }
473                 }
474                 return ret;
475                 
476  
477         }
478          
479         public void jsonObjectsetMember(Json.Object o, string key, string val) {
480                 if (Lang.isBoolean(val)) {
481                         o.set_boolean_member(key, val.down() == "false" ? false : true);
482                         return;
483                 }
484                 
485                 
486                 if (Lang.isNumber(val)) {
487                         if (val.contains(".")) {
488                                 //print( "ADD " + key + "=" + val + " as a double?\n");
489                                 o.set_double_member(key, double.parse (val));
490                                 return;
491
492                         }
493                         //print( "ADD " + key + "=" + val + " as a int?\n")  ;
494                         o.set_int_member(key,long.parse(val));
495                         return;
496                 }
497                 ///print( "ADD " + key + "=" + val + " as a string?\n");
498                 o.set_string_member(key,val);
499                 
500         }
501         public string nodeTip()
502         {
503                 var ret = this.nodeTitle(true);
504                 var funcs = "";
505                 var props = "";
506                 var listen = "";
507                 var iter = this.props.map_iterator();
508                 while (iter.next()) {
509                         var i =  iter.get_key().strip();
510                         var val = iter.get_value().strip();
511                         if (val == null || val.length < 1) {
512                                 continue;
513                         }
514                         if ( i[0] != '|') {
515                                 props += "\n\t<b>" + 
516                                         GLib.Markup.escape_text(i) +"</b> : " + 
517                                         GLib.Markup.escape_text(val.split("\n")[0]);
518                                  
519                                 continue;
520                         }
521                 
522                         //if (i == "* init") { 
523                         //      continue;
524                         //}
525                         
526                         if (Regex.match_simple("^\\s*function", val)) { 
527                                 funcs += "\n\t<b>" + 
528                                         GLib.Markup.escape_text(i.substring(1)).strip() +"</b> : " + 
529                                         GLib.Markup.escape_text(val.split("\n")[0]);
530                                 continue;
531                         }
532                         if (Regex.match_simple("^\\s*\\(", val)) {
533                                 funcs += "\n\t<b>" + GLib.Markup.escape_text(i.substring(1)).strip() +
534                                         "</b> : " + 
535                                         GLib.Markup.escape_text(val.split("\n")[0]);
536                                 continue;
537                         }
538                         
539                 }
540                 iter = this.listeners.map_iterator();
541                 while (iter.next()) {
542                         var i =  iter.get_key().strip();
543                         var val = iter.get_value().strip();
544                         if (val == null || val.length < 1) {
545                                 continue;
546                         }
547                          listen += "\n\t<b>" + 
548                                         GLib.Markup.escape_text(i) +"</b> : " + 
549                                         GLib.Markup.escape_text(val.split("\n")[0]);
550                         
551                 }
552                 
553                 
554                 if (props.length > 0) {
555                         ret+="\n\nProperties:" + props;
556                 } 
557                 if (funcs.length > 0) {
558                         ret+="\n\nMethods:" + funcs;
559                 } 
560                 if (listen.length > 0) {
561                         ret+="\n\nListeners:" + listen;
562                 } 
563                 return ret;
564
565         }
566         public string nodeTitle(bool for_tip = false) {
567                 string[] txt = {};
568
569                 //var sr = (typeof(c['+buildershow']) != 'undefined') &&  !c['+buildershow'] ? true : false;
570                 //if (sr) txt.push('<s>');
571
572                 if (this.has("* prop"))   { txt += (GLib.Markup.escape_text(this.get("* prop")) + ":"); }
573                 
574                 //if (renderfull && c['|xns']) {
575                 var fqn = this.fqn();
576                 var fqn_ar = fqn.split(".");
577                 txt += for_tip || fqn.length < 1 ? fqn : fqn_ar[fqn_ar.length -1];
578                         
579                 //}
580                 
581                 //if (c.xtype)    { txt.push(c.xtype); }
582                         
583                 if (this.has("id"))      { txt += ("<b>[id=" + GLib.Markup.escape_text(this.get("id")) + "]</b>"); }
584                 if (this.has("fieldLabel")){ txt += ("[" + GLib.Markup.escape_text(this.get("fieldLabel")) + "]"); }
585                 if (this.has("boxLabel"))  { txt += ("[" + GLib.Markup.escape_text(this.get("boxLabel"))+ "]"); }
586                 
587                 
588                 if (this.has("layout")) { txt += ("<i>" + GLib.Markup.escape_text(this.get("layout")) + "</i>"); }
589                 if (this.has("title"))   { txt += ("<b>" + GLib.Markup.escape_text(this.get("title")) + "</b>"); }
590                 if (this.has("html") && this.get("html").length > 0)     { 
591                         var ht = this.get("html").split("\n");
592                         if (ht.length > 1) {
593                                 txt += ("<b>" + GLib.Markup.escape_text(ht[0]) + "...</b>");
594                         } else { 
595                                 txt += ("<b>" + GLib.Markup.escape_text(this.get("html")) + "</b>");
596                         }
597                 }
598                 if (this.has("label"))   { txt += ("<b>" + GLib.Markup.escape_text(this.get("label"))+ "</b>"); }
599                 if (this.has("header"))   { txt += ("<b>" + GLib.Markup.escape_text(this.get("header")) + "</b>"); }
600                 if (this.has("legend"))  { txt += ("<b>" + GLib.Markup.escape_text(this.get("legend")) + "</b>"); }
601                 if (this.has("text"))     { txt += ("<b>" + GLib.Markup.escape_text(this.get("text")) + "</b>"); }
602                 if (this.has("name"))     { txt += ("<b>" + GLib.Markup.escape_text(this.get("name"))+ "</b>"); }
603                 if (this.has("region")) { txt += ("<i>(" + GLib.Markup.escape_text(this.get("region")) + ")</i>"); }
604                 if (this.has("dataIndex")){ txt += ("[" + GLib.Markup.escape_text(this.get("dataIndex")) + "]"); }
605                 
606                 // for flat classes...
607                 //if (typeof(c["*class"]"))!= "undefined")  { txt += ("<b>" +  c["*class"]+  "</b>"); }
608                 //if (typeof(c["*extends"]"))!= "undefined")  { txt += (": <i>" +  c["*extends"]+  "</i>"); }
609                 
610                 
611                 //if (sr) txt.push('</s>');
612                 return (txt.length == 0) ? "Element" : string.joinv(" ", txt);
613         }
614
615 }