Variables.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. "use strict";
  2. // TODO: Look into merging with ExpressionContext and possibly ObjectCtx.
  3. /**
  4. * The state used as input and working space for Expressions.
  5. * @class Variables
  6. * @namespace mungedb-aggregate.pipeline.expressions
  7. * @module mungedb-aggregate
  8. * @constructor
  9. */
  10. var Variables = module.exports = function Variables(numVars, root){
  11. if (arguments.length === 0) numVars = 0; // This is only for expressions that use no variables (even ROOT).
  12. if (typeof numVars !== "number") throw new Error("numVars must be a Number");
  13. this._root = root || {};
  14. this._rest = numVars === 0 ? null : new Array(numVars);
  15. this._numVars = numVars;
  16. }, klass = Variables, proto = klass.prototype;
  17. klass.uassertValidNameForUserWrite = function uassertValidNameForUserWrite(varName) {
  18. // System variables users allowed to write to (currently just one)
  19. if (varName === "CURRENT") {
  20. return;
  21. }
  22. if (!varName)
  23. throw new Error("empty variable names are not allowed; uassert code 16866");
  24. var firstCharIsValid = (varName[0] >= "a" && varName[0] <= "z") ||
  25. (varName[0] & "\x80"); // non-ascii
  26. if (!firstCharIsValid)
  27. throw new Error("'" + varName + "' starts with an invalid character for a user variable name; uassert code 16867");
  28. for (var i = 1, l = varName.length; i < l; i++) {
  29. var charIsValid = (varName[i] >= 'a' && varName[i] <= 'z') ||
  30. (varName[i] >= 'A' && varName[i] <= 'Z') ||
  31. (varName[i] >= '0' && varName[i] <= '9') ||
  32. (varName[i] == '_') ||
  33. (varName[i] & '\x80'); // non-ascii
  34. if (!charIsValid)
  35. throw new Error("'" + varName + "' contains an invalid character " +
  36. "for a variable name: '" + varName[i] + "'; uassert code 16868");
  37. }
  38. };
  39. klass.uassertValidNameForUserRead = function uassertValidNameForUserRead(varName) {
  40. if (!varName)
  41. throw new Error("empty variable names are not allowed; uassert code 16869");
  42. var firstCharIsValid = (varName[0] >= "a" && varName[0] <= "z") ||
  43. (varName[0] >= "A" && varName[0] <= "Z") ||
  44. (varName[0] & "\x80"); // non-ascii
  45. if (!firstCharIsValid)
  46. throw new Error("'" + varName + "' starts with an invalid character for a variable name; uassert code 16870");
  47. for (var i = 1, l = varName.length; i < l; i++) {
  48. var charIsValid = (varName[i] >= "a" && varName[i] <= "z") ||
  49. (varName[i] >= "A" && varName[i] <= "Z") ||
  50. (varName[i] >= "0" && varName[i] <= "9") ||
  51. (varName[i] == "_") ||
  52. (varName[i] & "\x80"); // non-ascii
  53. if (!charIsValid)
  54. throw new Error("'" + varName + "' contains an invalid character " +
  55. "for a variable name: '" + varName[i] + "'; uassert code 16871");
  56. }
  57. };
  58. /**
  59. * Inserts a value with the given id
  60. * @method setValue
  61. * @param id {Number} The index where the value is stored in the _rest Array
  62. * @param value {Value} The value to store
  63. */
  64. proto.setValue = function setValue(id, value) {
  65. if (typeof id !== "number") throw new Error("id must be a Number");
  66. if (id === klass.ROOT_ID) throw new Error("can't use Variables#setValue to set ROOT; massert code 17199");
  67. if (id >= this._numVars) throw new Error("Assertion error");
  68. this._rest[id] = value;
  69. };
  70. /**
  71. * Get the value at the given id
  72. * @method getValue
  73. * @param id {Number} The index where the value was stored
  74. * @return {Value} The value
  75. */
  76. proto.getValue = function getValue(id) {
  77. if (typeof id !== "number") throw new Error("id must be a Number");
  78. if (id === klass.ROOT_ID)
  79. return this._root;
  80. if (id >= this._numVars) throw new Error("Assertion error");
  81. return this._rest[id];
  82. };
  83. /**
  84. * Get the value for id if it's a document
  85. * @method getDocument
  86. * @param id {Number} The index where the document was stored
  87. * @return {Object} The document
  88. */
  89. proto.getDocument = function getDocument(id) {
  90. if (typeof id !== "number") throw new Error("id must be a Number");
  91. if (id === klass.ROOT_ID)
  92. return this._root;
  93. if (id >= this._numVars) throw new Error("Assertion error");
  94. var value = this._rest[id];
  95. if (value instanceof Object && value.constructor === Object)
  96. return value;
  97. return {};
  98. };
  99. klass.ROOT_ID = -1;
  100. /**
  101. * Use this instead of setValue for setting ROOT
  102. * @method setRoot
  103. * @parameter root {Document} The root variable
  104. */
  105. proto.setRoot = function setRoot(root){
  106. if (!(root instanceof Object && root.constructor === Object)) throw new Error("Assertion failure");
  107. this._root = root;
  108. };
  109. /**
  110. * Clears the root variable
  111. * @method clearRoot
  112. */
  113. proto.clearRoot = function clearRoot(){
  114. this._root = {};
  115. };
  116. /**
  117. * Gets the root variable
  118. * @method getRoot
  119. * @return {Document} the root variable
  120. */
  121. proto.getRoot = function getRoot(){
  122. return this._root;
  123. };