package client.net.sf.saxon.ce.value;
import client.net.sf.saxon.ce.expr.XPathContext;
import client.net.sf.saxon.ce.lib.StringCollator;
import client.net.sf.saxon.ce.om.StandardNames;
import client.net.sf.saxon.ce.expr.sort.CodepointCollator;
import client.net.sf.saxon.ce.trans.Err;
import client.net.sf.saxon.ce.trans.XPathException;
import client.net.sf.saxon.ce.type.*;
import client.net.sf.saxon.ce.Configuration;
/**
* An Untyped Atomic value. This inherits from StringValue for implementation convenience, even
* though an untypedAtomic value is not a String in the data model type hierarchy.
*/
public class UntypedAtomicValue extends StringValue {
// If the value is used once as a number, it's likely that it will be used
// repeatedly as a number, so we cache the result of conversion
DoubleValue doubleValue = null;
/**
* Constructor
* @param value the String value. Null is taken as equivalent to "".
*/
public UntypedAtomicValue(CharSequence value) {
this.value = (value==null ? "" : value);
typeLabel = BuiltInAtomicType.UNTYPED_ATOMIC;
}
/**
* Create a copy of this atomic value, with a different type label
*
* @param typeLabel the type label of the new copy. The caller is responsible for checking that
* the value actually conforms to this type.
*/
public AtomicValue copyAsSubType(AtomicType typeLabel) {
UntypedAtomicValue v = new UntypedAtomicValue(value);
v.noSurrogates = noSurrogates;
v.doubleValue = doubleValue;
v.typeLabel = typeLabel;
return v;
}
/**
* Determine the primitive type of the value. This delivers the same answer as
* getItemType().getPrimitiveItemType(). The primitive types are
* the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
* and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
*/
public BuiltInAtomicType getPrimitiveType() {
return BuiltInAtomicType.UNTYPED_ATOMIC;
}
/**
* Convert a value to another primitive data type, with control over how validation is
* handled.
* @param requiredType type code of the required atomic type. This must not be a namespace-sensitive type.
* @param validate true if validation is required. If set to false, the caller guarantees that
* the value is valid for the target data type, and that further validation is therefore not required.
* Note that a validation failure may be reported even if validation was not requested.
* @return the result of the conversion, if successful. If unsuccessful, the value returned
* will be a ValidationErrorValue. The caller must check for this condition. No exception is thrown, instead
* the exception will be encapsulated within the ErrorValue.
*/
public ConversionResult convertPrimitive(BuiltInAtomicType requiredType, boolean validate) {
int req = requiredType.getFingerprint();
if (req== StandardNames.XS_STRING) {
if (value.length() == 0) {
// this case is common!
return StringValue.EMPTY_STRING;
} else {
return new StringValue(value);
}
} else if (req== StandardNames.XS_UNTYPED_ATOMIC) {
return this;
} else if (req== StandardNames.XS_DOUBLE || req== StandardNames.XS_NUMERIC) {
// for conversion to double (common in 1.0 mode), cache the result
try {
return toDouble();
} catch (XPathException e) {
return new ValidationFailure(e);
}
} else {
return super.convertPrimitive(requiredType, validate);
}
}
/**
* Convert the value to a double, returning a DoubleValue
*/
private AtomicValue toDouble() throws XPathException {
if (doubleValue == null) {
try {
double d = StringToDouble.stringToNumber(value);
doubleValue = new DoubleValue(d);
} catch (NumberFormatException e) {
throw new XPathException("Cannot convert string " + Err.wrap(value) + " to a double");
}
}
return doubleValue;
}
/**
* Compare an untypedAtomic value with another value, using a given collator to perform
* any string comparisons. This works by converting the untypedAtomic value to the type
* of the other operand, which is the correct behavior for operators like "=" and "!=",
* but not for "eq" and "ne": in the latter case, the untypedAtomic value is converted
* to a string and this method is therefore not used.
* @return -1 if the this value is less than the other, 0 if they are equal, +1 if this
* value is greater.
* @throws ClassCastException if the value cannot be cast to the type of the other operand
*/
public int compareTo(AtomicValue other, StringCollator collator, XPathContext context) {
if (other instanceof NumericValue) {
if (doubleValue == null) {
try {
doubleValue = (DoubleValue)convertPrimitive(BuiltInAtomicType.DOUBLE, true
).asAtomic();
} catch (XPathException err) {
throw new ClassCastException("Cannot convert untyped value " +
'\"' + getStringValueCS() + "\" to a double");
}
}
return doubleValue.compareTo(other);
} else if (other instanceof StringValue) {
if (collator instanceof CodepointCollator) {
// This optimization avoids creating String objects for the purpose of the comparison
return ((CodepointCollator)collator).compareCS(getStringValueCS(),
other.getStringValueCS());
} else {
return collator.compareStrings(getStringValue(), other.getStringValue());
}
} else {
final Configuration config = context.getConfiguration();
final TypeHierarchy th = config.getTypeHierarchy();
ConversionResult result =
convert((AtomicType)other.getItemType(th), true);
if (result instanceof ValidationFailure) {
throw new ClassCastException("Cannot convert untyped atomic value '" + getStringValue()
+ "' to type " + other.getItemType(th));
}
return ((Comparable)((AtomicValue)result)).compareTo(other);
}
}
/**
* Convert to Java object (for passing to external functions)
*/
// public Object convertAtomicToJava(Class target, XPathContext context) throws XPathException {
// if (target == Object.class) {
// return getStringValue();
// } else if (target.isAssignableFrom(StringValue.class)) {
// return this;
// } else if (target == String.class || target == CharSequence.class) {
// return getStringValue();
// } else if (target == boolean.class) {
// BooleanValue bval = (BooleanValue)convertPrimitive(BuiltInAtomicType.BOOLEAN, true, context).asAtomic();
// return Boolean.valueOf(bval.getBooleanValue());
// } else if (target == Boolean.class) {
// BooleanValue bval = (BooleanValue)convertPrimitive(BuiltInAtomicType.BOOLEAN, true, context).asAtomic();
// return Boolean.valueOf(bval.getBooleanValue());
// } else if (target == double.class) {
// DoubleValue dval = (DoubleValue)convertPrimitive(BuiltInAtomicType.DOUBLE, true, context).asAtomic();
// return new Double(dval.getDoubleValue());
// } else if (target == Double.class) {
// DoubleValue dval = (DoubleValue)convertPrimitive(BuiltInAtomicType.DOUBLE, true, context).asAtomic();
// return new Double(dval.getDoubleValue());
// } else if (target == float.class) {
// DoubleValue dval = (DoubleValue)convertPrimitive(BuiltInAtomicType.DOUBLE, true, context).asAtomic();
// return new Float(dval.getDoubleValue());
// } else if (target == Float.class) {
// DoubleValue dval = (DoubleValue)convertPrimitive(BuiltInAtomicType.DOUBLE, true, context).asAtomic();
// return new Float(dval.getDoubleValue());
// } else if (target == long.class) {
// Int64Value dval = (Int64Value)convertPrimitive(BuiltInAtomicType.INTEGER, true, context).asAtomic();
// return new Long(dval.longValue());
// } else if (target == Long.class) {
// Int64Value dval = (Int64Value)convertPrimitive(BuiltInAtomicType.INTEGER, true, context).asAtomic();
// return new Long(dval.longValue());
// } else if (target == int.class) {
// Int64Value dval = (Int64Value)convertPrimitive(BuiltInAtomicType.INTEGER, true, context).asAtomic();
// return new Integer((int) dval.longValue());
// } else if (target == Integer.class) {
// Int64Value dval = (Int64Value)convertPrimitive(BuiltInAtomicType.INTEGER, true, context).asAtomic();
// return new Integer((int) dval.longValue());
// } else if (target == short.class) {
// Int64Value dval = (Int64Value)convertPrimitive(BuiltInAtomicType.INTEGER, true, context).asAtomic();
// return new Short((short) dval.longValue());
// } else if (target == Short.class) {
// Int64Value dval = (Int64Value)convertPrimitive(BuiltInAtomicType.INTEGER, true, context).asAtomic();
// return new Short((short) dval.longValue());
// } else if (target == byte.class) {
// Int64Value dval = (Int64Value)convertPrimitive(BuiltInAtomicType.INTEGER, true, context).asAtomic();
// return new Byte((byte) dval.longValue());
// } else if (target == Byte.class) {
// Int64Value dval = (Int64Value)convertPrimitive(BuiltInAtomicType.INTEGER, true, context).asAtomic();
// return new Byte((byte) dval.longValue());
// } else if (target == char.class || target == Character.class) {
// if (value.length() == 1) {
// return new Character(value.charAt(0));
// } else {
// XPathException de = new XPathException("Cannot convert xs:string to Java char unless length is 1");
// de.setXPathContext(context);
// de.setErrorCode(SaxonErrorCode.SXJE0005);
// throw de;
// }
// } else {
// Object o = super.convertSequenceToJava(target, context);
// if (o == null) {
// XPathException err = new XPathException("Conversion of xs:untypedAtomic to " + target.getName() + " is not supported");
// err.setXPathContext(context);
// err.setErrorCode(SaxonErrorCode.SXJE0006);
// throw err;
// }
// return o;
// }
// }
}
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.