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