/*
* 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();
}
}
}