PageRenderTime 67ms CodeModel.GetById 34ms app.highlight 28ms RepoModel.GetById 1ms app.codeStats 0ms

/trunk/jsdoc-toolkit/app/lib/JSDOC/Symbol.js

http://jsdoc-toolkit.googlecode.com/
JavaScript | 644 lines | 371 code | 96 blank | 177 comment | 110 complexity | 60b6f2533ec82bf689293340582596c6 MD5 | raw file
  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