object.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. "use strict";
  2. module.exports = ReflectionObject;
  3. ReflectionObject.className = "ReflectionObject";
  4. var util = require("./util");
  5. var Root; // cyclic
  6. /**
  7. * Constructs a new reflection object instance.
  8. * @classdesc Base class of all reflection objects.
  9. * @constructor
  10. * @param {string} name Object name
  11. * @param {Object.<string,*>} [options] Declared options
  12. * @abstract
  13. */
  14. function ReflectionObject(name, options) {
  15. if (!util.isString(name))
  16. throw TypeError("name must be a string");
  17. if (options && !util.isObject(options))
  18. throw TypeError("options must be an object");
  19. /**
  20. * Options.
  21. * @type {Object.<string,*>|undefined}
  22. */
  23. this.options = options; // toJSON
  24. /**
  25. * Parsed Options.
  26. * @type {Array.<Object.<string,*>>|undefined}
  27. */
  28. this.parsedOptions = null;
  29. /**
  30. * Unique name within its namespace.
  31. * @type {string}
  32. */
  33. this.name = name;
  34. /**
  35. * Parent namespace.
  36. * @type {Namespace|null}
  37. */
  38. this.parent = null;
  39. /**
  40. * Whether already resolved or not.
  41. * @type {boolean}
  42. */
  43. this.resolved = false;
  44. /**
  45. * Comment text, if any.
  46. * @type {string|null}
  47. */
  48. this.comment = null;
  49. /**
  50. * Defining file name.
  51. * @type {string|null}
  52. */
  53. this.filename = null;
  54. }
  55. Object.defineProperties(ReflectionObject.prototype, {
  56. /**
  57. * Reference to the root namespace.
  58. * @name ReflectionObject#root
  59. * @type {Root}
  60. * @readonly
  61. */
  62. root: {
  63. get: function() {
  64. var ptr = this;
  65. while (ptr.parent !== null)
  66. ptr = ptr.parent;
  67. return ptr;
  68. }
  69. },
  70. /**
  71. * Full name including leading dot.
  72. * @name ReflectionObject#fullName
  73. * @type {string}
  74. * @readonly
  75. */
  76. fullName: {
  77. get: function() {
  78. var path = [ this.name ],
  79. ptr = this.parent;
  80. while (ptr) {
  81. path.unshift(ptr.name);
  82. ptr = ptr.parent;
  83. }
  84. return path.join(".");
  85. }
  86. }
  87. });
  88. /**
  89. * Converts this reflection object to its descriptor representation.
  90. * @returns {Object.<string,*>} Descriptor
  91. * @abstract
  92. */
  93. ReflectionObject.prototype.toJSON = /* istanbul ignore next */ function toJSON() {
  94. throw Error(); // not implemented, shouldn't happen
  95. };
  96. /**
  97. * Called when this object is added to a parent.
  98. * @param {ReflectionObject} parent Parent added to
  99. * @returns {undefined}
  100. */
  101. ReflectionObject.prototype.onAdd = function onAdd(parent) {
  102. if (this.parent && this.parent !== parent)
  103. this.parent.remove(this);
  104. this.parent = parent;
  105. this.resolved = false;
  106. var root = parent.root;
  107. if (root instanceof Root)
  108. root._handleAdd(this);
  109. };
  110. /**
  111. * Called when this object is removed from a parent.
  112. * @param {ReflectionObject} parent Parent removed from
  113. * @returns {undefined}
  114. */
  115. ReflectionObject.prototype.onRemove = function onRemove(parent) {
  116. var root = parent.root;
  117. if (root instanceof Root)
  118. root._handleRemove(this);
  119. this.parent = null;
  120. this.resolved = false;
  121. };
  122. /**
  123. * Resolves this objects type references.
  124. * @returns {ReflectionObject} `this`
  125. */
  126. ReflectionObject.prototype.resolve = function resolve() {
  127. if (this.resolved)
  128. return this;
  129. if (this.root instanceof Root)
  130. this.resolved = true; // only if part of a root
  131. return this;
  132. };
  133. /**
  134. * Gets an option value.
  135. * @param {string} name Option name
  136. * @returns {*} Option value or `undefined` if not set
  137. */
  138. ReflectionObject.prototype.getOption = function getOption(name) {
  139. if (this.options)
  140. return this.options[name];
  141. return undefined;
  142. };
  143. /**
  144. * Sets an option.
  145. * @param {string} name Option name
  146. * @param {*} value Option value
  147. * @param {boolean} [ifNotSet] Sets the option only if it isn't currently set
  148. * @returns {ReflectionObject} `this`
  149. */
  150. ReflectionObject.prototype.setOption = function setOption(name, value, ifNotSet) {
  151. if (!ifNotSet || !this.options || this.options[name] === undefined)
  152. (this.options || (this.options = {}))[name] = value;
  153. return this;
  154. };
  155. /**
  156. * Sets a parsed option.
  157. * @param {string} name parsed Option name
  158. * @param {*} value Option value
  159. * @param {string} propName dot '.' delimited full path of property within the option to set. if undefined\empty, will add a new option with that value
  160. * @returns {ReflectionObject} `this`
  161. */
  162. ReflectionObject.prototype.setParsedOption = function setParsedOption(name, value, propName) {
  163. if (!this.parsedOptions) {
  164. this.parsedOptions = [];
  165. }
  166. var parsedOptions = this.parsedOptions;
  167. if (propName) {
  168. // If setting a sub property of an option then try to merge it
  169. // with an existing option
  170. var opt = parsedOptions.find(function (opt) {
  171. return Object.prototype.hasOwnProperty.call(opt, name);
  172. });
  173. if (opt) {
  174. // If we found an existing option - just merge the property value
  175. var newValue = opt[name];
  176. util.setProperty(newValue, propName, value);
  177. } else {
  178. // otherwise, create a new option, set it's property and add it to the list
  179. opt = {};
  180. opt[name] = util.setProperty({}, propName, value);
  181. parsedOptions.push(opt);
  182. }
  183. } else {
  184. // Always create a new option when setting the value of the option itself
  185. var newOpt = {};
  186. newOpt[name] = value;
  187. parsedOptions.push(newOpt);
  188. }
  189. return this;
  190. };
  191. /**
  192. * Sets multiple options.
  193. * @param {Object.<string,*>} options Options to set
  194. * @param {boolean} [ifNotSet] Sets an option only if it isn't currently set
  195. * @returns {ReflectionObject} `this`
  196. */
  197. ReflectionObject.prototype.setOptions = function setOptions(options, ifNotSet) {
  198. if (options)
  199. for (var keys = Object.keys(options), i = 0; i < keys.length; ++i)
  200. this.setOption(keys[i], options[keys[i]], ifNotSet);
  201. return this;
  202. };
  203. /**
  204. * Converts this instance to its string representation.
  205. * @returns {string} Class name[, space, full name]
  206. */
  207. ReflectionObject.prototype.toString = function toString() {
  208. var className = this.constructor.className,
  209. fullName = this.fullName;
  210. if (fullName.length)
  211. return className + " " + fullName;
  212. return className;
  213. };
  214. // Sets up cyclic dependencies (called in index-light)
  215. ReflectionObject._configure = function(Root_) {
  216. Root = Root_;
  217. };