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 org.apache.commons.jxpath.ri.EvalContext;
020
021/**
022 * The common subclass for tree elements representing core operations like "+",
023 * "- ", "*" etc.
024 *
025 * @author Dmitri Plotnikov
026 * @version $Revision: 652845 $ $Date: 2008-05-02 19:46:46 +0200 (Fr, 02 Mai 2008) $
027 */
028public abstract class CoreOperation extends Operation {
029
030    /** or precedence */
031    protected static final int OR_PRECEDENCE = 0;
032    /** and precedence */
033    protected static final int AND_PRECEDENCE = 1;
034    /** compare precedence */
035    protected static final int COMPARE_PRECEDENCE = 2;
036    /** relational expression precedence */
037    protected static final int RELATIONAL_EXPR_PRECEDENCE = 3;
038    /** add/subtract precedence */
039    protected static final int ADD_PRECEDENCE = 4;
040    /** multiply/divide/mod precedence */
041    protected static final int MULTIPLY_PRECEDENCE = 5;
042    /** negate precedence */
043    protected static final int NEGATE_PRECEDENCE = 6;
044    /** union precedence */
045    protected static final int UNION_PRECEDENCE = 7;
046
047    /**
048     * Create a new CoreOperation.
049     * @param args Expression[]
050     */
051    public CoreOperation(Expression[] args) {
052        super(args);
053    }
054
055    public Object compute(EvalContext context) {
056        return computeValue(context);
057    }
058
059    public abstract Object computeValue(EvalContext context);
060
061    /**
062     * Returns the XPath symbol for this operation, e.g. "+", "div", etc.
063     * @return String symbol
064     */
065    public abstract String getSymbol();
066
067    /**
068     * Returns true if the operation is not sensitive to the order of arguments,
069     * e.g. "=", "and" etc, and false if it is, e.g. "<=", "div".
070     * @return boolean
071     */
072    protected abstract boolean isSymmetric();
073
074    /**
075     * Computes the precedence of the operation.
076     * @return int precedence
077     */
078    protected abstract int getPrecedence();
079
080    public String toString() {
081        if (args.length == 1) {
082            return getSymbol() + parenthesize(args[0], false);
083        }
084        StringBuffer buffer = new StringBuffer();
085        for (int i = 0; i < args.length; i++) {
086            if (i > 0) {
087                buffer.append(' ');
088                buffer.append(getSymbol());
089                buffer.append(' ');
090            }
091            buffer.append(parenthesize(args[i], i == 0));
092        }
093        return buffer.toString();
094    }
095
096    /**
097     * Wrap an expression in parens if necessary.
098     * @param expression other Expression
099     * @param left whether <code>expression</code> is left of this one.
100     * @return String
101     */
102    private String parenthesize(Expression expression, boolean left) {
103        String s = expression.toString();
104        if (!(expression instanceof CoreOperation)) {
105            return s;
106        }
107        int compared = getPrecedence() - ((CoreOperation) expression).getPrecedence();
108
109        if (compared < 0) {
110            return s;
111        }
112        if (compared == 0 && (isSymmetric() || left)) {
113            return s;
114        }
115        return '(' + s + ')';
116    }
117}