KickJava   Java API By Example, From Geeks To Geeks.

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


1 package net.sf.saxon.expr;
2 import net.sf.saxon.functions.*;
3 import net.sf.saxon.om.Item;
4 import net.sf.saxon.sort.AtomicComparer;
5 import net.sf.saxon.sort.CodepointCollator;
6 import net.sf.saxon.trans.DynamicError;
7 import net.sf.saxon.trans.StaticError;
8 import net.sf.saxon.trans.XPathException;
9 import net.sf.saxon.type.AtomicType;
10 import net.sf.saxon.type.ItemType;
11 import net.sf.saxon.type.Type;
12 import net.sf.saxon.value.*;
13
14 import java.util.Comparator JavaDoc;
15
16 /**
17 * ValueComparison: a boolean expression that compares two atomic values
18 * for equals, not-equals, greater-than or less-than. Implements the operators
19 * eq, ne, lt, le, gt, ge
20 */

21
22 public final class ValueComparison extends BinaryExpression {
23
24     private AtomicComparer comparer;
25
26     /**
27     * Create a relational expression identifying the two operands and the operator
28     * @param p1 the left-hand operand
29     * @param op the operator, as a token returned by the Tokenizer (e.g. Token.LT)
30     * @param p2 the right-hand operand
31     */

32
33     public ValueComparison(Expression p1, int op, Expression p2) {
34         super(p1, op, p2);
35     }
36
37     /**
38     * Type-check the expression
39     */

40
41     public Expression typeCheck(StaticContext env, ItemType contextItemType) throws XPathException {
42
43         operand0 = operand0.typeCheck(env, contextItemType);
44         if (operand0 instanceof EmptySequence) {
45             return operand0;
46         }
47         operand1 = operand1.typeCheck(env, contextItemType);
48         if (operand1 instanceof EmptySequence) {
49             return operand1;
50         }
51
52         final SequenceType optionalAtomic = SequenceType.OPTIONAL_ATOMIC;
53
54         RoleLocator role0 = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 0, null);
55         role0.setSourceLocator(this);
56         operand0 = TypeChecker.staticTypeCheck(operand0, optionalAtomic, false, role0, env);
57
58         RoleLocator role1 = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 1, null);
59         role1.setSourceLocator(this);
60         operand1 = TypeChecker.staticTypeCheck(operand1, optionalAtomic, false, role1, env);
61
62         AtomicType t1 = operand0.getItemType().getAtomizedItemType();
63         AtomicType t2 = operand1.getItemType().getAtomizedItemType();
64
65         int p1 = t1.getPrimitiveType();
66         if (p1 == Type.UNTYPED_ATOMIC) {
67             p1 = Type.STRING;
68         }
69         int p2 = t2.getPrimitiveType();
70         if (p2 == Type.UNTYPED_ATOMIC) {
71             p2 = Type.STRING;
72         }
73
74         if (!Type.isComparable(p1, p2)) {
75             boolean opt0 = Cardinality.allowsZero(operand0.getCardinality());
76             boolean opt1 = Cardinality.allowsZero(operand1.getCardinality());
77             if (opt0 || opt1) {
78                 // This is a comparison such as (xs:integer? eq xs:date?). This is almost
79
// certainly an error, but we need to let it through because it will work if
80
// one of the operands is an empty sequence.
81

82                 String JavaDoc which = null;
83                 if (opt0) which = "the first operand is";
84                 if (opt1) which = "the second operand is";
85                 if (opt0 && opt1) which = "one or both operands are";
86                 env.issueWarning(
87                         "Comparison of " + t1.toString(env.getNamePool()) + (opt0?"?":"") + " to " + t2.toString(env.getNamePool()) +
88                         (opt1?"?":"") + " will fail unless " + which + " empty", this);
89
90             } else {
91                 StaticError err =
92                     new StaticError("Cannot compare " + t1.toString(env.getNamePool()) +
93                                               " to " + t2.toString(env.getNamePool()));
94                 err.setIsTypeError(true);
95                 err.setErrorCode("XPTY0004");
96                 throw err;
97             }
98         }
99         if (!(operator == Token.FEQ || operator == Token.FNE)) {
100             if (!Type.isOrdered(p1)) {
101                 StaticError err = new StaticError(
102                         "Type " + t1.toString(env.getNamePool()) + " is not an ordered type");
103                 err.setErrorCode("XPTY0004");
104                 err.setIsTypeError(true);
105                 throw err;
106             }
107             if (!Type.isOrdered(p2)) {
108                 StaticError err = new StaticError(
109                         "Type " + t2.toString(env.getNamePool()) + " is not an ordered type");
110                 err.setErrorCode("XPTY0004");
111                 err.setIsTypeError(true);
112                 throw err;
113             }
114         }
115
116         Comparator JavaDoc comp = env.getCollation(env.getDefaultCollationName());
117         if (comp==null) comp = CodepointCollator.getInstance();
118         comparer = new AtomicComparer(comp, env.getConfiguration());
119         return this;
120     }
121
122     /**
123      * Perform optimisation of an expression and its subexpressions.
124      * <p/>
125      * <p>This method is called after all references to functions and variables have been resolved
126      * to the declaration of the function or variable, and after all type checking has been done.</p>
127      *
128      * @param opt the optimizer in use. This provides access to supporting functions; it also allows
129      * different optimization strategies to be used in different circumstances.
130      * @param env the static context of the expression
131      * @param contextItemType the static type of "." at the point where this expression is invoked.
132      * The parameter is set to null if it is known statically that the context item will be undefined.
133      * If the type of the context item is not known statically, the argument is set to
134      * {@link net.sf.saxon.type.Type#ITEM_TYPE}
135      * @return the original expression, rewritten if appropriate to optimize execution
136      * @throws net.sf.saxon.trans.StaticError if an error is discovered during this phase
137      * (typically a type error)
138      */

139
140     public Expression optimize(Optimizer opt, StaticContext env, ItemType contextItemType) throws XPathException {
141
142         operand0 = operand0.optimize(opt, env, contextItemType);
143         operand1 = operand1.optimize(opt, env, contextItemType);
144                 // optimise count(x) eq 0 (or gt 0, ne 0, eq 0, etc)
145

146         if (Aggregate.isCountFunction(operand0) &&
147                 operand1 instanceof AtomicValue) {
148             if (isZero(operand1)) {
149                 if (operator == Token.FEQ || operator == Token.FLE ) {
150                     // rewrite count(x)=0 as empty(x)
151
FunctionCall fn = SystemFunction.makeSystemFunction("empty", 1, env.getNamePool());
152                     Expression[] args = new Expression[1];
153                     args[0] = ((FunctionCall)operand0).argument[0];
154                     fn.setArguments(args);
155                     return fn;
156                 } else if (operator == Token.FNE || operator == Token.FGT) {
157                     // rewrite count(x)!=0, count(x)>0 as exists(x)
158
FunctionCall fn = SystemFunction.makeSystemFunction("exists", 1, env.getNamePool());
159                     Expression[] args = new Expression[1];
160                     args[0] = ExpressionTool.unsorted(opt, ((FunctionCall)operand0).argument[0], false);
161                     fn.setArguments(args);
162                     return fn;
163                 } else if (operator == Token.FGE) {
164                     // rewrite count(x)>=0 as true()
165
return BooleanValue.TRUE;
166                 } else { // singletonOperator == Token.FLT
167
// rewrite count(x)<0 as false()
168
return BooleanValue.FALSE;
169                 }
170             } else if (operand1 instanceof IntegerValue &&
171                     (operator == Token.FGT || operator == Token.FGE) ) {
172                 // rewrite count(x) gt n as exists(x[n+1])
173
// and count(x) ge n as exists(x[n])
174
long val = ((IntegerValue)operand1).longValue();
175                 if (operator == Token.FGT) {
176                     val++;
177                 }
178                 FunctionCall fn = SystemFunction.makeSystemFunction("exists", 1, env.getNamePool());
179                 Expression[] args = new Expression[1];
180                 FilterExpression filter =
181                         new FilterExpression(((FunctionCall)operand0).argument[0],
182                                 new IntegerValue(val), env);
183                 args[0] = filter;
184                 fn.setArguments(args);
185                 return fn;
186             }
187         }
188
189         // optimise (0 eq count(x)), etc
190

191         if (Aggregate.isCountFunction(operand1) && isZero(operand0)) {
192             Expression s =
193                 new ValueComparison(operand1, Token.inverse(operator), operand0).typeCheck(env, contextItemType);
194             //((ValueComparison)s).defaultCollation = defaultCollation;
195
return s.optimize(opt, env, contextItemType);
196         }
197
198         // optimise string-length(x) = 0, >0, !=0 etc
199

200         if ((operand0 instanceof StringLength) &&
201                 (((StringLength)operand0).getNumberOfArguments()==1) && isZero(operand1)) {
202             ((StringLength)operand0).setShortcut();
203         }
204
205         // optimise (0 = string-length(x)), etc
206

207         if ((operand1 instanceof StringLength) &&
208                 (((StringLength)operand1).getNumberOfArguments()==1) && isZero(operand0)) {
209             ((StringLength)operand1).setShortcut();
210         }
211
212         // optimise [position()=last()] etc
213

214         if ((operand0 instanceof Position) && (operand1 instanceof Last)) {
215             switch (operator) {
216                 case Token.FEQ:
217                 case Token.FGE:
218                     return new IsLastExpression(true);
219                 case Token.FNE:
220                 case Token.FLT:
221                     return new IsLastExpression(false);
222                 case Token.FGT:
223                     return BooleanValue.FALSE;
224                 case Token.FLE:
225                     return BooleanValue.TRUE;
226             }
227         }
228         if ((operand0 instanceof Last) && (operand1 instanceof Position)) {
229             switch (operator) {
230                 case Token.FEQ:
231                 case Token.FLE:
232                     return new IsLastExpression(true);
233                 case Token.FNE:
234                 case Token.FGT:
235                     return new IsLastExpression(false);
236                 case Token.FLT:
237                     return BooleanValue.FALSE;
238                 case Token.FGE:
239                     return BooleanValue.TRUE;
240             }
241         }
242
243         // optimise [position() < n] etc
244

245         if (operand0 instanceof Position) {
246             boolean isInteger = (operand1 instanceof IntegerValue);
247             int pos = 0;
248             if (isInteger) {
249                 pos = (int)((IntegerValue)operand1).longValue();
250                 if (pos < 0) {
251                     pos = 0;
252                 }
253             }
254             switch (operator) {
255                 case Token.FEQ:
256                     return new PositionRange(operand1, operand1);
257                 case Token.FGE:
258                     return new PositionRange(operand1, null);
259                 case Token.FNE:
260                     if (isInteger && pos==1) {
261                         return new PositionRange(2, Integer.MAX_VALUE);
262                     } else {
263                         break;
264                     }
265                 case Token.FLT:
266                     if (isInteger) {
267                         return new PositionRange(1, pos-1);
268                     } else {
269                         break;
270                     }
271                 case Token.FGT:
272                     if (isInteger) {
273                         return new PositionRange(pos+1, Integer.MAX_VALUE);
274                     } else {
275                         break;
276                     }
277                 case Token.FLE:
278                     if (isInteger) {
279                         return new PositionRange(1, pos);
280                     } else {
281                         break;
282                     }
283             }
284         }
285
286         if (operand1 instanceof Position) {
287             int pos = 0;
288             boolean isInteger = (operand0 instanceof IntegerValue);
289             if (isInteger) {
290                 pos = (int)((IntegerValue)operand0).longValue();
291                 if (pos < 0) {
292                     pos = 0;
293                 }
294             }
295
296             switch (operator) {
297                 case Token.FEQ:
298                     return new PositionRange(operand0, operand0);
299                 case Token.FLE:
300                     return new PositionRange(operand0, null);
301                 case Token.FNE:
302                    if (isInteger && pos==1) {
303                         return new PositionRange(2, Integer.MAX_VALUE);
304                     } else {
305                         break;
306                     }
307                 case Token.FGT:
308                     if (isInteger) {
309                         return new PositionRange(1, pos - 1);
310                     } else {
311                         break;
312                     }
313                 case Token.FLT:
314                     if (isInteger) {
315                         return new PositionRange(pos + 1, Integer.MAX_VALUE);
316                     } else {
317                         break;
318                     }
319                 case Token.FGE:
320                     if (isInteger) {
321                         return new PositionRange(1, pos);
322                     } else {
323                         break;
324                     }
325             }
326         }
327
328         // optimize generate-id(X) = generate-id(Y) as "X is Y"
329
// This construct is often used in XSLT 1.0 stylesheets.
330
// Only do this if we know the arguments are singletons, because "is" doesn't
331
// do first-value extraction.
332

333         if (NamePart.isGenerateIdFunction(operand0) && NamePart.isGenerateIdFunction(operand1)) {
334             FunctionCall f0 = (FunctionCall)operand0;
335             FunctionCall f1 = (FunctionCall)operand1;
336             if (!Cardinality.allowsMany(f0.argument[0].getCardinality()) &&
337                     !Cardinality.allowsMany(f1.argument[0].getCardinality()) &&
338                     (operator == Token.FEQ) ) {
339                 IdentityComparison id =
340                         new IdentityComparison (
341                                 f0.argument[0],
342                                 Token.IS,
343                                 f1.argument[0] );
344                 id.setGenerateIdEmulation(true);
345                 return id.simplify(env).typeCheck(env, contextItemType).optimize(opt, env, contextItemType);
346             }
347         }
348
349         // evaluate the expression now if both arguments are constant
350

351         if ((operand0 instanceof Value) && (operand1 instanceof Value)) {
352             return (AtomicValue)evaluateItem(null);
353         }
354
355         return this;
356     }
357
358
359     /**
360     * Test whether an expression is constant zero
361     */

362
363     private static boolean isZero(Expression exp) {
364         try {
365             if (!(exp instanceof AtomicValue)) return false;
366             if (exp instanceof IntegerValue) {
367                 return ((IntegerValue)exp).longValue()==0;
368             }
369             if (exp instanceof BigIntegerValue) {
370                 return ((BigIntegerValue)exp).compareTo(BigIntegerValue.ZERO) == 0;
371             }
372
373             Value val = ((AtomicValue)exp).convert(Type.INTEGER, null);
374             return isZero(val);
375         } catch (XPathException err) {
376             return false;
377         }
378     }
379
380     /**
381     * Evaluate the expression in a boolean context
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         try {
388             AtomicValue v1 = ((AtomicValue)operand0.evaluateItem(context));
389             if (v1==null) return false;
390             if (v1 instanceof UntypedAtomicValue) {
391                 v1 = v1.convert(Type.STRING, context);
392             }
393             AtomicValue v2 = ((AtomicValue)operand1.evaluateItem(context));
394             if (v2==null) return false;
395             if (v2 instanceof UntypedAtomicValue) {
396                 v2 = v2.convert(Type.STRING, context);
397             }
398             return compare(v1, operator, v2, comparer);
399         } catch (DynamicError e) {
400             // re-throw the exception with location information added
401
if (e.getXPathContext() == null) {
402                 e.setXPathContext(context);
403             }
404             if (e.getLocator() == null) {
405                 e.setLocator(this);
406             }
407             throw e;
408         }
409     }
410
411     /**
412     * Compare two atomic values, using a specified operator and collation
413      * @param v1 the first operand
414      * @param op the operator, as defined by constants such as {@link Token#FEQ} or
415      * {@link Token#FLT}
416      * @param v2 the second operand
417      * @param collator the Collator to be used when comparing strings
418     * @throws net.sf.saxon.trans.DynamicError if the values are not comparable
419     */

420
421     static boolean compare(AtomicValue v1, int op, AtomicValue v2,
422                                      AtomicComparer collator)
423             throws DynamicError {
424         if (v1 instanceof NumericValue && ((NumericValue)v1).isNaN()) {
425             return false;
426         }
427         if (v2 instanceof NumericValue && ((NumericValue)v2).isNaN()) {
428             return false;
429         }
430         try {
431             switch (op) {
432                 case Token.FEQ:
433                     return collator.comparesEqual(v1, v2);
434                 case Token.FNE:
435                     return !collator.comparesEqual(v1, v2);
436                 case Token.FGT:
437                     return collator.compare(v1, v2) > 0;
438                 case Token.FLT:
439                     return collator.compare(v1, v2) < 0;
440                 case Token.FGE:
441                     return collator.compare(v1, v2) >= 0;
442                 case Token.FLE:
443                     return collator.compare(v1, v2) <= 0;
444                 default:
445                     throw new UnsupportedOperationException JavaDoc("Unknown operator " + op);
446             }
447         } catch (ClassCastException JavaDoc err) {
448             DynamicError e2 = new DynamicError (
449                 "Cannot compare " + v1.getItemType() + " to " + v2.getItemType());
450             e2.setErrorCode("XPTY0004");
451             e2.setIsTypeError(true);
452             throw e2;
453         }
454     }
455
456     /**
457     * Evaluate the expression in a given context
458     * @param context the given context for evaluation
459     * @return a BooleanValue representing the result of the numeric comparison of the two operands,
460      * or null representing the empty sequence
461     */

462
463     public Item evaluateItem(XPathContext context) throws XPathException {
464        try {
465             AtomicValue v1 = (AtomicValue)operand0.evaluateItem(context);
466             if (v1==null) return null;
467             if (v1 instanceof UntypedAtomicValue) {
468                 v1 = v1.convert(Type.STRING, context);
469             }
470             AtomicValue v2 = (AtomicValue)operand1.evaluateItem(context);
471             if (v2==null) return null;
472             if (v2 instanceof UntypedAtomicValue) {
473                 v2 = v2.convert(Type.STRING, context);
474             }
475             return BooleanValue.get(compare(v1, operator, v2, comparer));
476         } catch (DynamicError e) {
477             // re-throw the exception with location information added
478
if (e.getXPathContext() == null) {
479                 e.setXPathContext(context);
480             }
481             if (e.getLocator() == null) {
482                 e.setLocator(this);
483             }
484             throw e;
485         }
486     }
487
488
489     /**
490     * Determine the data type of the expression
491     * @return Type.BOOLEAN
492     */

493
494     public ItemType getItemType() {
495         return Type.BOOLEAN_TYPE;
496     }
497
498 }
499
500 //
501
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
502
// you may not use this file except in compliance with the License. You may obtain a copy of the
503
// License at http://www.mozilla.org/MPL/
504
//
505
// Software distributed under the License is distributed on an "AS IS" basis,
506
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
507
// See the License for the specific language governing rights and limitations under the License.
508
//
509
// The Original Code is: all this file.
510
//
511
// The Initial Developer of the Original Code is Michael H. Kay.
512
//
513
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
514
//
515
// Contributor(s): none.
516
//
517
Popular Tags