Value.js 5.5 KB

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