/* * 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.soytree; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.template.soy.base.SourceLocation; import com.google.template.soy.base.internal.BaseUtils; import com.google.template.soy.basetree.CopyState; import com.google.template.soy.exprparse.ExpressionParser; import com.google.template.soy.exprparse.SoyParsingContext; import com.google.template.soy.exprtree.ExprRootNode; import com.google.template.soy.soytree.SoyNode.ExprHolderNode; import com.google.template.soy.soytree.SoyNode.MsgPlaceholderInitialNode; import com.google.template.soy.soytree.SoyNode.SplitLevelTopNode; import com.google.template.soy.soytree.SoyNode.StandaloneNode; import com.google.template.soy.soytree.SoyNode.StatementNode; import javax.annotation.Nullable; /** * Node representing a 'print' statement. * * <p>Important: Do not use outside of Soy code (treat as superpackage-private). * */ public final class PrintNode extends AbstractParentCommandNode<PrintDirectiveNode> implements StandaloneNode, SplitLevelTopNode<PrintDirectiveNode>, StatementNode, ExprHolderNode, MsgPlaceholderInitialNode { /** Fallback base placeholder name. */ public static final String FALLBACK_BASE_PLACEHOLDER_NAME = "XXX"; /** Whether the command 'print' is implicit. */ private final boolean isImplicit; /** The parsed expression. */ private final ExprRootNode expr; /** The user-supplied placeholder name, or null if not supplied or not applicable. */ @Nullable private final String userSuppliedPlaceholderName; @Nullable private HtmlContext htmlContext; private PrintNode( int id, boolean isImplicit, ExprRootNode expr, SourceLocation sourceLocation, @Nullable String userSuppliedPlaceholderName) { super(id, sourceLocation, "print", ""); this.isImplicit = isImplicit; this.expr = Preconditions.checkNotNull(expr); this.userSuppliedPlaceholderName = userSuppliedPlaceholderName; } /** * Copy constructor. * * @param orig The node to copy. */ private PrintNode(PrintNode orig, CopyState copyState) { super(orig, copyState); this.isImplicit = orig.isImplicit; this.expr = orig.expr.copy(copyState); this.userSuppliedPlaceholderName = orig.userSuppliedPlaceholderName; this.htmlContext = orig.htmlContext; } /** * Gets the HTML source context (typically tag, attribute value, HTML PCDATA, or plain text) which * this node emits in. This affects how the node is escaped (for traditional backends) or how it's * passed to incremental DOM APIs. */ public HtmlContext getHtmlContext() { return Preconditions.checkNotNull( htmlContext, "Cannot access HtmlContext before HtmlTransformVisitor"); } public void setHtmlContext(HtmlContext value) { this.htmlContext = value; } @Override public Kind getKind() { return Kind.PRINT_NODE; } /** Returns whether the 'print' command name was implicit. */ public boolean isImplicit() { return isImplicit; } /** Returns the text of the expression to print. */ public String getExprText() { return expr.toSourceString(); } /** Returns the parsed expression, or null if the expression is not in V2 syntax. */ public ExprRootNode getExpr() { return expr; } @Override public String getUserSuppliedPhName() { return userSuppliedPlaceholderName; } @Override public String genBasePhName() { if (userSuppliedPlaceholderName != null) { return BaseUtils.convertToUpperUnderscore(userSuppliedPlaceholderName); } if (this.expr == null) { return FALLBACK_BASE_PLACEHOLDER_NAME; } return MsgSubstUnitBaseVarNameUtils.genNaiveBaseNameForExpr( expr.getRoot(), FALLBACK_BASE_PLACEHOLDER_NAME); } @Override public Object genSamenessKey() { // PrintNodes are considered the same placeholder if they have the same command text. return getCommandText(); } @Override public ImmutableList<ExprRootNode> getExprList() { return ImmutableList.of(expr); } @Override public String getCommandText() { StringBuilder sb = new StringBuilder(); sb.append(expr.toSourceString()); for (PrintDirectiveNode child : getChildren()) { sb.append(' ').append(child.toSourceString()); } if (userSuppliedPlaceholderName != null) { sb.append(" phname=\"").append(userSuppliedPlaceholderName).append('"'); } return sb.toString(); } @Override public String getTagString() { return buildTagStringHelper(false, isImplicit); } @Override public String toSourceString() { return getTagString(); } @SuppressWarnings("unchecked") @Override public ParentSoyNode<StandaloneNode> getParent() { return (ParentSoyNode<StandaloneNode>) super.getParent(); } @Override public PrintNode copy(CopyState copyState) { return new PrintNode(this, copyState); } /** Builder for {@link PrintNode}. */ public static final class Builder { private final int id; private final boolean isImplicit; private final SourceLocation sourceLocation; @Nullable private String exprText; @Nullable private ExprRootNode expr; @Nullable private String userSuppliedPlaceholderName; /** * @param id The node's id. * @param isImplicit Whether the command {@code print} is implicit. * @param sourceLocation The node's source location. */ public Builder(int id, boolean isImplicit, SourceLocation sourceLocation) { this.id = id; this.isImplicit = isImplicit; this.sourceLocation = sourceLocation; } /** * @param exprText The node's expression text. * @return This builder, for chaining. * @throws java.lang.IllegalStateException if {@link #exprText} or {@link #expr} has already * been set. */ public Builder exprText(String exprText) { Preconditions.checkState(this.exprText == null); Preconditions.checkState(this.expr == null); this.exprText = exprText; return this; } /** * @param exprRoot The parsed expression for this print node. * @return This builder, for chaining. * @throws java.lang.IllegalStateException if {@link #exprText} or {@link #expr} has already * been set. */ public Builder exprRoot(ExprRootNode exprRoot) { Preconditions.checkState(this.exprText == null); Preconditions.checkState(this.expr == null); this.expr = exprRoot; return this; } /** * @param userSuppliedPlaceholderName The user-supplied placeholder name. * @return This object, for chaining. */ public Builder userSuppliedPlaceholderName(String userSuppliedPlaceholderName) { this.userSuppliedPlaceholderName = userSuppliedPlaceholderName; return this; } /** * Returns a new {@link PrintNode} built from this builder's state. * * @throws java.lang.IllegalStateException if neither {@link #exprText} nor {@link #expr} have * been set. */ public PrintNode build(SoyParsingContext context) { ExprRootNode exprRoot = getOrParseExpr(context); return new PrintNode(id, isImplicit, exprRoot, sourceLocation, userSuppliedPlaceholderName); } private ExprRootNode getOrParseExpr(SoyParsingContext context) { if (expr != null) { return expr; } Preconditions.checkNotNull(exprText); return new ExprRootNode( new ExpressionParser(exprText, sourceLocation, context).parseExpression()); } } }