/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

  1. if (typeof JSDOC == "undefined") JSDOC = {};
  2. /**
  3. Create a new Symbol.
  4. @class Represents a symbol in the source code.
  5. */
  6. JSDOC.Symbol = function() {
  7. this.init();
  8. if (arguments.length) this.populate.apply(this, arguments);
  9. }
  10. JSDOC.Symbol.count = 0;
  11. JSDOC.Symbol.prototype.init = function() {
  12. this._name = "";
  13. this._params = [];
  14. this.$args = [];
  15. this.addOn = "";
  16. this.alias = "";
  17. this.augments = [];
  18. this.author = "";
  19. this.classDesc = "";
  20. this.comment = {};
  21. this.defaultValue = undefined;
  22. this.deprecated = "";
  23. this.desc = "";
  24. this.example = [];
  25. this.exceptions = [];
  26. this.fires = [];
  27. this.id = JSDOC.Symbol.count++;
  28. this.inherits = [];
  29. this.inheritsFrom = [];
  30. this.isa = "OBJECT";
  31. this.isConstant = false;
  32. this.isEvent = false;
  33. this.isIgnored = false;
  34. this.isInner = false;
  35. this.isNamespace = false;
  36. this.isPrivate = false;
  37. this.isStatic = false;
  38. this.memberOf = "";
  39. this.methods = [];
  40. this.properties = [];
  41. this.requires = [];
  42. this.returns = [];
  43. this.see = [];
  44. this.since = "";
  45. this.srcFile = {};
  46. this.type = "";
  47. this.version = "";
  48. }
  49. JSDOC.Symbol.prototype.serialize = function() {
  50. var keys = [];
  51. for (var p in this) {
  52. keys.push (p);
  53. }
  54. keys = keys.sort();
  55. var out = "";
  56. for (var i in keys) {
  57. if (typeof this[keys[i]] == "function") continue;
  58. out += keys[i]+" => "+Dumper.dump(this[keys[i]])+",\n";
  59. }
  60. return "\n{\n" + out + "}\n";
  61. }
  62. JSDOC.Symbol.prototype.clone = function() {
  63. var clone = new JSDOC.Symbol();
  64. clone.populate.apply(clone, this.$args); // repopulate using the original arguments
  65. clone.srcFile = this.srcFile; // not the current srcFile, the one when the original was made
  66. return clone;
  67. }
  68. JSDOC.Symbol.prototype.__defineSetter__("name",
  69. function(n) { n = n.replace(/^_global_[.#-]/, ""); n = n.replace(/\.prototype\.?/g, '#'); this._name = n; }
  70. );
  71. JSDOC.Symbol.prototype.__defineGetter__("name",
  72. function() { return this._name; }
  73. );
  74. JSDOC.Symbol.prototype.__defineSetter__("params",
  75. function(v) {
  76. for (var i = 0, l = v.length; i < l; i++) {
  77. if (v[i].constructor != JSDOC.DocTag) { // may be a generic object parsed from signature, like {type:..., name:...}
  78. this._params[i] = new JSDOC.DocTag("param"+((v[i].type)?" {"+v[i].type+"}":"")+" "+v[i].name);
  79. }
  80. else {
  81. this._params[i] = v[i];
  82. }
  83. }
  84. }
  85. );
  86. JSDOC.Symbol.prototype.__defineGetter__("params",
  87. function() { return this._params; }
  88. );
  89. JSDOC.Symbol.prototype.getEvents = function() {
  90. var events = [];
  91. for (var i = 0, l = this.methods.length; i < l; i++) {
  92. if (this.methods[i].isEvent) {
  93. this.methods[i].name = this.methods[i].name.replace("event:", "");
  94. events.push(this.methods[i]);
  95. }
  96. }
  97. return events;
  98. }
  99. JSDOC.Symbol.prototype.getMethods = function() {
  100. var nonEvents = [];
  101. for (var i = 0, l = this.methods.length; i < l; i++) {
  102. if (!this.methods[i].isEvent) {
  103. nonEvents.push(this.methods[i]);
  104. }
  105. }
  106. return nonEvents;
  107. }
  108. JSDOC.Symbol.prototype.populate = function(
  109. /** String */ name,
  110. /** Object[] */ params,
  111. /** String */ isa,
  112. /** JSDOC.DocComment */ comment
  113. ) {
  114. this.$args = arguments;
  115. this.name = name;
  116. this.alias = this.name;
  117. this.params = params;
  118. this.isa = (isa == "VIRTUAL")? "OBJECT":isa;
  119. this.comment = comment || new JSDOC.DocComment("");
  120. this.srcFile = JSDOC.Symbol.srcFile;
  121. if (this.is("FILE") && !this.alias) this.alias = this.srcFile;
  122. this.setTags();
  123. if (typeof JSDOC.PluginManager != "undefined") {
  124. JSDOC.PluginManager.run("onSymbol", this);
  125. }
  126. }
  127. JSDOC.Symbol.prototype.setTags = function() {
  128. // @author
  129. var authors = this.comment.getTag("author");
  130. if (authors.length) {
  131. this.author = authors.map(function($){return $.desc;}).join(", ");
  132. }
  133. /*t:
  134. plan(34, "testing JSDOC.Symbol");
  135. requires("../lib/JSDOC/DocComment.js");
  136. requires("../frame/String.js");
  137. requires("../lib/JSDOC/DocTag.js");
  138. var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@author Joe Smith*"+"/"));
  139. is(sym.author, "Joe Smith", "@author tag, author is found.");
  140. */
  141. // @desc
  142. var descs = this.comment.getTag("desc");
  143. if (descs.length) {
  144. this.desc = descs.map(function($){return $.desc;}).join("\n"); // multiple descriptions are concatenated into one
  145. }
  146. /*t:
  147. var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@desc This is a description.*"+"/"));
  148. is(sym.desc, "This is a description.", "@desc tag, description is found.");
  149. */
  150. // @overview
  151. if (this.is("FILE")) {
  152. if (!this.alias) this.alias = this.srcFile;
  153. var overviews = this.comment.getTag("overview");
  154. if (overviews.length) {
  155. this.desc = [this.desc].concat(overviews.map(function($){return $.desc;})).join("\n");
  156. }
  157. }
  158. /*t:
  159. var sym = new JSDOC.Symbol("foo", [], "FILE", new JSDOC.DocComment("/**@overview This is an overview.*"+"/"));
  160. is(sym.desc, "\nThis is an overview.", "@overview tag, description is found.");
  161. */
  162. // @since
  163. var sinces = this.comment.getTag("since");
  164. if (sinces.length) {
  165. this.since = sinces.map(function($){return $.desc;}).join(", ");
  166. }
  167. /*t:
  168. var sym = new JSDOC.Symbol("foo", [], "FILE", new JSDOC.DocComment("/**@since 1.01*"+"/"));
  169. is(sym.since, "1.01", "@since tag, description is found.");
  170. */
  171. // @constant
  172. if (this.comment.getTag("constant").length) {
  173. this.isConstant = true;
  174. }
  175. /*t:
  176. var sym = new JSDOC.Symbol("foo", [], "FILE", new JSDOC.DocComment("/**@constant*"+"/"));
  177. is(sym.isConstant, true, "@constant tag, isConstant set.");
  178. */
  179. // @version
  180. var versions = this.comment.getTag("version");
  181. if (versions.length) {
  182. this.version = versions.map(function($){return $.desc;}).join(", ");
  183. }
  184. /*t:
  185. var sym = new JSDOC.Symbol("foo", [], "FILE", new JSDOC.DocComment("/**@version 2.0x*"+"/"));
  186. is(sym.version, "2.0x", "@version tag, version is found.");
  187. */
  188. // @deprecated
  189. var deprecateds = this.comment.getTag("deprecated");
  190. if (deprecateds.length) {
  191. this.deprecated = deprecateds.map(function($){return $.desc;}).join("\n");
  192. }
  193. /*t:
  194. var sym = new JSDOC.Symbol("foo", [], "FILE", new JSDOC.DocComment("/**@deprecated Use other method.*"+"/"));
  195. is(sym.deprecated, "Use other method.", "@deprecated tag, desc is found.");
  196. */
  197. // @example
  198. var examples = this.comment.getTag("example");
  199. if (examples.length) {
  200. this.example = examples.map(
  201. // trim trailing whitespace
  202. function($) {
  203. $.desc = $.desc.replace(/\s+$/, "");
  204. return $;
  205. }
  206. );
  207. }
  208. /*t:
  209. var sym = new JSDOC.Symbol("foo", [], "FILE", new JSDOC.DocComment("/**@example This\n is an example. \n*"+"/"));
  210. isnt(typeof sym.example[0], "undefined", "@example tag, creates sym.example array.");
  211. is(sym.example[0], "This\n is an example.", "@example tag, desc is found.");
  212. */
  213. // @see
  214. var sees = this.comment.getTag("see");
  215. if (sees.length) {
  216. var thisSee = this.see;
  217. sees.map(function($){thisSee.push($.desc);});
  218. }
  219. /*t:
  220. var sym = new JSDOC.Symbol("foo", [], "FILE", new JSDOC.DocComment("/**@see The other thing.*"+"/"));
  221. is(sym.see, "The other thing.", "@see tag, desc is found.");
  222. */
  223. // @class
  224. var classes = this.comment.getTag("class");
  225. if (classes.length) {
  226. this.isa = "CONSTRUCTOR";
  227. this.classDesc = classes[0].desc; // desc can't apply to the constructor as there is none.
  228. }
  229. /*t:
  230. var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@class This describes the class.*"+"/"));
  231. is(sym.isa, "CONSTRUCTOR", "@class tag, makes symbol a constructor.");
  232. is(sym.classDesc, "This describes the class.", "@class tag, class description is found.");
  233. */
  234. // @namespace
  235. var namespaces = this.comment.getTag("namespace");
  236. if (namespaces.length) {
  237. this.classDesc = namespaces[0].desc;
  238. this.isNamespace = true;
  239. }
  240. /*t:
  241. var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@namespace This describes the namespace.*"+"/"));
  242. is(sym.classDesc, "This describes the namespace.", "@namespace tag, class description is found.");
  243. */
  244. // @param
  245. var params = this.comment.getTag("param");
  246. if (params.length) {
  247. // user-defined params overwrite those with same name defined by the parser
  248. var thisParams = this.params;
  249. if (thisParams.length == 0) { // none exist yet, so just bung all these user-defined params straight in
  250. this.params = params;
  251. }
  252. else { // need to overlay these user-defined params on to existing parser-defined params
  253. for (var i = 0, l = params.length; i < l; i++) {
  254. if (thisParams[i]) {
  255. if (params[i].type) thisParams[i].type = params[i].type;
  256. thisParams[i].name = params[i].name;
  257. thisParams[i].desc = params[i].desc;
  258. thisParams[i].isOptional = params[i].isOptional;
  259. thisParams[i].defaultValue = params[i].defaultValue;
  260. }
  261. else thisParams[i] = params[i];
  262. }
  263. }
  264. }
  265. /*t:
  266. var sym = new JSDOC.Symbol("foo", [{type: "array", name: "pages"}], "FUNCTION", new JSDOC.DocComment("/**Description.*"+"/"));
  267. is(sym.params.length, 1, "parser defined param is found.");
  268. sym = new JSDOC.Symbol("foo", [], "FUNCTION", new JSDOC.DocComment("/**Description.\n@param {array} pages*"+"/"));
  269. is(sym.params.length, 1, "user defined param is found.");
  270. is(sym.params[0].type, "array", "user defined param type is found.");
  271. is(sym.params[0].name, "pages", "user defined param name is found.");
  272. sym = new JSDOC.Symbol("foo", [{type: "array", name: "pages"}], "FUNCTION", new JSDOC.DocComment("/**Description.\n@param {string} uid*"+"/"));
  273. is(sym.params.length, 1, "user defined param overwrites parser defined param.");
  274. is(sym.params[0].type, "string", "user defined param type overwrites parser defined param type.");
  275. is(sym.params[0].name, "uid", "user defined param name overwrites parser defined param name.");
  276. sym = new JSDOC.Symbol("foo", [{type: "array", name: "pages"}, {type: "number", name: "count"}], "FUNCTION", new JSDOC.DocComment("/**Description.\n@param {string} uid*"+"/"));
  277. is(sym.params.length, 2, "user defined params overlay parser defined params.");
  278. is(sym.params[1].type, "number", "user defined param type overlays parser defined param type.");
  279. is(sym.params[1].name, "count", "user defined param name overlays parser defined param name.");
  280. sym = new JSDOC.Symbol("foo", [], "FUNCTION", new JSDOC.DocComment("/**Description.\n@param {array} pages The pages description.*"+"/"));
  281. is(sym.params.length, 1, "user defined param with description is found.");
  282. is(sym.params[0].desc, "The pages description.", "user defined param description is found.");
  283. */
  284. // @constructor
  285. if (this.comment.getTag("constructor").length) {
  286. this.isa = "CONSTRUCTOR";
  287. }
  288. /*t:
  289. var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@constructor*"+"/"));
  290. is(sym.isa, "CONSTRUCTOR", "@constructor tag, makes symbol a constructor.");
  291. */
  292. // @static
  293. if (this.comment.getTag("static").length) {
  294. this.isStatic = true;
  295. if (this.isa == "CONSTRUCTOR") {
  296. this.isNamespace = true;
  297. }
  298. }
  299. /*t:
  300. var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@static\n@constructor*"+"/"));
  301. is(sym.isStatic, true, "@static tag, makes isStatic true.");
  302. is(sym.isNamespace, true, "@static and @constructor tag, makes isNamespace true.");
  303. */
  304. // @inner
  305. if (this.comment.getTag("inner").length) {
  306. this.isInner = true;
  307. this.isStatic = false;
  308. }
  309. /*t:
  310. var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@inner*"+"/"));
  311. is(sym.isStatic, false, "@inner tag, makes isStatic false.");
  312. is(sym.isInner, true, "@inner makes isInner true.");
  313. */
  314. // @name
  315. var names = this.comment.getTag("name");
  316. if (names.length) {
  317. this.name = names[0].desc;
  318. }
  319. /*t:
  320. // todo
  321. */
  322. // @field
  323. if (this.comment.getTag("field").length) {
  324. this.isa = "OBJECT";
  325. }
  326. /*t:
  327. var sym = new JSDOC.Symbol("foo", [], "FUNCTION", new JSDOC.DocComment("/**@field*"+"/"));
  328. is(sym.isa, "OBJECT", "@field tag, makes symbol an object.");
  329. */
  330. // @function
  331. if (this.comment.getTag("function").length) {
  332. this.isa = "FUNCTION";
  333. if (/event:/.test(this.alias)) this.isEvent = true;
  334. }
  335. /*t:
  336. var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@function*"+"/"));
  337. is(sym.isa, "FUNCTION", "@function tag, makes symbol a function.");
  338. */
  339. // @event
  340. var events = this.comment.getTag("event");
  341. if (events.length) {
  342. this.isa = "FUNCTION";
  343. this.isEvent = true;
  344. if (!/event:/.test(this.alias))
  345. this.alias = this.alias.replace(/^(.*[.#-])([^.#-]+)$/, "$1event:$2");
  346. }
  347. /*t:
  348. var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@event*"+"/"));
  349. is(sym.isa, "FUNCTION", "@event tag, makes symbol a function.");
  350. is(sym.isEvent, true, "@event makes isEvent true.");
  351. */
  352. // @fires
  353. var fires = this.comment.getTag("fires");
  354. if (fires.length) {
  355. for (var i = 0; i < fires.length; i++) {
  356. this.fires.push(fires[i].desc);
  357. }
  358. }
  359. /*t:
  360. // todo
  361. */
  362. // @property
  363. var properties = this.comment.getTag("property");
  364. if (properties.length) {
  365. thisProperties = this.properties;
  366. for (var i = 0; i < properties.length; i++) {
  367. var property = new JSDOC.Symbol(this.alias+"#"+properties[i].name, [], "OBJECT", new JSDOC.DocComment("/**"+properties[i].desc+"*/"));
  368. // TODO: shouldn't the following happen in the addProperty method of Symbol?
  369. if (properties[i].type) property.type = properties[i].type;
  370. if (properties[i].defaultValue) property.defaultValue = properties[i].defaultValue;
  371. this.addProperty(property);
  372. if (!JSDOC.Parser.symbols.getSymbolByName(property.name))
  373. JSDOC.Parser.addSymbol(property);
  374. }
  375. }
  376. /*t:
  377. // todo
  378. */
  379. // @return
  380. var returns = this.comment.getTag("return");
  381. if (returns.length) { // there can be many return tags in a single doclet
  382. this.returns = returns;
  383. this.type = returns.map(function($){return $.type}).join(", ");
  384. }
  385. /*t:
  386. // todo
  387. */
  388. // @exception
  389. this.exceptions = this.comment.getTag("throws");
  390. /*t:
  391. // todo
  392. */
  393. // @requires
  394. var requires = this.comment.getTag("requires");
  395. if (requires.length) {
  396. this.requires = requires.map(function($){return $.desc});
  397. }
  398. /*t:
  399. // todo
  400. */
  401. // @type
  402. var types = this.comment.getTag("type");
  403. if (types.length) {
  404. this.type = types[0].desc; //multiple type tags are ignored
  405. }
  406. /*t:
  407. // todo
  408. */
  409. // @private
  410. if (this.comment.getTag("private").length || this.isInner) {
  411. this.isPrivate = true;
  412. }
  413. // @ignore
  414. if (this.comment.getTag("ignore").length) {
  415. this.isIgnored = true;
  416. }
  417. /*t:
  418. // todo
  419. */
  420. // @inherits ... as ...
  421. var inherits = this.comment.getTag("inherits");
  422. if (inherits.length) {
  423. for (var i = 0; i < inherits.length; i++) {
  424. if (/^\s*([a-z$0-9_.#:-^]+)(?:\s+as\s+([a-z$0-9_.#:-^]+))?/i.test(inherits[i].desc)) {
  425. var inAlias = RegExp.$1;
  426. var inAs = RegExp.$2 || inAlias;
  427. if (inAlias) inAlias = inAlias.replace(/\.prototype\.?/g, "#");
  428. if (inAs) {
  429. inAs = inAs.replace(/\.prototype\.?/g, "#");
  430. inAs = inAs.replace(/^this\.?/, "#");
  431. }
  432. if (inAs.indexOf(inAlias) != 0) { //not a full namepath
  433. var joiner = ".";
  434. if (this.alias.charAt(this.alias.length-1) == "#" || inAs.charAt(0) == "#") {
  435. joiner = "";
  436. }
  437. inAs = this.alias + joiner + inAs;
  438. }
  439. }
  440. this.inherits.push({alias: inAlias, as: inAs});
  441. }
  442. }
  443. /*t:
  444. // todo
  445. */
  446. // @augments
  447. this.augments = this.comment.getTag("augments");
  448. // @default
  449. var defaults = this.comment.getTag("default");
  450. if (defaults.length) {
  451. if (this.is("OBJECT")) {
  452. this.defaultValue = defaults[0].desc;
  453. }
  454. }
  455. /*t:
  456. // todo
  457. */
  458. // @memberOf
  459. var memberOfs = this.comment.getTag("memberOf");
  460. if (memberOfs.length) {
  461. this.memberOf = memberOfs[0].desc;
  462. this.memberOf = this.memberOf.replace(/\.prototype\.?/g, "#");
  463. }
  464. /*t:
  465. // todo
  466. */
  467. // @public
  468. if (this.comment.getTag("public").length) {
  469. this.isPrivate = false;
  470. }
  471. /*t:
  472. // todo
  473. */
  474. if (JSDOC.PluginManager) {
  475. JSDOC.PluginManager.run("onSetTags", this);
  476. }
  477. }
  478. JSDOC.Symbol.prototype.is = function(what) {
  479. return this.isa === what;
  480. }
  481. JSDOC.Symbol.prototype.isBuiltin = function() {
  482. return JSDOC.Lang.isBuiltin(this.alias);
  483. }
  484. JSDOC.Symbol.prototype.setType = function(/**String*/comment, /**Boolean*/overwrite) {
  485. if (!overwrite && this.type) return;
  486. var typeComment = JSDOC.DocComment.unwrapComment(comment);
  487. this.type = typeComment;
  488. }
  489. JSDOC.Symbol.prototype.inherit = function(symbol) {
  490. if (!this.hasMember(symbol.name) && !symbol.isInner) {
  491. if (symbol.is("FUNCTION"))
  492. this.methods.push(symbol);
  493. else if (symbol.is("OBJECT"))
  494. this.properties.push(symbol);
  495. }
  496. }
  497. JSDOC.Symbol.prototype.hasMember = function(name) {
  498. return (this.hasMethod(name) || this.hasProperty(name));
  499. }
  500. JSDOC.Symbol.prototype.addMember = function(symbol) {
  501. if (symbol.is("FUNCTION")) { this.addMethod(symbol); }
  502. else if (symbol.is("OBJECT")) { this.addProperty(symbol); }
  503. }
  504. JSDOC.Symbol.prototype.hasMethod = function(name) {
  505. var thisMethods = this.methods;
  506. for (var i = 0, l = thisMethods.length; i < l; i++) {
  507. if (thisMethods[i].name == name) return true;
  508. if (thisMethods[i].alias == name) return true;
  509. }
  510. return false;
  511. }
  512. JSDOC.Symbol.prototype.addMethod = function(symbol) {
  513. var methodAlias = symbol.alias;
  514. var thisMethods = this.methods;
  515. for (var i = 0, l = thisMethods.length; i < l; i++) {
  516. if (thisMethods[i].alias == methodAlias) {
  517. thisMethods[i] = symbol; // overwriting previous method
  518. return;
  519. }
  520. }
  521. thisMethods.push(symbol); // new method with this alias
  522. }
  523. JSDOC.Symbol.prototype.hasProperty = function(name) {
  524. var thisProperties = this.properties;
  525. for (var i = 0, l = thisProperties.length; i < l; i++) {
  526. if (thisProperties[i].name == name) return true;
  527. if (thisProperties[i].alias == name) return true;
  528. }
  529. return false;
  530. }
  531. JSDOC.Symbol.prototype.addProperty = function(symbol) {
  532. var propertyAlias = symbol.alias;
  533. var thisProperties = this.properties;
  534. for (var i = 0, l = thisProperties.length; i < l; i++) {
  535. if (thisProperties[i].alias == propertyAlias) {
  536. thisProperties[i] = symbol; // overwriting previous property
  537. return;
  538. }
  539. }
  540. thisProperties.push(symbol); // new property with this alias
  541. }
  542. JSDOC.Symbol.srcFile = ""; //running reference to the current file being parsed