blob: 1aaf2c82d74d27699ce36b63260e7e206adc7d35 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2010 Andrea Bittau, University College London, and others
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Andrea Bittau - initial API and implementation from the PsychoPath XPath 2.0
* Mukul Gandhi - bug 274805 - improvements to xs:integer data type
* David Carver - bug 277770 - format of XSDouble for zero values incorrect.
* Mukul Gandhi - bug 279406 - improvements to negative zero values for xs:double
* David Carver (STAR) - bug 262765 - various numeric formatting fixes and calculations
* David Carver (STAR) - bug 262765 - fixed rounding errors.
* Jesper Steen Moller - Bug 286062 - Fix idiv error cases and increase precision
* Mukul Gandhi - bug 280798 - PsychoPath support for JDK 1.4
*******************************************************************************/
package org.eclipse.wst.xml.xpath2.processor.internal.types;
import java.math.BigDecimal;
import java.util.Iterator;
import org.eclipse.wst.xml.xpath2.api.DynamicContext;
import org.eclipse.wst.xml.xpath2.api.Item;
import org.eclipse.wst.xml.xpath2.api.ResultBuffer;
import org.eclipse.wst.xml.xpath2.api.ResultSequence;
import org.eclipse.wst.xml.xpath2.api.typesystem.TypeDefinition;
import org.eclipse.wst.xml.xpath2.processor.DynamicError;
import org.eclipse.wst.xml.xpath2.processor.ResultSequenceFactory;
import org.eclipse.wst.xml.xpath2.processor.internal.types.builtin.BuiltinTypeLibrary;
/**
* A representation of the Double datatype
*/
public class XSDouble extends NumericType {
private static final String XS_DOUBLE = "xs:double";
private Double _value;
private XPathDecimalFormat format = new XPathDecimalFormat(
"0.################E0");
/**
* Initialises a representation of the supplied number
*
* @param x
* Number to be stored
*/
public XSDouble(double x) {
_value = new Double(x);
}
/**
* Initializes a representation of 0
*/
public XSDouble() {
this(0);
}
/**
* Initialises using a String represented number
*
* @param init
* String representation of the number to be stored
*/
public XSDouble(String init) throws DynamicError {
try {
if (init.equals("-INF")) {
_value = new Double(Double.NEGATIVE_INFINITY);
} else if (init.equals("INF")) {
_value = new Double(Double.POSITIVE_INFINITY);
} else {
_value = new Double(init);
}
} catch (NumberFormatException e) {
throw DynamicError.cant_cast(null);
}
}
/**
* Creates a new representation of the String represented number
*
* @param i
* String representation of the number to be stored
* @return New XSDouble representing the number supplied
*/
public static XSDouble parse_double(String i) {
try {
Double d = null;
if (i.equals("INF")) {
d = new Double(Double.POSITIVE_INFINITY);
} else if (i.equals("-INF")) {
d = new Double(Double.NEGATIVE_INFINITY);
} else {
d = new Double(i);
}
return new XSDouble(d.doubleValue());
} catch (NumberFormatException e) {
return null;
}
}
/**
* Creates a new result sequence consisting of the retrievable double number
* in the supplied result sequence
*
* @param arg
* The result sequence from which to extract the double number.
* @throws DynamicError
* @return A new result sequence consisting of the double number supplied.
*/
public ResultSequence constructor(ResultSequence arg) throws DynamicError {
if (arg.empty())
return ResultBuffer.EMPTY;
Item aat = arg.first();
if (aat instanceof XSDuration || aat instanceof CalendarType ||
aat instanceof XSBase64Binary || aat instanceof XSHexBinary ||
aat instanceof XSAnyURI) {
throw DynamicError.invalidType();
}
if (!isCastable(aat)) {
throw DynamicError.cant_cast(null);
}
XSDouble d = castDouble(aat);
if (d == null)
throw DynamicError.cant_cast(null);
return d;
}
private boolean isCastable(Item aat) {
if (aat instanceof XSString || aat instanceof XSUntypedAtomic ||
aat instanceof NodeType) {
return true;
}
if (aat instanceof XSBoolean || aat instanceof NumericType) {
return true;
}
return false;
}
private XSDouble castDouble(Item aat) {
if (aat instanceof XSBoolean) {
if (aat.getStringValue().equals("true")) {
return new XSDouble(1.0E0);
} else {
return new XSDouble(0.0E0);
}
}
return parse_double(aat.getStringValue());
}
/**
* Retrieves the datatype's full pathname
*
* @return "xs:double" which is the datatype's full pathname
*/
public String string_type() {
return XS_DOUBLE;
}
/**
* Retrieves the datatype's name
*
* @return "double" which is the datatype's name
*/
public String type_name() {
return "double";
}
/**
* Retrieves a String representation of the Decimal value stored
*
* @return String representation of the Decimal value stored
*/
public String getStringValue() {
if (zero()) {
return "0";
}
if (negativeZero()) {
return "-0";
}
if (nan()) {
return "NaN";
}
return format.xpathFormat(_value);
}
/**
* Check for whether this XSDouble represents NaN
*
* @return True if this XSDouble represents NaN. False otherwise.
*/
public boolean nan() {
return Double.isNaN(_value.doubleValue());
}
/**
* Check for whether this XSDouble represents an infinite number (negative or positive)
*
* @return True if this XSDouble represents infinity. False otherwise.
*/
public boolean infinite() {
return Double.isInfinite(_value.doubleValue());
}
/**
* Check for whether this XSDouble represents 0
*
* @return True if this XSDouble represents 0. False otherwise.
*/
public boolean zero() {
return (Double.compare(_value.doubleValue(), 0.0E0) == 0);
}
/*
* Check for whether this XSDouble represents -0
*
* @return True if this XSDouble represents -0. False otherwise.
*
* @since 1.1
*/
public boolean negativeZero() {
return (Double.compare(_value.doubleValue(), -0.0E0) == 0);
}
/**
* Retrieves the actual value of the number stored
*
* @return The actual value of the number stored
*/
public double double_value() {
return _value.doubleValue();
}
/**
* Equality comparison between this number and the supplied representation.
* @param aa
* Representation to be compared with (must currently be of type
* XSDouble)
*
* @return True if the 2 representations represent the same number. False
* otherwise
* @since 1.1
*/
public boolean eq(AnyType aa, DynamicContext dynamicContext) throws DynamicError {
ResultSequence rs = ResultSequenceFactory.create_new(aa);
ResultSequence crs = constructor(rs);
if (crs.empty()) {
throw DynamicError.throw_type_error();
}
Item cat = crs.first();
XSDouble d = (XSDouble) cat;
if (d.nan() && nan()) {
return false;
}
Double thatvalue = new Double(d.double_value());
Double thisvalue = new Double(double_value());
return thisvalue.equals(thatvalue);
}
/**
* Comparison between this number and the supplied representation.
*
* @param arg
* Representation to be compared with (must currently be of type
* XSDouble)
* @return True if the supplied type represents a number smaller than this
* one stored. False otherwise
*/
public boolean gt(AnyType arg, DynamicContext context) throws DynamicError {
Item carg = convertArg(arg);
XSDouble val = (XSDouble) get_single_type(carg, XSDouble.class);
return double_value() > val.double_value();
}
protected Item convertArg(AnyType arg) throws DynamicError {
ResultSequence rs = ResultSequenceFactory.create_new(arg);
rs = constructor(rs);
Item carg = rs.first();
return carg;
}
/**
* Comparison between this number and the supplied representation. Currently
* no numeric type promotion exists so the supplied representation must be
* of type XSDouble.
*
* @param arg
* Representation to be compared with (must currently be of type
* XSDouble)
* @return True if the supplied type represents a number greater than this
* one stored. False otherwise
*/
public boolean lt(AnyType arg, DynamicContext context) throws DynamicError {
Item carg = convertArg(arg);
XSDouble val = (XSDouble) get_single_type(carg, XSDouble.class);
return double_value() < val.double_value();
}
// math
/**
* Mathematical addition operator between this XSDouble and the supplied
* ResultSequence.
*
* @param arg
* The ResultSequence to perform an addition with
* @return A XSDouble consisting of the result of the mathematical addition.
*/
public ResultSequence plus(ResultSequence arg) throws DynamicError {
ResultSequence carg = convertResultSequence(arg);
Item at = get_single_arg(carg);
if (!(at instanceof XSDouble))
DynamicError.throw_type_error();
XSDouble val = (XSDouble) at;
return ResultSequenceFactory.create_new(new XSDouble(double_value()
+ val.double_value()));
}
private ResultSequence convertResultSequence(ResultSequence arg)
throws DynamicError {
ResultSequence carg = arg;
Iterator it = carg.iterator();
while (it.hasNext()) {
AnyType type = (AnyType) it.next();
if (type.string_type().equals("xs:untypedAtomic") ||
type.string_type().equals("xs:string")) {
throw DynamicError.throw_type_error();
}
}
carg = constructor(carg);
return carg;
}
/**
* Mathematical subtraction operator between this XSDouble and the supplied
* ResultSequence.
*
* @param arg
* The ResultSequence to perform an subtraction with
* @return A XSDouble consisting of the result of the mathematical
* subtraction.
*/
public ResultSequence minus(ResultSequence arg) throws DynamicError {
ResultSequence carg = convertResultSequence(arg);
XSDouble val = (XSDouble) get_single_type(carg, XSDouble.class);
return ResultSequenceFactory.create_new(new XSDouble(double_value()
- val.double_value()));
}
/**
* Mathematical multiplication operator between this XSDouble and the
* supplied ResultSequence. Due to no numeric type promotion or conversion,
* the ResultSequence must be of type XSDouble.
*
* @param arg
* The ResultSequence to perform an multiplication with
* @return A XSDouble consisting of the result of the mathematical
* multiplication.
*/
public ResultSequence times(ResultSequence arg) throws DynamicError {
ResultSequence carg = convertResultSequence(arg);
XSDouble val = (XSDouble) get_single_type(carg, XSDouble.class);
return ResultSequenceFactory.create_new(new XSDouble(double_value()
* val.double_value()));
}
/**
* Mathematical division operator between this XSDouble and the supplied
* ResultSequence.
*
* @param arg
* The ResultSequence to perform an division with
* @return A XSDouble consisting of the result of the mathematical division.
*/
public ResultSequence div(ResultSequence arg) throws DynamicError {
ResultSequence carg = convertResultSequence(arg);
XSDouble val = (XSDouble) get_single_type(carg, XSDouble.class);
return ResultSequenceFactory.create_new(new XSDouble(double_value()
/ val.double_value()));
}
/**
* Mathematical integer division operator between this XSDouble and the
* supplied ResultSequence.
*
* @param arg
* The ResultSequence to perform an integer division with
* @return A XSInteger consisting of the result of the mathematical integer
* division.
*/
public ResultSequence idiv(ResultSequence arg) throws DynamicError {
ResultSequence carg = convertResultSequence(arg);
XSDouble val = (XSDouble) get_single_type(carg, XSDouble.class);
if (this.nan() || val.nan())
throw DynamicError.numeric_overflow("Dividend or divisor is NaN");
if (this.infinite())
throw DynamicError.numeric_overflow("Dividend is infinite");
if (val.zero())
throw DynamicError.div_zero(null);
BigDecimal result = new BigDecimal((double_value() / val.double_value()));
return ResultSequenceFactory.create_new(new XSInteger(result.toBigInteger()));
}
/**
* Mathematical modulus operator between this XSDouble and the supplied
* ResultSequence.
*
* @param arg
* The ResultSequence to perform a modulus with
* @return A XSDouble consisting of the result of the mathematical modulus.
*/
public ResultSequence mod(ResultSequence arg) throws DynamicError {
ResultSequence carg = convertResultSequence(arg);
XSDouble val = (XSDouble) get_single_type(carg, XSDouble.class);
return ResultSequenceFactory.create_new(new XSDouble(double_value()
% val.double_value()));
}
/**
* Negation of the number stored
*
* @return A XSDouble representing the negation of this XSDecimal
*/
public ResultSequence unary_minus() {
return ResultSequenceFactory.create_new(new XSDouble(-1
* double_value()));
}
// functions
/**
* Absolutes the number stored
*
* @return A XSDouble representing the absolute value of the number stored
*/
public NumericType abs() {
return new XSDouble(Math.abs(double_value()));
}
/**
* Returns the smallest integer greater than the number stored
*
* @return A XSDouble representing the smallest integer greater than the
* number stored
*/
public NumericType ceiling() {
return new XSDouble(Math.ceil(double_value()));
}
/**
* Returns the largest integer smaller than the number stored
*
* @return A XSDouble representing the largest integer smaller than the
* number stored
*/
public NumericType floor() {
return new XSDouble(Math.floor(double_value()));
}
/**
* Returns the closest integer of the number stored.
*
* @return A XSDouble representing the closest long of the number stored.
*/
public NumericType round() {
BigDecimal value = new BigDecimal(_value.doubleValue());
BigDecimal round = value.setScale(0, BigDecimal.ROUND_HALF_UP);
return new XSDouble(round.doubleValue());
}
/**
* Returns the closest integer of the number stored.
*
* @return A XSDouble representing the closest long of the number stored.
*/
public NumericType round_half_to_even() {
return round_half_to_even(0);
}
/**
* Returns the closest integer of the number stored with the specified
* precision.
*
* @param precision
* An integer precision
* @return A XSDouble representing the closest long of the number stored.
*/
public NumericType round_half_to_even(int precision) {
BigDecimal value = new BigDecimal(_value.doubleValue());
BigDecimal round = value.setScale(precision, BigDecimal.ROUND_HALF_EVEN);
return new XSDouble(round.doubleValue());
}
public TypeDefinition getTypeDefinition() {
return BuiltinTypeLibrary.XS_DOUBLE;
}
public Object getNativeValue() {
return double_value();
}
}