View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.jxpath.ri.compiler;
18  
19  import java.util.Collection;
20  import java.util.HashSet;
21  import java.util.Iterator;
22  
23  import org.apache.commons.jxpath.Pointer;
24  import org.apache.commons.jxpath.ri.EvalContext;
25  import org.apache.commons.jxpath.ri.InfoSetUtil;
26  import org.apache.commons.jxpath.ri.axes.InitialContext;
27  import org.apache.commons.jxpath.ri.axes.SelfContext;
28  
29  /**
30   * Common superclass for the implementations of Expression for the operations
31   * "=" and "!=".
32   *
33   * @author Dmitri Plotnikov
34   * @version $Revision: 652845 $ $Date: 2008-05-02 19:46:46 +0200 (Fr, 02 Mai 2008) $
35   */
36  public abstract class CoreOperationCompare extends CoreOperation {
37      private boolean invert;
38  
39      /**
40       * Create a new CoreOperationCompare.
41       * @param arg1 left operand
42       * @param arg2 right operand
43       */
44      public CoreOperationCompare(Expression arg1, Expression arg2) {
45          this(arg1, arg2, false);
46      }
47  
48      /**
49       * Create a new CoreOperationCompare.
50       * @param arg1 left operand
51       * @param arg2 right operand
52       * @param invert whether to invert (not) the comparison
53       */
54      protected CoreOperationCompare(Expression arg1, Expression arg2, boolean invert) {
55          super(new Expression[] { arg1, arg2 });
56          this.invert = invert;
57      }
58  
59      public Object computeValue(EvalContext context) {
60          return equal(context, args[0], args[1]) ? Boolean.TRUE : Boolean.FALSE;
61      }
62  
63      protected int getPrecedence() {
64          return COMPARE_PRECEDENCE;
65      }
66  
67      protected boolean isSymmetric() {
68          return true;
69      }
70  
71      /**
72       * Compares two values.
73       * @param context evaluation context
74       * @param left operand
75       * @param right operand
76       * @return whether left = right in XPath terms
77       */
78      protected boolean equal(EvalContext context, Expression left,
79              Expression right) {
80          Object l = left.compute(context);
81          Object r = right.compute(context);
82  
83          if (l instanceof InitialContext) {
84              ((EvalContext) l).reset();
85          }
86  
87          if (l instanceof SelfContext) {
88              l = ((EvalContext) l).getSingleNodePointer();
89          }
90  
91          if (r instanceof InitialContext) {
92              ((EvalContext) r).reset();
93          }
94  
95          if (r instanceof SelfContext) {
96              r = ((EvalContext) r).getSingleNodePointer();
97          }
98  
99          if (l instanceof Collection) {
100             l = ((Collection) l).iterator();
101         }
102 
103         if (r instanceof Collection) {
104             r = ((Collection) r).iterator();
105         }
106 
107         if (l instanceof Iterator && r instanceof Iterator) {
108             return findMatch((Iterator) l, (Iterator) r);
109         }
110         if (l instanceof Iterator) {
111             return contains((Iterator) l, r);
112         }
113         if (r instanceof Iterator) {
114             return contains((Iterator) r, l);
115         }
116         return equal(l, r);
117     }
118 
119     /**
120      * Learn whether it contains value.
121      * @param it Iterator to check
122      * @param value for which to look
123      * @return whether value was found
124      */
125     protected boolean contains(Iterator it, Object value) {
126         while (it.hasNext()) {
127             Object element = it.next();
128             if (equal(element, value)) {
129                 return true;
130             }
131         }
132         return false;
133     }
134 
135     /**
136      * Learn whether lit intersects rit.
137      * @param lit left Iterator
138      * @param rit right Iterator
139      * @return boolean
140      */
141     protected boolean findMatch(Iterator lit, Iterator rit) {
142         HashSet left = new HashSet();
143         while (lit.hasNext()) {
144             left.add(lit.next());
145         }
146         while (rit.hasNext()) {
147             if (contains(left.iterator(), rit.next())) {
148                 return true;
149             }
150         }
151         return false;
152     }
153 
154     /**
155      * Learn whether l equals r in XPath terms.
156      * @param l left operand
157      * @param r right operand
158      * @return whether l = r
159      */
160     protected boolean equal(Object l, Object r) {
161         if (l instanceof Pointer) {
162             l = ((Pointer) l).getValue();
163         }
164 
165         if (r instanceof Pointer) {
166             r = ((Pointer) r).getValue();
167         }
168 
169         boolean result;
170         if (l instanceof Boolean || r instanceof Boolean) {
171             result = l == r || InfoSetUtil.booleanValue(l) == InfoSetUtil.booleanValue(r);
172         }
173         else if (l instanceof Number || r instanceof Number) {
174             //if either side is NaN, no comparison returns true:
175             double ld = InfoSetUtil.doubleValue(l);
176             if (Double.isNaN(ld)) {
177                 return false;
178             }
179             double rd = InfoSetUtil.doubleValue(r);
180             if (Double.isNaN(rd)) {
181                 return false;
182             }
183             result = ld == rd;
184         }
185         else {
186             if (l instanceof String || r instanceof String) {
187                 l = InfoSetUtil.stringValue(l);
188                 r = InfoSetUtil.stringValue(r);
189             }
190             result = l == r || l != null && l.equals(r);
191         }
192         return result ^ invert;
193     }
194 
195 }