com.google.template.soy.soytree.TemplateDelegateNodeBuilder.java Source code

Java tutorial

Introduction

Here is the source code for com.google.template.soy.soytree.TemplateDelegateNodeBuilder.java

Source

/*
 * 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.data.SanitizedContent.ContentKind;
import com.google.template.soy.data.internalutils.NodeContentKinds;
import com.google.template.soy.error.ErrorReporter;
import com.google.template.soy.error.SoyError;
import com.google.template.soy.exprparse.ExpressionParser;
import com.google.template.soy.exprtree.ExprNode;
import com.google.template.soy.exprtree.ExprRootNode;
import com.google.template.soy.exprtree.GlobalNode;
import com.google.template.soy.exprtree.StringNode;
import com.google.template.soy.soytree.CommandTextAttributesParser.Attribute;
import com.google.template.soy.soytree.TemplateDelegateNode.DelTemplateKey;
import com.google.template.soy.soytree.TemplateNode.Priority;
import com.google.template.soy.soytree.TemplateNode.SoyFileHeaderInfo;
import com.google.template.soy.types.SoyTypeRegistry;

import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Builder for TemplateDelegateNode.
 *
 * <p>Important: Do not use outside of Soy code (treat as superpackage-private).
 *
 */
public class TemplateDelegateNodeBuilder extends TemplateNodeBuilder {

    private static final SoyError INVALID_DELTEMPLATE_COMMAND_TEXT = SoyError
            .of("Invalid delegate template command text.");
    private static final SoyError INVALID_DELTEMPLATE_NAME = SoyError.of("Invalid delegate template name");
    private static final SoyError INVALID_VARIANT_EXPR = SoyError.of("Invalid variant expression "
            + "(must be a string literal containing an identifier or global expression).");
    private static final SoyError NO_SOY_DOC = SoyError.of("Delegate templates require SoyDoc.");

    /** Pattern for the command text. */
    // 2 capturing groups: del template name, attributes.
    private static final Pattern COMMAND_TEXT_PATTERN = Pattern.compile("([.\\w]+) ( \\s .* | $ )",
            Pattern.COMMENTS | Pattern.DOTALL);

    /** Parser for the attributes in command text. */
    private static final CommandTextAttributesParser ATTRIBUTES_PARSER = new CommandTextAttributesParser(
            "deltemplate", new Attribute("variant", Attribute.ALLOW_ALL_VALUES, null),
            new Attribute("autoescape", AutoescapeMode.getAttributeValues(), null),
            new Attribute("kind", NodeContentKinds.getAttributeValues(), null),
            new Attribute("requirecss", Attribute.ALLOW_ALL_VALUES, null),
            new Attribute("cssbase", Attribute.ALLOW_ALL_VALUES, null));

    /** The delegate template name. */
    private String delTemplateName;

    /** Value of a delegate template variant. */
    private String delTemplateVariant = null;

    /** Expression that will evaluate to the value of a delegate template variant. */
    private ExprRootNode delTemplateVariantExpr = null;

    /** The delegate template key (name and variant). */
    private DelTemplateKey delTemplateKey;

    /** The delegate priority. */
    private Priority delPriority;

    /**
     * @param soyFileHeaderInfo Info from the containing Soy file's header declarations.
     * @param sourceLocation The template's source location.
     */
    public TemplateDelegateNodeBuilder(SoyFileHeaderInfo soyFileHeaderInfo, SourceLocation sourceLocation,
            ErrorReporter errorReporter) {
        super(soyFileHeaderInfo, sourceLocation, errorReporter, null /* typeRegistry */);
    }

    /**
     * @param soyFileHeaderInfo Info from the containing Soy file's header declarations.
     * @param sourceLocation The template's source location.
     */
    public TemplateDelegateNodeBuilder(SoyFileHeaderInfo soyFileHeaderInfo, SourceLocation sourceLocation,
            ErrorReporter errorReporter, SoyTypeRegistry typeRegistry) {
        super(soyFileHeaderInfo, sourceLocation, errorReporter, typeRegistry);
    }

    @Override
    public TemplateDelegateNodeBuilder setId(int id) {
        return (TemplateDelegateNodeBuilder) super.setId(id);
    }

    @Override
    public TemplateDelegateNodeBuilder setCmdText(String cmdText) {
        Preconditions.checkState(this.cmdText == null);
        this.cmdText = cmdText;

        Matcher matcher = COMMAND_TEXT_PATTERN.matcher(cmdText);
        if (!matcher.matches()) {
            errorReporter.report(sourceLocation, INVALID_DELTEMPLATE_COMMAND_TEXT);
            return this; // avoid IllegalStateExceptions in matcher.group(...) below.
        }

        this.delTemplateName = matcher.group(1);
        if (!BaseUtils.isDottedIdentifier(delTemplateName)) {
            errorReporter.report(sourceLocation, INVALID_DELTEMPLATE_NAME);
        }

        Map<String, String> attributes = ATTRIBUTES_PARSER.parse(matcher.group(2).trim(), errorReporter,
                sourceLocation);

        String variantExprText = attributes.get("variant");
        if (variantExprText == null) {
            this.delTemplateVariant = "";
        } else {
            ExprNode variantExpr = new ExpressionParser(variantExprText, sourceLocation, errorReporter)
                    .parseExpression();
            if (variantExpr instanceof StringNode) {
                // A string literal is being used as template variant, so the expression value can
                // immediately be evaluated.
                this.delTemplateVariant = ((StringNode) variantExpr).getValue();
                TemplateDelegateNode.verifyVariantName(delTemplateVariant);
            } else if (variantExpr instanceof GlobalNode) {
                // A global expression was used as template variant. The expression will be stored and later
                // resolved into a value when the global expressions are resolved.
                this.delTemplateVariantExpr = new ExprRootNode(variantExpr);
                this.templateNameForUserMsgs = delTemplateName + ":" + (((GlobalNode) variantExpr).getName());
            } else {
                errorReporter.report(sourceLocation, INVALID_VARIANT_EXPR);
            }
        }

        if (delTemplateVariant != null) {
            // The variant value is already available (i.e. we either have a string literal or no variant
            // was defined). In these cases we can already define a template key.
            this.delTemplateKey = DelTemplateKey.create(delTemplateName, delTemplateVariant);
            this.templateNameForUserMsgs = delTemplateKey.toString();
        }

        this.delPriority = soyFileHeaderInfo.priority;

        setAutoescapeCmdText(attributes);
        setRequireCssCmdText(attributes);
        setCssBaseCmdText(attributes);

        genInternalTemplateNameHelper();

        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 delTemplateName The delegate template name.
     * @param delTemplateVariant The delegate template variant.
     * @param delPriority The delegate priority.
     * @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 TemplateDelegateNodeBuilder setCmdTextInfo(String delTemplateName, String delTemplateVariant,
            Priority delPriority, AutoescapeMode autoescapeMode, ContentKind contentKind,
            ImmutableList<String> requiredCssNamespaces) {

        Preconditions.checkState(this.cmdText == null);
        Preconditions.checkArgument(BaseUtils.isDottedIdentifier(delTemplateName));
        Preconditions.checkArgument(delTemplateVariant.length() == 0 || BaseUtils.isIdentifier(delTemplateVariant));
        Preconditions.checkArgument((contentKind != null) == (autoescapeMode == AutoescapeMode.STRICT));

        this.delTemplateName = delTemplateName;
        this.delTemplateVariant = delTemplateVariant;
        this.delTemplateKey = DelTemplateKey.create(delTemplateName, delTemplateVariant);
        this.templateNameForUserMsgs = delTemplateKey.toString();
        this.delPriority = delPriority;
        setAutoescapeInfo(autoescapeMode, contentKind);
        setRequiredCssNamespaces(requiredCssNamespaces);

        String cmdText = delTemplateName
                + ((delTemplateVariant.length() == 0) ? "" : " variant=\"" + delTemplateVariant + "\"")
                + " autoescape=\"" + autoescapeMode.getAttributeValue() + "\"";
        if (contentKind != null) {
            cmdText += " kind=\"" + NodeContentKinds.toAttributeValue(contentKind) + '"';
        }
        if (!requiredCssNamespaces.isEmpty()) {
            cmdText += " requirecss=\"" + Joiner.on(", ").join(requiredCssNamespaces) + "\"";
        }
        this.cmdText = cmdText;

        genInternalTemplateNameHelper();

        return this;
    }

    /**
     * Private helper for both setCmdText() and setCmdTextInfo() to generate and set the internal-use
     * partial template name and template name.
     */
    private void genInternalTemplateNameHelper() {
        Preconditions.checkState(id != null);

        // Compute a SHA-1 hash value for the delPackageName plus delTemplateKey, and take the first 32
        // bits worth as a hex string. This will be included in the generated internal-use template name
        // to prevent collisions in the case where not all Soy files are compiled at once (not really
        // the intended usage of the Soy compiler, but some projects use it this way). Note that the
        // node id is also included in the generated name, which is already sufficient for guaranteeing
        // unique names in the case where all Soy files are compiled together at once.
        // TODO(lukes): why calculate a hash? just cat the bits together to get a
        // reasonable and unique string?  is this for client obfuscation?
        String delPackageAndDelTemplateStr = (soyFileHeaderInfo.delPackageName == null ? ""
                : soyFileHeaderInfo.delPackageName) + "~" + delTemplateName + "~" + delTemplateVariant;
        String collisionPreventionStr = BaseUtils.computePartialSha1AsHexString(delPackageAndDelTemplateStr, 32);

        // Generate the actual internal-use template name.
        String generatedPartialTemplateName = ".__deltemplate_s" + id + "_" + collisionPreventionStr;
        String generatedTemplateName = soyFileHeaderInfo.namespace + generatedPartialTemplateName;
        setTemplateNames(generatedTemplateName, generatedPartialTemplateName);
    }

    @Override
    public TemplateDelegateNodeBuilder setSoyDoc(String soyDoc) {
        if (soyDoc == null) {
            errorReporter.report(sourceLocation, NO_SOY_DOC);
        }
        return (TemplateDelegateNodeBuilder) super.setSoyDoc(soyDoc);
    }

    @Override
    public TemplateDelegateNodeBuilder setHeaderDecls(DeclInfo... declInfos) {
        return (TemplateDelegateNodeBuilder) super.setHeaderDecls(declInfos);
    }

    @Override
    public TemplateDelegateNode build() {
        Preconditions.checkState(id != null && isSoyDocSet && cmdText != null);

        return new TemplateDelegateNode(this, soyFileHeaderInfo, delTemplateName, delTemplateVariant,
                delTemplateVariantExpr, delTemplateKey, delPriority, params);
    }
}