7 class DocBuilder : Object
11 // extractable via JSON?
12 public string VERSION = "1.0.0" ;
14 private SymbolSet symbolSet;
16 private Packer packer;
18 public DocBuilder (Packer p)
22 GLib.debug("Roo JsDoc Toolkit started at %s ", (new GLib.DateTime.now_local()).format("Y/m/d H:i:s"));
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);
33 this.symbolSet = DocParser.symbols();
35 // this currently uses the concept of publish.js...
45 * Parse the source files.
49 private void parseSrcFiles()
54 //var useCache = PackerRun.opt_cache_dir == null ;
57 for (var i = 0, l = this.packer.files.size; i < l; i++) {
59 var srcFile = this.packer.files.get(i);
60 GLib.debug("Parsing source File: %s", srcFile);
64 cacheFile = PackerRun.opt_cache_dir + srcFile.replace("/", '_') + ".cache";
67 // disabled at present!@!!
69 if (GLib.FileUtils.test(cacheFile, GLib.FileTest.EXISTS)) {
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.
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();
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);
98 GLib.debug("reading : %s" , srcFile);
99 GLib.FileUtils.get_contents(srcFile, out src);
101 catch(GLib.FileError e) {
102 GLib.debug("Can't read source file '%s': %s", srcFile, e.message);
108 var tr = new TokenReader(this.packer);
111 tr.keepComments = true;
112 tr.sepIdents = false;
113 tr.collapseWhite = false;
117 var toks = tr.tokenize( new TextStream(src) );
118 if (PackerRun.singleton().opt_dump_tokens) {
121 //GLib.Process.exit(0);
125 var ts = new TokenStream(toks.tokens);
130 DocParser.parse(ts, srcFile);
134 var ar = DocParser.symbolsToObject(srcFile);
136 var builder = new Json.Builder ();
137 builder.begin_array ();
138 for (var i=0;i<ar.size;i++) {
140 builder.add_object_value (ar.get(i));
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;
148 generator.to_file(cacheFile);
164 var tr = new TokenReader(this.packer);
167 tr.keepComments = true;
168 tr.sepIdents = false;
169 tr.collapseWhite = false;
173 var toks = tr.tokenize( new TextStream(src));
174 if (PackerRun.opt_dump_tokens) {
177 //GLib.Process.exit(0);
181 var ts = new TokenStream(toks);
186 DocParser.parse(ts, srcFile);
190 var ar = DocParser.symbolsToObject(srcFile);
192 var builder = new Json.Builder ();
193 builder.begin_array ();
194 for (var i=0;i<ar.size;i++) {
196 builder.add_object_value (ar.get(i));
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;
204 generator.to_file(cacheFile);
221 GLib.debug("Publishing");
224 this.tempdir = GLib.DirUtils.make_tmp("roopackerXXXXXX");
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);
230 if (!FileUtils.test(PackerRun.singleton().opt_doc_target+"/symbols",FileTest.IS_DIR)) {
231 Posix.mkdir(PackerRun.singleton().opt_doc_target+"/symbols",0755);
233 if (!FileUtils.test(PackerRun.singleton().opt_doc_target+"/src",FileTest.IS_DIR)) {
234 Posix.mkdir(PackerRun.singleton().opt_doc_target+"/src",0755);
236 if (!FileUtils.test(PackerRun.singleton().opt_doc_target +"/json",FileTest.IS_DIR)) {
237 Posix.mkdir(PackerRun.singleton().opt_doc_target +"/json",0755);
240 GLib.debug("Copying files from static: %s " , PackerRun.singleton().opt_doc_template_dir);
241 // copy everything in 'static' into
243 if (PackerRun.singleton().opt_doc_template_dir != null) {
245 var iter = GLib.File.new_for_path(
246 PackerRun.singleton().opt_doc_template_dir + "/static"
247 ).enumerate_children (
249 FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
253 while ( (info = iter.next_file (null)) != null) {
254 if (info.get_file_type () == FileType.DIRECTORY) {
257 var src = File.new_for_path(info.get_name());
258 GLib.debug("Copy %s to %s/%s" ,
260 PackerRun.singleton().opt_doc_target , src.get_basename());
263 GLib.File.new_for_path(
264 PackerRun.singleton().opt_doc_target + "/" + src.get_basename()
266 GLib.FileCopyFlags.OVERWRITE
271 GLib.debug("Setting up templates");
275 var symbols = this.symbolSet.values();
277 var files = this.packer.files;
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);
284 //print(JSON.stringify(symbols,null,4));
285 var classes = new Gee.ArrayList<Symbol>();
287 foreach(var symbol in symbols) {
288 if (symbol.isaClass()) {
292 classes.sort( (a,b) => {
293 return a.alias.collate(b.alias);
296 //GLib.debug("classTemplate Process : all classes");
298 // var classesIndex = classesTemplate.process(classes); // kept in memory
300 GLib.debug("iterate classes");
302 var jsonAll = new Json.Object();
304 for (var i = 0, l = classes.size; i < l; i++) {
305 var symbol = classes.get(i);
308 GLib.debug("classTemplate Process : %s" , symbol.alias);
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");
320 jsonAll.set_object_member(symbol.alias, this.publishJSON(symbol));
324 // outptu class truee
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");
335 //GLib.debug("JSON: %s", class_tree_gen.to_data(out class_tree_l));
339 /*---- this is our 'builder' json file.. -- a full list of objects+functions */
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");
351 this.writeJson(generator,PackerRun.singleton().opt_doc_target+"/json/roodata.json");
353 //GLib.debug("JSON: %s", generator.to_data(out l));
358 GLib.debug("build index");
364 Json.Object class_to_json (Symbol cls)
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) {
375 ag.add_string_element(contributer.alias);
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());
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) :
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());
398 //ret.set_string_member("desc", cls.comment.getTagAsString(DocTagTitle.DESC));
399 /// fixme - @see ... any others..
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("optvals",p.optvalues.size > 0 ? p.optvalue_as_json_array() : new Json.Array());
412 props.add_object_element(add );
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) {
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) :
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());
448 /// fixme - @see ... any others..
451 methods.add_object_element(add);
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) {
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());
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) :
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));
480 add.set_array_member("params", m.paramsToJson());
481 add.set_array_member("returns", m.returnsToJson());
483 /// fixme - @see ... any others..
486 events.add_object_element(add);
495 * needed as Json dumps .xXXX into same directory as it writes...
497 void writeJson(Json.Generator g, string fname)
499 var tmp = this.tempdir + GLib.Path.get_basename(fname);
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();
512 GLib.File.new_for_path(tmp).move( File.new_for_path(fname), GLib.FileCopyFlags.OVERWRITE);
517 * JSON files are lookup files for the documentation
518 * - can be used by IDE's or AJAX based doc tools
522 Json.Object publishJSON (Symbol data)
524 // what we need to output to be usefull...
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);
535 var props = new Json.Array();
536 //println(cfgProperties.toSource());
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);
546 if (p.optvalues.size > 0) {
547 add.set_array_member("values",p.optvalue_as_json_array());
550 props.add_object_element(add );
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) {
561 ownEvents.sort((a,b) => {
562 return a.name.collate(b.name);
565 var events = new Json.Array();
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);
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) {
586 ownMethods.sort((a,b) => {
587 return a.name.collate(b.name);
590 var methods = new Json.Array();
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);
604 //println(props.toSource());
605 // we need to output:
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);
624 Gee.HashMap<string,Json.Object> class_tree_map;
625 Json.Array class_tree_top;
627 Json.Object? class_tree_new_obj(string name, bool is_class, out bool is_new)
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);
635 return ret; // no need to do anything
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) {
648 this.class_tree_top.add_object_element(add);
657 void class_tree_make_parents( Json.Object add)
659 var name = add.get_string_member("name");
660 var bits = name.split(".");
661 if (bits.length < 2) {
664 // got aaa.bb or aaa.bb.cc
667 for(var i=0; i < bits.length-1; i++) {
670 var pname = string.joinv(".", nn);
671 GLib.debug("Class Tree: adding to parent %s => %s", name, pname);
673 // no parent found.. make one..
675 var parent = this.class_tree_new_obj(pname, false, out is_new);
676 parent.get_array_member("cn").add_object_element(add);
678 this.class_tree_make_parents( parent);
683 Json.Array class_tree (Gee.ArrayList<Symbol> classes )
687 // produce a tree array that can be used to render the navigation.
705 to do this, we will need to create the objects in a hashmap
706 Roo.util => Json.Object
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_") {
716 var add = this.class_tree_new_obj(cls.alias, cls.methods.size > 0 ? true : false,out is_new);
718 this.class_tree_make_parents( add);
723 return this.class_tree_top;
729 string srcFileRelName(string sourceFile)
731 var rp = Posix.realpath(sourceFile);
732 return rp.substring(PackerRun.singleton().opt_real_basedir.length);
734 string srcFileFlatName(string sourceFile)
736 var name = this.srcFileRelName(sourceFile);
737 name = /\.\.?[\/]/.replace(name, name.length, 0, "");
738 name = name.replace("/", "_").replace(":", "_") + ".html";
743 void makeSrcFile(string sourceFile)
745 // this stuff works...
749 // this check does not appear to work according to the doc's - need to check it out.
752 var name = this.srcFileFlatName(sourceFile);
754 GLib.debug("Write Source file : %s/src/%s",
755 PackerRun.singleton().opt_doc_target, name);
757 FileUtils.get_contents(sourceFile, out str);
758 var pretty = PrettyPrint.toPretty(str);
759 var fname = PackerRun.singleton().opt_doc_target+"/src/" + name;
761 var tmp = this.tempdir + GLib.Path.get_basename(fname);
762 FileUtils.set_contents(
765 "<title>" + this.srcFileRelName(sourceFile) + "</title>" +
766 "<link rel=\"stylesheet\" type=\"text/css\" href=\"../../css/highlight-js.css\"/>" +
767 "</head><body class=\"highlightpage\">" +
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();
782 GLib.File.new_for_path(tmp).move( File.new_for_path(fname), GLib.FileCopyFlags.OVERWRITE);