智慧水务管理系统 - 精河县供水工程综合管理平台

namespace.js 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. "use strict";
  2. module.exports = Namespace;
  3. // extends ReflectionObject
  4. var ReflectionObject = require("./object");
  5. ((Namespace.prototype = Object.create(ReflectionObject.prototype)).constructor = Namespace).className = "Namespace";
  6. var Field = require("./field"),
  7. util = require("./util"),
  8. OneOf = require("./oneof");
  9. var Type, // cyclic
  10. Service,
  11. Enum;
  12. /**
  13. * Constructs a new namespace instance.
  14. * @name Namespace
  15. * @classdesc Reflected namespace.
  16. * @extends NamespaceBase
  17. * @constructor
  18. * @param {string} name Namespace name
  19. * @param {Object.<string,*>} [options] Declared options
  20. */
  21. /**
  22. * Constructs a namespace from JSON.
  23. * @memberof Namespace
  24. * @function
  25. * @param {string} name Namespace name
  26. * @param {Object.<string,*>} json JSON object
  27. * @param {number} [depth] Current nesting depth, defaults to `0`
  28. * @returns {Namespace} Created namespace
  29. * @throws {TypeError} If arguments are invalid
  30. */
  31. Namespace.fromJSON = function fromJSON(name, json, depth) {
  32. if (depth === undefined)
  33. depth = 0;
  34. if (depth > util.recursionLimit)
  35. throw Error("max depth exceeded");
  36. return new Namespace(name, json.options).addJSON(json.nested, depth);
  37. };
  38. /**
  39. * Converts an array of reflection objects to JSON.
  40. * @memberof Namespace
  41. * @param {ReflectionObject[]} array Object array
  42. * @param {IToJSONOptions} [toJSONOptions] JSON conversion options
  43. * @returns {Object.<string,*>|undefined} JSON object or `undefined` when array is empty
  44. */
  45. function arrayToJSON(array, toJSONOptions) {
  46. if (!(array && array.length))
  47. return undefined;
  48. var obj = {};
  49. for (var i = 0; i < array.length; ++i)
  50. obj[array[i].name] = array[i].toJSON(toJSONOptions);
  51. return obj;
  52. }
  53. Namespace.arrayToJSON = arrayToJSON;
  54. /**
  55. * Tests if the specified id is reserved.
  56. * @param {Array.<number[]|string>|undefined} reserved Array of reserved ranges and names
  57. * @param {number} id Id to test
  58. * @returns {boolean} `true` if reserved, otherwise `false`
  59. */
  60. Namespace.isReservedId = function isReservedId(reserved, id) {
  61. if (reserved)
  62. for (var i = 0; i < reserved.length; ++i)
  63. if (typeof reserved[i] !== "string" && reserved[i][0] <= id && reserved[i][1] >= id)
  64. return true;
  65. return false;
  66. };
  67. /**
  68. * Tests if the specified name is reserved.
  69. * @param {Array.<number[]|string>|undefined} reserved Array of reserved ranges and names
  70. * @param {string} name Name to test
  71. * @returns {boolean} `true` if reserved, otherwise `false`
  72. */
  73. Namespace.isReservedName = function isReservedName(reserved, name) {
  74. if (reserved)
  75. for (var i = 0; i < reserved.length; ++i)
  76. if (reserved[i] === name)
  77. return true;
  78. return false;
  79. };
  80. /**
  81. * Not an actual constructor. Use {@link Namespace} instead.
  82. * @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.
  83. * @exports NamespaceBase
  84. * @extends ReflectionObject
  85. * @abstract
  86. * @constructor
  87. * @param {string} name Namespace name
  88. * @param {Object.<string,*>} [options] Declared options
  89. * @see {@link Namespace}
  90. */
  91. function Namespace(name, options) {
  92. ReflectionObject.call(this, name, options);
  93. /**
  94. * Nested objects by name.
  95. * @type {Object.<string,ReflectionObject>|undefined}
  96. */
  97. this.nested = undefined; // toJSON
  98. /**
  99. * Cached nested objects as an array.
  100. * @type {ReflectionObject[]|null}
  101. * @private
  102. */
  103. this._nestedArray = null;
  104. /**
  105. * Cache lookup calls for any objects contains anywhere under this namespace.
  106. * This drastically speeds up resolve for large cross-linked protos where the same
  107. * types are looked up repeatedly.
  108. * @type {Object.<string,ReflectionObject|null>}
  109. * @private
  110. */
  111. this._lookupCache = Object.create(null);
  112. /**
  113. * Whether or not objects contained in this namespace need feature resolution.
  114. * @type {boolean}
  115. * @protected
  116. */
  117. this._needsRecursiveFeatureResolution = true;
  118. /**
  119. * Whether or not objects contained in this namespace need a resolve.
  120. * @type {boolean}
  121. * @protected
  122. */
  123. this._needsRecursiveResolve = true;
  124. }
  125. function clearCache(namespace) {
  126. namespace._nestedArray = null;
  127. namespace._lookupCache = Object.create(null);
  128. // Also clear parent caches, since they include nested lookups.
  129. var parent = namespace;
  130. while(parent = parent.parent) {
  131. parent._lookupCache = Object.create(null);
  132. }
  133. return namespace;
  134. }
  135. /**
  136. * Nested objects of this namespace as an array for iteration.
  137. * @name NamespaceBase#nestedArray
  138. * @type {ReflectionObject[]}
  139. * @readonly
  140. */
  141. Object.defineProperty(Namespace.prototype, "nestedArray", {
  142. get: function() {
  143. return this._nestedArray || (this._nestedArray = util.toArray(this.nested));
  144. }
  145. });
  146. /**
  147. * Namespace descriptor.
  148. * @interface INamespace
  149. * @property {Object.<string,*>} [options] Namespace options
  150. * @property {Object.<string,AnyNestedObject>} [nested] Nested object descriptors
  151. */
  152. /**
  153. * Any extension field descriptor.
  154. * @typedef AnyExtensionField
  155. * @type {IExtensionField|IExtensionMapField}
  156. */
  157. /**
  158. * Any nested object descriptor.
  159. * @typedef AnyNestedObject
  160. * @type {IEnum|IType|IService|AnyExtensionField|INamespace|IOneOf}
  161. */
  162. /**
  163. * Converts this namespace to a namespace descriptor.
  164. * @param {IToJSONOptions} [toJSONOptions] JSON conversion options
  165. * @returns {INamespace} Namespace descriptor
  166. */
  167. Namespace.prototype.toJSON = function toJSON(toJSONOptions) {
  168. return util.toObject([
  169. "options" , this.options,
  170. "nested" , arrayToJSON(this.nestedArray, toJSONOptions)
  171. ]);
  172. };
  173. /**
  174. * Adds nested objects to this namespace from nested object descriptors.
  175. * @param {Object.<string,AnyNestedObject>} nestedJson Any nested object descriptors
  176. * @param {number} [depth] Current nesting depth, defaults to `0`
  177. * @returns {Namespace} `this`
  178. */
  179. Namespace.prototype.addJSON = function addJSON(nestedJson, depth) {
  180. if (depth === undefined)
  181. depth = 0;
  182. if (depth > util.recursionLimit)
  183. throw Error("max depth exceeded");
  184. var ns = this;
  185. /* istanbul ignore else */
  186. if (nestedJson) {
  187. for (var names = Object.keys(nestedJson), i = 0, nested; i < names.length; ++i) {
  188. nested = nestedJson[names[i]];
  189. ns.add( // most to least likely
  190. ( nested.fields !== undefined
  191. ? Type.fromJSON
  192. : nested.values !== undefined
  193. ? Enum.fromJSON
  194. : nested.methods !== undefined
  195. ? Service.fromJSON
  196. : nested.id !== undefined
  197. ? Field.fromJSON
  198. : Namespace.fromJSON )(names[i], nested, depth + 1)
  199. );
  200. }
  201. }
  202. return this;
  203. };
  204. /**
  205. * Gets the nested object of the specified name.
  206. * @param {string} name Nested object name
  207. * @returns {ReflectionObject|null} The reflection object or `null` if it doesn't exist
  208. */
  209. Namespace.prototype.get = function get(name) {
  210. return this.nested && Object.prototype.hasOwnProperty.call(this.nested, name)
  211. ? this.nested[name]
  212. : null;
  213. };
  214. /**
  215. * Gets the values of the nested {@link Enum|enum} of the specified name.
  216. * This methods differs from {@link Namespace#get|get} in that it returns an enum's values directly and throws instead of returning `null`.
  217. * @param {string} name Nested enum name
  218. * @returns {Object.<string,number>} Enum values
  219. * @throws {Error} If there is no such enum
  220. */
  221. Namespace.prototype.getEnum = function getEnum(name) {
  222. if (this.nested && Object.prototype.hasOwnProperty.call(this.nested, name) && this.nested[name] instanceof Enum)
  223. return this.nested[name].values;
  224. throw Error("no such enum: " + name);
  225. };
  226. /**
  227. * Adds a nested object to this namespace.
  228. * @param {ReflectionObject} object Nested object to add
  229. * @returns {Namespace} `this`
  230. * @throws {TypeError} If arguments are invalid
  231. * @throws {Error} If there is already a nested object with this name
  232. */
  233. Namespace.prototype.add = function add(object) {
  234. if (!(object instanceof Field && object.extend !== undefined || object instanceof Type || object instanceof OneOf || object instanceof Enum || object instanceof Service || object instanceof Namespace))
  235. throw TypeError("object must be a valid nested object");
  236. if (object.name === "__proto__")
  237. return this;
  238. if (!this.nested)
  239. this.nested = {};
  240. else {
  241. var prev = this.get(object.name);
  242. if (prev) {
  243. if (prev instanceof Namespace && object instanceof Namespace && !(prev instanceof Type || prev instanceof Service)) {
  244. // replace plain namespace but keep existing nested elements and options
  245. var nested = prev.nestedArray;
  246. for (var i = 0; i < nested.length; ++i)
  247. object.add(nested[i]);
  248. this.remove(prev);
  249. if (!this.nested)
  250. this.nested = {};
  251. object.setOptions(prev.options, true);
  252. } else
  253. throw Error("duplicate name '" + object.name + "' in " + this);
  254. }
  255. }
  256. this.nested[object.name] = object;
  257. if (!(this instanceof Type || this instanceof Service || this instanceof Enum || this instanceof Field)) {
  258. // This is a package or a root namespace.
  259. if (!object._edition) {
  260. // Make sure that some edition is set if it hasn't already been specified.
  261. object._edition = object._defaultEdition;
  262. }
  263. }
  264. this._needsRecursiveFeatureResolution = true;
  265. this._needsRecursiveResolve = true;
  266. // Also clear parent caches, since they need to recurse down.
  267. var parent = this;
  268. while(parent = parent.parent) {
  269. parent._needsRecursiveFeatureResolution = true;
  270. parent._needsRecursiveResolve = true;
  271. }
  272. object.onAdd(this);
  273. return clearCache(this);
  274. };
  275. /**
  276. * Removes a nested object from this namespace.
  277. * @param {ReflectionObject} object Nested object to remove
  278. * @returns {Namespace} `this`
  279. * @throws {TypeError} If arguments are invalid
  280. * @throws {Error} If `object` is not a member of this namespace
  281. */
  282. Namespace.prototype.remove = function remove(object) {
  283. if (!(object instanceof ReflectionObject))
  284. throw TypeError("object must be a ReflectionObject");
  285. if (object.parent !== this)
  286. throw Error(object + " is not a member of " + this);
  287. if (!util.remove(this.nested, object, object.name))
  288. throw Error(object + " is not a member of " + this);
  289. if (!Object.keys(this.nested).length)
  290. this.nested = undefined;
  291. object.onRemove(this);
  292. return clearCache(this);
  293. };
  294. /**
  295. * Defines additial namespaces within this one if not yet existing.
  296. * @param {string|string[]} path Path to create
  297. * @param {*} [json] Nested types to create from JSON
  298. * @returns {Namespace} Pointer to the last namespace created or `this` if path is empty
  299. */
  300. Namespace.prototype.define = function define(path, json) {
  301. if (util.isString(path))
  302. path = path.split(".");
  303. else if (!Array.isArray(path))
  304. throw TypeError("illegal path");
  305. if (path && path.length && path[0] === "")
  306. throw Error("path must be relative");
  307. if (path.length > util.recursionLimit)
  308. throw Error("max depth exceeded");
  309. var ptr = this;
  310. while (path.length > 0) {
  311. var part = path.shift();
  312. if (ptr.nested && ptr.nested[part]) {
  313. ptr = ptr.nested[part];
  314. if (!(ptr instanceof Namespace))
  315. throw Error("path conflicts with non-namespace objects");
  316. } else
  317. ptr.add(ptr = new Namespace(part));
  318. }
  319. if (json)
  320. ptr.addJSON(json);
  321. return ptr;
  322. };
  323. /**
  324. * Resolves this namespace's and all its nested objects' type references. Useful to validate a reflection tree, but comes at a cost.
  325. * @returns {Namespace} `this`
  326. */
  327. Namespace.prototype.resolveAll = function resolveAll() {
  328. if (!this._needsRecursiveResolve) return this;
  329. if (this._needsRecursiveFeatureResolution)
  330. this._resolveFeaturesRecursive(this._edition);
  331. var nested = this.nestedArray, i = 0;
  332. this.resolve();
  333. while (i < nested.length)
  334. if (nested[i] instanceof Namespace)
  335. nested[i++].resolveAll();
  336. else
  337. nested[i++].resolve();
  338. this._needsRecursiveResolve = false;
  339. return this;
  340. };
  341. /**
  342. * @override
  343. */
  344. Namespace.prototype._resolveFeaturesRecursive = function _resolveFeaturesRecursive(edition) {
  345. if (!this._needsRecursiveFeatureResolution) return this;
  346. this._needsRecursiveFeatureResolution = false;
  347. edition = this._edition || edition;
  348. ReflectionObject.prototype._resolveFeaturesRecursive.call(this, edition);
  349. this.nestedArray.forEach(nested => {
  350. nested._resolveFeaturesRecursive(edition);
  351. });
  352. return this;
  353. };
  354. /**
  355. * Recursively looks up the reflection object matching the specified path in the scope of this namespace.
  356. * @param {string|string[]} path Path to look up
  357. * @param {*|Array.<*>} filterTypes Filter types, any combination of the constructors of `protobuf.Type`, `protobuf.Enum`, `protobuf.Service` etc.
  358. * @param {boolean} [parentAlreadyChecked=false] If known, whether the parent has already been checked
  359. * @returns {ReflectionObject|null} Looked up object or `null` if none could be found
  360. */
  361. Namespace.prototype.lookup = function lookup(path, filterTypes, parentAlreadyChecked) {
  362. /* istanbul ignore next */
  363. if (typeof filterTypes === "boolean") {
  364. parentAlreadyChecked = filterTypes;
  365. filterTypes = undefined;
  366. } else if (filterTypes && !Array.isArray(filterTypes))
  367. filterTypes = [ filterTypes ];
  368. if (util.isString(path) && path.length) {
  369. if (path === ".")
  370. return this.root;
  371. path = path.split(".");
  372. } else if (!path.length)
  373. return this;
  374. var flatPath = path.join(".");
  375. // Start at root if path is absolute
  376. if (path[0] === "")
  377. return this.root.lookup(path.slice(1), filterTypes);
  378. // Lookup at this namespace and below
  379. var found = this._lookupImpl(path, flatPath);
  380. if (found && (!filterTypes || filterTypes.indexOf(found.constructor) > -1)) {
  381. return found;
  382. }
  383. // Fall back to respective absolute path once relative scope has been checked (non-standard)
  384. found = this.root._fullyQualifiedObjects && this.root._fullyQualifiedObjects["." + flatPath];
  385. if (found && (!filterTypes || filterTypes.indexOf(found.constructor) > -1)) {
  386. return found;
  387. }
  388. if (parentAlreadyChecked)
  389. return null;
  390. // If there hasn't been a match, walk up the tree and look more broadly
  391. var current = this;
  392. while (current.parent) {
  393. found = current.parent._lookupImpl(path, flatPath);
  394. if (found && (!filterTypes || filterTypes.indexOf(found.constructor) > -1)) {
  395. return found;
  396. }
  397. current = current.parent;
  398. }
  399. return null;
  400. };
  401. /**
  402. * Internal helper for lookup that handles searching just at this namespace and below along with caching.
  403. * @param {string[]} path Path to look up
  404. * @param {string} flatPath Flattened version of the path to use as a cache key
  405. * @returns {ReflectionObject|null} Looked up object or `null` if none could be found
  406. * @private
  407. */
  408. Namespace.prototype._lookupImpl = function lookup(path, flatPath) {
  409. if(Object.prototype.hasOwnProperty.call(this._lookupCache, flatPath)) {
  410. return this._lookupCache[flatPath];
  411. }
  412. // Test if the first part matches any nested object, and if so, traverse if path contains more
  413. var found = this.get(path[0]);
  414. var exact = null;
  415. if (found) {
  416. if (path.length === 1) {
  417. exact = found;
  418. } else if (found instanceof Namespace) {
  419. path = path.slice(1);
  420. exact = found._lookupImpl(path, path.join("."));
  421. }
  422. // Otherwise try each nested namespace
  423. } else {
  424. for (var i = 0; i < this.nestedArray.length; ++i)
  425. if (this._nestedArray[i] instanceof Namespace && (found = this._nestedArray[i]._lookupImpl(path, flatPath))) {
  426. exact = found;
  427. break;
  428. }
  429. }
  430. // Set this even when null, so that when we walk up the tree we can quickly bail on repeated checks back down.
  431. this._lookupCache[flatPath] = exact;
  432. return exact;
  433. };
  434. /**
  435. * Looks up the reflection object at the specified path, relative to this namespace.
  436. * @name NamespaceBase#lookup
  437. * @function
  438. * @param {string|string[]} path Path to look up
  439. * @param {boolean} [parentAlreadyChecked=false] Whether the parent has already been checked
  440. * @returns {ReflectionObject|null} Looked up object or `null` if none could be found
  441. * @variation 2
  442. */
  443. // lookup(path: string, [parentAlreadyChecked: boolean])
  444. /**
  445. * Looks up the {@link Type|type} at the specified path, relative to this namespace.
  446. * Besides its signature, this methods differs from {@link Namespace#lookup|lookup} in that it throws instead of returning `null`.
  447. * @param {string|string[]} path Path to look up
  448. * @returns {Type} Looked up type
  449. * @throws {Error} If `path` does not point to a type
  450. */
  451. Namespace.prototype.lookupType = function lookupType(path) {
  452. var found = this.lookup(path, [ Type ]);
  453. if (!found)
  454. throw Error("no such type: " + path);
  455. return found;
  456. };
  457. /**
  458. * Looks up the values of the {@link Enum|enum} at the specified path, relative to this namespace.
  459. * Besides its signature, this methods differs from {@link Namespace#lookup|lookup} in that it throws instead of returning `null`.
  460. * @param {string|string[]} path Path to look up
  461. * @returns {Enum} Looked up enum
  462. * @throws {Error} If `path` does not point to an enum
  463. */
  464. Namespace.prototype.lookupEnum = function lookupEnum(path) {
  465. var found = this.lookup(path, [ Enum ]);
  466. if (!found)
  467. throw Error("no such Enum '" + path + "' in " + this);
  468. return found;
  469. };
  470. /**
  471. * Looks up the {@link Type|type} or {@link Enum|enum} at the specified path, relative to this namespace.
  472. * Besides its signature, this methods differs from {@link Namespace#lookup|lookup} in that it throws instead of returning `null`.
  473. * @param {string|string[]} path Path to look up
  474. * @returns {Type} Looked up type or enum
  475. * @throws {Error} If `path` does not point to a type or enum
  476. */
  477. Namespace.prototype.lookupTypeOrEnum = function lookupTypeOrEnum(path) {
  478. var found = this.lookup(path, [ Type, Enum ]);
  479. if (!found)
  480. throw Error("no such Type or Enum '" + path + "' in " + this);
  481. return found;
  482. };
  483. /**
  484. * Looks up the {@link Service|service} at the specified path, relative to this namespace.
  485. * Besides its signature, this methods differs from {@link Namespace#lookup|lookup} in that it throws instead of returning `null`.
  486. * @param {string|string[]} path Path to look up
  487. * @returns {Service} Looked up service
  488. * @throws {Error} If `path` does not point to a service
  489. */
  490. Namespace.prototype.lookupService = function lookupService(path) {
  491. var found = this.lookup(path, [ Service ]);
  492. if (!found)
  493. throw Error("no such Service '" + path + "' in " + this);
  494. return found;
  495. };
  496. // Sets up cyclic dependencies (called in index-light)
  497. Namespace._configure = function(Type_, Service_, Enum_) {
  498. Type = Type_;
  499. Service = Service_;
  500. Enum = Enum_;
  501. };