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