thirdparty/jsdoctoolkit/app/lib/JSDOC/SymbolSet.js
author Mario Ferraro <fadinlight@gmail.com>
Sun, 15 Nov 2009 22:12:20 +0100
changeset 3093 d1be59b6b627
parent 3041 c8f47f0b6697
permissions -rw-r--r--
GMaps related JS changed to use new google namespace. Google is going to change permanently in the future the way to load its services, so better stay safe. Also this commit shows uses of the new melange.js module. Fixes Issue 634.

/** @constructor */
JSDOC.SymbolSet = function() {
	this.init();
}

JSDOC.SymbolSet.prototype.init = function() {
	this._index = new Hash();
}

JSDOC.SymbolSet.prototype.keys = function() {
	return this._index.keys();
}

JSDOC.SymbolSet.prototype.hasSymbol = function(alias) {
	return this._index.hasKey(alias);
}

JSDOC.SymbolSet.prototype.addSymbol = function(symbol) {
	if (this.hasSymbol(symbol.alias)) {
		LOG.warn("Overwriting symbol documentation for: "+symbol.alias + ".");
	}
	this._index.set(symbol.alias, symbol);
}

JSDOC.SymbolSet.prototype.getSymbol = function(alias) {
	if (this.hasSymbol(alias)) return this._index.get(alias);
}

JSDOC.SymbolSet.prototype.getSymbolByName = function(name) {
	for (var p = this._index.first(); p; p = this._index.next()) {
		var symbol = p.value;
		if (symbol.name == name) return symbol;
	}
}

JSDOC.SymbolSet.prototype.toArray = function() {
	return this._index.values();
}

JSDOC.SymbolSet.prototype.deleteSymbol = function(alias) {
	if (!this.hasSymbol(alias)) return;
	this._index.drop(alias);
}

JSDOC.SymbolSet.prototype.renameSymbol = function(oldName, newName) {
	// todo: should check if oldname or newname already exist
	this._index.replace(oldName, newName);
	this._index.get(newName).alias = newName;
	return newName;
}

JSDOC.SymbolSet.prototype.relate = function() {
	this.resolveBorrows();
	this.resolveMemberOf();
	this.resolveAugments();
}

JSDOC.SymbolSet.prototype.resolveBorrows = function() {
	for (var p = this._index.first(); p; p = this._index.next()) {
		var symbol = p.value;
		if (symbol.is("FILE") || symbol.is("GLOBAL")) continue;
		
		var borrows = symbol.inherits;
		for (var i = 0; i < borrows.length; i++) {
		
if (/#$/.test(borrows[i].alias)) {
	LOG.warn("Attempted to borrow entire instance of "+borrows[i].alias+" but that feature is not yet implemented.");
	return;
}
			var borrowed = this.getSymbol(borrows[i].alias);
			
			if (!borrowed) {
				LOG.warn("Can't borrow undocumented "+borrows[i].alias+".");
				continue;
			}

			if (borrows[i].as == borrowed.alias) {
				var assumedName = borrowed.name.split(/([#.-])/).pop();
				borrows[i].as = symbol.name+RegExp.$1+assumedName;
				LOG.inform("Assuming borrowed as name is "+borrows[i].as+" but that feature is experimental.");
			}
			
			var borrowAsName = borrows[i].as;
			var borrowAsAlias = borrowAsName;
			if (!borrowAsName) {
				LOG.warn("Malformed @borrow, 'as' is required.");
				continue;
			}
			
			if (borrowAsName.length > symbol.alias.length && borrowAsName.indexOf(symbol.alias) == 0) {
				borrowAsName = borrowAsName.replace(borrowed.alias, "")
			}
			else {
				var joiner = "";
				if (borrowAsName.charAt(0) != "#") joiner = ".";
				borrowAsAlias = borrowed.alias + joiner + borrowAsName;
			}
			
			borrowAsName = borrowAsName.replace(/^[#.]/, "");
					
			if (this.hasSymbol(borrowAsAlias)) continue;

			var clone = borrowed.clone();
			clone.name = borrowAsName;
			clone.alias = borrowAsAlias;
			this.addSymbol(clone);
		}
	}
}

JSDOC.SymbolSet.prototype.resolveMemberOf = function() {
	for (var p = this._index.first(); p; p = this._index.next()) {
		var symbol = p.value;
		if (symbol.is("FILE") || symbol.is("GLOBAL")) continue;
		
		// the memberOf value was provided in the @memberOf tag
		else if (symbol.memberOf) {
			
			// like foo.bar is a memberOf foo
			if (symbol.alias.indexOf(symbol.memberOf) == 0) {
				var memberMatch = new RegExp("^("+symbol.memberOf+")[.#-]?(.+)$");
				var aliasParts = symbol.alias.match(memberMatch);
				
				if (aliasParts) {
					symbol.memberOf = aliasParts[1];
					symbol.name = aliasParts[2];
				}
				
				var nameParts = symbol.name.match(memberMatch);

				if (nameParts) {
					symbol.name = nameParts[2];
				}
			}
			// like bar is a memberOf foo
			else {
				var joiner = symbol.memberOf.charAt(symbol.memberOf.length-1);
				if (!/[.#-]/.test(joiner)) symbol.memberOf += ".";
				this.renameSymbol(symbol.alias, symbol.memberOf + symbol.name);
			}
		}
		// the memberOf must be calculated
		else {
			var parts = symbol.alias.match(/^(.*[.#-])([^.#-]+)$/);
			if (parts) {
				symbol.memberOf = parts[1];
				symbol.name = parts[2];				
			}
		}

		// set isStatic, isInner
		if (symbol.memberOf) {
			switch (symbol.memberOf.charAt(symbol.memberOf.length-1)) {
				case '#' :
					symbol.isStatic = false;
					symbol.isInner = false;
				break;
				case '.' :
					symbol.isStatic = true;
					symbol.isInner = false;
				break;
				case '-' :
					symbol.isStatic = false;
					symbol.isInner = true;
				break;
				default: // memberOf ends in none of the above
					symbol.isStatic = true;
				break;
			}
		}
		
		// unowned methods and fields belong to the global object
		if (!symbol.is("CONSTRUCTOR") && !symbol.isNamespace && symbol.memberOf == "") {
			symbol.memberOf = "_global_";
		}

		// clean up
		if (symbol.memberOf.match(/[.#-]$/)) {
			symbol.memberOf = symbol.memberOf.substr(0, symbol.memberOf.length-1);
		}
		// add to parent's methods or properties list
		if (symbol.memberOf) {

			var container = this.getSymbol(symbol.memberOf);
			if (!container) {
				if (JSDOC.Lang.isBuiltin(symbol.memberOf)) container = JSDOC.Parser.addBuiltin(symbol.memberOf);
				else {
					LOG.warn("Trying to document "+symbol.name +" as a member of undocumented symbol "+symbol.memberOf+".");
				}
			}
			
			if (container) container.addMember(symbol);
		}
	}
}

JSDOC.SymbolSet.prototype.resolveAugments = function() {
	for (var p = this._index.first(); p; p = this._index.next()) {
		var symbol = p.value;
		
		if (symbol.alias == "_global_" || symbol.is("FILE")) continue;
		JSDOC.SymbolSet.prototype.walk.apply(this, [symbol]);
	}
}

JSDOC.SymbolSet.prototype.walk = function(symbol) {
	var augments = symbol.augments;
	for(var i = 0; i < augments.length; i++) {
		var contributer = this.getSymbol(augments[i]);
		if (!contributer && JSDOC.Lang.isBuiltin(''+augments[i])) {
			contributer = new JSDOC.Symbol("_global_."+augments[i], [], augments[i], new JSDOC.DocComment("Built in."));
			contributer.isNamespace = true;
			contributer.srcFile = "";
			contributer.isPrivate = false;
			JSDOC.Parser.addSymbol(contributer);
		}
		
		if (contributer) {			
			if (contributer.augments.length) {
				JSDOC.SymbolSet.prototype.walk.apply(this, [contributer]);
			}
			
			symbol.inheritsFrom.push(contributer.alias);
			//if (!isUnique(symbol.inheritsFrom)) {
			//	LOG.warn("Can't resolve augments: Circular reference: "+symbol.alias+" inherits from "+contributer.alias+" more than once.");
			//}
			//else {
				var cmethods = contributer.methods;
				var cproperties = contributer.properties;
				
				for (var ci = 0, cl = cmethods.length; ci < cl; ci++) {
					if (!cmethods[ci].isStatic) symbol.inherit(cmethods[ci]);
				}
				for (var ci = 0, cl = cproperties.length; ci < cl; ci++) {
					if (!cproperties[ci].isStatic) symbol.inherit(cproperties[ci]);
				}	
			//}
		}
		else LOG.warn("Can't augment contributer: "+augments[i]+", not found.");
	}
}