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