Java tutorial
/* * Copyright 2016 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.jssrc.dsl; import static com.google.template.soy.jssrc.dsl.OutputContext.STATEMENT; import static com.google.template.soy.jssrc.dsl.OutputContext.TRAILING_EXPRESSION; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.errorprone.annotations.ForOverride; import com.google.errorprone.annotations.Immutable; import com.google.template.soy.base.internal.BaseUtils; import com.google.template.soy.base.internal.UniqueNameGenerator; import com.google.template.soy.exprtree.IntegerNode; import com.google.template.soy.exprtree.Operator; import com.google.template.soy.exprtree.Operator.Associativity; import com.google.template.soy.jssrc.restricted.JsExpr; import java.util.Arrays; import java.util.List; /** * DSL for constructing sequences of JavaScript code. Unlike {@link JsExpr}, it can handle code that * cannot be represented as single expressions. * * <p>Sample usage: <code> * CodeChunk.WithValue fraction = cg.declare( * number(3) * .divideBy(number(4))); * cg * .newChunk(fraction) * .if_( * fraction.doubleEqualsNull(), * id("someFunction").call()) * .endif() * .assign(fraction.times(number(5))) * .build() * .getCode(); * </code> produces <code> * var $$tmp0 = 3 / 4; * if ($$tmp0 == null) { * someFunction(); * } * $$tmp0 = $$tmp0 * 5; * </code> TODO(user): do all JS code generation with this DSL (that is, remove {@link * com.google.template.soy.jssrc.internal.JsCodeBuilder}). */ @Immutable public abstract class CodeChunk { /** Returns a chunk representing the concatenation of this chunk with the other. */ public final CodeChunk concat(CodeChunk other) { return StatementList.of(ImmutableList.of(this, other)); } /** Starts a conditional statement beginning with the given predicate and consequent chunks. */ public static ConditionalBuilder ifStatement(CodeChunk.WithValue predicate, CodeChunk consequent) { return new ConditionalBuilder(predicate, consequent); } /** Starts a conditional expression beginning with the given predicate and consequent chunks. */ public static ConditionalExpressionBuilder ifExpression(CodeChunk.WithValue predicate, CodeChunk.WithValue consequent) { return new ConditionalExpressionBuilder(predicate, consequent); } /** * Creates a new code chunk from the given expression. The expression's precedence is preserved. */ public static WithValue fromExpr(JsExpr expr, Iterable<GoogRequire> requires) { return Leaf.create(expr, requires); } /** * Creates a code chunk representing a JavaScript identifier. * * @throws IllegalArgumentException if {@code id} is not a valid JavaScript identifier. */ public static WithValue id(String id) { CodeChunkUtils.checkId(id); return Leaf.create(id); } /** * Creates a code chunk representing a JavaScript identifier. * * @throws IllegalArgumentException if {@code id} is not a valid JavaScript identifier. */ static WithValue id(String id, Iterable<GoogRequire> requires) { CodeChunkUtils.checkId(id); return Leaf.create(id, requires); } /** * Creates a code chunk representing a JavaScript "dotted identifier" which needs no {@code * goog.require} statements to be added. * * <p>"Dotted identifiers" are really just sequences of dot-access operations off some base * identifier, so this method is just a convenience for <code>id(...).dotAccess(...)...</code>. * It's provided because working with raw dot-separated strings is common. * * <p>Most dotted identifiers should be accessed via the {@link GoogRequire} api. */ public static WithValue dottedIdNoRequire(String dotSeparatedIdentifiers) { return dottedIdWithRequires(dotSeparatedIdentifiers, ImmutableSet.<GoogRequire>of()); } static WithValue dottedIdWithRequires(String dotSeparatedIdentifiers, Iterable<GoogRequire> requires) { List<String> ids = Splitter.on('.').splitToList(dotSeparatedIdentifiers); Preconditions.checkState(!ids.isEmpty(), "not a dot-separated sequence of JavaScript identifiers: %s", dotSeparatedIdentifiers); // Associate the requires with the base id for convenience. It is arguable that they should // be instead associated with the last dot. Or perhaps with the 'whole' expression somehow. // This is a minor philosophical concern but it should be fine in practice because nothing would // ever split apart a code chunk into sub-chunks. So the requires could really go anywhere. CodeChunk.WithValue tip = id(ids.get(0), requires); for (int i = 1; i < ids.size(); ++i) { tip = tip.dotAccess(ids.get(i)); } return tip; } /** * Creates a code chunk representing a JavaScript string literal. * * @param contents The contents of the string literal. The contents will be escaped appropriately * and embedded inside single quotes. */ public static WithValue stringLiteral(String contents) { // Escape non-ASCII characters since browsers are inconsistent in how they interpret utf-8 in // JS source files. String escaped = BaseUtils.escapeToSoyString(contents, true /* shouldEscapeToAscii */); // </script in a JavaScript string will end the current script tag in most browsers. Escape the // forward slash in the string to get around this issue. escaped = escaped.replace("</script", "<\\/script"); return Leaf.create(escaped); } /** Creates a code chunk representing a JavaScript number literal. */ public static WithValue number(long value) { Preconditions.checkArgument(IntegerNode.isInRange(value), "Number is outside JS safe integer range: %s", value); return Leaf.create(Long.toString(value)); } /** Creates a code chunk representing a JavaScript number literal. */ public static WithValue number(double value) { return Leaf.create(Double.toString(value)); } /** Creates a code chunk that assigns value to a preexisting variable with the given name. */ public static CodeChunk assign(String varName, CodeChunk.WithValue rhs) { return Assignment.create(varName, rhs); } /** Creates a code chunk that declares a new variable and assigns a value to it. */ public static Declaration declare(String varName, CodeChunk.WithValue rhs) { return Declaration.create(varName, rhs); } public static Declaration declare(String varName, CodeChunk.WithValue value, String typeExpr, Iterable<GoogRequire> requires) { return Declaration.create(varName, value, typeExpr, requires); } /** Creates a code chunk representing the logical negation {@code !} of the given chunk. */ public static WithValue not(CodeChunk.WithValue arg) { return PrefixUnaryOperation.create(Operator.NOT, arg); } /** Starts a {@code switch} statement dispatching on the given chunk. */ public static SwitchBuilder switch_(CodeChunk.WithValue switchOn) { return new SwitchBuilder(switchOn); } /** * Creates a code chunk representing the {@code new} operator applied to the given constructor. If * you need to call the constructor with arguments, call {@link WithValue#call} on the returned * chunk. */ public static WithValue new_(WithValue ctor) { return New.create(ctor); } /** * Creates a code chunk representing the given Soy operator applied to the given operands. * * <p>Cannot be used for {@link Operator#AND}, {@link Operator#OR}, or {@link * Operator#CONDITIONAL}, as they require access to a {@link CodeChunk.Generator} to generate * temporary variables for short-circuiting. Use {@link CodeChunk.WithValue#and}, {@link * CodeChunk.WithValue#or}, and {@link CodeChunk.Generator#conditionalExpression} instead. */ public static WithValue operation(Operator op, List<WithValue> operands) { Preconditions.checkArgument(operands.size() == op.getNumOperands()); Preconditions.checkArgument(op != Operator.AND && op != Operator.OR && op != Operator.CONDITIONAL); switch (op.getNumOperands()) { case 1: return PrefixUnaryOperation.create(op, operands.get(0)); case 2: return BinaryOperation.create(op, operands.get(0), operands.get(1)); default: throw new AssertionError(); } } /** Creates a code chunk representing a javascript array literal. */ public static WithValue arrayLiteral(Iterable<? extends WithValue> elements) { return ArrayLiteral.create(ImmutableList.copyOf(elements)); } /** Creates a code chunk representing a javascript map literal. */ public static WithValue mapLiteral(Iterable<? extends WithValue> keys, Iterable<? extends WithValue> values) { return MapLiteral.create(ImmutableList.copyOf(keys), ImmutableList.copyOf(values)); } /** Creates a code chunk representing a for loop. */ public static CodeChunk forLoop(String localVar, CodeChunk.WithValue initial, CodeChunk.WithValue limit, CodeChunk.WithValue increment, CodeChunk body) { return For.create(localVar, initial, limit, increment, body); } /** Creates a code chunk representing a for loop, with default values for initial & increment. */ public static CodeChunk forLoop(String localVar, CodeChunk.WithValue limit, CodeChunk body) { return For.create(localVar, number(0), limit, number(1), body); } /** Creates a code chunk that represents a return statement returning the given value. */ public static CodeChunk return_(CodeChunk.WithValue returnValue) { return Return.create(returnValue); } /** * Wraps a {@link JsExpr} that could have incorrect precedence in parens. * * <p>The JsExpr constructor is inherently error-prone. It allows callers to pass a precedence * unrelated to the topmost operator in the text string. While JsExprs created in the Soy codebase * can be audited, JsExprs are also returned by {@link SoyJsSrcFunction functions} and {@link * SoyJsSrcPrintDirective print directives} owned by others. This method should be used to wrap * the results of those plugins. */ public static WithValue dontTrustPrecedenceOf(JsExpr couldHaveWrongPrecedence, Iterable<GoogRequire> requires) { return Group.create(fromExpr(couldHaveWrongPrecedence, requires)); } /** * Creates a code chunk from the given text, treating it as a series of statements rather than an * expression. For use only by {@link * com.google.template.soy.jssrc.internal.GenJsCodeVisitor#visitReturningCodeChunk}. * * <p>TODO(user): remove. */ public static CodeChunk treatRawStringAsStatementLegacyOnly(String rawString, Iterable<GoogRequire> requires) { return LeafStatement.create(rawString.trim(), requires); } /** * Marker class for a chunk of code that represents a value. * * <p>Expressions represent values. Sequences of statements can represent a value (for example, if * the first statement declares a variable and subsequent statements update the variable's state), * but they are not required to. * * <p>Chunks representing values are required in certain contexts (for example, the right-hand * side of an {@link CodeChunk.WithValue#assign assignment}). */ @Immutable public abstract static class WithValue extends CodeChunk { public static final WithValue LITERAL_TRUE = id("true"); public static final WithValue LITERAL_FALSE = id("false"); public static final WithValue LITERAL_NULL = id("null"); public static final WithValue LITERAL_EMPTY_STRING = Leaf.create("''"); public static final WithValue EMPTY_OBJECT_LITERAL = Leaf.create("{}"); WithValue() { /* no subclasses outside this package */ } public final CodeChunk.WithValue plus(CodeChunk.WithValue rhs) { return BinaryOperation.create(Operator.PLUS, this, rhs); } public final CodeChunk.WithValue minus(CodeChunk.WithValue rhs) { return BinaryOperation.create(Operator.MINUS, this, rhs); } public final CodeChunk.WithValue plusEquals(CodeChunk.WithValue rhs) { return BinaryOperation.create("+=", 0, // the precedence of JS assignments (including +=) is lower than any Soy operator Associativity.RIGHT, this, rhs); } public final CodeChunk.WithValue doubleEquals(CodeChunk.WithValue rhs) { return BinaryOperation.create(Operator.EQUAL, this, rhs); } public final CodeChunk.WithValue doubleNotEquals(CodeChunk.WithValue rhs) { return BinaryOperation.create(Operator.NOT_EQUAL, this, rhs); } public final CodeChunk.WithValue tripleEquals(CodeChunk.WithValue rhs) { return BinaryOperation.create("===", Operator.EQUAL.getPrecedence(), Operator.EQUAL.getAssociativity(), this, rhs); } public final CodeChunk.WithValue doubleEqualsNull() { return doubleEquals(LITERAL_NULL); } public final CodeChunk.WithValue times(CodeChunk.WithValue rhs) { return BinaryOperation.create(Operator.TIMES, this, rhs); } public final CodeChunk.WithValue divideBy(CodeChunk.WithValue rhs) { return BinaryOperation.create(Operator.DIVIDE_BY, this, rhs); } /** * Returns a code chunk representing the logical and ({@code &&}) of this chunk with the given * chunk. * * @param codeGenerator Required in case temporary variables need to be allocated for * short-circuiting behavior ({@code rhs} should be evaluated only if the current chunk * evaluates as true). */ public final CodeChunk.WithValue and(CodeChunk.WithValue rhs, CodeChunk.Generator codeGenerator) { return BinaryOperation.and(this, rhs, codeGenerator); } /** * Returns a code chunk representing the logical or ({@code ||}) of this chunk with the given * chunk. * * @param codeGenerator Required in case temporary variables need to be allocated for * short-circuiting behavior ({@code rhs} should be evaluated only if the current chunk * evaluates as false). */ public final CodeChunk.WithValue or(CodeChunk.WithValue rhs, CodeChunk.Generator codeGenerator) { return BinaryOperation.or(this, rhs, codeGenerator); } public final CodeChunk.WithValue op(Operator op, CodeChunk.WithValue rhs) { return BinaryOperation.operation(op, ImmutableList.of(this, rhs)); } /** Takes in a String identifier for convenience, since that's what most use cases need. */ public final CodeChunk.WithValue dotAccess(String identifier) { return Dot.create(this, id(identifier)); } public final CodeChunk.WithValue bracketAccess(CodeChunk.WithValue arg) { return Bracket.create(this, arg); } public final CodeChunk.WithValue call(CodeChunk.WithValue... args) { return call(Arrays.asList(args)); } public final CodeChunk.WithValue call(Iterable<? extends CodeChunk.WithValue> args) { return Call.create(this, ImmutableList.copyOf(args)); } public final CodeChunk.WithValue instanceof_(CodeChunk.WithValue identifier) { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence // instanceof has the same precedence as LESS_THAN return BinaryOperation.create("instanceof", Operator.LESS_THAN.getPrecedence(), Associativity.LEFT, this, identifier); } public final CodeChunk.WithValue assign(CodeChunk.WithValue rhs) { return BinaryOperation.create("=", 0, // the precedence of JS assignments is lower than any Soy operator Associativity.RIGHT, this, rhs); } /** * Returns a chunk whose output expression is the same as this chunk's, but which includes the * given initial statements. * * <p>This method is designed for interoperability with parts of the JS codegen system that do * not understand code chunks. For example, when applying plugin functions, {@link * com.google.template.soy.jssrc.internal.TranslateExprNodeVisitor#visitFunctionNode} needs to * downgrade the plugin arguments from CodeChunk.WithValues to {@link JsExpr}s for the plugin * API to process. The result (a JsExpr) needs to be upgraded back to a CodeChunk.WithValue that * includes the initial statements from the original arguments. */ public final CodeChunk.WithValue withInitialStatements(Iterable<? extends CodeChunk> initialStatements) { // If there are no new initial statements, return the current chunk. if (Iterables.isEmpty(initialStatements)) { return this; } // Otherwise, return a code chunk that includes all of the dependent code. return Composite.create(ImmutableList.copyOf(initialStatements), this); } /** Convenience method for {@code withInitialStatements(ImmutableList.of(statement))}. */ public final CodeChunk.WithValue withInitialStatement(CodeChunk initialStatement) { return withInitialStatements(ImmutableList.of(initialStatement)); } /** * Returns true if this chunk can be represented as a single expression. This method should be * rarely used, but is needed when interoperating with parts of the codegen system that do not * yet understand CodeChunks (e.g. {@link SoyJsSrcFunction}). */ final boolean isRepresentableAsSingleExpression() { return Iterables.isEmpty(initialStatements()); } /** * If this chunk can be represented as a single expression, returns that expression. If this * chunk cannot be represented as a single expression, returns an expression containing * references to a variable defined by the corresponding {@link #doFormatInitialStatements * initial statements}. * * <p>This method should rarely be used, but is needed when interoperating with parts of the * codegen system that do not yet understand CodeChunks (e.g. {@link SoyJsSrcFunction}). */ public abstract JsExpr singleExprOrName(); /** * If this chunk can be represented as a single expression, writes that single expression to the * buffer. If the chunk cannot be represented as a single expression, writes an expression to * the buffer containing references to a variable defined by the corresponding {@link * #doFormatInitialStatements initial statements}. * * <p>Must only be called by {@link FormattingContext#appendOutputExpression}. */ abstract void doFormatOutputExpr(FormattingContext ctx); /** * Returns the initial statements associated with this value. The statements must be serialized * before this value (for example, they could contain declarations of variables referenced in * this value). * * <p>These are direct dependencies only, not transitive. */ public abstract ImmutableSet<CodeChunk> initialStatements(); } /** * A trivial interface for {@link #collectRequires(RequiresCollector)} that can be used to collect * all required namespaces from a code chunk. */ public interface RequiresCollector { /** Drops all requires. */ final RequiresCollector NULL = new RequiresCollector() { @Override public void add(GoogRequire require) { } }; /** Collects requires into an ImmutableSet that can be accessed via {@link #get} */ final class IntoImmutableSet implements RequiresCollector { private final ImmutableSet.Builder<GoogRequire> builder = ImmutableSet.builder(); @Override public void add(GoogRequire require) { builder.add(require); } public ImmutableSet<GoogRequire> get() { return builder.build(); } } void add(GoogRequire require); } /** Adds all the 'goog.require' identifiers needed by this CodeChunk to the given collection. */ public abstract void collectRequires(RequiresCollector collector); /** * Returns a sequence of JavaScript statements. In the special case that this chunk is * representable as a single expression, returns that expression followed by a semicolon. * * <p>This method is intended to be used at the end of codegen to emit the entire gencode. It * should not be used within the codegen system for intermediate representations. * * <p>Because the returned code is intended to be used at the end of codegen, it does not end * in a newline. */ public final String getCode() { return getCode(0, OutputContext.STATEMENT); } /** * Returns a sequence of JavaScript statements suitable for inserting into JS code * that is not managed by the CodeChunk DSL. The string is guaranteed to end in a newline. * * <p>Callers should use {@link #getCode()} when the CodeChunk DSL is managing the entire * code generation. getCode may drop variable declarations if there is no other code referencing * those variables. * * <p>By contrast, this method is provided for incremental migration to the CodeChunk DSL. * Variable declarations will not be dropped, since there may be gencode not managed by the * CodeChunk DSL that references them. * * TODO(user): remove. * * @param startingIndent The indent level of the foreign code into which this code * will be inserted. This doesn't affect the correctness of the composed code, * only its readability. * */ public final String getStatementsForInsertingIntoForeignCodeAtIndent(int startingIndent) { String code = getCode(startingIndent, STATEMENT); return code.endsWith("\n") ? code : code + "\n"; } /** * Returns a sequence of JavaScript statements. In the special case that this chunk is * representable as a single expression, returns that expression * <em>without</em> a trailing semicolon. (By contrast, {@link #getCode()} does send * the trailing semicolon in such cases.) * * <p>This method is generally not safe, since concatenating statements that do not end * in semicolons can cause arbitrary lexical errors. It's intended for use by unit tests * whose assertions are currently written without trailing semicolons. * TODO: migrate the unit tests and delete this method. */ @VisibleForTesting public final String getExpressionTestOnly() { return getCode(0, TRAILING_EXPRESSION); } /** * Temporary method to ease migration to the CodeChunk DSL. * * <p>Because of the recursive nature of the JS codegen system, it is generally not possible * to convert one codegen method at a time to use the CodeChunk DSL. * However, the business logic inside those methods can be migrated incrementally. * Methods that do not yet use the CodeChunk DSL can "unwrap" inputs using this method * and "wrap" results using {@link CodeChunk#fromExpr(JsExpr)}. This is safe as long as * each CodeChunk generated for production code is * {@link CodeChunk.WithValue#isRepresentableAsSingleExpression}. * * TODO(user): remove. */ public final JsExpr assertExpr() { RequiresCollector.IntoImmutableSet collector = new RequiresCollector.IntoImmutableSet(); JsExpr expr = assertExprAndCollectRequires(collector); ImmutableSet<GoogRequire> requires = collector.get(); if (!requires.isEmpty()) { throw new IllegalStateException("calling assertExpr() would drop requires!: " + requires); } return expr; } /** * Temporary method to ease migration to the CodeChunk DSL. * * <p>Because of the recursive nature of the JS codegen system, it is generally not possible to * convert one codegen method at a time to use the CodeChunk DSL. However, the business logic * inside those methods can be migrated incrementally. Methods that do not yet use the CodeChunk * DSL can "unwrap" inputs using this method and "wrap" results using {@link * CodeChunk#fromExpr(JsExpr)}. This is safe as long as each CodeChunk generated for production * code is {@link CodeChunk.WithValue#isRepresentableAsSingleExpression}. * * <p>TODO(user): remove. */ public final JsExpr assertExprAndCollectRequires(RequiresCollector collector) { WithValue withValue = (WithValue) this; if (!withValue.isRepresentableAsSingleExpression()) { throw new IllegalStateException(String.format("Not an expr:\n%s", this.getCode())); } collectRequires(collector); return withValue.singleExprOrName(); } /** * {@link #doFormatInitialStatements} and {@link CodeChunk.WithValue#doFormatOutputExpr} are the * main methods subclasses should override to control their formatting. Subclasses should only * override this method in the special case that a code chunk needs to control its formatting when * it is the only chunk being serialized. TODO(brndn): only one override, can probably be declared * final. * * @param startingIndent The indent level of the foreign code into which this code will be * inserted. This doesn't affect the correctness of the composed code, only its readability. * @param outputContext The grammatical context into which the output expression generated by this * chunk (if any) will be inserted. * <ul> * <li>{@link OutputContext#STATEMENT}: the output expression will appear as its own * statement. Include a trailing semicolon and newline. * <li>{@link OutputContext#EXPRESSION}: the output expression is being inserted into * another expression. Omit the trailing semicolon and newline. * <li>{@link OutputContext#TRAILING_EXPRESSION}: the output expression is being inserted * into another expression, but it is the last component of the entire unit of code that * is being {@link CodeChunk#getCode() formatted}. There is therefore no need to * serialize the name of variable that holds this expression's value, if any (since * there is no following code that could reference it). * </ul> */ @ForOverride String getCode(int startingIndent, OutputContext outputContext) { FormattingContext initialStatements = new FormattingContext(startingIndent); initialStatements.appendInitialStatements(this); FormattingContext outputExprs = new FormattingContext(startingIndent); if (this instanceof WithValue) { outputExprs.appendOutputExpression((WithValue) this); if (outputContext == STATEMENT) { outputExprs.append(';').endLine(); } } return initialStatements.concat(outputExprs).toString(); } /** * If this chunk can be represented as a single expression, does nothing. If this chunk cannot be * represented as a single expression, writes everything except the final expression to the * buffer. Must only be called by {@link FormattingContext#appendInitialStatements}. */ abstract void doFormatInitialStatements(FormattingContext ctx); CodeChunk() { } /** * Code chunks in a single Soy template emit code into a shared JavaScript lexical scope, so they * must use distinct variable names. This class enforces that. */ public static final class Generator { private final UniqueNameGenerator nameGenerator; private Generator(UniqueNameGenerator nameGenerator) { this.nameGenerator = nameGenerator; } /** Returns an object that can be used to build code chunks. */ public static Generator create(UniqueNameGenerator nameGenerator) { return new Generator(nameGenerator); } private String newVarName() { return nameGenerator.generateName("$tmp"); } /** * Creates a code chunk declaring an automatically-named variable initialized to the given * value. */ public Declaration declare(CodeChunk.WithValue rhs) { return CodeChunk.declare(newVarName(), rhs); } /** * Returns a code chunk representing an if-then-else condition. * * <p>If all the parameters are {@link WithValue#isRepresentableAsSingleExpression representable * as single expressions}, the returned chunk will use the JavaScript ternary syntax ({@code * predicate ? consequent : alternate}). Otherwise, the returned chunk will use JavaScript * conditional statement syntax: <code> * var $tmp = null; * if (predicate) { * $tmp = consequent; * } else { * $tmp = alternate; * } * </code> */ public CodeChunk.WithValue conditionalExpression(CodeChunk.WithValue predicate, CodeChunk.WithValue consequent, CodeChunk.WithValue alternate) { if (predicate.initialStatements().containsAll(consequent.initialStatements()) && predicate.initialStatements().containsAll(alternate.initialStatements())) { return Ternary.create(predicate, consequent, alternate); } return ifExpression(predicate, consequent).else_(alternate).build(this); } } }