public static final class Builder { private static CallDelegateNode error() { return new Builder(-1, SourceLocation.UNKNOWN) .commandText("error.error") .build(ExplodingErrorReporter.get()); // guaranteed to be valid } private final int id; private final SourceLocation sourceLocation; private boolean allowEmptyDefault; private DataAttribute dataAttribute = DataAttribute.none(); private ImmutableList<String> escapingDirectiveNames = ImmutableList.of(); @Nullable private String commandText; @Nullable private String delCalleeName; @Nullable private ExprRootNode delCalleeVariantExpr; @Nullable private String userSuppliedPlaceholderName; public Builder(int id, SourceLocation sourceLocation) { this.id = id; this.sourceLocation = sourceLocation; } public Builder allowEmptyDefault(boolean allowEmptyDefault) { this.allowEmptyDefault = allowEmptyDefault; return this; } public Builder commandText(String commandText) { this.commandText = commandText; return this; } public Builder delCalleeName(String delCalleeName) { this.delCalleeName = delCalleeName; return this; } public Builder delCalleeVariantExpr(ExprRootNode delCalleeVariantExpr) { this.delCalleeVariantExpr = delCalleeVariantExpr; return this; } public Builder escapingDirectiveNames(ImmutableList<String> escapingDirectiveNames) { this.escapingDirectiveNames = escapingDirectiveNames; return this; } public Builder dataAttribute(DataAttribute dataAttribute) { this.dataAttribute = dataAttribute; return this; } public Builder userSuppliedPlaceholderName(String userSuppliedPlaceholderName) { this.userSuppliedPlaceholderName = userSuppliedPlaceholderName; return this; } public CallDelegateNode build(ErrorReporter errorReporter) { Checkpoint checkpoint = errorReporter.checkpoint(); CommandTextInfo commandTextInfo = commandText != null ? parseCommandText(errorReporter) : buildCommandText(); if (errorReporter.errorsSince(checkpoint)) { return error(); } CallDelegateNode callDelegateNode = new CallDelegateNode(id, sourceLocation, commandTextInfo, escapingDirectiveNames); return callDelegateNode; } private CommandTextInfo parseCommandText(ErrorReporter errorReporter) { String commandTextWithoutPhnameAttr = this.commandText; String commandText = commandTextWithoutPhnameAttr + ((userSuppliedPlaceholderName != null) ? " phname=\"" + userSuppliedPlaceholderName + "\"" : ""); // Handle callee name not listed as an attribute. Matcher ncnMatcher = NONATTRIBUTE_CALLEE_NAME.matcher(commandTextWithoutPhnameAttr); String delCalleeName; if (ncnMatcher.find()) { delCalleeName = ncnMatcher.group(1); if (!BaseUtils.isDottedIdentifier(delCalleeName)) { errorReporter.report(sourceLocation, INVALID_DELEGATE_NAME, delCalleeName); } commandTextWithoutPhnameAttr = commandTextWithoutPhnameAttr.substring(ncnMatcher.end()).trim(); } else { delCalleeName = null; errorReporter.report(sourceLocation, MISSING_CALLEE_NAME, commandText); } Map<String, String> attributes = ATTRIBUTES_PARSER.parse(commandTextWithoutPhnameAttr, errorReporter, sourceLocation); String variantExprText = attributes.get("variant"); ExprRootNode delCalleeVariantExpr; if (variantExprText == null) { delCalleeVariantExpr = null; } else { ExprNode expr = new ExpressionParser(variantExprText, sourceLocation, errorReporter).parseExpression(); // If the variant is a fixed string, do a sanity check. if (expr instanceof StringNode) { String fixedVariantStr = ((StringNode) expr).getValue(); if (!BaseUtils.isIdentifier(fixedVariantStr)) { errorReporter.report(sourceLocation, INVALID_VARIANT_EXPRESSION, variantExprText); } } delCalleeVariantExpr = new ExprRootNode(expr); } DataAttribute dataAttrInfo = parseDataAttributeHelper(attributes.get("data"), sourceLocation, errorReporter); String allowemptydefaultAttr = attributes.get("allowemptydefault"); Boolean allowsEmptyDefault = (allowemptydefaultAttr == null) ? null : allowemptydefaultAttr.equals("true"); return new CommandTextInfo( commandText, delCalleeName, delCalleeVariantExpr, allowsEmptyDefault, dataAttrInfo, userSuppliedPlaceholderName); } private CommandTextInfo buildCommandText() { Preconditions.checkArgument(BaseUtils.isDottedIdentifier(delCalleeName)); String commandText = ""; commandText += delCalleeName; if (dataAttribute.isPassingAllData()) { commandText += " data=\"all\""; } else if (dataAttribute.isPassingData()) { assert dataAttribute.dataExpr() != null; // suppress warnings commandText += " data=\"" + dataAttribute.dataExpr().toSourceString() + '"'; } if (userSuppliedPlaceholderName != null) { commandText += " phname=\"" + userSuppliedPlaceholderName + '"'; } return new CommandTextInfo( commandText, delCalleeName, delCalleeVariantExpr, allowEmptyDefault, dataAttribute, userSuppliedPlaceholderName); } }