    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    }