Source: eclairjs/Tuple.js

/*
 * Copyright 2016 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
(function () {

    var JavaWrapper = require(EclairJS_Globals.NAMESPACE + '/JavaWrapper');
    var Logger = require(EclairJS_Globals.NAMESPACE + '/Logger');
    var Utils = require(EclairJS_Globals.NAMESPACE + '/Utils');
    var logger = Logger.getLogger("Tuple_js");
    /**
     * Simple tuple implementation. This constructor will create new instances
     * and store immutable values within them.
     * @classdesc
     * @class
     * @memberof module:eclairjs
     * @param {objects[]} List of values to store within the tuple.
     */
    var Tuple = function () {
        throw ("Tuple deprecated");
        /**
         * Contains the number of elements held within the tuple.
         */
        var i = this.length = arguments.length;
        this._objectTypes = [];
        if ((arguments[0] instanceof Serialize.scalaProductClass) && (arguments[0].getClass().getName().indexOf("scala.Tuple") > -1)) {
            this.setJavaObject(arguments[0]);
        } else {
            for (var i = 0; i < arguments.length; i++) {
                this[i] = arguments[i];
                /*
                 for some reason javaScript numbers with the type of java.lang.Double and
                 a no fractional value eg 1.0 ar stored as java.lang.integers. so we need save the type
                 of the object so if it is a double we can force them
                 back to java.lang.Double on the way out in getJavaObject
                 */
                this._objectTypes[i] = arguments[i].class ? arguments[i].class : null;
            }
        }

    }
    Tuple.prototype = Object.create(Tuple.prototype);

    Tuple.prototype.constructor = Tuple;

    /**
     * Passes the values as arguments, in the same order they were set, to the
     * provided unpacker function. It will return the value that the unpacker
     * returns.
     *
     * @param {Function} unpacker Is passed all of the tuples values in order, it's return value will be returned.
     * @return {object} The value that the unpacker function returns.
     */
    Tuple.prototype.unpack = function (unpacker) {
        return unpacker.apply(this, this);
    };

    /**
     * Flattens the tuples values into a string.
     *
     * @return {String} A textual representation of the tuples contents.
     */
    Tuple.prototype.toString = function () {
        var values = this.toArray().join(',');
        return ['(', values, ')'].join('');
    };

    /**
     * Coerces the tuple into an array. This runs through
     * `Array.prototype.slice.call` because tuples are array-like objects.
     *
     * @return {object[]} All of the tuples values contained within an array.
     */
    Tuple.prototype.toArray = function () {
        return Array.prototype.slice.call(this);
    };

    /**
     * Iterates over every value within the tuple and pass the said values to
     * the provided callback individually.
     *
     * The callback is also passed the current index and tuple instance in that
     * order. This matches the normal `forEach` API found in most libraries and
     * modern JavaScript.
     *
     * @param {Function} callback Is passed every value in the tuple, one at a time.
     */
    Tuple.prototype.forEach = function (callback) {
        var length = this.length;
        var i;

        for (i = 0; i < length; i += 1) {
            callback(this[i], i, this);
        }
    };

    /**
     * Compares each value in both tuples, one value at a time in order. Both
     * tuples have to be of the same length and need to contain the exact same
     * values for this to return true. This can be used to compare against any
     * array-like object.
     *
     * @param {Object} target A tuple instance or any other array-like object you wish to compare to.
     * @return {Boolean} True if the tuples length and values match, false if not.
     */
    Tuple.prototype.equals = function (target) {
        var i = this.length;

        if (i !== target.length) {
            return false;
        }

        while (i--) {
            if (this[i] !== target[i]) {
                return false;
            }
        }

        return true;
    };

    /**
     * Returns the product of adding all contained values together. If you only
     * have numbers within your tuple you will get back all of those numbers
     * added together. If you have strings too then you will get a string
     * containing all of the values.
     *
     * This function is called automatically when using greater or less than
     * comparisons on tuples. So `tuple1 > tuple2` will add all of the
     * containing values together and then compare them.
     *
     * @return {object} The product of all values contained within the tuple.
     */
    Tuple.prototype.valueOf = function () {
        var value = this[0];
        var length = this.length;
        var i;

        for (i = 1; i < length; i += 1) {
            value = value + this[i];
        }

        return value;
    };

    Tuple.prototype.getJavaObject = function () {
        var length = this.length;
        var i;
        var javaObj = [];
        var Tuple = Java.type('scala.Tuple' + length);
        var expression = "new Tuple(";
        for (i = 0; i < length; i += 1) {
            //javaObj.push(org.eclairjs.nashorn.Utils.jsToJava(this[i]));
            /*
             for some reason javaScript numbers with the type of java.lang.Double and
             a no fractional value eg 1.0 ar stored as java.lang.integers. so we need to force them
             back to java.lang.Double on the way out
             */
            var obj;
            if (this._objectTypes[i]) {
                logger.debug("de-serialized this._objectTypes[i]" , this._objectTypes[i]);
                if (this._objectTypes[i] == java.lang.Double.class) {
                    obj = Serialize.jsToJava(Number(this[i])); // returns Double type
                    expression += "javaObj[" + i + "]";
               } else if (this._objectTypes[i] == java.lang.Integer.class) {
                    logger.debug("force to int");
                    obj =  + this[i]; // force to Integer type
                    expression += "java.lang.Integer.parseInt(javaObj[" + i + "])";
                } else if (this._objectTypes[i] == java.lang.Long.class) {
                    logger.debug("force to int");
                    obj =  + this[i]; // force to Long type
                    expression += "java.lang.Long.parseLong(javaObj[" + i + "])";
                } else {
                    obj = Serialize.jsToJava(this[i]);
                    expression += "javaObj[" + i + "]";
                }
            } else {
                obj = Serialize.jsToJava(this[i]);
                expression += "javaObj[" + i + "]";

            }
            logger.debug("de-serialized " , obj);
            javaObj.push(obj);

            //expression += "javaObj[" + i + "]";
            if (i < length - 1) {
                expression += ",";
            }
        }
        expression += ")";
        logger.debug("javaObj " , javaObj);
        logger.debug("expression " , expression);
        var retObj = eval('(' + expression + ')');
        logger.debug("getJavaObj returning " , retObj.class , " with value " , retObj.toString());
        return retObj;

    };

    Tuple.prototype.setJavaObject = function (obj) {
        var list = obj.productIterator();
        var x = 0;
        while (list.hasNext()) {
            var o = list.next();
            var r = Serialize.javaToJs(o);
            this[x] = r;
            x++
        }

        this.length = x;
        logger.debug("setJavaObject " , JSON.stringify(this));
    };


    Tuple.prototype.toJSON = function () {
        logger.debug("toJSON");
        var jsonObj = {};
        jsonObj.length = this.length;
        for (var i = 0; i < this.length; i++) {
            jsonObj[i] = this[i];
        }
        // no need to copy the _objectTypes, that is for use with Java
        return jsonObj;
    };

    module.exports = Tuple;

})();