JsTemplate/Template.js
[gnome.introspection-doc-generator] / JSDOC / Symbol.js
1 //<script type="text/javascript">
2
3 XObject         = imports.XObject.XObject;
4
5 SymbolSet       = imports.SymbolSet.SymbolSet;
6 Parser          = imports.Parser.Parser;
7 DocComment      = imports.DocComment.DocComment;
8
9 /**
10         Create a new Symbol.
11         @class Represents a symbol in the source code.
12  */
13 Symbol = XObject.define(
14     function() {
15         this.init();
16         if (arguments.length) this.populate.apply(this, arguments);
17         
18     },
19     Object,
20     {
21         
22             
23         /*
24         toQDump : function(t)
25         {
26             return toQDump(t, 'Symbol.fromDump({', '})', {});//new Symbol());
27         },
28         */
29         init : function() {
30             this.name = "";
31             this.defaultValue = "";
32             this.params = [];
33             this.$args = [];
34             this.addOn = "";
35             this.alias = "";
36             this.augments = [];
37             this.author = "";
38             this.classDesc = "";
39             this.comment = { isUserComment: false };
40             //this.defaultValue = null;
41             this.deprecated = "";
42             this.desc = "";
43             this.events = [];
44             this.example = "";
45             this.exceptions = [];
46             this.inherits = [];
47             this.inheritsFrom = [];
48             this.isa = "OBJECT"; // OBJECT//FUNCTION
49             this.isEvent = false;
50             this.isConstant = false;
51             this.isIgnored = false;
52             this.isInner = false;
53             this.isNamespace = false;
54             this.isPrivate = false;
55             this.isStatic = false;
56             this.memberOf = "";
57             this.methods = [];
58             this._name = "";
59             this._params = [];
60             this.properties = [];
61             this.requires = [];
62             this.returns = [];
63             this.see = [];
64             this.since = "";
65             this.srcFile = {};
66             this.type = "";
67             this.version = "";
68             this.childClasses = [];
69             this.cfgs = {};
70                
71         },
72
73         serialize : function() {
74             var keys = [];
75             for (var p in this) {
76                 keys.push (p);
77             }
78             keys = keys.sort();
79             
80             var out = "";
81             for (var i in keys) {
82                 if (typeof this[keys[i]] == "function") continue;
83                 out += "     " +keys[i]+" => "+
84                     (   
85                         (typeof(this[keys[i]]) != "object") ?  
86                             this[keys[i]] :
87                             "[" +typeof(this[keys[i]])+"]"
88                     ) + 
89                     ",\n";
90             }
91             return "\n{\n" + out + "}\n";
92         },
93
94         clone : function() {
95             var clone = new Symbol();
96             clone.populate.apply(clone, this.$args); // repopulate using the original arguments
97             clone.srcFile = this.srcFile; // not the current srcFile, the one when the original was made
98             return clone;
99         },
100
101
102
103
104         //__defineSetter__("name",
105         setName  : function(n) { 
106                 n = n.replace(/^_global_[.#-]/, ""); 
107                 n = n.replace(/\.prototype\.?/g, '#'); 
108                  n = n.replace(/#$/g, ''); 
109                 this._name = n;
110                 this.name = n; // real!
111             },
112         //);
113         //__defineGetter__("name",
114         getName : function() { return this._name; },
115         //);
116         //__defineSetter__("params", 
117         setParams  :function(v) {
118                 for (var i = 0, l = v.length; i < l; i++) {
119                     if (v[i].constructor != DocTag) { // may be a generic object parsed from signature, like {type:..., name:...}
120                         var ty = v[i].hasOwnProperty('type') ? v[i].type : '';
121                         this._params[i] = new DocTag(
122                             "param"+((ty)?" {"+ty+"}":"")+" "+v[i].name);
123                     }
124                     else {
125                         this._params[i] = v[i];
126                     }
127                 }
128                 this.params = this._params;
129             },
130         //);
131
132
133         //__defineGetter__("params",
134         getParams : function() { return this._params; },
135         //);
136
137         populate : function(
138                 /** String */ name,
139                 /** Object[] */ params,
140                 /** String */ isa,
141                 /** DocComment */ comment
142         ) {
143             this.$args = arguments;
144             //println("Symbol created: " + isa + ":" + name);
145             this.setName(name);
146             this.alias = this.getName();
147             this.setParams(params);
148             this.isa = (isa == "VIRTUAL")? "OBJECT":isa;
149             this.comment = comment || new DocComment("");
150             this.srcFile = Symbol.srcFile;
151             
152            
153             
154             if (this.is("FILE") && !this.alias) this.alias = this.srcFile;
155
156             this.setTags();
157             
158             //if (typeof PluginManager != "undefined") {
159             //    PluginManager.run("onSymbol", this);
160             //}
161         },
162
163         setTags : function() {
164             // @author
165             var authors = this.comment.getTag("author");
166             if (authors.length) {
167                 this.author = authors.map(function($){return $.desc;}).join(", ");
168             }
169             
170             /*~t
171                 assert("testing Symbol");
172                 
173                 requires("../lib/JSDOC/DocComment.js");
174                 requires("../frame/String.js");
175                 requires("../lib/JSDOC/DocTag.js");
176
177                 var sym = new Symbol("foo", [], "OBJECT", new DocComment("/**@author Joe Smith*"+"/"));
178                 assertEqual(sym.author, "Joe Smith", "@author tag, author is found.");
179             */
180             // @desc
181             var mth = this.comment.getTag("method");
182             if (mth.length) {
183                 this.isa = "FUNCTION";
184             }
185             // @desc
186             var descs = this.comment.getTag("desc");
187             if (descs.length) {
188                 this.desc = descs.map(function($){return $.desc;}).join("\n"); // multiple descriptions are concatenated into one
189             }
190             
191             /*~t
192                 var sym = new Symbol("foo", [], "OBJECT", new DocComment("/**@desc This is a description.*"+"/"));
193                 assertEqual(sym.desc, "This is a description.", "@desc tag, description is found.");
194             */
195             
196             // @overview
197             if (this.is("FILE")) {
198                 if (!this.alias) this.alias = this.srcFile;
199                 
200                 var overviews = this.comment.getTag("overview");
201                 if (overviews.length) {
202                     this.desc = [this.desc].concat(overviews.map(function($){return $.desc;})).join("\n");
203                 }
204             }
205             
206             /*~t
207                 var sym = new Symbol("foo", [], "FILE", new DocComment("/**@overview This is an overview.*"+"/"));
208                 assertEqual(sym.desc, "\nThis is an overview.", "@overview tag, description is found.");
209             */
210             
211             // @since
212             var sinces = this.comment.getTag("since");
213             if (sinces.length) {
214                 this.since = sinces.map(function($){return $.desc;}).join(", ");
215             }
216             
217             /*~t
218                 var sym = new Symbol("foo", [], "FILE", new DocComment("/**@since 1.01*"+"/"));
219                 assertEqual(sym.since, "1.01", "@since tag, description is found.");
220             */
221             
222             // @constant
223             if (this.comment.getTag("constant").length) {
224                 this.isConstant = true;
225                 this.isa = 'OBJECT';
226             }
227             
228             /*~t
229                 var sym = new Symbol("foo", [], "FILE", new DocComment("/**@constant*"+"/"));
230                 assertEqual(sym.isConstant, true, "@constant tag, isConstant set.");
231             */
232             
233             // @version
234             var versions = this.comment.getTag("version");
235             if (versions.length) {
236                 this.version = versions.map(function($){return $.desc;}).join(", ");
237             }
238             
239             /*~t
240                 var sym = new Symbol("foo", [], "FILE", new DocComment("/**@version 2.0x*"+"/"));
241                 assertEqual(sym.version, "2.0x", "@version tag, version is found.");
242             */
243             
244             // @deprecated
245             var deprecateds = this.comment.getTag("deprecated");
246             if (deprecateds.length) {
247                 this.deprecated = deprecateds.map(function($){return $.desc;}).join("\n");
248             }
249             
250             /*~t
251                 var sym = new Symbol("foo", [], "FILE", new DocComment("/**@deprecated Use other method.*"+"/"));
252                 assertEqual(sym.deprecated, "Use other method.", "@deprecated tag, desc is found.");
253             */
254             
255             // @example
256             var examples = this.comment.getTag("example");
257             if (examples.length) {
258                 this.example = examples[0];
259             }
260             
261             /*~t
262                 var sym = new Symbol("foo", [], "FILE", new DocComment("/**@example This\n  is an example.*"+"/"));
263                 assertEqual(sym.example, "This\n  is an example.", "@deprecated tag, desc is found.");
264             */
265             
266             // @see
267             var sees = this.comment.getTag("see");
268             if (sees.length) {
269                 var thisSee = this.see;
270                 sees.map(function($){thisSee.push($.desc);});
271             }
272             
273             /*~t
274                 var sym = new Symbol("foo", [], "FILE", new DocComment("/**@see The other thing.*"+"/"));
275                 assertEqual(sym.see, "The other thing.", "@see tag, desc is found.");
276             */
277             
278             // @class
279             var classes = this.comment.getTag("class");
280             if (classes.length) {
281                 this.isa = "CONSTRUCTOR";
282                 this.classDesc = classes[0].desc; // desc can't apply to the constructor as there is none.
283                 if (!this.classDesc) {
284                     this.classDesc = this.desc;
285                    }
286                 
287                 
288             }
289             
290             /*~t
291                 var sym = new Symbol("foo", [], "OBJECT", new DocComment("/**@class This describes the class.*"+"/"));
292                 assertEqual(sym.isa, "CONSTRUCTOR", "@class tag, makes symbol a constructor.");
293                 assertEqual(sym.classDesc, "This describes the class.", "@class tag, class description is found.");
294             */
295             
296             // @namespace
297             var namespaces = this.comment.getTag("namespace");
298             if (namespaces.length) {
299                 this.classDesc = namespaces[0].desc+"\n"+this.desc; // desc can't apply to the constructor as there is none.
300                 this.isNamespace = true;
301             }
302             
303             /*~t
304                 var sym = new Symbol("foo", [], "OBJECT", new DocComment("/**@namespace This describes the namespace.*"+"/"));
305                 assertEqual(sym.classDesc, "This describes the namespace.\n", "@namespace tag, class description is found.");
306             */
307             
308             // @param
309             var params = this.comment.getTag("param");
310             if (params.length) {
311                 // user-defined params overwrite those with same name defined by the parser
312                 var thisParams = this.getParams();
313
314                 if (thisParams.length == 0) { // none exist yet, so just bung all these user-defined params straight in
315                     this.setParams(params);
316                 }
317                 else { // need to overlay these user-defined params on to existing parser-defined params
318                     for (var i = 0, l = params.length; i < l; i++) {
319                         if (thisParams[i]) {
320                             if (params[i].type) thisParams[i].type = params[i].type;
321                             thisParams[i].name = params[i].name;
322                             thisParams[i].desc = params[i].desc;
323                             thisParams[i].isOptional = params[i].isOptional;
324                             thisParams[i].defaultValue = params[i].defaultValue;
325                         }
326                         else thisParams[i] = params[i];
327                     }
328                 }
329             }
330             
331             /*~t
332                 var sym = new Symbol("foo", [{type: "array", name: "pages"}], "FUNCTION", new DocComment("/**Description.*"+"/"));
333                 assertEqual(sym.params.length, 1, "parser defined param is found.");
334                 
335                 sym = new Symbol("foo", [], "FUNCTION", new DocComment("/**Description.\n@param {array} pages*"+"/"));
336                 assertEqual(sym.params.length, 1, "user defined param is found.");
337                 assertEqual(sym.params[0].type, "array", "user defined param type is found.");
338                 assertEqual(sym.params[0].name, "pages", "user defined param name is found.");
339                 
340                 sym = new Symbol("foo", [{type: "array", name: "pages"}], "FUNCTION", new DocComment("/**Description.\n@param {string} uid*"+"/"));
341                 assertEqual(sym.params.length, 1, "user defined param overwrites parser defined param.");
342                 assertEqual(sym.params[0].type, "string", "user defined param type overwrites parser defined param type.");
343                 assertEqual(sym.params[0].name, "uid", "user defined param name overwrites parser defined param name.");
344             
345                 sym = new Symbol("foo", [{type: "array", name: "pages"}, {type: "number", name: "count"}], "FUNCTION", new DocComment("/**Description.\n@param {string} uid*"+"/"));
346                 assertEqual(sym.params.length, 2, "user defined params  overlay parser defined params.");
347                 assertEqual(sym.params[1].type, "number", "user defined param type overlays parser defined param type.");
348                 assertEqual(sym.params[1].name, "count", "user defined param name overlays parser defined param name.");
349
350                 sym = new Symbol("foo", [], "FUNCTION", new DocComment("/**Description.\n@param {array} pages The pages description.*"+"/"));
351                 assertEqual(sym.params.length, 1, "user defined param with description is found.");
352                 assertEqual(sym.params[0].desc, "The pages description.", "user defined param description is found.");
353             */
354             
355             // @constructor
356             if (this.comment.getTag("constructor").length) {
357                 this.isa = "CONSTRUCTOR";
358             }
359             
360             /*~t
361                 var sym = new Symbol("foo", [], "OBJECT", new DocComment("/**@constructor*"+"/"));
362                 assertEqual(sym.isa, "CONSTRUCTOR", "@constructor tag, makes symbol a constructor.");
363             */
364             
365             // @static
366             if (this.comment.getTag("static").length) {
367                 this.isStatic = true;
368                 if (this.isa == "CONSTRUCTOR") {
369                     this.isNamespace = true;
370                 }
371             }
372             
373                 // @static
374             if (this.comment.getTag("singleton").length) {
375                 this.isStatic = true;
376                 //if (this.isa == "CONSTRUCTOR") {
377                 //      this.isNamespace = true;
378                 //}
379             }
380             
381             
382             
383             /*~t
384                 var sym = new Symbol("foo", [], "OBJECT", new DocComment("/**@static\n@constructor*"+"/"));
385                 assertEqual(sym.isStatic, true, "@static tag, makes isStatic true.");
386                 assertEqual(sym.isNamespace, true, "@static and @constructor tag, makes isNamespace true.");
387             */
388             
389             // @inner
390             if (this.comment.getTag("inner").length) {
391                 this.isInner = true;
392                 this.isStatic = false;
393             }
394             
395             /*~t
396                 var sym = new Symbol("foo", [], "OBJECT", new DocComment("/**@inner*"+"/"));
397                 assertEqual(sym.isStatic, false, "@inner tag, makes isStatic false.");
398                 assertEqual(sym.isInner, true, "@inner makes isInner true.");
399             */
400             
401             // @field
402             if (this.comment.getTag("field").length) {
403                 this.isa = "OBJECT";
404             }
405             
406             /*~t
407                 var sym = new Symbol("foo", [], "FUNCTION", new DocComment("/**@field*"+"/"));
408                 assertEqual(sym.isa, "OBJECT", "@field tag, makes symbol an object.");
409             */
410             
411             // @function
412             if (this.comment.getTag("function").length) {
413                 this.isa = "FUNCTION";
414             }
415             
416             // @param
417             if (this.comment.getTag("param").length && this.isa == "OBJECT" ) {
418                 // change a property to a function..
419                 this.isa = "FUNCTION";
420             }
421             
422             
423             /*~t
424                 var sym = new Symbol("foo", [], "OBJECT", new DocComment("/**@function*"+"/"));
425                 assertEqual(sym.isa, "FUNCTION", "@function tag, makes symbol a function.");
426             */
427             
428             // @event
429             var events = this.comment.getTag("event");
430             if (events.length) {
431                 this.isa = "FUNCTION";
432                 this.isEvent = true;
433             }
434             
435             /*~t
436                 var sym = new Symbol("foo", [], "OBJECT", new DocComment("/**@event*"+"/"));
437                 assertEqual(sym.isa, "FUNCTION", "@event tag, makes symbol a function.");
438                 assertEqual(sym.isEvent, true, "@event makes isEvent true.");
439             */
440             
441             // @name
442             var names = this.comment.getTag("name");
443             if (names.length) {
444                 this.setName(names[0].desc);
445             }
446             
447             /*~t
448                 // todo
449             */
450             
451             // @property
452             var properties = this.comment.getTag("property");
453             if (properties.length) {
454                 thisProperties = this.properties;
455                 for (var i = 0; i < properties.length; i++) {
456                     var property = new Symbol(this.alias+"#"+properties[i].name, [], "OBJECT", new DocComment("/**"+properties[i].desc+"\n@name "+properties[i].name+"\n@memberOf "+this.alias+"#*/"));
457                     // TODO: shouldn't the following happen in the addProperty method of Symbol?
458                     property.name = properties[i].name;
459                     property.memberOf = this.alias;
460                     if (properties[i].type) property.type = properties[i].type;
461                     if (properties[i].defaultValue) property.defaultValue = properties[i].defaultValue;
462                     this.addProperty(property);
463                     Parser.addSymbol(property);
464                 }
465             }
466             
467             // config..
468             var conf = this.comment.getTag("cfg");
469             if (conf.length) {
470                 for (var i = 0; i < conf.length; i++) {
471                     this.addConfig(conf[i]);
472                 }
473             }
474             
475             /*~t
476                 // todo
477             */
478
479             // @return
480             var returns = this.comment.getTag("return");
481             if (returns.length) { // there can be many return tags in a single doclet
482                 this.returns = returns;
483                 this.type = returns.map(function($){return $.type}).join(", ");
484             }
485             
486             /*~t
487                 // todo
488             */
489             
490             // @exception
491             this.exceptions = this.comment.getTag("throws");
492             
493             /*~t
494                 // todo
495             */
496             
497             // @requires
498             var requires = this.comment.getTag("requires");
499             if (requires.length) {
500                 this.requires = requires.map(function($){return $.desc});
501             }
502             
503             /*~t
504                 // todo
505             */
506             
507             // @type
508             var types = this.comment.getTag("type");
509             if (types.length) {
510                 this.type = types[0].desc; //multiple type tags are ignored
511             }
512             
513             /*~t
514                 // todo
515             */
516             
517             // @private
518             if (this.comment.getTag("private").length || this.isInner) {
519                 this.isPrivate = true;
520             }
521             
522             // @ignore
523             if (this.comment.getTag("ignore").length) {
524                 this.isIgnored = true;
525             }
526             
527             /*~t
528                 // todo
529             */
530             
531             // @inherits ... as ...
532             var inherits = this.comment.getTag("inherits");
533             if (inherits.length) {
534                 for (var i = 0; i < inherits.length; i++) {
535                     if (/^\s*([a-z$0-9_.#-]+)(?:\s+as\s+([a-z$0-9_.#]+))?/i.test(inherits[i].desc)) {
536                         var inAlias = RegExp.$1;
537                         var inAs = RegExp.$2 || inAlias;
538
539                         if (inAlias) inAlias = inAlias.replace(/\.prototype\.?/g, "#");
540                         
541                         if (inAs) {
542                             inAs = inAs.replace(/\.prototype\.?/g, "#");
543                             inAs = inAs.replace(/^this\.?/, "#");
544                         }
545
546                         if (inAs.indexOf(inAlias) != 0) { //not a full namepath
547                             var joiner = ".";
548                             if (this.alias.charAt(this.alias.length-1) == "#" || inAs.charAt(0) == "#") {
549                                 joiner = "";
550                             }
551                             inAs = this.alias + joiner + inAs;
552                         }
553                     }
554                     this.inherits.push({alias: inAlias, as: inAs});
555                 }
556             }
557             
558             /*~t
559                 // todo
560             */
561
562             // @augments
563             this.augments = this.comment.getTag("augments");
564             
565             //@extends - Ext
566             if (this.comment.getTag("extends")) {   
567                 this.augments = this.comment.getTag("extends");
568             }
569             
570             
571             // @default
572             var defaults = this.comment.getTag("default");
573             if (defaults.length) {
574                 if (this.is("OBJECT")) {
575                     this.defaultValue = defaults[0].desc;
576                 }
577             }
578             
579             /*~t
580                 // todo
581             */
582             
583             // @memberOf
584             var memberOfs = this.comment.getTag("memberOf");
585             if (memberOfs.length) {
586                 this.memberOf = memberOfs[0].desc;
587                 this.memberOf = this.memberOf.replace(/\.prototype\.?/g, "#");
588             }
589
590             /*~t
591                 // todo
592             */
593             
594             // @public
595             if (this.comment.getTag("public").length) {
596                 this.isPrivate = false;
597             }
598             
599             /*~t
600                 // todo
601             */
602         },
603
604         is : function(what) {
605             return this.isa === what;
606         },
607
608         isBuiltin : function() {
609             return SymbolSet.isBuiltin(this.alias);
610         },
611
612         setType : function(/**String*/comment, /**Boolean*/overwrite) {
613             if (!overwrite && this.type) return;
614             var typeComment = DocComment.unwrapComment(comment);
615             this.type = typeComment;
616         },
617
618         inherit : function(symbol) {
619             if (!this.hasMember(symbol.name) && !symbol.isInner) {
620                 if (symbol.is("FUNCTION"))
621                     this.methods.push(symbol);
622                 else if (symbol.is("OBJECT"))
623                     this.properties.push(symbol);
624             }
625         },
626
627         hasMember : function(name) {
628             return (this.hasMethod(name) || this.hasProperty(name));
629         },
630
631         addMember : function(symbol) {
632             //println("ADDMEMBER: " + this.name +  " ++ " + symbol.name);
633             
634             if (symbol.comment.getTag("cfg").length == 1) { 
635                 symbol.comment.getTag("cfg")[0].memberOf = this.alias;
636                 this.addConfig(symbol.comment.getTag("cfg")[0]);
637                 return;
638             }
639             
640             if (symbol.is("FUNCTION")) { this.addMethod(symbol); }
641             else if (symbol.is("OBJECT")) { this.addProperty(symbol); }
642         },
643
644         hasMethod : function(name) {
645             var thisMethods = this.methods;
646             for (var i = 0, l = thisMethods.length; i < l; i++) {
647                 if (thisMethods[i].name == name) return true;
648                 if (thisMethods[i].alias == name) return true;
649             }
650             return false;
651         },
652
653         addMethod : function(symbol) {
654             var methodAlias = symbol.alias;
655             var thisMethods = this.methods;
656             for (var i = 0, l = thisMethods.length; i < l; i++) {
657                 if (thisMethods[i].alias == methodAlias) {
658                     thisMethods[i] = symbol; // overwriting previous method
659                     return;
660                 }
661             }
662             thisMethods.push(symbol); // new method with this alias
663         },
664
665         hasProperty : function(name) {
666             var thisProperties = this.properties;
667             for (var i = 0, l = thisProperties.length; i < l; i++) {
668                 if (thisProperties[i].name == name) return true;
669                 if (thisProperties[i].alias == name) return true;
670             }
671             return false;
672         },
673
674         addProperty : function(symbol) {
675             var propertyAlias = symbol.alias;
676             var thisProperties = this.properties;
677             for (var i = 0, l = thisProperties.length; i < l; i++) {
678                 if (thisProperties[i].alias == propertyAlias) {
679                     thisProperties[i] = symbol; // overwriting previous property
680                     return;
681                 }
682             }
683
684             thisProperties.push(symbol); // new property with this alias
685         },
686         
687         addDocTag : function(docTag)
688         {
689             this.comment.tags.push(docTag);
690             if (docTag.title == 'cfg') {
691                 this.addConfig(docTag);
692             }
693             
694         },
695         
696         addConfig : function(docTag)
697         {
698             if (typeof(docTag['memberOf']) == 'undefined') {
699                 // remove prototype data...
700                 //var a = this.alias.split('#')[0];
701                 //docTag.memberOf = a;
702                 docTag.memberOf = this.alias;
703             }
704             if (typeof(this.cfgs[docTag.name]) == 'undefined') {
705                 this.cfgs[docTag.name] = docTag;
706             }
707             
708         },
709         configToArray: function()
710         {
711             var r = [];
712             for(var ci in this.cfgs) {
713                 // dont show hidden!!
714                 if (this.cfgs[ci].desc.match(/@hide/)) {
715                     continue;
716                 }
717                 r.push(this.cfgs[ci]); 
718                
719             }
720             return r;
721           }
722 });
723
724
725
726
727
728 Symbol.srcFile = ""; //running reference to the current file being parsed
729
730
731 Symbol.fromDump = function(t)
732 {
733     var ns = new Symbol();
734     for (var i in t) {
735         if (typeof(ns[i]) == "undefined") {
736             println("ERR:no default for Symbol:"+ i);
737         }
738         ns[i] = t[i];
739     }
740     return ns;
741 }