/*
* Copyright 2015 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;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.inject.Guice;
import com.google.inject.Key;
import com.google.template.soy.SoyFileSetParser.ParseResult;
import com.google.template.soy.base.internal.SoyFileKind;
import com.google.template.soy.base.internal.SoyFileSupplier;
import com.google.template.soy.basetree.SyntaxVersion;
import com.google.template.soy.error.ErrorReporter;
import com.google.template.soy.error.ExplodingErrorReporter;
import com.google.template.soy.passes.PassManager;
import com.google.template.soy.shared.AutoEscapingType;
import com.google.template.soy.shared.SharedTestUtils;
import com.google.template.soy.shared.SoyAstCache;
import com.google.template.soy.shared.SoyGeneralOptions;
import com.google.template.soy.shared.internal.SharedModule;
import com.google.template.soy.shared.restricted.SoyFunction;
import com.google.template.soy.types.SoyTypeRegistry;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nullable;
/**
* Fluent builder for configuring {@link com.google.template.soy.SoyFileSetParser}s in tests.
*
* @author brndn@google.com (Brendan Linn)
*/
public final class SoyFileSetParserBuilder {
private final ImmutableMap<String, SoyFileSupplier> soyFileSuppliers;
private SoyTypeRegistry typeRegistry = new SoyTypeRegistry();
private SyntaxVersion declaredSyntaxVersion = SyntaxVersion.V2_0;
@Nullable private SoyAstCache astCache = null;
private ErrorReporter errorReporter = ExplodingErrorReporter.get(); // See #parse for discussion.
private boolean allowUnboundGlobals;
private ImmutableMap<String, ? extends SoyFunction> soyFunctionMap =
Guice.createInjector(new SharedModule())
.getInstance(new Key<ImmutableMap<String, ? extends SoyFunction>>() {});
private SoyGeneralOptions options = new SoyGeneralOptions();
/**
* Returns a builder that gets its Soy inputs from the given strings, treating each string as the
* full contents of a Soy file.
*/
public static SoyFileSetParserBuilder forFileContents(String... fileContents) {
return new SoyFileSetParserBuilder(fileContents);
}
/** Returns a builder that gets its Soy inputs from the given {@link SoyFileSupplier}s. */
public static SoyFileSetParserBuilder forSuppliers(SoyFileSupplier... suppliers) {
return new SoyFileSetParserBuilder(ImmutableList.copyOf(Arrays.asList(suppliers)));
}
/**
* Returns a builder that gets its Soy inputs from the given strings, treating each string as the
* contents of a Soy template.
*/
public static SoyFileSetParserBuilder forTemplateContents(String... templateContents) {
return forTemplateContents(AutoEscapingType.DEPRECATED_NONCONTEXTUAL, templateContents);
}
/**
* Returns a builder that gets its Soy inputs from the given strings, treating each string as the
* contents of a Soy template, and using the given {@link AutoEscapingType}.
*/
public static SoyFileSetParserBuilder forTemplateContents(
AutoEscapingType autoEscapingType, String... templateContents) {
String[] fileContents = new String[templateContents.length];
for (int i = 0; i < fileContents.length; ++i) {
fileContents[i] =
SharedTestUtils.buildTestSoyFileContent(
autoEscapingType, null /* soyDocParamNames */, templateContents[i]);
}
return new SoyFileSetParserBuilder(fileContents);
}
private SoyFileSetParserBuilder(String... soyCode) {
this(ImmutableList.copyOf(buildTestSoyFileSuppliers(soyCode)));
}
private SoyFileSetParserBuilder(ImmutableList<SoyFileSupplier> suppliers) {
ImmutableMap.Builder<String, SoyFileSupplier> builder = ImmutableMap.builder();
for (SoyFileSupplier supplier : suppliers) {
builder.put(supplier.getFilePath(), supplier);
}
this.soyFileSuppliers = builder.build();
}
/** Sets the parser's declared syntax version. Returns this object, for chaining. */
public SoyFileSetParserBuilder declaredSyntaxVersion(SyntaxVersion version) {
this.declaredSyntaxVersion = version;
return this;
}
public SoyFileSetParserBuilder errorReporter(ErrorReporter errorReporter) {
this.errorReporter = errorReporter;
return this;
}
public SoyFileSetParserBuilder addSoyFunction(SoyFunction function) {
this.soyFunctionMap =
ImmutableMap.<String, SoyFunction>builder()
.putAll(soyFunctionMap)
.put(function.getName(), function)
.build();
return this;
}
public SoyFileSetParserBuilder options(SoyGeneralOptions options) {
this.options = checkNotNull(options);
// allow the version in the options to override the the declared default, if there is one.
this.declaredSyntaxVersion = options.getDeclaredSyntaxVersion(declaredSyntaxVersion);
return this;
}
public SoyFileSetParserBuilder typeRegistry(SoyTypeRegistry typeRegistry) {
this.typeRegistry = typeRegistry;
return this;
}
public SoyFileSetParserBuilder allowUnboundGlobals(boolean allowUnboundGlobals) {
this.allowUnboundGlobals = allowUnboundGlobals;
return this;
}
private static List<SoyFileSupplier> buildTestSoyFileSuppliers(String... soyFileContents) {
List<SoyFileSupplier> soyFileSuppliers = Lists.newArrayList();
for (int i = 0; i < soyFileContents.length; i++) {
String soyFileContent = soyFileContents[i];
// Names are now required to be unique in a SoyFileSet. Use one-based indexing.
String filePath = (i == 0) ? "no-path" : ("no-path-" + (i + 1));
soyFileSuppliers.add(
SoyFileSupplier.Factory.create(soyFileContent, SoyFileKind.SRC, filePath));
}
return soyFileSuppliers;
}
/**
* Constructs a parse tree from the builder's state, returning the root of the tree.
*
* <p>Note: since {@link SoyFileSetParserBuilder} can only be used in tests, this method will
* throw an {@link AssertionError} if any error is encountered during parsing. For tests that
* require different behavior (for example, tests that need to inspect the full list of errors
* encountered during compilation), pass a different {@link ErrorReporter} implementation to
* {@link #errorReporter}.
*/
public ParseResult parse() {
PassManager.Builder passManager =
new PassManager.Builder()
.setDeclaredSyntaxVersion(declaredSyntaxVersion)
.setSoyFunctionMap(soyFunctionMap)
.setErrorReporter(errorReporter)
.setTypeRegistry(typeRegistry)
.setGeneralOptions(options);
if (allowUnboundGlobals) {
passManager.allowUnknownGlobals();
}
return new SoyFileSetParser(
typeRegistry, astCache, soyFileSuppliers, passManager.build(), errorReporter)
.parse();
}
}