    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
    018package org.apache.commons.jxpath.ri.compiler;
    019
    020import java.util.Collection;
    021import java.util.HashSet;
    022import java.util.Iterator;
    023
    024import org.apache.commons.jxpath.Pointer;
    025import org.apache.commons.jxpath.ri.EvalContext;
    026import org.apache.commons.jxpath.ri.InfoSetUtil;
    027import org.apache.commons.jxpath.ri.axes.InitialContext;
    028import org.apache.commons.jxpath.ri.axes.SelfContext;
    029
    030/**
    031 * Common superclass for the implementations of Expression for the operations "=" and "!=".
    032 */
    033public abstract class CoreOperationCompare extends CoreOperation {
    034
    035    private final boolean invert;
    036
    037    /**
    038     * Constructs a new CoreOperationCompare.
    039     *
    040     * @param arg1 left operand
    041     * @param arg2 right operand
    042     */
    043    public CoreOperationCompare(final Expression arg1, final Expression arg2) {
    044        this(arg1, arg2, false);
    045    }
    046
    047    /**
    048     * Constructs a new CoreOperationCompare.
    049     *
    050     * @param arg1   left operand
    051     * @param arg2   right operand
    052     * @param invert whether to invert (not) the comparison
    053     */
    054    protected CoreOperationCompare(final Expression arg1, final Expression arg2, final boolean invert) {
    055        super(new Expression[] { arg1, arg2 });
    056        this.invert = invert;
    057    }
    058
    059    @Override
    060    public Object computeValue(final EvalContext context) {
    061        return equal(context, args[0], args[1]) ? Boolean.TRUE : Boolean.FALSE;
    062    }
    063
    064    /**
    065     * Tests whether it contains value.
    066     *
    067     * @param it    Iterator to check
    068     * @param value for which to look
    069     * @return whether value was found
    070     */
    071    protected boolean contains(final Iterator it, final Object value) {
    072        while (it.hasNext()) {
    073            final Object element = it.next();
    074            if (equal(element, value)) {
    075                return true;
    076            }
    077        }
    078        return false;
    079    }
    080
    081    /**
    082     * Compares two values.
    083     *
    084     * @param context evaluation context
    085     * @param left    operand
    086     * @param right   operand
    087     * @return whether left = right in XPath terms
    088     */
    089    protected boolean equal(final EvalContext context, final Expression left, final Expression right) {
    090        Object l = left.compute(context);
    091        Object r = right.compute(context);
    092        if (l instanceof InitialContext) {
    093            ((EvalContext) l).reset();
    094        }
    095        if (l instanceof SelfContext) {
    096            l = ((EvalContext) l).getSingleNodePointer();
    097        }
    098        if (r instanceof InitialContext) {
    099            ((EvalContext) r).reset();
    100        }
    101        if (r instanceof SelfContext) {
    102            r = ((EvalContext) r).getSingleNodePointer();
    103        }
    104        if (l instanceof Collection) {
    105            l = ((Collection) l).iterator();
    106        }
    107        if (r instanceof Collection) {
    108            r = ((Collection) r).iterator();
    109        }
    110        if (l instanceof Iterator && r instanceof Iterator) {
    111            return findMatch((Iterator) l, (Iterator) r);
    112        }
    113        if (l instanceof Iterator) {
    114            return contains((Iterator) l, r);
    115        }
    116        if (r instanceof Iterator) {
    117            return contains((Iterator) r, l);
    118        }
    119        return equal(l, r);
    120    }
    121
    122    /**
    123     * Tests whether l equals r in XPath terms.
    124     *
    125     * @param l left operand
    126     * @param r right operand
    127     * @return whether l = r
    128     */
    129    protected boolean equal(Object l, Object r) {
    130        if (l instanceof Pointer) {
    131            l = ((Pointer) l).getValue();
    132        }
    133        if (r instanceof Pointer) {
    134            r = ((Pointer) r).getValue();
    135        }
    136        boolean result;
    137        if (l instanceof Boolean || r instanceof Boolean) {
    138            result = l == r || InfoSetUtil.booleanValue(l) == InfoSetUtil.booleanValue(r);
    139        } else if (l instanceof Number || r instanceof Number) {
    140            // if either side is NaN, no comparison returns true:
    141            final double ld = InfoSetUtil.doubleValue(l);
    142            if (Double.isNaN(ld)) {
    143                return false;
    144            }
    145            final double rd = InfoSetUtil.doubleValue(r);
    146            if (Double.isNaN(rd)) {
    147                return false;
    148            }
    149            result = ld == rd;
    150        } else {
    151            if (l instanceof String || r instanceof String) {
    152                l = InfoSetUtil.stringValue(l);
    153                r = InfoSetUtil.stringValue(r);
    154            }
    155            result = l == r || l != null && l.equals(r);
    156        }
    157        return result ^ invert;
    158    }
    159
    160    /**
    161     * Tests whether lit intersects rit.
    162     *
    163     * @param lit left Iterator
    164     * @param rit right Iterator
    165     * @return boolean
    166     */
    167    protected boolean findMatch(final Iterator lit, final Iterator rit) {
    168        final HashSet left = new HashSet();
    169        while (lit.hasNext()) {
    170            left.add(lit.next());
    171        }
    172        while (rit.hasNext()) {
    173            if (contains(left.iterator(), rit.next())) {
    174                return true;
    175            }
    176        }
    177        return false;
    178    }
    179
    180    @Override
    181    protected int getPrecedence() {
    182        return COMPARE_PRECEDENCE;
    183    }
    184
    185    @Override
    186    protected boolean isSymmetric() {
    187        return true;
    188    }
    189}