tra-analysis/website/functions/node_modules/protobufjs/src/namespace.js

434 lines
15 KiB
JavaScript
Raw Normal View History

2019-01-06 19:14:45 +00:00
"use strict";
module.exports = Namespace;
// extends ReflectionObject
var ReflectionObject = require("./object");
((Namespace.prototype = Object.create(ReflectionObject.prototype)).constructor = Namespace).className = "Namespace";
var Field = require("./field"),
util = require("./util");
var Type, // cyclic
Service,
Enum;
/**
* Constructs a new namespace instance.
* @name Namespace
* @classdesc Reflected namespace.
* @extends NamespaceBase
* @constructor
* @param {string} name Namespace name
* @param {Object.<string,*>} [options] Declared options
*/
/**
* Constructs a namespace from JSON.
* @memberof Namespace
* @function
* @param {string} name Namespace name
* @param {Object.<string,*>} json JSON object
* @returns {Namespace} Created namespace
* @throws {TypeError} If arguments are invalid
*/
Namespace.fromJSON = function fromJSON(name, json) {
return new Namespace(name, json.options).addJSON(json.nested);
};
/**
* Converts an array of reflection objects to JSON.
* @memberof Namespace
* @param {ReflectionObject[]} array Object array
* @param {IToJSONOptions} [toJSONOptions] JSON conversion options
* @returns {Object.<string,*>|undefined} JSON object or `undefined` when array is empty
*/
function arrayToJSON(array, toJSONOptions) {
if (!(array && array.length))
return undefined;
var obj = {};
for (var i = 0; i < array.length; ++i)
obj[array[i].name] = array[i].toJSON(toJSONOptions);
return obj;
}
Namespace.arrayToJSON = arrayToJSON;
/**
* Tests if the specified id is reserved.
* @param {Array.<number[]|string>|undefined} reserved Array of reserved ranges and names
* @param {number} id Id to test
* @returns {boolean} `true` if reserved, otherwise `false`
*/
Namespace.isReservedId = function isReservedId(reserved, id) {
if (reserved)
for (var i = 0; i < reserved.length; ++i)
if (typeof reserved[i] !== "string" && reserved[i][0] <= id && reserved[i][1] >= id)
return true;
return false;
};
/**
* Tests if the specified name is reserved.
* @param {Array.<number[]|string>|undefined} reserved Array of reserved ranges and names
* @param {string} name Name to test
* @returns {boolean} `true` if reserved, otherwise `false`
*/
Namespace.isReservedName = function isReservedName(reserved, name) {
if (reserved)
for (var i = 0; i < reserved.length; ++i)
if (reserved[i] === name)
return true;
return false;
};
/**
* Not an actual constructor. Use {@link Namespace} instead.
* @classdesc Base class of all reflection objects containing nested objects. This is not an actual class but here for the sake of having consistent type definitions.
* @exports NamespaceBase
* @extends ReflectionObject
* @abstract
* @constructor
* @param {string} name Namespace name
* @param {Object.<string,*>} [options] Declared options
* @see {@link Namespace}
*/
function Namespace(name, options) {
ReflectionObject.call(this, name, options);
/**
* Nested objects by name.
* @type {Object.<string,ReflectionObject>|undefined}
*/
this.nested = undefined; // toJSON
/**
* Cached nested objects as an array.
* @type {ReflectionObject[]|null}
* @private
*/
this._nestedArray = null;
}
function clearCache(namespace) {
namespace._nestedArray = null;
return namespace;
}
/**
* Nested objects of this namespace as an array for iteration.
* @name NamespaceBase#nestedArray
* @type {ReflectionObject[]}
* @readonly
*/
Object.defineProperty(Namespace.prototype, "nestedArray", {
get: function() {
return this._nestedArray || (this._nestedArray = util.toArray(this.nested));
}
});
/**
* Namespace descriptor.
* @interface INamespace
* @property {Object.<string,*>} [options] Namespace options
* @property {Object.<string,AnyNestedObject>} [nested] Nested object descriptors
*/
/**
* Any extension field descriptor.
* @typedef AnyExtensionField
* @type {IExtensionField|IExtensionMapField}
*/
/**
* Any nested object descriptor.
* @typedef AnyNestedObject
* @type {IEnum|IType|IService|AnyExtensionField|INamespace}
*/
// ^ BEWARE: VSCode hangs forever when using more than 5 types (that's why AnyExtensionField exists in the first place)
/**
* Converts this namespace to a namespace descriptor.
* @param {IToJSONOptions} [toJSONOptions] JSON conversion options
* @returns {INamespace} Namespace descriptor
*/
Namespace.prototype.toJSON = function toJSON(toJSONOptions) {
return util.toObject([
"options" , this.options,
"nested" , arrayToJSON(this.nestedArray, toJSONOptions)
]);
};
/**
* Adds nested objects to this namespace from nested object descriptors.
* @param {Object.<string,AnyNestedObject>} nestedJson Any nested object descriptors
* @returns {Namespace} `this`
*/
Namespace.prototype.addJSON = function addJSON(nestedJson) {
var ns = this;
/* istanbul ignore else */
if (nestedJson) {
for (var names = Object.keys(nestedJson), i = 0, nested; i < names.length; ++i) {
nested = nestedJson[names[i]];
ns.add( // most to least likely
( nested.fields !== undefined
? Type.fromJSON
: nested.values !== undefined
? Enum.fromJSON
: nested.methods !== undefined
? Service.fromJSON
: nested.id !== undefined
? Field.fromJSON
: Namespace.fromJSON )(names[i], nested)
);
}
}
return this;
};
/**
* Gets the nested object of the specified name.
* @param {string} name Nested object name
* @returns {ReflectionObject|null} The reflection object or `null` if it doesn't exist
*/
Namespace.prototype.get = function get(name) {
return this.nested && this.nested[name]
|| null;
};
/**
* Gets the values of the nested {@link Enum|enum} of the specified name.
* This methods differs from {@link Namespace#get|get} in that it returns an enum's values directly and throws instead of returning `null`.
* @param {string} name Nested enum name
* @returns {Object.<string,number>} Enum values
* @throws {Error} If there is no such enum
*/
Namespace.prototype.getEnum = function getEnum(name) {
if (this.nested && this.nested[name] instanceof Enum)
return this.nested[name].values;
throw Error("no such enum: " + name);
};
/**
* Adds a nested object to this namespace.
* @param {ReflectionObject} object Nested object to add
* @returns {Namespace} `this`
* @throws {TypeError} If arguments are invalid
* @throws {Error} If there is already a nested object with this name
*/
Namespace.prototype.add = function add(object) {
if (!(object instanceof Field && object.extend !== undefined || object instanceof Type || object instanceof Enum || object instanceof Service || object instanceof Namespace))
throw TypeError("object must be a valid nested object");
if (!this.nested)
this.nested = {};
else {
var prev = this.get(object.name);
if (prev) {
if (prev instanceof Namespace && object instanceof Namespace && !(prev instanceof Type || prev instanceof Service)) {
// replace plain namespace but keep existing nested elements and options
var nested = prev.nestedArray;
for (var i = 0; i < nested.length; ++i)
object.add(nested[i]);
this.remove(prev);
if (!this.nested)
this.nested = {};
object.setOptions(prev.options, true);
} else
throw Error("duplicate name '" + object.name + "' in " + this);
}
}
this.nested[object.name] = object;
object.onAdd(this);
return clearCache(this);
};
/**
* Removes a nested object from this namespace.
* @param {ReflectionObject} object Nested object to remove
* @returns {Namespace} `this`
* @throws {TypeError} If arguments are invalid
* @throws {Error} If `object` is not a member of this namespace
*/
Namespace.prototype.remove = function remove(object) {
if (!(object instanceof ReflectionObject))
throw TypeError("object must be a ReflectionObject");
if (object.parent !== this)
throw Error(object + " is not a member of " + this);
delete this.nested[object.name];
if (!Object.keys(this.nested).length)
this.nested = undefined;
object.onRemove(this);
return clearCache(this);
};
/**
* Defines additial namespaces within this one if not yet existing.
* @param {string|string[]} path Path to create
* @param {*} [json] Nested types to create from JSON
* @returns {Namespace} Pointer to the last namespace created or `this` if path is empty
*/
Namespace.prototype.define = function define(path, json) {
if (util.isString(path))
path = path.split(".");
else if (!Array.isArray(path))
throw TypeError("illegal path");
if (path && path.length && path[0] === "")
throw Error("path must be relative");
var ptr = this;
while (path.length > 0) {
var part = path.shift();
if (ptr.nested && ptr.nested[part]) {
ptr = ptr.nested[part];
if (!(ptr instanceof Namespace))
throw Error("path conflicts with non-namespace objects");
} else
ptr.add(ptr = new Namespace(part));
}
if (json)
ptr.addJSON(json);
return ptr;
};
/**
* Resolves this namespace's and all its nested objects' type references. Useful to validate a reflection tree, but comes at a cost.
* @returns {Namespace} `this`
*/
Namespace.prototype.resolveAll = function resolveAll() {
var nested = this.nestedArray, i = 0;
while (i < nested.length)
if (nested[i] instanceof Namespace)
nested[i++].resolveAll();
else
nested[i++].resolve();
return this.resolve();
};
/**
* Recursively looks up the reflection object matching the specified path in the scope of this namespace.
* @param {string|string[]} path Path to look up
* @param {*|Array.<*>} filterTypes Filter types, any combination of the constructors of `protobuf.Type`, `protobuf.Enum`, `protobuf.Service` etc.
* @param {boolean} [parentAlreadyChecked=false] If known, whether the parent has already been checked
* @returns {ReflectionObject|null} Looked up object or `null` if none could be found
*/
Namespace.prototype.lookup = function lookup(path, filterTypes, parentAlreadyChecked) {
/* istanbul ignore next */
if (typeof filterTypes === "boolean") {
parentAlreadyChecked = filterTypes;
filterTypes = undefined;
} else if (filterTypes && !Array.isArray(filterTypes))
filterTypes = [ filterTypes ];
if (util.isString(path) && path.length) {
if (path === ".")
return this.root;
path = path.split(".");
} else if (!path.length)
return this;
// Start at root if path is absolute
if (path[0] === "")
return this.root.lookup(path.slice(1), filterTypes);
// Test if the first part matches any nested object, and if so, traverse if path contains more
var found = this.get(path[0]);
if (found) {
if (path.length === 1) {
if (!filterTypes || filterTypes.indexOf(found.constructor) > -1)
return found;
} else if (found instanceof Namespace && (found = found.lookup(path.slice(1), filterTypes, true)))
return found;
// Otherwise try each nested namespace
} else
for (var i = 0; i < this.nestedArray.length; ++i)
if (this._nestedArray[i] instanceof Namespace && (found = this._nestedArray[i].lookup(path, filterTypes, true)))
return found;
// If there hasn't been a match, try again at the parent
if (this.parent === null || parentAlreadyChecked)
return null;
return this.parent.lookup(path, filterTypes);
};
/**
* Looks up the reflection object at the specified path, relative to this namespace.
* @name NamespaceBase#lookup
* @function
* @param {string|string[]} path Path to look up
* @param {boolean} [parentAlreadyChecked=false] Whether the parent has already been checked
* @returns {ReflectionObject|null} Looked up object or `null` if none could be found
* @variation 2
*/
// lookup(path: string, [parentAlreadyChecked: boolean])
/**
* Looks up the {@link Type|type} at the specified path, relative to this namespace.
* Besides its signature, this methods differs from {@link Namespace#lookup|lookup} in that it throws instead of returning `null`.
* @param {string|string[]} path Path to look up
* @returns {Type} Looked up type
* @throws {Error} If `path` does not point to a type
*/
Namespace.prototype.lookupType = function lookupType(path) {
var found = this.lookup(path, [ Type ]);
if (!found)
throw Error("no such type: " + path);
return found;
};
/**
* Looks up the values of the {@link Enum|enum} at the specified path, relative to this namespace.
* Besides its signature, this methods differs from {@link Namespace#lookup|lookup} in that it throws instead of returning `null`.
* @param {string|string[]} path Path to look up
* @returns {Enum} Looked up enum
* @throws {Error} If `path` does not point to an enum
*/
Namespace.prototype.lookupEnum = function lookupEnum(path) {
var found = this.lookup(path, [ Enum ]);
if (!found)
throw Error("no such Enum '" + path + "' in " + this);
return found;
};
/**
* Looks up the {@link Type|type} or {@link Enum|enum} at the specified path, relative to this namespace.
* Besides its signature, this methods differs from {@link Namespace#lookup|lookup} in that it throws instead of returning `null`.
* @param {string|string[]} path Path to look up
* @returns {Type} Looked up type or enum
* @throws {Error} If `path` does not point to a type or enum
*/
Namespace.prototype.lookupTypeOrEnum = function lookupTypeOrEnum(path) {
var found = this.lookup(path, [ Type, Enum ]);
if (!found)
throw Error("no such Type or Enum '" + path + "' in " + this);
return found;
};
/**
* Looks up the {@link Service|service} at the specified path, relative to this namespace.
* Besides its signature, this methods differs from {@link Namespace#lookup|lookup} in that it throws instead of returning `null`.
* @param {string|string[]} path Path to look up
* @returns {Service} Looked up service
* @throws {Error} If `path` does not point to a service
*/
Namespace.prototype.lookupService = function lookupService(path) {
var found = this.lookup(path, [ Service ]);
if (!found)
throw Error("no such Service '" + path + "' in " + this);
return found;
};
// Sets up cyclic dependencies (called in index-light)
Namespace._configure = function(Type_, Service_, Enum_) {
Type = Type_;
Service = Service_;
Enum = Enum_;
};