| "use strict"; |
| module.exports = ReflectionObject; |
| |
| ReflectionObject.className = "ReflectionObject"; |
| |
| var util = require("./util"); |
| |
| var Root; // cyclic |
| |
| /** |
| * Constructs a new reflection object instance. |
| * @classdesc Base class of all reflection objects. |
| * @constructor |
| * @param {string} name Object name |
| * @param {Object.<string,*>} [options] Declared options |
| * @abstract |
| */ |
| function ReflectionObject(name, options) { |
| |
| if (!util.isString(name)) |
| throw TypeError("name must be a string"); |
| |
| if (options && !util.isObject(options)) |
| throw TypeError("options must be an object"); |
| |
| /** |
| * Options. |
| * @type {Object.<string,*>|undefined} |
| */ |
| this.options = options; // toJSON |
| |
| /** |
| * Parsed Options. |
| * @type {Array.<Object.<string,*>>|undefined} |
| */ |
| this.parsedOptions = null; |
| |
| /** |
| * Unique name within its namespace. |
| * @type {string} |
| */ |
| this.name = name; |
| |
| /** |
| * Parent namespace. |
| * @type {Namespace|null} |
| */ |
| this.parent = null; |
| |
| /** |
| * Whether already resolved or not. |
| * @type {boolean} |
| */ |
| this.resolved = false; |
| |
| /** |
| * Comment text, if any. |
| * @type {string|null} |
| */ |
| this.comment = null; |
| |
| /** |
| * Defining file name. |
| * @type {string|null} |
| */ |
| this.filename = null; |
| } |
| |
| Object.defineProperties(ReflectionObject.prototype, { |
| |
| /** |
| * Reference to the root namespace. |
| * @name ReflectionObject#root |
| * @type {Root} |
| * @readonly |
| */ |
| root: { |
| get: function() { |
| var ptr = this; |
| while (ptr.parent !== null) |
| ptr = ptr.parent; |
| return ptr; |
| } |
| }, |
| |
| /** |
| * Full name including leading dot. |
| * @name ReflectionObject#fullName |
| * @type {string} |
| * @readonly |
| */ |
| fullName: { |
| get: function() { |
| var path = [ this.name ], |
| ptr = this.parent; |
| while (ptr) { |
| path.unshift(ptr.name); |
| ptr = ptr.parent; |
| } |
| return path.join("."); |
| } |
| } |
| }); |
| |
| /** |
| * Converts this reflection object to its descriptor representation. |
| * @returns {Object.<string,*>} Descriptor |
| * @abstract |
| */ |
| ReflectionObject.prototype.toJSON = /* istanbul ignore next */ function toJSON() { |
| throw Error(); // not implemented, shouldn't happen |
| }; |
| |
| /** |
| * Called when this object is added to a parent. |
| * @param {ReflectionObject} parent Parent added to |
| * @returns {undefined} |
| */ |
| ReflectionObject.prototype.onAdd = function onAdd(parent) { |
| if (this.parent && this.parent !== parent) |
| this.parent.remove(this); |
| this.parent = parent; |
| this.resolved = false; |
| var root = parent.root; |
| if (root instanceof Root) |
| root._handleAdd(this); |
| }; |
| |
| /** |
| * Called when this object is removed from a parent. |
| * @param {ReflectionObject} parent Parent removed from |
| * @returns {undefined} |
| */ |
| ReflectionObject.prototype.onRemove = function onRemove(parent) { |
| var root = parent.root; |
| if (root instanceof Root) |
| root._handleRemove(this); |
| this.parent = null; |
| this.resolved = false; |
| }; |
| |
| /** |
| * Resolves this objects type references. |
| * @returns {ReflectionObject} `this` |
| */ |
| ReflectionObject.prototype.resolve = function resolve() { |
| if (this.resolved) |
| return this; |
| if (this.root instanceof Root) |
| this.resolved = true; // only if part of a root |
| return this; |
| }; |
| |
| /** |
| * Gets an option value. |
| * @param {string} name Option name |
| * @returns {*} Option value or `undefined` if not set |
| */ |
| ReflectionObject.prototype.getOption = function getOption(name) { |
| if (this.options) |
| return this.options[name]; |
| return undefined; |
| }; |
| |
| /** |
| * Sets an option. |
| * @param {string} name Option name |
| * @param {*} value Option value |
| * @param {boolean} [ifNotSet] Sets the option only if it isn't currently set |
| * @returns {ReflectionObject} `this` |
| */ |
| ReflectionObject.prototype.setOption = function setOption(name, value, ifNotSet) { |
| if (!ifNotSet || !this.options || this.options[name] === undefined) |
| (this.options || (this.options = {}))[name] = value; |
| return this; |
| }; |
| |
| /** |
| * Sets a parsed option. |
| * @param {string} name parsed Option name |
| * @param {*} value Option value |
| * @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 |
| * @returns {ReflectionObject} `this` |
| */ |
| ReflectionObject.prototype.setParsedOption = function setParsedOption(name, value, propName) { |
| if (!this.parsedOptions) { |
| this.parsedOptions = []; |
| } |
| var parsedOptions = this.parsedOptions; |
| if (propName) { |
| // If setting a sub property of an option then try to merge it |
| // with an existing option |
| var opt = parsedOptions.find(function (opt) { |
| return Object.prototype.hasOwnProperty.call(opt, name); |
| }); |
| if (opt) { |
| // If we found an existing option - just merge the property value |
| var newValue = opt[name]; |
| util.setProperty(newValue, propName, value); |
| } else { |
| // otherwise, create a new option, set it's property and add it to the list |
| opt = {}; |
| opt[name] = util.setProperty({}, propName, value); |
| parsedOptions.push(opt); |
| } |
| } else { |
| // Always create a new option when setting the value of the option itself |
| var newOpt = {}; |
| newOpt[name] = value; |
| parsedOptions.push(newOpt); |
| } |
| return this; |
| }; |
| |
| /** |
| * Sets multiple options. |
| * @param {Object.<string,*>} options Options to set |
| * @param {boolean} [ifNotSet] Sets an option only if it isn't currently set |
| * @returns {ReflectionObject} `this` |
| */ |
| ReflectionObject.prototype.setOptions = function setOptions(options, ifNotSet) { |
| if (options) |
| for (var keys = Object.keys(options), i = 0; i < keys.length; ++i) |
| this.setOption(keys[i], options[keys[i]], ifNotSet); |
| return this; |
| }; |
| |
| /** |
| * Converts this instance to its string representation. |
| * @returns {string} Class name[, space, full name] |
| */ |
| ReflectionObject.prototype.toString = function toString() { |
| var className = this.constructor.className, |
| fullName = this.fullName; |
| if (fullName.length) |
| return className + " " + fullName; |
| return className; |
| }; |
| |
| // Sets up cyclic dependencies (called in index-light) |
| ReflectionObject._configure = function(Root_) { |
| Root = Root_; |
| }; |