1 /** 2 * @class 3 * SymbolSetはSymbolオブジェクトのコレクションクラスです。 4 * 通常JsDoc Toolkitによって1つだけインスタンスが作成され、publish.js内の publish 関数は引数としてそのインスタンスを受け取ります。 5 */ 6 JSDOC.SymbolSet = function() { 7 this.init(); 8 } 9 10 /** @ignore */ 11 JSDOC.SymbolSet.prototype.init = function() { 12 this._index = new Hash(); 13 } 14 15 /** 16 * オブジェクトに登録されている全てのシンボルのエイリアスの配列を返します。 17 * @return {String} シンボルのエイリアスの配列 18 */ 19 JSDOC.SymbolSet.prototype.keys = function() { 20 return this._index.keys(); 21 } 22 23 /** 24 * シンボルのエイリアスを引数として受け取り、対応するシンボルが存在すればtrueを、しなければfalseを返します。 25 * @param {String} alias シンボルのエイリアス 26 * @return {String} シンボルが存在すればtrue、そうでなければfalse 27 */ 28 JSDOC.SymbolSet.prototype.hasSymbol = function(alias) { 29 return this._index.hasKey(alias); 30 } 31 32 /** 33 * シンボルを追加します。追加しようとしているシンボルと同じエイリアスを持つシンボルが既に登録されている場合、 34 * エイリアスと関連付けられたオブジェクト内の参照は新たに追加されたシンボルに移ります。 35 * @param {JSDOC.Symbol} symbol 追加するシンボルオブジェクト 36 */ 37 JSDOC.SymbolSet.prototype.addSymbol = function(symbol) { 38 if (this.hasSymbol(symbol.alias)) { 39 LOG.warn("Overwriting symbol documentation for: "+symbol.alias + "."); 40 } 41 this._index.set(symbol.alias, symbol); 42 } 43 44 /** 45 * シンボルのエイリアスを引数として受け取り、対応するシンボルオブジェクトを返します。 46 * @param {String} alias シンボルのエイリアス 47 * @return {JSDOC.Symbol} シンボルオブジェクト。対応するシンボルが存在しない場合の戻り値はundefinedです。 48 * 49 */ 50 JSDOC.SymbolSet.prototype.getSymbol = function(alias) { 51 if (this.hasSymbol(alias)) return this._index.get(alias); 52 } 53 54 /** 55 * (エイリアスではなく)名前でシンボルを検索し、最初に見つかったものを返します。 56 * @param {String} name シンボルの名前 57 * @return {JSDOC.Symbol} シンボルオブジェクト。対応するシンボルが存在しない場合の戻り値はundefinedです。 58 */ 59 JSDOC.SymbolSet.prototype.getSymbolByName = function(name) { 60 for (var p = this._index.first(); p; p = this._index.next()) { 61 var symbol = p.value; 62 if (symbol.name == name) return symbol; 63 } 64 } 65 66 /** 67 * このオブジェクトに保持されている全てのシンボルオブジェクトの配列を返します。 68 * @return {JSDOC.Symbol[]} シンボルオブジェクトの配列 69 */ 70 JSDOC.SymbolSet.prototype.toArray = function() { 71 return this._index.values(); 72 } 73 74 /** 75 * シンボルのエイリアスを引数として受け取り、対応するシンボルオブジェクトの登録を削除します。 76 * @param {String} alias シンボルのエイリアス 77 * @return {JSDOC.Symbol} シンボルオブジェクト。対応するシンボルが存在しない場合の戻り値はundefinedです。 78 */ 79 JSDOC.SymbolSet.prototype.deleteSymbol = function(alias) { 80 if (!this.hasSymbol(alias)) return; 81 this._index.drop(alias); 82 } 83 84 /** 85 * oldNameで登録されたシンボルのエイリアスをnewNameに置換し、新しいエイリアスを返します。 86 * このメソッドがシンボルオブジェクトのaliasプロパティそのものを書き換えてしまう点に注意してください。 87 * @param {String} oldName 変更前のエイリアス 88 * @param {String} newName 新しいエイリアス 89 * @return {String} 新しいエイリアス 90 */ 91 JSDOC.SymbolSet.prototype.renameSymbol = function(oldName, newName) { 92 // todo: should check if oldname or newname already exist 93 this._index.replace(oldName, newName); 94 this._index.get(newName).alias = newName; 95 return newName; 96 } 97 98 /** @ignore */ 99 JSDOC.SymbolSet.prototype.relate = function() { 100 this.resolveBorrows(); 101 this.resolveMemberOf(); 102 this.resolveAugments(); 103 } 104 105 /** @ignore */ 106 JSDOC.SymbolSet.prototype.resolveBorrows = function() { 107 for (var p = this._index.first(); p; p = this._index.next()) { 108 var symbol = p.value; 109 if (symbol.is("FILE") || symbol.is("GLOBAL")) continue; 110 111 var borrows = symbol.inherits; 112 for (var i = 0; i < borrows.length; i++) { 113 114 if (/#$/.test(borrows[i].alias)) { 115 LOG.warn("Attempted to borrow entire instance of "+borrows[i].alias+" but that feature is not yet implemented."); 116 return; 117 } 118 var borrowed = this.getSymbol(borrows[i].alias); 119 120 if (!borrowed) { 121 LOG.warn("Can't borrow undocumented "+borrows[i].alias+"."); 122 continue; 123 } 124 125 if (borrows[i].as == borrowed.alias) { 126 var assumedName = borrowed.name.split(/([#.-])/).pop(); 127 borrows[i].as = symbol.name+RegExp.$1+assumedName; 128 LOG.inform("Assuming borrowed as name is "+borrows[i].as+" but that feature is experimental."); 129 } 130 131 var borrowAsName = borrows[i].as; 132 var borrowAsAlias = borrowAsName; 133 if (!borrowAsName) { 134 LOG.warn("Malformed @borrow, 'as' is required."); 135 continue; 136 } 137 138 if (borrowAsName.length > symbol.alias.length && borrowAsName.indexOf(symbol.alias) == 0) { 139 borrowAsName = borrowAsName.replace(borrowed.alias, "") 140 } 141 else { 142 var joiner = ""; 143 if (borrowAsName.charAt(0) != "#") joiner = "."; 144 borrowAsAlias = borrowed.alias + joiner + borrowAsName; 145 } 146 147 borrowAsName = borrowAsName.replace(/^[#.]/, ""); 148 149 if (this.hasSymbol(borrowAsAlias)) continue; 150 151 var clone = borrowed.clone(); 152 clone.name = borrowAsName; 153 clone.alias = borrowAsAlias; 154 this.addSymbol(clone); 155 } 156 } 157 } 158 159 /** @ignore */ 160 JSDOC.SymbolSet.prototype.resolveMemberOf = function() { 161 for (var p = this._index.first(); p; p = this._index.next()) { 162 var symbol = p.value; 163 if (symbol.is("FILE") || symbol.is("GLOBAL")) continue; 164 165 // the memberOf value was provided in the @memberOf tag 166 else if (symbol.memberOf) { 167 168 // like foo.bar is a memberOf foo 169 if (symbol.alias.indexOf(symbol.memberOf) == 0) { 170 var memberMatch = new RegExp("^("+symbol.memberOf+")[.#-]?(.+)$"); 171 var aliasParts = symbol.alias.match(memberMatch); 172 173 if (aliasParts) { 174 symbol.memberOf = aliasParts[1]; 175 symbol.name = aliasParts[2]; 176 } 177 178 var nameParts = symbol.name.match(memberMatch); 179 180 if (nameParts) { 181 symbol.name = nameParts[2]; 182 } 183 } 184 // like bar is a memberOf foo 185 else { 186 var joiner = symbol.memberOf.charAt(symbol.memberOf.length-1); 187 if (!/[.#-]/.test(joiner)) symbol.memberOf += "."; 188 this.renameSymbol(symbol.alias, symbol.memberOf + symbol.name); 189 } 190 } 191 // the memberOf must be calculated 192 else { 193 var parts = symbol.alias.match(/^(.*[.#-])([^.#-]+)$/); 194 if (parts) { 195 symbol.memberOf = parts[1]; 196 symbol.name = parts[2]; 197 } 198 } 199 200 // set isStatic, isInner 201 if (symbol.memberOf) { 202 switch (symbol.memberOf.charAt(symbol.memberOf.length-1)) { 203 case '#' : 204 symbol.isStatic = false; 205 symbol.isInner = false; 206 break; 207 case '.' : 208 symbol.isStatic = true; 209 symbol.isInner = false; 210 break; 211 case '-' : 212 symbol.isStatic = false; 213 symbol.isInner = true; 214 break; 215 default: // memberOf ends in none of the above 216 symbol.isStatic = true; 217 break; 218 } 219 } 220 221 // unowned methods and fields belong to the global object 222 if (!symbol.is("CONSTRUCTOR") && !symbol.isNamespace && symbol.memberOf == "") { 223 symbol.memberOf = "_global_"; 224 } 225 226 // clean up 227 if (symbol.memberOf.match(/[.#-]$/)) { 228 symbol.memberOf = symbol.memberOf.substr(0, symbol.memberOf.length-1); 229 } 230 // add to parent's methods or properties list 231 if (symbol.memberOf) { 232 233 var container = this.getSymbol(symbol.memberOf); 234 if (!container) { 235 if (JSDOC.Lang.isBuiltin(symbol.memberOf)) container = JSDOC.Parser.addBuiltin(symbol.memberOf); 236 else { 237 LOG.warn("Trying to document "+symbol.name +" as a member of undocumented symbol "+symbol.memberOf+"."); 238 } 239 } 240 241 if (container) container.addMember(symbol); 242 } 243 } 244 } 245 246 /** @ignore */ 247 JSDOC.SymbolSet.prototype.resolveAugments = function() { 248 for (var p = this._index.first(); p; p = this._index.next()) { 249 var symbol = p.value; 250 251 if (symbol.alias == "_global_" || symbol.is("FILE")) continue; 252 JSDOC.SymbolSet.prototype.walk.apply(this, [symbol]); 253 } 254 } 255 256 /** @ignore */ 257 JSDOC.SymbolSet.prototype.walk = function(symbol) { 258 var augments = symbol.augments; 259 for(var i = 0; i < augments.length; i++) { 260 var contributer = this.getSymbol(augments[i]); 261 if (!contributer && JSDOC.Lang.isBuiltin(''+augments[i])) { 262 contributer = new JSDOC.Symbol("_global_."+augments[i], [], augments[i], new JSDOC.DocComment("Built in.")); 263 contributer.isNamespace = true; 264 contributer.srcFile = ""; 265 contributer.isPrivate = false; 266 JSDOC.Parser.addSymbol(contributer); 267 } 268 269 if (contributer) { 270 if (contributer.augments.length) { 271 JSDOC.SymbolSet.prototype.walk.apply(this, [contributer]); 272 } 273 274 symbol.inheritsFrom.push(contributer.alias); 275 //if (!isUnique(symbol.inheritsFrom)) { 276 // LOG.warn("Can't resolve augments: Circular reference: "+symbol.alias+" inherits from "+contributer.alias+" more than once."); 277 //} 278 //else { 279 var cmethods = contributer.methods; 280 var cproperties = contributer.properties; 281 282 for (var ci = 0, cl = cmethods.length; ci < cl; ci++) { 283 if (!cmethods[ci].isStatic) symbol.inherit(cmethods[ci]); 284 } 285 for (var ci = 0, cl = cproperties.length; ci < cl; ci++) { 286 if (!cproperties[ci].isStatic) symbol.inherit(cproperties[ci]); 287 } 288 //} 289 } 290 else LOG.warn("Can't augment contributer: "+augments[i]+", not found."); 291 } 292 } 293