001: package net.sf.saxon.expr;
002:
003: import net.sf.saxon.om.Item;
004: import net.sf.saxon.trans.XPathException;
005: import net.sf.saxon.type.ItemType;
006: import net.sf.saxon.type.Type;
007: import net.sf.saxon.type.TypeHierarchy;
008: import net.sf.saxon.value.BooleanValue;
009: import net.sf.saxon.functions.SystemFunction;
010: import net.sf.saxon.functions.BooleanFn;
011:
012: import java.util.Iterator;
013: import java.util.List;
014:
015: /**
016: * Boolean expression: two truth values combined using AND or OR.
017: */
018:
019: public class BooleanExpression extends BinaryExpression {
020:
021: public BooleanExpression(Expression p1, int operator, Expression p2) {
022: super (p1, operator, p2);
023: }
024:
025: /**
026: * Determine the static cardinality. Returns [1..1]
027: */
028:
029: public int computeCardinality() {
030: return StaticProperty.EXACTLY_ONE;
031: }
032:
033: /**
034: * Perform optimisation of an expression and its subexpressions.
035: * <p/>
036: * <p>This method is called after all references to functions and variables have been resolved
037: * to the declaration of the function or variable, and after all type checking has been done.</p>
038: *
039: * @param opt the optimizer in use. This provides access to supporting functions; it also allows
040: * different optimization strategies to be used in different circumstances.
041: * @param env the static context of the expression
042: * @param contextItemType the static type of "." at the point where this expression is invoked.
043: * The parameter is set to null if it is known statically that the context item will be undefined.
044: * If the type of the context item is not known statically, the argument is set to
045: * {@link net.sf.saxon.type.Type#ITEM_TYPE}
046: * @return the original expression, rewritten if appropriate to optimize execution
047: * @throws net.sf.saxon.trans.StaticError if an error is discovered during this phase
048: * (typically a type error)
049: */
050:
051: public Expression optimize(Optimizer opt, StaticContext env,
052: ItemType contextItemType) throws XPathException {
053: // Rewrite [A and B] as [if (A) then B else false()]. The benefit of this is that when B is a recursive
054: // function call, it is treated as a tail call (test qxmp290). To avoid disrupting other optimizations
055: // of "and" expressions (specifically, where clauses in FLWOR expressions), do this ONLY if B is a user
056: // function call (we can't tell if it's recursive), and it's not in a loop.
057: final Expression e = super .optimize(opt, env, contextItemType);
058: final TypeHierarchy th = env.getNamePool().getTypeHierarchy();
059: if (e == this
060: && operator == Token.AND
061: && operand1 instanceof UserFunctionCall
062: && th.isSubType(operand1.getItemType(th),
063: Type.BOOLEAN_TYPE) && !containedInLoop(env)) {
064: IfExpression cond = new IfExpression(operand0, operand1,
065: BooleanValue.FALSE);
066: cond.setParentExpression(getParentExpression());
067: return cond;
068: }
069: return this ;
070: }
071:
072: private boolean containedInLoop(StaticContext env) {
073: ComputedExpression e = this ;
074: while (true) {
075: Container c = e.getParentExpression();
076: if (c instanceof Expression
077: && ExpressionTool.isRepeatedSubexpression(
078: (Expression) c, e, env)) {
079: return true;
080: } else if (c instanceof ComputedExpression) {
081: e = (ComputedExpression) c;
082: } else {
083: return false;
084: }
085: }
086: }
087:
088: /**
089: * Return the negation of this boolean expression, that is, an expression that returns true
090: * when this expression returns false, and vice versa
091: */
092:
093: public Expression negate(StaticContext env) {
094: // Apply de Morgan's laws
095: if (operator == Token.AND) {
096: BooleanFn not0 = (BooleanFn) SystemFunction
097: .makeSystemFunction("not", 1, env.getNamePool());
098: Expression[] args0 = { operand0 };
099: not0.setArguments(args0);
100: BooleanFn not1 = (BooleanFn) SystemFunction
101: .makeSystemFunction("not", 1, env.getNamePool());
102: Expression[] args1 = { operand1 };
103: not1.setArguments(args1);
104: return new BooleanExpression(not0, Token.OR, not1);
105: } else {
106: BooleanFn not0 = (BooleanFn) SystemFunction
107: .makeSystemFunction("not", 1, env.getNamePool());
108: Expression[] args0 = { operand0 };
109: not0.setArguments(args0);
110: BooleanFn not1 = (BooleanFn) SystemFunction
111: .makeSystemFunction("not", 1, env.getNamePool());
112: Expression[] args1 = { operand1 };
113: not1.setArguments(args1);
114: return new BooleanExpression(not0, Token.AND, not1);
115: }
116: }
117:
118: /**
119: * Evaluate the expression
120: */
121:
122: public Item evaluateItem(XPathContext context)
123: throws XPathException {
124: return BooleanValue.get(effectiveBooleanValue(context));
125: }
126:
127: /**
128: * Evaluate as a boolean.
129: */
130:
131: public boolean effectiveBooleanValue(XPathContext c)
132: throws XPathException {
133: switch (operator) {
134: case Token.AND:
135: return operand0.effectiveBooleanValue(c)
136: && operand1.effectiveBooleanValue(c);
137:
138: case Token.OR:
139: return operand0.effectiveBooleanValue(c)
140: || operand1.effectiveBooleanValue(c);
141:
142: default:
143: throw new UnsupportedOperationException(
144: "Unknown operator in boolean expression");
145: }
146: }
147:
148: /**
149: * Determine the data type of the expression
150: * @return Type.BOOLEAN
151: * @param th
152: */
153:
154: public ItemType getItemType(TypeHierarchy th) {
155: return Type.BOOLEAN_TYPE;
156: }
157:
158: /**
159: * Construct a list containing the "anded" subexpressions of an expression:
160: * if the expression is (A and B and C), this returns (A, B, C).
161: * @param exp the expression to be decomposed
162: * @param list the list to which the subexpressions are to be added.
163: */
164:
165: public static void listAndComponents(Expression exp, List list) {
166: if (exp instanceof BooleanExpression
167: && ((BooleanExpression) exp).getOperator() == Token.AND) {
168: for (Iterator iter = exp.iterateSubExpressions(); iter
169: .hasNext();) {
170: listAndComponents((Expression) iter.next(), list);
171: }
172: } else {
173: list.add(exp);
174: }
175:
176: // TODO: could do more complete analysis to convert the expression to conjunctive normal form.
177: // This is done by applying various transformations:
178: // not(not(X)) => X
179: // not(P and Q) => not(P) or not(Q)
180: // not(P or Q) => not(P) and not(Q)
181: // A or (B and C) => (A or B) and (A or C)
182: }
183: }
184:
185: //
186: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
187: // you may not use this file except in compliance with the License. You may obtain a copy of the
188: // License at http://www.mozilla.org/MPL/
189: //
190: // Software distributed under the License is distributed on an "AS IS" basis,
191: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
192: // See the License for the specific language governing rights and limitations under the License.
193: //
194: // The Original Code is: all this file.
195: //
196: // The Initial Developer of the Original Code is Michael H. Kay.
197: //
198: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
199: //
200: // Contributor(s): none.
201: //
|