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}