0001: package net.sf.saxon.expr;
0002:
0003: import net.sf.saxon.Err;
0004: import net.sf.saxon.om.Item;
0005: import net.sf.saxon.om.NamePool;
0006: import net.sf.saxon.trans.DynamicError;
0007: import net.sf.saxon.trans.StaticError;
0008: import net.sf.saxon.trans.XPathException;
0009: import net.sf.saxon.type.AtomicType;
0010: import net.sf.saxon.type.ItemType;
0011: import net.sf.saxon.type.Type;
0012: import net.sf.saxon.type.TypeHierarchy;
0013: import net.sf.saxon.value.*;
0014:
0015: /**
0016: * Arithmetic Expression: an expression using one of the operators
0017: * plus, minus, multiply, div, idiv, mod.
0018: */
0019:
0020: class ArithmeticExpression extends BinaryExpression {
0021:
0022: private static final class Signature {
0023: ItemType operand0;
0024: ItemType operand1;
0025: int operation;
0026: ItemType resultType;
0027:
0028: Signature(ItemType t0, ItemType t1, int op, ItemType r) {
0029: operand0 = t0;
0030: operand1 = t1;
0031: operation = op;
0032: resultType = r;
0033: }
0034: }
0035:
0036: private static final int NUMERIC_ARITHMETIC = 0;
0037: private static final int DATE_AND_DURATION = 1;
0038: private static final int DATE_DIFFERENCE = 2;
0039: private static final int DURATION_ADDITION = 3;
0040: private static final int DURATION_MULTIPLICATION = 4;
0041: private static final int DURATION_DIVISION = 5;
0042:
0043: private static final int UNKNOWN = -1;
0044: private static final int UNKNOWN_10 = -2;
0045:
0046: private static final Signature[] plusTable = {
0047: new Signature(Type.NUMBER_TYPE, Type.NUMBER_TYPE,
0048: NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
0049: new Signature(Type.NUMBER_TYPE, Type.UNTYPED_ATOMIC_TYPE,
0050: NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
0051: new Signature(Type.UNTYPED_ATOMIC_TYPE, Type.NUMBER_TYPE,
0052: NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
0053: new Signature(Type.UNTYPED_ATOMIC_TYPE,
0054: Type.UNTYPED_ATOMIC_TYPE, NUMERIC_ARITHMETIC,
0055: Type.NUMBER_TYPE),
0056:
0057: new Signature(Type.DATE_TYPE, Type.DURATION_TYPE,
0058: DATE_AND_DURATION, Type.DATE_TYPE),
0059: new Signature(Type.DURATION_TYPE, Type.DATE_TYPE,
0060: DATE_AND_DURATION, Type.DATE_TYPE),
0061:
0062: new Signature(Type.TIME_TYPE, Type.DURATION_TYPE,
0063: DATE_AND_DURATION, Type.TIME_TYPE),
0064: new Signature(Type.DURATION_TYPE, Type.TIME_TYPE,
0065: DATE_AND_DURATION, Type.TIME_TYPE),
0066:
0067: new Signature(Type.DATE_TIME_TYPE, Type.DURATION_TYPE,
0068: DATE_AND_DURATION, Type.DATE_TIME_TYPE),
0069: new Signature(Type.DURATION_TYPE, Type.DATE_TIME_TYPE,
0070: DATE_AND_DURATION, Type.DATE_TIME_TYPE),
0071:
0072: new Signature(Type.DURATION_TYPE, Type.DURATION_TYPE,
0073: DURATION_ADDITION, Type.DURATION_TYPE)
0074:
0075: };
0076:
0077: private static final Signature[] minusTable = {
0078: new Signature(Type.NUMBER_TYPE, Type.NUMBER_TYPE,
0079: NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
0080: new Signature(Type.NUMBER_TYPE, Type.UNTYPED_ATOMIC_TYPE,
0081: NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
0082: new Signature(Type.UNTYPED_ATOMIC_TYPE, Type.NUMBER_TYPE,
0083: NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
0084: new Signature(Type.UNTYPED_ATOMIC_TYPE,
0085: Type.UNTYPED_ATOMIC_TYPE, NUMERIC_ARITHMETIC,
0086: Type.NUMBER_TYPE),
0087:
0088: new Signature(Type.DATE_TYPE, Type.DATE_TYPE,
0089: DATE_DIFFERENCE, Type.DAY_TIME_DURATION_TYPE),
0090: new Signature(Type.DATE_TYPE, Type.DURATION_TYPE,
0091: DATE_AND_DURATION, Type.DATE_TYPE),
0092:
0093: new Signature(Type.TIME_TYPE, Type.TIME_TYPE,
0094: DATE_DIFFERENCE, Type.DAY_TIME_DURATION_TYPE),
0095: new Signature(Type.TIME_TYPE, Type.DURATION_TYPE,
0096: DATE_AND_DURATION, Type.TIME_TYPE),
0097:
0098: new Signature(Type.DATE_TIME_TYPE, Type.DATE_TIME_TYPE,
0099: DATE_DIFFERENCE, Type.DAY_TIME_DURATION_TYPE),
0100: new Signature(Type.DATE_TIME_TYPE, Type.DURATION_TYPE,
0101: DATE_AND_DURATION, Type.DATE_TIME_TYPE),
0102:
0103: new Signature(Type.DURATION_TYPE, Type.DURATION_TYPE,
0104: DURATION_ADDITION, Type.DURATION_TYPE)
0105:
0106: };
0107:
0108: private static final Signature[] multiplyTable = {
0109: new Signature(Type.NUMBER_TYPE, Type.NUMBER_TYPE,
0110: NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
0111: new Signature(Type.NUMBER_TYPE, Type.UNTYPED_ATOMIC_TYPE,
0112: NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
0113: new Signature(Type.UNTYPED_ATOMIC_TYPE, Type.NUMBER_TYPE,
0114: NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
0115: new Signature(Type.UNTYPED_ATOMIC_TYPE,
0116: Type.UNTYPED_ATOMIC_TYPE, NUMERIC_ARITHMETIC,
0117: Type.NUMBER_TYPE),
0118:
0119: new Signature(Type.NUMBER_TYPE, Type.DURATION_TYPE,
0120: DURATION_MULTIPLICATION, Type.DURATION_TYPE),
0121: new Signature(Type.DURATION_TYPE, Type.NUMBER_TYPE,
0122: DURATION_MULTIPLICATION, Type.DURATION_TYPE) };
0123:
0124: private static final Signature[] divideTable = {
0125: new Signature(Type.NUMBER_TYPE, Type.NUMBER_TYPE,
0126: NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
0127: new Signature(Type.NUMBER_TYPE, Type.UNTYPED_ATOMIC_TYPE,
0128: NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
0129: new Signature(Type.UNTYPED_ATOMIC_TYPE, Type.NUMBER_TYPE,
0130: NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
0131: new Signature(Type.UNTYPED_ATOMIC_TYPE,
0132: Type.UNTYPED_ATOMIC_TYPE, NUMERIC_ARITHMETIC,
0133: Type.NUMBER_TYPE),
0134:
0135: new Signature(Type.DURATION_TYPE, Type.NUMBER_TYPE,
0136: DURATION_MULTIPLICATION, Type.DURATION_TYPE),
0137: new Signature(Type.DURATION_TYPE, Type.DURATION_TYPE,
0138: DURATION_DIVISION, Type.NUMBER_TYPE) };
0139:
0140: private static final Signature[] idivTable = {
0141: new Signature(Type.NUMBER_TYPE, Type.NUMBER_TYPE,
0142: NUMERIC_ARITHMETIC, Type.INTEGER_TYPE),
0143: new Signature(Type.NUMBER_TYPE, Type.UNTYPED_ATOMIC_TYPE,
0144: NUMERIC_ARITHMETIC, Type.INTEGER_TYPE),
0145: new Signature(Type.UNTYPED_ATOMIC_TYPE, Type.NUMBER_TYPE,
0146: NUMERIC_ARITHMETIC, Type.INTEGER_TYPE),
0147: new Signature(Type.UNTYPED_ATOMIC_TYPE,
0148: Type.UNTYPED_ATOMIC_TYPE, NUMERIC_ARITHMETIC,
0149: Type.INTEGER_TYPE) };
0150:
0151: private static final Signature[] modTable = {
0152: new Signature(Type.NUMBER_TYPE, Type.NUMBER_TYPE,
0153: NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
0154: new Signature(Type.NUMBER_TYPE, Type.UNTYPED_ATOMIC_TYPE,
0155: NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
0156: new Signature(Type.UNTYPED_ATOMIC_TYPE, Type.NUMBER_TYPE,
0157: NUMERIC_ARITHMETIC, Type.NUMBER_TYPE),
0158: new Signature(Type.UNTYPED_ATOMIC_TYPE,
0159: Type.UNTYPED_ATOMIC_TYPE, NUMERIC_ARITHMETIC,
0160: Type.NUMBER_TYPE) };
0161:
0162: private boolean backwardsCompatible = false;
0163:
0164: public ArithmeticExpression(Expression p1, int operator,
0165: Expression p2) {
0166: super (p1, operator, p2);
0167: }
0168:
0169: /**
0170: * Type-check the expression statically. We try to work out which particular
0171: * arithmetic function to use if the types of operands are known an compile time.
0172: */
0173:
0174: public Expression typeCheck(StaticContext env,
0175: ItemType contextItemType) throws XPathException {
0176:
0177: final TypeHierarchy th = env.getNamePool().getTypeHierarchy();
0178: backwardsCompatible = env.isInBackwardsCompatibleMode();
0179:
0180: operand0 = operand0.typeCheck(env, contextItemType);
0181: operand1 = operand1.typeCheck(env, contextItemType);
0182:
0183: SequenceType atomicType = SequenceType.OPTIONAL_ATOMIC;
0184:
0185: RoleLocator role0 = new RoleLocator(RoleLocator.BINARY_EXPR,
0186: Token.tokens[operator], 0, null);
0187: role0.setSourceLocator(this );
0188: operand0 = TypeChecker.staticTypeCheck(operand0, atomicType,
0189: backwardsCompatible, role0, env);
0190: // System.err.println("First operand"); operand0.display(10);
0191:
0192: RoleLocator role1 = new RoleLocator(RoleLocator.BINARY_EXPR,
0193: Token.tokens[operator], 1, null);
0194: role1.setSourceLocator(this );
0195: operand1 = TypeChecker.staticTypeCheck(operand1, atomicType,
0196: backwardsCompatible, role1, env);
0197: // System.err.println("Second operand"); operand1.display(10);
0198:
0199: if (backwardsCompatible) {
0200: Expression exp = new Arithmetic10(operand0, operator,
0201: operand1);
0202: return exp.simplify(env).typeCheck(env, contextItemType);
0203: }
0204:
0205: Expression e = super .typeCheck(env, contextItemType);
0206: if (e instanceof ArithmeticExpression) {
0207: AtomicType type0 = (AtomicType) operand0.getItemType(th)
0208: .getPrimitiveItemType();
0209: AtomicType type1 = (AtomicType) operand1.getItemType(th)
0210: .getPrimitiveItemType();
0211: final int action = getAction(type0, operator, type1,
0212: backwardsCompatible);
0213: switch (action) {
0214: case NUMERIC_ARITHMETIC:
0215: e = new NumericArithmetic(operand0, operator, operand1);
0216: break;
0217: case DURATION_ADDITION:
0218: e = new DurationAddition(operand0, operator, operand1);
0219: break;
0220: case DURATION_MULTIPLICATION:
0221: e = new DurationMultiplication(operand0, operator,
0222: operand1, env.getNamePool());
0223: break;
0224: case DURATION_DIVISION:
0225: e = new DurationDivision(operand0, operator, operand1);
0226: break;
0227: case DATE_AND_DURATION:
0228: e = new DateAndDuration(operand0, operator, operand1,
0229: env.getNamePool());
0230: break;
0231: case DATE_DIFFERENCE:
0232: e = new DateDifference(operand0, operator, operand1);
0233: break;
0234:
0235: case UNKNOWN_10:
0236: e = new Arithmetic10(operand0, operator, operand1);
0237: break;
0238:
0239: case UNKNOWN:
0240: // either the types are not known yet, or they are wrong
0241: if (type0.isAtomicType()
0242: && type0 != Type.UNTYPED_ATOMIC_TYPE
0243: && type0 != Type.ANY_ATOMIC_TYPE
0244: && type1.isAtomicType()
0245: && type1 != Type.UNTYPED_ATOMIC_TYPE
0246: && type1 != Type.ANY_ATOMIC_TYPE) {
0247: StaticError err = new StaticError(
0248: "Unsuitable operands for arithmetic operation ("
0249: + type0.toString(env.getNamePool())
0250: + ", "
0251: + type1.toString(env.getNamePool())
0252: + ')');
0253: err.setErrorCode("XPTY0004");
0254: err.setIsTypeError(true);
0255: err.setLocator(ExpressionTool.getLocator(this ));
0256: throw err;
0257: }
0258: return e;
0259: }
0260: ExpressionTool.copyLocationInfo(this , e);
0261: if (e instanceof ComputedExpression) {
0262: ((ComputedExpression) e)
0263: .setParentExpression(getParentExpression());
0264: }
0265: try {
0266: if (operand0 instanceof Value
0267: && operand1 instanceof Value) {
0268: return ExpressionTool.eagerEvaluate(e, env
0269: .makeEarlyEvaluationContext());
0270: }
0271: } catch (DynamicError err) {
0272: // Defer any error reporting until run-time
0273: }
0274: }
0275: return e;
0276: }
0277:
0278: /**
0279: * Decide what action is required based on types of operands
0280: */
0281:
0282: private static int getAction(AtomicType type1, int operator,
0283: AtomicType type2, boolean backwardsCompatible) {
0284: if (type1.getFingerprint() == Type.DAY_TIME_DURATION) {
0285: type1 = Type.DURATION_TYPE;
0286: } else if (type1.getFingerprint() == Type.YEAR_MONTH_DURATION) {
0287: type1 = Type.DURATION_TYPE;
0288: }
0289: if (type2.getFingerprint() == Type.DAY_TIME_DURATION) {
0290: type2 = Type.DURATION_TYPE;
0291: } else if (type2.getFingerprint() == Type.YEAR_MONTH_DURATION) {
0292: type2 = Type.DURATION_TYPE;
0293: }
0294: Signature[] table = getOperatorTable(operator);
0295: int entry = getEntry(table, type1, type2);
0296: if (entry < 0) {
0297: return (backwardsCompatible ? UNKNOWN_10 : UNKNOWN);
0298: }
0299: return table[entry].operation;
0300: }
0301:
0302: private static Signature[] getOperatorTable(int operator) {
0303: switch (operator) {
0304: case Token.PLUS:
0305: return plusTable;
0306: case Token.MINUS:
0307: case Token.NEGATE:
0308: return minusTable;
0309: case Token.MULT:
0310: return multiplyTable;
0311: case Token.DIV:
0312: return divideTable;
0313: case Token.IDIV:
0314: return idivTable;
0315: case Token.MOD:
0316: return modTable;
0317: default:
0318: throw new IllegalArgumentException(
0319: "Unknown arithmetic operator");
0320: }
0321: }
0322:
0323: private static int getEntry(Signature[] table, ItemType type1,
0324: ItemType type2) {
0325: if (Type.isNumericPrimitiveType(type1)) {
0326: type1 = Type.NUMBER_TYPE;
0327: }
0328: if (Type.isNumericPrimitiveType(type2)) {
0329: type2 = Type.NUMBER_TYPE;
0330: }
0331: for (int i = 0; i < table.length; i++) {
0332: if (type1.equals(table[i].operand0)
0333: && type2.equals(table[i].operand1)) {
0334: return i;
0335: }
0336: }
0337: return -1;
0338: }
0339:
0340: /**
0341: * Determine the data type of the expression, if this is known statically
0342: * @param th
0343: */
0344:
0345: public ItemType getItemType(TypeHierarchy th) {
0346: final ItemType t1 = operand0.getItemType(th);
0347: final ItemType pt1 = t1.getPrimitiveItemType();
0348: final ItemType t2 = operand1.getItemType(th);
0349: final ItemType pt2 = t2.getPrimitiveItemType();
0350: final Signature[] table = getOperatorTable(operator);
0351: final int entry = getEntry(table, pt1, pt2);
0352:
0353: if (entry < 0) {
0354: return Type.ANY_ATOMIC_TYPE; // type is not known statically
0355: }
0356:
0357: ItemType resultType = table[entry].resultType;
0358: if (resultType == Type.NUMBER_TYPE) {
0359: resultType = NumericValue.promote(pt1, pt2, th);
0360: if (operator == Token.DIV
0361: && resultType == Type.INTEGER_TYPE) {
0362: // exception: integer div integer => decimal
0363: resultType = Type.DECIMAL_TYPE;
0364: } else {
0365: resultType = NumericValue.promote(pt1, pt2, th);
0366: }
0367: } else if (resultType == Type.DURATION_TYPE) {
0368: // if one of the operands is a subtype of duration, then the result will be the same subtype
0369: // (this isn't captured in the table because these types are not primitive).
0370: if (th.isSubType(t1, Type.DAY_TIME_DURATION_TYPE)) {
0371: resultType = Type.DAY_TIME_DURATION_TYPE;
0372: } else if (th.isSubType(t2, Type.DAY_TIME_DURATION_TYPE)) {
0373: resultType = Type.DAY_TIME_DURATION_TYPE;
0374: } else if (th.isSubType(t1, Type.YEAR_MONTH_DURATION_TYPE)) {
0375: resultType = Type.YEAR_MONTH_DURATION_TYPE;
0376: } else if (th.isSubType(t2, Type.YEAR_MONTH_DURATION_TYPE)) {
0377: resultType = Type.YEAR_MONTH_DURATION_TYPE;
0378: }
0379: }
0380: return resultType;
0381: }
0382:
0383: /**
0384: * Evaluate the expression. We only take this path if the type could not be determined
0385: * statically.
0386: */
0387:
0388: public Item evaluateItem(XPathContext context)
0389: throws XPathException {
0390:
0391: AtomicValue v1 = (AtomicValue) operand0.evaluateItem(context);
0392: if (v1 == null) {
0393: return null;
0394: }
0395: v1 = v1.getPrimitiveValue();
0396:
0397: AtomicValue v2 = (AtomicValue) operand1.evaluateItem(context);
0398: if (v2 == null) {
0399: return null;
0400: }
0401: v2 = v2.getPrimitiveValue();
0402:
0403: final NamePool namePool = context.getNamePool();
0404: final TypeHierarchy th = namePool.getTypeHierarchy();
0405: int action = getAction((AtomicType) v1.getItemType(th)
0406: .getPrimitiveItemType(), operator, (AtomicType) v2
0407: .getItemType(th).getPrimitiveItemType(),
0408: backwardsCompatible);
0409:
0410: Expression e;
0411: switch (action) {
0412: case NUMERIC_ARITHMETIC:
0413: e = new NumericArithmetic(v1, operator, v2);
0414: break;
0415: case DURATION_ADDITION:
0416: e = new DurationAddition(v1, operator, v2);
0417: break;
0418: case DURATION_MULTIPLICATION:
0419: e = new DurationMultiplication(v1, operator, v2, namePool);
0420: break;
0421: case DURATION_DIVISION:
0422: e = new DurationDivision(v1, operator, v2);
0423: break;
0424: case DATE_AND_DURATION:
0425: e = new DateAndDuration(v1, operator, v2, namePool);
0426: break;
0427: case DATE_DIFFERENCE:
0428: e = new DateDifference(v1, operator, v2);
0429: break;
0430: default:
0431: // types are not known yet. Force to double if in 1.0 mode
0432: if (backwardsCompatible) {
0433: NumericValue nv1;
0434: NumericValue nv2;
0435: try {
0436: nv1 = (NumericValue) v1.convert(Type.DOUBLE,
0437: context);
0438: nv2 = (NumericValue) v2.convert(Type.DOUBLE,
0439: context);
0440: } catch (XPathException err) {
0441: typeError(
0442: "Unsuitable operands for arithmetic operation ("
0443: + v1.getItemType(th) + ", "
0444: + v2.getItemType(th) + ')',
0445: "XPTY0004", context);
0446: return null;
0447: }
0448: e = new NumericArithmetic(nv1, operator, nv2);
0449: } else {
0450: typeError(
0451: "Unsuitable operands for arithmetic operation ("
0452: + v1.getItemType(th) + ", "
0453: + v2.getItemType(th) + ')', "XPTY0004",
0454: context);
0455: return null;
0456: }
0457: }
0458: ExpressionTool.copyLocationInfo(this , e);
0459: if (e instanceof ComputedExpression) {
0460: ((ComputedExpression) e)
0461: .setParentExpression(getParentExpression());
0462: }
0463: return e.evaluateItem(context);
0464: }
0465:
0466: /**
0467: * Inner class to handle 1.0 backwards compatible arithmetic
0468: */
0469: private static class Arithmetic10 extends BinaryExpression {
0470:
0471: public boolean backwardsCompatible;
0472:
0473: public Arithmetic10(Expression p1, int operator, Expression p2) {
0474: super (p1, operator, p2);
0475: }
0476:
0477: public Expression typeCheck(StaticContext env,
0478: ItemType contextItemType) throws XPathException {
0479:
0480: final TypeHierarchy th = env.getNamePool()
0481: .getTypeHierarchy();
0482: SequenceType atomicType = SequenceType.OPTIONAL_ATOMIC;
0483:
0484: RoleLocator role0 = new RoleLocator(
0485: RoleLocator.BINARY_EXPR, Token.tokens[operator], 0,
0486: null);
0487: role0.setSourceLocator(this );
0488: operand0 = TypeChecker.staticTypeCheck(operand0,
0489: atomicType, backwardsCompatible, role0, env);
0490:
0491: RoleLocator role1 = new RoleLocator(
0492: RoleLocator.BINARY_EXPR, Token.tokens[operator], 1,
0493: null);
0494: role1.setSourceLocator(this );
0495: operand1 = TypeChecker.staticTypeCheck(operand1,
0496: atomicType, backwardsCompatible, role1, env);
0497:
0498: Expression e = super .typeCheck(env, contextItemType);
0499: if (e instanceof ArithmeticExpression) {
0500: AtomicType type0 = (AtomicType) operand0
0501: .getItemType(th).getPrimitiveItemType();
0502: AtomicType type1 = (AtomicType) operand1
0503: .getItemType(th).getPrimitiveItemType();
0504: if (backwardsCompatible) {
0505: if (type0 == Type.BOOLEAN_TYPE) {
0506: type0 = Type.NUMBER_TYPE;
0507: } else if (type0 == Type.STRING_TYPE) {
0508: type0 = Type.NUMBER_TYPE;
0509: }
0510: if (type1 == Type.BOOLEAN_TYPE) {
0511: type1 = Type.NUMBER_TYPE;
0512: } else if (type1 == Type.STRING_TYPE) {
0513: type1 = Type.NUMBER_TYPE;
0514: }
0515: }
0516: final int action = getAction(type0, operator, type1,
0517: backwardsCompatible);
0518: switch (action) {
0519: case NUMERIC_ARITHMETIC:
0520: e = new NumericArithmetic(operand0, operator,
0521: operand1);
0522: ((NumericArithmetic) e)
0523: .setBackwardsCompatible(backwardsCompatible);
0524: break;
0525: case DURATION_ADDITION:
0526: e = new DurationAddition(operand0, operator,
0527: operand1);
0528: break;
0529: case DURATION_MULTIPLICATION:
0530: e = new DurationMultiplication(operand0, operator,
0531: operand1, env.getNamePool());
0532: break;
0533: case DURATION_DIVISION:
0534: e = new DurationDivision(operand0, operator,
0535: operand1);
0536: break;
0537: case DATE_AND_DURATION:
0538: e = new DateAndDuration(operand0, operator,
0539: operand1, env.getNamePool());
0540: break;
0541: case DATE_DIFFERENCE:
0542: e = new DateDifference(operand0, operator, operand1);
0543: break;
0544:
0545: case UNKNOWN_10:
0546: e = new Arithmetic10(operand0, operator, operand1);
0547: break;
0548:
0549: case UNKNOWN:
0550: // either the types are not known yet, or they are wrong
0551: if (type0.isAtomicType()
0552: && type0 != Type.UNTYPED_ATOMIC_TYPE
0553: && type0 != Type.ANY_ATOMIC_TYPE
0554: && type1.isAtomicType()
0555: && type1 != Type.UNTYPED_ATOMIC_TYPE
0556: && type1 != Type.ANY_ATOMIC_TYPE) {
0557: StaticError err = new StaticError(
0558: "Unsuitable operands for arithmetic operation ("
0559: + type0.toString(env
0560: .getNamePool())
0561: + ", "
0562: + type1.toString(env
0563: .getNamePool()) + ')');
0564: err.setErrorCode("XPTY0004");
0565: err.setIsTypeError(true);
0566: throw err;
0567: }
0568: return e;
0569: }
0570: ExpressionTool.copyLocationInfo(this , e);
0571: if (e instanceof ComputedExpression) {
0572: ((ComputedExpression) e)
0573: .setParentExpression(getParentExpression());
0574: }
0575: try {
0576: if (operand0 instanceof Value
0577: && operand1 instanceof Value) {
0578: return ExpressionTool.eagerEvaluate(e, env
0579: .makeEarlyEvaluationContext());
0580: }
0581: } catch (DynamicError err) {
0582: // Defer any error reporting until run-time
0583: }
0584: }
0585: return e;
0586: }
0587:
0588: /**
0589: * Determine the data type of the expression, if possible. All expression return
0590: * sequences, in general; this method determines the type of the items within the
0591: * sequence, assuming that (a) this is known in advance, and (b) it is the same for
0592: * all items in the sequence.
0593: * <p/>
0594: * <p>This method should always return a result, though it may be the best approximation
0595: * that is available at the time.</p>
0596: *
0597: * @return a value such as Type.STRING, Type.BOOLEAN, Type.NUMBER,
0598: * Type.NODE, or Type.ITEM (meaning not known at compile time)
0599: * @param th
0600: */
0601:
0602: public ItemType getItemType(TypeHierarchy th) {
0603: return Type.ANY_ATOMIC_TYPE;
0604: }
0605:
0606: /**
0607: * Evaluate the expression.
0608: */
0609:
0610: public Item evaluateItem(XPathContext context)
0611: throws XPathException {
0612:
0613: AtomicValue v1 = (AtomicValue) operand0
0614: .evaluateItem(context);
0615: if (v1 == null) {
0616: return DoubleValue.NaN;
0617: }
0618: v1 = v1.getPrimitiveValue();
0619: if (v1 instanceof BooleanValue || v1 instanceof StringValue
0620: || v1 instanceof NumericValue) {
0621: try {
0622: v1 = v1.convert(Type.DOUBLE, context);
0623: } catch (XPathException e) {
0624: return DoubleValue.NaN;
0625: }
0626: }
0627:
0628: AtomicValue v2 = (AtomicValue) operand1
0629: .evaluateItem(context);
0630: if (v2 == null) {
0631: return DoubleValue.NaN;
0632: }
0633: v2 = v2.getPrimitiveValue();
0634: if (v2 instanceof BooleanValue || v2 instanceof StringValue
0635: || v2 instanceof NumericValue) {
0636: try {
0637: v2 = v2.convert(Type.DOUBLE, context);
0638: } catch (XPathException e) {
0639: return DoubleValue.NaN;
0640: }
0641: }
0642:
0643: final TypeHierarchy th = context.getNamePool()
0644: .getTypeHierarchy();
0645: int action = getAction((AtomicType) v1.getItemType(th)
0646: .getPrimitiveItemType(), operator, (AtomicType) v2
0647: .getItemType(th).getPrimitiveItemType(),
0648: backwardsCompatible);
0649:
0650: switch (action) {
0651: case NUMERIC_ARITHMETIC:
0652: try {
0653: return NumericArithmetic.doArithmetic(v1, operator,
0654: v2, context, backwardsCompatible);
0655: } catch (XPathException ex) {
0656: if (ex.getLocator() == null) {
0657: ex.setLocator(this );
0658: }
0659: throw ex;
0660: }
0661:
0662: case DURATION_ADDITION:
0663: try {
0664: return DurationAddition.doArithmetic(v1, operator,
0665: v2, context);
0666: } catch (XPathException ex) {
0667: if (ex.getLocator() == null) {
0668: ex.setLocator(this );
0669: }
0670: throw ex;
0671: }
0672:
0673: case DURATION_MULTIPLICATION:
0674: try {
0675: return DurationMultiplication.doArithmetic(v1,
0676: operator, v2, context);
0677: } catch (XPathException ex) {
0678: if (ex.getLocator() == null) {
0679: ex.setLocator(this );
0680: }
0681: throw ex;
0682: }
0683:
0684: case DURATION_DIVISION:
0685: try {
0686: return DurationDivision.doArithmetic(v1, v2,
0687: context);
0688: } catch (XPathException ex) {
0689: if (ex.getLocator() == null) {
0690: ex.setLocator(this );
0691: }
0692: throw ex;
0693: }
0694:
0695: case DATE_AND_DURATION:
0696: try {
0697: return DateAndDuration.doArithmetic(v1, operator,
0698: v2, context);
0699: } catch (XPathException ex) {
0700: if (ex.getLocator() == null) {
0701: ex.setLocator(this );
0702: }
0703: throw ex;
0704: }
0705:
0706: case DATE_DIFFERENCE:
0707: try {
0708: return DateDifference.doArithmetic(v1, v2, context);
0709: } catch (XPathException ex) {
0710: if (ex.getLocator() == null) {
0711: ex.setLocator(this );
0712: }
0713: throw ex;
0714: }
0715:
0716: default:
0717: typeError(
0718: "Unsuitable operands for arithmetic operation ("
0719: + v1.getItemType(th) + ", "
0720: + v2.getItemType(th) + ')', "XPTY0004",
0721: context);
0722: return null;
0723: }
0724:
0725: }
0726: }
0727:
0728: /**
0729: * Inner class to handle numeric arithmetic expressions
0730: */
0731:
0732: public static class NumericArithmetic extends ArithmeticExpression {
0733:
0734: boolean backwardsCompatible = false;
0735:
0736: public NumericArithmetic(Expression p1, int operator,
0737: Expression p2) {
0738: super (p1, operator, p2);
0739: }
0740:
0741: public void setBackwardsCompatible(boolean flag) {
0742: backwardsCompatible = flag;
0743: }
0744:
0745: /**
0746: * Evaluate the expression.
0747: */
0748:
0749: public Item evaluateItem(XPathContext context)
0750: throws XPathException {
0751: try {
0752: return doArithmetic(operand0, operator, operand1,
0753: context, backwardsCompatible);
0754: } catch (XPathException err) {
0755: if (err.getLocator() == null) {
0756: err.setLocator(this );
0757: }
0758: throw err;
0759: }
0760: }
0761:
0762: public static Item doArithmetic(Expression operand0,
0763: int operator, Expression operand1,
0764: XPathContext context, boolean backwardsCompatible)
0765: throws XPathException {
0766:
0767: AtomicValue v1 = ((AtomicValue) operand0
0768: .evaluateItem(context));
0769: if (v1 == null) {
0770: return null;
0771: }
0772:
0773: if (v1 instanceof UntypedAtomicValue) {
0774: try {
0775: v1 = new DoubleValue(Value.stringToNumber(v1
0776: .getStringValueCS()));
0777: } catch (NumberFormatException e) {
0778: if (backwardsCompatible) {
0779: v1 = DoubleValue.NaN;
0780: } else {
0781: DynamicError err = new DynamicError(
0782: "Failure converting untyped value "
0783: + Err.wrap(v1
0784: .getStringValueCS(),
0785: Err.VALUE)
0786: + " to a number");
0787: err.setErrorCode("FORG0001");
0788: err.setXPathContext(context);
0789: throw err;
0790: }
0791: }
0792: } else {
0793: v1 = v1.getPrimitiveValue();
0794: }
0795: AtomicValue v2 = ((AtomicValue) operand1
0796: .evaluateItem(context));
0797: if (v2 == null) {
0798: return null;
0799: }
0800:
0801: if (v2 instanceof UntypedAtomicValue) {
0802: try {
0803: v2 = new DoubleValue(Value.stringToNumber(v2
0804: .getStringValueCS()));
0805: } catch (NumberFormatException e) {
0806: if (backwardsCompatible) {
0807: v2 = DoubleValue.NaN;
0808: } else {
0809: DynamicError err = new DynamicError(
0810: "Failure converting untyped value "
0811: + Err.wrap(v2
0812: .getStringValueCS(),
0813: Err.VALUE)
0814: + " to a number");
0815: err.setErrorCode("FORG0001");
0816: err.setXPathContext(context);
0817: throw err;
0818: }
0819: }
0820: } else {
0821: v2 = v2.getPrimitiveValue();
0822: }
0823:
0824: if (operator == Token.NEGATE) {
0825: return ((NumericValue) v2).negate();
0826: } else {
0827: try {
0828: return ((NumericValue) v1).arithmetic(operator,
0829: (NumericValue) v2, context);
0830: } catch (DynamicError err) {
0831: if (err.getXPathContext() == null) {
0832: err.setXPathContext(context);
0833: }
0834: throw err;
0835: } catch (ArithmeticException err) {
0836: DynamicError e = new DynamicError(
0837: "Arithmetic exception: " + err.getMessage());
0838: e.setXPathContext(context);
0839: throw e;
0840: }
0841: }
0842: }
0843: }
0844:
0845: /**
0846: * Inner class to handle addition and subtraction of two durations
0847: */
0848:
0849: public static class DurationAddition extends ArithmeticExpression {
0850:
0851: public DurationAddition(Expression p1, int operator,
0852: Expression p2) {
0853: super (p1, operator, p2);
0854: }
0855:
0856: /**
0857: * Evaluate the expression.
0858: */
0859:
0860: public Item evaluateItem(XPathContext context)
0861: throws XPathException {
0862: try {
0863: return doArithmetic(operand0, operator, operand1,
0864: context);
0865: } catch (XPathException err) {
0866: if (err.getLocator() == null) {
0867: err.setLocator(this );
0868: }
0869: throw err;
0870: }
0871: }
0872:
0873: public static Item doArithmetic(Expression operand0,
0874: int operator, Expression operand1, XPathContext context)
0875: throws XPathException {
0876:
0877: AtomicValue av1 = (AtomicValue) operand0
0878: .evaluateItem(context);
0879: if (av1 == null) {
0880: return null;
0881: }
0882: DurationValue v1 = (DurationValue) av1.getPrimitiveValue();
0883:
0884: AtomicValue av2 = (AtomicValue) operand1
0885: .evaluateItem(context);
0886: if (av2 == null) {
0887: return null;
0888: }
0889: DurationValue v2 = (DurationValue) av2.getPrimitiveValue();
0890:
0891: if (operator == Token.PLUS) {
0892: return v1.add(v2, context);
0893: } else if (operator == Token.MINUS) {
0894: return v1.subtract(v2, context);
0895: } else {
0896: throw new AssertionError(
0897: "Unknown operation on durations");
0898: }
0899:
0900: }
0901: }
0902:
0903: /**
0904: * Inner class to handle multiplication (or division) of a duration by a number
0905: */
0906:
0907: public static class DurationMultiplication extends
0908: ArithmeticExpression {
0909:
0910: public DurationMultiplication(Expression p1, int operator,
0911: Expression p2, NamePool pool) {
0912:
0913: // by the time we get here, we know that one of the operands is a duration,
0914: // but it might be either one. We make it the first.
0915:
0916: super (p1, operator, p2);
0917: final TypeHierarchy th = pool.getTypeHierarchy();
0918: if (th.isSubType(p2.getItemType(th), Type.DURATION_TYPE)) {
0919: operand0 = p2;
0920: operand1 = p1;
0921: }
0922: }
0923:
0924: /**
0925: * Evaluate the expression.
0926: */
0927:
0928: public Item evaluateItem(XPathContext context)
0929: throws XPathException {
0930: try {
0931: return doArithmetic(operand0, operator, operand1,
0932: context);
0933: } catch (XPathException err) {
0934: if (err.getLocator() == null) {
0935: err.setLocator(this );
0936: }
0937: throw err;
0938: }
0939: }
0940:
0941: public static Item doArithmetic(Expression operand0,
0942: int operator, Expression operand1, XPathContext context)
0943: throws XPathException {
0944:
0945: AtomicValue av1 = (AtomicValue) operand0
0946: .evaluateItem(context);
0947: if (av1 == null) {
0948: return null;
0949: }
0950: DurationValue v1 = (DurationValue) av1.getPrimitiveValue();
0951:
0952: AtomicValue av2 = (AtomicValue) operand1
0953: .evaluateItem(context);
0954: if (av2 == null) {
0955: return null;
0956: }
0957: NumericValue v2 = (NumericValue) av2.getPrimitiveValue();
0958:
0959: double d = v2.getDoubleValue();
0960:
0961: if (operator == Token.DIV) {
0962: d = 1.0 / d;
0963: }
0964:
0965: return v1.multiply(d, context);
0966:
0967: }
0968: }
0969:
0970: /**
0971: * Inner class to handle division of two durations to give a number
0972: */
0973:
0974: public static class DurationDivision extends ArithmeticExpression {
0975:
0976: public DurationDivision(Expression p1, int operator,
0977: Expression p2) {
0978: super (p1, operator, p2);
0979: }
0980:
0981: /**
0982: * Evaluate the expression.
0983: */
0984:
0985: public Item evaluateItem(XPathContext context)
0986: throws XPathException {
0987: try {
0988: return doArithmetic(operand0, operand1, context);
0989: } catch (XPathException err) {
0990: if (err.getLocator() == null) {
0991: err.setLocator(this );
0992: }
0993: throw err;
0994: }
0995: }
0996:
0997: public static Item doArithmetic(Expression operand0,
0998: Expression operand1, XPathContext context)
0999: throws XPathException {
1000:
1001: AtomicValue av1 = (AtomicValue) operand0
1002: .evaluateItem(context);
1003: if (av1 == null) {
1004: return null;
1005: }
1006: DurationValue v1 = (DurationValue) av1.getPrimitiveValue();
1007:
1008: AtomicValue av2 = (AtomicValue) operand1
1009: .evaluateItem(context);
1010: if (av2 == null) {
1011: return null;
1012: }
1013: DurationValue v2 = (DurationValue) av2.getPrimitiveValue();
1014:
1015: return v1.divide(v2, context);
1016:
1017: }
1018: }
1019:
1020: /**
1021: * Inner class to handle addition or subtraction of a Date (or Time, or DateTime) and a Duration
1022: */
1023:
1024: public static class DateAndDuration extends ArithmeticExpression {
1025:
1026: public DateAndDuration(Expression p1, int operator,
1027: Expression p2, NamePool pool) {
1028:
1029: // by the time we get here, we know that one of the operands is a duration,
1030: // but it might be either one. We make it the second.
1031:
1032: super (p1, operator, p2);
1033: final TypeHierarchy th = pool.getTypeHierarchy();
1034: if (th.isSubType(p1.getItemType(th), Type.DURATION_TYPE)) {
1035: operand0 = p2;
1036: operand1 = p1;
1037: }
1038:
1039: }
1040:
1041: /**
1042: * Evaluate the expression.
1043: */
1044:
1045: public Item evaluateItem(XPathContext context)
1046: throws XPathException {
1047: try {
1048: return doArithmetic(operand0, operator, operand1,
1049: context);
1050: } catch (XPathException err) {
1051: if (err.getLocator() == null) {
1052: err.setLocator(this );
1053: }
1054: throw err;
1055: }
1056: }
1057:
1058: public static Item doArithmetic(Expression operand0,
1059: int operator, Expression operand1, XPathContext context)
1060: throws XPathException {
1061: AtomicValue av1 = (AtomicValue) operand0
1062: .evaluateItem(context);
1063: if (av1 == null) {
1064: return null;
1065: }
1066: CalendarValue v1 = (CalendarValue) av1.getPrimitiveValue();
1067:
1068: AtomicValue av2 = (AtomicValue) operand1
1069: .evaluateItem(context);
1070: if (av2 == null) {
1071: return null;
1072: }
1073: DurationValue v2 = (DurationValue) av2.getPrimitiveValue();
1074:
1075: if (operator == Token.MINUS) {
1076: v2 = v2.multiply(-1.0, context);
1077: }
1078:
1079: return v1.add(v2);
1080:
1081: }
1082: }
1083:
1084: /**
1085: * Inner class to handle subtraction of a Date (or Time, or DateTime) from another, to return a Duration
1086: */
1087:
1088: public static class DateDifference extends ArithmeticExpression {
1089:
1090: public DateDifference(Expression p1, int operator, Expression p2) {
1091: super (p1, operator, p2);
1092: }
1093:
1094: /**
1095: * Evaluate the expression.
1096: */
1097:
1098: public Item evaluateItem(XPathContext context)
1099: throws XPathException {
1100: try {
1101: return doArithmetic(operand0, operand1, context);
1102: } catch (XPathException err) {
1103: if (err.getLocator() == null) {
1104: err.setLocator(this );
1105: }
1106: throw err;
1107: }
1108: }
1109:
1110: public static Item doArithmetic(Expression operand0,
1111: Expression operand1, XPathContext context)
1112: throws XPathException {
1113: AtomicValue av1 = (AtomicValue) operand0
1114: .evaluateItem(context);
1115: if (av1 == null) {
1116: return null;
1117: }
1118: CalendarValue v1 = (CalendarValue) av1.getPrimitiveValue();
1119:
1120: AtomicValue av2 = (AtomicValue) operand1
1121: .evaluateItem(context);
1122: if (av2 == null) {
1123: return null;
1124: }
1125: CalendarValue v2 = (CalendarValue) av2.getPrimitiveValue();
1126:
1127: return v1.subtract(v2, context);
1128:
1129: }
1130: }
1131:
1132: }
1133:
1134: //
1135: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
1136: // you may not use this file except in compliance with the License. You may obtain a copy of the
1137: // License at http://www.mozilla.org/MPL/
1138: //
1139: // Software distributed under the License is distributed on an "AS IS" basis,
1140: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
1141: // See the License for the specific language governing rights and limitations under the License.
1142: //
1143: // The Original Code is: all this file.
1144: //
1145: // The Initial Developer of the Original Code is Michael H. Kay
1146: //
1147: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
1148: //
1149: // Contributor(s): none.
1150: //
|