Value.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. var Value = module.exports = Value = (function(){
  2. // CONSTRUCTOR
  3. var klass = function Value(){
  4. if(this.constructor == Value) throw new Error("Never create instances of this! Use the static helpers only.");
  5. }, base = Object, proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
  6. // PRIVATE STUFF
  7. function getTypeVerifier(type, IClass, isStrict) {
  8. return function verifyType(value) {
  9. if (typeof(value) != type) throw new Error("typeof value is not: " + type + "; actual: " + typeof(value));
  10. if (typeof(IClass) == "function" && !(isStrict ? value.constructor == IClass : value instanceof IClass)) throw new Error("instanceof value is not: " + IClass.name + "; actual: " + value.constructor.name);
  11. return value;
  12. };
  13. }
  14. // STATIC MEMBERS
  15. klass.verifyNumber = getTypeVerifier("number", Number); //NOTE: replaces #getDouble(), #getInt(), and #getLong()
  16. klass.verifyString = getTypeVerifier("string", String);
  17. klass.verifyDocument = getTypeVerifier("object", Object, true); //TODO: change to verifyDocument?
  18. klass.verifyArray = getTypeVerifier("object", Array, true);
  19. klass.verifyDate = getTypeVerifier("object", Date, true);
  20. klass.verifyRegExp = getTypeVerifier("object", RegExp, true); //NOTE: renamed from #getRegex()
  21. //TODO: klass.verifyOid = ...?
  22. //TODO: klass.VerifyTimestamp = ...?
  23. klass.verifyBool = getTypeVerifier("boolean", Boolean, true);
  24. klass.coerceToBool = function coerceToBool(value) {
  25. if (typeof(value) == "string") return true;
  26. return !!value; // including null or undefined
  27. };
  28. klass.coerceToInt =
  29. klass.coerceToLong =
  30. klass.coerceToDouble =
  31. klass._coerceToNumber = function _coerceToNumber(value) { //NOTE: replaces .coerceToInt(), .coerceToLong(), and .coerceToDouble()
  32. if (value === null) return 0;
  33. switch (typeof(value)) {
  34. case "undefined":
  35. return 0;
  36. case "number":
  37. return value;
  38. default:
  39. throw new Error("can't convert from BSON type " + typeof(value) + " to int; codes 16003, 16004, 16005");
  40. }
  41. };
  42. klass.coerceToDate = function coerceToDate(value) {
  43. //TODO: Support Timestamp BSON type?
  44. if (value instanceof Date) return value;
  45. throw new Error("can't convert from BSON type " + typeof(value) + " to Date; codes 16006");
  46. };
  47. //TODO: klass.coerceToTimeT = ...? try to use as Date first rather than having coerceToDate return Date.parse or dateObj.getTime() or similar
  48. //TODO: klass.coerceToTm = ...?
  49. klass.coerceToString = function coerceToString(value) {
  50. if (value === null) return "";
  51. switch (typeof(value)) {
  52. case "undefined":
  53. return "";
  54. case "number":
  55. return value.toString();
  56. case "string":
  57. return value;
  58. case "object":
  59. if (value instanceof Array) {
  60. Value.verifyArray(r);
  61. for (var i = 0, ll = l.length, rl = r.length, len = Math.min(ll, rl); i < len; i++) {
  62. if (i >= ll) {
  63. if (i >= rl) return 0; // arrays are same length
  64. return -1; // left array is shorter
  65. }
  66. if (i >= rl) return 1; // right array is shorter
  67. var cmp = Value.compare(l[i], r[i]);
  68. if (cmp !== 0) return cmp;
  69. }
  70. throw new Error("logic error in Value.compare for Array types!");
  71. } else if (value instanceof Date) { //TODO: Timestamp ??
  72. return value.toISOString();
  73. }
  74. /* falls through */
  75. default:
  76. throw new Error("can't convert from BSON type " + typeof(value) + " to String; code 16007");
  77. }
  78. };
  79. //TODO: klass.coerceToTimestamp = ...?
  80. klass.compare = function compare(l, r) {
  81. var lt = typeof(l),
  82. rt = typeof(r);
  83. // Special handling for Undefined and NULL values ...
  84. if (lt === "undefined") {
  85. if (rt === "undefined") return 0;
  86. return -1;
  87. }
  88. if (l === null) {
  89. if (rt === "undefined") return 1;
  90. if (r === null) return 0;
  91. return -1;
  92. }
  93. // We know the left value isn't Undefined, because of the above. Count a NULL value as greater than an undefined one.
  94. if (rt === "undefined" || r === null) return 1;
  95. // Numbers
  96. if (lt === "number" && rt === "number") return l < r ? -1 : l > r ? 1 : 0;
  97. // CW TODO for now, only compare like values
  98. if (lt !== rt) throw new Error("can't compare values of BSON types [" + lt + " " + l.constructor.name + "] and [" + rt + ":" + r.constructor.name + "]; code 16016");
  99. // Compare everything else
  100. switch (lt) {
  101. case "number":
  102. throw new Error("number types should have been handled earlier!");
  103. case "string":
  104. return l < r ? -1 : l > r ? 1 : 0;
  105. case "boolean":
  106. return l == r ? 0 : l ? 1 : -1;
  107. case "object":
  108. if (l instanceof Array) {
  109. for (var i = 0, ll = l.length, rl = r.length, len = Math.min(ll, rl); i < len; i++) {
  110. if (i >= ll) {
  111. if (i >= rl) return 0; // arrays are same length
  112. return -1; // left array is shorter
  113. }
  114. if (i >= rl) return 1; // right array is shorter
  115. var cmp = Value.compare(l[i], r[i]);
  116. if (cmp !== 0) return cmp;
  117. }
  118. throw new Error("logic error in Value.compare for Array types!");
  119. }
  120. if (l instanceof Date) return l < r ? -1 : l > r ? 1 : 0;
  121. if (l instanceof RegExp) return l < r ? -1 : l > r ? 1 : 0;
  122. return Document.compare(l, r);
  123. default:
  124. throw new Error("unhandled left hand type:" + lt);
  125. }
  126. };
  127. //TODO: klass.hashCombine = ...?
  128. //TODO: klass.getWidestNumeric = ...?
  129. //TODO: klass.getApproximateSize = ...?
  130. //TODO: klass.addRef = ...?
  131. //TODO: klass.release = ...?
  132. return klass;
  133. })();