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