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