--- /dev/null
+"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_;
+};