/* * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.template.soy.exprtree; import com.google.template.soy.base.SourceLocation; import com.google.template.soy.basetree.CopyState; import com.google.template.soy.exprtree.ExprNode.OperatorNode; import com.google.template.soy.exprtree.Operator.Associativity; import com.google.template.soy.exprtree.Operator.Operand; import com.google.template.soy.exprtree.Operator.Spacer; import com.google.template.soy.exprtree.Operator.SyntaxElement; import com.google.template.soy.exprtree.Operator.Token; import java.util.List; /** * Abstract implementation of an OperatorNode. * * <p>Important: Do not use outside of Soy code (treat as superpackage-private). * */ public abstract class AbstractOperatorNode extends AbstractParentExprNode implements OperatorNode { /** The operator. */ private final Operator operator; public AbstractOperatorNode(Operator operator, SourceLocation sourceLocation) { super(sourceLocation); this.operator = operator; } /** * Copy constructor. * * @param orig The node to copy. */ protected AbstractOperatorNode(AbstractOperatorNode orig, CopyState copyState) { super(orig, copyState); this.operator = orig.operator; } @Override public Operator getOperator() { return operator; } @Override public String toSourceString() { boolean isLeftAssociative = operator.getAssociativity() == Associativity.LEFT; StringBuilder sourceSb = new StringBuilder(); List<SyntaxElement> syntax = operator.getSyntax(); for (int i = 0, n = syntax.size(); i < n; ++i) { SyntaxElement syntaxEl = syntax.get(i); if (syntaxEl instanceof Operand) { Operand operand = (Operand) syntaxEl; // If left (right) associative, first (last) operand doesn't need protection if it's an // operator of equal precedence to this one. (Note: Actually, the middle operand of our only // ternary operator doesn't need protection either, but we do it anyway for readability.) if (i == (isLeftAssociative ? 0 : n - 1)) { sourceSb.append(getOperandProtectedForLowerPrec(operand.getIndex())); } else { sourceSb.append(getOperandProtectedForLowerOrEqualPrec(operand.getIndex())); } } else if (syntaxEl instanceof Token) { sourceSb.append(((Token) syntaxEl).getValue()); } else if (syntaxEl instanceof Spacer) { sourceSb.append(' '); } else { throw new AssertionError(); } } return sourceSb.toString(); } /** * Gets the source string for the operand at the given index, possibly protected by surrounding * parentheses if the operand is an operator with lower precedence than this operator. * * @param index The index of the operand to get. * @return The source string for the operand at the given index, possibly protected by surrounding * parentheses if the operand is an operator with lower precedence than this operator. */ private String getOperandProtectedForLowerPrec(int index) { return getOperandProtectedForPrecHelper(index, false); } /** * Gets the source string for the operand at the given index, possibly protected by surrounding * parentheses if the operand is an operator with lower or equal precedence to this operator. * * @param index The index of the operand to get. * @return The source string for the operand at the given index, possibly protected by surrounding * parentheses if the operand is an operator with lower or equal precedence to this operator. */ private String getOperandProtectedForLowerOrEqualPrec(int index) { return getOperandProtectedForPrecHelper(index, true); } /** * Helper for getOperandProtectedForLowerPrec() and getOperandProtectedForLowerOrEqualPrec(). * * @param index The index of the operand to get. * @param shouldProtectEqualPrec Whether to proect the operand if it is an operator with equal * precedence to this operator. * @return The source string for the operand at the given index, possibly protected by surrounding * parentheses. */ private String getOperandProtectedForPrecHelper(int index, boolean shouldProtectEqualPrec) { int thisOpPrec = operator.getPrecedence(); ExprNode child = getChild(index); boolean shouldProtect; if (child instanceof OperatorNode) { int childOpPrec = ((OperatorNode) child).getOperator().getPrecedence(); shouldProtect = shouldProtectEqualPrec ? childOpPrec <= thisOpPrec : childOpPrec < thisOpPrec; } else { shouldProtect = false; } if (shouldProtect) { return "(" + child.toSourceString() + ")"; } else { return child.toSourceString(); } } }