Source: data/model/object-descriptor.js

var Montage = require("core/core").Montage,
    PropertyDescriptor = require("data/model/property-descriptor").PropertyDescriptor;

/**
 * Describes a type of object.
 * @deprecated
 * @class
 * @extends external:Montage
 */
exports.ObjectDescriptor = Montage.specialize(/** @lends ObjectDescriptor.prototype */ {

    /**
     * Name of the type of object described by this descriptor.
     *
     * @type {string}
     */
    typeName: {
        value: undefined
    },

    /**
     * Prototype of objects of this type.
     *
     * @type {function}
     */
    objectPrototype: {
        value: Montage.prototype
    },

    /**
     * Descriptors of the properties of objects of this type, by property name.
     *
     * The returned map should not be modified.
     * [setPropertyDescriptor()]{@link ObjectDescriptor#setPropertyDescriptor}
     * and
     * [deletePropertyDescriptor()]{@link ObjectDescriptor#deletePropertyDescriptor}
     * should be used instead to modify the property descriptors.
     *
     * @type {Object.<string, PropertyDescriptor>}
     */
    propertyDescriptors: {
        get: function () {
            if (!this._propertyDescriptors) {
                this._propertyDescriptors = {};
            }
            return this._propertyDescriptors;
        }
    },

    /**
     * Add or replace a property descriptor.
     *
     * @method
     * @argument {string} name                   - The name of the property.
     * @argument {PropertyDescriptor} descriptor - Describes the property.
     */
    setPropertyDescriptor: {
        value: function (name, descriptor) {
            this.propertyDescriptors[name] = descriptor;
        }
    },

    /**
     * Remove a property descriptor.
     *
     * @method
     * @argument {string} name - The name of the property whose descriptor
     *                           should be removed.
     */
    deletePropertyDescriptor: {
        value: function (name) {
            delete this.propertyDescriptors[name];
        }
    },

    /**
     * Create a property descriptor.
     *
     * This implementation creates a plain {@link PropertyDescriptor} instance.
     * Subclasses can override this to create property descriptors of a
     * type more appropriate for the subclass. Any such type should be a
     * subclass of {@link PropertyDescriptor}.
     *
     * @method
     * @returns {PropertyDescriptor} - The created property descriptor.
     */
    makePropertyDescriptor: {
        value: function () {
            return new PropertyDescriptor();
        }
    },

    /**
     * @private
     * @method
     * @argument {Object<string, string>} types
     */
    _setPropertyDescriptorsFromTypes: {
        value: function (types) {
            var i, descriptor, key,
                keys = Object.keys(types);
            for (i = 0; (key = keys[i]); ++i) {
                descriptor = this.makePropertyDescriptor();
                this.setPropertyDescriptor(key, descriptor);
            }
        }
    },

    /**
     * @private
     * @method
     * @argument {Object} prototype
     */
    _setPropertyDescriptorsFromPrototype: {
        value: function (prototype) {
            var names, descriptor, i, n;
            for (; prototype !== Montage.prototype && prototype !== null; prototype = Object.getPrototypeOf(prototype)) {
                names = Object.getOwnPropertyNames(prototype);
                for (i = 0, n = names.length; i < n; i += 1) {
                    if (!this.propertyDescriptors[names[i]]) {
                        descriptor = this.makePropertyDescriptor();
                        this.setPropertyDescriptor(names[i], descriptor);
                    }
                }
            }
        }
    }

}, /** @lends ObjectDescriptor */ {

    /**
     * Generates a getter function that will create and cache an object
     * descriptor.
     *
     * @method
     * @argument {Object} exports         - A
     *                                      [Montage Require]{@link external:Require}
     *                                      exports object defining the
     *                                      constructor for the objects to
     *                                      describe. Usually this is `exports`.
     * @argument {string} constructorName - The name with which that constructor
     *                                      can be looked up in the exports.
     *                                      This will also be used as the
     *                                      created descriptor's type name.
     * @argument {Object<string, string>}
     *           [propertyTypes]          - The types of each of the described
     *                                      objects' properties, by property
     *                                      name. If this is not specified the
     *                                      property information will be derived
     *                                      from the properties of the objects'
     *                                      prototype.
     */
    getterFor: {
        value: function (exports, constructorName, propertyTypes) {
            // The returned getter function has to check
            // `this.hasOwnProperty("_TYPE")`, not just `this._TYPE`, because if
            // the class using the getter is a subclass of another class using a
            // similar getter `this._TYPE` will return the value of the the
            // superclass type instead of the desired subclass type.
            var self = this;
            return function () {
                if (!this.hasOwnProperty("_TYPE")) {
                    this._TYPE = new self();
                    this._TYPE.typeName = constructorName;
                    this._TYPE.objectPrototype = exports[constructorName].prototype;
                    if (propertyTypes) {
                        this._TYPE._setPropertyDescriptorsFromTypes(propertyTypes);
                    } else {
                        this._TYPE._setPropertyDescriptorsFromPrototype(this._TYPE.objectPrototype);
                    }
                }
                return this._TYPE;
            };
        }
    }

});