/*
* Copyright (c) 1998-2010 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 Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.quercus.env;
import com.caucho.quercus.marshal.Marshal;
import com.caucho.vfs.WriteStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.IdentityHashMap;
/**
* Represents a PHP double value.
*/
public class DoubleValue extends NumberValue
implements Serializable {
public static final DoubleValue ZERO = new DoubleValue(0);
private final double _value;
public DoubleValue(double value) {
_value = value;
}
public static DoubleValue create(double value) {
return new DoubleValue(value);
}
public static Value create(Number value) {
if (value == null) {
// php/3c2d
return NullValue.NULL;
} else {
return new DoubleValue(value.doubleValue());
}
}
/**
* Returns the type.
*/
@Override
public String getType() {
// php/0142
return "double";
}
/**
* Returns the ValueType.
*/
@Override
public ValueType getValueType() {
return ValueType.DOUBLE;
}
/**
* Returns true for a double.
*/
@Override
public boolean isDoubleConvertible() {
return true;
}
/**
* Returns true for integer looking doubles.
*/
@Override
public boolean isLongConvertible() {
return _value == (double) ((long) _value);
}
/**
* Returns true for a long-value.
*/
@Override
public boolean isLong() {
return false;
}
/**
* Returns true for a double-value.
*/
@Override
public boolean isDouble() {
return true;
}
/**
* Returns true for is_numeric
*/
@Override
public boolean isNumeric() {
return true;
}
/**
* Returns true for a scalar
*/
public boolean isScalar() {
return true;
}
//
// marshal cost
//
/**
* Cost to convert to a double
*/
@Override
public int toDoubleMarshalCost() {
return Marshal.COST_EQUAL;
}
/**
* Cost to convert to a long
*/
@Override
public int toLongMarshalCost() {
return Marshal.COST_NUMERIC_LOSSY;
}
/**
* Cost to convert to an integer
*/
@Override
public int toIntegerMarshalCost() {
return Marshal.COST_NUMERIC_LOSSY;
}
/**
* Cost to convert to a short
*/
@Override
public int toShortMarshalCost() {
return Marshal.COST_NUMERIC_LOSSY;
}
/**
* Cost to convert to a byte
*/
@Override
public int toByteMarshalCost() {
return Marshal.COST_NUMERIC_LOSSY;
}
//
// conversions
//
/**
* Converts to a boolean.
*/
@Override
public boolean toBoolean() {
return _value != 0;
}
/**
* Converts to a long.
*/
@Override
public long toLong() {
if ((_value > (double) Long.MAX_VALUE)
|| (_value < (double) Long.MIN_VALUE)) {
return 0;
} else {
return (long) _value;
}
}
/**
* Converts to a double.
*/
@Override
public double toDouble() {
return _value;
}
/**
* Converts to a double.
*/
@Override
public DoubleValue toDoubleValue() {
return this;
}
/**
* Converts to a string builder
*/
@Override
public StringValue toStringBuilder(Env env) {
return env.createUnicodeBuilder().append(toString());
}
/**
* Converts to a key.
*/
@Override
public Value toKey() {
return LongValue.create((long) _value);
}
/**
* Converts to a java object.
*/
@Override
public Object toJavaObject() {
return new Double(_value);
}
/**
* Negates the value.
*/
@Override
public Value neg() {
return new DoubleValue(-_value);
}
/**
* Returns the value
*/
@Override
public Value pos() {
return this;
}
/**
* Multiplies to the following value.
*/
@Override
public Value add(Value rValue) {
return new DoubleValue(_value + rValue.toDouble());
}
/**
* Multiplies to the following value.
*/
@Override
public Value add(long lValue) {
return new DoubleValue(lValue + _value);
}
/**
* Increment the following value.
*/
@Override
public Value addOne() {
return new DoubleValue(_value + 1);
}
/**
* Increment the following value.
*/
@Override
public Value subOne() {
double next = _value - 1;
/*
if (next == (long) next)
return LongValue.create(next);
else
*/
return new DoubleValue(next);
}
/**
* Increment the following value.
*/
@Override
public Value preincr() {
return new DoubleValue(_value + 1);
}
/**
* Increment the following value.
*/
@Override
public Value predecr() {
return new DoubleValue(_value - 1);
}
/**
* Increment the following value.
*/
@Override
public Value postincr() {
return new DoubleValue(_value + 1);
}
/**
* Increment the following value.
*/
@Override
public Value postdecr() {
return new DoubleValue(_value - 1);
}
/**
* Increment the following value.
*/
@Override
public Value increment(int incr) {
return new DoubleValue(_value + incr);
}
/**
* Multiplies to the following value.
*/
@Override
public Value mul(Value rValue) {
return new DoubleValue(_value * rValue.toDouble());
}
/**
* Multiplies to the following value.
*/
@Override
public Value mul(long lValue) {
return new DoubleValue(lValue * _value);
}
/**
* Absolute value.
*/
@Override
public Value abs() {
if (_value >= 0) {
return this;
} else {
return DoubleValue.create(-_value);
}
}
/**
* Returns true for equality
*/
@Override
public boolean eql(Value rValue) {
rValue = rValue.toValue();
if (!(rValue instanceof DoubleValue)) {
return false;
}
double rDouble = ((DoubleValue) rValue)._value;
return _value == rDouble;
}
/**
* Converts to a string.
* @param env
*/
@Override
public String toString() {
long longValue = (long) _value;
double abs = _value < 0 ? -_value : _value;
int exp = (int) Math.log10(abs);
// php/0c02
if (longValue == _value && exp < 18) {
return String.valueOf(longValue);
}
if (-5 < exp && exp < 18) {
int digits = 13 - exp;
if (digits > 13) {
digits = 13;
} else if (digits < 0) {
digits = 0;
}
String v = String.format("%." + digits + "f", _value);
int len = v.length();
int nonzero = -1;
boolean dot = false;
for (len--; len >= 0; len--) {
int ch = v.charAt(len);
if (ch == '.') {
dot = true;
}
if (ch != '0' && nonzero < 0) {
if (ch == '.') {
nonzero = len - 1;
} else {
nonzero = len;
}
}
}
if (dot && nonzero >= 0) {
return v.substring(0, nonzero + 1);
} else {
return v;
}
} else {
return String.format("%.13E", _value);
}
}
/**
* Converts to an object.
*/
public Object toObject() {
return toString();
}
/**
* Prints the value.
* @param env
*/
@Override
public void print(Env env) {
env.print(toString());
}
/**
* Serializes the value.
*/
@Override
public void serialize(Env env, StringBuilder sb) {
sb.append("d:");
sb.append(_value);
sb.append(";");
}
/**
* Exports the value.
*/
@Override
public void varExport(StringBuilder sb) {
sb.append(toString());
}
//
// Java generator code
//
/**
* Generates code to recreate the expression.
*
* @param out the writer to the Java source code.
*/
@Override
public void generate(PrintWriter out)
throws IOException {
if (_value == 0) {
out.print("DoubleValue.ZERO");
} else if (_value == Double.POSITIVE_INFINITY) {
out.print("new DoubleValue(Double.POSITIVE_INFINITY)");
} else if (_value == Double.NEGATIVE_INFINITY) {
out.print("new DoubleValue(Double.NEGATIVE_INFINITY)");
} else {
out.print("new DoubleValue(" + _value + ")");
}
}
/**
* Returns the hash code
*/
@Override
public int hashCode() {
return (int) (37 + 65521 * _value);
}
/**
* Compare for equality.
*/
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (!(o instanceof DoubleValue)) {
return false;
}
DoubleValue value = (DoubleValue) o;
return _value == value._value;
}
@Override
public void varDumpImpl(Env env,
WriteStream out,
int depth,
IdentityHashMap<Value, String> valueSet)
throws IOException {
out.print("float(" + toString() + ")");
}
//
// Java Serialization
//
private Object readResolve() {
if (_value == 0) {
return ZERO;
} else {
return this;
}
}
}