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.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   * Base implementation of Expression for the operations ">", ">=", "<", "<=".
31   *
32   * @since JXPath 1.3
33   */
34  public abstract class CoreOperationRelationalExpression extends CoreOperation {
35  
36      /**
37       * Constructs a new CoreOperationRelationalExpression.
38       *
39       * @param args arguments
40       */
41      protected CoreOperationRelationalExpression(final Expression[] args) {
42          super(args);
43      }
44  
45      /**
46       * Compare left to right.
47       *
48       * @param left  left operand
49       * @param right right operand
50       * @return operation success/failure
51       */
52      private boolean compute(Object left, Object right) {
53          left = reduce(left);
54          right = reduce(right);
55          if (left instanceof InitialContext) {
56              ((InitialContext) left).reset();
57          }
58          if (right instanceof InitialContext) {
59              ((InitialContext) right).reset();
60          }
61          if (left instanceof Iterator && right instanceof Iterator) {
62              return findMatch((Iterator) left, (Iterator) right);
63          }
64          if (left instanceof Iterator) {
65              return containsMatch((Iterator) left, right);
66          }
67          if (right instanceof Iterator) {
68              return containsMatch(left, (Iterator) right);
69          }
70          final double ld = InfoSetUtil.doubleValue(left);
71          if (Double.isNaN(ld)) {
72              return false;
73          }
74          final double rd = InfoSetUtil.doubleValue(right);
75          if (Double.isNaN(rd)) {
76              return false;
77          }
78          return evaluateCompare(ld == rd ? 0 : ld < rd ? -1 : 1);
79      }
80  
81      @Override
82      public final Object computeValue(final EvalContext context) {
83          return compute(args[0].compute(context), args[1].compute(context)) ? Boolean.TRUE : Boolean.FALSE;
84      }
85  
86      /**
87       * Tests whether any element returned from an Iterator matches a given value.
88       *
89       * @param it    Iterator
90       * @param value to look for
91       * @return whether a match was found
92       */
93      private boolean containsMatch(final Iterator it, final Object value) {
94          while (it.hasNext()) {
95              final Object element = it.next();
96              if (compute(element, value)) {
97                  return true;
98              }
99          }
100         return false;
101     }
102 
103     /**
104      * Tests whether any element returned from an Iterator matches a given value.
105      *
106      * @param it    Iterator
107      * @param value to look for
108      * @return whether a match was found
109      */
110     private boolean containsMatch(final Object value, final Iterator it) {
111         while (it.hasNext()) {
112             final Object element = it.next();
113             if (compute(value, element)) {
114                 return true;
115             }
116         }
117         return false;
118     }
119 
120     /**
121      * Template method for subclasses to evaluate the result of a comparison.
122      *
123      * @param compare result of comparison to evaluate
124      * @return ultimate operation success/failure
125      */
126     protected abstract boolean evaluateCompare(int compare);
127 
128     /**
129      * Tests whether there is an intersection between two Iterators.
130      *
131      * @param lit left Iterator
132      * @param rit right Iterator
133      * @return whether a match was found
134      */
135     private boolean findMatch(final Iterator lit, final Iterator rit) {
136         final HashSet left = new HashSet();
137         while (lit.hasNext()) {
138             left.add(lit.next());
139         }
140         while (rit.hasNext()) {
141             if (containsMatch(left.iterator(), rit.next())) {
142                 return true;
143             }
144         }
145         return false;
146     }
147 
148     @Override
149     protected final int getPrecedence() {
150         return RELATIONAL_EXPR_PRECEDENCE;
151     }
152 
153     @Override
154     protected final boolean isSymmetric() {
155         return false;
156     }
157 
158     /**
159      * Reduce an operand for comparison.
160      *
161      * @param o Object to reduce
162      * @return reduced operand
163      */
164     private Object reduce(Object o) {
165         if (o instanceof SelfContext) {
166             o = ((EvalContext) o).getSingleNodePointer();
167         }
168         if (o instanceof Collection) {
169             o = ((Collection) o).iterator();
170         }
171         return o;
172     }
173 }