/*
* Copyright 2013 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.Joiner;
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.base.internal.Identifier;
import com.google.template.soy.data.SanitizedContent.ContentKind;
import com.google.template.soy.data.internalutils.NodeContentKinds;
import com.google.template.soy.error.ErrorReporter;
import com.google.template.soy.error.SoyErrorKind;
import com.google.template.soy.soytree.TemplateNode.SoyFileHeaderInfo;
import com.google.template.soy.soytree.defn.TemplateParam;
import java.util.List;
import javax.annotation.Nullable;
/**
* Builder for TemplateBasicNode.
*
* <p>Important: Do not use outside of Soy code (treat as superpackage-private).
*
*/
public class TemplateBasicNodeBuilder extends TemplateNodeBuilder {
private static final SoyErrorKind PRIVATE_AND_VISIBILITY =
SoyErrorKind.of("Cannot specify both private=\"true\" and visibility=\"{0}\".");
/** @param soyFileHeaderInfo Info from the containing Soy file's header declarations. */
public TemplateBasicNodeBuilder(
SoyFileHeaderInfo soyFileHeaderInfo, ErrorReporter errorReporter) {
super(soyFileHeaderInfo, errorReporter);
}
@Override
public TemplateBasicNodeBuilder setId(int id) {
return (TemplateBasicNodeBuilder) super.setId(id);
}
@Override
public TemplateBasicNodeBuilder setSourceLocation(SourceLocation location) {
return (TemplateBasicNodeBuilder) super.setSourceLocation(location);
}
@Override
public TemplateNodeBuilder setCommandValues(
Identifier templateName, List<CommandTagAttribute> attrs) {
this.cmdText = templateName.identifier() + " " + Joiner.on(' ').join(attrs);
AutoescapeMode autoescapeMode = soyFileHeaderInfo.defaultAutoescapeMode;
ContentKind kind = null;
SourceLocation kindLocation = null;
for (CommandTagAttribute attribute : attrs) {
Identifier name = attribute.getName();
switch (name.identifier()) {
case "private":
if (attribute.valueAsBoolean(errorReporter, false)) {
if (visibility != null) {
errorReporter.report(
attribute.getName().location(),
PRIVATE_AND_VISIBILITY,
visibility.getAttributeValue());
}
// See go/soy-visibility for why this is considered "legacy private".
visibility = Visibility.LEGACY_PRIVATE;
}
break;
case "visibility":
if (visibility != null) {
errorReporter.report(
attribute.getName().location(), PRIVATE_AND_VISIBILITY, attribute.getValue());
}
visibility = attribute.valueAsVisibility(errorReporter);
break;
case "autoescape":
autoescapeMode = attribute.valueAsAutoescapeMode(errorReporter);
break;
case "kind":
kind = attribute.valueAsContentKind(errorReporter);
kindLocation = attribute.getValueLocation();
break;
case "requirecss":
setRequiredCssNamespaces(attribute.valueAsRequireCss(errorReporter));
break;
case "cssbase":
setCssBaseNamespace(attribute.valueAsCssBase(errorReporter));
break;
case "deprecatedV1":
markDeprecatedV1(attribute.valueAsBoolean(errorReporter, false));
break;
case "stricthtml":
strictHtmlMode = attribute.valueAsStrictHtmlMode(errorReporter);
break;
default:
errorReporter.report(
name.location(),
CommandTagAttribute.UNSUPPORTED_ATTRIBUTE_KEY,
name.identifier(),
ImmutableList.of(
"private",
"visibility",
"autoescape",
"kind",
"requirecss",
"cssbase",
"deprecatedV1",
"stricthtml"));
}
}
if (visibility == null) {
visibility = Visibility.PUBLIC;
}
setAutoescapeInfo(autoescapeMode, kind, kindLocation);
setTemplateNames(
soyFileHeaderInfo.namespace + templateName.identifier(),
templateName.location(),
templateName.identifier());
this.templateNameForUserMsgs = getTemplateName();
return this;
}
/**
* Alternative to {@code setCmdText()} that sets command text info directly as opposed to having
* it parsed from the command text string. The cmdText field will be set to a canonical string
* generated from the given info.
*
* @param templateName This template's name.
* @param partialTemplateName This template's partial name. Only applicable for V2; null for V1.
* @param visibility Visibility of this template.
* @param autoescapeMode The mode of autoescaping for this template.
* @param contentKind Strict mode context. Nonnull iff autoescapeMode is strict.
* @param requiredCssNamespaces CSS namespaces required to render the template.
* @return This builder.
*/
public TemplateBasicNodeBuilder setCmdTextInfo(
String templateName,
@Nullable String partialTemplateName,
Visibility visibility,
AutoescapeMode autoescapeMode,
ContentKind contentKind,
ImmutableList<String> requiredCssNamespaces) {
Preconditions.checkState(this.sourceLocation != null);
Preconditions.checkState(this.cmdText == null);
Preconditions.checkArgument(BaseUtils.isDottedIdentifier(templateName));
Preconditions.checkArgument(
partialTemplateName == null || BaseUtils.isIdentifierWithLeadingDot(partialTemplateName));
Preconditions.checkArgument((contentKind != null) == (autoescapeMode == AutoescapeMode.STRICT));
setTemplateNames(templateName, sourceLocation, partialTemplateName);
this.templateNameForUserMsgs = templateName;
this.visibility = visibility;
setAutoescapeInfo(autoescapeMode, contentKind, sourceLocation);
setRequiredCssNamespaces(requiredCssNamespaces);
StringBuilder cmdTextBuilder = new StringBuilder();
cmdTextBuilder.append((partialTemplateName != null) ? partialTemplateName : templateName);
cmdTextBuilder.append(" autoescape=\"").append(autoescapeMode.getAttributeValue()).append('"');
if (contentKind != null) {
cmdTextBuilder
.append(" kind=\"")
.append(NodeContentKinds.toAttributeValue(contentKind))
.append('"');
}
if (visibility == Visibility.LEGACY_PRIVATE) {
// TODO(brndn): generate code for other visibility levels. b/15190131
cmdTextBuilder.append(" private=\"true\"");
}
if (!requiredCssNamespaces.isEmpty()) {
cmdTextBuilder
.append(" requirecss=\"")
.append(Joiner.on(", ").join(requiredCssNamespaces))
.append("\"");
}
this.cmdText = cmdTextBuilder.toString();
return this;
}
@Override
public TemplateBasicNodeBuilder setSoyDoc(String soyDoc, SourceLocation soyDocLocation) {
return (TemplateBasicNodeBuilder) super.setSoyDoc(soyDoc, soyDocLocation);
}
@Override
public TemplateBasicNodeBuilder addParams(Iterable<? extends TemplateParam> allParams) {
return (TemplateBasicNodeBuilder) super.addParams(allParams);
}
@Override
public TemplateBasicNode build() {
Preconditions.checkState(id != null && cmdText != null);
return new TemplateBasicNode(this, soyFileHeaderInfo, visibility, params);
}
}