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 ">", ">=", "<", "<=".
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}