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 */
017package org.apache.commons.jxpath.ri.compiler;
018
019import java.util.Collection;
020import java.util.HashSet;
021import java.util.Iterator;
022
023import org.apache.commons.jxpath.ri.EvalContext;
024import org.apache.commons.jxpath.ri.InfoSetUtil;
025import org.apache.commons.jxpath.ri.axes.InitialContext;
026import org.apache.commons.jxpath.ri.axes.SelfContext;
027
028/**
029 * Base implementation of Expression for the operations ">", ">=", "<", "<=".
030 * @since JXPath 1.3
031 *
032 * @author Matt Benson
033 * @version $Revision: 1133485 $ $Date: 2011-06-08 13:58:55 -0400 (Wed, 08 Jun 2011) $
034 */
035public 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(left, (Iterator) right);
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 any element returned from an Iterator matches a given value.
134     * @param it Iterator
135     * @param value to look for
136     * @return whether a match was found
137     */
138    private boolean containsMatch(Object value, Iterator it) {
139        while (it.hasNext()) {
140            Object element = it.next();
141            if (compute(value, element)) {
142                return true;
143            }
144        }
145        return false;
146    }
147
148    /**
149     * Learn whether there is an intersection between two Iterators.
150     * @param lit left Iterator
151     * @param rit right Iterator
152     * @return whether a match was found
153     */
154    private boolean findMatch(Iterator lit, Iterator rit) {
155        HashSet left = new HashSet();
156        while (lit.hasNext()) {
157            left.add(lit.next());
158        }
159        while (rit.hasNext()) {
160            if (containsMatch(left.iterator(), rit.next())) {
161                return true;
162            }
163        }
164        return false;
165    }
166
167}