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