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 */
017package org.apache.commons.jxpath.ri.compiler;
018
019import java.util.Collection;
020import java.util.HashSet;
021import java.util.Iterator;
022
023import org.apache.commons.jxpath.Pointer;
024import org.apache.commons.jxpath.ri.EvalContext;
025import org.apache.commons.jxpath.ri.InfoSetUtil;
026import org.apache.commons.jxpath.ri.axes.InitialContext;
027import 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 19:46:46 +0200 (Fr, 02 Mai 2008) $
035 */
036public 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}