CastExpression.java :  » XML » saxonb » net » sf » saxon » expr » Java Open Source

Java Open Source » XML » saxonb 
saxonb » net » sf » saxon » expr » CastExpression.java
package net.sf.saxon.expr;
import net.sf.saxon.om.Item;
import net.sf.saxon.om.NamePool;
import net.sf.saxon.sort.IntHashMap;
import net.sf.saxon.trans.DynamicError;
import net.sf.saxon.trans.StaticError;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.*;
import net.sf.saxon.value.*;

/**
* Cast Expression: implements "cast as data-type ( expression )". It also allows an internal
* cast, which has the same semantics as a user-requested cast, but maps an empty sequence to
* an empty sequence.
*/

public final class CastExpression extends UnaryExpression  {

    static IntHashMap castingTable = new IntHashMap(25);

    static void addAllowedCasts(int source, int[] target) {
        castingTable.put(source, target);
    }

    /**
     * The following data represents all the "Y" and "M" entries in section 17.1 of the F+O spec
     */

    static {
        final int uat = Type.UNTYPED_ATOMIC;
        final int str = Type.STRING;
        final int flt = Type.FLOAT;
        final int dbl = Type.DOUBLE;
        final int dec = Type.DECIMAL;
        final int ing = Type.INTEGER;
        final int dur = Type.DURATION;
        final int ymd = Type.YEAR_MONTH_DURATION;
        final int dtd = Type.DAY_TIME_DURATION;
        final int dtm = Type.DATE_TIME;
        final int tim = Type.TIME;
        final int dat = Type.DATE;
        final int gym = Type.G_YEAR_MONTH;
        final int gyr = Type.G_YEAR;
        final int gmd = Type.G_MONTH_DAY;
        final int gdy = Type.G_DAY;
        final int gmo = Type.G_MONTH;
        final int boo = Type.BOOLEAN;
        final int b64 = Type.BASE64_BINARY;
        final int hxb = Type.HEX_BINARY;
        final int uri = Type.ANY_URI;
        final int qnm = Type.QNAME;
        final int not = Type.NOTATION;

        final int[] t01 = {uat, str, flt, dbl, dec, ing, dur, ymd, dtd, dtm, tim, dat,
                          gym, gyr, gmd, gdy, gmo, boo, b64, hxb, uri};
        addAllowedCasts(uat, t01);
        final int[] t02 = {uat, str, flt, dbl, dec, ing, dur, ymd, dtd, dtm, tim, dat,
                          gym, gyr, gmd, gdy, gmo, boo, b64, hxb, uri, qnm, not};
        addAllowedCasts(str, t02);
        final int[] t03 = {uat, str, flt, dbl, dec, ing, boo};
        addAllowedCasts(flt, t03);
        addAllowedCasts(dbl, t03);
        addAllowedCasts(dec, t03);
        addAllowedCasts(ing, t03);
        final int[] t04 = {uat, str, dur, ymd, dtd};
        addAllowedCasts(dur, t04);
        addAllowedCasts(ymd, t04);
        addAllowedCasts(dtd, t04);
        final int[] t05 = {uat, str, dtm, tim, dat, gym, gyr, gmd, gdy, gmo};
        addAllowedCasts(dtm, t05);
        final int[] t06 = {uat, str, tim};
        addAllowedCasts(tim, t06);
        final int[] t07 = {uat, str, dtm, dat, gym, gyr, gmd, gdy, gmo};
        addAllowedCasts(dat, t07);
        final int[] t08 = {uat, str, gym};
        addAllowedCasts(gym, t08);
        final int[] t09 = {uat, str, gyr};
        addAllowedCasts(gyr, t09);
        final int[] t10 = {uat, str, gmd};
        addAllowedCasts(gmd, t10);
        final int[] t11 = {uat, str, gdy};
        addAllowedCasts(gdy, t11);
        final int[] t12 = {uat, str, gmo};
        addAllowedCasts(gmo, t12);
        final int[] t13 = {uat, str, flt, dbl, dec, ing, boo};
        addAllowedCasts(boo, t13);
        final int[] t14 = {uat, str, b64, hxb};
        addAllowedCasts(b64, t14);
        addAllowedCasts(hxb, t14);
        final int[] t15 = {uat, str, uri};
        addAllowedCasts(uri, t15);
        final int[] t16 = {uat, str, qnm};
        addAllowedCasts(qnm, t16);
        final int[] t17 = {uat, str, not};
        addAllowedCasts(not, t17);
    }

    /**
     * Determine whether casting from a source type to a target type is possible
     * @param source a primitive type (one that has an entry in the casting table)
     * @param target another primitive type
     * @return true if the entry in the casting table is either "Y" (casting always succeeds)
     * or "M" (casting allowed but may fail for some values)
     */

    public static boolean isPossibleCast(int source, int target) {
        if (source == Type.ANY_ATOMIC || source == Type.EMPTY) {
            return true;
        }
        int[] targets = (int[])castingTable.get(source);
        if (targets == null) {
            return false;
        }
        for (int i=0; i<targets.length; i++) {
            if (targets[i] == target) {
                return true;
            }
        }
        return false;
    }


    private AtomicType targetType;
    private AtomicType targetPrimitiveType;
    private boolean allowEmpty = false;
    private boolean derived = false;



    public CastExpression(Expression source, AtomicType target, boolean allowEmpty) {
        super(source);
        this.allowEmpty = allowEmpty;
        targetType = target;
        targetPrimitiveType = (AtomicType)target.getPrimitiveItemType();
        derived = (targetType.getFingerprint() != targetPrimitiveType.getFingerprint());
        adoptChildExpression(source);
    }

    /**
     * Handle a cast to QName or NOTATION. The argument must be a string literal.
     */

    public AtomicValue doQNameCast(StaticContext env) throws XPathException {
        if (!(operand instanceof StringValue)) {
            throw new StaticError("The argument of a QName or NOTATION constructor must be a string literal");
        }
        return QNameValue.castToQName((StringValue)operand, targetType, env);
    }


    /**
    * Simplify the expression
    * @return the simplified expression
    */

     public Expression simplify(StaticContext env) throws XPathException {
        if ((targetType instanceof BuiltInAtomicType) && !env.isAllowedBuiltInType(targetType)) {
            // this is checked here because the ConstructorFunctionLibrary doesn't have access to the static
            // context at bind time
            StaticError err = new StaticError("The type " + targetType.getDisplayName() +
                    " is not recognized by a Basic XSLT Processor", this);
            err.setErrorCode("XPST0080");
            throw err;
        }
        operand = operand.simplify(env);
        if (operand instanceof AtomicValue) {
            return typeCheck(env, Type.ITEM_TYPE);
        }
        return this;
    }

    /**
    * Type-check the expression
    */

    public Expression typeCheck(StaticContext env, ItemType contextItemType) throws XPathException {
        operand = operand.typeCheck(env, contextItemType);
        SequenceType atomicType = SequenceType.makeSequenceType(Type.ANY_ATOMIC_TYPE, getCardinality());

        RoleLocator role = new RoleLocator(RoleLocator.TYPE_OP, "cast as", 0, null);
        role.setSourceLocator(this);
        operand = TypeChecker.staticTypeCheck(operand, atomicType, false, role, env);

        final TypeHierarchy th = env.getNamePool().getTypeHierarchy();
        ItemType sourceType = operand.getItemType(th);
        if (th.isSubType(sourceType, targetType)) {
            return operand;
            // It's not entirely clear that the spec permits this. Perhaps we should change the type label?
            // On the other hand, it's generally true that any expression defined to return an X
            // is allowed to return a subtype of X.
        }
        if (targetType.isNamespaceSensitive()) {
            return new CastAsQName(operand, targetType).analyze(env, contextItemType);
        }
        if (operand instanceof AtomicValue) {
            return (AtomicValue)evaluateItem(env.makeEarlyEvaluationContext());
        }
        if (operand instanceof EmptySequence) {
            if (allowEmpty) {
                return operand;
            } else {
                StaticError err = new StaticError("Cast can never succeed: the operand must not be an empty sequence");
                err.setErrorCode("XPTY0004");
                err.setIsTypeError(true);
                throw err;
            }
        }
        int p = sourceType.getPrimitiveType();
        if (!isPossibleCast(p, targetType.getPrimitiveType())) {
            StaticError err = new StaticError("Casting from " + sourceType + " to " + targetType +
                    " can never succeed");
            err.setErrorCode("XPTY0004");
            err.setIsTypeError(true);
            throw err;
        }

        return this;
    }

    /**
    * Get the static cardinality of the expression
    */

    public int computeCardinality() {
        return (allowEmpty ? StaticProperty.ALLOWS_ZERO_OR_ONE : StaticProperty.EXACTLY_ONE);
    }

    /**
    * Get the static type of the expression
     * @param th
     */

    public ItemType getItemType(TypeHierarchy th) {
        return targetType;
    }

    /**
     * Determine the special properties of this expression
     * @return {@link StaticProperty#NON_CREATIVE}.
     */

    public int computeSpecialProperties() {
        int p = super.computeSpecialProperties();
        return p | StaticProperty.NON_CREATIVE;
    }

    /**
    * Evaluate the expression
    */

    public Item evaluateItem(XPathContext context) throws XPathException {
        AtomicValue value = (AtomicValue)operand.evaluateItem(context);
        if (value==null) {
            if (allowEmpty) {
                return null;
            } else {
                DynamicError e = new DynamicError("Cast does not allow an empty sequence");
                e.setXPathContext(context);
                throw e;
            }
        }
        AtomicValue result = value.convert(targetPrimitiveType, context, true);
        if (result instanceof ValidationErrorValue) {
            XPathException err = ((ValidationErrorValue)result).getException();
            String code = err.getErrorCodeLocalPart();
            dynamicError(err.getMessage(), code, context);
        }
        if (derived) {
            result = result.convert(targetType, context, true);
            if (result instanceof ValidationErrorValue) {
                XPathException err = ((ValidationErrorValue)result).getException();
                String code = err.getErrorCodeLocalPart();
                dynamicError(err.getMessage(), code, context);
            }
        }
        return result;
    }

    /**
     * Is this expression the same as another expression?
     */

    public boolean equals(Object other) {
        return super.equals(other) &&
                targetType == ((CastExpression)other).targetType &&
                allowEmpty == ((CastExpression)other).allowEmpty;
    }

    /**
     * Give a string representation of the operator for use in diagnostics
     * @return the operator, as a string
     */

    protected String displayOperator(NamePool pool) {
        return "cast as " + targetType.toString(pool);
    }
}

//
// 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.
//
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.