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...
37 if (PackerRun.singleton().opt_doc_dump_tree) {
40 var symbols = this.symbolSet.values();
45 //print(JSON.stringify(symbols,null,4));
46 var classes = new Gee.ArrayList<Symbol>();
48 foreach(var symbol in symbols) {
49 if (symbol.isaClass()) {
53 classes.sort( (a,b) => {
54 return a.alias.collate(b.alias);
57 var jsonAll = new Json.Object();
58 for (var i = 0, l = classes.size; i < l; i++) {
59 var symbol = classes.get(i);
61 jsonAll.set_object_member(symbol.alias, this.publishJSON(symbol));
65 var generator = new Json.Generator ();
66 var root = new Json.Node(Json.NodeType.OBJECT);
68 root.init_object(jsonAll);
69 generator.set_root (root);
70 generator.pretty= true;
74 stdout.printf("%s\n",generator.to_data(out l));
79 //GLib.debug("JSON: %s", generator.to_data(out l));
92 * Parse the source files.
96 private void parseSrcFiles()
101 //var useCache = PackerRun.opt_cache_dir == null ;
102 //var cacheFile = "";
104 for (var i = 0, l = this.packer.files.size; i < l; i++) {
106 var srcFile = this.packer.files.get(i);
107 GLib.debug("Parsing source File: %s", srcFile);
111 cacheFile = PackerRun.opt_cache_dir + srcFile.replace("/", '_') + ".cache";
114 // disabled at present!@!!
116 if (GLib.FileUtils.test(cacheFile, GLib.FileTest.EXISTS)) {
118 var cache_mt = File.new_for_path (cacheFile).queryInfo(FileAttribute.TIME_MODIFIED,
119 GLib.FileQueryInfoFlags.NONE, null).
120 get_modification_time();
121 var original_mt = File.new_for_path (sourceInfo).queryInfo(FileAttribute.TIME_MODIFIED,
122 GLib.FileQueryInfoFlags.NONE, null).
123 get_modification_time();
124 // this check does not appear to work according to the doc's - need to check it out.
126 if (cache_mt > original_mt) { // cached time > original time!
127 // use the cached mtimes..
128 GLib.debug("Read %s" , cacheFile);
129 var parser = new Json.Parser();
130 parser.load_from_file(cacheFile);
131 var ar = parser.get_root ().get_array();
133 for(var i = 0;i < ar.get_length();i++) {
134 var o = ar.get_object_element(i);
135 var sym = JSON.gobject_from_data(typeof(Symbol), o) as Symbol;
136 DocParser.symbols.add(sym);
145 GLib.debug("reading : %s" , srcFile);
146 GLib.FileUtils.get_contents(srcFile, out src);
148 catch(GLib.FileError e) {
149 GLib.debug("Can't read source file '%s': %s", srcFile, e.message);
155 var tr = new TokenReader(this.packer);
158 tr.keepComments = true;
159 tr.sepIdents = false;
160 tr.collapseWhite = false;
164 var toks = tr.tokenize( new TextStream(src) );
165 if (PackerRun.singleton().opt_dump_tokens) {
168 //GLib.Process.exit(0);
172 var ts = new TokenStream(toks.tokens);
177 DocParser.parse(ts, srcFile);
181 var ar = DocParser.symbolsToObject(srcFile);
183 var builder = new Json.Builder ();
184 builder.begin_array ();
185 for (var i=0;i<ar.size;i++) {
187 builder.add_object_value (ar.get(i));
189 builder.end_array ();
190 Json.Generator generator = new Json.Generator ();
191 Json.Node root = builder.get_root ();
192 generator.set_root (root);
193 generator.pretty= true;
195 generator.to_file(cacheFile);
211 var tr = new TokenReader(this.packer);
214 tr.keepComments = true;
215 tr.sepIdents = false;
216 tr.collapseWhite = false;
220 var toks = tr.tokenize( new TextStream(src));
221 if (PackerRun.opt_dump_tokens) {
224 //GLib.Process.exit(0);
228 var ts = new TokenStream(toks);
233 DocParser.parse(ts, srcFile);
237 var ar = DocParser.symbolsToObject(srcFile);
239 var builder = new Json.Builder ();
240 builder.begin_array ();
241 for (var i=0;i<ar.size;i++) {
243 builder.add_object_value (ar.get(i));
245 builder.end_array ();
246 Json.Generator generator = new Json.Generator ();
247 Json.Node root = builder.get_root ();
248 generator.set_root (root);
249 generator.pretty= true;
251 generator.to_file(cacheFile);
268 GLib.debug("Publishing");
271 this.tempdir = GLib.DirUtils.make_tmp("roopackerXXXXXX");
273 GLib.debug("Making directories");
274 if (!FileUtils.test (PackerRun.singleton().opt_doc_target,FileTest.IS_DIR )) {
275 Posix.mkdir(PackerRun.singleton().opt_doc_target,0755);
277 if (!FileUtils.test(PackerRun.singleton().opt_doc_target+"/symbols",FileTest.IS_DIR)) {
278 Posix.mkdir(PackerRun.singleton().opt_doc_target+"/symbols",0755);
280 if (!FileUtils.test(PackerRun.singleton().opt_doc_target+"/src",FileTest.IS_DIR)) {
281 Posix.mkdir(PackerRun.singleton().opt_doc_target+"/src",0755);
283 if (!FileUtils.test(PackerRun.singleton().opt_doc_target +"/json",FileTest.IS_DIR)) {
284 Posix.mkdir(PackerRun.singleton().opt_doc_target +"/json",0755);
287 GLib.debug("Copying files from static: %s " , PackerRun.singleton().opt_doc_template_dir);
288 // copy everything in 'static' into
290 if (PackerRun.singleton().opt_doc_template_dir != null) {
292 var iter = GLib.File.new_for_path(
293 PackerRun.singleton().opt_doc_template_dir + "/static"
294 ).enumerate_children (
296 FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
300 while ( (info = iter.next_file (null)) != null) {
301 if (info.get_file_type () == FileType.DIRECTORY) {
304 var src = File.new_for_path(info.get_name());
305 GLib.debug("Copy %s to %s/%s" ,
307 PackerRun.singleton().opt_doc_target , src.get_basename());
310 GLib.File.new_for_path(
311 PackerRun.singleton().opt_doc_target + "/" + src.get_basename()
313 GLib.FileCopyFlags.OVERWRITE
318 GLib.debug("Setting up templates");
322 var symbols = this.symbolSet.values();
324 var files = this.packer.files;
326 for (var i = 0, l = files.size; i < l; i++) {
327 var file = files.get(i);
328 // var targetDir = PackerRun.singleton().opt_doc_target + "/symbols/src/";
329 this.makeSrcFile(file);
331 //print(JSON.stringify(symbols,null,4));
332 var classes = new Gee.ArrayList<Symbol>();
334 foreach(var symbol in symbols) {
335 if (symbol.isaClass()) {
339 classes.sort( (a,b) => {
340 return a.alias.collate(b.alias);
343 //GLib.debug("classTemplate Process : all classes");
345 // var classesIndex = classesTemplate.process(classes); // kept in memory
347 GLib.debug("iterate classes");
349 var jsonAll = new Json.Object();
351 for (var i = 0, l = classes.size; i < l; i++) {
352 var symbol = classes.get(i);
355 GLib.debug("classTemplate Process : %s" , symbol.alias);
358 var class_gen = new Json.Generator ();
359 var class_root = new Json.Node(Json.NodeType.OBJECT);
360 class_root.init_object(this.class_to_json(symbol));
361 class_gen.set_root (class_root);
362 class_gen.pretty= true;
363 class_gen.indent = 2;
364 GLib.warning("writing JSON: %s", PackerRun.singleton().opt_doc_target+"/symbols/" +symbol.alias+".json");
365 this.writeJson(class_gen, PackerRun.singleton().opt_doc_target+"/symbols/" +symbol.alias+".json");
367 jsonAll.set_object_member(symbol.alias, this.publishJSON(symbol));
371 // outptu class truee
373 var class_tree_gen = new Json.Generator ();
374 var class_tree_root = new Json.Node(Json.NodeType.ARRAY);
375 class_tree_root.init_array(this.class_tree(classes));
376 class_tree_gen.set_root (class_tree_root);
377 class_tree_gen.pretty= true;
378 class_tree_gen.indent = 2;
379 GLib.warning("writing JSON: %s", PackerRun.singleton().opt_doc_target+"/tree.json");
380 this.writeJson(class_tree_gen,PackerRun.singleton().opt_doc_target+"/tree.json");
382 //GLib.debug("JSON: %s", class_tree_gen.to_data(out class_tree_l));
386 /*---- this is our 'builder' json file.. -- a full list of objects+functions */
389 var generator = new Json.Generator ();
390 var root = new Json.Node(Json.NodeType.OBJECT);
391 root.init_object(jsonAll);
392 generator.set_root (root);
393 generator.pretty= true;
394 generator.indent = 2;
395 GLib.warning("writing JSON: %s", PackerRun.singleton().opt_doc_target+"/json/roodata.json");
398 this.writeJson(generator,PackerRun.singleton().opt_doc_target+"/json/roodata.json");
400 //GLib.debug("JSON: %s", generator.to_data(out l));
405 GLib.debug("build index");
411 Json.Object class_to_json (Symbol cls)
413 var ret = new Json.Object();
414 ret.set_string_member("name", cls.alias);
415 var ag = new Json.Array();
416 ret.set_array_member("augments", ag);
417 for(var ii = 0, il = cls.augments.size; ii < il; ii++) {
418 var contributer = this.symbolSet.getSymbol(cls.augments[ii]);
419 if (contributer == null) {
422 ag.add_string_element(contributer.alias);
424 ret.set_string_member("name", cls.alias);
425 ret.set_string_member("desc", cls.desc);
426 ret.set_boolean_member("isSingleton", cls.comment.getTag(DocTagTitle.SINGLETON).size > 0);
427 ret.set_boolean_member("isStatic", cls.isa != "CONSTRUCTOR");
428 ret.set_boolean_member("isBuiltin", cls.isBuiltin());
430 // needded so that the class can fake a ctor..
431 ret.set_string_member("memberOf", cls.name);
432 ret.set_string_member("example", cls.comment.getTagAsString(DocTagTitle.EXAMPLE));
433 ret.set_string_member("deprecated", // as depricated is used as a flag...
434 cls.comment.getTag(DocTagTitle.DEPRECATED).size > 0 ?
435 "This has been deprecated: "+ cls.comment.getTagAsString(DocTagTitle.DEPRECATED) :
437 ret.set_string_member("since", cls.comment.getTagAsString(DocTagTitle.SINCE));
438 ret.set_string_member("see", cls.comment.getTagAsString(DocTagTitle.SINCE));
439 // not supported or used yet?
440 //add.set_string_member("exceptions", m.comment.getTagAsString(DocTagTitle.EXCEPTIONS));
441 //add.set_string_member("requires", m.comment.getTagAsString(DocTagTitle.REQUIRES));
442 ret.set_array_member("params", cls.paramsToJson());
443 ret.set_array_member("returns", new Json.Array());
445 //ret.set_string_member("desc", cls.comment.getTagAsString(DocTagTitle.DESC));
446 /// fixme - @see ... any others..
448 var props = new Json.Array();
449 ret.set_array_member("config", props);
450 var cfgProperties = cls.configToArray();
451 for(var i =0; i < cfgProperties.size;i++) {
452 var p = cfgProperties.get(i);
453 var add = new Json.Object();
454 add.set_string_member("name",p.name);
455 add.set_string_member("type",p.type);
456 add.set_string_member("desc",p.desc);
457 add.set_string_member("memberOf", p.memberOf);
458 add.set_array_member("optvals",p.optvalues.size > 0 ? p.optvalue_as_json_array() : new Json.Array());
459 props.add_object_element(add );
465 var methods = new Json.Array();
466 ret.set_array_member("methods", methods);
467 foreach(var m in cls.methods) {
468 if (m.isEvent || m.isIgnored) {
472 var add = new Json.Object();
473 add.set_string_member("name",m.name);
474 //add.set_string_member("type","function");
475 add.set_string_member("desc",m.desc);
476 //add.set_string_member("sig", m.makeMethodSkel());
477 add.set_boolean_member("isStatic", m.isStatic);
478 add.set_boolean_member("isConstructor", m.isa == "CONSTRUCTOR");
479 add.set_boolean_member("isPrivate", m.isPrivate);
480 //add.set_string_member("instanceOf", m.comment.getTagAsString(DocTagTitle.INSTANCEOF));
481 add.set_string_member("memberOf", m.memberOf);
482 add.set_string_member("example", m.comment.getTagAsString(DocTagTitle.EXAMPLE));
483 add.set_string_member("deprecated", // as depricated is used as a flag...
484 m.comment.getTag(DocTagTitle.DEPRECATED).size > 0 ?
485 "This has been deprecated: "+ m.comment.getTagAsString(DocTagTitle.DEPRECATED) :
487 add.set_string_member("since", m.comment.getTagAsString(DocTagTitle.SINCE));
488 add.set_string_member("see", m.comment.getTagAsString(DocTagTitle.SINCE));
489 // not supported or used yet?
490 //add.set_string_member("exceptions", m.comment.getTagAsString(DocTagTitle.EXCEPTIONS));
491 //add.set_string_member("requires", m.comment.getTagAsString(DocTagTitle.REQUIRES));
492 add.set_array_member("params", m.paramsToJson());
493 add.set_array_member("returns", m.returnsToJson());
495 /// fixme - @see ... any others..
498 methods.add_object_element(add);
502 var events = new Json.Array();
503 ret.set_array_member("events", events);
504 foreach(var m in cls.methods) {
505 if (!m.isEvent || m.isIgnored) {
509 var add = new Json.Object();
510 add.set_string_member("name",m.name.substring(1)); // all prefixed with '*'...
511 //add.set_string_member("type","function");
512 add.set_string_member("desc",m.desc);
513 //add.set_string_member("sig", m.makeMethodSkel());
515 add.set_string_member("memberOf", m.memberOf);
516 add.set_string_member("example", m.comment.getTagAsString(DocTagTitle.EXAMPLE));
517 add.set_string_member("deprecated", // as depricated is used as a flag...
518 m.comment.getTag(DocTagTitle.DEPRECATED).size > 0 ?
519 "This has been deprecated: "+ m.comment.getTagAsString(DocTagTitle.DEPRECATED) :
521 add.set_string_member("since", m.comment.getTagAsString(DocTagTitle.SINCE));
522 add.set_string_member("see", m.comment.getTagAsString(DocTagTitle.SINCE));
523 // not supported or used yet?
524 //add.set_string_member("exceptions", m.comment.getTagAsString(DocTagTitle.EXCEPTIONS));
525 //add.set_string_member("requires", m.comment.getTagAsString(DocTagTitle.REQUIRES));
527 add.set_array_member("params", m.paramsToJson());
528 add.set_array_member("returns", m.returnsToJson());
530 /// fixme - @see ... any others..
533 events.add_object_element(add);
542 * needed as Json dumps .xXXX into same directory as it writes...
544 void writeJson(Json.Generator g, string fname)
546 var tmp = this.tempdir + GLib.Path.get_basename(fname);
549 if (GLib.FileUtils.test(fname, GLib.FileTest.EXISTS)) {
550 string new_data, old_data;
551 FileUtils.get_contents(tmp, out new_data);
552 FileUtils.get_contents(fname, out old_data);
553 if (old_data == new_data) {
554 GLib.File.new_for_path(tmp).delete();
559 GLib.File.new_for_path(tmp).move( File.new_for_path(fname), GLib.FileCopyFlags.OVERWRITE);
564 * JSON files are lookup files for the documentation
565 * - can be used by IDE's or AJAX based doc tools
569 Json.Object publishJSON (Symbol data)
571 // what we need to output to be usefull...
573 var cfgProperties = new Gee.ArrayList<DocTag>();
574 if (data.comment.getTag(DocTagTitle.SINGLETON).size < 1) {
575 cfgProperties = data.configToArray();
576 cfgProperties.sort((a,b) =>{
577 return a.name.collate(b.name);
582 var props = new Json.Array();
583 //println(cfgProperties.toSource());
585 for(var i =0; i < cfgProperties.size;i++) {
586 var p = cfgProperties.get(i);
587 var add = new Json.Object();
588 add.set_string_member("name",p.name);
589 add.set_string_member("type",p.type);
590 add.set_string_member("desc",p.desc);
591 add.set_string_member("memberOf", p.memberOf == data.alias ? "" : p.memberOf);
593 if (p.optvalues.size > 0) {
594 add.set_array_member("optvals",p.optvalue_as_json_array());
597 props.add_object_element(add );
601 var ownEvents = new Gee.ArrayList<Symbol>();
602 for(var i =0; i < data.methods.size;i++) {
603 var e = data.methods.get(i);
604 if (e.isEvent && !e.isIgnored) {
608 ownEvents.sort((a,b) => {
609 return a.name.collate(b.name);
612 var events = new Json.Array();
614 for(var i =0; i < ownEvents.size;i++) {
615 var m = ownEvents.get(i);
616 var add = new Json.Object();
617 add.set_string_member("name",m.name.substring(1,-1)); // remove'*' on events..
618 add.set_string_member("type","function");
619 add.set_string_member("desc",m.desc);
620 add.set_string_member("sig", m.makeFuncSkel());
621 add.set_string_member("memberOf", m.memberOf == data.alias ? "" : m.memberOf);
622 events.add_object_element(add);
626 var ownMethods = new Gee.ArrayList<Symbol>();
627 for(var i =0; i < data.methods.size;i++) {
628 var e = data.methods.get(i);
629 if (!e.isEvent && !e.isIgnored) {
633 ownMethods.sort((a,b) => {
634 return a.name.collate(b.name);
637 var methods = new Json.Array();
639 for(var i =0; i < ownMethods.size;i++) {
640 var m = ownMethods.get(i);
641 var add = new Json.Object();
642 add.set_string_member("name",m.name);
643 add.set_string_member("type","function");
644 add.set_string_member("desc",m.desc);
645 add.set_string_member("sig", m.makeMethodSkel());
646 add.set_boolean_member("static", m.isStatic);
647 add.set_string_member("memberOf", m.memberOf == data.alias ? "" : m.memberOf);
648 methods.add_object_element(add);
651 //println(props.toSource());
652 // we need to output:
658 var ret = new Json.Object();
659 ret.set_array_member("props", props);
660 ret.set_array_member("events", events);
661 ret.set_array_member("methods", methods);
671 Gee.HashMap<string,Json.Object> class_tree_map;
672 Json.Array class_tree_top;
674 Json.Object? class_tree_new_obj(string name, bool is_class, out bool is_new)
676 if (this.class_tree_map.has_key(name)) {
677 var ret = this.class_tree_map.get(name);
678 if (!ret.get_boolean_member("is_class") && is_class) {
679 ret.set_boolean_member("is_class", is_class);
682 return ret; // no need to do anything
686 GLib.debug("Class Tree: new object %s", name);
687 var add = new Json.Object();
688 add.set_string_member("name", name);
689 add.set_array_member("cn", new Json.Array());
690 add.set_boolean_member("is_class", is_class);
691 this.class_tree_map.set(name, add);
692 var bits = name.split(".");
693 if (bits.length == 1) {
695 this.class_tree_top.add_object_element(add);
704 void class_tree_make_parents( Json.Object add)
706 var name = add.get_string_member("name");
707 var bits = name.split(".");
708 if (bits.length < 2) {
711 // got aaa.bb or aaa.bb.cc
714 for(var i=0; i < bits.length-1; i++) {
717 var pname = string.joinv(".", nn);
718 GLib.debug("Class Tree: adding to parent %s => %s", name, pname);
720 // no parent found.. make one..
722 var parent = this.class_tree_new_obj(pname, false, out is_new);
723 parent.get_array_member("cn").add_object_element(add);
725 this.class_tree_make_parents( parent);
730 Json.Array class_tree (Gee.ArrayList<Symbol> classes )
734 // produce a tree array that can be used to render the navigation.
752 to do this, we will need to create the objects in a hashmap
753 Roo.util => Json.Object
756 this.class_tree_top = new Json.Array();
757 this.class_tree_map = new Gee.HashMap<string,Json.Object>();
758 foreach (var cls in classes) {
759 if(cls.alias.length < 1 || cls.alias == "this" || cls.alias == "_global_") {
763 var add = this.class_tree_new_obj(cls.alias, cls.methods.size > 0 ? true : false,out is_new);
765 this.class_tree_make_parents( add);
770 return this.class_tree_top;
776 string srcFileRelName(string sourceFile)
778 var rp = Posix.realpath(sourceFile);
779 return rp.substring(PackerRun.singleton().opt_real_basedir.length);
781 string srcFileFlatName(string sourceFile)
783 var name = this.srcFileRelName(sourceFile);
784 name = /\.\.?[\/]/.replace(name, name.length, 0, "");
785 name = name.replace("/", "_").replace(":", "_") + ".html";
790 void makeSrcFile(string sourceFile)
792 // this stuff works...
796 // this check does not appear to work according to the doc's - need to check it out.
799 var name = this.srcFileFlatName(sourceFile);
801 GLib.debug("Write Source file : %s/src/%s",
802 PackerRun.singleton().opt_doc_target, name);
804 FileUtils.get_contents(sourceFile, out str);
805 var pretty = PrettyPrint.toPretty(str);
806 var fname = PackerRun.singleton().opt_doc_target+"/src/" + name;
808 var tmp = this.tempdir + GLib.Path.get_basename(fname);
809 FileUtils.set_contents(
812 "<title>" + this.srcFileRelName(sourceFile) + "</title>" +
813 "<link rel=\"stylesheet\" type=\"text/css\" href=\"../../css/highlight-js.css\"/>" +
814 "</head><body class=\"highlightpage\">" +
819 if (GLib.FileUtils.test(fname, GLib.FileTest.EXISTS)) {
820 string new_data, old_data;
821 FileUtils.get_contents(tmp, out new_data);
822 FileUtils.get_contents(fname, out old_data);
823 if (old_data == new_data) {
824 GLib.File.new_for_path(tmp).delete();
829 GLib.File.new_for_path(tmp).move( File.new_for_path(fname), GLib.FileCopyFlags.OVERWRITE);