/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * Free SoftwareFoundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.es; import com.caucho.vfs.VfsWriteObject; import com.caucho.vfs.WriteStream; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.Locale; /** * Implementation class for JavaScript numbers. Essentially, these are * equivalent to Java doubles. */ public class ESNumber extends ESBase implements VfsWriteObject, Externalizable { public static ESNumber ZERO = new ESNumber(0.0); public static ESNumber ONE = new ESNumber(1.0); public static ESNumber NaN = new ESNumber(0.0/0.0); static ESNumber ints[]; static { ints = new ESNumber[128]; for (int i = 0; i < ints.length; i++) ints[i] = new ESNumber(i); } private double value; /** * Null-arg constructor for serialization. */ public ESNumber() { prototype = esNull; } /** * Create a new object based on a prototype */ private ESNumber(double value) { prototype = esNull; this.value = value; } public static ESNumber create(double value) { try { // Can't use 0 because of -0 if (value >= 128 || value <= 0) return new ESNumber(value); int intValue = (int) value; if (intValue == value) return ints[intValue]; else return new ESNumber(value); } catch (Exception e) { return new ESNumber(value); } } /** * Any non-zero number is true. * * XXX: NaN and inf? */ public boolean toBoolean() { return ! Double.isNaN(value) && value != 0.0; } public boolean isNum() { return true; } public double toNum() { return value; } public ESObject toObject() throws ESException { return new ESWrapper("Number", Global.getGlobalProto().numProto, this); } public Object toJavaObject() { return new Double(value); } public ESBase typeof() throws ESException { return ESString.create("number"); } public Class getJavaType() { if ((int) value == value) return int.class; else return double.class; } public ESBase getProperty(ESString key) throws Throwable { return Global.getGlobalProto().numProto.getProperty(key); } public ESString toStr() { int intValue = (int) value; if (intValue == value) return ESString.create(intValue); else return ESString.create(toString()); } /** * Returns the string representation of the number. * * Notes: the spec says * 1) -0 should be printed at 0. * 2) 20 decimal digit integers should be printed as integers. * This is insane since the double can only almost a 16 digit decimal. * 3) The exponent should be lower case. */ public String toString() { int intValue = (int) value; if (intValue == value) return String.valueOf(intValue); else if ((long) value == value) return String.valueOf((long) value); else if (Double.isNaN(value)) return "NaN"; else if (Double.isInfinite(value)) return (value < 0 ? "-Infinity" : "Infinity"); return String.valueOf(value).toLowerCase(Locale.ENGLISH); } public void print(WriteStream os) throws IOException { int intValue = (int) value; if (intValue == value) os.print(intValue); else if ((long) value == value) os.print((long) value); else if (Double.isNaN(value)) os.print("NaN"); else if (Double.isInfinite(value)) os.print(value < 0 ? "-Infinity" : "Infinity"); else os.print(value); } public int hashCode() { long bits = Double.doubleToLongBits(value); return (int) bits + 65517 * (int) (bits >> 32); } public boolean equals(Object b) { return (b instanceof ESNumber) && value == ((ESNumber) b).value; } public boolean ecmaEquals(ESBase b) throws Throwable { return b != esNull && value == b.toNum(); } public boolean lessThan(ESBase b, boolean neg) throws Throwable { double db = b.toNum(); if (Double.isNaN(value) || Double.isNaN(db)) return false; else return (value < db) != neg; } public ESBase plus(ESBase b) throws Throwable { if (b instanceof ESNumber) return create(value + ((ESNumber) b).value); else { ESBase primB = b.toPrimitive(NONE); if (primB instanceof ESString) return ESString.create(toString() + primB.toString()); else return create(value + primB.toNum()); } } /** * Save the external representation. */ public void writeExternal(ObjectOutput os) throws IOException { os.writeDouble(value); } /** * Restore the external representation. */ public void readExternal(ObjectInput is) throws IOException { value = is.readDouble(); } }