src/jsdoc/DocBuilder.vala
[roojspacker] / src / jsdoc / DocBuilder.vala
1  
2  
3
4 namespace JSDOC 
5 {
6
7         class DocBuilder : Object 
8         {
9                 
10
11
12                 // extractable via JSON?
13                 public string VERSION = "1.0.0" ;
14                 
15                 
16                 private Packer packer;
17         
18                 public DocBuilder (Packer p) 
19                 {
20                         
21                         DocBuilder.init();
22                         GLib.debug("Roo JsDoc Toolkit started  at %s ",  (new GLib.DateTime()).format("Y/m/d H:i:s"));
23                         
24                         this.packer = p;
25         
26                     if (PackerRun.opt_tmp_dir != null && !FileUtils.test(PackerRun.opt_tmp_dir, GLib.FileTest.IS_DIR)) {   
27                         Posix.mkdir(PackerRun.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                 static bool done_init = false;
43                 
44                 static GLib.Regex regex_dotdot;
45                 
46                 static void init()
47                 {
48                         if (done_init) {
49                                 return;
50                         }
51                         // ./ or ../
52                         DocBuilder.regex_dotdot = new Regex("\\\\.\\\\.?[/]");
53
54
55                 }
56                 
57                 
58                 /**
59                  * Parse the source files.
60                  * 
61                  */
62
63                 private void parseSrcFiles() 
64                 {
65                     DocParser.init();
66                     
67                     
68                     var useCache = PackerRun.opt_cache_dir == null ;
69                     var cacheFile = "";
70                     
71                     for (var i = 0, l = this.packer.files.size; i < l; i++) {
72                         
73                         var srcFile = this.packer.files.get(i);
74                         
75                         if (useCache) {
76                         
77                                 cacheFile = PackerRun.opt_cache_dir + srcFile.replace("/", '_') + ".cache";
78                                     
79                                     //print(cacheFile);
80                                     // disabled at present!@!!
81                                     
82                                     if (GLib.FileUtils.test(cacheFile, GLib.FileTest.EXISTS)) {
83                                         // check filetime?
84                                         var cache_mt = File.new_for_path (cacheFile).queryInfo(FileAttribute.TIME_MODIFIED,
85                                                             GLib.FileQueryInfoFlags.NONE, null).
86                                                             get_modification_time();
87                                         var original_mt = File.new_for_path (sourceInfo).queryInfo(FileAttribute.TIME_MODIFIED,
88                                                             GLib.FileQueryInfoFlags.NONE, null).
89                                                             get_modification_time();
90                                         // this check does not appear to work according to the doc's - need to check it out.
91                                        
92                                         if (cache_mt > original_mt) { // cached time  > original time!
93                                             // use the cached mtimes..
94                                             GLib.debug("Read %s" , cacheFile);
95                                                         var parser = new Json.Parser();
96                                             parser.load_from_file(cacheFile);
97                                             var ar = parser.get_root ().get_array();
98
99                                             for(var i = 0;i < ar.get_length();i++) {
100                                                         var o = ar.get_object_element(i);
101                                                         var sym = JSON.gobject_from_data(typeof(Symbol), o) as Symbol;
102                                                         DocParser.symbols.add(sym);
103                                                 }
104                                                 continue;
105                                         }
106                             }
107                         }
108                         
109                         var src = "";
110                         try {
111                             GLib.debug("reading : %s" , srcFile);
112                             src = GLib.FileUtils.get_contents(srcFile);
113                         }
114                         catch(GLib.FileError e) {
115                             GLib.debug("Can't read source file '%s': %s", srcFile, e.to_string());
116                             continue;
117                         }
118
119                           
120                         
121                         var tr = new  TokenReader(this.packer);
122                                 tr.keepDocs = true;
123                                 tr.keepWhite = true;
124                                 tr.keepComments = true;
125                                 tr.sepIdents = false;
126                                 tr.collapseWhite = false;
127                                 tr.filename = src;
128                         
129
130                         var toks = tr.tokenize( new TextStream(src) );
131                         if (PackerRun.opt_dump_tokens) {
132                                         toks.dump();
133                                         return "";
134                                         //GLib.Process.exit(0);
135                                 }
136                         
137                         
138                         var ts = new TokenStream(toks);
139                     
140                     
141                     
142                                  
143                         DocParser.parse(ts, srcFile);
144                         
145                         if (useCache) {
146                                 
147                                 var ar = DocParser.symbolsToObject(srcFile);
148                                 
149                                 var builder = new Json.Builder ();
150                                 builder.begin_array ();
151                                 for (var i=0;i<ar.size;i++) {
152                                 
153                                                 builder.add_object_value (ar.get(i));
154                                         }
155                                         builder.end_array ();
156                                         Json.Generator generator = new Json.Generator ();
157                                         Json.Node root = builder.get_root ();
158                                         generator.set_root (root);
159                                         generator.pretty=  true;
160                                         generator.ident = 2;
161                                         generator.to_file(cacheFile);
162                         
163                          
164                             
165                                  }
166                     }
167                     
168                     
169                     
170                     Parser.finish();
171                 }
172                 
173
174             //var txs =
175             
176             var tr = new  TokenReader(this.packer);
177                         tr.keepDocs = true;
178                         tr.keepWhite = true;
179                         tr.keepComments = true;
180                         tr.sepIdents = false;
181                         tr.collapseWhite = false;
182                         tr.filename = src;
183             
184
185             var toks = tr.tokenize( new TextStream(src));
186             if (PackerRun.opt_dump_tokens) {
187                                 toks.dump();
188                                 return "";
189                                 //GLib.Process.exit(0);
190                         }
191             
192             
193             var ts = new TokenStream(toks);
194         
195         
196         
197                      
198             DocParser.parse(ts, srcFile);
199             
200             if (useCache) {
201                         
202                         var ar = DocParser.symbolsToObject(srcFile);
203                         
204                         var builder = new Json.Builder ();
205                 builder.begin_array ();
206                 for (var i=0;i<ar.size;i++) {
207                 
208                                         builder.add_object_value (ar.get(i));
209                                 }
210                                 builder.end_array ();
211                                 Json.Generator generator = new Json.Generator ();
212                                 Json.Node root = builder.get_root ();
213                                 generator.set_root (root);
214                                 generator.pretty=  true;
215                                 generator.ident = 2;
216                                 generator.to_file(cacheFile);
217             
218              
219                 
220     //          }
221         }
222         
223         
224         
225         Parser.finish();
226     }
227     
228      
229         
230                 void publish() 
231                 {
232                     GLib.debug("Publishing");
233                      
234                     // link!!!
235                     
236                     
237                     GLib.debug("Making directories");
238                     if (!File.isDirectory(PackerRun.opt_doc_target)) {
239                         Posix.mkdir(PackerRun.opt_doc_target,0755);
240                     }
241                     if (!File.isDirectory(PackerRun.opt_doc_target+"/symbols")) {
242                         Posix.mkdir(PackerRun.opt_doc_target+"/symbols",0755);
243                     }
244                     if (!File.isDirectory(PackerRun.opt_doc_target+"/symbols/src")) {
245                         Posix.mkdir(PackerRun.opt_doc_target+"/symbols/src",075);
246                     }
247                     if (!File.isDirectory(PackerRun.opt_doc_target +"/json")) {
248                         File.mkdir(PackerRun.opt_doc_target +"/json",0755);
249                     }
250                     
251                     GLib.debug("Copying files from static: %s " , PackerRun.opt_doc_template_dir);
252                     // copy everything in 'static' into 
253                     
254                     var iter = GLib.File.new_from_path(PackerRun.opt_doc_template_dir + "/static").enumerate_children (
255                                 "standard::*",
256                                 FileQueryInfoFlags.NOFOLLOW_SYMLINKS, 
257                                 null);
258                     
259                     
260                     while ( (info = enumerator.next_file (null)) != null) {
261                                 if (info.get_file_type () == FileType.DIRECTORY) {
262                                         continue;
263                                 } 
264                                 var src = File.new_from_path(info.get_name());
265                         GLib.debug("Copy %s to %s/%s" , info.get_name() , f,  PackerRun.opt_doc_target , src.get_basename());                   
266                         
267                                 src.copy(
268                                         GLib.File.new_from_path(PackerRun.opt_doc_target + '/' + src.get_basename()),
269                                         GLib.FileCopyFlags.OVERWRITE
270                                 );
271                         }
272         
273                     
274                     GLib.debug("Setting up templates");
275                     // used to check the details of things being linked to
276                     Link.symbolSet = this.symbolSet;// need to work out where 'symbolset will be stored/set!
277                     Link.base = "../";
278                     
279                     Link.srcFileFlatName = this.srcFileFlatName; // where set?
280                     Link.srcFileRelName = this.srcFileRelName; // where set?
281                     
282                     var classTemplate = new Template( PackerRun.opt_doc_template_dir  + "/class." + PackerRun.opt_doc_ext );
283                     var classesTemplate = new Template( PackerRun.opt_doc_template_dir+"/allclasses." + PackerRun.opt_doc_ext  );
284                     var classesindexTemplate = new Template( PackerRun.opt_doc_template_dir +"/index."  + PackerRun.opt_doc_ext );
285                     var fileindexTemplate = new Template( PackerRun.opt_doc_template_dir +"/allfiles."+ PackerRun.opt_doc_ext );
286
287                     
288                     classTemplate.symbolSet = this.symbolSet; // where?
289                     
290                     /*
291                     function hasNoParent($) {
292                         return ($.memberOf == "")
293                     }
294                     function isaFile($) {
295                         return ($.is("FILE"))
296                     }
297                     function isaClass($) {
298                         return ($.is("CONSTRUCTOR") || $.isNamespace || $.isClass); 
299                     }
300                     */
301                     
302                     
303                     
304                     
305                     
306                     
307                     
308                     
309                     
310                     var symbols = this.symbolSet.toArray();
311                     
312                     var files = this.packer.files;
313                     
314                     for (var i = 0, l = files.size; i < l; i++) {
315                         var file = files.get(i);
316                         var targetDir = PackerRun.opt_doc_target + "/symbols/src/";
317                         this.makeSrcFile(file, targetDir);
318                     }
319                     //print(JSON.stringify(symbols,null,4));
320                     var classes = new Gee.ArrayList<Symbol>();
321                     
322                     foreach(var symbol in symbols) {
323                                 if (symbol.isaClass()) { 
324                                         classes.add(symbol);
325                                 }
326                     }   
327                     classes.sort( (a,b) => {
328                                 return a.alias.collate(b.alias); 
329                         });
330                      
331                      //GLib.debug("classTemplate Process : all classes");
332                         
333                    // var classesIndex = classesTemplate.process(classes); // kept in memory
334                     
335                     GLib.debug("iterate classes");
336                     
337                     var jsonAll = new JSON.Object(); 
338                     
339                     for (var i = 0, l = classes.size; i < l; i++) {
340                         var symbol = classes.get(i);
341                         var output = "";
342                         
343                         GLib.debug("classTemplate Process : %s" , symbol.alias);
344                         
345                         
346                         
347                         
348                         FileUtils.set_contents(
349                                                 PackerRun.opt_doc_target+"/symbols/" +symbol.alias+'.' + PackerRun.opt_doc_ext ,
350                                 classTemplate.process(symbol)
351                         );
352                         
353                         jsonAll.set_object_member(symbol.alias,  this.publishJSON(symbol));
354
355                     }
356                     Json.Generator generator = new Json.Generator ();
357                         generator.set_root (jsonAll.get_node());
358                         generator.pretty=  true;
359                         generator.ident = 2;
360                         generator.to_file(PackerRun.opt_doc_target+"/json/roodata.json");
361
362                     
363                     
364                     // regenrate the index with different relative links
365                     Link.base = "";
366                     //var classesIndex = classesTemplate.process(classes);
367                     
368                     GLib.debug("build index");
369                     
370                     FileUtils.set_contents(
371                                 PackerRun.opt_doc_target +  "/index." + PackerRun.opt_doc_ext , 
372                         classesindexTemplate.process(classes)
373                     );
374                     
375                     // blank everything???? classesindexTemplate = classesIndex = classes = null;
376                     
377          
378                     /*
379                     var documentedFiles = symbols.filter(function ($) {
380                         return ($.is("FILE"))
381                     });
382                     
383                     var allFiles = [];
384                     
385                     for (var i = 0; i < files.length; i++) {
386                         allFiles.push(new  Symbol(files[i], [], "FILE", new DocComment("/** *" + "/")));
387                     }
388                     
389                     for (var i = 0; i < documentedFiles.length; i++) {
390                         var offset = files.indexOf(documentedFiles[i].alias);
391                         allFiles[offset] = documentedFiles[i];
392                     }
393                         
394                     allFiles = allFiles.sort(makeSortby("name"));
395                     GLib.debug("write files index");
396                     
397                     FileUtils.set_contents(
398                                 PackerRun.opt_doc_target + "/files." + PackerRun.opt_doc_ext , 
399                         fileindexTemplate.process(allFiles)
400                     );
401                     */
402                     
403                     
404                     
405                 }
406                 /**
407                  * JSON files are lookup files for the documentation
408                  * - can be used by IDE's or AJAX based doc tools
409                  * 
410                  * 
411                  */
412                 JSON.Object publishJSON (Symbol data)
413                 {
414                     // what we need to output to be usefull...
415                     // a) props..
416                     var cfgProperties = new GLib.ArrayList<Symbol>();
417                     if (!data.comment.getTag(DocTagTitle.SINGLETON).length) {
418                         cfgProperties = data.configToArray();
419                         cfgProperties = cfgProperties.sort((a,b) =>{
420                                 return a.alias.collate(b.alias);
421                         });
422                         
423                     }
424                     
425                     var props = new JSON.Array();; 
426                     //println(cfgProperties.toSource());
427                     
428                     for(var i =0; i < cfgProperties.size;i++) {
429                         var p = cfgPropertiesget.get(i);
430                         var add = new JSON.Object();
431                         add.set_string_member("name",p.name);
432                         add.set_string_member("type",p.type);
433                         add.set_string_member("desc",p.desc);
434                         add.set_string_member("memberOf", p.memberOf == data.alias ? '' : p.memberOf);
435                             
436                         if (p.optvalues.size) {
437                                 add.set_array_member("desc",p.optvalues_as_json_array());
438                         }
439                         props.add_object(add );
440                     }
441                     
442                     ///// --- events
443                     var ownEvents = new Gee.ArrayList<Symbol>();
444                     for(var i =0; i < data.methods.size;i++) {
445                                 var e = data.methods.get(i);
446                                 if (e.isEvent && e.comment.getTag(DocTagTitle.HIDE) == "") {
447                                         ownEvents.add(e);
448                                 }
449                         };
450                         ownEvents.sort((a,b) => {
451                                 return a.name.collate(b.name);
452                         });
453                     
454                     var events = new JSON.Array();
455                      
456                     for(var i =0; i < ownEvents.size;i++) {
457                         var m = ownEvents.get(i);
458                         var add = new JSON.Object();
459                         add.set_string_member("name",m.name.substring(1,m.name.length-1));
460                         add.set_string_member("type","function");
461                         add.set_string_member("desc",m.desc);
462                         add.set_string_member("sig", this.makeFuncSkel(m.params));
463                         events.add(add);
464                     }
465                     
466                     // methods
467                     var ownMethods = new Gee.ArrayList<Symbol>();
468                     for(var i =0; i < data.methods.size;i++) {
469                                 var e = data.methods.get(i);
470                                 if (!e.isEvent && e.comment.getTag(DocTagTitle.HIDE) == "") {
471                                         ownMethods.add(e);
472                                 }
473                         };
474                         ownMethods.sort((a,b) => {
475                                 return a.name.collate(b.name);
476                         });
477                     
478                         var methods = new JSON.Array();
479                      
480                     for(var i =0; i < ownMethods.size;i++) {
481                         var m = ownMethods.get(i);
482                         var add = new JSON.Object();
483                         add.set_string_member("name",m.name.substring(1,m.name.length-1));
484                         add.set_string_member("type","function");
485                         add.set_string_member("desc",m.desc);
486                         add.set_string_member("sig", this.makeMethodSkel(m.params));
487                         events.add(add);
488                     }
489                      
490                     //println(props.toSource());
491                     // we need to output:
492                     //classname => {
493                     //    propname => 
494                     //        type=>
495                     //        desc=>
496                     //    }
497                         var ret =  new JSON.Object();
498                         ret.set_object_member("props", props);
499                         ret.set_object_member("events", events);
500                         ret.set_object_member("methods", methods);
501                 
502                    return ret;
503                     
504                     
505                     // b) methods
506                     // c) events
507                     
508                     
509                 }
510                  
511                 // in Link (js) ???
512                 string srcFileRelName(string sourceFile)
513                 {
514                         return sourceFile.substring(PackerRun.opt_real_basedir.length+1);
515                 }
516                 string srcFileFlatName(string sourceFile)
517                 {
518                     var name = this.srcFileRelName(sourceFile);
519                     name = DocBuilder.regex_dotdot.replace(name, name.length, 0, "");
520                     name = name.replace("/", "_").replace(":", "_") + ".html";
521                     
522                 }
523                 
524                 
525                 void makeSrcFile(string sourceFile) 
526                 {
527                     // this stuff works...
528                  
529                     
530                     var name = this.srcFileFlatName(sourceFile);
531                     
532                     GLib.debug("Write Source file : %s/symbols/src/%s", opt_doc_target, name);
533                     var pretty = PrettyPrint.toPretty(FileUtils.get_contenst(sourceFile));
534                     File.write(PackerRun.opt_doc_target+"/symbols/src/" + name, 
535                         "<html><head>" +
536                         "<title>" + sourceFile + "</title>" +
537                         "<link rel=\"stylesheet\" type=\"text/css\" href=\"../../../css/highlight-js.css\"/>" + 
538                         "</head><body class=\"highlightpage\">" +
539                         pretty +
540                         "</body></html>");
541                 }
542                 /**
543                  * used by JSON output to generate a function skeleton
544                  */
545                  /*
546                 string makeFuncSkel(Gee.ArrayList<Symbol> params) {
547                     if (params.length < 0) {
548                                  return "function ()\n{\n\n}";
549                         }
550                 
551                     return "function (" +
552                         params.filter(
553                             function($) {
554                                 return $.name.indexOf(".") == -1; // don't show config params in signature
555                             }
556                         ).map( function($) { return $.name == 'this' ? '_self' : $.name; } ).join(", ") +
557                     ")\n{\n\n}";
558                 },
559                 makeMethodSkel :function(params) {
560                     if (!params) return "()";
561                     return "("  +
562                         params.filter(
563                             function($) {
564                                 return $.name.indexOf(".") == -1; // don't show config params in signature
565                             }
566                         ).map( function($) { return  $.type + " "  +(  $.name == 'this' ? '_self' : $.name ); } ).join(", ") +
567                     ")";
568                 }
569          
570     */
571         }
572 }
573   
574
575
576
577
578
579  
580
581
582
583