    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.ri.EvalContext;
    024    import org.apache.commons.jxpath.ri.InfoSetUtil;
    025    import org.apache.commons.jxpath.ri.axes.InitialContext;
    026    import org.apache.commons.jxpath.ri.axes.SelfContext;
    027
    028    /**
    029     * Base implementation of Expression for the operations "&gt;", "&gt;=", "&lt;", "&lt;=".
    030     * @since JXPath 1.3
    031     *
    032     * @author Matt Benson
    033     * @version $Revision: 652845 $ $Date: 2008-05-02 12:46:46 -0500 (Fri, 02 May 2008) $
    034     */
    035    public abstract class CoreOperationRelationalExpression extends CoreOperation {
    036
    037        /**
    038         * Create a new CoreOperationRelationalExpression.
    039         * @param args arguments
    040         */
    041        protected CoreOperationRelationalExpression(Expression[] args) {
    042            super(args);
    043        }
    044
    045        public final Object computeValue(EvalContext context) {
    046            return compute(args[0].compute(context), args[1].compute(context))
    047                    ? Boolean.TRUE : Boolean.FALSE;
    048        }
    049
    050        protected final int getPrecedence() {
    051            return RELATIONAL_EXPR_PRECEDENCE;
    052        }
    053
    054        protected final boolean isSymmetric() {
    055            return false;
    056        }
    057
    058        /**
    059         * Template method for subclasses to evaluate the result of a comparison.
    060         * @param compare result of comparison to evaluate
    061         * @return ultimate operation success/failure
    062         */
    063        protected abstract boolean evaluateCompare(int compare);
    064
    065        /**
    066         * Compare left to right.
    067         * @param left left operand
    068         * @param right right operand
    069         * @return operation success/failure
    070         */
    071        private boolean compute(Object left, Object right) {
    072            left = reduce(left);
    073            right = reduce(right);
    074
    075            if (left instanceof InitialContext) {
    076                ((InitialContext) left).reset();
    077            }
    078            if (right instanceof InitialContext) {
    079                ((InitialContext) right).reset();
    080            }
    081            if (left instanceof Iterator && right instanceof Iterator) {
    082                return findMatch((Iterator) left, (Iterator) right);
    083            }
    084            if (left instanceof Iterator) {
    085                return containsMatch((Iterator) left, right);
    086            }
    087            if (right instanceof Iterator) {
    088                return containsMatch((Iterator) right, left);
    089            }
    090            double ld = InfoSetUtil.doubleValue(left);
    091            if (Double.isNaN(ld)) {
    092                return false;
    093            }
    094            double rd = InfoSetUtil.doubleValue(right);
    095            if (Double.isNaN(rd)) {
    096                return false;
    097            }
    098            return evaluateCompare(ld == rd ? 0 : ld < rd ? -1 : 1);
    099        }
    100
    101        /**
    102         * Reduce an operand for comparison.
    103         * @param o Object to reduce
    104         * @return reduced operand
    105         */
    106        private Object reduce(Object o) {
    107            if (o instanceof SelfContext) {
    108                o = ((EvalContext) o).getSingleNodePointer();
    109            }
    110            if (o instanceof Collection) {
    111                o = ((Collection) o).iterator();
    112            }
    113            return o;
    114        }
    115
    116        /**
    117         * Learn whether any element returned from an Iterator matches a given value.
    118         * @param it Iterator
    119         * @param value to look for
    120         * @return whether a match was found
    121         */
    122        private boolean containsMatch(Iterator it, Object value) {
    123            while (it.hasNext()) {
    124                Object element = it.next();
    125                if (compute(element, value)) {
    126                    return true;
    127                }
    128            }
    129            return false;
    130        }
    131
    132        /**
    133         * Learn whether there is an intersection between two Iterators.
    134         * @param lit left Iterator
    135         * @param rit right Iterator
    136         * @return whether a match was found
    137         */
    138        private boolean findMatch(Iterator lit, Iterator rit) {
    139            HashSet left = new HashSet();
    140            while (lit.hasNext()) {
    141                left.add(lit.next());
    142            }
    143            while (rit.hasNext()) {
    144                if (containsMatch(left.iterator(), rit.next())) {
    145                    return true;
    146                }
    147            }
    148            return false;
    149        }
    150
    151    }