KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > expr > GeneralComparison


1 package net.sf.saxon.expr;
2
3 import net.sf.saxon.om.Item;
4 import net.sf.saxon.om.SequenceIterator;
5 import net.sf.saxon.sort.AtomicComparer;
6 import net.sf.saxon.sort.CodepointCollator;
7 import net.sf.saxon.trans.DynamicError;
8 import net.sf.saxon.trans.StaticError;
9 import net.sf.saxon.trans.XPathException;
10 import net.sf.saxon.type.AtomicType;
11 import net.sf.saxon.type.ItemType;
12 import net.sf.saxon.type.Type;
13 import net.sf.saxon.type.ValidationException;
14 import net.sf.saxon.value.*;
15 import net.sf.saxon.functions.Position;
16
17 import java.util.Comparator JavaDoc;
18
19
20 /**
21  * GeneralComparison: a boolean expression that compares two expressions
22  * for equals, not-equals, greater-than or less-than. This implements the operators
23  * =, !=, <, >, etc. This implementation is not used when in backwards-compatible mode
24  */

25
26 public class GeneralComparison extends BinaryExpression {
27
28     protected int singletonOperator;
29     protected AtomicComparer comparer;
30
31     /**
32      * Create a relational expression identifying the two operands and the operator
33      *
34      * @param p0 the left-hand operand
35      * @param op the operator, as a token returned by the Tokenizer (e.g. Token.LT)
36      * @param p1 the right-hand operand
37      */

38
39     public GeneralComparison(Expression p0, int op, Expression p1) {
40         super(p0, op, p1);
41         singletonOperator = getSingletonOperator(op);
42     }
43
44     /**
45      * Determine the static cardinality. Returns [1..1]
46      */

47
48     public int computeCardinality() {
49         return StaticProperty.EXACTLY_ONE;
50     }
51
52     /**
53      * Type-check the expression
54      *
55      * @return the checked expression
56      */

57
58     public Expression typeCheck(StaticContext env, ItemType contextItemType) throws XPathException {
59
60         operand0 = operand0.typeCheck(env, contextItemType);
61         operand1 = operand1.typeCheck(env, contextItemType);
62
63         // If either operand is statically empty, return false
64

65         if (operand0 == EmptySequence.getInstance() || operand1 == EmptySequence.getInstance()) {
66             return BooleanValue.FALSE;
67         }
68
69         // Neither operand needs to be sorted
70

71         Optimizer opt = env.getConfiguration().getOptimizer();
72         operand0 = ExpressionTool.unsorted(opt, operand0, false);
73         operand1 = ExpressionTool.unsorted(opt, operand1, false);
74
75         SequenceType atomicType = SequenceType.ATOMIC_SEQUENCE;
76
77         RoleLocator role0 = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 0, null);
78         role0.setSourceLocator(this);
79         operand0 = TypeChecker.staticTypeCheck(operand0, atomicType, false, role0, env);
80
81         RoleLocator role1 = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 1, null);
82         role1.setSourceLocator(this);
83         operand1 = TypeChecker.staticTypeCheck(operand1, atomicType, false, role1, env);
84
85         ItemType t0 = operand0.getItemType();
86         ItemType t1 = operand1.getItemType();
87
88         int c0 = operand0.getCardinality();
89         int c1 = operand1.getCardinality();
90
91         if (t0 == Type.ANY_ATOMIC_TYPE || t0 == Type.UNTYPED_ATOMIC_TYPE ||
92                 t1 == Type.ANY_ATOMIC_TYPE || t1 == Type.UNTYPED_ATOMIC_TYPE) {
93             // then no static type checking is possible
94
} else {
95             int pt0 = t0.getPrimitiveType();
96             int pt1 = t1.getPrimitiveType();
97             if (!Type.isComparable(pt0, pt1)) {
98                 StaticError err = new StaticError("Cannot compare " + t0.toString(env.getNamePool()) +
99                         " to " + t1.toString(env.getNamePool()));
100                 err.setErrorCode("XPTY0004");
101                 err.setIsTypeError(true);
102                 throw err;
103             }
104         }
105
106         if (c0 == StaticProperty.EXACTLY_ONE &&
107                 c1 == StaticProperty.EXACTLY_ONE &&
108                 t0 != Type.ANY_ATOMIC_TYPE &&
109                 t1 != Type.ANY_ATOMIC_TYPE) {
110
111             // Use a value comparison if both arguments are singletons, and if the comparison operator to
112
// be used can be determined.
113

114             Expression e0 = operand0;
115             Expression e1 = operand1;
116
117             if (t0 == Type.UNTYPED_ATOMIC_TYPE) {
118                 if (t1 == Type.UNTYPED_ATOMIC_TYPE) {
119                     e0 = new CastExpression(operand0, Type.STRING_TYPE, false);
120                     adoptChildExpression(e0);
121                     e1 = new CastExpression(operand1, Type.STRING_TYPE, false);
122                     adoptChildExpression(e1);
123                 } else if (Type.isSubType(t1, Type.NUMBER_TYPE)) {
124                     e0 = new CastExpression(operand0, Type.DOUBLE_TYPE, false);
125                     adoptChildExpression(e0);
126                 } else {
127                     e0 = new CastExpression(operand0, (AtomicType)t1, false);
128                     adoptChildExpression(e0);
129                 }
130             } else if (t1 == Type.UNTYPED_ATOMIC_TYPE) {
131                 if (Type.isSubType(t0, Type.NUMBER_TYPE)) {
132                     e1 = new CastExpression(operand1, Type.DOUBLE_TYPE, false);
133                     adoptChildExpression(e1);
134                 } else {
135                     e1 = new CastExpression(operand1, (AtomicType)t0, false);
136                     adoptChildExpression(e1);
137                 }
138             }
139
140             ValueComparison vc = new ValueComparison(e0, singletonOperator, e1);
141             ExpressionTool.copyLocationInfo(this, vc);
142             vc.setParentExpression(getParentExpression());
143             return vc.simplify(env).typeCheck(env, contextItemType);
144         }
145
146         Comparator JavaDoc comp = env.getCollation(env.getDefaultCollationName());
147         if (comp == null) {
148             comp = CodepointCollator.getInstance();
149         }
150         comparer = new AtomicComparer(comp, env.getConfiguration());
151
152         // Check if neither argument allows a sequence of >1
153

154         if (!Cardinality.allowsMany(c0) && !Cardinality.allowsMany(c1)) {
155
156             // Use a singleton comparison if both arguments are singletons
157

158             SingletonComparison sc = new SingletonComparison(operand0, singletonOperator, operand1);
159             ExpressionTool.copyLocationInfo(this, sc);
160             sc.setParentExpression(getParentExpression());
161             sc.setComparator(comparer, env.getConfiguration());
162             return sc.typeCheck(env, contextItemType);
163         }
164
165         // see if second argument is a singleton...
166

167         if (!Cardinality.allowsMany(c0)) {
168
169             // if first argument is a singleton, reverse the arguments
170
//ManyToOneComparison mc = new ManyToOneComparison(operand1, Value.inverse(singletonOperator), operand0);
171
GeneralComparison mc = getInverseComparison();
172             ExpressionTool.copyLocationInfo(this, mc);
173             mc.setParentExpression(getParentExpression());
174             mc.comparer = comparer;
175             return mc.typeCheck(env, contextItemType);
176         }
177
178         // If the operator is gt, ge, lt, le then replace X < Y by min(X) < max(Y)
179

180         // This optimization is done only in the case where at least one of the
181
// sequences is known to be purely numeric. It isn't safe if both sequences
182
// contain untyped atomic values, because in that case, the type of the
183
// comparison isn't known in advance. For example [(1, U1) < ("fred", U2)]
184
// involves both string and numeric comparisons.
185

186
187         if (operator != Token.EQUALS && operator != Token.NE &&
188                 (Type.isSubType(t0, Type.NUMBER_TYPE) || Type.isSubType(t1, Type.NUMBER_TYPE))) {
189
190             Expression e0 = operand0;
191             if (!Type.isSubType(t0, Type.NUMBER_TYPE)) {
192                 RoleLocator role = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 0, null);
193                 role.setSourceLocator(this);
194                 e0 = TypeChecker.staticTypeCheck(e0, SequenceType.NUMERIC_SEQUENCE, false, role, env);
195             }
196             Expression e1 = operand1;
197             if (!Type.isSubType(t1, Type.NUMBER_TYPE)) {
198                 RoleLocator role = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 1, null);
199                 role.setSourceLocator(this);
200                 e1 = TypeChecker.staticTypeCheck(e1, SequenceType.NUMERIC_SEQUENCE, false, role, env);
201             }
202             MinimaxComparison mc = new MinimaxComparison(e0, operator, e1);
203             ExpressionTool.copyLocationInfo(this, mc);
204             mc.setParentExpression(getParentExpression());
205             return mc.typeCheck(env, contextItemType);
206
207         }
208
209
210         // evaluate the expression now if both arguments are constant
211

212         if ((operand0 instanceof Value) && (operand1 instanceof Value)) {
213             return (AtomicValue)evaluateItem(null);
214         }
215
216         return this;
217     }
218
219
220     /**
221      * Type-check the expression
222      *
223      * @return the checked expression
224      */

225
226     public Expression optimize(Optimizer opt, StaticContext env, ItemType contextItemType) throws XPathException {
227
228         operand0 = operand0.optimize(opt, env, contextItemType);
229         operand1 = operand1.optimize(opt, env, contextItemType);
230
231         // If either operand is statically empty, return false
232

233         if (operand0 == EmptySequence.getInstance() || operand1 == EmptySequence.getInstance()) {
234             return BooleanValue.FALSE;
235         }
236
237         // Neither operand needs to be sorted
238

239         operand0 = ExpressionTool.unsorted(opt, operand0, false);
240         operand1 = ExpressionTool.unsorted(opt, operand1, false);
241
242         ItemType t0 = operand0.getItemType();
243         ItemType t1 = operand1.getItemType();
244
245         int c0 = operand0.getCardinality();
246         int c1 = operand1.getCardinality();
247
248         if (c0 == StaticProperty.EXACTLY_ONE &&
249                 c1 == StaticProperty.EXACTLY_ONE &&
250                 t0 != Type.ANY_ATOMIC_TYPE &&
251                 t1 != Type.ANY_ATOMIC_TYPE) {
252
253             // Use a value comparison if both arguments are singletons, and if the comparison operator to
254
// be used can be determined.
255

256             Expression e0 = operand0;
257             Expression e1 = operand1;
258
259             if (t0 == Type.UNTYPED_ATOMIC_TYPE) {
260                 if (t1 == Type.UNTYPED_ATOMIC_TYPE) {
261                     e0 = new CastExpression(operand0, Type.STRING_TYPE, false);
262                     adoptChildExpression(e0);
263                     e1 = new CastExpression(operand1, Type.STRING_TYPE, false);
264                     adoptChildExpression(e1);
265                 } else if (Type.isSubType(t1, Type.NUMBER_TYPE)) {
266                     e0 = new CastExpression(operand0, Type.DOUBLE_TYPE, false);
267                     adoptChildExpression(e0);
268                 } else {
269                     e0 = new CastExpression(operand0, (AtomicType)t1, false);
270                     adoptChildExpression(e0);
271                 }
272             } else if (t1 == Type.UNTYPED_ATOMIC_TYPE) {
273                 if (Type.isSubType(t0, Type.NUMBER_TYPE)) {
274                     e1 = new CastExpression(operand1, Type.DOUBLE_TYPE, false);
275                     adoptChildExpression(e1);
276                 } else {
277                     e1 = new CastExpression(operand1, (AtomicType)t0, false);
278                     adoptChildExpression(e1);
279                 }
280             }
281
282             ValueComparison vc = new ValueComparison(e0, singletonOperator, e1);
283             ExpressionTool.copyLocationInfo(this, vc);
284             vc.setParentExpression(getParentExpression());
285             return vc.simplify(env).typeCheck(env, contextItemType).optimize(opt, env, contextItemType);
286         }
287
288         Comparator JavaDoc comp = env.getCollation(env.getDefaultCollationName());
289         if (comp == null) {
290             comp = CodepointCollator.getInstance();
291         }
292         comparer = new AtomicComparer(comp, env.getConfiguration());
293
294         // Check if neither argument allows a sequence of >1
295

296         if (!Cardinality.allowsMany(c0) && !Cardinality.allowsMany(c1)) {
297
298             // Use a singleton comparison if both arguments are singletons
299

300             SingletonComparison sc = new SingletonComparison(operand0, singletonOperator, operand1);
301             ExpressionTool.copyLocationInfo(this, sc);
302             sc.setParentExpression(getParentExpression());
303             sc.setComparator(comparer, env.getConfiguration());
304             return sc.typeCheck(env, contextItemType).optimize(opt, env, contextItemType);
305         }
306
307         // see if first argument is a singleton...
308

309         if (!Cardinality.allowsMany(c0)) {
310
311             // if first argument is a singleton, reverse the arguments
312
GeneralComparison mc = getInverseComparison();
313             ExpressionTool.copyLocationInfo(this, mc);
314             mc.setParentExpression(getParentExpression());
315             mc.comparer = comparer;
316             return mc.optimize(opt, env, contextItemType);
317         }
318
319         // look for (N to M = I)
320

321
322         if (operand0 instanceof RangeExpression &&
323                 Type.isSubType(operand1.getItemType(), Type.INTEGER_TYPE) &&
324                 !Cardinality.allowsMany(operand1.getCardinality())) {
325             Expression min = ((RangeExpression)operand0).operand0;
326             Expression max = ((RangeExpression)operand0).operand1;
327             if (operand1 instanceof Position) {
328                 PositionRange pr = new PositionRange(min, max);
329                 ExpressionTool.copyLocationInfo(this, pr);
330                 pr.setParentExpression(getParentExpression());
331                 return pr;
332             } else {
333                 IntegerRangeTest ir = new IntegerRangeTest(operand1, min, max);
334                 ExpressionTool.copyLocationInfo(this, ir);
335                 ir.setParentExpression(getParentExpression());
336                 return ir;
337             }
338         }
339
340         if (operand0 instanceof IntegerRange &&
341                 Type.isSubType(operand1.getItemType(), Type.INTEGER_TYPE) &&
342                 !Cardinality.allowsMany(operand1.getCardinality())) {
343             long min = ((IntegerRange)operand0).getStart();
344             long max = ((IntegerRange)operand0).getEnd();
345             if (operand1 instanceof Position) {
346                 PositionRange pr = new PositionRange(new IntegerValue(min), new IntegerValue(max));
347                 ExpressionTool.copyLocationInfo(this, pr);
348                 pr.setParentExpression(getParentExpression());
349                 return pr;
350             } else {
351                 IntegerRangeTest ir = new IntegerRangeTest(operand1, new IntegerValue(min), new IntegerValue(max));
352                 ExpressionTool.copyLocationInfo(this, ir);
353                 ir.setParentExpression(getParentExpression());
354                 return ir;
355             }
356         }
357
358         // evaluate the expression now if both arguments are constant
359

360         if ((operand0 instanceof Value) && (operand1 instanceof Value)) {
361             return (AtomicValue)evaluateItem(null);
362         }
363
364         return this;
365     }
366
367
368     /**
369      * Evaluate the expression in a given context
370      *
371      * @param context the given context for evaluation
372      * @return a BooleanValue representing the result of the numeric comparison of the two operands
373      */

374
375     public Item evaluateItem(XPathContext context) throws XPathException {
376         return BooleanValue.get(effectiveBooleanValue(context));
377     }
378
379     /**
380      * Evaluate the expression in a boolean context
381      *
382      * @param context the given context for evaluation
383      * @return a boolean representing the result of the numeric comparison of the two operands
384      */

385
386     public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
387
388         try {
389             SequenceIterator iter1 = operand0.iterate(context);
390             SequenceIterator iter2 = operand1.iterate(context);
391
392             Value seq2 = SequenceExtent.makeSequenceExtent(iter2);
393             // we choose seq2 because it's more likely to be a singleton
394
int count2 = seq2.getLength();
395
396             if (count2 == 0) {
397                 return false;
398             }
399
400             if (count2 == 1) {
401                 AtomicValue s2 = (AtomicValue)seq2.itemAt(0);
402                 while (true) {
403                     AtomicValue s1 = (AtomicValue)iter1.next();
404                     if (s1 == null) {
405                         break;
406                     }
407                     if (compare(s1, singletonOperator, s2, comparer, context)) {
408                         return true;
409                     }
410                 }
411                 return false;
412             }
413
414             while (true) {
415                 AtomicValue s1 = (AtomicValue)iter1.next();
416                 if (s1 == null) {
417                     break;
418                 }
419                 SequenceIterator e2 = seq2.iterate(null);
420                 while (true) {
421                     AtomicValue s2 = (AtomicValue)e2.next();
422                     if (s2 == null) {
423                         break;
424                     }
425                     if (compare(s1, singletonOperator, s2, comparer, context)) {
426                         return true;
427                     }
428                 }
429             }
430
431             return false;
432         } catch (DynamicError e) {
433             // re-throw the exception with location information added
434
if (e.getXPathContext() == null) {
435                 e.setXPathContext(context);
436             }
437             if (e.getLocator() == null) {
438                 e.setLocator(this);
439             }
440             throw e;
441         } catch (ValidationException e) {
442             DynamicError err = new DynamicError(e);
443             err.setXPathContext(context);
444             if (e.getLineNumber() == -1) {
445                 err.setLocator(this);
446             } else {
447                 err.setLocator(e);
448             }
449             err.setErrorCode(e.getErrorCodeLocalPart());
450             throw err;
451         }
452
453     }
454
455     /**
456      * Compare two atomic values
457      */

458
459     protected static boolean compare(AtomicValue a1,
460                                      int operator,
461                                      AtomicValue a2,
462                                      AtomicComparer comparer,
463                                      XPathContext context) throws XPathException {
464
465         AtomicValue v1 = a1;
466         AtomicValue v2 = a2;
467         if (a1 instanceof UntypedAtomicValue) {
468             if (a2 instanceof NumericValue) {
469                 v1 = a1.convert(Type.DOUBLE, context);
470             } else if (a2 instanceof UntypedAtomicValue) {
471                 // the spec says convert it to a string, but this doesn't affect the outcome
472
} else {
473                 v1 = a1.convert(a2.getItemType().getPrimitiveType(), context);
474             }
475         }
476         if (a2 instanceof UntypedAtomicValue) {
477             if (a1 instanceof NumericValue) {
478                 v2 = a2.convert(Type.DOUBLE, context);
479             } else if (a1 instanceof UntypedAtomicValue) {
480                 // the spec says convert it to a string, but this doesn't affect the outcome
481
} else {
482                 v2 = a2.convert(a1.getItemType().getPrimitiveType(), context);
483             }
484         }
485         return ValueComparison.compare(v1, operator, v2, comparer);
486
487     }
488
489     /**
490      * Determine the data type of the expression
491      *
492      * @return Type.BOOLEAN
493      */

494
495     public ItemType getItemType() {
496         return Type.BOOLEAN_TYPE;
497     }
498
499     /**
500      * Return the singleton form of the comparison operator, e.g. FEQ for EQUALS, FGT for GT
501      */

502
503     private static int getSingletonOperator(int op) {
504         switch (op) {
505             case Token.EQUALS:
506                 return Token.FEQ;
507             case Token.GE:
508                 return Token.FGE;
509             case Token.NE:
510                 return Token.FNE;
511             case Token.LT:
512                 return Token.FLT;
513             case Token.GT:
514                 return Token.FGT;
515             case Token.LE:
516                 return Token.FLE;
517             default:
518                 return op;
519         }
520     }
521
522     protected GeneralComparison getInverseComparison() {
523         return new GeneralComparison(operand1, Token.inverse(operator), operand0);
524     }
525
526     protected String JavaDoc displayOperator() {
527         return "many-to-many " + super.displayOperator();
528     }
529
530 }
531
532 //
533
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
534
// you may not use this file except in compliance with the License. You may obtain a copy of the
535
// License at http://www.mozilla.org/MPL/
536
//
537
// Software distributed under the License is distributed on an "AS IS" basis,
538
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
539
// See the License for the specific language governing rights and limitations under the License.
540
//
541
// The Original Code is: all this file.
542
//
543
// The Initial Developer of the Original Code is Michael H. Kay.
544
//
545
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
546
//
547
// Contributor(s): none.
548
//
549
Popular Tags