001: package net.sf.saxon.value;
002:
003: import net.sf.saxon.expr.XPathContext;
004: import net.sf.saxon.sort.CodepointCollator;
005: import net.sf.saxon.trans.XPathException;
006: import net.sf.saxon.type.*;
007:
008: import java.util.Comparator;
009:
010: /**
011: * An Untyped Atomic value. This inherits from StringValue for implementation convenience, even
012: * though an untypedAtomic value is not a String in the data model type hierarchy.
013: */
014:
015: public class UntypedAtomicValue extends StringValue {
016:
017: public static final UntypedAtomicValue ZERO_LENGTH_UNTYPED = new UntypedAtomicValue(
018: "");
019:
020: // If the value is used once as a number, it's likely that it will be used
021: // repeatedly as a number, so we cache the result of conversion
022:
023: DoubleValue doubleValue = null;
024:
025: /**
026: * Constructor
027: * @param value the String value. Null is taken as equivalent to "".
028: */
029:
030: public UntypedAtomicValue(CharSequence value) {
031: this .value = (value == null ? "" : value);
032: }
033:
034: /**
035: * Return the type of the expression
036: * @return Type.UNTYPED_ATOMIC (always)
037: * @param th
038: */
039:
040: public ItemType getItemType(TypeHierarchy th) {
041: return Type.UNTYPED_ATOMIC_TYPE;
042: }
043:
044: /**
045: * Convert to target data type
046: */
047:
048: public AtomicValue convertPrimitive(BuiltInAtomicType requiredType,
049: boolean validate, XPathContext context) {
050: int req = requiredType.getFingerprint();
051: if (req == Type.STRING) {
052: if (value.length() == 0) {
053: // this case is common!
054: return StringValue.EMPTY_STRING;
055: } else {
056: return new StringValue(value);
057: }
058: } else if (req == Type.DOUBLE || req == Type.NUMBER) {
059: // for conversion to double (common in 1.0 mode), cache the result
060: if (doubleValue == null) {
061: AtomicValue v = super .convertPrimitive(requiredType,
062: validate, context);
063: if (v instanceof DoubleValue) {
064: // the alternative is that it's an ErrorValue
065: doubleValue = (DoubleValue) v;
066: }
067: return v;
068: }
069: return doubleValue;
070: } else {
071: return super .convertPrimitive(requiredType, validate,
072: context);
073: }
074: }
075:
076: /**
077: * Compare an untypedAtomic value with another value, using a given collator to perform
078: * any string comparisons. This works by converting the untypedAtomic value to the type
079: * of the other operand, which is the correct behavior for operators like "=" and "!=",
080: * but not for "eq" and "ne": in the latter case, the untypedAtomic value is converted
081: * to a string and this method is therefore not used.
082: * @return -1 if the this value is less than the other, 0 if they are equal, +1 if this
083: * value is greater.
084: */
085:
086: public int compareTo(Object other, Comparator collator,
087: XPathContext context) {
088: if (other instanceof NumericValue) {
089: if (doubleValue == null) {
090: try {
091: doubleValue = (DoubleValue) convert(Type.DOUBLE,
092: context); // FIXED in 8.6
093: } catch (XPathException err) {
094: throw new ClassCastException(
095: "Cannot convert untyped value " + '\"'
096: + getStringValueCS()
097: + "\" to a double");
098: }
099: }
100: return doubleValue.compareTo(other);
101: } else if (other instanceof StringValue) {
102: if (collator instanceof CodepointCollator) {
103: // This optimization avoids creating String objects for the purpose of the comparison
104: return ((CodepointCollator) collator).compareCS(
105: getStringValueCS(), ((StringValue) other)
106: .getStringValueCS());
107: } else {
108: return collator.compare(getStringValue(),
109: ((StringValue) other).getStringValue());
110: }
111: } else if (other instanceof AtomicValue) {
112: final TypeHierarchy th = context.getNamePool()
113: .getTypeHierarchy();
114: AtomicValue conv = convert((AtomicType) ((Value) other)
115: .getItemType(th), context, true);
116: if (conv instanceof ValidationErrorValue) {
117: throw new ClassCastException(
118: "Cannot convert untyped atomic value '"
119: + getStringValue() + "' to type "
120: + ((Value) other).getItemType(th));
121: }
122: if (!(conv instanceof Comparable)) {
123: throw new ClassCastException("Type "
124: + ((Value) other).getItemType(th)
125: + " is not ordered");
126: }
127: return ((Comparable) conv).compareTo(other);
128:
129: } else {
130: // I'm not sure if we need this, but it does no harm
131: return collator.compare(getStringValue(), other.toString());
132: }
133: }
134:
135: /**
136: * Compare two values for equality. This supports identity constraints in XML Schema,
137: * which allow list-valued elements and attributes to participate in key and uniqueness constraints.
138: * This method returns false if any error occurs during the comparison, or if any of the items
139: * in either sequence is a node rather than an atomic value. The default implementation of
140: * schemaEquals() is the same as equals(), but subclasses can override this.
141: */
142:
143: public boolean schemaEquals(Value obj) {
144: throw new UnsupportedOperationException(
145: "schemaEquals() cannot be applied to untyped atomic values");
146: }
147:
148: }
149:
150: //
151: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
152: // you may not use this file except in compliance with the License. You may obtain a copy of the
153: // License at http://www.mozilla.org/MPL/
154: //
155: // Software distributed under the License is distributed on an "AS IS" basis,
156: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
157: // See the License for the specific language governing rights and limitations under the License.
158: //
159: // The Original Code is: all this file.
160: //
161: // The Initial Developer of the Original Code is Michael H. Kay.
162: //
163: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
164: //
165: // Contributor(s): none.
166: //
|