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