cb08c8f9fb575eb6e9b61a62b92f68d224a475b0
[roobuilder] / src / Palete / GirObject.vala
1 /**
2  * This is the base class for representing the vala API
3  *  
4  * it was originally based on parsing Gir files - but since then
5  * has evolved into using libvala  
6  * 
7  * 
8  */
9
10
11
12 namespace Palete {
13         public errordomain GirError {
14                 INVALID_TYPE,
15                 NEED_IMPLEMENTING,
16                 MISSING_FILE,
17                 INVALID_VALUE,
18                 INVALID_FORMAT
19         }
20         public class GirObject: Object {
21                 public string name;
22                 public string ns;
23                 public string propertyof;
24                 public string type;
25                 public string nodetype;  // eg. Signal / prop etc.
26                 public string package;
27                 public string direction; // used for vala in/out/ref...
28                 
29                 public GirObject paramset = null;
30                 public GirObject return_value = null;
31                 public bool is_deprecated = false;                  
32                 public bool is_instance;
33                 public bool is_array;
34                 public bool  is_varargs;
35                 public bool  ctor_only; // specially added ctor properties..
36                 public bool is_writable = true;
37                 public bool is_readable = true;
38                 public bool is_abstract = false;
39                 public  string parent;
40                 public  string value;
41                 // to be filled in...
42          
43                 public  string sig; // signture (used to create event handlers)
44
45                 public bool is_overlaid;
46
47                 public  GirObject gparent;
48                 public Gee.ArrayList<GirObject> params;
49                 public Gee.ArrayList<string> implements;
50                 public Gee.ArrayList<string> implementations;
51                 public Gee.ArrayList<string> inherits; // full list of all classes and interfaces...
52                 public Gee.HashMap<string,GirObject> ctors;
53                 public Gee.HashMap<string,GirObject> methods;
54                 public Gee.HashMap<string,string>    includes;
55                 public Gee.HashMap<string,GirObject> classes;
56                 public Gee.HashMap<string,GirObject> props;
57                 public Gee.HashMap<string,GirObject> consts;
58                 public Gee.HashMap<string,GirObject> signals;
59                 public Gee.HashMap<string,GirObject> delegates;
60                 
61                 public Gee.ArrayList<string> optvalues; // used by Roo only..
62                 
63                 public Gee.ArrayList<string> can_drop_onto; // used by Roo only.. at present
64                 public Gee.ArrayList<string> valid_cn; // used by Roo only.. at present
65                 
66                 public string doctxt;
67
68
69                 
70                 public GirObject(string nodetype, string n)
71                 {
72                         this.nodetype = nodetype;
73                         this.name = n;
74                         this.ns = "";
75                         this.parent = "";
76                         this.type = "";
77                         this.propertyof = "";
78                         this.is_array = false;
79                         this.is_instance = false;
80                         this.is_varargs = false;
81                         this.ctor_only  =false;
82                         this.doctxt = "";
83                 
84                         this.sig = "";
85
86                         this.gparent = null;
87                         
88                         this.implements = new Gee.ArrayList<string>();
89                         this.implementations  = new Gee.ArrayList<string>();
90                         this.inherits  = new Gee.ArrayList<string>(); // list of all ancestors. (interfaces and parents)
91                         this.includes   = new Gee.HashMap<string,string>();
92
93                         this.params = new Gee.ArrayList<GirObject>();
94                         this.ctors      = new Gee.HashMap<string,GirObject>();
95                         this.methods    =new Gee.HashMap<string,GirObject>();
96
97                         this.classes    = new Gee.HashMap<string,GirObject>();
98                         this.props      = new Gee.HashMap<string,GirObject>();
99                         this.consts     = new Gee.HashMap<string,GirObject>();
100                         this.signals    = new Gee.HashMap<string,GirObject>();
101                         this.delegates    = new Gee.HashMap<string,GirObject>();
102                         
103                         this.optvalues = new Gee.ArrayList<string>();
104                         this.can_drop_onto = new Gee.ArrayList<string>();
105                         this.valid_cn = new Gee.ArrayList<string>();
106                         
107                         
108                         
109                         this.is_overlaid = false;
110                         this.paramset = null;
111                 }
112
113                 public string[] inheritsToStringArray()
114                 {
115                         string[] ret = {};
116                         for(var i =0;i< this.inherits.size; i++) {
117                                 ret += this.inherits.get(i);
118                         }
119                         for(var i =0;i< this.implements.size; i++) {
120                                 ret += this.implements.get(i);
121                         }
122                         return ret;
123
124                 }
125
126                 
127                 public void  overlayParent(Project.Project project)
128                 {
129                         
130                         if (this.parent.length < 1 || this.is_overlaid) {
131                                 this.is_overlaid = true;
132                                 return;
133                         }
134                          
135                         //print("Overlaying " +this.name + " with " + this.parent + "\n");
136
137                         var pcls = this.clsToObject( project, this.parent);
138                         if (pcls == null) {
139                                 return;
140                                 //throw new GirError.INVALID_VALUE("Could not find class : " + 
141                                 //      this.parent + " of " + this.name  + " in " + this.ns);
142                         }
143                         
144                         pcls.overlayParent( project );
145                         this.copyFrom(pcls,false);
146                         for(var i=0; i < this.implements.size; i++) {
147                                 var clsname = this.implements.get(i);
148                                 var picls = this.clsToObject(project, clsname);
149                                 this.copyFrom(picls,true);
150                         }
151                         this.is_overlaid = true;
152                         
153                 }
154
155                 public void overlayCtorProperties() 
156                 {
157                         //print("Check overlay Ctor %s\n", this.name);
158                         if (!this.ctors.has_key("new")) {
159                                 return;
160                         }
161                         var ctor = this.ctors.get("new");
162                         if (ctor.paramset == null || ctor.paramset.params.size < 1) {
163                                 return;
164                         }
165                         //print("Found Ctor\n");
166                         var iter = ctor.paramset.params.list_iterator();
167                         while (iter.next()) {
168                                 var n = iter.get().name;
169                                 
170                                 if (this.props.has_key(n)) {
171                                         continue;
172                                 }
173                                 if (n == "...") {
174                                         continue;
175                                 }
176                                 //print("Adding prop %s\n", n);
177                                 
178                                 // it's a new prop..
179                                 var c = new GirObject("Prop",n);
180                                 c.gparent = this;
181                                 c.ns = this.ns;
182                                 c.propertyof = this.name;
183                                 c.type = iter.get().type;
184                                 c.ctor_only = true;
185                                 this.props.set(n, c);
186                         }
187                         
188
189                 }
190
191
192                 
193                 public string fqn() {
194                         // not sure if fqn really is correct here...
195                         // 
196                         return this.nodetype.down() == "class" || this.nodetype.down() =="interface"
197                                         ? this.name : (this.ns + this.name);
198                 }
199                 
200                 public void copyFrom(GirObject pcls, bool is_interface) 
201                 {
202
203                         this.inherits.add(pcls.fqn());
204
205                         var liter = pcls.inherits.list_iterator();
206                         while(liter.next()) {
207                         if (this.inherits.contains(liter.get())) {
208                                         continue;
209                                 }
210                                 this.inherits.add(liter.get()); 
211                         }          
212                         
213                         
214                         var iter = pcls.methods.map_iterator();
215                         while(iter.next()) {
216                         if (null != this.methods.get(iter.get_key())) {
217                                         continue;
218                                 }
219                                 
220                                 this.methods.set(iter.get_key(), iter.get_value());
221                         }
222                         
223                         iter = pcls.props.map_iterator();
224                         while(iter.next()) {
225                                  if (null != this.props.get(iter.get_key())) {
226                                         continue;
227                                 }
228                                 
229                                 this.props.set(iter.get_key(), iter.get_value());
230                         }               
231                         
232                         iter = pcls.signals.map_iterator();
233                         while(iter.next()) {
234                                 if (null != this.signals.get(iter.get_key())) {
235                                                 continue;
236                                 }
237         
238                                 this.signals.set(iter.get_key(), iter.get_value());
239                         }       
240                 }
241                 
242                 public Json.Object toJSON()
243                 {
244                     var r = new Json.Object();
245                     r.set_string_member("nodetype", this.nodetype);
246                     r.set_string_member("name", this.name);
247                                 if (this.propertyof.length > 0) {
248                         r.set_string_member("of", this.propertyof);
249                     }
250                     if (this.type.length > 0) {
251                         r.set_string_member("type", this.type);
252                     }
253                     if (this.parent != null && this.parent.length > 0) {
254                         r.set_string_member("parent", this.parent);
255                     }
256                     if (this.sig.length > 0) {
257                         r.set_string_member("sig", this.sig);
258                     }
259                 
260                     // is_arary / is_instance / is_varargs..
261
262                 
263                         if (this.inherits.size > 0) {
264                         r.set_array_member("inherits", this.toJSONArrayString(this.inherits));
265                     }
266                     
267                     if (this.implements.size > 0) {
268                         r.set_array_member("implements", this.toJSONArrayString(this.implements));
269                     }
270                     
271                     if (this.params.size > 0) {
272                         r.set_array_member("params", this.toJSONArrayObject(this.params));
273                     }
274                     if (this.ctors.size > 0) {
275                         r.set_object_member("ctors", this.toJSONObject(this.ctors));
276                     }
277                     if (this.methods.size > 0) {
278                         r.set_object_member("methods", this.toJSONObject(this.methods));
279                     }
280                     if (this.includes.size > 0) {
281                         r.set_object_member("includes", this.toJSONObjectString(this.includes));
282                     }
283                     if (this.classes.size > 0) {
284                         r.set_object_member("classes", this.toJSONObject(this.classes));
285                     }
286                     if (this.props.size > 0) {
287                         r.set_object_member("props", this.toJSONObject(this.props));
288                     }
289                     if (this.consts.size > 0) {
290                         r.set_object_member("consts", this.toJSONObject(this.consts));
291                     }
292                     if (this.signals.size > 0) {
293                         r.set_object_member("signals", this.toJSONObject(this.signals));
294                     }
295                     if (this.paramset != null) {
296                         r.set_object_member("paramset", this.paramset.toJSON());
297                     }
298                     if (this.return_value != null) {
299                         r.set_object_member("return_value", this.return_value.toJSON());
300                     }
301                     return r;
302                 }
303                 public Json.Object toJSONObject(Gee.HashMap<string,GirObject> map)
304                 {
305                     var r = new Json.Object();
306                     var iter = map.map_iterator();
307                     while(iter.next()) {
308                         r.set_object_member(iter.get_key(), iter.get_value().toJSON());
309                     }
310                     return r;
311                 }
312                 public Json.Object  toJSONObjectString(Gee.HashMap<string,string> map)
313                 {
314                     var r = new Json.Object();
315                     var iter = map.map_iterator();
316                     while(iter.next()) {
317                         r.set_string_member(iter.get_key(), iter.get_value());
318                     }
319                     return r;
320                 }
321                 public Json.Array toJSONArrayString(Gee.ArrayList<string> map)
322                 {
323                     var r = new Json.Array();
324                     for(var i =0;i< map.size;i++) {
325                     
326                         r.add_string_element(map.get(i));
327                     }
328                     return r;
329                 }
330                 public Json.Array toJSONArrayObject(Gee.ArrayList<GirObject> map)
331                 {
332                     var r = new Json.Array();
333                     for(var i =0;i< map.size;i++) {
334                     
335                         r.add_object_element(map.get(i).toJSON());
336                     }
337                     return r;
338                 }
339                 public string asJSONString()
340                 {
341                         var generator = new Json.Generator ();
342                         generator.indent = 4;
343                         generator.pretty = true;
344                         var n = new Json.Node(Json.NodeType.OBJECT);
345                         n.set_object(this.toJSON());
346                         generator.set_root(n);
347         
348                         return generator.to_data(null);
349                 }
350
351  
352                 public GirObject? fetchByFqn(string fqn) {
353                         GLib.debug("Searching (%s)%s for %s\n", this.nodetype, this.name, fqn);
354                         var bits = fqn.split(".");
355                         
356                         var ret = this.classes.get(bits[0]);
357                         if (ret != null) {
358                                 if (bits.length < 2) {
359                                         return ret;
360                                 }
361                                 return ret.fetchByFqn(fqn.substring(bits[0].length+1));
362                         }
363
364                         ret = this.ctors.get(bits[0]);                  
365                 if (ret != null) {
366                                 if (bits.length < 2) {
367                                         return ret;
368                                 }
369                                 return ret.fetchByFqn(fqn.substring(bits[0].length+1));
370                         }
371
372                         ret = this.methods.get(bits[0]);                        
373                 if (ret != null) {
374                                 if (bits.length < 2) {
375                                         return ret;
376                                 }
377                                 return ret.fetchByFqn(fqn.substring(bits[0].length+1));
378                         }
379                         ret = this.props.get(bits[0]);                  
380                 if (ret != null) {
381                                 if (bits.length < 2) {
382                                         return ret;
383                                 }
384                                 return ret.fetchByFqn(fqn.substring(bits[0].length+1));
385                         }
386                         ret = this.consts.get(bits[0]);                 
387                 if (ret != null) {
388                                 if (bits.length < 2) {
389                                         return ret;
390                                 }
391                                 return ret.fetchByFqn(fqn.substring(bits[0].length+1));
392                         }
393
394                         ret = this.signals.get(bits[0]);                        
395                 if (ret != null) {
396                                 if (bits.length < 2) {
397                                         return ret;
398                                 }
399                                 return ret.fetchByFqn(fqn.substring(bits[0].length+1));
400                         }
401                         ret = this.delegates.get(bits[0]);                      
402                 if (ret != null) {
403                         // delegates are only on namespaces...
404                                  return ret;
405                                  
406                         }
407                         
408                         if (this.paramset == null) {
409                                 return null;
410                         }
411                         var iter = this.paramset.params.list_iterator();
412                         while (iter.next()) {
413                                 var p = iter.get();
414                                 if (p.name != bits[0]) {
415                                         continue;
416                                 }
417                                 return p;
418                         }
419                                  
420                         // fixme - other queires? - enums?
421                         return null;
422                 }
423                 /**
424                  *  -----------------------------------------------
425                  *  code relating to the structure loader ....
426                  * 
427                  */
428
429                 public GirObject clsToObject(Project.Project project , string in_pn)
430                 {
431                         var pn = in_pn;
432                   
433                         
434                         var gir = Gir.factory (project, this.ns);
435                         if (in_pn.contains(".")) {
436                                 gir =  Gir.factory(project, in_pn.split(".")[0]);
437                                 pn = in_pn.split(".")[1];
438                         }
439                         
440                         
441                         return gir.classes.get(pn);
442
443                         
444                 }
445                 
446                 
447                 public JsRender.NodeProp toNodeProp( Palete pal, string par_xtype)
448                 {
449                         
450                         if (this.nodetype.down() == "signal") { // gtk is Signal, roo is signal??
451                                 // when we add properties, they are actually listeners attached to signals
452                                 // was a listener overrident?? why?
453                                 var r = new JsRender.NodeProp.listener(this.name,   this.sig);  
454                                 r.propertyof = this.propertyof;
455                                 if (this.name == "notify" && pal.name == "Gtk") {
456                                         this.nodePropAddNotify(r, par_xtype, pal);
457                                 }
458                                 
459                                 return r;
460                         }
461                         
462                         // does not handle Enums... - no need to handle anything else.
463                         var def = this.type.contains(".") ?  "" :  Gir.guessDefaultValueForType(this.type);
464                         if (this.type.contains(".") || this.type.contains("|") || this.type.contains("/")) {
465                                 var ret = new JsRender.NodeProp.prop(this.name, this.type, def);  ///< was raw..?
466                                 ret.propertyof = this.propertyof;
467                                 this.nodePropAddChildren(ret, this.type, pal);
468                                 if (ret.childstore.n_items == 1) {
469                                         var np = (JsRender.NodeProp) ret.childstore.get_item(0);
470                                         ret.add_node = np.add_node;
471                                         ret.childstore.remove_all();
472                                 }
473                                 
474                                 
475                                 return ret;
476                         }
477                         if (this.type.down() == "function"  ) {
478                                 var  r =   new JsRender.NodeProp.raw(this.name, this.type, "function()\n{\n\n}");
479                                 r.propertyof = this.propertyof;
480                                 return  r;                      
481                         }
482                         if (this.type.down() == "array"  ) {
483                                 var  r = new JsRender.NodeProp.raw(this.name, this.type, "[\n\n]");
484                                 r.propertyof = this.propertyof;
485                                 return  r;                      
486                         }
487                         if (this.type.down() == "object"  ) {
488                                 var  r =  new JsRender.NodeProp.raw(this.name, this.type, "{\n\n}");
489                                 r.propertyof = this.propertyof;
490                                 return  r;                      
491                         }
492                         // plain property.. no children..
493                         var r = new JsRender.NodeProp.prop(this.name, this.type, def); // signature?
494                         r.propertyof = this.propertyof;
495                         return  r;
496                 
497                 }
498                 public void nodePropAddChildren(JsRender.NodeProp par, string str,  Palete pal)
499                 {
500                         
501                         
502                         if (str.contains("|")) {
503                                 var ar = str.split("|");
504                                 for(var i = 0; i < ar.length; i++) {
505                                         this.nodePropAddChildren(par, ar[i], pal);
506                                 }
507                                 return;
508                         }
509                         if (str.contains("/")) {
510                                 var ar = str.split("/");
511                                 for(var i = 0; i < ar.length; i++) {
512                                         this.nodePropAddChildren(par, ar[i], pal);
513                                 }
514                                 return;
515                         }
516                         var cls = pal.getClass(str);
517                         // it's an object..
518                         // if node does not have any children and the object type only has 1 type.. then we dont add anything...
519                         // note all classes are expected to have '.' seperators
520                         if (cls == null || !str.contains(".")) {
521                                 GLib.debug("nodepropaddchildren: check class %s - not found in classes", str);
522                                 par.childstore.append( new JsRender.NodeProp.prop(this.name, str,  Gir.guessDefaultValueForType(str)));
523                                 return;
524                         }
525                         GLib.debug("nodepropaddchildren: check class %s - type = %s", str, cls.nodetype);
526                         if (cls.nodetype.down() == "enum") {                    
527                                 var add = new JsRender.NodeProp.raw(this.name, str, "");
528                                 par.childstore.append( add);
529                                 return ;
530                         }
531                         
532                          
533                         if (cls.nodetype.down() == "class") {
534                                 var add = new JsRender.NodeProp.raw(this.name, str, "");
535                                 // no propertyof ?
536                                 
537                                 
538                                 add.add_node = pal.fqnToNode(str);
539                                 add.add_node.add_prop(new JsRender.NodeProp.special("prop", this.name));
540                                 par.childstore.append( add);
541                         }
542
543
544                         
545                         if (cls.implementations.size < 1) {
546                                 GLib.debug("nodepropaddchildren: check class %s - no implementations", str);
547                                 return;
548                         }
549                         
550                         GLib.debug("nodepropaddchildren: check class %s", str);                 
551                         
552                         foreach (var cname in cls.implementations) {
553
554                                 
555                                 var subcls = pal.getClass(cname);
556                                 
557                                 GLib.debug("nodepropaddchildren: check class %s add %s type %s", str, cname, subcls == null ? "NO?" :subcls.nodetype );
558                                 if (subcls.nodetype.down() != "class") {
559
560                                         continue;
561                                 }
562                          
563                                 var add = new JsRender.NodeProp.raw(this.name, cname, "");
564                                 // no propertyof ?
565                                 add.add_node = pal.fqnToNode(cname);
566                                 add.add_node.add_prop(new JsRender.NodeProp.special("prop", this.name));
567                                 par.childstore.append( add);
568  
569                         
570                         }
571                         
572                         
573                         
574                         
575                 }
576                 public void  nodePropAddNotify(JsRender.NodeProp par, string par_xtype, Palete pal)
577                 {
578                         var els = pal.getPropertiesFor( par_xtype, JsRender.NodePropType.PROP);
579                         foreach(var elname in els.keys) {
580                                  var add = new JsRender.NodeProp.listener("notify[\"" + elname  +"\"]" ,  "() => {\n }");  
581                                 add.propertyof = par.propertyof;
582                                 par.childstore.append( add);
583                         }
584                 
585                 }
586                 
587                 
588                  
589         }
590             
591 }