Package net.sf.saxon.value

Source Code of net.sf.saxon.value.Value$ValueSchemaComparable

package net.sf.saxon.value;
import net.sf.saxon.AugmentedSource;
import net.sf.saxon.Configuration;
import net.sf.saxon.event.Builder;
import net.sf.saxon.event.PipelineConfiguration;
import net.sf.saxon.event.Sender;
import net.sf.saxon.event.SequenceReceiver;
import net.sf.saxon.expr.*;
import net.sf.saxon.functions.Aggregate;
import net.sf.saxon.om.*;
import net.sf.saxon.tinytree.TinyBuilder;
import net.sf.saxon.trans.SaxonErrorCode;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.*;

import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.net.URL;
import java.util.*;
import java.util.regex.Pattern;

/**
* A value is the result of an expression but it is also an expression in its own right.
* Note that every value can be regarded as a sequence - in many cases, a sequence of
* length one.
*/

public abstract class Value
        implements Serializable, SequenceIterable, ValueRepresentation /*, Comparable */ {

    /**
     * Static method to make a Value from a given Item (which may be either an AtomicValue
     * or a NodeInfo
     * @param val       The supplied value, or null, indicating the empty sequence.
     * @return          The supplied value, if it is a value, or a SingletonNode that
     *                  wraps the item, if it is a node. If the supplied value was null,
     *                  return an EmptySequence
     */

    public static Value asValue(ValueRepresentation val) {
        if (val instanceof Value) {
            return (Value)val;
        } else if (val == null) {
            return EmptySequence.getInstance();
        } else {
            return new SingletonNode((NodeInfo)val);
        }
    }

    /**
     * Static method to make an Item from a Value
     * @param value the value to be converted
     * @return null if the value is an empty sequence; or the only item in the value
     * if it is a singleton sequence
     * @throws XPathException if the Value contains multiple items
     */

    public static Item asItem(ValueRepresentation value) throws XPathException {
        if (value instanceof Item) {
            return (Item)value;
        } else {
            return ((Value)value).asItem();
        }
    }

    /**
     * Return the value in the form of an Item
     * @return the value in the form of an Item
     */

    public Item asItem() throws XPathException {
        SequenceIterator iter = iterate();
        Item item = iter.next();
        if (item == null) {
            return null;
        } else if (iter.next() != null) {
            throw new XPathException("Attempting to access a sequence as a singleton item");
        } else {
            return item;
        }
    }

    /**
     * Static method to get a Value from an Item
     * @param item the supplied item
     * @return the item expressed as a Value
     */

    public static Value fromItem(Item item) {
        if (item == null) {
            return EmptySequence.getInstance();
        } else if (item instanceof AtomicValue) {
            return (AtomicValue)item;
        } else {
            return new SingletonNode((NodeInfo)item);
        }
    }

    /**
     * Static method to get an Iterator over any ValueRepresentation (which may be either a Value
     * or a NodeInfo
     * @param val       The supplied value, or null, indicating the empty sequence.
     * @return          The supplied value, if it is a value, or a SingletonNode that
     *                  wraps the item, if it is a node. If the supplied value was null,
     *                  return an EmptySequence
     */

    public static SequenceIterator asIterator(ValueRepresentation val) throws XPathException {
        if (val instanceof Value) {
            return ((Value)val).iterate();
        } else if (val == null) {
            return EmptyIterator.getInstance();
        } else {
            return SingletonIterator.makeIterator((NodeInfo)val);
        }
    }

    /**
     * Static method to convert strings to doubles.
     * @param s the String to be converted
     * @return a double representing the value of the String
     * @throws NumberFormatException if the value cannot be converted
    */

    public static double stringToNumber(CharSequence s) throws NumberFormatException {
        // first try to parse simple numbers by hand (it's cheaper)
        int len = s.length();
        if (len < 9) {
            boolean useJava = false;
            long num = 0;
            int dot = -1;
            int lastDigit = -1;
            boolean onlySpaceAllowed = false;
            loop: for (int i=0; i<s.length(); i++) {
                char c = s.charAt(i);
                switch (c) {
                    case ' ':
                    case '\n':
                    case '\t':
                    case '\r':
                        if (lastDigit != -1) {
                            onlySpaceAllowed = true;
                        }
                        break;
                    case '0': case '1': case '2': case '3': case '4':
                    case '5': case '6': case '7': case '8': case '9':
                        if (onlySpaceAllowed) {
                            throw new NumberFormatException("Numeric value contains embedded whitespace");
                        }
                        lastDigit = i;
                        num = num*10 + (c - '0');
                        break;
                    case '.':
                        if (onlySpaceAllowed) {
                            throw new NumberFormatException("Numeric value contains embedded whitespace");
                        }
                        if (dot != -1) {
                            throw new NumberFormatException("Only one decimal point allowed");
                        }
                        dot = i;
                        break;
                    default:
                        // there's something like a sign or an exponent: take the slow train instead
                        useJava = true;
                        break loop;
                }
            }
            if (!useJava) {
                if (lastDigit == -1) {
                    throw new NumberFormatException("No digits found");
                } else if (dot == -1 || dot > lastDigit) {
                    return (double)num;
                } else {
                    int afterPoint = lastDigit - dot;
                    return ((double)num)/powers[afterPoint];
                }
            }
        }
        String n = Whitespace.trimWhitespace(s).toString();
        if ("INF".equals(n)) {
            return Double.POSITIVE_INFINITY;
        } else if ("-INF".equals(n)) {
            return Double.NEGATIVE_INFINITY;
        } else if ("NaN".equals(n)) {
            return Double.NaN;
        } else if (!doublePattern.matcher(n).matches()) {
            // Need to disallow values that are OK in Java but not in XPath, specifically
            // - special values like +NaN or -Infinity
            // - hex digits
            // - binary exponents
            // TODO: this checking incurs a performance hit. Perhaps we should do the whole conversion in-house
            throw new NumberFormatException("Invalid characters in float/double value");
        } else {
            return Double.parseDouble(n);
        }
    }

    private static double[] powers = new double[]{1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};
    private static Pattern doublePattern = Pattern.compile("^[0-9.eE+-]+$");

    /**
     * Get a SequenceIterator over a ValueRepresentation
     * @param val the value to iterate over
     * @return the iterator
     */

    public static SequenceIterator getIterator(ValueRepresentation val) throws XPathException {
        if (val instanceof Value) {
            return ((Value)val).iterate();
        } else if (val instanceof NodeInfo) {
            return SingletonIterator.makeIterator((NodeInfo)val);
        } else if (val == null) {
            throw new AssertionError("Value of variable is undefined (null)");
        } else {
            throw new AssertionError("Unknown value representation " + val.getClass());
        }
    }

    /**
     * Iterate over the items contained in this value.
     * @return an iterator over the sequence of items
     * @throws XPathException if a dynamic error occurs. This is possible only in the case of values
     * that are materialized lazily, that is, where the iterate() method leads to computation of an
     * expression that delivers the values.
     */

    public abstract SequenceIterator iterate() throws XPathException;

    /**
     * Return an iterator over the results of evaluating an expression
     * @param context the dynamic evaluation context (not used in this implementation)
     * @return an iterator over the items delivered by the expression
     */

    public final SequenceIterator iterate(XPathContext context) throws XPathException {
        // Note, this method, and the SequenceIterable interface, are used from XQuery compiled code
        return iterate();
    }

    /**
     * Get the value of the item as a CharSequence. This is in some cases more efficient than
     * the version of the method that returns a String.
     */

    public CharSequence getStringValueCS() throws XPathException {
        return getStringValue();
    }

    /**
     * Get the canonical lexical representation as defined in XML Schema. This is not always the same
     * as the result of casting to a string according to the XPath rules.
     * @return the canonical lexical representation if defined in XML Schema; otherwise, the result
     * of casting to string according to the XPath 2.0 rules
     */

    public CharSequence getCanonicalLexicalRepresentation() {
        try {
            return getStringValueCS();
        } catch (XPathException err) {
            throw new IllegalStateException("Failed to get canonical lexical representation: " + err.getMessage());
        }
    }

    /**
     * Determine the data type of the items in the expression, if possible
     * @return for the default implementation: AnyItemType (not known)
     * @param th The TypeHierarchy. Can be null if the target is an AtomicValue.
     */

    public ItemType getItemType(TypeHierarchy th) {
        return AnyItemType.getInstance();
    }

    /**
     * Determine the cardinality
     * @return the cardinality
     */

    public int getCardinality() {
        try {
            SequenceIterator iter = iterate();
            Item next = iter.next();
            if (next == null) {
                return StaticProperty.EMPTY;
            } else {
                if (iter.next() != null) {
                    return StaticProperty.ALLOWS_ONE_OR_MORE;
                } else {
                    return StaticProperty.EXACTLY_ONE;
                }
            }
        } catch (XPathException err) {
            // can't actually happen
            return StaticProperty.ALLOWS_ZERO_OR_MORE;
        }
    }

    /**
     * Get the n'th item in the sequence (starting from 0). This is defined for all
     * Values, but its real benefits come for a sequence Value stored extensionally
     * (or for a MemoClosure, once all the values have been read)
     * @param n position of the required item, counting from zero.
     * @return the n'th item in the sequence, where the first item in the sequence is
     * numbered zero. If n is negative or >= the length of the sequence, returns null.
     */

    public Item itemAt(int n) throws XPathException {
        if (n < 0) {
            return null;
        }
        int i = 0;        // indexing is zero-based
        SequenceIterator iter = iterate();
        while (true) {
            Item item = iter.next();
            if (item == null) {
                return null;
            }
            if (i++ == n) {
                return item;
            }
        }
    }

    /**
     * Get the length of the sequence
     * @return the number of items in the sequence
     */

    public int getLength() throws XPathException {
        return Aggregate.count(iterate());
    }

    /**
      * Process the value as an instruction, without returning any tail calls
      * @param context The dynamic context, giving access to the current node,
      * the current variables, etc.
      */

    public void process(XPathContext context) throws XPathException {
        SequenceIterator iter = iterate();
        SequenceReceiver out = context.getReceiver();
        while (true) {
            Item it = iter.next();
            if (it==null) break;
            out.append(it, 0, NodeInfo.ALL_NAMESPACES);
        }
    }


    /**
     * Convert the value to a string, using the serialization rules.
     * For atomic values this is the same as a cast; for sequence values
     * it gives a space-separated list.
     * @throws XPathException The method can fail if evaluation of the value
     * has been deferred, and if a failure occurs during the deferred evaluation.
     * No failure is possible in the case of an AtomicValue.
     */

    public String getStringValue() throws XPathException {
        FastStringBuffer sb = new FastStringBuffer(1024);
        SequenceIterator iter = iterate();
        Item item = iter.next();
        if (item != null) {
            while (true) {
                sb.append(item.getStringValueCS());
                item = iter.next();
                if (item == null) {
                    break;
                }
                sb.append(' ');
            }
        }
        return sb.toString();
    }


    /**
     * Get the effective boolean value of the expression. This returns false if the value
     * is the empty sequence, a zero-length string, a number equal to zero, or the boolean
     * false. Otherwise it returns true.
     *
     * @exception XPathException if any dynamic error occurs evaluating the
     *     expression
     * @return the effective boolean value
     */

    public boolean effectiveBooleanValue() throws XPathException {
        return ExpressionTool.effectiveBooleanValue(iterate());
    }

    /**
     * Get a Comparable value that implements the XML Schema ordering comparison semantics for this value.
     * The default implementation is written to compare sequences of atomic values.
     * This method is overridden for AtomicValue and its subclasses.
     *
     * <p>In the case of data types that are partially ordered, the returned Comparable extends the standard
     * semantics of the compareTo() method by returning the value {@link #INDETERMINATE_ORDERING} when there
     * is no defined order relationship between two given values.</p>
     *
     * @return a Comparable that follows XML Schema comparison rules
     */

    public Comparable getSchemaComparable() {
        return new ValueSchemaComparable();
    }

    private class ValueSchemaComparable implements Comparable {
        public Value getValue() {
            return Value.this;
        }
        public int compareTo(Object obj) {
            try {
                if (obj instanceof ValueSchemaComparable) {
                    SequenceIterator iter1 = getValue().iterate();
                    SequenceIterator iter2 = ((ValueSchemaComparable)obj).getValue().iterate();
                    while (true) {
                        Item item1 = iter1.next();
                        Item item2 = iter2.next();
                        if (item1 == null && item2 == null) {
                            return 0;
                        }
                        if (item1 == null) {
                            return -1;
                        } else if (item2 == null) {
                            return +1;
                        }
                        if (item1 instanceof NodeInfo || item2 instanceof NodeInfo) {
                            throw new UnsupportedOperationException("Sequences containing nodes are not schema-comparable");
                        }
                        int c = ((AtomicValue)item1).getSchemaComparable().compareTo(
                                    ((AtomicValue)item2).getSchemaComparable());
                        if (c != 0) {
                            return c;
                        }
                    }
                } else {
                    return INDETERMINATE_ORDERING;
                }
            } catch (XPathException e) {
                throw new AssertionError("Failure comparing schema values: " + e.getMessage());
            }
        }

        public boolean equals(Object obj) {
            return compareTo(obj) == 0;
        }

        public int hashCode() {
            try {
                int hash = 0x06639662// arbitrary seed
                SequenceIterator iter = getValue().iterate();
                while (true) {
                    Item item = iter.next();
                    if (item == null) {
                        return hash;
                    }
                    hash ^= ((AtomicValue)item).getSchemaComparable().hashCode();
                }
            } catch (XPathException e) {
                return 0;
            }
        }
    }

    /**
     * Constant returned by compareTo() method to indicate an indeterminate ordering between two values
     */

    public static final int INDETERMINATE_ORDERING = Integer.MIN_VALUE;

    /**
     * Compare two (sequence) values for equality. This method implements the XPath eq operator, for cases
     * where it is defined. For values containing nodes, nodes are compared for identity.
     * In cases where eq is not defined, it throws ClassCastException. In cases
     * where the result of eq is an empty sequence, this function returns false, except that two empty
     * sequences compare equal. The method also returns a ClassCastException
     * if any failure occurs evaluating either of the values.
     */

    public boolean equals(Object obj) {
        throw new UnsupportedOperationException("Value.equals()");
//        try {
//            if (obj instanceof Value) {
//                SequenceIterator iter1 = iterate();
//                SequenceIterator iter2 = ((Value)obj).iterate();
//                while (true) {
//                    Item item1 = iter1.next();
//                    Item item2 = iter2.next();
//                    if (item1 == null || item2 == null) {
//                        return item1 == null && item2 == null;
//                    }
//                    if (!item1.equals(item2)) {
//                        return false;
//                    }
//                }
//            } else {
//                return false;
//            }
//        } catch (XPathException e) {
//            throw new ClassCastException(e.getMessage());
//        }
    }


    /**
     * Check statically that the results of the expression are capable of constructing the content
     * of a given schema type.
     * @param parentType The schema type
     * @param env the static context
     * @param whole true if this value accounts for the entire content of the containing node
     * @throws XPathException if the expression doesn't match the required content type
     */

    public void checkPermittedContents(SchemaType parentType, StaticContext env, boolean whole) throws XPathException {
        //return;
    }

    /**
     * Reduce a value to its simplest form. If the value is a closure or some other form of deferred value
     * such as a FunctionCallPackage, then it is reduced to a SequenceExtent. If it is a SequenceExtent containing
     * a single item, then it is reduced to that item. One consequence that is exploited by class FilterExpression
     * is that if the value is a singleton numeric value, then the result will be an instance of NumericValue
     * @return the value in simplified form
     */

    public Value reduce() throws XPathException {
        return this;
    }

    /**
     * Convert to Java object (for passing to external functions)
     * @param target the required target class
     * @param context the XPath dynamic evaluation context
     * @return the (boxed) Java object that best represents the XPath value
     */

    public Object convertToJava(Class target, XPathContext context) throws XPathException {

        if (target == Object.class) {
            List list = new ArrayList(20);
            return convertToJavaList(list, context);
        }

        // See if the extension function is written to accept native Saxon objects

        if (target.isAssignableFrom(getClass())) {
            return this;
        } else if (target.isAssignableFrom(SequenceIterator.class)) {
            return iterate();
        }

        // Offer the object to registered external object models

        if ((this instanceof ObjectValue || !(this instanceof AtomicValue)) && !(this instanceof EmptySequence)) {
            List externalObjectModels = context.getConfiguration().getExternalObjectModels();
            for (int m=0; m<externalObjectModels.size(); m++) {
                ExternalObjectModel model = (ExternalObjectModel)externalObjectModels.get(m);
                Object object = model.convertXPathValueToObject(this, target, context);
                if (object != null) {
                    return object;
                }
            }
        }

        if (Collection.class.isAssignableFrom(target)) {
            Collection list;
            if (target.isAssignableFrom(ArrayList.class)) {
                list = new ArrayList(100);
            } else {
                try {
                    list = (Collection)target.newInstance();
                } catch (InstantiationException e) {
                    XPathException de = new XPathException("Cannot instantiate collection class " + target);
                    de.setXPathContext(context);
                    throw de;
                } catch (IllegalAccessException e) {
                    XPathException de = new XPathException("Cannot access collection class " + target);
                    de.setXPathContext(context);
                    throw de;
                }
            }
            return convertToJavaList(list, context);
        } else if (target.isArray()) {
            Class component = target.getComponentType();
            if (component.isAssignableFrom(Item.class) ||
                    component.isAssignableFrom(NodeInfo.class) ||
                    component.isAssignableFrom(DocumentInfo.class)) {
                Value extent = this;
                if (extent instanceof Closure) {
                    extent = Value.asValue(SequenceExtent.makeSequenceExtent(extent.iterate()));
                }
                int length = extent.getLength();
                Object array = Array.newInstance(component, length);
                SequenceIterator iter = extent.iterate();
                for (int i=0; i<length; i++) {
                    Item item = iter.next();
                    try {
                        Array.set(array, i, item);
                    } catch (IllegalArgumentException err) {
                        XPathException d = new XPathException("Item " + i + " in supplied sequence cannot be converted " +
                                "to the component type of the Java array (" + component + ')', err);
                        d.setErrorCode(SaxonErrorCode.SXJE0023);
                        d.setXPathContext(context);
                        throw d;
                    }
                }
                return array;
            } else /* if (!(this instanceof AtomicValue)) */ {
                // try atomizing the sequence, unless this is a single atomic value, in which case we've already
                // tried that.
                SequenceIterator it = Atomizer.getAtomizingIterator(iterate());
                int length;
                if ((it.getProperties() & SequenceIterator.LAST_POSITION_FINDER) == 0) {
                    SequenceExtent extent = new SequenceExtent(it);
                    length = extent.getLength();
                    it = extent.iterate();
                } else {
                    length = ((LastPositionFinder)it).getLastPosition();
                }
                Object array = Array.newInstance(component, length);
                for (int i=0; i<length; i++) {
                    try {
                        AtomicValue val = (AtomicValue)it.next();
                        Object jval = val.convertToJava(component, context);
                        Array.set(array, i, jval);
                    } catch (XPathException err) {
                        XPathException d = new XPathException("Cannot convert item in atomized sequence to the component type of the Java array", err);
                        d.setErrorCode(SaxonErrorCode.SXJE0023);
                        d.setXPathContext(context);
                        throw d;
                    }
                }
                return array;
            }

        } else if (target.isAssignableFrom(Item.class) ||
                target.isAssignableFrom(NodeInfo.class) ||
                target.isAssignableFrom(DocumentInfo.class)) {

            // try passing the first item in the sequence provided it is the only one
            SequenceIterator iter = iterate();
            Item first = null;
            while (true) {
                Item next = iter.next();
                if (next == null) {
                    break;
                }
                if (first != null) {
                    XPathException err = new XPathException("Sequence contains more than one value; Java method expects only one", SaxonErrorCode.SXJE0022);
                    err.setXPathContext(context);
                    throw err;
                }
                first = next;
            }
            if (first == null) {
                // sequence is empty; pass a Java null
                return null;
            }
            if (target.isAssignableFrom(first.getClass())) {
                // covers Item and NodeInfo
                return first;
            }

            Object n = first;
            while (n instanceof VirtualNode) {
                // If we've got a wrapper around a DOM or JDOM node, and the user wants a DOM
                // or JDOM node, we unwrap it
                Object vn = ((VirtualNode) n).getUnderlyingNode();
                if (target.isAssignableFrom(vn.getClass())) {
                    return vn;
                } else {
                    n = vn;
                }
            }

            throw new XPathException(
                    "Cannot convert supplied XPath value to the required type for the extension function",
                    SaxonErrorCode.SXJE0021);
        } else if (!(this instanceof AtomicValue)) {
            // try atomizing the value, unless this is an atomic value, in which case we've already tried that
            SequenceIterator it = Atomizer.getAtomizingIterator(iterate());
            Item first = null;
            while (true) {
                Item next = it.next();
                if (next == null) {
                    break;
                }
                if (first != null) {
                    XPathException err = new XPathException("Sequence contains more than one value; Java method expects only one", SaxonErrorCode.SXJE0022);
                    err.setXPathContext(context);
                    throw err;
                }
                first = next;
            }
            if (first == null) {
                // sequence is empty; pass a Java null
                return null;
            }
            if (target.isAssignableFrom(first.getClass())) {
                return first;
            } else {
                return ((AtomicValue)first).convertToJava(target, context);
            }
        } else {
            throw new XPathException("Cannot convert supplied XPath value to the required type for the extension function",
                    SaxonErrorCode.SXJE0021);
        }
    }
   
    /**
     * Convert this XPath value to a Java collection
     * @param list an empty Collection, to which the relevant values will be added
     * @param context the evaluation context
     * @return the supplied list, with relevant values added
     * @throws XPathException
     */

    private Collection convertToJavaList(Collection list, XPathContext context) throws XPathException {
        // TODO: with JDK 1.5, check to see if the item type of the list is constrained
        SequenceIterator iter = iterate();
        while (true) {
            Item it = iter.next();
            if (it == null) {
                return list;
            }
            if (it instanceof AtomicValue) {
                list.add(((AtomicValue)it).convertToJava(Object.class, context));
            } else if (it instanceof VirtualNode) {
                list.add(((VirtualNode)it).getUnderlyingNode());
            } else {
                list.add(it);
            }
        }
    }

    /**
    * Convert a Java object to an XPath value. This method is called to handle the result
    * of an external function call, and also to process global parameters passed to the stylesheet or query.
    * @param object The Java object to be converted
    * @param requiredType The required type of the result (if known)
    * @param context The XPath dynamic context
     * @return the result of converting the value. If the value is null, returns null.
    */

    public static Value convertJavaObjectToXPath(Object object, SequenceType requiredType, XPathContext context)
                                          throws XPathException {

        Configuration config= context.getConfiguration();
        ItemType requiredItemType = requiredType.getPrimaryType();

        // TODO: make more use of the requiredType, e.g. to decide what to convert a Date into.

        if (object==null) {
            return EmptySequence.getInstance();
        }

        // Offer the object to all the registered external object models

        List externalObjectModels = config.getExternalObjectModels();
        for (int m=0; m<externalObjectModels.size(); m++) {
            ExternalObjectModel model = (ExternalObjectModel)externalObjectModels.get(m);
            Value val = model.convertObjectToXPathValue(object, config);
            if (val != null && TypeChecker.testConformance(val, requiredType, context) == null) {
                return val;
            }
        }

        if (requiredItemType instanceof ExternalObjectType) {
            Class theClass = ((ExternalObjectType)requiredItemType).getJavaClass();
            if (theClass.isAssignableFrom(object.getClass())) {
                return new ObjectValue(object, (ExternalObjectType)requiredItemType);
            } else {
                throw new XPathException("Supplied parameter value is not of class " + theClass.getName());
            }
        }

        return convertToBestFit(object, config);

    }

    private static Value convertToBestFit(Object object, Configuration config) throws XPathException {
        if (object instanceof String) {
            return StringValue.makeStringValue((String)object);

        } else if (object instanceof Character) {
            return new StringValue(object.toString());

        } else if (object instanceof Boolean) {
            return BooleanValue.get(((Boolean)object).booleanValue());

        } else if (object instanceof Double) {
            return new DoubleValue(((Double)object).doubleValue());

        } else if (object instanceof Float) {
            return new FloatValue(((Float)object).floatValue());

        } else if (object instanceof Short) {
            return new Int64Value(((Short)object).shortValue(), BuiltInAtomicType.SHORT, false);
        } else if (object instanceof Integer) {
            return new Int64Value(((Integer)object).intValue(), BuiltInAtomicType.INT, false);
        } else if (object instanceof Long) {
            return new Int64Value(((Long)object).longValue(), BuiltInAtomicType.LONG, false);
        } else if (object instanceof Byte) {
            return new Int64Value(((Byte)object).byteValue(), BuiltInAtomicType.BYTE, false);

        } else if (object instanceof BigInteger) {
            return BigIntegerValue.makeIntegerValue(((BigInteger)object));

        } else if (object instanceof BigDecimal) {
            return new DecimalValue(((BigDecimal)object));

//        } else if (object instanceof QName) {
//            return new QNameValue((QName)object);
            // TODO: reinstate above lines in JDK 1.5
        } else if (object.getClass().getName().equals("javax.xml.namespace.QName")) {
            return makeQNameValue(object, config);

        } else if (object instanceof URI) {
            return new AnyURIValue(object.toString());

        } else if (object instanceof URL) {
            return new AnyURIValue(object.toString());

        } else if (object instanceof Date) {
            return DateTimeValue.fromJavaDate((Date)object);

        // TODO: recognize GregorianCalendar...

        } else if (object instanceof Closure) {
            // Force eager evaluation, because of problems with side-effects.
            // (The value might depend on data that is mutable.)
            //return Value.asValue(ExpressionTool.evaluate((Closure)object, ExpressionTool.ITERATE_AND_MATERIALIZE, config.getConversionContext(), 10));
            return Value.asValue(
                    SequenceExtent.makeSequenceExtent(((Closure)object).iterate()));
        } else if (object instanceof Value) {
            return (Value)object;

        } else if (object instanceof NodeInfo) {
            if (!((NodeInfo)object).getConfiguration().isCompatible(config)) {
                throw new XPathException(
                        "Externally-supplied NodeInfo belongs to a different and incompatible Configuration",
                        SaxonErrorCode.SXXP0004);
            }
            return new SingletonNode((NodeInfo)object);

        } else if (object instanceof SequenceIterator) {
            return Closure.makeIteratorClosure((SequenceIterator)object);

        } else if (object instanceof List) {
            Item[] array = new Item[((List)object).size()];
            int a = 0;
            for (Iterator i=((List)object).iterator(); i.hasNext(); ) {
                Object obj = i.next();
                if (obj instanceof NodeInfo) {
                    if (!((NodeInfo)obj).getConfiguration().isCompatible(config)) {
                        throw new XPathException("Externally-supplied NodeInfo belongs to wrong Configuration",
                        SaxonErrorCode.SXXP0004);
                    }
                    array[a++] = (NodeInfo)obj;
                } else {
                    Value v = convertToBestFit(obj, config);
                    if (v!=null) {
                        if (v instanceof Item) {
                            array[a++] = (Item)v;
                        } else if (v instanceof EmptySequence) {
                            // no action
                        } else if (v instanceof SingletonNode) {
                            NodeInfo node = ((SingletonNode)v).getNode();
                            if (node != null) {
                                array[a++] = node;
                            }
                        } else {
                            throw new XPathException(
                                    "Returned List contains an object that cannot be converted to an Item (" + obj.getClass() + ')',
                                    SaxonErrorCode.SXJE0051);
                        }
                    }
                }
            }

            return new SequenceExtent(array);

        } else if (object instanceof Object[]) {
            Object[] arrayObject = (Object[])object;
            Item[] newArray = new Item[arrayObject.length];
             int a = 0;
             for (int i = 0; i < arrayObject.length; i++){
                 Object itemObject = arrayObject[i];
                 if (itemObject instanceof NodeInfo) {
                     if (!((NodeInfo)itemObject).getConfiguration().isCompatible(config)) {
                         throw new XPathException(
                                 "Externally-supplied NodeInfo belongs to a different and incompatible Configuration",
                                 SaxonErrorCode.SXXP0004);
                     }
                     newArray[a++] = (NodeInfo)itemObject;
                 } else if (itemObject != null) {
                     Value v = convertToBestFit(itemObject, config);
                     if (v!=null) {
                         if (v instanceof Item) {
                             newArray[a++] = (Item)v;
                         } else {
                             throw new XPathException(
                                     "Returned array contains an object that cannot be converted to an Item (" +
                                            itemObject.getClass() + ')',
                                     SaxonErrorCode.SXJE0051);
                         }
                     }
                 }
             }
             return new SequenceExtent(newArray, 0, a);

        } else if (object instanceof long[]) {
             Item[] array = new Item[((long[])object).length];
             for (int i = 0; i < array.length; i++){
                 array[i] = Int64Value.makeIntegerValue(((long[])object)[i]);
             }
             return new SequenceExtent(array);

        } else if (object instanceof int[]) {
             Item[] array = new Item[((int[])object).length];
             for (int i = 0; i < array.length; i++){
                 array[i] = Int64Value.makeIntegerValue(((int[])object)[i]);
             }
             return new SequenceExtent(array);

        } else if (object instanceof short[]) {
             Item[] array = new Item[((short[])object).length];
             for (int i = 0; i < array.length; i++){
                 array[i] = Int64Value.makeIntegerValue(((short[])object)[i]);
             }
             return new SequenceExtent(array);

        } else if (object instanceof byte[]) {  // interpret this as unsigned bytes
             Item[] array = new Item[((byte[])object).length];
             for (int i = 0; i < array.length; i++){
                 array[i] = Int64Value.makeIntegerValue(255 & (int)((byte[])object)[i]);
             }
             return new SequenceExtent(array);

        } else if (object instanceof char[]) {
             return StringValue.makeStringValue(new String((char[])object));

        } else if (object instanceof boolean[]) {
             Item[] array = new Item[((boolean[])object).length];
             for (int i = 0; i < array.length; i++){
                 array[i] = BooleanValue.get(((boolean[])object)[i]);
             }
             return new SequenceExtent(array);

        } else if (object instanceof double[]) {
             Item[] array = new Item[((double[])object).length];
             for (int i = 0; i < array.length; i++){
                 array[i] = new DoubleValue(((double[])object)[i]);
             }
             return new SequenceExtent(array);

        } else if (object instanceof float[]) {
             Item[] array = new Item[((float[])object).length];
             for (int i = 0; i < array.length; i++){
                 array[i] = new FloatValue(((float[])object)[i]);
             }
             return new SequenceExtent(array);

        } else if (object instanceof Source && config != null) {
            if (object instanceof DOMSource) {
                NodeInfo node = config.unravel((Source)object);
                if (!node.getConfiguration().isCompatible(config)) {
                    throw new XPathException(
                            "Externally-supplied DOM Node belongs to a different and incompatible Configuration",
                            SaxonErrorCode.SXXP0004);
                }
                return new SingletonNode(node);
            }
            try {
                Builder b = new TinyBuilder();
                PipelineConfiguration pipe = config.makePipelineConfiguration();
                b.setPipelineConfiguration(pipe);
                new Sender(pipe).send((Source)object, b);
                if (object instanceof AugmentedSource && ((AugmentedSource)object).isPleaseCloseAfterUse()) {
                     ((AugmentedSource)object).close();
                }
                return new SingletonNode(b.getCurrentRoot());
            } catch (XPathException err) {
                throw new XPathException(err);
            }
        } else {
            // See whether this is an object representing a Node in some recognized object model
            ExternalObjectModel model = config.findExternalObjectModel(object);
            if (model != null) {
                DocumentInfo doc = model.wrapDocument(object, "", config);
                NodeInfo node = model.wrapNode(doc, object);
                return Value.asValue(node);
            }
        }
        return new ObjectValue(object);
    }

    /**
     * Temporary method to make a QNameValue from a JAXP 1.3 QName, without creating a compile-time link
     * to the JDK 1.5 QName class
     * @param object an instance of javax.xml.namespace.QName
     * @param config the Saxon configuration (used for dynamic loading)
     * @return a corresponding Saxon QNameValue, or null if any error occurs performing the conversion
     */

    public static QNameValue makeQNameValue(Object object, Configuration config) {
        try {
            Class qnameClass = config.getClass("javax.xml.namespace.QName", false, null);
            Class[] args = EMPTY_CLASS_ARRAY;
            Method getPrefix = qnameClass.getMethod("getPrefix", args);
            Method getLocalPart = qnameClass.getMethod("getLocalPart", args);
            Method getNamespaceURI = qnameClass.getMethod("getNamespaceURI", args);
            String prefix = (String)getPrefix.invoke(object, (Object[])args);
            String localPart = (String)getLocalPart.invoke(object, (Object[])args);
            String uri = (String)getNamespaceURI.invoke(object, (Object[])args);
            return new QNameValue(prefix, uri, localPart, BuiltInAtomicType.QNAME, config.getNameChecker());
        } catch (XPathException e) {
            return null;
        } catch (NoSuchMethodException e) {
            return null;
        } catch (IllegalAccessException e) {
            return null;
        } catch (InvocationTargetException e) {
            return null;
        }
    }

    public static final Class[] EMPTY_CLASS_ARRAY = new Class[0];

    /**
     * Convert to a string for diagnostic output
     */

    public String toString() {
        try {
            return getStringValue();
        } catch (XPathException err) {
            return super.toString();
        }
    }

    /**
     * Convert an XPath value to a Java object.
     * An atomic value is returned as an instance
     * of the best available Java class. If the item is a node, the node is "unwrapped",
     * to return the underlying node in the original model (which might be, for example,
     * a DOM or JDOM node).
     * @param item the item to be converted
     * @return the value after conversion
    */

    public static Object convertToJava(Item item) throws XPathException {
        if (item instanceof NodeInfo) {
            Object node = item;
            while (node instanceof VirtualNode) {
                // strip off any layers of wrapping
                node = ((VirtualNode)node).getUnderlyingNode();
            }
            return node;
        } else if (item instanceof ObjectValue) {
            return ((ObjectValue)item).getObject();
        } else {
            AtomicValue value = (AtomicValue)item;
            switch (value.getItemType(null).getPrimitiveType()) {
                case StandardNames.XS_STRING:
                case StandardNames.XS_UNTYPED_ATOMIC:
                case StandardNames.XS_ANY_URI:
                case StandardNames.XS_DURATION:
                    return value.getStringValue();
                case StandardNames.XS_BOOLEAN:
                    return (((BooleanValue)value).getBooleanValue() ? Boolean.TRUE : Boolean.FALSE );
                case StandardNames.XS_DECIMAL:
                    return ((DecimalValue)value).getDecimalValue();
                case StandardNames.XS_INTEGER:
                    return new Long(((NumericValue)value).longValue());
                case StandardNames.XS_DOUBLE:
                    return new Double(((DoubleValue)value).getDoubleValue());
                case StandardNames.XS_FLOAT:
                    return new Float(((FloatValue)value).getFloatValue());
                case StandardNames.XS_DATE_TIME:
                    return ((DateTimeValue)value).getCalendar().getTime();
                case StandardNames.XS_DATE:
                    return ((DateValue)value).getCalendar().getTime();
                case StandardNames.XS_TIME:
                    return value.getStringValue();
                case StandardNames.XS_BASE64_BINARY:
                    return ((Base64BinaryValue)value).getBinaryValue();
                case StandardNames.XS_HEX_BINARY:
                    return ((HexBinaryValue)value).getBinaryValue();
                default:
                    return item;
            }
        }
    }

}

//
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
// you may not use this file except in compliance with the License. You may obtain a copy of the
// License at http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
// See the License for the specific language governing rights and limitations under the License.
//
// The Original Code is: all this file.
//
// The Initial Developer of the Original Code is Michael H. Kay.
//
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
//
// Contributor(s): none.
//
TOP

Related Classes of net.sf.saxon.value.Value$ValueSchemaComparable

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.