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    }