src/JsRender/NodeToVala.vala
[app.Builder.js] / src / JsRender / NodeToVala.vala
1 /**
2  * 
3  * Code to convert node tree to Javascript...
4  * 
5  * usage : x = (new JsRender.NodeToJs(node)).munge();
6  * 
7  * 
8  * 
9  * 
10  * 
11 */
12
13
14
15
16 public class JsRender.NodeToVala : Object {
17
18         Node node;
19
20         int depth;
21         string inpad;
22         string pad;
23         string ipad;
24         string cls;
25         string xcls;
26         
27         string ret;
28         int line;
29
30         Gee.ArrayList<string> ignoreList;
31         Gee.ArrayList<string> ignoreWrappedList; 
32         Gee.ArrayList<string> myvars;
33         Gee.ArrayList<Node> vitems; // top level items
34         NodeToVala top;
35         JsRender file;
36         
37         /* 
38          * ctor - just initializes things
39          * - wraps a render node 
40          */
41         public NodeToVala( Node node,  int depth, NodeToVala? parent) 
42         {
43
44                 
45                 this.node = node;
46                 this.depth = depth;
47                 this.inpad = string.nfill(depth > 0 ? 4 : 0, ' ');
48                 this.pad = this.inpad + "    ";
49                 this.ipad = this.inpad + "        ";
50                 this.cls = node.xvala_cls;
51                 this.xcls = node.xvala_xcls;
52                 this.ret = "";
53                 this.line = 0;
54                 this.top = top == null ? this : top;
55                 this.ignoreList = new Gee.ArrayList<string>();
56                 this.ignoreWrappedList  = new Gee.ArrayList<string>();
57                 this.myvars = new Gee.ArrayList<string>();
58                 this.vitems = new Gee.ArrayList<Node>();
59                 this.file = null;
60         }
61
62         public int vcnt = 0;
63         string toValaNS(Node item)
64         {
65                 var ns = item.get("xns") ;
66                 if (ns == "GtkSource") {
67                         return "Gtk.Source";
68                 }
69                 return ns + ".";
70         }
71         public void  toValaName(Node item, int depth =0) 
72         {
73                 this.vcnt++;
74
75                 var ns =  this.toValaNS(item) ;
76                 var cls = ns + item.get("xtype");
77                 
78                 
79                 item.xvala_cls = cls;
80                 
81                 
82                 string id = item.get("id").length > 0 ?
83                         item.get("id") :  "%s%d".printf(item.get("xtype"), this.vcnt);
84
85                 
86                 
87                 
88                 if (id[0] == '*' || id[0] == '+') {
89                         item.xvala_xcls = "Xcls_" + id.substring(1);
90                 } else {
91                         item.xvala_xcls = "Xcls_" + id;
92                 }
93                         
94                 
95                 item.xvala_id =  id;
96                 if (depth > 0) {                        
97                         this.vitems.add(item);
98                 } else if (!item.props.has_key("id")) {
99                         // use the file name..
100                         item.xvala_xcls =  this.file.name;
101                         // is id used?
102                         item.xvala_id = this.file.name;
103
104                 }
105                 // loop children..
106                                                                                                                            
107                 if (item.items.size < 1) {
108                         return;
109                 }
110                 for(var i =0;i<item.items.size;i++) {
111                         this.toValaName(item.items.get(i), depth+1);
112                 }
113                                           
114         }
115         /**
116          *  Main entry point to convert a file into a string..
117          */
118         public static string mungeFile(JsRender file) 
119         {
120                 if (file.tree == null) {
121                         return "";
122                 }
123
124                 var n = new NodeToVala(file.tree, 0, null);
125                 n.file = file;
126                 n.vcnt = 0;
127                 
128                 n.toValaName(file.tree);
129                 
130                 
131                 GLib.debug("top cls %s / xlcs %s\n ",file.tree.xvala_cls,file.tree.xvala_cls); 
132                 n.cls = file.tree.xvala_cls;
133                 n.xcls = file.tree.xvala_xcls;
134                 return n.munge();
135                 
136
137         }
138         
139         public string munge ( )
140         {
141                 //return this.mungeToString(this.node);
142
143                 this.ignore("pack");
144                 this.ignore("init");
145                 this.ignore("xns");
146                 this.ignore("xtype");
147                 this.ignore("id");
148                 
149                 this.globalVars();
150                 this.classHeader();
151                 this.addSingleton();
152                 this.addTopProperties();
153                 this.addMyVars();
154                 this.addPlusProperties();
155                 this.addValaCtor();
156                 this.addUnderThis();
157                 this.addWrappedCtor();
158
159                 this.addInitMyVars();
160                 this.addWrappedProperties();
161                 this.addChildren();
162                 this.addInit();
163                 this.addListeners();
164                 this.addEndCtor();
165                 this.addUserMethods();
166                 this.iterChildren();
167                 
168                 return this.ret;
169                  
170                          
171         } 
172         public string mungeChild(  Node cnode)
173         {
174                 var x = new  NodeToVala(cnode,  this.depth+1, this);
175                 return x.munge();
176         }
177
178         public void globalVars()
179         {
180                 if (this.depth > 0) {
181                         return;
182                 }
183                                 // Global Vars..
184                                 //this.ret += this.inpad + "public static " + this.xcls + "  " + this.node.xvala_id+ ";\n\n";
185
186                 
187                 this.ret += this.inpad + "static " + this.xcls + "  _" + this.node.xvala_id+ ";\n\n";
188                                 
189                                 
190         }
191
192         void classHeader()
193         {
194                            
195                         // class header..
196                         // class xxx {   WrappedGtk  el; }
197                         this.ret += inpad + "public class " + this.xcls + " : Object \n" + this.inpad + "{\n";
198                         this.ret +=  this.pad + "public " + this.cls + " el;\n";
199
200                           
201                         this.ret += this.pad + "private " + this.top.xcls + "  _this;\n\n";
202                         
203                         
204                         
205                         // singleton
206         }
207         void addSingleton() 
208         {
209                         if (depth > 0) {
210                         return;
211                 }
212                         this.ret += pad + "public static " + xcls + " singleton()\n" + 
213                                 this.pad + "{\n" +
214                                 this.ipad + "if (_" + this.node.xvala_id  + " == null) {\n" +
215                                 this.ipad + "    _" + this.node.xvala_id + "= new "+ this.xcls + "();\n" + // what about args?
216                         this.ipad + "}\n" +
217                         this.ipad + "return _" + this.node.xvala_id +";\n" + 
218                                 this.pad + "}\n";
219         }
220                         
221
222         void addTopProperties()
223         {
224                 if (this.depth > 0) {
225                         return;
226                 }
227                 // properties - global..??
228
229                 var iter = this.vitems.list_iterator();
230                 while(iter.next()) {
231                         var n = iter.get();
232
233                          
234                                         if (!n.props.has_key("id") || n.xvala_id.length < 0) {
235                                                 continue;
236                                                 
237                                         }
238                                         if (n.xvala_id[0] == '*') {
239                                                 continue;
240                                         }
241                                         if (n.xvala_id[0] == '+') {
242                                                 continue;
243                                         }
244                                         this.ret += this.pad + "public " + n.xvala_xcls + " " + n.xvala_id + ";\n";
245                                 }
246                                 
247         }
248          
249                 void addMyVars()
250         {
251                 this.ret += "\n" + this.ipad + "// my vars (def)\n";
252                         
253
254  
255                 var cls = Palete.Gir.factoryFqn(this.node.fqn());
256                    
257                 if (cls == null) {
258                         return;
259                 }
260           
261                 
262                         // Key = TYPE:name
263                 var iter = this.node.props.map_iterator();
264                 while (iter.next()) {
265                                 var k = iter.get_key();
266                         if (this.shouldIgnore(k)) {
267                                 continue;
268                         }
269                         var vv = k.strip().split(" ");
270                         // user defined method
271                         if (vv[0] == "|") {
272                                 continue;
273                         }
274                         if (vv[0] == "*") {
275                                 continue;
276                         }
277                                 
278                                 if (vv[0] == "@") {
279                                         this.ret += this.pad + "public signal" + k.substring(1)  + " "  + iter.get_value() + ";\n";
280                                 this.ignore(k);
281                                 continue;
282                                 }
283                         var min = (vv[0] == "$" || vv[0] == "#") ? 3 : 2; 
284                         if (vv.length < min) {
285                                 // skip 'old js style properties without a type'
286                                 continue;
287                         }
288                         
289                         var kname = vv[vv.length-1];
290
291                         if (this.shouldIgnore(kname)) {
292                                 continue;
293                         }
294                         
295                         // is it a class property...
296                         if (cls.props.has_key(kname) && vv[0] != "#") {
297                                 continue;
298                         }
299                         
300                         this.myvars.add(k);
301
302                                 
303                         this.ret += this.pad + "public " + 
304                                 (k[0] == '$' || k[0] == '#' ? k.substring(2) : k ) + ";\n";
305                                 
306                         this.ignore(k);
307                         
308                                 
309                 }
310         }
311         
312                         // if id of child is '+' then it's a property of this..
313                 void addPlusProperties()
314         {
315                         if (this.node.items.size < 1) {
316                           return;
317                 }
318                 var iter = this.node.items.list_iterator();
319                 while (iter.next()) {
320                         var ci = iter.get();
321                                         
322                                         if (ci.xvala_id[0] != '+') {
323                                                 continue; // skip generation of children?
324                                                 
325                                         }
326                                         this.ret += this.pad + "public " + ci.xvala_xcls + " " + ci.xvala_id.substring(1) + ";\n";
327                                                            
328                                         
329                                 }
330         }
331
332         void addValaCtor()
333         {
334                         
335                         
336                         // .vala props.. 
337                         
338                         string[] cargs = {};
339                         var cargs_str = "";
340                         // ctor..
341                         this.ret += "\n" + this.pad + "// ctor \n";
342                         if (this.node.has("* args")) {
343                                 // not sure what this is supposed to be ding..
344                         
345                                 cargs_str = ", " + this.node.get("* args");
346                                 //var ar = this.node.get("* args");.split(",");
347                                 //for (var ari =0; ari < ar.length; ari++) {
348                                         //      cargs +=  (ar[ari].trim().split(" ").pop();
349                                           // }
350                                 }
351                 
352                         if (this.depth < 1) {
353                                 this.ret += this.pad + "public " + this.xcls + "(" + 
354                                         cargs_str +")\n" + this.pad + "{\n";
355                         } else {
356                                         
357                                                 //code 
358                                         
359                                 this.ret+= this.pad + "public " + this.xcls + "(" + 
360                                         this.top.xcls + " _owner " + cargs_str + ")\n" + this.pad + "{\n";
361                         }
362                         
363
364         }
365         void addUnderThis() 
366         {
367                         // public static?
368                         if (depth < 1) {
369                         this.ret += this.ipad + "_this = this;\n";
370                         return;
371                 }
372                 this.ret+= this.ipad + "_this = _owner;\n";
373
374                 if (this.node.props.has_key("id")
375                         &&
376                         this.node.xvala_id != "" 
377                         && 
378                         this.node.xvala_id[0] != '*' 
379                         && 
380                         this.node.xvala_id[0] != '+' 
381                         ) {
382                                 this.ret+= this.ipad + "_this." + node.xvala_id  + " = this;\n";
383                    
384                 }
385                                 
386                                 
387    
388         }
389
390         void addWrappedCtor()
391         {
392                 // wrapped ctor..
393                 // this may need to look up properties to fill in the arguments..
394                 // introspection does not workk..... - as things like gtkmessagedialog
395                 /*
396                 if (cls == 'Gtk.Table') {
397
398                 var methods = this.palete.getPropertiesFor(cls, 'methods');
399
400                 print(JSON.stringify(this.palete.proplist[cls], null,4));
401                 Seed.quit();
402                 }
403                 */
404                 if (this.node.has("* ctor")) {
405                         
406                         
407                                 this.ret +=  this.ipad + "this.el = " + this.node.get("* ctor")+ ";\n";
408                         return;
409                 }
410                 // the ctor arguments...
411
412                 // see what the 
413                 //var default_ctor = Palete.Gir.factoryFqn(this.node.fqn() + ".newv");
414                 //if (default_ctor == null) {
415                         var  default_ctor = Palete.Gir.factoryFqn(this.node.fqn() + ".new");
416
417                 //}
418                 if (default_ctor != null && default_ctor.paramset != null && default_ctor.paramset.params.size > 0) {
419                         string[] args  = {};
420                         var iter =default_ctor.paramset.params.list_iterator();
421                         while (iter.next()) {
422                                 var n = iter.get().name;
423                                 if (!this.node.has(n)) {
424
425                                         if (iter.get().type.contains("int")) {
426                                                 args += "0";
427                                                 continue;
428                                         }
429                                         if (iter.get().type.contains("float")) {
430                                                 args += "0f";
431                                                 continue;
432                                         }
433                                         if (iter.get().type.contains("bool")) {
434                                                 args += "true"; // always default to true?
435                                                 continue;
436                                         }
437                                         // any other types???
438                                         
439                                         args += "null";
440                                         continue;
441                                 }
442                                 this.ignoreWrapped(n);
443                                 this.ignore(n);
444                                 
445                                 var v = this.node.get(n);
446
447                                 if (iter.get().type == "string") {
448                                         v = "\"" +  v.escape("") + "\"";
449                                 }
450                                 if (v == "TRUE" || v == "FALSE") {
451                                         v = v.down();
452                                 }
453
454                                 
455                                 args += v;
456
457                         }
458                         this.ret += this.ipad + "this.el = new " + cls + "( "+ string.joinv(", ",args) + " );\n" ;
459                         return;
460                         
461                 }
462                 
463                 
464                                 this.ret += this.ipad + "this.el = new " + this.cls + "();\n";
465
466                         
467         }
468
469         void addInitMyVars()
470         {
471                         //var meths = this.palete.getPropertiesFor(item['|xns'] + '.' + item.xtype, 'methods');
472                         //print(JSON.stringify(meths,null,4));Seed.quit();
473                         
474                         
475                         
476                         // initialize.. my vars..
477                 this.ret += "\n" + this.ipad + "// my vars (dec)\n";
478                 
479                 var iter = this.myvars.list_iterator();
480                 while(iter.next()) {
481                         
482                                 var k = iter.get();
483                         
484                                 var ar  = k.strip().split(" ");
485                         var kname = ar[ar.length-1];
486                         
487                                 var v = this.node.props.get(k);
488                         // ignore signals.. 
489                                 if (v.length < 1) {
490                                                 continue; 
491                                 }
492                         if (v == "FALSE" || v == "TRUE") {
493                                 v = v.down();
494                         }
495 //FIXME -- check for raw string.. "string XXXX"
496                         
497                         // if it's a string...
498                         
499                                 this.ret += this.ipad + "this." + kname + " = " +   v +";\n";
500                         }
501         }
502
503         
504
505
506         
507         void addWrappedProperties()
508         {
509                 var cls = Palete.Gir.factoryFqn(this.node.fqn());
510                 if (cls == null) {
511                         return;
512                 }
513                         // what are the properties of this class???
514                 this.ret += "\n" + this.ipad + "// set gobject values\n";
515
516                 var iter = cls.props.map_iterator();
517                 while (iter.next()) {
518                         var p = iter.get_key();
519                         print("Check Write %s\n", p);
520                         if (!this.node.has(p)) {
521                                 continue;
522                         }
523                         if (this.shouldIgnoreWrapped(p)) {
524                                 continue;
525                         }
526                         
527                                 this.ignore(p);
528                         var v = this.node.get(p);
529
530                         var nodekey = this.node.get_key(p);
531
532                         // user defined properties.
533                         if (nodekey[0] == '#') {
534                                 continue;
535                         }
536                                 
537
538                         
539                         var is_raw = nodekey[0] == '$';
540                         
541                         // what's the type.. - if it's a string.. then we quote it..
542                         if (iter.get_value().type == "string" && !is_raw) {
543                                  v = "\"" +  v.escape("") + "\"";
544                         }
545                         if (v == "TRUE" || v == "FALSE") {
546                                 v = v.down();
547                         }
548                         if (iter.get_value().type == "float" && v[v.length-1] != 'f') {
549                                 v += "f";
550                         }
551                         
552                         
553                         this.ret += "%sthis.el.%s = %s;\n".printf(ipad,p,v); // // %s,  iter.get_value().type);
554                                         
555                            // got a property..
556                            
557
558                 }
559                 
560         }
561
562         void addChildren()
563         {
564                                 //code
565                 if (this.node.items.size < 1) {
566                         return;
567                 }
568                          
569                         var iter = this.node.items.list_iterator();
570                 var i = -1;
571                 while (iter.next()) {
572                         i++;
573                                 
574                                         var ci = iter.get();
575
576                         if (ci.xvala_id[0] == '*') {
577                                                 continue; // skip generation of children?
578                                         }
579                                         
580                                         var xargs = "";
581                                         if (ci.has("* args")) {
582                                                 
583                                                 var ar = ci.get("* args").split(",");
584                                                 for (var ari = 0 ; ari < ar.length; ari++ ) {
585                                         var arg = ar[ari].split(" ");
586                                                                 xargs += "," + arg[arg.length -1];
587                                                 }
588                                         }
589                                         
590                                         this.ret += this.ipad + "var child_" + "%d".printf(i) + " = new " + ci.xvala_xcls +
591                                         "( _this " + xargs + ");\n" ;
592                                         
593                                         this.ret+= this.ipad + "child_" + "%d".printf(i) +".ref();\n"; // we need to reference increase unnamed children...
594                                         
595                                         if (ci.has("* prop")) {
596                                                 this.ret+= ipad + "this.el." + ci.get("* prop") + " = child_" + "%d".printf(i) + ".el;\n";
597                                                 continue;
598                                         }
599
600                         // not sure why we have 'true' in pack?!?
601                                         if (!ci.has("pack") || ci.get("pack").down() == "false" || ci.get("pack").down() == "true") {
602                                                 continue;
603                                         }
604                                         
605                                         string[]  packing =  { "add" };
606                         if (ci.has("pack")) {
607                                 packing = ci.get("pack").split(",");
608                         }
609                                         
610                                         var pack = packing[0];
611                         this.ret += this.ipad + "this.el." + pack.strip() + " (  child_" + "%d".printf(i) + ".el " +
612                                                            (packing.length > 1 ? 
613                                                                 (", " + string.joinv(",", packing).substring(pack.length+1))
614                                                         :
615                                                                         ""
616                                                                 ) + " );\n";
617                         
618                                                           
619                                         if (ci.xvala_id[0] != '+') {
620                                                 continue; // skip generation of children?
621                                                                 
622                                         }
623                                         this.ret+= this.ipad + "this." + ci.xvala_id.substring(1) + " =  child_" + "%d".printf(i) +  ";\n";
624                                                   
625                 }
626         }
627
628         void addInit()
629         {
630
631                 
632                 if (!this.node.has("init")) {
633                                 return;
634                 }
635                 this.ret+= "\n" + ipad + "// init method \n";
636         
637                 this.ret+= "\n" + ipad + this.padMultiline(ipad, this.node.get("init")) + "\n";
638
639          }
640          void addListeners()
641          {
642                 if (this.node.listeners.size < 1) {
643                         return;
644                 }
645                                 
646                         
647                         
648                 this.ret+= "\n" + ipad + "// listeners \n";
649
650                 var iter = this.node.listeners.map_iterator();
651                 while (iter.next()) {
652                         var k = iter.get_key();
653                         var v = iter.get_value();
654                                         this.ret+= this.ipad + "this.el." + k + ".connect( " + 
655                                         this.padMultiline(this.ipad,v) +");\n"; 
656                                         
657                                 }
658         }    
659                 void addEndCtor()
660         {
661                         
662                         
663                         
664                         // end ctor..
665                         this.ret+= this.pad + "}\n";
666         }
667
668
669         /*
670  * Standardize this crap...
671  * 
672  * standard properties (use to set)
673  *          If they are long values show the dialog..
674  *
675  * someprop : ....
676  * bool is_xxx  :: can show a pulldown.. (true/false)
677  * string html  
678  * $ string html  = string with value interpolated eg. baseURL + ".." 
679  *  Clutter.ActorAlign x_align  (typed)  -- shows pulldowns if type is ENUM? 
680  * $ untypedvalue = javascript untyped value...  
681  * _ string html ... = translatable..
682
683  * 
684  * object properties (not part of the GOjbect being wrapped?
685  * # Gee.ArrayList<Xcls_fileitem> fileitems
686  * 
687  * signals
688  * @ void open 
689  * 
690  * methods -- always text editor..
691  * | void clearFiles
692  * | someJSmethod
693  * 
694  * specials
695  * * prop -- string
696  * * args  -- string
697  * * ctor -- string
698  * * init -- big string?
699  * 
700  * event handlers (listeners)
701  *   just shown 
702  * 
703  * -----------------
704  * special ID values
705  *  +XXXX -- indicates it's a instance property / not glob...
706  *  *XXXX -- skip writing glob property (used as classes that can be created...)
707  * 
708  * 
709  */
710          
711         void addUserMethods()
712         {
713                         
714                 this.ret+= "\n" + pad + "// user defined functions \n";  
715                         
716                         // user defined functions...
717                 var iter = this.node.props.map_iterator();
718                 while(iter.next()) {
719                                 var k = iter.get_key();
720                         if (this.shouldIgnore(k)) {
721                                 continue;
722                         }
723                         // HOW TO DETERIME if its a method?            
724                                 if (k[0] != '|') {
725                                                 //strbuilder("\n" + pad + "// skip " + k + " - not pipe \n"); 
726                                                 continue;
727                         }       
728                                 // function in the format of {type} (args) { .... }
729                                 var kk = k.substring(2);
730                                 var vv = iter.get_value();
731                                 this.ret += this.pad + "public " + kk + " " + this.padMultiline(this.pad, vv) + "\n";
732                         
733                                 
734                         }
735         }
736
737         void iterChildren()
738         {
739                         
740                         if (this.depth > 0) {
741                         this.ret+= this.inpad + "}\n";
742                         }
743                 
744                 var iter = this.node.items.list_iterator();
745                 var i = -1;
746                 while (iter.next()) {
747                                 this.ret += this.mungeChild(iter.get());
748                 }
749                          
750                         if (this.depth < 1) {
751                                 this.ret+= this.inpad + "}\n";
752                         }
753                         
754                 }
755
756         string padMultiline(string pad, string str)
757         {
758                 var ar = str.strip().split("\n");
759                 return string.joinv("\n" + pad , ar);
760         }
761         
762         void ignore(string i) {
763                 this.ignoreList.add(i);
764                 
765         }
766         void ignoreWrapped(string i) {
767                 this.ignoreWrappedList.add(i);
768                 
769         }
770         bool shouldIgnore(string i)
771         {
772                 return ignoreList.contains(i);
773         }
774         bool shouldIgnoreWrapped(string i)
775         {
776                 return ignoreWrappedList.contains(i);
777         }
778         
779 }
780         
781          
782         
783         
784
785