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