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  
18  package org.apache.commons.jxpath.ri.compiler;
19  
20  import java.util.Collection;
21  import java.util.HashSet;
22  import java.util.Iterator;
23  
24  import org.apache.commons.jxpath.Pointer;
25  import org.apache.commons.jxpath.ri.EvalContext;
26  import org.apache.commons.jxpath.ri.InfoSetUtil;
27  import org.apache.commons.jxpath.ri.axes.InitialContext;
28  import org.apache.commons.jxpath.ri.axes.SelfContext;
29  
30  /**
31   * Common superclass for the implementations of Expression for the operations "=" and "!=".
32   */
33  public abstract class CoreOperationCompare extends CoreOperation {
34  
35      private final boolean invert;
36  
37      /**
38       * Constructs a new CoreOperationCompare.
39       *
40       * @param arg1 left operand
41       * @param arg2 right operand
42       */
43      public CoreOperationCompare(final Expression arg1, final Expression arg2) {
44          this(arg1, arg2, false);
45      }
46  
47      /**
48       * Constructs a new CoreOperationCompare.
49       *
50       * @param arg1   left operand
51       * @param arg2   right operand
52       * @param invert whether to invert (not) the comparison
53       */
54      protected CoreOperationCompare(final Expression arg1, final Expression arg2, final boolean invert) {
55          super(new Expression[] { arg1, arg2 });
56          this.invert = invert;
57      }
58  
59      @Override
60      public Object computeValue(final EvalContext context) {
61          return equal(context, args[0], args[1]) ? Boolean.TRUE : Boolean.FALSE;
62      }
63  
64      /**
65       * Tests whether it contains value.
66       *
67       * @param it    Iterator to check
68       * @param value for which to look
69       * @return whether value was found
70       */
71      protected boolean contains(final Iterator it, final Object value) {
72          while (it.hasNext()) {
73              final Object element = it.next();
74              if (equal(element, value)) {
75                  return true;
76              }
77          }
78          return false;
79      }
80  
81      /**
82       * Compares two values.
83       *
84       * @param context evaluation context
85       * @param left    operand
86       * @param right   operand
87       * @return whether left = right in XPath terms
88       */
89      protected boolean equal(final EvalContext context, final Expression left, final Expression right) {
90          Object l = left.compute(context);
91          Object r = right.compute(context);
92          if (l instanceof InitialContext) {
93              ((EvalContext) l).reset();
94          }
95          if (l instanceof SelfContext) {
96              l = ((EvalContext) l).getSingleNodePointer();
97          }
98          if (r instanceof InitialContext) {
99              ((EvalContext) r).reset();
100         }
101         if (r instanceof SelfContext) {
102             r = ((EvalContext) r).getSingleNodePointer();
103         }
104         if (l instanceof Collection) {
105             l = ((Collection) l).iterator();
106         }
107         if (r instanceof Collection) {
108             r = ((Collection) r).iterator();
109         }
110         if (l instanceof Iterator && r instanceof Iterator) {
111             return findMatch((Iterator) l, (Iterator) r);
112         }
113         if (l instanceof Iterator) {
114             return contains((Iterator) l, r);
115         }
116         if (r instanceof Iterator) {
117             return contains((Iterator) r, l);
118         }
119         return equal(l, r);
120     }
121 
122     /**
123      * Tests whether l equals r in XPath terms.
124      *
125      * @param l left operand
126      * @param r right operand
127      * @return whether l = r
128      */
129     protected boolean equal(Object l, Object r) {
130         if (l instanceof Pointer) {
131             l = ((Pointer) l).getValue();
132         }
133         if (r instanceof Pointer) {
134             r = ((Pointer) r).getValue();
135         }
136         boolean result;
137         if (l instanceof Boolean || r instanceof Boolean) {
138             result = l == r || InfoSetUtil.booleanValue(l) == InfoSetUtil.booleanValue(r);
139         } else if (l instanceof Number || r instanceof Number) {
140             // if either side is NaN, no comparison returns true:
141             final double ld = InfoSetUtil.doubleValue(l);
142             if (Double.isNaN(ld)) {
143                 return false;
144             }
145             final double rd = InfoSetUtil.doubleValue(r);
146             if (Double.isNaN(rd)) {
147                 return false;
148             }
149             result = ld == rd;
150         } else {
151             if (l instanceof String || r instanceof String) {
152                 l = InfoSetUtil.stringValue(l);
153                 r = InfoSetUtil.stringValue(r);
154             }
155             result = l == r || l != null && l.equals(r);
156         }
157         return result ^ invert;
158     }
159 
160     /**
161      * Tests whether lit intersects rit.
162      *
163      * @param lit left Iterator
164      * @param rit right Iterator
165      * @return boolean
166      */
167     protected boolean findMatch(final Iterator lit, final Iterator rit) {
168         final HashSet left = new HashSet();
169         while (lit.hasNext()) {
170             left.add(lit.next());
171         }
172         while (rit.hasNext()) {
173             if (contains(left.iterator(), rit.next())) {
174                 return true;
175             }
176         }
177         return false;
178     }
179 
180     @Override
181     protected int getPrecedence() {
182         return COMPARE_PRECEDENCE;
183     }
184 
185     @Override
186     protected boolean isSymmetric() {
187         return true;
188     }
189 }