001: package net.sf.saxon.expr;
002:
003: import net.sf.saxon.functions.NumberFn;
004: import net.sf.saxon.om.Item;
005: import net.sf.saxon.om.SequenceIterator;
006: import net.sf.saxon.sort.AtomicComparer;
007: import net.sf.saxon.sort.CodepointCollator;
008: import net.sf.saxon.trans.DynamicError;
009: import net.sf.saxon.trans.XPathException;
010: import net.sf.saxon.type.AtomicType;
011: import net.sf.saxon.type.ItemType;
012: import net.sf.saxon.type.Type;
013: import net.sf.saxon.type.TypeHierarchy;
014: import net.sf.saxon.value.*;
015:
016: import java.util.ArrayList;
017: import java.util.Comparator;
018: import java.util.Iterator;
019: import java.util.List;
020:
021: /**
022: * GeneralComparison10: a boolean expression that compares two expressions
023: * for equals, not-equals, greater-than or less-than. This implements the operators
024: * =, !=, <, >, etc. This version of the class implements general comparisons
025: * in XPath 1.0 backwards compatibility mode, as defined in the Oct 2004 revision
026: * of the specifications.
027: */
028:
029: public class GeneralComparison10 extends BinaryExpression {
030:
031: protected int singletonOperator;
032: protected AtomicComparer comparer;
033: private boolean atomize0 = true;
034: private boolean atomize1 = true;
035: private boolean maybeBoolean0 = true;
036: private boolean maybeBoolean1 = true;
037:
038: /**
039: * Create a general comparison identifying the two operands and the operator
040: * @param p0 the left-hand operand
041: * @param op the operator, as a token returned by the Tokenizer (e.g. Token.LT)
042: * @param p1 the right-hand operand
043: */
044:
045: public GeneralComparison10(Expression p0, int op, Expression p1) {
046: super (p0, op, p1);
047: singletonOperator = getSingletonOperator(op);
048: }
049:
050: /**
051: * Determine the static cardinality. Returns [1..1]
052: */
053:
054: public int computeCardinality() {
055: return StaticProperty.EXACTLY_ONE;
056: }
057:
058: /**
059: * Type-check the expression
060: * @return the checked expression
061: */
062:
063: public Expression typeCheck(StaticContext env,
064: ItemType contextItemType) throws XPathException {
065:
066: operand0 = operand0.typeCheck(env, contextItemType);
067: operand1 = operand1.typeCheck(env, contextItemType);
068:
069: Comparator comp = env.getCollation(env
070: .getDefaultCollationName());
071: if (comp == null)
072: comp = CodepointCollator.getInstance();
073: comparer = new AtomicComparer(comp, env.getConfiguration());
074:
075: return this ;
076: }
077:
078: /**
079: * Optimize the expression
080: * @return the checked expression
081: */
082:
083: public Expression optimize(Optimizer opt, StaticContext env,
084: ItemType contextItemType) throws XPathException {
085:
086: operand0 = operand0.optimize(opt, env, contextItemType);
087: operand1 = operand1.optimize(opt, env, contextItemType);
088:
089: // Neither operand needs to be sorted
090:
091: operand0 = ExpressionTool.unsorted(opt, operand0, false);
092: operand1 = ExpressionTool.unsorted(opt, operand1, false);
093:
094: // evaluate the expression now if both arguments are constant
095:
096: if ((operand0 instanceof Value) && (operand1 instanceof Value)) {
097: return (AtomicValue) evaluateItem(env
098: .makeEarlyEvaluationContext());
099: }
100:
101: final TypeHierarchy th = env.getNamePool().getTypeHierarchy();
102: ItemType type0 = operand0.getItemType(th);
103: ItemType type1 = operand1.getItemType(th);
104:
105: if (type0 instanceof AtomicType) {
106: atomize0 = false;
107: }
108: if (type1 instanceof AtomicType) {
109: atomize1 = false;
110: }
111:
112: if (th.relationship(type0, Type.BOOLEAN_TYPE) == TypeHierarchy.DISJOINT) {
113: maybeBoolean0 = false;
114: }
115: if (th.relationship(type1, Type.BOOLEAN_TYPE) == TypeHierarchy.DISJOINT) {
116: maybeBoolean1 = false;
117: }
118:
119: if (!maybeBoolean0 && !maybeBoolean1) {
120: int n0 = th.relationship(type0, Type.NUMBER_TYPE);
121: int n1 = th.relationship(type1, Type.NUMBER_TYPE);
122: boolean maybeNumeric0 = (n0 != TypeHierarchy.DISJOINT);
123: boolean maybeNumeric1 = (n1 != TypeHierarchy.DISJOINT);
124: boolean numeric0 = (n0 == TypeHierarchy.SUBSUMED_BY || n0 == TypeHierarchy.SAME_TYPE);
125: boolean numeric1 = (n1 == TypeHierarchy.SUBSUMED_BY || n1 == TypeHierarchy.SAME_TYPE);
126:
127: // Use the 2.0 path if we don't have to deal with the possibility of boolean values,
128: // or the complications of converting values to numbers
129: if (!maybeNumeric0 && !maybeNumeric1) {
130: return new GeneralComparison(operand0, operator,
131: operand1).typeCheck(env, contextItemType)
132: .optimize(opt, env, contextItemType);
133: }
134: if (numeric0 && numeric1) {
135: return new GeneralComparison(operand0, operator,
136: operand1).typeCheck(env, contextItemType)
137: .optimize(opt, env, contextItemType);
138: }
139: }
140:
141: return this ;
142: }
143:
144: /**
145: * Evaluate the expression in a given context
146: * @param context the given context for evaluation
147: * @return a BooleanValue representing the result of the numeric comparison of the two operands
148: */
149:
150: public Item evaluateItem(XPathContext context)
151: throws XPathException {
152: return BooleanValue.get(effectiveBooleanValue(context));
153: }
154:
155: /**
156: * Evaluate the expression in a boolean context
157: * @param context the given context for evaluation
158: * @return a boolean representing the result of the numeric comparison of the two operands
159: */
160:
161: public boolean effectiveBooleanValue(XPathContext context)
162: throws XPathException {
163:
164: // If the first operand is a singleton boolean,
165: // compare it with the effective boolean value of the other operand
166:
167: SequenceIterator iter0 = null;
168:
169: if (maybeBoolean0) {
170: iter0 = operand0.iterate(context);
171: Item i01 = iter0.next();
172: Item i02 = (i01 == null ? null : iter0.next());
173: if (i01 instanceof BooleanValue && i02 == null) {
174: boolean b = operand1.effectiveBooleanValue(context);
175: return compare((BooleanValue) i01, singletonOperator,
176: BooleanValue.get(b), comparer, context);
177: }
178: if (i01 == null && !maybeBoolean1) {
179: return false;
180: }
181: }
182:
183: // If the second operand is a singleton boolean,
184: // compare it with the effective boolean value of the other operand
185:
186: SequenceIterator iter1 = null;
187:
188: if (maybeBoolean1) {
189: iter1 = operand1.iterate(context);
190: Item i11 = iter1.next();
191: Item i12 = (i11 == null ? null : iter1.next());
192: if (i11 instanceof BooleanValue && i12 == null) {
193: boolean b = operand0.effectiveBooleanValue(context);
194: return compare(BooleanValue.get(b), singletonOperator,
195: (BooleanValue) i11, comparer, context);
196: }
197: if (i11 == null && !maybeBoolean0) {
198: return false;
199: }
200: }
201:
202: // Atomize both operands where necessary
203:
204: if (iter0 == null) {
205: iter0 = operand0.iterate(context);
206: } else {
207: iter0 = iter0.getAnother();
208: }
209:
210: if (iter1 == null) {
211: iter1 = operand1.iterate(context);
212: } else {
213: iter1 = iter1.getAnother();
214: }
215:
216: if (atomize0) {
217: iter0 = Atomizer.AtomizingFunction
218: .getAtomizingIterator(iter0);
219: }
220:
221: if (atomize1) {
222: iter1 = Atomizer.AtomizingFunction
223: .getAtomizingIterator(iter1);
224: }
225:
226: // If the operator is one of <, >, <=, >=, then convert both operands to sequences of xs:double
227: // using the number() function
228:
229: if (operator == Token.LT || operator == Token.LE
230: || operator == Token.GT || operator == Token.GE) {
231: iter0 = new MappingIterator(iter0, new NumberFn(), null);
232: iter1 = new MappingIterator(iter1, new NumberFn(), null);
233: }
234:
235: // Compare all pairs of atomic values in the two atomized sequences
236:
237: List seq1 = null;
238: while (true) {
239: AtomicValue item0 = (AtomicValue) iter0.next();
240: if (item0 == null) {
241: return false;
242: }
243: if (iter1 != null) {
244: while (true) {
245: AtomicValue item1 = (AtomicValue) iter1.next();
246: if (item1 == null) {
247: iter1 = null;
248: if (seq1 == null) {
249: // second operand is empty
250: return false;
251: }
252: break;
253: }
254: try {
255: if (compare(item0, singletonOperator, item1,
256: comparer, context)) {
257: return true;
258: }
259: if (seq1 == null) {
260: seq1 = new ArrayList(40);
261: }
262: seq1.add(item1);
263: } catch (DynamicError e) {
264: // re-throw the exception with location information added
265: if (e.getXPathContext() == null) {
266: e.setXPathContext(context);
267: }
268: if (e.getLocator() == null) {
269: e.setLocator(this );
270: }
271: throw e;
272: }
273: }
274: } else {
275: Iterator listIter1 = seq1.iterator();
276: while (listIter1.hasNext()) {
277: AtomicValue item1 = (AtomicValue) listIter1.next();
278: if (compare(item0, singletonOperator, item1,
279: comparer, context)) {
280: return true;
281: }
282: }
283: }
284: }
285: }
286:
287: /**
288: * Compare two atomic values
289: */
290:
291: protected static boolean compare(AtomicValue a0, int operator,
292: AtomicValue a1, AtomicComparer comparer,
293: XPathContext context) throws XPathException {
294:
295: final TypeHierarchy th = context.getNamePool()
296: .getTypeHierarchy();
297: AtomicType t0 = (AtomicType) a0.getItemType(th)
298: .getPrimitiveItemType();
299: AtomicType t1 = (AtomicType) a1.getItemType(th)
300: .getPrimitiveItemType();
301:
302: // If either operand is a number, convert both operands to xs:double using
303: // the rules of the number() function, and compare them
304:
305: if (Type.isNumericPrimitiveType(t0)
306: || Type.isNumericPrimitiveType(t1)) {
307: DoubleValue v0 = NumberFn.convert(a0);
308: DoubleValue v1 = NumberFn.convert(a1);
309: return ValueComparison.compare(v0, operator, v1, comparer);
310: }
311:
312: // If either operand is a string, or if both are untyped atomic, convert
313: // both operands to strings and compare them
314:
315: if (t0 == Type.STRING_TYPE
316: || t1 == Type.STRING_TYPE
317: || (t0 == Type.UNTYPED_ATOMIC_TYPE && t1 == Type.UNTYPED_ATOMIC_TYPE)) {
318: StringValue s0 = (StringValue) a0.convert(Type.STRING,
319: context);
320: StringValue s1 = (StringValue) a1.convert(Type.STRING,
321: context);
322: return ValueComparison.compare(s0, operator, s1, comparer);
323: }
324:
325: // If either operand is untyped atomic,
326: // convert it to the type of the other operand, and compare
327:
328: if (t0 == Type.UNTYPED_ATOMIC_TYPE) {
329: a0 = a0.convert(t1, context, true);
330: if (a0 instanceof ValidationErrorValue) {
331: throw ((ValidationErrorValue) a0).getException();
332: }
333: }
334:
335: if (t1 == Type.UNTYPED_ATOMIC_TYPE) {
336: a1 = a1.convert(t0, context, true);
337: if (a1 instanceof ValidationErrorValue) {
338: throw ((ValidationErrorValue) a1).getException();
339: }
340: }
341:
342: return ValueComparison.compare(a0, operator, a1, comparer);
343: }
344:
345: /**
346: * Determine the data type of the expression
347: * @return Type.BOOLEAN
348: * @param th
349: */
350:
351: public ItemType getItemType(TypeHierarchy th) {
352: return Type.BOOLEAN_TYPE;
353: }
354:
355: /**
356: * Return the singleton form of the comparison operator, e.g. FEQ for EQUALS, FGT for GT
357: */
358:
359: private static int getSingletonOperator(int op) {
360: switch (op) {
361: case Token.EQUALS:
362: return Token.FEQ;
363: case Token.GE:
364: return Token.FGE;
365: case Token.NE:
366: return Token.FNE;
367: case Token.LT:
368: return Token.FLT;
369: case Token.GT:
370: return Token.FGT;
371: case Token.LE:
372: return Token.FLE;
373: default:
374: return op;
375: }
376: }
377:
378: protected String displayOperator() {
379: return "many-to-many (1.0) " + super .displayOperator();
380: }
381:
382: }
383:
384: //
385: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
386: // you may not use this file except in compliance with the License. You may obtain a copy of the
387: // License at http://www.mozilla.org/MPL/
388: //
389: // Software distributed under the License is distributed on an "AS IS" basis,
390: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
391: // See the License for the specific language governing rights and limitations under the License.
392: //
393: // The Original Code is: all this file.
394: //
395: // The Initial Developer of the Original Code is Michael H. Kay.
396: //
397: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
398: //
399: // Contributor(s): none.
400: //
|