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