| "use strict"; |
| module.exports = OneOf; |
| |
| // extends ReflectionObject |
| var ReflectionObject = require("./object"); |
| ((OneOf.prototype = Object.create(ReflectionObject.prototype)).constructor = OneOf).className = "OneOf"; |
| |
| var Field = require("./field"), |
| util = require("./util"); |
| |
| /** |
| * Constructs a new oneof instance. |
| * @classdesc Reflected oneof. |
| * @extends ReflectionObject |
| * @constructor |
| * @param {string} name Oneof name |
| * @param {string[]|Object.<string,*>} [fieldNames] Field names |
| * @param {Object.<string,*>} [options] Declared options |
| * @param {string} [comment] Comment associated with this field |
| */ |
| function OneOf(name, fieldNames, options, comment) { |
| if (!Array.isArray(fieldNames)) { |
| options = fieldNames; |
| fieldNames = undefined; |
| } |
| ReflectionObject.call(this, name, options); |
| |
| /* istanbul ignore if */ |
| if (!(fieldNames === undefined || Array.isArray(fieldNames))) |
| throw TypeError("fieldNames must be an Array"); |
| |
| /** |
| * Field names that belong to this oneof. |
| * @type {string[]} |
| */ |
| this.oneof = fieldNames || []; // toJSON, marker |
| |
| /** |
| * Fields that belong to this oneof as an array for iteration. |
| * @type {Field[]} |
| * @readonly |
| */ |
| this.fieldsArray = []; // declared readonly for conformance, possibly not yet added to parent |
| |
| /** |
| * Comment for this field. |
| * @type {string|null} |
| */ |
| this.comment = comment; |
| } |
| |
| /** |
| * Oneof descriptor. |
| * @interface IOneOf |
| * @property {Array.<string>} oneof Oneof field names |
| * @property {Object.<string,*>} [options] Oneof options |
| */ |
| |
| /** |
| * Constructs a oneof from a oneof descriptor. |
| * @param {string} name Oneof name |
| * @param {IOneOf} json Oneof descriptor |
| * @returns {OneOf} Created oneof |
| * @throws {TypeError} If arguments are invalid |
| */ |
| OneOf.fromJSON = function fromJSON(name, json) { |
| return new OneOf(name, json.oneof, json.options, json.comment); |
| }; |
| |
| /** |
| * Converts this oneof to a oneof descriptor. |
| * @param {IToJSONOptions} [toJSONOptions] JSON conversion options |
| * @returns {IOneOf} Oneof descriptor |
| */ |
| OneOf.prototype.toJSON = function toJSON(toJSONOptions) { |
| var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false; |
| return util.toObject([ |
| "options" , this.options, |
| "oneof" , this.oneof, |
| "comment" , keepComments ? this.comment : undefined |
| ]); |
| }; |
| |
| /** |
| * Adds the fields of the specified oneof to the parent if not already done so. |
| * @param {OneOf} oneof The oneof |
| * @returns {undefined} |
| * @inner |
| * @ignore |
| */ |
| function addFieldsToParent(oneof) { |
| if (oneof.parent) |
| for (var i = 0; i < oneof.fieldsArray.length; ++i) |
| if (!oneof.fieldsArray[i].parent) |
| oneof.parent.add(oneof.fieldsArray[i]); |
| } |
| |
| /** |
| * Adds a field to this oneof and removes it from its current parent, if any. |
| * @param {Field} field Field to add |
| * @returns {OneOf} `this` |
| */ |
| OneOf.prototype.add = function add(field) { |
| |
| /* istanbul ignore if */ |
| if (!(field instanceof Field)) |
| throw TypeError("field must be a Field"); |
| |
| if (field.parent && field.parent !== this.parent) |
| field.parent.remove(field); |
| this.oneof.push(field.name); |
| this.fieldsArray.push(field); |
| field.partOf = this; // field.parent remains null |
| addFieldsToParent(this); |
| return this; |
| }; |
| |
| /** |
| * Removes a field from this oneof and puts it back to the oneof's parent. |
| * @param {Field} field Field to remove |
| * @returns {OneOf} `this` |
| */ |
| OneOf.prototype.remove = function remove(field) { |
| |
| /* istanbul ignore if */ |
| if (!(field instanceof Field)) |
| throw TypeError("field must be a Field"); |
| |
| var index = this.fieldsArray.indexOf(field); |
| |
| /* istanbul ignore if */ |
| if (index < 0) |
| throw Error(field + " is not a member of " + this); |
| |
| this.fieldsArray.splice(index, 1); |
| index = this.oneof.indexOf(field.name); |
| |
| /* istanbul ignore else */ |
| if (index > -1) // theoretical |
| this.oneof.splice(index, 1); |
| |
| field.partOf = null; |
| return this; |
| }; |
| |
| /** |
| * @override |
| */ |
| OneOf.prototype.onAdd = function onAdd(parent) { |
| ReflectionObject.prototype.onAdd.call(this, parent); |
| var self = this; |
| // Collect present fields |
| for (var i = 0; i < this.oneof.length; ++i) { |
| var field = parent.get(this.oneof[i]); |
| if (field && !field.partOf) { |
| field.partOf = self; |
| self.fieldsArray.push(field); |
| } |
| } |
| // Add not yet present fields |
| addFieldsToParent(this); |
| }; |
| |
| /** |
| * @override |
| */ |
| OneOf.prototype.onRemove = function onRemove(parent) { |
| for (var i = 0, field; i < this.fieldsArray.length; ++i) |
| if ((field = this.fieldsArray[i]).parent) |
| field.parent.remove(field); |
| ReflectionObject.prototype.onRemove.call(this, parent); |
| }; |
| |
| /** |
| * Decorator function as returned by {@link OneOf.d} (TypeScript). |
| * @typedef OneOfDecorator |
| * @type {function} |
| * @param {Object} prototype Target prototype |
| * @param {string} oneofName OneOf name |
| * @returns {undefined} |
| */ |
| |
| /** |
| * OneOf decorator (TypeScript). |
| * @function |
| * @param {...string} fieldNames Field names |
| * @returns {OneOfDecorator} Decorator function |
| * @template T extends string |
| */ |
| OneOf.d = function decorateOneOf() { |
| var fieldNames = new Array(arguments.length), |
| index = 0; |
| while (index < arguments.length) |
| fieldNames[index] = arguments[index++]; |
| return function oneOfDecorator(prototype, oneofName) { |
| util.decorateType(prototype.constructor) |
| .add(new OneOf(oneofName, fieldNames)); |
| Object.defineProperty(prototype, oneofName, { |
| get: util.oneOfGetter(fieldNames), |
| set: util.oneOfSetter(fieldNames) |
| }); |
| }; |
| }; |