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