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