baja/obj/Long.js

/**
 * @copyright 2015 Tridium, Inc. All Rights Reserved.
 * @author Gareth Johnson
 */

/**
 * Defines {@link baja.Long}.
 * @module baja/obj/Long
 */
define([ 'bajaScript/sys',
        'bajaScript/baja/obj/Simple',
        'bajaScript/baja/obj/numberUtil',
        'bajaScript/baja/obj/objUtil' ], function (
        baja,
        Simple,
        numberUtil,
        objUtil) {
  
  'use strict';
  
  var subclass = baja.subclass,
      callSuper = baja.callSuper,
      strictArg = baja.strictArg,

      integralToString = numberUtil.integralToString,

      cacheDecode = objUtil.cacheDecode,
      cacheEncode = objUtil.cacheEncode,
      uncacheConstantEncodeDecode = objUtil.uncacheConstantEncodeDecode;
  
      // TODO: Long can't really be represented in JavaScript. This needs more investigation
    
  /**
   * Represents a `baja:Long` in BajaScript.
   * 
   * Boxes JavaScript `Number` to represent a `baja:Long`.
   * 
   * When creating a `Simple`, always use the `make()` method instead of 
   * creating a new Object.
   *
   * @class
   * @alias baja.Long
   * @extends baja.Simple
   */
  var Long = function Long(val) {
    callSuper(Long, this, arguments); 
    this.$val = strictArg(val, Number);
  };
  
  subclass(Long, Simple);
      
  /**
   * Make a `Long`.
   *
   * @param {Number} val - the number value.
   *
   * @returns {baja.Long}
   */
  Long.make = function (val) {  
    if (val === 0 || isNaN(val)) {
      return Long.DEFAULT;
    }
    
    if (val >= Long.MAX_VALUE.valueOf() || val === Number.MAX_VALUE) {
      return Long.MAX_VALUE;
    }
    
    if (val <= Long.MIN_VALUE.valueOf() || val === Number.MIN_VALUE) {
      return Long.MIN_VALUE;
    }
  
    val = Math.floor(val);
    
    if (val === 0) {
      return Long.DEFAULT;
    }
  
    return new Long(val);
  };
  
  /**
   * Make a `Long`.
   *
   * @param {Number} val - the number value.
   *
   * @returns {baja.Long}
   */
  Long.prototype.make = function (val) {
    return Long.make(val);
  };
    
  /**
   * Decode a `Long` from a `String`.
   *
   * @param {String} str
   *
   * @returns {baja.Long}
   */
  Long.prototype.decodeFromString = function (str) {
    switch (str) {
    case "0": return Long.DEFAULT;
    // Min and max limits
    case "max": return Long.MAX_VALUE;
    case "min": return Long.MIN_VALUE;
    
    default: return Long.make(Number(str));
    }
  };
  
  /**
   * Encode the `Long` (itself) to a `String`.
   *
   * @returns {String}
   */
  Long.prototype.encodeToString = function () {
        
    // Check range limits and NAN
    if (isNaN(this.$val)) {
      return "0";
    }
    
    if (this.$val >= Long.MAX_VALUE.valueOf()) {
      return "max";
    }
    
    if (this.$val <= Long.MIN_VALUE.valueOf()) {
      return "min";
    }
        
    return Math.floor(this.$val).toFixed(0);
  };
  
  /**
   * Default `Long` instance.
   * @type {baja.Long}
   */
  Long.DEFAULT = uncacheConstantEncodeDecode(new Long(0));
  
  /**
   * `Long` Max Value.
   * @type {baja.Long}
   */
  Long.MAX_VALUE = uncacheConstantEncodeDecode(new Long(9223372036854775807));
  
  /**
   * `Long` Min Value.
   * @type {baja.Long}
   */
  Long.MIN_VALUE = uncacheConstantEncodeDecode(new Long(-9223372036854775808));
  
  Long.prototype.decodeFromString = cacheDecode(Long.prototype.decodeFromString);
  Long.prototype.encodeToString = cacheEncode(Long.prototype.encodeToString);
      
  /**
   * Return the data type symbol.
   *
   * Used for encoding this data type (primarily for facets).
   *
   * @returns {String}
   */
  Long.prototype.getDataTypeSymbol = function () {
    return "l";
  };

  /**
   * Equality test.
   *
   * @param obj
   *
   * @returns {Boolean}
   */
  Long.prototype.equals = function (obj) {
    return objUtil.valueOfEquals(this, obj);
  };
  
  /**
   * Return the `Number` encapsulated in the `Long` (itself).
   *
   * @returns {Number}
   */
  Long.prototype.valueOf = function () {
    return this.$val;
  };
  
  /**
   * Returns the `String` representation of the `Long` (itself).
   *
   * @param {baja.Facets|Object} [cx] - used to specify formatting facets. The
   * argument can also be an Object Literal.
   *
   * @param {Boolean} [cx.forceSign] - specifying 'true' will concatenate a '+'
   * to the beginning of the number if positive.
   *
   * @param {Boolean} [cx.showSeparators] - include separators.
   *
   * @param {Number} [cx.radix] - specify the number base of the return string.
   *
   * @param {baja.Unit} [cx.units] - the baja Unit to apply to the return
   * string.
   *
   * @param {baja.Enum|Number|String} [cx.unitConversion] - the
   * `baja:UnitConversion` enum, an ordinal, or tag.
   *
   * @param {Number} [cx.zeroPad] - the minimum number of the whole-number
   * digits to be displayed, filling in zeroes when necessary.
   *
   * @returns {String|Promise.<String>} returns a Promise if a cx is passed in.
   */
  Long.prototype.toString = function (cx) {
    if (cx) {
      return integralToString(this, cx);
    }
    return this.encodeToString();
  };
  
  return Long;
});