Variables.js 4.4 KB

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