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