001: package net.sf.saxon.expr;
002:
003: import net.sf.saxon.om.NamePool;
004: import net.sf.saxon.trans.DynamicError;
005: import net.sf.saxon.trans.XPathException;
006: import net.sf.saxon.type.ItemType;
007: import net.sf.saxon.value.Cardinality;
008: import net.sf.saxon.value.Value;
009:
010: import java.io.PrintStream;
011: import java.util.Iterator;
012:
013: /**
014: * Binary Expression: a numeric or boolean expression consisting of the
015: * two operands and an operator
016: */
017:
018: public abstract class BinaryExpression extends ComputedExpression {
019:
020: protected Expression operand0;
021: protected Expression operand1;
022: protected int operator; // represented by the token number from class Tokenizer
023:
024: /**
025: * Create a binary expression identifying the two operands and the operator
026: * @param p0 the left-hand operand
027: * @param op the operator, as a token returned by the Tokenizer (e.g. Token.AND)
028: * @param p1 the right-hand operand
029: */
030:
031: public BinaryExpression(Expression p0, int op, Expression p1) {
032: this .operator = op;
033: operand0 = p0;
034: operand1 = p1;
035: adoptChildExpression(p0);
036: adoptChildExpression(p1);
037: }
038:
039: /**
040: * Simplify an expression
041: * @return the simplified expression
042: */
043:
044: public Expression simplify(StaticContext env) throws XPathException {
045: operand0 = operand0.simplify(env);
046: operand1 = operand1.simplify(env);
047: return this ;
048: }
049:
050: /**
051: * Type-check the expression. Default implementation for binary operators that accept
052: * any kind of operand
053: */
054:
055: public Expression typeCheck(StaticContext env,
056: ItemType contextItemType) throws XPathException {
057: operand0 = operand0.typeCheck(env, contextItemType);
058: operand1 = operand1.typeCheck(env, contextItemType);
059: // if both operands are known, pre-evaluate the expression
060: try {
061: if ((operand0 instanceof Value)
062: && (operand1 instanceof Value)) {
063: return ExpressionTool.eagerEvaluate(this , env
064: .makeEarlyEvaluationContext());
065: }
066: } catch (DynamicError err) {
067: // if early evaluation fails, suppress the error: the value might
068: // not be needed at run-time
069: }
070: return this ;
071: }
072:
073: /**
074: * Perform optimisation of an expression and its subexpressions.
075: * <p/>
076: * <p>This method is called after all references to functions and variables have been resolved
077: * to the declaration of the function or variable, and after all type checking has been done.</p>
078: *
079: * @param opt the optimizer in use. This provides access to supporting functions; it also allows
080: * different optimization strategies to be used in different circumstances.
081: * @param env the static context of the expression
082: * @param contextItemType the static type of "." at the point where this expression is invoked.
083: * The parameter is set to null if it is known statically that the context item will be undefined.
084: * If the type of the context item is not known statically, the argument is set to
085: * {@link net.sf.saxon.type.Type#ITEM_TYPE}
086: * @return the original expression, rewritten if appropriate to optimize execution
087: * @throws net.sf.saxon.trans.StaticError if an error is discovered during this phase
088: * (typically a type error)
089: */
090:
091: public Expression optimize(Optimizer opt, StaticContext env,
092: ItemType contextItemType) throws XPathException {
093: operand0 = operand0.optimize(opt, env, contextItemType);
094: operand1 = operand1.optimize(opt, env, contextItemType);
095: // if both operands are known, pre-evaluate the expression
096: try {
097: if ((operand0 instanceof Value)
098: && (operand1 instanceof Value)) {
099: return ExpressionTool.eagerEvaluate(this , env
100: .makeEarlyEvaluationContext());
101: }
102: } catch (DynamicError err) {
103: // if early evaluation fails, suppress the error: the value might
104: // not be needed at run-time
105: }
106: return this ;
107: }
108:
109: /**
110: * Promote this expression if possible
111: */
112:
113: public Expression promote(PromotionOffer offer)
114: throws XPathException {
115: Expression exp = offer.accept(this );
116: if (exp != null) {
117: return exp;
118: } else {
119: if (offer.action != PromotionOffer.UNORDERED) {
120: operand0 = doPromotion(operand0, offer);
121: operand1 = doPromotion(operand1, offer);
122: }
123: return this ;
124: }
125: }
126:
127: /**
128: * Get the immediate subexpressions of this expression
129: */
130:
131: public Iterator iterateSubExpressions() {
132: return new PairIterator(operand0, operand1);
133: }
134:
135: /**
136: * Get the operator
137: */
138:
139: public int getOperator() {
140: return operator;
141: }
142:
143: /**
144: * Get the operands
145: */
146:
147: public Expression[] getOperands() {
148: Expression[] ops = { operand0, operand1 };
149: return ops;
150: }
151:
152: /**
153: * Determine the static cardinality. Default implementation returns [0..1] if either operand
154: * can be empty, or [1..1] otherwise.
155: */
156:
157: public int computeCardinality() {
158: if (Cardinality.allowsZero(operand0.getCardinality())
159: || Cardinality.allowsZero(operand1.getCardinality())) {
160: return StaticProperty.ALLOWS_ZERO_OR_ONE;
161: } else {
162: return StaticProperty.EXACTLY_ONE;
163: }
164: }
165:
166: /**
167: * Determine the special properties of this expression
168: * @return {@link StaticProperty#NON_CREATIVE}. This is overridden
169: * for some subclasses.
170: */
171:
172: public int computeSpecialProperties() {
173: int p = super .computeSpecialProperties();
174: return p | StaticProperty.NON_CREATIVE;
175: }
176:
177: protected static boolean isCommutative(int operator) {
178: return (operator == Token.AND || operator == Token.OR
179: || operator == Token.UNION
180: || operator == Token.INTERSECT
181: || operator == Token.PLUS || operator == Token.MULT
182: || operator == Token.EQUALS || operator == Token.FEQ
183: || operator == Token.NE || operator == Token.FNE);
184: }
185:
186: /**
187: * Test if one operator is the inverse of another, so that (A op1 B) is
188: * equivalent to (B op2 A). Commutative operators are the inverse of themselves
189: * and are therefore not listed here.
190: * @param op1 the first operator
191: * @param op2 the second operator
192: * @return true if the operators are the inverse of each other
193: */
194: protected static boolean isInverse(int op1, int op2) {
195: return op1 != op2 && op1 == Token.inverse(op2);
196: }
197:
198: /**
199: * Is this expression the same as another expression?
200: */
201:
202: public boolean equals(Object other) {
203: if (other instanceof BinaryExpression) {
204: BinaryExpression b = (BinaryExpression) other;
205: if (operator == b.operator) {
206: if (operand0.equals(b.operand0)
207: && operand1.equals(b.operand1)) {
208: return true;
209: }
210: if (isCommutative(operator)
211: && operand0.equals(b.operand1)
212: && operand1.equals(b.operand0)) {
213: return true;
214: }
215: }
216: if (isInverse(operator, b.operator)
217: && operand0.equals(b.operand1)
218: && operand1.equals(b.operand0)) {
219: return true;
220: }
221: // TODO: recognize associative operators (A|(B|C)) == ((A|B)|C)
222: }
223: return false;
224: }
225:
226: /**
227: * get HashCode for comparing two expressions. Note that this hashcode gives the same
228: * result for (A op B) and for (B op A), whether or not the operator is commutative.
229: */
230:
231: public int hashCode() {
232: // Ensure that an operator and its inverse get the same hash code,
233: // so that (A lt B) has the same hash code as (B gt A)
234: int op = Math.min(operator, Token.inverse(operator));
235: return ("BinaryExpression " + op).hashCode()
236: ^ operand0.hashCode() ^ operand1.hashCode();
237: }
238:
239: /**
240: * Diagnostic print of expression structure
241: */
242:
243: public void display(int level, NamePool pool, PrintStream out) {
244: out.println(ExpressionTool.indent(level) + "operator "
245: + displayOperator());
246: operand0.display(level + 1, pool, out);
247: operand1.display(level + 1, pool, out);
248: }
249:
250: protected String displayOperator() {
251: return Token.tokens[operator];
252: }
253:
254: }
255:
256: //
257: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
258: // you may not use this file except in compliance with the License. You may obtain a copy of the
259: // License at http://www.mozilla.org/MPL/
260: //
261: // Software distributed under the License is distributed on an "AS IS" basis,
262: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
263: // See the License for the specific language governing rights and limitations under the License.
264: //
265: // The Original Code is: all this file.
266: //
267: // The Initial Developer of the Original Code is Michael H. Kay.
268: //
269: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
270: //
271: // Contributor(s): none.
272: //
|