001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.jxpath.ri.compiler;
018
019 import java.util.Collection;
020 import java.util.HashSet;
021 import java.util.Iterator;
022
023 import org.apache.commons.jxpath.Pointer;
024 import org.apache.commons.jxpath.ri.EvalContext;
025 import org.apache.commons.jxpath.ri.InfoSetUtil;
026 import org.apache.commons.jxpath.ri.axes.InitialContext;
027 import org.apache.commons.jxpath.ri.axes.SelfContext;
028
029 /**
030 * Common superclass for the implementations of Expression for the operations
031 * "=" and "!=".
032 *
033 * @author Dmitri Plotnikov
034 * @version $Revision: 652845 $ $Date: 2008-05-02 12:46:46 -0500 (Fri, 02 May 2008) $
035 */
036 public abstract class CoreOperationCompare extends CoreOperation {
037 private boolean invert;
038
039 /**
040 * Create a new CoreOperationCompare.
041 * @param arg1 left operand
042 * @param arg2 right operand
043 */
044 public CoreOperationCompare(Expression arg1, Expression arg2) {
045 this(arg1, arg2, false);
046 }
047
048 /**
049 * Create a new CoreOperationCompare.
050 * @param arg1 left operand
051 * @param arg2 right operand
052 * @param invert whether to invert (not) the comparison
053 */
054 protected CoreOperationCompare(Expression arg1, Expression arg2, boolean invert) {
055 super(new Expression[] { arg1, arg2 });
056 this.invert = invert;
057 }
058
059 public Object computeValue(EvalContext context) {
060 return equal(context, args[0], args[1]) ? Boolean.TRUE : Boolean.FALSE;
061 }
062
063 protected int getPrecedence() {
064 return COMPARE_PRECEDENCE;
065 }
066
067 protected boolean isSymmetric() {
068 return true;
069 }
070
071 /**
072 * Compares two values.
073 * @param context evaluation context
074 * @param left operand
075 * @param right operand
076 * @return whether left = right in XPath terms
077 */
078 protected boolean equal(EvalContext context, Expression left,
079 Expression right) {
080 Object l = left.compute(context);
081 Object r = right.compute(context);
082
083 if (l instanceof InitialContext) {
084 ((EvalContext) l).reset();
085 }
086
087 if (l instanceof SelfContext) {
088 l = ((EvalContext) l).getSingleNodePointer();
089 }
090
091 if (r instanceof InitialContext) {
092 ((EvalContext) r).reset();
093 }
094
095 if (r instanceof SelfContext) {
096 r = ((EvalContext) r).getSingleNodePointer();
097 }
098
099 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 }