| 1 | /** @constructor */ |
| 2 | JSDOC.SymbolSet = function() { |
| 3 | this.init(); |
| 4 | } |
| 5 | |
| 6 | JSDOC.SymbolSet.prototype.init = function() { |
| 7 | this._index = new Hash(); |
| 8 | } |
| 9 | |
| 10 | JSDOC.SymbolSet.prototype.keys = function() { |
| 11 | return this._index.keys(); |
| 12 | } |
| 13 | |
| 14 | JSDOC.SymbolSet.prototype.hasSymbol = function(alias) { |
| 15 | return this._index.hasKey(alias); |
| 16 | } |
| 17 | |
| 18 | JSDOC.SymbolSet.prototype.addSymbol = function(symbol) { |
| 19 | if (JSDOC.opt.a && this.hasSymbol(symbol.alias)) { |
| 20 | LOG.warn("Overwriting symbol documentation for: " + symbol.alias + "."); |
| 21 | this.deleteSymbol(symbol.alias); |
| 22 | } |
| 23 | this._index.set(symbol.alias, symbol); |
| 24 | } |
| 25 | |
| 26 | JSDOC.SymbolSet.prototype.getSymbol = function(alias) { |
| 27 | if (this.hasSymbol(alias)) return this._index.get(alias); |
| 28 | } |
| 29 | |
| 30 | JSDOC.SymbolSet.prototype.getSymbolByName = function(name) { |
| 31 | for (var p = this._index.first(); p; p = this._index.next()) { |
| 32 | var symbol = p.value; |
| 33 | if (symbol.name == name) return symbol; |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | JSDOC.SymbolSet.prototype.toArray = function() { |
| 38 | return this._index.values(); |
| 39 | } |
| 40 | |
| 41 | JSDOC.SymbolSet.prototype.deleteSymbol = function(alias) { |
| 42 | if (!this.hasSymbol(alias)) return; |
| 43 | this._index.drop(alias); |
| 44 | } |
| 45 | |
| 46 | JSDOC.SymbolSet.prototype.renameSymbol = function(oldName, newName) { |
| 47 | // todo: should check if oldname or newname already exist |
| 48 | this._index.replace(oldName, newName); |
| 49 | this._index.get(newName).alias = newName; |
| 50 | return newName; |
| 51 | } |
| 52 | |
| 53 | JSDOC.SymbolSet.prototype.relate = function() { |
| 54 | this.resolveBorrows(); |
| 55 | this.resolveMemberOf(); |
| 56 | this.resolveAugments(); |
| 57 | } |
| 58 | |
| 59 | JSDOC.SymbolSet.prototype.resolveBorrows = function() { |
| 60 | for (var p = this._index.first(); p; p = this._index.next()) { |
| 61 | var symbol = p.value; |
| 62 | if (symbol.is("FILE") || symbol.is("GLOBAL")) continue; |
| 63 | |
| 64 | var borrows = symbol.inherits; |
| 65 | for (var i = 0; i < borrows.length; i++) { |
| 66 | |
| 67 | if (/#$/.test(borrows[i].alias)) { |
| 68 | LOG.warn("Attempted to borrow entire instance of "+borrows[i].alias+" but that feature is not yet implemented."); |
| 69 | return; |
| 70 | } |
| 71 | var borrowed = this.getSymbol(borrows[i].alias); |
| 72 | |
| 73 | if (!borrowed) { |
| 74 | LOG.warn("Can't borrow undocumented "+borrows[i].alias+"."); |
| 75 | continue; |
| 76 | } |
| 77 | |
| 78 | if (borrows[i].as == borrowed.alias) { |
| 79 | var assumedName = borrowed.name.split(/([#.-])/).pop(); |
| 80 | borrows[i].as = symbol.name+RegExp.$1+assumedName; |
| 81 | LOG.inform("Assuming borrowed as name is "+borrows[i].as+" but that feature is experimental."); |
| 82 | } |
| 83 | |
| 84 | var borrowAsName = borrows[i].as; |
| 85 | var borrowAsAlias = borrowAsName; |
| 86 | if (!borrowAsName) { |
| 87 | LOG.warn("Malformed @borrow, 'as' is required."); |
| 88 | continue; |
| 89 | } |
| 90 | |
| 91 | if (borrowAsName.length > symbol.alias.length && borrowAsName.indexOf(symbol.alias) == 0) { |
| 92 | borrowAsName = borrowAsName.replace(borrowed.alias, "") |
| 93 | } |
| 94 | else { |
| 95 | var joiner = ""; |
| 96 | if (borrowAsName.charAt(0) != "#") joiner = "."; |
| 97 | borrowAsAlias = borrowed.alias + joiner + borrowAsName; |
| 98 | } |
| 99 | |
| 100 | borrowAsName = borrowAsName.replace(/^[#.]/, ""); |
| 101 | |
| 102 | if (this.hasSymbol(borrowAsAlias)) continue; |
| 103 | |
| 104 | var clone = borrowed.clone(); |
| 105 | clone.name = borrowAsName; |
| 106 | clone.alias = borrowAsAlias; |
| 107 | this.addSymbol(clone); |
| 108 | } |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | JSDOC.SymbolSet.prototype.resolveMemberOf = function() { |
| 113 | for (var p = this._index.first(); p; p = this._index.next()) { |
| 114 | var symbol = p.value; |
| 115 | |
| 116 | if (symbol.is("FILE") || symbol.is("GLOBAL")) continue; |
| 117 | |
| 118 | // the memberOf value was provided in the @memberOf tag |
| 119 | else if (symbol.memberOf) { |
| 120 | // like foo.bar is a memberOf foo |
| 121 | if (symbol.alias.indexOf(symbol.memberOf) == 0) { |
| 122 | var memberMatch = new RegExp("^("+symbol.memberOf+")[.#-]?(.+)$"); |
| 123 | var aliasParts = symbol.alias.match(memberMatch); |
| 124 | |
| 125 | if (aliasParts) { |
| 126 | symbol.memberOf = aliasParts[1]; |
| 127 | symbol.name = aliasParts[2]; |
| 128 | } |
| 129 | |
| 130 | var nameParts = symbol.name.match(memberMatch); |
| 131 | |
| 132 | if (nameParts) { |
| 133 | symbol.name = nameParts[2]; |
| 134 | } |
| 135 | } |
| 136 | // like bar is a memberOf foo |
| 137 | else { |
| 138 | var joiner = symbol.memberOf.charAt(symbol.memberOf.length-1); |
| 139 | if (!/[.#-]/.test(joiner)) symbol.memberOf += "."; |
| 140 | this.renameSymbol(symbol.alias, symbol.memberOf + symbol.name); |
| 141 | } |
| 142 | } |
| 143 | // the memberOf must be calculated |
| 144 | else { |
| 145 | var parts = symbol.alias.match(/^(.*[.#-])([^.#-]+)$/); |
| 146 | |
| 147 | if (parts) { |
| 148 | symbol.memberOf = parts[1]; |
| 149 | symbol.name = parts[2]; |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | // set isStatic, isInner |
| 154 | if (symbol.memberOf) { |
| 155 | switch (symbol.memberOf.charAt(symbol.memberOf.length-1)) { |
| 156 | case '#' : |
| 157 | symbol.isStatic = false; |
| 158 | symbol.isInner = false; |
| 159 | break; |
| 160 | case '.' : |
| 161 | symbol.isStatic = true; |
| 162 | symbol.isInner = false; |
| 163 | break; |
| 164 | case '-' : |
| 165 | symbol.isStatic = false; |
| 166 | symbol.isInner = true; |
| 167 | break; |
| 168 | default: // memberOf ends in none of the above |
| 169 | symbol.isStatic = true; |
| 170 | break; |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | // unowned methods and fields belong to the global object |
| 175 | if (!symbol.is("CONSTRUCTOR") && !symbol.isNamespace && symbol.memberOf == "") { |
| 176 | symbol.memberOf = "_global_"; |
| 177 | } |
| 178 | |
| 179 | // clean up |
| 180 | if (symbol.memberOf.match(/[.#-]$/)) { |
| 181 | symbol.memberOf = symbol.memberOf.substr(0, symbol.memberOf.length-1); |
| 182 | } |
| 183 | // add to parent's methods or properties list |
| 184 | if (symbol.memberOf) { |
| 185 | |
| 186 | var container = this.getSymbol(symbol.memberOf); |
| 187 | if (!container) { |
| 188 | if (JSDOC.Lang.isBuiltin(symbol.memberOf)) container = JSDOC.Parser.addBuiltin(symbol.memberOf); |
| 189 | else { |
| 190 | LOG.warn("Trying to document "+symbol.name +" as a member of undocumented symbol "+symbol.memberOf+"."); |
| 191 | } |
| 192 | } |
| 193 | |
| 194 | if (container) container.addMember(symbol); |
| 195 | } |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | JSDOC.SymbolSet.prototype.resolveAugments = function() { |
| 200 | for (var p = this._index.first(); p; p = this._index.next()) { |
| 201 | var symbol = p.value; |
| 202 | |
| 203 | if (symbol.alias == "_global_" || symbol.is("FILE")) continue; |
| 204 | JSDOC.SymbolSet.prototype.walk.apply(this, [symbol]); |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | JSDOC.SymbolSet.prototype.walk = function(symbol) { |
| 209 | var augments = symbol.augments; |
| 210 | for(var i = 0; i < augments.length; i++) { |
| 211 | var contributer = this.getSymbol(augments[i]); |
| 212 | if (!contributer && JSDOC.Lang.isBuiltin(''+augments[i])) { |
| 213 | contributer = new JSDOC.Symbol("_global_."+augments[i], [], augments[i], new JSDOC.DocComment("Built in.")); |
| 214 | contributer.isNamespace = true; |
| 215 | contributer.srcFile = ""; |
| 216 | contributer.isPrivate = false; |
| 217 | JSDOC.Parser.addSymbol(contributer); |
| 218 | } |
| 219 | |
| 220 | if (contributer) { |
| 221 | if (contributer.augments.length) { |
| 222 | JSDOC.SymbolSet.prototype.walk.apply(this, [contributer]); |
| 223 | } |
| 224 | |
| 225 | symbol.inheritsFrom.push(contributer.alias); |
| 226 | //if (!isUnique(symbol.inheritsFrom)) { |
| 227 | // LOG.warn("Can't resolve augments: Circular reference: "+symbol.alias+" inherits from "+contributer.alias+" more than once."); |
| 228 | //} |
| 229 | //else { |
| 230 | var cmethods = contributer.methods; |
| 231 | var cproperties = contributer.properties; |
| 232 | |
| 233 | for (var ci = 0, cl = cmethods.length; ci < cl; ci++) { |
| 234 | if (!cmethods[ci].isStatic) symbol.inherit(cmethods[ci]); |
| 235 | } |
| 236 | for (var ci = 0, cl = cproperties.length; ci < cl; ci++) { |
| 237 | if (!cproperties[ci].isStatic) symbol.inherit(cproperties[ci]); |
| 238 | } |
| 239 | //} |
| 240 | } |
| 241 | else LOG.warn("Can't augment contributer: "+augments[i]+", not found."); |
| 242 | } |
| 243 | } |