summaryrefslogtreecommitdiffstats
path: root/configgen/src/main/java/com/yahoo/config/codegen/BuilderGenerator.java
diff options
context:
space:
mode:
Diffstat (limited to 'configgen/src/main/java/com/yahoo/config/codegen/BuilderGenerator.java')
-rw-r--r--configgen/src/main/java/com/yahoo/config/codegen/BuilderGenerator.java351
1 files changed, 351 insertions, 0 deletions
diff --git a/configgen/src/main/java/com/yahoo/config/codegen/BuilderGenerator.java b/configgen/src/main/java/com/yahoo/config/codegen/BuilderGenerator.java
new file mode 100644
index 00000000000..bf3fc2902a1
--- /dev/null
+++ b/configgen/src/main/java/com/yahoo/config/codegen/BuilderGenerator.java
@@ -0,0 +1,351 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.config.codegen;
+
+import com.yahoo.config.codegen.LeafCNode.FileLeaf;
+import com.yahoo.config.codegen.LeafCNode.PathLeaf;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.yahoo.config.codegen.ConfigGenerator.boxedDataType;
+import static com.yahoo.config.codegen.ConfigGenerator.indentCode;
+import static com.yahoo.config.codegen.ConfigGenerator.nodeClass;
+import static com.yahoo.config.codegen.ConfigGenerator.userDataType;
+import static com.yahoo.config.codegen.JavaClassBuilder.INDENTATION;
+import static com.yahoo.config.codegen.JavaClassBuilder.createUniqueSymbol;
+import static com.yahoo.config.codegen.ReservedWords.INTERNAL_PREFIX;
+import static java.util.Arrays.stream;
+
+/**
+ * @author gjoranv
+ * @author ollivir
+ */
+
+public class BuilderGenerator {
+ public static String getBuilder(InnerCNode node) {
+ return getDeclaration(node) + "\n" + //
+ indentCode(INDENTATION, getUninitializedScalars(node) + "\n\n" + //
+ stream(node.getChildren()).map(BuilderGenerator::getBuilderFieldDefinition).collect(Collectors.joining("\n"))
+ + "\n\n" + //
+ getBuilderConstructors(node, nodeClass(node)) + "\n\n" + //
+ getOverrideMethod(node) + "\n\n" + //
+ getBuilderSetters(node) + "\n" + //
+ getSpecialRootBuilderCode(node))
+ + "}";
+ }
+
+ private static String getDeclaration(InnerCNode node) {
+ String getInterfaces = (node.getParent() == null) ? "implements ConfigInstance.Builder" : "implements ConfigBuilder";
+
+ return "public static class Builder " + getInterfaces + " {";
+ }
+
+ private static String getSpecialRootBuilderCode(InnerCNode node) {
+ return (node.getParent() == null) ? "\n" + getDispatchCode() + "\n" : "";
+ }
+
+ private static String getDispatchCode() {
+ // Use full path to @Override, as users are free to define an inner node called
+ // 'override'. (summarymap.def does)
+ // The generated inner 'Override' class would otherwise be mistaken for the
+ // annotation.
+ return "@java.lang.Override\n" + //
+ "public final boolean dispatchGetConfig(ConfigInstance.Producer producer) {\n" + //
+ " if (producer instanceof Producer) {\n" + //
+ " ((Producer)producer).getConfig(this);\n" + //
+ " return true;\n" + //
+ " }\n" + //
+ " return false;\n" + //
+ "}\n" + //
+ "\n" + //
+ "@java.lang.Override\n" + //
+ "public final String getDefMd5() { return CONFIG_DEF_MD5; }\n" + //
+ "@java.lang.Override\n" + //
+ "public final String getDefName() { return CONFIG_DEF_NAME; }\n" + //
+ "@java.lang.Override\n" + //
+ "public final String getDefNamespace() { return CONFIG_DEF_NAMESPACE; }";
+ }
+
+ private static String getUninitializedScalars(InnerCNode node) {
+ List<String> scalarsWithoutDefault = new ArrayList<>();
+ for (CNode child : node.getChildren()) {
+ if (child instanceof LeafCNode && (!child.isArray && !child.isMap && ((LeafCNode) child).getDefaultValue() == null)) {
+ scalarsWithoutDefault.add("\"" + child.getName() + "\"");
+ }
+ }
+
+ String uninitializedList = (scalarsWithoutDefault.size() > 0)
+ ? "Arrays.asList(\n" + indentCode(INDENTATION, String.join(",\n", scalarsWithoutDefault) + "\n)")
+ : "";
+
+ return "private Set<String> " + INTERNAL_PREFIX + "uninitialized = new HashSet<String>(" + uninitializedList + ");";
+ }
+
+ private static String getBuilderFieldDefinition(CNode node) {
+ if (node.isArray) {
+ return String.format("public List<%s> %s = new ArrayList<>();", builderType(node), node.getName());
+ } else if (node.isMap) {
+ return String.format("public Map<String, %s> %s = new LinkedHashMap<>();", builderType(node), node.getName());
+ } else if (node instanceof InnerCNode) {
+ return String.format("public %s %s = new %s();", builderType(node), node.getName(), builderType(node));
+ } else if (node instanceof LeafCNode) {
+ return String.format("private %s %s = null;", boxedBuilderType((LeafCNode) node), node.getName());
+ } else {
+ throw new IllegalStateException("Cannot produce builder field definition for node"); // Should not happen
+ }
+ }
+
+ private static String getBuilderSetters(CNode node) {
+ List<String> elem = new ArrayList<>();
+ CNode[] children = node.getChildren();
+
+ for (CNode child : children) {
+ if (child instanceof InnerCNode && child.isArray) {
+ elem.add(BuilderSetters.innerArraySetters((InnerCNode) child));
+ } else if (child instanceof InnerCNode && child.isMap) {
+ elem.add(BuilderSetters.innerMapSetters(child));
+ } else if (child instanceof LeafCNode && child.isArray) {
+ elem.add(BuilderSetters.leafArraySetters((LeafCNode) child));
+ } else if (child instanceof LeafCNode && child.isMap) {
+ elem.add(BuilderSetters.leafMapSetters(child));
+ } else if (child instanceof InnerCNode) {
+ elem.add(BuilderSetters.structSetter((InnerCNode) child));
+ } else if (child instanceof LeafCNode) {
+ elem.add(BuilderSetters.scalarSetters((LeafCNode) child));
+ }
+ }
+ return String.join("\n\n", elem);
+ }
+
+ private static class BuilderSetters {
+ private static String structSetter(InnerCNode n) {
+ return "public Builder " + n.getName() + "(" + builderType(n) + " " + INTERNAL_PREFIX + "builder) {\n" + //
+ " " + n.getName() + " = " + INTERNAL_PREFIX + "builder;\n" + //
+ " return this;\n" + //
+ "}";
+ }
+
+ private static String innerArraySetters(InnerCNode n) {
+ return "/**\n" + //
+ " * Add the given builder to this builder's list of " + nodeClass(n) + " builders\n" + //
+ " * @param " + INTERNAL_PREFIX + "builder a builder\n" + //
+ " * @return this builder\n" + //
+ " */\n" + //
+ "public Builder " + n.getName() + "(" + builderType(n) + " " + INTERNAL_PREFIX + "builder) {\n" + //
+ " " + n.getName() + ".add(" + INTERNAL_PREFIX + "builder);\n" + //
+ " return this;\n" + //
+ "}\n" + //
+ "\n" + //
+ "/**\n" + //
+ " * Set the given list as this builder's list of " + nodeClass(n) + " builders\n" + //
+ " * @param __builders a list of builders\n" + //
+ " * @return this builder\n" + //
+ " */\n" + //
+ "public Builder " + n.getName() + "(List<" + builderType(n) + "> __builders) {\n" + //
+ " " + n.getName() + " = __builders;\n" + //
+ " return this;\n" + //
+ "}";
+ }
+
+ private static String publicLeafNodeSetters(LeafCNode n) {
+ return "public Builder " + n.getName() + "(" + builderType(n) + " " + INTERNAL_PREFIX + "value) {\n" + //
+ " " + n.getName() + ".add(" + INTERNAL_PREFIX + "value);\n" + //
+ " return this;\n" + //
+ "}\n" + //
+ "\n" + //
+ "public Builder " + n.getName() + "(Collection<" + builderType(n) + "> " + INTERNAL_PREFIX + "values) {\n" + //
+ " " + n.getName() + ".addAll(" + INTERNAL_PREFIX + "values);\n" + //
+ " return this;\n" + //
+ "}";
+ }
+
+ private static String privateLeafNodeSetter(LeafCNode n) {
+ if ("String".equals(builderType(n)) || "FileReference".equals(builderType(n))) {
+ return "";
+ } else {
+ return "\n\n" + //
+ "private Builder " + n.getName() + "(String " + INTERNAL_PREFIX + "value) {\n" + //
+ " return " + n.getName() + "(" + builderType(n) + ".valueOf(" + INTERNAL_PREFIX + "value));\n" + //
+ "}";
+ }
+ }
+
+ private static String leafArraySetters(LeafCNode n) {
+ return publicLeafNodeSetters(n) + privateLeafNodeSetter(n);
+ }
+
+ private static String innerMapSetters(CNode n) {
+ return "public Builder " + n.getName() + "(String " + INTERNAL_PREFIX + "key, " + builderType(n) + " " + INTERNAL_PREFIX
+ + "value) {\n" + //
+ " " + n.getName() + ".put(" + INTERNAL_PREFIX + "key, " + INTERNAL_PREFIX + "value);\n" + //
+ " return this;\n" + //
+ "}\n" + //
+ "\n" + //
+ "public Builder " + n.getName() + "(Map<String, " + builderType(n) + "> " + INTERNAL_PREFIX + "values) {\n" + //
+ " " + n.getName() + ".putAll(" + INTERNAL_PREFIX + "values);\n" + //
+ " return this;\n" + //
+ "}";
+ }
+
+ private static String privateLeafMapSetter(CNode n) {
+ if ("String".equals(builderType(n)) || "FileReference".equals(builderType(n))) {
+ return "";
+ } else {
+ return "\n\n" + //
+ "private Builder " + n.getName() + "(String " + INTERNAL_PREFIX + "key, String " + INTERNAL_PREFIX + "value) {\n" + //
+ " return " + n.getName() + "(" + INTERNAL_PREFIX + "key, " + builderType(n) + ".valueOf(" + INTERNAL_PREFIX
+ + "value));\n" + //
+ "}";
+ }
+ }
+
+ private static String leafMapSetters(CNode n) {
+ return innerMapSetters(n) + privateLeafMapSetter(n);
+ }
+
+ private static String scalarSetters(LeafCNode n) {
+ String name = n.getName();
+
+ String signalInitialized = (n.getDefaultValue() == null) ? " " + INTERNAL_PREFIX + "uninitialized.remove(\"" + name + "\");\n"
+ : "";
+
+ String bType = builderType(n);
+ String stringSetter = "String".equals(bType) || "FileReference".equals(bType) ? ""
+ : String.format("\nprivate Builder %s(String %svalue) {\n" + //
+ " return %s(%s.valueOf(%svalue));\n" + //
+ "}", name, INTERNAL_PREFIX, name, boxedDataType(n), INTERNAL_PREFIX);
+
+ String getNullGuard = bType.equals(boxedBuilderType(n)) ? String.format(
+ "\nif (%svalue == null) throw new IllegalArgumentException(\"Null value is not allowed.\");", INTERNAL_PREFIX) : "";
+
+ return String.format("public Builder %s(%s %svalue) {%s\n" + //
+ " %s = %svalue;\n" + //
+ "%s", name, bType, INTERNAL_PREFIX, getNullGuard, name, INTERNAL_PREFIX, signalInitialized) + //
+ " return this;" + "\n}\n" + stringSetter;
+ }
+ }
+
+ private static String setBuilderValueFromConfig(CNode child, CNode node) {
+ final String name = child.getName();
+ final boolean isArray = child.isArray;
+ final boolean isMap = child.isMap;
+
+ if (child instanceof FileLeaf && isArray) {
+ return name + "(" + userDataType(child) + ".toValues(config." + name + "()));";
+ } else if (child instanceof FileLeaf && isMap) {
+ return name + "(" + userDataType(child) + ".toValueMap(config." + name + "()));";
+ } else if (child instanceof FileLeaf) {
+ return name + "(config." + name + "().value());";
+ } else if (child instanceof PathLeaf && isArray) {
+ return name + "(" + nodeClass(child) + ".toFileReferences(config." + name + "));";
+ } else if (child instanceof PathLeaf && isMap) {
+ return name + "(" + nodeClass(child) + ".toFileReferenceMap(config." + name + "));";
+ } else if (child instanceof PathLeaf) {
+ return name + "(config." + name + ".getFileReference());";
+ } else if (child instanceof LeafCNode) {
+ return name + "(config." + name + "());";
+ } else if (child instanceof InnerCNode && isArray) {
+ return setInnerArrayBuildersFromConfig((InnerCNode) child, node);
+ } else if (child instanceof InnerCNode && isMap) {
+ return setInnerMapBuildersFromConfig((InnerCNode) child);
+ } else {
+ return name + "(new " + builderType(child) + "(config." + name + "()));";
+ }
+ }
+
+ private static String setInnerArrayBuildersFromConfig(InnerCNode innerArr, CNode node) {
+ final String elemName = createUniqueSymbol(node, innerArr.getName());
+
+ return "for (" + userDataType(innerArr) + " " + elemName + " : config." + innerArr.getName() + "()) {\n" + //
+ " " + innerArr.getName() + "(new " + builderType(innerArr) + "(" + elemName + "));\n" + //
+ "}";
+ }
+
+ private static String setInnerMapBuildersFromConfig(InnerCNode innerMap) {
+ final String entryName = INTERNAL_PREFIX + "entry";
+ return "for (Map.Entry<String, " + userDataType(innerMap) + "> " + entryName + " : config." + innerMap.getName()
+ + "().entrySet()) {\n" + //
+ " " + innerMap.getName() + "(" + entryName + ".getKey(), new " + userDataType(innerMap) + ".Builder(" + entryName
+ + ".getValue()));\n" + //
+ "}";
+ }
+
+ private static String getBuilderConstructors(CNode node, String className) {
+ return "public Builder() { }\n" + //
+ "\n" + //
+ "public Builder(" + className + " config) {\n" + //
+ indentCode(INDENTATION,
+ stream(node.getChildren()).map(child -> setBuilderValueFromConfig(child, node)).collect(Collectors.joining("\n")))
+ + //
+ "\n}";
+ }
+
+ private static String conditionStatement(CNode child) {
+ final String superior = INTERNAL_PREFIX + "superior";
+
+ if (child.isArray) {
+ return "if (!" + superior + "." + child.getName() + ".isEmpty())";
+ } else if (child.isMap) {
+ return "";
+ } else if (child instanceof LeafCNode) {
+ return "if (" + superior + "." + child.getName() + " != null)";
+ } else {
+ return "";
+ }
+ }
+
+ private static String overrideBuilderValue(CNode child) {
+ final String superior = INTERNAL_PREFIX + "superior";
+ final String method = "override";
+ final String name = child.getName();
+ final String callSetter = name + "(" + superior + "." + name + ");";
+
+ if (child.isArray) {
+ String arrayOverride = INDENTATION + name + ".addAll(" + superior + "." + name + ");";
+ return conditionStatement(child) + "\n" + arrayOverride;
+ } else if (child instanceof InnerCNode && !child.isArray && !child.isMap) {
+ return name + "(" + name + "." + method + "(" + superior + "." + name + "));";
+ } else if (child.isMap) {
+ return callSetter;
+ } else {
+ return conditionStatement(child) + "\n" + INDENTATION + callSetter;
+ }
+ }
+
+ private static String getOverrideMethod(CNode node) {
+ final String superior = INTERNAL_PREFIX + "superior";
+ final String method = "override";
+
+ return "private Builder " + method + "(Builder " + superior + ") {\n" + //
+ indentCode(INDENTATION,
+ stream(node.getChildren()).map(BuilderGenerator::overrideBuilderValue).collect(Collectors.joining("\n")))
+ + "\n" + //
+ " return this;\n" + //
+ "}";
+ }
+
+ private static String builderType(CNode node) {
+ if (node instanceof InnerCNode) {
+ return boxedDataType(node) + ".Builder";
+ } else if (node instanceof FileLeaf) {
+ return "String";
+ } else if (node instanceof PathLeaf) {
+ return "FileReference";
+ } else if (node instanceof LeafCNode && (node.isArray || node.isMap)) {
+ return boxedDataType(node);
+ } else {
+ return userDataType(node);
+ }
+ }
+
+ private static String boxedBuilderType(LeafCNode node) {
+ if (node instanceof FileLeaf) {
+ return "String";
+ } else if (node instanceof PathLeaf) {
+ return "FileReference";
+ } else {
+ return boxedDataType(node);
+ }
+ }
+}