Fix #5666 - roojspacker - create temporary files in temporary location
[roojspacker] / src / jsdoc / DocBuilder.vala
1  
2  
3
4 namespace JSDOC 
5 {
6
7         class DocBuilder : Object 
8         {
9                 
10  
11                 // extractable via JSON?
12                 public string VERSION = "1.0.0" ;
13                 
14                 private SymbolSet symbolSet;
15                 
16                 private Packer packer;
17         
18                 public DocBuilder (Packer p) 
19                 {
20                         
21  
22                         GLib.debug("Roo JsDoc Toolkit started  at %s ",  (new GLib.DateTime.now_local()).format("Y/m/d H:i:s"));
23                         
24                         this.packer = p;
25         
26                     //if (PackerRun.singleton().opt_tmp_dir != null && !FileUtils.test(PackerRun.singleton().opt_tmp_dir, GLib.FileTest.IS_DIR)) {   
27                     //    Posix.mkdir(PackerRun.singleton().opt_tmp_dir, 0700);
28                     //}
29         
30         
31                     this.parseSrcFiles();
32                     
33                     this.symbolSet = DocParser.symbols();
34                      
35                     // this currently uses the concept of publish.js...
36                     
37                     this.publish();
38         
39         
40                 }
41                 
42          
43                 
44                 /**
45                  * Parse the source files.
46                  * 
47                  */
48  
49                 private void parseSrcFiles() 
50                 {
51                    
52                     
53                     
54                     //var useCache = PackerRun.opt_cache_dir == null ;
55                     //var cacheFile = "";
56                     
57                     for (var i = 0, l = this.packer.files.size; i < l; i++) {
58                         
59                         var srcFile = this.packer.files.get(i);
60                         GLib.debug("Parsing source File: %s", srcFile);
61                      /*   
62                         if (useCache) {
63                         
64                                 cacheFile = PackerRun.opt_cache_dir + srcFile.replace("/", '_') + ".cache";
65                                     
66                                     //print(cacheFile);
67                                     // disabled at present!@!!
68                                     
69                                     if (GLib.FileUtils.test(cacheFile, GLib.FileTest.EXISTS)) {
70                                         // check filetime?
71                                         var cache_mt = File.new_for_path (cacheFile).queryInfo(FileAttribute.TIME_MODIFIED,
72                                                             GLib.FileQueryInfoFlags.NONE, null).
73                                                             get_modification_time();
74                                         var original_mt = File.new_for_path (sourceInfo).queryInfo(FileAttribute.TIME_MODIFIED,
75                                                             GLib.FileQueryInfoFlags.NONE, null).
76                                                             get_modification_time();
77                                         // this check does not appear to work according to the doc's - need to check it out.
78                                        
79                                         if (cache_mt > original_mt) { // cached time  > original time!
80                                             // use the cached mtimes..
81                                             GLib.debug("Read %s" , cacheFile);
82                                                         var parser = new Json.Parser();
83                                             parser.load_from_file(cacheFile);
84                                             var ar = parser.get_root ().get_array();
85
86                                             for(var i = 0;i < ar.get_length();i++) {
87                                                         var o = ar.get_object_element(i);
88                                                         var sym = JSON.gobject_from_data(typeof(Symbol), o) as Symbol;
89                                                         DocParser.symbols.add(sym);
90                                                 }
91                                                 continue;
92                                         }
93                             }
94                         }
95                        */ 
96                         var src = "";
97                         try {
98                             GLib.debug("reading : %s" , srcFile);
99                             GLib.FileUtils.get_contents(srcFile, out src);
100                         }
101                         catch(GLib.FileError e) {
102                             GLib.debug("Can't read source file '%s': %s", srcFile, e.message);
103                             continue;
104                         }
105
106                           
107                         
108                         var tr = new  TokenReader(this.packer);
109                                 tr.keepDocs = true;
110                                 tr.keepWhite = true;
111                                 tr.keepComments = true;
112                                 tr.sepIdents = false;
113                                 tr.collapseWhite = false;
114                                 tr.filename = src;
115                         
116
117                         var toks = tr.tokenize( new TextStream(src) );
118                         if (PackerRun.singleton().opt_dump_tokens) {
119                                         toks.dump();
120                                         return;
121                                         //GLib.Process.exit(0);
122                                 }
123                         
124                         
125                         var ts = new TokenStream(toks.tokens);
126                     
127                     
128                     
129                                  
130                         DocParser.parse(ts, srcFile);
131                         /*
132                         if (useCache) {
133                                 
134                                 var ar = DocParser.symbolsToObject(srcFile);
135                                 
136                                 var builder = new Json.Builder ();
137                                 builder.begin_array ();
138                                 for (var i=0;i<ar.size;i++) {
139                                 
140                                                 builder.add_object_value (ar.get(i));
141                                         }
142                                         builder.end_array ();
143                                         Json.Generator generator = new Json.Generator ();
144                                         Json.Node root = builder.get_root ();
145                                         generator.set_root (root);
146                                         generator.pretty=  true;
147                                         generator.ident = 2;
148                                         generator.to_file(cacheFile);
149                         
150                          
151                             
152                                  }
153                                  */
154                     }
155                     
156                      
157                     
158                     DocParser.finish();
159                 }
160                 /*
161
162             //var txs =
163             
164             var tr = new  TokenReader(this.packer);
165                         tr.keepDocs = true;
166                         tr.keepWhite = true;
167                         tr.keepComments = true;
168                         tr.sepIdents = false;
169                         tr.collapseWhite = false;
170                         tr.filename = src;
171             
172
173             var toks = tr.tokenize( new TextStream(src));
174             if (PackerRun.opt_dump_tokens) {
175                                 toks.dump();
176                                 return "";
177                                 //GLib.Process.exit(0);
178                         }
179             
180             
181             var ts = new TokenStream(toks);
182         
183         
184         
185                      
186             DocParser.parse(ts, srcFile);
187             
188             if (useCache) {
189                         
190                         var ar = DocParser.symbolsToObject(srcFile);
191                         
192                         var builder = new Json.Builder ();
193                 builder.begin_array ();
194                 for (var i=0;i<ar.size;i++) {
195                 
196                                         builder.add_object_value (ar.get(i));
197                                 }
198                                 builder.end_array ();
199                                 Json.Generator generator = new Json.Generator ();
200                                 Json.Node root = builder.get_root ();
201                                 generator.set_root (root);
202                                 generator.pretty=  true;
203                                 generator.ident = 2;
204                                 generator.to_file(cacheFile);
205             
206              
207                 
208     //          }
209         }
210         
211         
212         
213         Parser.finish();
214     }
215     
216      */
217         string tempdir;
218         
219                 void publish() 
220                 {
221                     GLib.debug("Publishing");
222                      
223                     // link!!!
224                     this.tempdir = GLib.DirUtils.make_tmp("roopackerXXXXXX");
225                     
226                     GLib.debug("Making directories");
227                     if (!FileUtils.test (PackerRun.singleton().opt_doc_target,FileTest.IS_DIR )) {
228                         Posix.mkdir(PackerRun.singleton().opt_doc_target,0755);
229                     }
230                     if (!FileUtils.test(PackerRun.singleton().opt_doc_target+"/symbols",FileTest.IS_DIR)) {
231                         Posix.mkdir(PackerRun.singleton().opt_doc_target+"/symbols",0755);
232                     }
233                     if (!FileUtils.test(PackerRun.singleton().opt_doc_target+"/src",FileTest.IS_DIR)) {
234                         Posix.mkdir(PackerRun.singleton().opt_doc_target+"/src",0755);
235                     }
236                     if (!FileUtils.test(PackerRun.singleton().opt_doc_target +"/json",FileTest.IS_DIR)) {
237                         Posix.mkdir(PackerRun.singleton().opt_doc_target +"/json",0755);
238                     }
239                     
240                     GLib.debug("Copying files from static: %s " , PackerRun.singleton().opt_doc_template_dir);
241                     // copy everything in 'static' into 
242                     
243                     if (PackerRun.singleton().opt_doc_template_dir  != null) {
244                                 
245                                 var iter = GLib.File.new_for_path(
246                                                 PackerRun.singleton().opt_doc_template_dir + "/static"
247                                         ).enumerate_children (
248                                         "standard::*",
249                                         FileQueryInfoFlags.NOFOLLOW_SYMLINKS, 
250                                         null);
251                                 FileInfo info;
252                                 
253                                 while ( (info = iter.next_file (null)) != null) {
254                                         if (info.get_file_type () == FileType.DIRECTORY) {
255                                                 continue;
256                                         } 
257                                         var src = File.new_for_path(info.get_name());
258                                     GLib.debug("Copy %s to %s/%s" ,
259                                          info.get_name() ,
260                                           PackerRun.singleton().opt_doc_target , src.get_basename());                   
261                                 
262                                         src.copy(
263                                                 GLib.File.new_for_path(
264                                                         PackerRun.singleton().opt_doc_target + "/" + src.get_basename()
265                                                 ),
266                                                 GLib.FileCopyFlags.OVERWRITE
267                                         );
268                                 }
269                 
270                         }                   
271                     GLib.debug("Setting up templates");
272                      
273                     
274                     
275                     var symbols = this.symbolSet.values();
276                     
277                     var files = this.packer.files;
278                     
279                     for (var i = 0, l = files.size; i < l; i++) {
280                         var file = files.get(i);
281                        // var targetDir = PackerRun.singleton().opt_doc_target + "/symbols/src/";
282                         this.makeSrcFile(file);
283                     }
284                     //print(JSON.stringify(symbols,null,4));
285                     var classes = new Gee.ArrayList<Symbol>();
286                     
287                     foreach(var symbol in symbols) {
288                                 if (symbol.isaClass()) { 
289                                         classes.add(symbol);
290                                 }
291                     }    
292                     classes.sort( (a,b) => {
293                                 return a.alias.collate(b.alias); 
294                         });
295                      
296                      //GLib.debug("classTemplate Process : all classes");
297                         
298                    // var classesIndex = classesTemplate.process(classes); // kept in memory
299                     
300                     GLib.debug("iterate classes");
301                    
302                     var jsonAll = new Json.Object(); 
303                     
304                     for (var i = 0, l = classes.size; i < l; i++) {
305                         var symbol = classes.get(i);
306                         var output = "";
307                         
308                         GLib.debug("classTemplate Process : %s" , symbol.alias);
309                         
310                         
311                         var   class_gen = new Json.Generator ();
312                             var  class_root = new Json.Node(Json.NodeType.OBJECT);
313                                 class_root.init_object(this.class_to_json(symbol));
314                                 class_gen.set_root (class_root);
315                                 class_gen.pretty=  true;
316                                 class_gen.indent = 2;
317                                 GLib.warning("writing JSON:  %s", PackerRun.singleton().opt_doc_target+"/symbols/" +symbol.alias+".json");
318                                 this.writeJson(class_gen, PackerRun.singleton().opt_doc_target+"/symbols/" +symbol.alias+".json");
319                         
320                         jsonAll.set_object_member(symbol.alias,  this.publishJSON(symbol));
321
322                     }
323                     
324                     // outptu class truee
325                     
326                     var   class_tree_gen = new Json.Generator ();
327             var  class_tree_root = new Json.Node(Json.NodeType.ARRAY);
328                         class_tree_root.init_array(this.class_tree(classes));
329                         class_tree_gen.set_root (class_tree_root);
330                         class_tree_gen.pretty=  true;
331                         class_tree_gen.indent = 2;
332                         GLib.warning("writing JSON:  %s", PackerRun.singleton().opt_doc_target+"/tree.json");
333                         this.writeJson(class_tree_gen,PackerRun.singleton().opt_doc_target+"/tree.json");
334                         size_t class_tree_l;
335                         //GLib.debug("JSON: %s", class_tree_gen.to_data(out class_tree_l));
336                     
337                     
338                     
339                     /*---- this is our 'builder' json file.. -- a full list of objects+functions */
340                     
341                     
342                     var   generator = new Json.Generator ();
343             var  root = new Json.Node(Json.NodeType.OBJECT);
344                         root.init_object(jsonAll);
345                         generator.set_root (root);
346                         generator.pretty=  true;
347                         generator.indent = 2;
348                         GLib.warning("writing JSON:  %s", PackerRun.singleton().opt_doc_target+"/json/roodata.json");
349                         
350                         
351                         this.writeJson(generator,PackerRun.singleton().opt_doc_target+"/json/roodata.json");
352                         size_t l;
353                         //GLib.debug("JSON: %s", generator.to_data(out l));
354                     
355                     
356                      
357                     
358                     GLib.debug("build index");
359                    
360                     
361                     
362                 }
363                 
364                 Json.Object class_to_json (Symbol cls)
365                 {
366                         var ret = new Json.Object();
367                         ret.set_string_member("name", cls.alias);
368                         var ag = new Json.Array();
369                         ret.set_array_member("augments", ag);                   
370                         for(var ii = 0, il = cls.augments.size; ii < il; ii++) {
371                   var contributer = this.symbolSet.getSymbol(cls.augments[ii]);
372                   if (contributer == null) {
373                         continue;
374                         }
375                   ag.add_string_element(contributer.alias);
376             }
377             ret.set_string_member("name", cls.alias);  
378             ret.set_string_member("desc", cls.desc);
379                 ret.set_boolean_member("isSingleton", cls.comment.getTag(DocTagTitle.SINGLETON).size > 0);
380                 ret.set_boolean_member("isStatic", cls.isa != "CONSTRUCTOR");
381                 ret.set_boolean_member("isBuiltin", cls.isBuiltin());
382                 
383                 // needded so that the class can fake a ctor..
384             ret.set_string_member("memberOf", cls.name);
385                         ret.set_string_member("example", cls.comment.getTagAsString(DocTagTitle.EXAMPLE));
386                     ret.set_string_member("deprecated", // as depricated is used as a flag...
387                                         cls.comment.getTag(DocTagTitle.DEPRECATED).size > 0 ? 
388                                         "This has been deprecated: "+  cls.comment.getTagAsString(DocTagTitle.DEPRECATED) : 
389                                 "");
390                 ret.set_string_member("since", cls.comment.getTagAsString(DocTagTitle.SINCE));
391                 ret.set_string_member("see", cls.comment.getTagAsString(DocTagTitle.SINCE));
392                         // not supported or used yet?
393                         //add.set_string_member("exceptions", m.comment.getTagAsString(DocTagTitle.EXCEPTIONS));
394                         //add.set_string_member("requires", m.comment.getTagAsString(DocTagTitle.REQUIRES));
395                 ret.set_array_member("params", cls.paramsToJson());
396                 ret.set_array_member("returns", new Json.Array()); 
397                                 
398                         //ret.set_string_member("desc", cls.comment.getTagAsString(DocTagTitle.DESC));
399                 /// fixme - @see ... any others..
400                         
401                         var props = new Json.Array(); 
402                         ret.set_array_member("config", props);
403                         var cfgProperties = cls.configToArray();
404                         for(var i =0; i < cfgProperties.size;i++) {
405                         var p = cfgProperties.get(i);
406                         var add = new Json.Object();
407                         add.set_string_member("name",p.name);
408                         add.set_string_member("type",p.type);
409                         add.set_string_member("desc",p.desc);
410                         add.set_string_member("memberOf",  p.memberOf);
411                         add.set_array_member("values",p.optvalues.size > 0 ? p.optvalue_as_json_array() : new Json.Array());
412                         props.add_object_element(add );
413                     }
414                      
415                     // methods
416
417                          
418                         var methods = new Json.Array();
419                         ret.set_array_member("methods", methods);                    
420                     foreach(var m in cls.methods) {
421                         if (m.isEvent || m.isIgnored) {
422                                 continue;
423                         }
424                         
425                         var add = new Json.Object();
426                         add.set_string_member("name",m.name);
427                         //add.set_string_member("type","function");
428                         add.set_string_member("desc",m.desc);
429                         //add.set_string_member("sig", m.makeMethodSkel());
430                         add.set_boolean_member("isStatic", m.isStatic);
431                         add.set_boolean_member("isConstructor", m.isa == "CONSTRUCTOR");
432                         add.set_boolean_member("isPrivate", m.isPrivate);
433                         //add.set_string_member("instanceOf", m.comment.getTagAsString(DocTagTitle.INSTANCEOF));
434                         add.set_string_member("memberOf", m.memberOf);
435                         add.set_string_member("example", m.comment.getTagAsString(DocTagTitle.EXAMPLE));
436                         add.set_string_member("deprecated", // as depricated is used as a flag...
437                                         m.comment.getTag(DocTagTitle.DEPRECATED).size > 0 ? 
438                                         "This has been deprecated: "+  m.comment.getTagAsString(DocTagTitle.DEPRECATED) : 
439                                         "");
440                         add.set_string_member("since", m.comment.getTagAsString(DocTagTitle.SINCE));
441                         add.set_string_member("see", m.comment.getTagAsString(DocTagTitle.SINCE));
442                         // not supported or used yet?
443                         //add.set_string_member("exceptions", m.comment.getTagAsString(DocTagTitle.EXCEPTIONS));
444                         //add.set_string_member("requires", m.comment.getTagAsString(DocTagTitle.REQUIRES));
445                         add.set_array_member("params", m.paramsToJson());
446                         add.set_array_member("returns", m.returnsToJson());
447                         
448                         /// fixme - @see ... any others..
449                           
450                         
451                         methods.add_object_element(add);
452                     }
453                     
454                     
455                         var events = new Json.Array();
456                         ret.set_array_member("events", events);              
457                     foreach(var m in cls.methods) {
458                         if (!m.isEvent || m.isIgnored) {
459                                 continue;
460                         }
461                         
462                         var add = new Json.Object();
463                         add.set_string_member("name",m.name.substring(1)); // all prefixed with '*'...
464                         //add.set_string_member("type","function");
465                         add.set_string_member("desc",m.desc);
466                         //add.set_string_member("sig", m.makeMethodSkel());
467
468                         add.set_string_member("memberOf", m.memberOf);
469                         add.set_string_member("example", m.comment.getTagAsString(DocTagTitle.EXAMPLE));
470                         add.set_string_member("deprecated", // as depricated is used as a flag...
471                                         m.comment.getTag(DocTagTitle.DEPRECATED).size > 0 ? 
472                                         "This has been deprecated: "+  m.comment.getTagAsString(DocTagTitle.DEPRECATED) : 
473                                         "");
474                         add.set_string_member("since", m.comment.getTagAsString(DocTagTitle.SINCE));
475                         add.set_string_member("see", m.comment.getTagAsString(DocTagTitle.SINCE));
476                         // not supported or used yet?
477                         //add.set_string_member("exceptions", m.comment.getTagAsString(DocTagTitle.EXCEPTIONS));
478                         //add.set_string_member("requires", m.comment.getTagAsString(DocTagTitle.REQUIRES));
479                         
480                         add.set_array_member("params", m.paramsToJson());
481                         add.set_array_member("returns", m.returnsToJson());
482                         
483                         /// fixme - @see ... any others..
484                           
485                         
486                         events.add_object_element(add);
487                     }
488                     
489                         
490                         
491                 
492                         return ret;
493                 }
494                 /**
495                 * needed as Json dumps .xXXX into same directory as it writes...
496                 */
497                 void writeJson(Json.Generator g, string fname)
498                 {
499                                 var tmp = this.tempdir + GLib.Path.get_basename(fname);
500                                 g.to_file(tmp);
501                                 
502                                 if (GLib.FileUtils.test(fname, GLib.FileTest.EXISTS)) {
503                                         string new_data, old_data;
504                                         FileUtils.get_contents(tmp, out new_data);
505                                         FileUtils.get_contents(fname, out old_data);
506                                         if (old_data == new_data) {
507                                                 GLib.File.new_for_path(tmp).delete();
508                                                 return;
509                                         }
510                            }
511                                 
512                         GLib.File.new_for_path(tmp).move( File.new_for_path(fname), GLib.FileCopyFlags.OVERWRITE);
513                       
514                 }
515                 
516                 /**
517                  * JSON files are lookup files for the documentation
518                  * - can be used by IDE's or AJAX based doc tools
519                  * 
520                  * 
521                  */
522                 Json.Object publishJSON (Symbol data)
523                 {
524                     // what we need to output to be usefull...
525                     // a) props..
526                     var cfgProperties = new Gee.ArrayList<DocTag>();
527                     if (data.comment.getTag(DocTagTitle.SINGLETON).size < 1) {
528                          cfgProperties = data.configToArray();
529                          cfgProperties.sort((a,b) =>{
530                                 return a.name.collate(b.name);
531                         }); 
532                         
533                     } 
534                     
535                     var props = new Json.Array(); 
536                     //println(cfgProperties.toSource());
537                     
538                     for(var i =0; i < cfgProperties.size;i++) {
539                         var p = cfgProperties.get(i);
540                         var add = new Json.Object();
541                         add.set_string_member("name",p.name);
542                         add.set_string_member("type",p.type);
543                         add.set_string_member("desc",p.desc);
544                         add.set_string_member("memberOf", p.memberOf == data.alias ? "" : p.memberOf);
545                             
546                         if (p.optvalues.size > 0) {
547                                 add.set_array_member("desc",p.optvalue_as_json_array());
548                         }
549                         
550                         props.add_object_element(add );
551                     }
552                     
553                     ///// --- events
554                     var ownEvents = new Gee.ArrayList<Symbol>();
555                     for(var i =0; i < data.methods.size;i++) {
556                                 var e = data.methods.get(i);
557                                 if (e.isEvent && !e.isIgnored) {
558                                         ownEvents.add(e);
559                                 }
560                         }; 
561                         ownEvents.sort((a,b) => {
562                                 return a.name.collate(b.name);
563                         });
564                     
565                     var events = new Json.Array();
566                      
567                     for(var i =0; i < ownEvents.size;i++) {
568                         var m = ownEvents.get(i);
569                         var add = new Json.Object();
570                         add.set_string_member("name",m.name.substring(1,-1)); // remove'*' on events..
571                         add.set_string_member("type","function");
572                         add.set_string_member("desc",m.desc);
573                         add.set_string_member("sig", m.makeFuncSkel());
574                         add.set_string_member("memberOf", m.memberOf == data.alias ? "" : m.memberOf);                  
575                         events.add_object_element(add);
576                     } 
577                      
578                     // methods
579                     var ownMethods = new Gee.ArrayList<Symbol>();
580                     for(var i =0; i < data.methods.size;i++) {
581                                 var e = data.methods.get(i);
582                                 if (!e.isEvent && !e.isIgnored) {
583                                         ownMethods.add(e);
584                                 }
585                         };
586                         ownMethods.sort((a,b) => {
587                                 return a.name.collate(b.name);
588                         });
589                     
590                         var methods = new Json.Array();
591                      
592                     for(var i =0; i < ownMethods.size;i++) {
593                         var m = ownMethods.get(i);
594                         var add = new Json.Object();
595                         add.set_string_member("name",m.name);
596                         add.set_string_member("type","function");
597                         add.set_string_member("desc",m.desc);
598                         add.set_string_member("sig", m.makeMethodSkel());
599                         add.set_boolean_member("static", m.isStatic);
600                         add.set_string_member("memberOf", m.memberOf == data.alias ? "" : m.memberOf);  
601                         methods.add_object_element(add);
602                     }
603                      
604                     //println(props.toSource());
605                     // we need to output:
606                     //classname => {
607                     //    propname => 
608                     //        type=>
609                     //        desc=>
610                     //    }
611                         var ret =  new Json.Object();
612                         ret.set_array_member("props", props);
613                         ret.set_array_member("events", events);
614                         ret.set_array_member("methods", methods);
615                 
616                     return ret;
617                     
618                     
619                     // b) methods
620                     // c) events
621                     
622                     
623                 }
624                 Gee.HashMap<string,Json.Object> class_tree_map;
625                 Json.Array class_tree_top;
626                 
627                 Json.Object? class_tree_new_obj(string name, bool is_class, out bool is_new) 
628                 {
629                 if (this.class_tree_map.has_key(name)) {
630                         var ret = this.class_tree_map.get(name);
631                         if (!ret.get_boolean_member("is_class") && is_class) {
632                                 ret.set_boolean_member("is_class", is_class);
633                         }
634                         is_new = false;
635                         return ret; // no need to do anything
636                 
637                 }
638                 
639                 GLib.debug("Class Tree: new object %s", name);
640                 var add =  new Json.Object();
641                 add.set_string_member("name", name);
642                 add.set_array_member("cn", new Json.Array());
643                 add.set_boolean_member("is_class", is_class);
644                 this.class_tree_map.set(name, add);
645                 var bits = name.split(".");
646                 if (bits.length == 1) {
647                         // top level..
648                         this.class_tree_top.add_object_element(add);
649                          
650                 } 
651                 is_new = true;
652                 
653                         return add;
654                 
655                 }
656                 
657                 void class_tree_make_parents(  Json.Object add)
658                 {
659                         var name = add.get_string_member("name");
660                         var bits = name.split(".");
661                 if (bits.length < 2) {
662                         return;
663                 }
664                 // got aaa.bb or aaa.bb.cc
665                 // find the parent..
666                 string[] nn = {};
667                 for(var i=0; i < bits.length-1; i++) {
668                         nn += bits[i];
669                 }
670                 var pname = string.joinv(".", nn);
671                 GLib.debug("Class Tree: adding to parent %s => %s", name, pname); 
672                          
673                         // no parent found.. make one..
674                         bool is_new;
675                         var parent = this.class_tree_new_obj(pname, false, out is_new); 
676                         parent.get_array_member("cn").add_object_element(add);
677                         if (is_new) {
678                                 this.class_tree_make_parents(  parent);
679                         }
680                 
681                 
682                 }
683                 Json.Array class_tree (Gee.ArrayList<Symbol> classes )
684                 {
685                 
686                 
687                     // produce a tree array that can be used to render the navigation.
688                     /*
689                     should produce:
690                     
691                     [
692                         {
693                                 name : Roo,
694                                 desc : ....
695                                 is_class : true,
696                                 cn : [
697                                         {
698                                                 name : 'Roo.util',
699                                                 basename : 'util',
700                                                 is_class : false,
701                                                 cn : [
702                                                         {
703                                                                 ....
704                     
705                     to do this, we will need to create the objects in a hashmap
706                     Roo.util => Json.Object
707                     
708                     */
709                     this.class_tree_top = new Json.Array();
710                     this.class_tree_map = new Gee.HashMap<string,Json.Object>();
711                     foreach (var cls in classes) {
712                         if(cls.alias.length < 1 || cls.alias == "this" || cls.alias == "_global_") {
713                                 continue;
714                         }
715                         bool is_new;
716                         var add =  this.class_tree_new_obj(cls.alias, cls.methods.size > 0 ? true : false,out is_new);
717                                 if (add != null) {
718                                         this.class_tree_make_parents( add);
719                                 }
720                         
721                     }
722                     
723                      return this.class_tree_top;
724                     
725                 }
726                 
727                 
728                 // in Link (js) ???
729                 string srcFileRelName(string sourceFile)
730                 {
731                         var rp = Posix.realpath(sourceFile);
732                         return rp.substring(PackerRun.singleton().opt_real_basedir.length);
733                 }
734                 string srcFileFlatName(string sourceFile)
735                 {
736                     var name = this.srcFileRelName(sourceFile);
737                     name = /\.\.?[\/]/.replace(name, name.length, 0, "");
738                     name = name.replace("/", "_").replace(":", "_") + ".html";
739                     return name;
740                 }
741                 
742                 
743                 void makeSrcFile(string sourceFile) 
744                 {
745                     // this stuff works...
746                     
747                    
748                     
749                         // this check does not appear to work according to the doc's - need to check it out.
750                
751                   
752                     var name = this.srcFileFlatName(sourceFile);
753                     
754                     GLib.debug("Write Source file : %s/src/%s", 
755                 PackerRun.singleton().opt_doc_target, name);
756                 var str = "";
757                 FileUtils.get_contents(sourceFile, out str);
758                     var pretty = PrettyPrint.toPretty(str); 
759                      var fname = PackerRun.singleton().opt_doc_target+"/src/" + name;
760                     
761                     var tmp = this.tempdir + GLib.Path.get_basename(fname);
762                     FileUtils.set_contents(
763                         tmp, 
764                         "<html><head>" +
765                         "<title>" + sourceFile + "</title>" +
766                         "<link rel=\"stylesheet\" type=\"text/css\" href=\"../../css/highlight-js.css\"/>" + 
767                         "</head><body class=\"highlightpage\">" +
768                         pretty +
769                         "</body></html>");
770                         
771                     // same content?
772                      if (GLib.FileUtils.test(fname, GLib.FileTest.EXISTS)) {
773                                 string new_data, old_data;
774                                 FileUtils.get_contents(tmp, out new_data);
775                                 FileUtils.get_contents(fname, out old_data);
776                                 if (old_data == new_data) {
777                                         GLib.File.new_for_path(tmp).delete();
778                                         return;
779                                 }
780                      }
781                         
782                 GLib.File.new_for_path(tmp).move( File.new_for_path(fname), GLib.FileCopyFlags.OVERWRITE);
783                       
784                     
785                     
786
787                 }
788         }
789                  
790 }
791   
792
793
794
795
796
797  
798
799
800
801