diff options
author | Olli Virtanen <olli.virtanen@oath.com> | 2018-05-24 16:53:17 +0200 |
---|---|---|
committer | Olli Virtanen <olli.virtanen@oath.com> | 2018-05-24 16:53:17 +0200 |
commit | 882407b5ed793f24fbd8b53334eb84e3548c1131 (patch) | |
tree | 3479808b9203cdb2cc76dcd1ad41f3a8a51bf639 /configgen/src | |
parent | 7a5eb504b4ce7b6bdcd0e0e613bcb2287fd358f1 (diff) |
Scala code replaced with Java equivalent
Diffstat (limited to 'configgen/src')
10 files changed, 3065 insertions, 1057 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); + } + } +} diff --git a/configgen/src/main/java/com/yahoo/config/codegen/ConfigGenerator.java b/configgen/src/main/java/com/yahoo/config/codegen/ConfigGenerator.java new file mode 100644 index 00000000000..9980cf565b1 --- /dev/null +++ b/configgen/src/main/java/com/yahoo/config/codegen/ConfigGenerator.java @@ -0,0 +1,444 @@ +// 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.BooleanLeaf; +import com.yahoo.config.codegen.LeafCNode.DoubleLeaf; +import com.yahoo.config.codegen.LeafCNode.EnumLeaf; +import com.yahoo.config.codegen.LeafCNode.FileLeaf; +import com.yahoo.config.codegen.LeafCNode.IntegerLeaf; +import com.yahoo.config.codegen.LeafCNode.LongLeaf; +import com.yahoo.config.codegen.LeafCNode.PathLeaf; +import com.yahoo.config.codegen.LeafCNode.ReferenceLeaf; +import com.yahoo.config.codegen.LeafCNode.StringLeaf; + +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +import static com.yahoo.config.codegen.BuilderGenerator.getBuilder; +import static com.yahoo.config.codegen.JavaClassBuilder.INDENTATION; +import static com.yahoo.config.codegen.ReservedWords.INTERNAL_PREFIX; +import static java.util.Arrays.stream; + +/** + * @author gjoranv + * @author Tony Vaagenes + * @author ollivir + */ +public class ConfigGenerator { + // TODO: don't take indent as method param - the caller should indent + public static String generateContent(String indent, InnerCNode node, boolean isOuter) { + CNode[] children = node.getChildren(); + + return indentCode(indent, + getBuilder(node) + "\n\n" + + stream(children).map(ConfigGenerator::getFieldDefinition).collect(Collectors.joining("\n")) + "\n\n" + + getConstructors(node) + "\n\n" + + getAccessors(children) + "\n\n" + + getGetChangesRequiringRestart(node) + "\n\n" + + getContainsFieldsFlaggedWithRestart(node, isOuter) + + getStaticMethods(node) + + generateCodeForChildren(children, indent) + ); + } + + private static String generateCodeForChildren(CNode[] children, String indent) { + List<String> pieces = new LinkedList<>(); + for (CNode child : children) { + if (child instanceof EnumLeaf) { + pieces.add(getEnumCode((EnumLeaf) child) + "\n"); + } else if (child instanceof InnerCNode) { + pieces.add(getInnerDefinition((InnerCNode) child, indent) + "\n"); + } + } + return String.join("\n", pieces); + } + + private static String getInnerDefinition(InnerCNode inner, String indent) { + return (getClassDoc(inner) + "\n" +// + getClassDeclaration(inner) + "\n" +// + generateContent(indent, inner, false)).trim() + "\n}"; + } + + private static String getClassDeclaration(CNode node) { + return "public final static class " + nodeClass(node) + " extends InnerNode { \n"; + } + + private static String getFieldDefinition(CNode node) { + String fieldDef; + if (node instanceof LeafCNode && node.isArray) { + fieldDef = String.format("LeafNodeVector<%s, %s> %s;", boxedDataType(node), nodeClass(node), node.getName()); + } else if (node instanceof InnerCNode && node.isArray) { + fieldDef = String.format("InnerNodeVector<%s> %s;", nodeClass(node), node.getName()); + } else if (node.isMap) { + fieldDef = String.format("Map<String, %s> %s;", nodeClass(node), node.getName()); + } else { + fieldDef = String.format("%s %s;", nodeClass(node), node.getName()); + } + return node.getCommentBlock("//") + "private final " + fieldDef; + } + + private static String getStaticMethods(InnerCNode node) { + if (node.isArray) { + return getStaticMethodsForInnerArray(node) + "\n\n"; + } else if (node.isMap) { + return getStaticMethodsForInnerMap(node) + "\n\n"; + } else { + return ""; + } + } + + private static String getContainsFieldsFlaggedWithRestart(CNode node, boolean isOuter) { + if (isOuter) { + return String.format("private static boolean containsFieldsFlaggedWithRestart() {\n" +// + " return %b;\n" +// + "}\n\n", node.needRestart()); + } else { + return ""; + } + } + + private static String getGetChangesRequiringRestart(InnerCNode node) { + List<String> comparisons = new LinkedList<>(); + for (CNode child : node.getChildren()) { + if (child.needRestart()) { + comparisons.add("\n " + getComparison(child)); + } + } + + return "private ChangesRequiringRestart getChangesRequiringRestart(" + nodeClass(node) + " newConfig) {\n" +// + " ChangesRequiringRestart changes = new ChangesRequiringRestart(\"" + node.getName() + "\");" + String.join("", comparisons) + "\n" +// + " return changes;\n" +// + "}"; + } + + private static String quotedComment(CNode node) { + return node.getComment().replace("\n", "\\n").replace("\"", "\\\""); + } + + private static String getComparison(CNode node) { + if (node instanceof InnerCNode && node.isArray) { + return " changes.compareArray(this." + node.getName() + ", newConfig." + node.getName() + ", \"" + node.getName() + "\", \"" + quotedComment(node) + "\",\n" +// + " (a,b) -> ((" + nodeClass(node) + ")a).getChangesRequiringRestart((" + nodeClass(node) + ")b));"; + } else if (node instanceof InnerCNode && node.isMap) { + return " changes.compareMap(this." + node.getName() + ", newConfig." + node.getName() + ", \"" + node.getName() + "\", \"" + quotedComment(node) + "\",\n" +// + " (a,b) -> ((" + nodeClass(node) + ")a).getChangesRequiringRestart((" + nodeClass(node) + ")b));"; + } else if (node instanceof InnerCNode) { + return " changes.mergeChanges(\"" + node.getName() + "\", this." + node.getName() + ".getChangesRequiringRestart(newConfig." + node.getName() + "));"; + } else if (node.isArray) { + return " changes.compareArray(this." + node.getName() + ", newConfig." + node.getName() + ", \"" + node.getName() + "\", \"" + quotedComment(node) + "\",\n" +// + " (a,b) -> new ChangesRequiringRestart(\"" + node.getName() + "\").compare(a,b,\"\",\"" + quotedComment(node) + "\"));"; + } else if (node.isMap) { + return " changes.compareMap(this." + node.getName() + ", newConfig." + node.getName() + ", \"" + node.getName() + "\", \"" + quotedComment(node) + "\",\n" +// + " (a,b) -> new ChangesRequiringRestart(\"" + node.getName() + "\").compare(a,b,\"\",\"" + quotedComment(node) + "\"));"; + } else { + return " changes.compare(this." + node.getName() + ", newConfig." + node.getName() + ", \"" + node.getName() + "\", \"" + quotedComment(node) + "\");"; + } + } + + private static String scalarDefault(LeafCNode scalar) { + if (scalar.getDefaultValue() == null) { + return ""; + } else if (scalar instanceof EnumLeaf && scalar.getDefaultValue().getValue() == null) { + return ""; + } else if (scalar instanceof EnumLeaf) { + return nodeClass(scalar) + "." + scalar.getDefaultValue().getStringRepresentation(); + } else if (scalar instanceof LongLeaf) { + return scalar.getDefaultValue().getStringRepresentation() + "L"; + } else if (scalar instanceof DoubleLeaf) { + return scalar.getDefaultValue().getStringRepresentation() + "D"; + } else { + return scalar.getDefaultValue().getStringRepresentation(); + } + } + + private static String assignFromBuilder(CNode child) { + final String name = child.getName(); + final String className = nodeClass(child); + final boolean isArray = child.isArray; + final boolean isMap = child.isMap; + + if (child instanceof FileLeaf && isArray) { + return name + " = LeafNodeVector.createFileNodeVector(builder." + name + ");"; + } else if (child instanceof PathLeaf && isArray) { + return name + " = LeafNodeVector.createPathNodeVector(builder." + name + ");"; + } else if (child instanceof LeafCNode && isArray) { + return name + " = new LeafNodeVector<>(builder." + name + ", new " + className + "());"; + } else if (child instanceof FileLeaf && isMap) { + return name + " = LeafNodeMaps.asFileNodeMap(builder." + name + ");"; + } else if (child instanceof PathLeaf && isMap) { + return name + " = LeafNodeMaps.asPathNodeMap(builder." + name + ");"; + } else if (child instanceof LeafCNode && isMap) { + return name + " = LeafNodeMaps.asNodeMap(builder." + name + ", new " + className + "());"; + } else if (child instanceof InnerCNode && isArray) { + return name + " = " + className + ".createVector(builder." + name + ");"; + } else if (child instanceof InnerCNode && isMap) { + return name + " = " + className + ".createMap(builder." + name + ");"; + } else if (child instanceof InnerCNode) { + return name + " = new " + className + "(builder." + name + ", throwIfUninitialized);"; + } else if (child instanceof LeafCNode) { + return name + " = (builder." + name + " == null) ?\n" +// + " new " + className + "(" + scalarDefault((LeafCNode) child) + ") : new " + className + "(builder." + name + ");"; + } else { + throw new IllegalStateException("Cannot create assignment for node"); // should not happen + } + } + + private static String getConstructors(InnerCNode inner) { + // TODO: merge these two constructors into one when the config library uses builders to set values from payload. + return "public " + nodeClass(inner) + "(Builder builder) {\n" +// + " this(builder, true);\n" +// + "}\n" +// + "\n" +// + "private " + nodeClass(inner) + "(Builder builder, boolean throwIfUninitialized) {\n" +// + " if (throwIfUninitialized && ! builder." + INTERNAL_PREFIX + "uninitialized.isEmpty())\n" +// + " throw new IllegalArgumentException(\"The following builder parameters for \" +\n" +// + " \"" + inner.getFullName() + " must be initialized: \" + builder." + INTERNAL_PREFIX + "uninitialized);\n" +// + "\n" +// + indentCode(INDENTATION, stream(inner.getChildren()).map(ConfigGenerator::assignFromBuilder).collect(Collectors.joining("\n"))) + "\n" +// + "}"; + } + + private static String getAccessorCode(CNode node) { + if (node.isArray) { + return accessorsForArray(node); + } else if (node.isMap) { + return accessorsForMap(node); + } else { + return accessorForStructOrScalar(node); + } + } + + private static String valueAccessor(CNode node) { + if (node instanceof LeafCNode) { + return ".value()"; + } else { + return ""; + } + } + + private static String listAccessor(CNode node) { + if (node instanceof LeafCNode) { + return node.getName() + ".asList()"; + } else { + return node.getName(); + } + } + + private static String mapAccessor(CNode node) { + if (node instanceof LeafCNode) { + return "LeafNodeMaps.asValueMap(" + node.getName() + ")"; + } else { + return "Collections.unmodifiableMap(" + node.getName() + ")"; + } + } + + private static String accessorsForArray(CNode node) { + final String name = node.getName(); + final String fullName = node.getFullName(); + return "/**\n" +// + " * @return " + fullName + "\n" +// + " */\n" +// + "public List<" + boxedDataType(node) + "> " + name + "() {\n" +// + " return " + listAccessor(node) + ";\n" +// + "}\n" +// + "\n" +// + "/**\n" +// + " * @param i the index of the value to return\n" +// + " * @return " + fullName + "\n" +// + " */\n" +// + "public " + userDataType(node) + " " + name + "(int i) {\n" +// + " return " + name + ".get(i)" + valueAccessor(node) + ";\n" +// + "}"; + } + + private static String accessorsForMap(CNode node) { + final String name = node.getName(); + final String fullName = node.getFullName(); + + return "/**\n" +// + " * @return " + fullName + "\n" +// + " */\n" +// + "public Map<String, " + boxedDataType(node) + "> " + name + "() {\n" +// + " return " + mapAccessor(node) + ";\n" +// + "}\n" +// + "\n" +// + "/**\n" +// + " * @param key the key of the value to return\n" +// + " * @return " + fullName + "\n" +// + " */\n" +// + "public " + userDataType(node) + " " + name + "(String key) {\n" +// + " return " + name + ".get(key)" + valueAccessor(node) + ";\n" +// + "}"; + } + + private static String accessorForStructOrScalar(CNode node) { + return "/**\n" +// + " * @return " + node.getFullName() + "\n" +// + " */\n" +// + "public " + userDataType(node) + " " + node.getName() + "() {\n" +// + " return " + node.getName() + valueAccessor(node) + ";\n" +// + "}"; + } + + private static String getAccessors(CNode[] children) { + List<String> accessors = new LinkedList<>(); + for (CNode child : children) { + String accessor = getAccessorCode(child); + if (accessor.isEmpty() == false) { + accessors.add(accessor); + } + } + return String.join("\n\n", accessors); + } + + private static String getStaticMethodsForInnerArray(InnerCNode inner) { + final String nc = nodeClass(inner); + return String.format("private static InnerNodeVector<%s> createVector(List<Builder> builders) {\n" +// + " List<%s> elems = new ArrayList<>();\n" +// + " for (Builder b : builders) {\n" +// + " elems.add(new %s(b));\n" +// + " }\n" +// + " return new InnerNodeVector<%s>(elems);\n" +// + "}", nc, nc, nc, nc); + } + + private static String getStaticMethodsForInnerMap(InnerCNode inner) { + final String nc = nodeClass(inner); + return String.format( + "private static Map<String, %s> createMap(Map<String, Builder> builders) {\n" +// + " Map<String, %s> ret = new LinkedHashMap<>();\n" +// + " for(String key : builders.keySet()) {\n" +// + " ret.put(key, new %s(builders.get(key)));\n" +// + " }\n" +// + " return Collections.unmodifiableMap(ret);\n" +// + "}", nc, nc, nc); + } + + private static String getEnumCode(EnumLeaf en) { + String enumValues = stream(en.getLegalValues()).map(e -> String.format(" public final static Enum %s = Enum.%s;", e, e)).collect(Collectors.joining("\n")); + + String code = String.format("%s\n" +// + "public final static class %s extends EnumNode<%s> {\n" +// + "\n" +// + " public %s(){\n" +// + " this.value = null;\n" +// + " }\n" +// + "\n" +// + " public %s(Enum enumValue) {\n" +// + " super(enumValue != null);\n" +// + " this.value = enumValue;\n" +// + " }\n" +// + "\n" +// + " public enum Enum {%s}\n" +// + "%s\n" +// + "\n" +// + " @Override\n" +// + " protected boolean doSetValue(@NonNull String name) {\n" +// + " try {\n" +// + " value = Enum.valueOf(name);\n" +// + " return true;\n" +// + " } catch (IllegalArgumentException e) {\n" +// + " }\n" +// + " return false;\n" +// + " }\n" +// + "}", getClassDoc(en), + nodeClass(en), + nodeClass(en) + ".Enum", + nodeClass(en), + nodeClass(en), + String.join(", ", en.getLegalValues()), + enumValues); + + return indentCode("", code); + } + + private static String getClassDoc(CNode node) { + String header = "/**\n" + " * This class represents " + node.getFullName(); + String nodeComment = node.getCommentBlock(" *"); + if (nodeComment.isEmpty()) { + return header + "\n */"; + } else { + if (nodeComment.endsWith("\n")) { + nodeComment = nodeComment.substring(0, nodeComment.length() - 1); + } + return header + "\n * \n" + nodeComment + "\n */"; + } + } + + static String indentCode(String indent, String code) { + List<String> indented = new LinkedList<>(); + for (String line : code.split("\n", -1)) { + indented.add(line.length() > 0 ? indent + line : line); + } + return String.join("\n", indented); + } + + /** + * @return the name of the class that is generated by this node. + */ + static String nodeClass(CNode node) { + if (node.getName().length() == 0) { + throw new CodegenRuntimeException("Node with empty name, under parent " + node.getParent().getName()); + } else if (node instanceof InnerCNode && node.getParent() == null) { + return ConfiggenUtil.createClassName(node.getName()); + } else if (node instanceof BooleanLeaf) { + return "BooleanNode"; + } else if (node instanceof DoubleLeaf) { + return "DoubleNode"; + } else if (node instanceof FileLeaf) { + return "FileNode"; + } else if (node instanceof PathLeaf) { + return "PathNode"; + } else if (node instanceof IntegerLeaf) { + return "IntegerNode"; + } else if (node instanceof LongLeaf) { + return "LongNode"; + } else if (node instanceof ReferenceLeaf) { + return "ReferenceNode"; + } else if (node instanceof StringLeaf) { + return "StringNode"; + } else { + return ConfiggenUtil.capitalize(node.getName()); + } + } + + static String userDataType(CNode node) { + if (node instanceof InnerCNode) { + return nodeClass(node); + } else if (node instanceof EnumLeaf) { + return nodeClass(node) + ".Enum"; + } else if (node instanceof BooleanLeaf) { + return "boolean"; + } else if (node instanceof DoubleLeaf) { + return "double"; + } else if (node instanceof FileLeaf) { + return "FileReference"; + } else if (node instanceof PathLeaf) { + return "Path"; + } else if (node instanceof IntegerLeaf) { + return "int"; + } else if (node instanceof LongLeaf) { + return "long"; + } else if (node instanceof StringLeaf) { + return "String"; + } else { + throw new IllegalStateException("Cannot determine user data type for node"); // should not occur + } + } + + /** + * @return the boxed java data type, e.g. Integer for int + */ + static String boxedDataType(CNode node) { + String rawType = userDataType(node); + + if ("int".equals(rawType)) { + return "Integer"; + } else if (rawType.toLowerCase().equals(rawType)) { + return ConfiggenUtil.capitalize(rawType); + } else { + return rawType; + } + } +} diff --git a/configgen/src/main/java/com/yahoo/config/codegen/ConfiggenUtil.java b/configgen/src/main/java/com/yahoo/config/codegen/ConfiggenUtil.java index 299a5540098..995ef419f30 100644 --- a/configgen/src/main/java/com/yahoo/config/codegen/ConfiggenUtil.java +++ b/configgen/src/main/java/com/yahoo/config/codegen/ConfiggenUtil.java @@ -26,7 +26,7 @@ public class ConfiggenUtil { return className; } - private static String capitalize(String in) { + static String capitalize(String in) { StringBuilder sb = new StringBuilder(in); sb.setCharAt(0, Character.toTitleCase(in.charAt(0))); return sb.toString(); diff --git a/configgen/src/main/java/com/yahoo/config/codegen/JavaClassBuilder.java b/configgen/src/main/java/com/yahoo/config/codegen/JavaClassBuilder.java new file mode 100644 index 00000000000..00498094db5 --- /dev/null +++ b/configgen/src/main/java/com/yahoo/config/codegen/JavaClassBuilder.java @@ -0,0 +1,170 @@ +// 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 java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.PrintStream; +import java.util.Arrays; +import java.util.Random; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.yahoo.config.codegen.ConfigGenerator.indentCode; +import static com.yahoo.config.codegen.ConfiggenUtil.createClassName; +import static com.yahoo.config.codegen.DefParser.DEFAULT_PACKAGE_PREFIX; + +/** + * Builds one Java class based on the given CNode tree. + * + * @author gjoranv + * @author Tony Vaagenes + * @author ollivir + */ +public class JavaClassBuilder implements ClassBuilder { + public static final String INDENTATION = " "; + + private final InnerCNode root; + private final NormalizedDefinition nd; + private final String packagePrefix; + private final String javaPackage; + private final String className; + private final File destDir; + + public JavaClassBuilder(InnerCNode root, NormalizedDefinition nd, File destDir, String rawPackagePrefix) { + this.root = root; + this.nd = nd; + this.packagePrefix = (rawPackagePrefix != null) ? rawPackagePrefix : DEFAULT_PACKAGE_PREFIX; + this.javaPackage = (root.getPackage() != null) ? root.getPackage() : packagePrefix + root.getNamespace(); + this.className = createClassName(root.getName()); + this.destDir = destDir; + } + + @Override + public void createConfigClasses() { + try { + File outFile = new File(getDestPath(destDir, javaPackage), className + ".java"); + try (PrintStream out = new PrintStream(new FileOutputStream(outFile))) { + out.print(getConfigClass(className)); + } + System.err.println(outFile.getPath() + " successfully written."); + } catch (FileNotFoundException e) { + throw new CodegenRuntimeException(e); + } + } + + public String getConfigClass(String className) { + return getHeader() + "\n\n" + // + getRootClassDeclaration(root, className) + "\n\n" + // + indentCode(INDENTATION, getFrameworkCode()) + "\n\n" + // + ConfigGenerator.generateContent(INDENTATION, root, true) + "\n" + // + "}\n"; + } + + private String getHeader() { + return "/**\n" + // + " * This file is generated from a config definition file.\n" + // + " * ------------ D O N O T E D I T ! ------------\n" + // + " */\n" + // + "\n" + // + "package " + javaPackage + ";\n" + // + "\n" + // + "import java.util.*;\n" + // + "import java.nio.file.Path;\n" + // + "import edu.umd.cs.findbugs.annotations.NonNull;\n" + // + getImportFrameworkClasses(root.getNamespace()); + } + + private String getImportFrameworkClasses(String namespace) { + if (CNode.DEFAULT_NAMESPACE.equals(namespace) == false) { + return "import " + packagePrefix + CNode.DEFAULT_NAMESPACE + ".*;"; + } else { + return ""; + } + } + + // TODO: remove the extra comment line " *" if root.getCommentBlock is empty + private String getRootClassDeclaration(InnerCNode root, String className) { + return "/**\n" + // + " * This class represents the root node of " + root.getFullName() + "\n" + // + " *\n" + // + "" + root.getCommentBlock(" *") + " */\n" + // + "public final class " + className + " extends ConfigInstance {\n" + // + "\n" + // + " public final static String CONFIG_DEF_MD5 = \"" + root.getMd5() + "\";\n" + // + " public final static String CONFIG_DEF_NAME = \"" + root.getName() + "\";\n" + // + " public final static String CONFIG_DEF_NAMESPACE = \"" + root.getNamespace() + "\";\n" + // + " public final static String CONFIG_DEF_VERSION = \"" + root.getVersion() + "\";\n" + // + " public final static String[] CONFIG_DEF_SCHEMA = {\n" + // + "" + indentCode(INDENTATION + INDENTATION, getDefSchema()) + "\n" + // + " };\n" + // + "\n" + // + " public static String getDefMd5() { return CONFIG_DEF_MD5; }\n" + // + " public static String getDefName() { return CONFIG_DEF_NAME; }\n" + // + " public static String getDefNamespace() { return CONFIG_DEF_NAMESPACE; }\n" + // + " public static String getDefVersion() { return CONFIG_DEF_VERSION; }"; + } + + private String getDefSchema() { + return nd.getNormalizedContent().stream().map(l -> "\"" + l.replace("\"", "\\\"") + "\"").collect(Collectors.joining(",\n")); + } + + private String getFrameworkCode() { + return "public interface Producer extends ConfigInstance.Producer {\n" + // + " void getConfig(Builder builder);\n" + // + "}"; + } + + /** + * @param rootDir + * The root directory for the destination path. + * @param javaPackage + * The java package + * @return the destination path for the generated config file, including the + * given rootDir. + */ + private File getDestPath(File rootDir, String javaPackage) { + File dir = rootDir; + for (String subDir : javaPackage.split("\\.")) { + dir = new File(dir, subDir); + synchronized (this) { + if (!dir.isDirectory() && !dir.mkdir()) { + throw new CodegenRuntimeException("Could not create " + dir.getPath()); + } + } + } + return dir; + } + + /** + * Returns a name that can be safely used as a local variable in the generated + * config class for the given node. The name will be based on the given basis + * string, but the basis itself is not a possible return value. + * + * @param node + * The node to find a unused symbol name for. + * @param basis + * The basis for the generated symbol name. + * @return A name that is not used in the given config node. + */ + static String createUniqueSymbol(CNode node, String basis) { + Set<String> usedSymbols = Arrays.stream(node.getChildren()).map(CNode::getName).collect(Collectors.toSet()); + Random rng = new Random(); + + for (int i = 1;; i++) { + String candidate = (i < basis.length()) ? basis.substring(0, i) + : ReservedWords.INTERNAL_PREFIX + basis + rng.nextInt(Integer.MAX_VALUE); + if (usedSymbols.contains(candidate) == false) { + return candidate; + } + } + } + + public String className() { + return className; + } + + public String javaPackage() { + return javaPackage; + } +} diff --git a/configgen/src/main/scala/com/yahoo/config/codegen/BuilderGenerator.scala b/configgen/src/main/scala/com/yahoo/config/codegen/BuilderGenerator.scala deleted file mode 100644 index 4f6f310e32e..00000000000 --- a/configgen/src/main/scala/com/yahoo/config/codegen/BuilderGenerator.scala +++ /dev/null @@ -1,350 +0,0 @@ -// Copyright 2017 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.ReservedWords.{INTERNAL_PREFIX => InternalPrefix} -import JavaClassBuilder.{Indentation, createUniqueSymbol} -import ConfigGenerator.{indentCode, nodeClass, userDataType, boxedDataType} -import com.yahoo.config.codegen.LeafCNode._ - -/** - * @author gjoranv - */ - -object BuilderGenerator { - - def getBuilder(node: InnerCNode): String = { - getDeclaration(node) + "\n" + - indentCode(Indentation, - getUninitializedScalars(node) + "\n\n" + - node.getChildren.map(getBuilderFieldDefinition).mkString("\n") + "\n\n" + - getBuilderConstructors(node, nodeClass(node)) + "\n\n" + - getOverrideMethod(node) + "\n\n" + - getBuilderSetters(node) + "\n" + - getSpecialRootBuilderCode(node) - ) + - "}" - } - - private def getDeclaration(node: InnerCNode) = { - def getInterfaces = - if (node.getParent == null) "implements ConfigInstance.Builder" - else "implements ConfigBuilder" - - "public static class Builder " + getInterfaces + " {" - } - - private def getSpecialRootBuilderCode(node: InnerCNode) = { - if (node.getParent == null) "\n" + getDispatchCode(node) + "\n" - else "" - } - - private def getDispatchCode(node: InnerCNode) = { - // 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. - """ - |@java.lang.Override - |public final boolean dispatchGetConfig(ConfigInstance.Producer producer) { - | if (producer instanceof Producer) { - | ((Producer)producer).getConfig(this); - | return true; - | } - | return false; - |} - | - |@java.lang.Override - |public final String getDefMd5() { return CONFIG_DEF_MD5; } - |@java.lang.Override - |public final String getDefName() { return CONFIG_DEF_NAME; } - |@java.lang.Override - |public final String getDefNamespace() { return CONFIG_DEF_NAMESPACE; } - """.stripMargin.trim - } - - private def getUninitializedScalars(node: InnerCNode): String = { - val scalarsWithoutDefault = { - node.getChildren.collect { - case leaf: LeafCNode if (!leaf.isArray && !leaf.isMap && leaf.getDefaultValue == null) => - "\"" + leaf.getName + "\"" - } - } - - val uninitializedList = - if (scalarsWithoutDefault.size > 0) - "Arrays.asList(\n" + indentCode(Indentation, scalarsWithoutDefault.mkString("",",\n","\n)")) - else - "" - - "private Set<String> " + InternalPrefix + "uninitialized = new HashSet<String>(" + uninitializedList + ");" - } - - private def getBuilderFieldDefinition(node: CNode): String = { - - (node match { - case array if node.isArray => - "public List<%s> %s = new ArrayList<>()".format(builderType(array), array.getName) - case map if node.isMap => - "public Map<String, %s> %s = new LinkedHashMap<>()".format(builderType(map), map.getName) - case struct: InnerCNode => - "public %s %s = new %s()".format(builderType(struct), struct.getName, builderType(struct)) - case scalar : LeafCNode => - "private " + boxedBuilderType(scalar) + " " + scalar.getName + " = null" - }) + ";" - } - - private def getBuilderSetters(node: CNode): String = { - val children: Array[CNode] = node.getChildren - - def structSetter(node: InnerCNode) = { - <code> - |public Builder {node.getName}({builderType(node)} {InternalPrefix}builder) {{ - | {node.getName} = {InternalPrefix}builder; - | return this; - |}} - </code>.text.stripMargin.trim - } - - def innerArraySetters(node: InnerCNode) = { - <code> - |/** - | * Add the given builder to this builder's list of {nodeClass(node)} builders - | * @param {InternalPrefix}builder a builder - | * @return this builder - | */ - |public Builder {node.getName}({builderType(node)} {InternalPrefix}builder) {{ - | {node.getName}.add({InternalPrefix}builder); - | return this; - |}} - | - |/** - | * Set the given list as this builder's list of {nodeClass(node)} builders - | * @param __builders a list of builders - | * @return this builder - | */ - |public Builder {node.getName}(List<{builderType(node)}> __builders) {{ - | {node.getName} = __builders; - | return this; - |}} - </code>.text.stripMargin.trim - } - - def leafArraySetters(node: LeafCNode) = { - val setters = - <code> - |public Builder {node.getName}({builderType(node)} {InternalPrefix}value) {{ - | {node.getName}.add({InternalPrefix}value); - | return this; - |}} - | - |public Builder {node.getName}(Collection<{builderType(node)}> {InternalPrefix}values) {{ - | {node.getName}.addAll({InternalPrefix}values); - | return this; - |}} - </code>.text.stripMargin.trim - - val privateSetter = - if (builderType(node) == "String" || builderType(node) == "FileReference") - "" - else - "\n\n" + - <code> - | - | - |private Builder {node.getName}(String {InternalPrefix}value) {{ - | return {node.getName}({builderType(node)}.valueOf({InternalPrefix}value)); - |}} - </code>.text.stripMargin.trim - - setters + privateSetter - } - - def innerMapSetters(node: CNode) = { - <code> - |public Builder {node.getName}(String {InternalPrefix}key, {builderType(node)} {InternalPrefix}value) {{ - | {node.getName}.put({InternalPrefix}key, {InternalPrefix}value); - | return this; - |}} - | - |public Builder {node.getName}(Map<String, {builderType(node)}> {InternalPrefix}values) {{ - | {node.getName}.putAll({InternalPrefix}values); - | return this; - |}} - </code>.text.stripMargin.trim - } - - def leafMapSetters(node: LeafCNode) = { - val privateSetter = - if (builderType(node) == "String" || builderType(node) == "FileReference") - "" - else - "\n\n" + - <code> - | - | - |private Builder {node.getName}(String {InternalPrefix}key, String {InternalPrefix}value) {{ - | return {node.getName}({InternalPrefix}key, {builderType(node)}.valueOf({InternalPrefix}value)); - |}} - </code>.text.stripMargin.trim - - innerMapSetters(node) + privateSetter - } - - def scalarSetters(node: LeafCNode): String = { - val name = node.getName - - val signalInitialized = - if (node.getDefaultValue == null) InternalPrefix + "uninitialized.remove(\"" + name + "\");\n" - else "" - - val stringSetter = - builderType(node) match { - case "String" => "" - case "FileReference" => "" - case _ => - """| - |private Builder %s(String %svalue) { - | return %s(%s.valueOf(%svalue)); - |}""".stripMargin.format(name, InternalPrefix, - name, boxedDataType(node), InternalPrefix) - } - - def getNullGuard = { - if (builderType(node) != boxedBuilderType(node)) - "" - else - "\n" + "if (%svalue == null) throw new IllegalArgumentException(\"Null value is not allowed.\");" - .format(InternalPrefix) - } - - // TODO: check if 2.9.2 allows string to start with a newline - """|public Builder %s(%s %svalue) {%s - | %s = %svalue; - | %s - """.stripMargin.format(name, builderType(node), InternalPrefix, getNullGuard, - name, InternalPrefix, - signalInitialized).trim + - "\n return this;" + "\n}\n" + - stringSetter - } - - (children collect { - case innerArray: InnerCNode if innerArray.isArray => innerArraySetters(innerArray) - case innerMap: InnerCNode if innerMap.isMap => innerMapSetters(innerMap) - case leafArray: LeafCNode if leafArray.isArray => leafArraySetters(leafArray) - case leafMap: LeafCNode if leafMap.isMap => leafMapSetters(leafMap) - case struct: InnerCNode => structSetter(struct) - case scalar: LeafCNode => scalarSetters(scalar) - } ).mkString("\n\n") - } - - private def getBuilderConstructors(node: CNode, className: String): String = { - def setBuilderValueFromConfig(child: CNode) = { - val name = child.getName - val isArray = child.isArray - val isMap = child.isMap - - child match { - case fileArray: FileLeaf if isArray => name + "(" + userDataType(fileArray) + ".toValues(config." + name + "()));" - case fileMap: FileLeaf if isMap => name + "(" + userDataType(fileMap) + ".toValueMap(config." + name + "()));" - case file: FileLeaf => name + "(config." + name + "().value());" - case pathArray: PathLeaf if isArray => name + "(" + nodeClass(pathArray) + ".toFileReferences(config." + name + "));" - case pathMap: PathLeaf if isMap => name + "(" + nodeClass(pathMap) + ".toFileReferenceMap(config." + name + "));" - case path: PathLeaf => name + "(config." + name + ".getFileReference());" - case leaf: LeafCNode => name + "(config." + name + "());" - case innerArray: InnerCNode if isArray => setInnerArrayBuildersFromConfig(innerArray) - case innerMap: InnerCNode if isMap => setInnerMapBuildersFromConfig(innerMap) - case struct => name + "(new " + builderType(struct) + "(config." + name + "()));" - } - } - - def setInnerArrayBuildersFromConfig(innerArr: InnerCNode) = { - val elemName = createUniqueSymbol(node, innerArr.getName) - <code> - |for ({userDataType(innerArr)} {elemName} : config.{innerArr.getName}()) {{ - | {innerArr.getName}(new {builderType(innerArr)}({elemName})); - |}} - </code>.text.stripMargin.trim - } - - def setInnerMapBuildersFromConfig(innerMap: InnerCNode) = { - val entryName = InternalPrefix + "entry" - <code> - |for (Map.Entry<String, {userDataType(innerMap)}> {entryName} : config.{innerMap.getName}().entrySet()) {{ - | {innerMap.getName}({entryName}.getKey(), new {userDataType(innerMap)}.Builder({entryName}.getValue())); - |}} - </code>.text.stripMargin.trim - } - - <code> - |public Builder() {{ }} - | - |public Builder({className} config) {{ - |{indentCode(Indentation, node.getChildren.map(setBuilderValueFromConfig).mkString("\n"))} - |}} - </code>.text.stripMargin.trim - } - - def arrayOverride(name: String, superior: String): String = { - Indentation + name + ".addAll(" + superior + "." + name + ");" - } - - private def getOverrideMethod(node:CNode): String = { - val method = "override" - val superior = InternalPrefix + "superior" - - def callSetter(name: String): String = { - name + "(" + superior + "." + name + ");" - } - def overrideBuilderValue(child: CNode) = { - val name = child.getName - child match { - case leafArray: CNode if (child.isArray) => - conditionStatement(child) + "\n" + arrayOverride(name, superior) - case struct: InnerCNode if !(child.isArray || child.isMap) => - name + "(" + name + "." + method + "(" + superior + "." + name + "));" - case map: CNode if child.isMap => - callSetter(name) - case _ => - conditionStatement(child) + "\n" + - Indentation + callSetter(name) - } - } - - def conditionStatement(child: CNode) = { - val name = child.getName - val isArray = child.isArray - val isMap = child.isMap - child match { - case _ if isArray => "if (!" + superior + "." + name + ".isEmpty())" - case _ if isMap => "" - case scalar: LeafCNode => "if (" + superior + "." + name + " != null)" - case struct => "" - } - } - - <code> - |private Builder {method}(Builder {superior}) {{ - |{indentCode(Indentation, node.getChildren.map(overrideBuilderValue).mkString("\n"))} - | return this; - |}} - </code>.text.stripMargin.trim - } - - private def builderType(node: CNode): String = { - node match { - case inner: InnerCNode => boxedDataType(node) + ".Builder" - case file: FileLeaf => "String" - case path: PathLeaf => "FileReference" - case leafArray: LeafCNode if (node.isArray || node.isMap) => boxedDataType(node) - case _ => userDataType(node) - } - } - - private def boxedBuilderType(node: LeafCNode): String = { - node match { - case file: FileLeaf => "String" - case path: PathLeaf => "FileReference" - case _ => boxedDataType(node) - } - } - -} diff --git a/configgen/src/main/scala/com/yahoo/config/codegen/ConfigGenerator.scala b/configgen/src/main/scala/com/yahoo/config/codegen/ConfigGenerator.scala deleted file mode 100644 index 38306a03575..00000000000 --- a/configgen/src/main/scala/com/yahoo/config/codegen/ConfigGenerator.scala +++ /dev/null @@ -1,423 +0,0 @@ -// Copyright 2017 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.BuilderGenerator.getBuilder -import com.yahoo.config.codegen.JavaClassBuilder.Indentation -import com.yahoo.config.codegen.LeafCNode._ -import com.yahoo.config.codegen.ReservedWords.{INTERNAL_PREFIX => InternalPrefix} - -/** - * @author gjoranv - * @author tonytv - */ -// TODO: don't take indent as method param - the caller should indent -object ConfigGenerator { - - def generateContent(indent: String, node: InnerCNode, isOuter: Boolean = true): String = { - val children: Array[CNode] = node.getChildren - - def generateCodeForChildren: String = { - (children collect { - case enum: EnumLeaf => getEnumCode(enum, "") + "\n" - case inner: InnerCNode => getInnerDefinition(inner, indent) + "\n" - } ).mkString("\n") - } - - def getInnerDefinition(inner: InnerCNode, indent: String) = { - <code> - |{getClassDoc(inner, indent)} - |{getClassDeclaration(inner)} - |{generateContent(indent, inner, false)} - </code>.text.stripMargin.trim + "\n}" - } - - def getClassDeclaration(node: CNode): String = { - "public final static class " + nodeClass(node)+ " extends InnerNode { " + "\n" - } - - def getFieldDefinition(node: CNode): String = { - node.getCommentBlock("//") + "private final " + - (node match { - case _: LeafCNode if node.isArray => - "LeafNodeVector<%s, %s> %s;".format(boxedDataType(node), nodeClass(node), node.getName) - case _: InnerCNode if node.isArray => - "InnerNodeVector<%s> %s;".format(nodeClass(node), node.getName) - case _ if node.isMap => - "Map<String, %s> %s;".format(nodeClass(node), node.getName) - case _ => - "%s %s;".format(nodeClass(node), node.getName) - }) - } - - def getStaticMethods = { - if (node.isArray) getStaticMethodsForInnerArray(node) + "\n\n" - else if (node.isMap) getStaticMethodsForInnerMap(node) + "\n\n" - else "" - } - - def getContainsFieldsFlaggedWithRestart(node: CNode): String = { - if (isOuter) { - """ - |private static boolean containsFieldsFlaggedWithRestart() { - | return %b; - |} - """.stripMargin.trim.format(node.needRestart) + "\n\n" - } else "" - } - - indentCode(indent, - getBuilder(node) + "\n\n" + - children.map(getFieldDefinition).mkString("\n") + "\n\n" + - getConstructors(node) + "\n\n" + - getAccessors(children) + "\n\n" + - getGetChangesRequiringRestart(node) + "\n\n" + - getContainsFieldsFlaggedWithRestart(node) + - getStaticMethods + - generateCodeForChildren - ) - } - - private def getGetChangesRequiringRestart(node: InnerCNode): String = { - def quotedComment(node: CNode): String = { - node.getComment.replace("\n", "\\n").replace("\"", "\\\"") - } - - def getComparison(node: CNode): String = node match { - case inner: InnerCNode if inner.isArray => - <code> - | changes.compareArray(this.{inner.getName}, newConfig.{inner.getName}, "{inner.getName}", "{quotedComment(inner)}", - | (a,b) -> (({nodeClass(inner)})a).getChangesRequiringRestart(({nodeClass(inner)})b)); - </code>.text.stripMargin.trim - case inner: InnerCNode if inner.isMap => - <code> - | changes.compareMap(this.{inner.getName}, newConfig.{inner.getName}, "{inner.getName}", "{quotedComment(inner)}", - | (a,b) -> (({nodeClass(inner)})a).getChangesRequiringRestart(({nodeClass(inner)})b)); - </code>.text.stripMargin.trim - case inner: InnerCNode => - <code> - | changes.mergeChanges("{inner.getName}", this.{inner.getName}.getChangesRequiringRestart(newConfig.{inner.getName})); - </code>.text.stripMargin.trim - case node: CNode if node.isArray => - <code> - | changes.compareArray(this.{node.getName}, newConfig.{node.getName}, "{node.getName}", "{quotedComment(node)}", - | (a,b) -> new ChangesRequiringRestart("{node.getName}").compare(a,b,"","{quotedComment(node)}")); - </code>.text.stripMargin.trim - case node: CNode if node.isMap => - <code> - | changes.compareMap(this.{node.getName}, newConfig.{node.getName}, "{node.getName}", "{quotedComment(node)}", - | (a,b) -> new ChangesRequiringRestart("{node.getName}").compare(a,b,"","{quotedComment(node)}")); - </code>.text.stripMargin.trim - case node: CNode => - <code> - | changes.compare(this.{node.getName}, newConfig.{node.getName}, "{node.getName}", "{quotedComment(node)}"); - </code>.text.stripMargin.trim - } - - val comparisons = - for { - c <- node.getChildren if c.needRestart - } yield "\n " + getComparison(c) - - <code> - |private ChangesRequiringRestart getChangesRequiringRestart({nodeClass(node)} newConfig) {{ - | ChangesRequiringRestart changes = new ChangesRequiringRestart("{node.getName}");{comparisons.mkString("")} - | return changes; - |}} - </code>.text.stripMargin.trim - } - - - private def scalarDefault(scalar: LeafCNode): String = { - scalar match { - case _ if scalar.getDefaultValue == null => "" - case enumWithNullDefault: EnumLeaf if enumWithNullDefault.getDefaultValue.getValue == null => "" - case enum: EnumLeaf => nodeClass(enum) + "." + enum.getDefaultValue.getStringRepresentation - case long: LongLeaf => long.getDefaultValue.getStringRepresentation + "L" - case double: DoubleLeaf => double.getDefaultValue.getStringRepresentation + "D" - case _ => scalar.getDefaultValue.getStringRepresentation - } - } - - private def getConstructors(inner: InnerCNode) = { - - def assignFromBuilder(child: CNode) = { - val name = child.getName - val className = nodeClass(child) - val dataType = boxedDataType(child) - val isArray = child.isArray - val isMap = child.isMap - - def assignIfInitialized(leaf: LeafCNode) = { - <code> - |{name} = (builder.{name} == null) ? - | new {className}({scalarDefault(leaf)}) : new {className}(builder.{name}); - </code>.text.stripMargin.trim - } - - child match { - case fileArray: FileLeaf if isArray => - name + " = LeafNodeVector.createFileNodeVector(builder."+ name +");" - case pathArray: PathLeaf if isArray => - name + " = LeafNodeVector.createPathNodeVector(builder."+ name +");" - case leafArray: LeafCNode if isArray => - name + " = new LeafNodeVector<>(builder."+ name +", new " + className + "());" - case fileMap: LeafCNode if isMap && child.isInstanceOf[FileLeaf] => - name + " = LeafNodeMaps.asFileNodeMap(builder."+ name +");" - case pathMap: LeafCNode if isMap && child.isInstanceOf[PathLeaf] => - name + " = LeafNodeMaps.asPathNodeMap(builder."+ name +");" - case leafMap: LeafCNode if isMap => - name + " = LeafNodeMaps.asNodeMap(builder."+ name +", new " + className + "());" - case innerArray: InnerCNode if isArray => - name + " = " + className + ".createVector(builder." + name + ");" - case innerMap: InnerCNode if isMap => - name + " = " + className + ".createMap(builder." + name + ");" - case struct: InnerCNode => - name + " = new " + className + "(builder." + name + ", throwIfUninitialized);" - case leaf: LeafCNode => - assignIfInitialized(leaf) - } - } - - // TODO: merge these two constructors into one when the config library uses builders to set values from payload. - <code> - |public {nodeClass(inner)}(Builder builder) {{ - | this(builder, true); - |}} - | - |private {nodeClass(inner)}(Builder builder, boolean throwIfUninitialized) {{ - | if (throwIfUninitialized && ! builder.{InternalPrefix}uninitialized.isEmpty()) - | throw new IllegalArgumentException("The following builder parameters for " + - | "{inner.getFullName} must be initialized: " + builder.{InternalPrefix}uninitialized); - | - |{indentCode(Indentation, inner.getChildren.map(assignFromBuilder).mkString("\n"))} - |}} - </code>.text.stripMargin.trim - } - - private def getAccessors(children: Array[CNode]): String = { - - def getAccessorCode(indent: String, node: CNode): String = { - indentCode(indent, - if (node.isArray) - accessorsForArray(node) - else if (node.isMap) - accessorsForMap(node) - else - accessorForStructOrScalar(node)) - } - - def valueAccessor(node: CNode) = node match { - case leaf: LeafCNode => ".value()" - case inner => "" - } - - def listAccessor(node: CNode) = node match { - case leaf: LeafCNode => "%s.asList()".format(leaf.getName) - case inner => inner.getName - } - - def mapAccessor(node: CNode) = node match { - case leaf: LeafCNode => "LeafNodeMaps.asValueMap(%s)".format(leaf.getName) - case inner => "Collections.unmodifiableMap(%s)".format(inner.getName) - } - - def accessorsForArray(node: CNode): String = { - val name = node.getName - val fullName = node.getFullName - <code> - |/** - | * @return {fullName} - | */ - |public List<{boxedDataType(node)}> {name}() {{ - | return {listAccessor(node)}; - |}} - | - |/** - | * @param i the index of the value to return - | * @return {fullName} - | */ - |public {userDataType(node)} {name}(int i) {{ - | return {name}.get(i){valueAccessor(node)}; - |}} - </code>.text.stripMargin.trim - } - - def accessorsForMap(node: CNode): String = { - val name = node.getName - val fullName = node.getFullName - <code> - |/** - | * @return {fullName} - | */ - |public Map<String, {boxedDataType(node)}> {name}() {{ - | return {mapAccessor(node)}; - |}} - | - |/** - | * @param key the key of the value to return - | * @return {fullName} - | */ - |public {userDataType(node)} {name}(String key) {{ - | return {name}.get(key){valueAccessor(node)}; - |}} - </code>.text.stripMargin.trim - } - - def accessorForStructOrScalar(node: CNode): String = { - <code> - |/** - | * @return {node.getFullName} - | */ - |public {userDataType(node)} {node.getName}() {{ - | return {node.getName}{valueAccessor(node)}; - |}} - </code>.text.stripMargin.trim - } - - val accessors = - for { - c <- children - accessor = getAccessorCode("", c) - if (accessor.length > 0) - } yield (accessor + "\n") - accessors.mkString("\n").trim - } - - private def getStaticMethodsForInnerArray(inner: InnerCNode) = { - """ - |private static InnerNodeVector<%s> createVector(List<Builder> builders) { - | List<%s> elems = new ArrayList<>(); - | for (Builder b : builders) { - | elems.add(new %s(b)); - | } - | return new InnerNodeVector<%s>(elems); - |} - """.stripMargin.format(List.fill(5)(nodeClass(inner)): _*).trim - } - - private def getStaticMethodsForInnerMap(inner: InnerCNode) = { - """ - |private static Map<String, %s> createMap(Map<String, Builder> builders) { - | Map<String, %s> ret = new LinkedHashMap<>(); - | for(String key : builders.keySet()) { - | ret.put(key, new %s(builders.get(key))); - | } - | return Collections.unmodifiableMap(ret); - |} - """.stripMargin.format(List.fill(3)(nodeClass(inner)): _*).trim - } - - private def getEnumCode(enum: EnumLeaf, indent: String): String = { - - def getEnumValues(enum: EnumLeaf): String = { - val enumValues = - for (value <- enum.getLegalValues) yield - """ public final static Enum %s = Enum.%s;""".format(value, value) - enumValues.mkString("\n") - } - - // TODO: try to rewrite to xml - val code = - """ - |%s - |public final static class %s extends EnumNode<%s> { - - | public %s(){ - | this.value = null; - | } - - | public %s(Enum enumValue) { - | super(enumValue != null); - | this.value = enumValue; - | } - - | public enum Enum {%s} - |%s - - | @Override - | protected boolean doSetValue(@NonNull String name) { - | try { - | value = Enum.valueOf(name); - | return true; - | } catch (IllegalArgumentException e) { - | } - | return false; - | } - |} - |""" - .stripMargin.format(getClassDoc(enum, indent), - nodeClass(enum), - nodeClass(enum)+".Enum", - nodeClass(enum), - nodeClass(enum), - enum.getLegalValues.mkString(", "), - getEnumValues(enum)) - - indentCode(indent, code).trim - } - - def getClassDoc(node: CNode, indent: String): String = { - val header = "/**\n" + " * This class represents " + node.getFullName - val nodeComment = node.getCommentBlock(" *") match { - case "" => "" - case s => "\n *\n" + s.stripLineEnd // TODO: strip trailing \n in CNode.getCommentBlock - } - header + nodeComment + "\n */" - } - - def indentCode(indent: String, code: String): String = { - val indentedLines = - for (s <- code.split("\n", -1)) yield - if (s.length() > 0) (indent + s) else s - indentedLines.mkString("\n") - } - - /** - * @return the name of the class that is generated by this node. - */ - def nodeClass(node: CNode): String = { - node match { - case emptyName: CNode if node.getName.length == 0 => - throw new CodegenRuntimeException("Node with empty name, under parent " + emptyName.getParent.getName) - case root: InnerCNode if root.getParent == null => ConfiggenUtil.createClassName(root.getName) - case b: BooleanLeaf => "BooleanNode" - case d: DoubleLeaf => "DoubleNode" - case f: FileLeaf => "FileNode" - case p: PathLeaf => "PathNode" - case i: IntegerLeaf => "IntegerNode" - case l: LongLeaf => "LongNode" - case r: ReferenceLeaf => "ReferenceNode" - case s: StringLeaf => "StringNode" - case _ => node.getName.capitalize - } - } - - def userDataType(node: CNode): String = { - node match { - case inner: InnerCNode => nodeClass(node) - case enum: EnumLeaf => nodeClass(enum) + ".Enum" - case b: BooleanLeaf => "boolean" - case d: DoubleLeaf => "double" - case f: FileLeaf => "FileReference" - case p: PathLeaf => "Path" - case i: IntegerLeaf => "int" - case l: LongLeaf => "long" - case s: StringLeaf => "String" - } - } - - /** - * @return the boxed java data type, e.g. Integer for int - */ - def boxedDataType(node: CNode): String = { - val rawType = userDataType(node) - - rawType match { - case "int" => "Integer" - case _ if rawType == rawType.toLowerCase => rawType.capitalize - case _ => rawType - } - } - -} diff --git a/configgen/src/main/scala/com/yahoo/config/codegen/JavaClassBuilder.scala b/configgen/src/main/scala/com/yahoo/config/codegen/JavaClassBuilder.scala deleted file mode 100644 index e03a6d3d04b..00000000000 --- a/configgen/src/main/scala/com/yahoo/config/codegen/JavaClassBuilder.scala +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.config.codegen - -import java.io.{File, FileNotFoundException, FileOutputStream, PrintStream} - -import com.yahoo.config.codegen.ConfigGenerator.indentCode -import com.yahoo.config.codegen.ConfiggenUtil.createClassName -import com.yahoo.config.codegen.DefParser.DEFAULT_PACKAGE_PREFIX - -import scala.collection.JavaConverters._ -import scala.util.Random -/** - * Builds one Java class based on the given CNode tree. - * - * @author gjoranv - * @author tonytv - */ -class JavaClassBuilder( - root: InnerCNode, - nd: NormalizedDefinition, - destDir: File, - rawPackagePrefix: String) - extends ClassBuilder -{ - import JavaClassBuilder._ - - val packagePrefix = if (rawPackagePrefix != null) rawPackagePrefix else DEFAULT_PACKAGE_PREFIX - val javaPackage = if (root.getPackage != null) root.getPackage else packagePrefix + root.getNamespace - val className = createClassName(root.getName) - - override def createConfigClasses() { - try { - val outFile = new File(getDestPath(destDir, javaPackage), className + ".java") - var out: PrintStream = null - try { - out = new PrintStream(new FileOutputStream(outFile)) - out.print(getConfigClass(className)) - } finally { - if (out != null) out.close() - } - System.err.println(outFile.getPath + " successfully written.") - } - catch { - case e: FileNotFoundException => { - throw new CodegenRuntimeException(e) - } - } - } - - def getConfigClass(className:String): String = { - val ret = new StringBuilder - - ret.append(getHeader).append("\n\n") - ret.append(getRootClassDeclaration(root, className)).append("\n\n") - ret.append(indentCode(Indentation, getFrameworkCode(className))).append("\n\n") - ret.append(ConfigGenerator.generateContent(Indentation, root)).append("\n") - ret.append("}\n") - - ret.toString() - } - - private def getHeader: String = { - <code> - |/** - | * This file is generated from a config definition file. - | * ------------ D O N O T E D I T ! ------------ - | */ - | - |package {javaPackage}; - | - |import java.util.*; - |import java.nio.file.Path; - |import edu.umd.cs.findbugs.annotations.NonNull; - |{getImportFrameworkClasses(root.getNamespace)} - </code>.text.stripMargin.trim - } - - private def getImportFrameworkClasses(namespace: String): String = { - if (namespace != CNode.DEFAULT_NAMESPACE) - "import " + packagePrefix + CNode.DEFAULT_NAMESPACE + ".*;\n" - else - "" - } - - // TODO: remove the extra comment line " *" if root.getCommentBlock is empty - private def getRootClassDeclaration(root:InnerCNode, className: String): String = { - <code> - |/** - | * This class represents the root node of {root.getFullName} - | * - |{root.getCommentBlock(" *")} */ - |public final class {className} extends ConfigInstance {{ - | - | public final static String CONFIG_DEF_MD5 = "{root.getMd5}"; - | public final static String CONFIG_DEF_NAME = "{root.getName}"; - | public final static String CONFIG_DEF_NAMESPACE = "{root.getNamespace}"; - | public final static String CONFIG_DEF_VERSION = "{root.getVersion}"; - | public final static String[] CONFIG_DEF_SCHEMA = {{ - |{indentCode(Indentation * 2, getDefSchema)} - | }}; - | - | public static String getDefMd5() {{ return CONFIG_DEF_MD5; }} - | public static String getDefName() {{ return CONFIG_DEF_NAME; }} - | public static String getDefNamespace() {{ return CONFIG_DEF_NAMESPACE; }} - | public static String getDefVersion() {{ return CONFIG_DEF_VERSION; }} - </code>.text.stripMargin.trim - } - - private def getDefSchema: String = { - nd.getNormalizedContent.asScala.map { line => - "\"" + - line.replace("\"", "\\\"") + - "\"" - }.mkString(",\n") - } - - private def getFrameworkCode(className: String): String = { - getProducerBase - } - - private def getProducerBase = { - """ - |public interface Producer extends ConfigInstance.Producer { - | void getConfig(Builder builder); - |} - """.stripMargin.trim - } - - /** - * @param rootDir The root directory for the destination path. - * @param javaPackage The java package - * @return the destination path for the generated config file, including the given rootDir. - */ - private def getDestPath(rootDir: File, javaPackage: String): File = { - var dir: File = rootDir - val subDirs: Array[String] = javaPackage.split("""\.""") - for (subDir <- subDirs) { - dir = new File(dir, subDir) - this.synchronized { - if (!dir.isDirectory && !dir.mkdir) throw new CodegenRuntimeException("Could not create " + dir.getPath) - } - } - dir - } - -} - - -object JavaClassBuilder { - - val Indentation = " " - - /** - * Returns a name that can be safely used as a local variable in the generated config class - * for the given node. The name will be based on the given basis string, but the basis itself is - * not a possible return value. - * - * @param node The node to find a unused symbol name for. - * @param basis The basis for the generated symbol name. - * @return A name that is not used in the given config node. - */ - def createUniqueSymbol(node: CNode, basis: String) = { - - def getCandidate(cnt: Int) = { - if (cnt < basis.length()) - basis.substring(0, cnt) - else - ReservedWords.INTERNAL_PREFIX + basis + Random.nextInt().abs - } - - def getUsedSymbols: Set[String] = { - (node.getChildren map (child => child.getName)).toSet - } - - // TODO: refactoring potential - val usedSymbols = getUsedSymbols - var count = 1 - var candidate = getCandidate(count) - while (usedSymbols contains(candidate)) { - count += 1 - candidate = getCandidate(count) - } - candidate - } - -} diff --git a/configgen/src/test/java/com/yahoo/config/codegen/JavaClassBuilderTest.java b/configgen/src/test/java/com/yahoo/config/codegen/JavaClassBuilderTest.java new file mode 100644 index 00000000000..744f8c9b1a2 --- /dev/null +++ b/configgen/src/test/java/com/yahoo/config/codegen/JavaClassBuilderTest.java @@ -0,0 +1,116 @@ +// 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 org.junit.Ignore; +import org.junit.Test; + +import java.io.IOException; +import java.io.StringReader; +import java.nio.file.FileSystems; +import java.nio.file.Files; + +import static com.yahoo.config.codegen.ConfiggenUtil.createClassName; +import static com.yahoo.config.codegen.JavaClassBuilder.createUniqueSymbol; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * @author gjoranv + * @author ollivir + */ +public class JavaClassBuilderTest { + private static final String TEST_DIR = "target/test-classes/"; + private static final String DEF_NAME = TEST_DIR + "allfeatures.def"; + private static final String REFERENCE_NAME = TEST_DIR + "allfeatures.reference"; + + @Ignore + @Test + public void visual_inspection_of_generated_class() { + final String testDefinition = "version=1\n" + // + "namespace=test\n" + // + "p path\n" + // + "pathArr[] path\n" + // + "f file\n" + // + "fileArr[] file\n" + // + "i int default=0\n" + // + "# A long value\n" + // + "l long default=0\n" + // + "s string default=\"\"\n" + // + "b bool\n" + // + "# An enum value\n" + // + "e enum {A, B, C}\n" + // + "intArr[] int\n" + // + "boolArr[] bool\n" + // + "enumArr[] enum {FOO, BAR}\n" + // + "intMap{} int\n" + // + "# A struct\n" + // + "# with multi-line\n" + // + "# comment and \"quotes\".\n" + // + "myStruct.i int\n" + // + "myStruct.s string\n" + // + "# An inner array\n" + // + "myArr[].i int\n" + // + "myArr[].newStruct.s string\n" + // + "myArr[].newStruct.b bool\n" + // + "myArr[].intArr[] int\n" + // + "# An inner map\n" + // + "myMap{}.i int\n" + // + "myMap{}.newStruct.s string\n" + // + "myMap{}.newStruct.b bool\n" + // + "myMap{}.intArr[] int\n" + // + "intMap{} int\n"; + + DefParser parser = new DefParser("test", new StringReader(testDefinition)); + InnerCNode root = parser.getTree(); + JavaClassBuilder builder = new JavaClassBuilder(root, parser.getNormalizedDefinition(), null, null); + String configClass = builder.getConfigClass("TestConfig"); + System.out.print(configClass); + } + + @Test + public void testCreateUniqueSymbol() { + final String testDefinition = "version=1\n" + // + "namespace=test\n" + // + "m int\n" + // + "n int\n"; + InnerCNode root = new DefParser("test", new StringReader(testDefinition)).getTree(); + + assertThat(createUniqueSymbol(root, "foo"), is("f")); + assertThat(createUniqueSymbol(root, "name"), is("na")); + assertTrue(createUniqueSymbol(root, "m").startsWith(ReservedWords.INTERNAL_PREFIX + "m")); + + // The basis string is not a legal return value, even if unique, to avoid + // multiple symbols with the same name if the same basis string is given twice. + assertTrue(createUniqueSymbol(root, "my").startsWith(ReservedWords.INTERNAL_PREFIX + "my")); + } + + @Test + public void testCreateClassName() { + assertThat(createClassName("simple"), is("SimpleConfig")); + assertThat(createClassName("a"), is("AConfig")); + assertThat(createClassName("a-b-c"), is("ABCConfig")); + assertThat(createClassName("a-1-2b"), is("A12bConfig")); + assertThat(createClassName("my-app"), is("MyAppConfig")); + assertThat(createClassName("MyApp"), is("MyAppConfig")); + } + + @Test(expected = CodegenRuntimeException.class) + public void testIllegalClassName() { + createClassName("+illegal"); + } + + @Test + public void verify_generated_class_against_reference() throws IOException { + final String testDefinition = String.join("\n", Files.readAllLines(FileSystems.getDefault().getPath(DEF_NAME))); + final String referenceClass = String.join("\n", Files.readAllLines(FileSystems.getDefault().getPath(REFERENCE_NAME))) + "\n"; + + DefParser parser = new DefParser("allfeatures", new StringReader(testDefinition)); + InnerCNode root = parser.getTree(); + JavaClassBuilder builder = new JavaClassBuilder(root, parser.getNormalizedDefinition(), null, null); + String configClass = builder.getConfigClass("AllfeaturesConfig"); + + assertEquals(referenceClass, configClass); + } +} diff --git a/configgen/src/test/resources/allfeatures.reference b/configgen/src/test/resources/allfeatures.reference new file mode 100644 index 00000000000..ebc21e8255c --- /dev/null +++ b/configgen/src/test/resources/allfeatures.reference @@ -0,0 +1,1983 @@ +/** + * This file is generated from a config definition file. + * ------------ D O N O T E D I T ! ------------ + */ + +package com.yahoo.configgen; + +import java.util.*; +import java.nio.file.Path; +import edu.umd.cs.findbugs.annotations.NonNull; +import com.yahoo.config.*; + +/** + * This class represents the root node of allfeatures + * + * Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + * + * This def file should test most aspects of def files that makes a difference + * for the generated config classes. The goal is to trigger all blocks of + * code in the code generators. This includes: + * + * - Use all legal special characters in the def file name, to ensure that those + * that needs to be replaced in type names are actually replaced. + * - Use the same enum type twice to verify that we dont declare or define it + * twice. + * - Use the same struct type twice for the same reason. + * - Include arrays of primitives and structs. + * - Include enum primitives and array of enums. Arrays of enums must be handled + * specially by the C++ code. + * - Include enums both with and without default values. + * - Include primitive string, numbers & doubles both with and without default + * values. + * - Have an array within a struct, to verify that we correctly recurse. + * - Reuse type name further within to ensure that this works. + */ +public final class AllfeaturesConfig extends ConfigInstance { + + public final static String CONFIG_DEF_MD5 = "eb2d24dbbcf054b21be729e2cfaafd93"; + public final static String CONFIG_DEF_NAME = "allfeatures"; + public final static String CONFIG_DEF_NAMESPACE = "configgen"; + public final static String CONFIG_DEF_VERSION = ""; + public final static String[] CONFIG_DEF_SCHEMA = { + "namespace=configgen", + "boolVal bool", + "bool_with_def bool default=false", + "intVal int", + "intWithDef int default=-545", + "longVal long", + "longWithDef long default=1234567890123", + "doubleVal double", + "double_with_def double default=-6.43", + "stringVal string", + "stringwithdef string default=\"foobar#notacomment\"", + "enumVal enum { FOO, BAR, FOOBAR }", + "enumwithdef enum { FOO2, BAR2, FOOBAR2 } default=BAR2", + "refVal reference", + "refwithdef reference default=\":parent:\"", + "fileVal file", + "pathVal path", + "boolarr[] bool", + "intarr[] int", + "longarr[] long", + "doublearr[] double", + "stringarr[] string", + "enumarr[] enum { ARRAY, VALUES }", + "refarr[] reference", + "filearr[] file", + "pathArr[] path", + "intMap{} int", + "pathMap{} file", + "basic_struct.foo string default=\"foo\"", + "basic_struct.bar int default=0", + "struct_of_struct.inner0.name string default=\"inner0\"", + "struct_of_struct.inner0.index int default=0", + "struct_of_struct.inner1.name string default=\"inner1\"", + "struct_of_struct.inner1.index int default=1", + "myArray[].intVal int default=14", + "myArray[].stringVal[] string", + "myArray[].enumVal enum { INNER, ENUM, TYPE } default=TYPE", + "myArray[].refVal reference", + "myArray[].anotherArray[].foo int default=-4", + "myMap{}.intVal int default=15", + "myMap{}.stringVal[] string", + "myMap{}.enumVal enum { INNER, ENUM, TYPE } default=ENUM", + "myMap{}.refVal reference", + "myMap{}.anotherArray[].foo int default=-5" + }; + + public static String getDefMd5() { return CONFIG_DEF_MD5; } + public static String getDefName() { return CONFIG_DEF_NAME; } + public static String getDefNamespace() { return CONFIG_DEF_NAMESPACE; } + public static String getDefVersion() { return CONFIG_DEF_VERSION; } + + public interface Producer extends ConfigInstance.Producer { + void getConfig(Builder builder); + } + + public static class Builder implements ConfigInstance.Builder { + private Set<String> __uninitialized = new HashSet<String>(Arrays.asList( + "boolVal", + "intVal", + "longVal", + "doubleVal", + "stringVal", + "enumVal", + "refVal", + "fileVal", + "pathVal" + )); + + private Boolean boolVal = null; + private Boolean bool_with_def = null; + private Integer intVal = null; + private Integer intWithDef = null; + private Long longVal = null; + private Long longWithDef = null; + private Double doubleVal = null; + private Double double_with_def = null; + private String stringVal = null; + private String stringwithdef = null; + private EnumVal.Enum enumVal = null; + private Enumwithdef.Enum enumwithdef = null; + private String refVal = null; + private String refwithdef = null; + private String fileVal = null; + private FileReference pathVal = null; + public List<Boolean> boolarr = new ArrayList<>(); + public List<Integer> intarr = new ArrayList<>(); + public List<Long> longarr = new ArrayList<>(); + public List<Double> doublearr = new ArrayList<>(); + public List<String> stringarr = new ArrayList<>(); + public List<Enumarr.Enum> enumarr = new ArrayList<>(); + public List<String> refarr = new ArrayList<>(); + public List<String> filearr = new ArrayList<>(); + public List<FileReference> pathArr = new ArrayList<>(); + public Map<String, Integer> intMap = new LinkedHashMap<>(); + public Map<String, String> pathMap = new LinkedHashMap<>(); + public Basic_struct.Builder basic_struct = new Basic_struct.Builder(); + public Struct_of_struct.Builder struct_of_struct = new Struct_of_struct.Builder(); + public List<MyArray.Builder> myArray = new ArrayList<>(); + public Map<String, MyMap.Builder> myMap = new LinkedHashMap<>(); + + public Builder() { } + + public Builder(AllfeaturesConfig config) { + boolVal(config.boolVal()); + bool_with_def(config.bool_with_def()); + intVal(config.intVal()); + intWithDef(config.intWithDef()); + longVal(config.longVal()); + longWithDef(config.longWithDef()); + doubleVal(config.doubleVal()); + double_with_def(config.double_with_def()); + stringVal(config.stringVal()); + stringwithdef(config.stringwithdef()); + enumVal(config.enumVal()); + enumwithdef(config.enumwithdef()); + refVal(config.refVal()); + refwithdef(config.refwithdef()); + fileVal(config.fileVal().value()); + pathVal(config.pathVal.getFileReference()); + boolarr(config.boolarr()); + intarr(config.intarr()); + longarr(config.longarr()); + doublearr(config.doublearr()); + stringarr(config.stringarr()); + enumarr(config.enumarr()); + refarr(config.refarr()); + filearr(FileReference.toValues(config.filearr())); + pathArr(PathNode.toFileReferences(config.pathArr)); + intMap(config.intMap()); + pathMap(FileReference.toValueMap(config.pathMap())); + basic_struct(new Basic_struct.Builder(config.basic_struct())); + struct_of_struct(new Struct_of_struct.Builder(config.struct_of_struct())); + for (MyArray m : config.myArray()) { + myArray(new MyArray.Builder(m)); + } + for (Map.Entry<String, MyMap> __entry : config.myMap().entrySet()) { + myMap(__entry.getKey(), new MyMap.Builder(__entry.getValue())); + } + } + + private Builder override(Builder __superior) { + if (__superior.boolVal != null) + boolVal(__superior.boolVal); + if (__superior.bool_with_def != null) + bool_with_def(__superior.bool_with_def); + if (__superior.intVal != null) + intVal(__superior.intVal); + if (__superior.intWithDef != null) + intWithDef(__superior.intWithDef); + if (__superior.longVal != null) + longVal(__superior.longVal); + if (__superior.longWithDef != null) + longWithDef(__superior.longWithDef); + if (__superior.doubleVal != null) + doubleVal(__superior.doubleVal); + if (__superior.double_with_def != null) + double_with_def(__superior.double_with_def); + if (__superior.stringVal != null) + stringVal(__superior.stringVal); + if (__superior.stringwithdef != null) + stringwithdef(__superior.stringwithdef); + if (__superior.enumVal != null) + enumVal(__superior.enumVal); + if (__superior.enumwithdef != null) + enumwithdef(__superior.enumwithdef); + if (__superior.refVal != null) + refVal(__superior.refVal); + if (__superior.refwithdef != null) + refwithdef(__superior.refwithdef); + if (__superior.fileVal != null) + fileVal(__superior.fileVal); + if (__superior.pathVal != null) + pathVal(__superior.pathVal); + if (!__superior.boolarr.isEmpty()) + boolarr.addAll(__superior.boolarr); + if (!__superior.intarr.isEmpty()) + intarr.addAll(__superior.intarr); + if (!__superior.longarr.isEmpty()) + longarr.addAll(__superior.longarr); + if (!__superior.doublearr.isEmpty()) + doublearr.addAll(__superior.doublearr); + if (!__superior.stringarr.isEmpty()) + stringarr.addAll(__superior.stringarr); + if (!__superior.enumarr.isEmpty()) + enumarr.addAll(__superior.enumarr); + if (!__superior.refarr.isEmpty()) + refarr.addAll(__superior.refarr); + if (!__superior.filearr.isEmpty()) + filearr.addAll(__superior.filearr); + if (!__superior.pathArr.isEmpty()) + pathArr.addAll(__superior.pathArr); + intMap(__superior.intMap); + pathMap(__superior.pathMap); + basic_struct(basic_struct.override(__superior.basic_struct)); + struct_of_struct(struct_of_struct.override(__superior.struct_of_struct)); + if (!__superior.myArray.isEmpty()) + myArray.addAll(__superior.myArray); + myMap(__superior.myMap); + return this; + } + + public Builder boolVal(boolean __value) { + boolVal = __value; + __uninitialized.remove("boolVal"); + return this; + } + + private Builder boolVal(String __value) { + return boolVal(Boolean.valueOf(__value)); + } + + public Builder bool_with_def(boolean __value) { + bool_with_def = __value; + return this; + } + + private Builder bool_with_def(String __value) { + return bool_with_def(Boolean.valueOf(__value)); + } + + public Builder intVal(int __value) { + intVal = __value; + __uninitialized.remove("intVal"); + return this; + } + + private Builder intVal(String __value) { + return intVal(Integer.valueOf(__value)); + } + + public Builder intWithDef(int __value) { + intWithDef = __value; + return this; + } + + private Builder intWithDef(String __value) { + return intWithDef(Integer.valueOf(__value)); + } + + public Builder longVal(long __value) { + longVal = __value; + __uninitialized.remove("longVal"); + return this; + } + + private Builder longVal(String __value) { + return longVal(Long.valueOf(__value)); + } + + public Builder longWithDef(long __value) { + longWithDef = __value; + return this; + } + + private Builder longWithDef(String __value) { + return longWithDef(Long.valueOf(__value)); + } + + public Builder doubleVal(double __value) { + doubleVal = __value; + __uninitialized.remove("doubleVal"); + return this; + } + + private Builder doubleVal(String __value) { + return doubleVal(Double.valueOf(__value)); + } + + public Builder double_with_def(double __value) { + double_with_def = __value; + return this; + } + + private Builder double_with_def(String __value) { + return double_with_def(Double.valueOf(__value)); + } + + public Builder stringVal(String __value) { + if (__value == null) throw new IllegalArgumentException("Null value is not allowed."); + stringVal = __value; + __uninitialized.remove("stringVal"); + return this; + } + + + public Builder stringwithdef(String __value) { + if (__value == null) throw new IllegalArgumentException("Null value is not allowed."); + stringwithdef = __value; + return this; + } + + + public Builder enumVal(EnumVal.Enum __value) { + if (__value == null) throw new IllegalArgumentException("Null value is not allowed."); + enumVal = __value; + __uninitialized.remove("enumVal"); + return this; + } + + private Builder enumVal(String __value) { + return enumVal(EnumVal.Enum.valueOf(__value)); + } + + public Builder enumwithdef(Enumwithdef.Enum __value) { + if (__value == null) throw new IllegalArgumentException("Null value is not allowed."); + enumwithdef = __value; + return this; + } + + private Builder enumwithdef(String __value) { + return enumwithdef(Enumwithdef.Enum.valueOf(__value)); + } + + public Builder refVal(String __value) { + if (__value == null) throw new IllegalArgumentException("Null value is not allowed."); + refVal = __value; + __uninitialized.remove("refVal"); + return this; + } + + + public Builder refwithdef(String __value) { + if (__value == null) throw new IllegalArgumentException("Null value is not allowed."); + refwithdef = __value; + return this; + } + + + public Builder fileVal(String __value) { + if (__value == null) throw new IllegalArgumentException("Null value is not allowed."); + fileVal = __value; + __uninitialized.remove("fileVal"); + return this; + } + + + public Builder pathVal(FileReference __value) { + if (__value == null) throw new IllegalArgumentException("Null value is not allowed."); + pathVal = __value; + __uninitialized.remove("pathVal"); + return this; + } + + + public Builder boolarr(Boolean __value) { + boolarr.add(__value); + return this; + } + + public Builder boolarr(Collection<Boolean> __values) { + boolarr.addAll(__values); + return this; + } + + private Builder boolarr(String __value) { + return boolarr(Boolean.valueOf(__value)); + } + + public Builder intarr(Integer __value) { + intarr.add(__value); + return this; + } + + public Builder intarr(Collection<Integer> __values) { + intarr.addAll(__values); + return this; + } + + private Builder intarr(String __value) { + return intarr(Integer.valueOf(__value)); + } + + public Builder longarr(Long __value) { + longarr.add(__value); + return this; + } + + public Builder longarr(Collection<Long> __values) { + longarr.addAll(__values); + return this; + } + + private Builder longarr(String __value) { + return longarr(Long.valueOf(__value)); + } + + public Builder doublearr(Double __value) { + doublearr.add(__value); + return this; + } + + public Builder doublearr(Collection<Double> __values) { + doublearr.addAll(__values); + return this; + } + + private Builder doublearr(String __value) { + return doublearr(Double.valueOf(__value)); + } + + public Builder stringarr(String __value) { + stringarr.add(__value); + return this; + } + + public Builder stringarr(Collection<String> __values) { + stringarr.addAll(__values); + return this; + } + + public Builder enumarr(Enumarr.Enum __value) { + enumarr.add(__value); + return this; + } + + public Builder enumarr(Collection<Enumarr.Enum> __values) { + enumarr.addAll(__values); + return this; + } + + private Builder enumarr(String __value) { + return enumarr(Enumarr.Enum.valueOf(__value)); + } + + public Builder refarr(String __value) { + refarr.add(__value); + return this; + } + + public Builder refarr(Collection<String> __values) { + refarr.addAll(__values); + return this; + } + + public Builder filearr(String __value) { + filearr.add(__value); + return this; + } + + public Builder filearr(Collection<String> __values) { + filearr.addAll(__values); + return this; + } + + public Builder pathArr(FileReference __value) { + pathArr.add(__value); + return this; + } + + public Builder pathArr(Collection<FileReference> __values) { + pathArr.addAll(__values); + return this; + } + + public Builder intMap(String __key, Integer __value) { + intMap.put(__key, __value); + return this; + } + + public Builder intMap(Map<String, Integer> __values) { + intMap.putAll(__values); + return this; + } + + private Builder intMap(String __key, String __value) { + return intMap(__key, Integer.valueOf(__value)); + } + + public Builder pathMap(String __key, String __value) { + pathMap.put(__key, __value); + return this; + } + + public Builder pathMap(Map<String, String> __values) { + pathMap.putAll(__values); + return this; + } + + public Builder basic_struct(Basic_struct.Builder __builder) { + basic_struct = __builder; + return this; + } + + public Builder struct_of_struct(Struct_of_struct.Builder __builder) { + struct_of_struct = __builder; + return this; + } + + /** + * Add the given builder to this builder's list of MyArray builders + * @param __builder a builder + * @return this builder + */ + public Builder myArray(MyArray.Builder __builder) { + myArray.add(__builder); + return this; + } + + /** + * Set the given list as this builder's list of MyArray builders + * @param __builders a list of builders + * @return this builder + */ + public Builder myArray(List<MyArray.Builder> __builders) { + myArray = __builders; + return this; + } + + public Builder myMap(String __key, MyMap.Builder __value) { + myMap.put(__key, __value); + return this; + } + + public Builder myMap(Map<String, MyMap.Builder> __values) { + myMap.putAll(__values); + return this; + } + + @java.lang.Override + public final boolean dispatchGetConfig(ConfigInstance.Producer producer) { + if (producer instanceof Producer) { + ((Producer)producer).getConfig(this); + return true; + } + return false; + } + + @java.lang.Override + public final String getDefMd5() { return CONFIG_DEF_MD5; } + @java.lang.Override + public final String getDefName() { return CONFIG_DEF_NAME; } + @java.lang.Override + public final String getDefNamespace() { return CONFIG_DEF_NAMESPACE; } + } + + // Some random bool without a default value. These comments exist to check + // that comment parsing works.e + private final BooleanNode boolVal; + // A bool with a default value set. + private final BooleanNode bool_with_def; + private final IntegerNode intVal; + private final IntegerNode intWithDef; + private final LongNode longVal; + private final LongNode longWithDef; + private final DoubleNode doubleVal; + private final DoubleNode double_with_def; + // Another comment + private final StringNode stringVal; + private final StringNode stringwithdef; + private final EnumVal enumVal; + private final Enumwithdef enumwithdef; + private final ReferenceNode refVal; + private final ReferenceNode refwithdef; + private final FileNode fileVal; + private final PathNode pathVal; + private final LeafNodeVector<Boolean, BooleanNode> boolarr; + private final LeafNodeVector<Integer, IntegerNode> intarr; + private final LeafNodeVector<Long, LongNode> longarr; + private final LeafNodeVector<Double, DoubleNode> doublearr; + private final LeafNodeVector<String, StringNode> stringarr; + private final LeafNodeVector<Enumarr.Enum, Enumarr> enumarr; + private final LeafNodeVector<String, ReferenceNode> refarr; + private final LeafNodeVector<FileReference, FileNode> filearr; + private final LeafNodeVector<Path, PathNode> pathArr; + private final Map<String, IntegerNode> intMap; + private final Map<String, FileNode> pathMap; + private final Basic_struct basic_struct; + private final Struct_of_struct struct_of_struct; + private final InnerNodeVector<MyArray> myArray; + private final Map<String, MyMap> myMap; + + public AllfeaturesConfig(Builder builder) { + this(builder, true); + } + + private AllfeaturesConfig(Builder builder, boolean throwIfUninitialized) { + if (throwIfUninitialized && ! builder.__uninitialized.isEmpty()) + throw new IllegalArgumentException("The following builder parameters for " + + "allfeatures must be initialized: " + builder.__uninitialized); + + boolVal = (builder.boolVal == null) ? + new BooleanNode() : new BooleanNode(builder.boolVal); + bool_with_def = (builder.bool_with_def == null) ? + new BooleanNode(false) : new BooleanNode(builder.bool_with_def); + intVal = (builder.intVal == null) ? + new IntegerNode() : new IntegerNode(builder.intVal); + intWithDef = (builder.intWithDef == null) ? + new IntegerNode(-545) : new IntegerNode(builder.intWithDef); + longVal = (builder.longVal == null) ? + new LongNode() : new LongNode(builder.longVal); + longWithDef = (builder.longWithDef == null) ? + new LongNode(1234567890123L) : new LongNode(builder.longWithDef); + doubleVal = (builder.doubleVal == null) ? + new DoubleNode() : new DoubleNode(builder.doubleVal); + double_with_def = (builder.double_with_def == null) ? + new DoubleNode(-6.43D) : new DoubleNode(builder.double_with_def); + stringVal = (builder.stringVal == null) ? + new StringNode() : new StringNode(builder.stringVal); + stringwithdef = (builder.stringwithdef == null) ? + new StringNode("foobar#notacomment") : new StringNode(builder.stringwithdef); + enumVal = (builder.enumVal == null) ? + new EnumVal() : new EnumVal(builder.enumVal); + enumwithdef = (builder.enumwithdef == null) ? + new Enumwithdef(Enumwithdef.BAR2) : new Enumwithdef(builder.enumwithdef); + refVal = (builder.refVal == null) ? + new ReferenceNode() : new ReferenceNode(builder.refVal); + refwithdef = (builder.refwithdef == null) ? + new ReferenceNode(":parent:") : new ReferenceNode(builder.refwithdef); + fileVal = (builder.fileVal == null) ? + new FileNode() : new FileNode(builder.fileVal); + pathVal = (builder.pathVal == null) ? + new PathNode() : new PathNode(builder.pathVal); + boolarr = new LeafNodeVector<>(builder.boolarr, new BooleanNode()); + intarr = new LeafNodeVector<>(builder.intarr, new IntegerNode()); + longarr = new LeafNodeVector<>(builder.longarr, new LongNode()); + doublearr = new LeafNodeVector<>(builder.doublearr, new DoubleNode()); + stringarr = new LeafNodeVector<>(builder.stringarr, new StringNode()); + enumarr = new LeafNodeVector<>(builder.enumarr, new Enumarr()); + refarr = new LeafNodeVector<>(builder.refarr, new ReferenceNode()); + filearr = LeafNodeVector.createFileNodeVector(builder.filearr); + pathArr = LeafNodeVector.createPathNodeVector(builder.pathArr); + intMap = LeafNodeMaps.asNodeMap(builder.intMap, new IntegerNode()); + pathMap = LeafNodeMaps.asFileNodeMap(builder.pathMap); + basic_struct = new Basic_struct(builder.basic_struct, throwIfUninitialized); + struct_of_struct = new Struct_of_struct(builder.struct_of_struct, throwIfUninitialized); + myArray = MyArray.createVector(builder.myArray); + myMap = MyMap.createMap(builder.myMap); + } + + /** + * @return allfeatures.boolVal + */ + public boolean boolVal() { + return boolVal.value(); + } + + /** + * @return allfeatures.bool_with_def + */ + public boolean bool_with_def() { + return bool_with_def.value(); + } + + /** + * @return allfeatures.intVal + */ + public int intVal() { + return intVal.value(); + } + + /** + * @return allfeatures.intWithDef + */ + public int intWithDef() { + return intWithDef.value(); + } + + /** + * @return allfeatures.longVal + */ + public long longVal() { + return longVal.value(); + } + + /** + * @return allfeatures.longWithDef + */ + public long longWithDef() { + return longWithDef.value(); + } + + /** + * @return allfeatures.doubleVal + */ + public double doubleVal() { + return doubleVal.value(); + } + + /** + * @return allfeatures.double_with_def + */ + public double double_with_def() { + return double_with_def.value(); + } + + /** + * @return allfeatures.stringVal + */ + public String stringVal() { + return stringVal.value(); + } + + /** + * @return allfeatures.stringwithdef + */ + public String stringwithdef() { + return stringwithdef.value(); + } + + /** + * @return allfeatures.enumVal + */ + public EnumVal.Enum enumVal() { + return enumVal.value(); + } + + /** + * @return allfeatures.enumwithdef + */ + public Enumwithdef.Enum enumwithdef() { + return enumwithdef.value(); + } + + /** + * @return allfeatures.refVal + */ + public String refVal() { + return refVal.value(); + } + + /** + * @return allfeatures.refwithdef + */ + public String refwithdef() { + return refwithdef.value(); + } + + /** + * @return allfeatures.fileVal + */ + public FileReference fileVal() { + return fileVal.value(); + } + + /** + * @return allfeatures.pathVal + */ + public Path pathVal() { + return pathVal.value(); + } + + /** + * @return allfeatures.boolarr[] + */ + public List<Boolean> boolarr() { + return boolarr.asList(); + } + + /** + * @param i the index of the value to return + * @return allfeatures.boolarr[] + */ + public boolean boolarr(int i) { + return boolarr.get(i).value(); + } + + /** + * @return allfeatures.intarr[] + */ + public List<Integer> intarr() { + return intarr.asList(); + } + + /** + * @param i the index of the value to return + * @return allfeatures.intarr[] + */ + public int intarr(int i) { + return intarr.get(i).value(); + } + + /** + * @return allfeatures.longarr[] + */ + public List<Long> longarr() { + return longarr.asList(); + } + + /** + * @param i the index of the value to return + * @return allfeatures.longarr[] + */ + public long longarr(int i) { + return longarr.get(i).value(); + } + + /** + * @return allfeatures.doublearr[] + */ + public List<Double> doublearr() { + return doublearr.asList(); + } + + /** + * @param i the index of the value to return + * @return allfeatures.doublearr[] + */ + public double doublearr(int i) { + return doublearr.get(i).value(); + } + + /** + * @return allfeatures.stringarr[] + */ + public List<String> stringarr() { + return stringarr.asList(); + } + + /** + * @param i the index of the value to return + * @return allfeatures.stringarr[] + */ + public String stringarr(int i) { + return stringarr.get(i).value(); + } + + /** + * @return allfeatures.enumarr[] + */ + public List<Enumarr.Enum> enumarr() { + return enumarr.asList(); + } + + /** + * @param i the index of the value to return + * @return allfeatures.enumarr[] + */ + public Enumarr.Enum enumarr(int i) { + return enumarr.get(i).value(); + } + + /** + * @return allfeatures.refarr[] + */ + public List<String> refarr() { + return refarr.asList(); + } + + /** + * @param i the index of the value to return + * @return allfeatures.refarr[] + */ + public String refarr(int i) { + return refarr.get(i).value(); + } + + /** + * @return allfeatures.filearr[] + */ + public List<FileReference> filearr() { + return filearr.asList(); + } + + /** + * @param i the index of the value to return + * @return allfeatures.filearr[] + */ + public FileReference filearr(int i) { + return filearr.get(i).value(); + } + + /** + * @return allfeatures.pathArr[] + */ + public List<Path> pathArr() { + return pathArr.asList(); + } + + /** + * @param i the index of the value to return + * @return allfeatures.pathArr[] + */ + public Path pathArr(int i) { + return pathArr.get(i).value(); + } + + /** + * @return allfeatures.intMap{} + */ + public Map<String, Integer> intMap() { + return LeafNodeMaps.asValueMap(intMap); + } + + /** + * @param key the key of the value to return + * @return allfeatures.intMap{} + */ + public int intMap(String key) { + return intMap.get(key).value(); + } + + /** + * @return allfeatures.pathMap{} + */ + public Map<String, FileReference> pathMap() { + return LeafNodeMaps.asValueMap(pathMap); + } + + /** + * @param key the key of the value to return + * @return allfeatures.pathMap{} + */ + public FileReference pathMap(String key) { + return pathMap.get(key).value(); + } + + /** + * @return allfeatures.basic_struct + */ + public Basic_struct basic_struct() { + return basic_struct; + } + + /** + * @return allfeatures.struct_of_struct + */ + public Struct_of_struct struct_of_struct() { + return struct_of_struct; + } + + /** + * @return allfeatures.myArray[] + */ + public List<MyArray> myArray() { + return myArray; + } + + /** + * @param i the index of the value to return + * @return allfeatures.myArray[] + */ + public MyArray myArray(int i) { + return myArray.get(i); + } + + /** + * @return allfeatures.myMap{} + */ + public Map<String, MyMap> myMap() { + return Collections.unmodifiableMap(myMap); + } + + /** + * @param key the key of the value to return + * @return allfeatures.myMap{} + */ + public MyMap myMap(String key) { + return myMap.get(key); + } + + private ChangesRequiringRestart getChangesRequiringRestart(AllfeaturesConfig newConfig) { + ChangesRequiringRestart changes = new ChangesRequiringRestart("allfeatures"); + return changes; + } + + private static boolean containsFieldsFlaggedWithRestart() { + return false; + } + + /** + * This class represents allfeatures.enumVal + */ + public final static class EnumVal extends EnumNode<EnumVal.Enum> { + + public EnumVal(){ + this.value = null; + } + + public EnumVal(Enum enumValue) { + super(enumValue != null); + this.value = enumValue; + } + + public enum Enum {FOO, BAR, FOOBAR} + public final static Enum FOO = Enum.FOO; + public final static Enum BAR = Enum.BAR; + public final static Enum FOOBAR = Enum.FOOBAR; + + @Override + protected boolean doSetValue(@NonNull String name) { + try { + value = Enum.valueOf(name); + return true; + } catch (IllegalArgumentException e) { + } + return false; + } + } + + /** + * This class represents allfeatures.enumwithdef + */ + public final static class Enumwithdef extends EnumNode<Enumwithdef.Enum> { + + public Enumwithdef(){ + this.value = null; + } + + public Enumwithdef(Enum enumValue) { + super(enumValue != null); + this.value = enumValue; + } + + public enum Enum {FOO2, BAR2, FOOBAR2} + public final static Enum FOO2 = Enum.FOO2; + public final static Enum BAR2 = Enum.BAR2; + public final static Enum FOOBAR2 = Enum.FOOBAR2; + + @Override + protected boolean doSetValue(@NonNull String name) { + try { + value = Enum.valueOf(name); + return true; + } catch (IllegalArgumentException e) { + } + return false; + } + } + + /** + * This class represents allfeatures.enumarr[] + */ + public final static class Enumarr extends EnumNode<Enumarr.Enum> { + + public Enumarr(){ + this.value = null; + } + + public Enumarr(Enum enumValue) { + super(enumValue != null); + this.value = enumValue; + } + + public enum Enum {ARRAY, VALUES} + public final static Enum ARRAY = Enum.ARRAY; + public final static Enum VALUES = Enum.VALUES; + + @Override + protected boolean doSetValue(@NonNull String name) { + try { + value = Enum.valueOf(name); + return true; + } catch (IllegalArgumentException e) { + } + return false; + } + } + + /** + * This class represents allfeatures.basic_struct + */ + public final static class Basic_struct extends InnerNode { + + public static class Builder implements ConfigBuilder { + private Set<String> __uninitialized = new HashSet<String>(); + + private String foo = null; + private Integer bar = null; + + public Builder() { } + + public Builder(Basic_struct config) { + foo(config.foo()); + bar(config.bar()); + } + + private Builder override(Builder __superior) { + if (__superior.foo != null) + foo(__superior.foo); + if (__superior.bar != null) + bar(__superior.bar); + return this; + } + + public Builder foo(String __value) { + if (__value == null) throw new IllegalArgumentException("Null value is not allowed."); + foo = __value; + return this; + } + + + public Builder bar(int __value) { + bar = __value; + return this; + } + + private Builder bar(String __value) { + return bar(Integer.valueOf(__value)); + } + } + + // A basic struct + private final StringNode foo; + private final IntegerNode bar; + + public Basic_struct(Builder builder) { + this(builder, true); + } + + private Basic_struct(Builder builder, boolean throwIfUninitialized) { + if (throwIfUninitialized && ! builder.__uninitialized.isEmpty()) + throw new IllegalArgumentException("The following builder parameters for " + + "allfeatures.basic_struct must be initialized: " + builder.__uninitialized); + + foo = (builder.foo == null) ? + new StringNode("foo") : new StringNode(builder.foo); + bar = (builder.bar == null) ? + new IntegerNode(0) : new IntegerNode(builder.bar); + } + + /** + * @return allfeatures.basic_struct.foo + */ + public String foo() { + return foo.value(); + } + + /** + * @return allfeatures.basic_struct.bar + */ + public int bar() { + return bar.value(); + } + + private ChangesRequiringRestart getChangesRequiringRestart(Basic_struct newConfig) { + ChangesRequiringRestart changes = new ChangesRequiringRestart("basic_struct"); + return changes; + } + } + + /** + * This class represents allfeatures.struct_of_struct + */ + public final static class Struct_of_struct extends InnerNode { + + public static class Builder implements ConfigBuilder { + private Set<String> __uninitialized = new HashSet<String>(); + + public Inner0.Builder inner0 = new Inner0.Builder(); + public Inner1.Builder inner1 = new Inner1.Builder(); + + public Builder() { } + + public Builder(Struct_of_struct config) { + inner0(new Inner0.Builder(config.inner0())); + inner1(new Inner1.Builder(config.inner1())); + } + + private Builder override(Builder __superior) { + inner0(inner0.override(__superior.inner0)); + inner1(inner1.override(__superior.inner1)); + return this; + } + + public Builder inner0(Inner0.Builder __builder) { + inner0 = __builder; + return this; + } + + public Builder inner1(Inner1.Builder __builder) { + inner1 = __builder; + return this; + } + } + + private final Inner0 inner0; + private final Inner1 inner1; + + public Struct_of_struct(Builder builder) { + this(builder, true); + } + + private Struct_of_struct(Builder builder, boolean throwIfUninitialized) { + if (throwIfUninitialized && ! builder.__uninitialized.isEmpty()) + throw new IllegalArgumentException("The following builder parameters for " + + "allfeatures.struct_of_struct must be initialized: " + builder.__uninitialized); + + inner0 = new Inner0(builder.inner0, throwIfUninitialized); + inner1 = new Inner1(builder.inner1, throwIfUninitialized); + } + + /** + * @return allfeatures.struct_of_struct.inner0 + */ + public Inner0 inner0() { + return inner0; + } + + /** + * @return allfeatures.struct_of_struct.inner1 + */ + public Inner1 inner1() { + return inner1; + } + + private ChangesRequiringRestart getChangesRequiringRestart(Struct_of_struct newConfig) { + ChangesRequiringRestart changes = new ChangesRequiringRestart("struct_of_struct"); + return changes; + } + + /** + * This class represents allfeatures.struct_of_struct.inner0 + */ + public final static class Inner0 extends InnerNode { + + public static class Builder implements ConfigBuilder { + private Set<String> __uninitialized = new HashSet<String>(); + + private String name = null; + private Integer index = null; + + public Builder() { } + + public Builder(Inner0 config) { + name(config.name()); + index(config.index()); + } + + private Builder override(Builder __superior) { + if (__superior.name != null) + name(__superior.name); + if (__superior.index != null) + index(__superior.index); + return this; + } + + public Builder name(String __value) { + if (__value == null) throw new IllegalArgumentException("Null value is not allowed."); + name = __value; + return this; + } + + + public Builder index(int __value) { + index = __value; + return this; + } + + private Builder index(String __value) { + return index(Integer.valueOf(__value)); + } + } + + // A struct of struct + private final StringNode name; + private final IntegerNode index; + + public Inner0(Builder builder) { + this(builder, true); + } + + private Inner0(Builder builder, boolean throwIfUninitialized) { + if (throwIfUninitialized && ! builder.__uninitialized.isEmpty()) + throw new IllegalArgumentException("The following builder parameters for " + + "allfeatures.struct_of_struct.inner0 must be initialized: " + builder.__uninitialized); + + name = (builder.name == null) ? + new StringNode("inner0") : new StringNode(builder.name); + index = (builder.index == null) ? + new IntegerNode(0) : new IntegerNode(builder.index); + } + + /** + * @return allfeatures.struct_of_struct.inner0.name + */ + public String name() { + return name.value(); + } + + /** + * @return allfeatures.struct_of_struct.inner0.index + */ + public int index() { + return index.value(); + } + + private ChangesRequiringRestart getChangesRequiringRestart(Inner0 newConfig) { + ChangesRequiringRestart changes = new ChangesRequiringRestart("inner0"); + return changes; + } + } + + /** + * This class represents allfeatures.struct_of_struct.inner1 + */ + public final static class Inner1 extends InnerNode { + + public static class Builder implements ConfigBuilder { + private Set<String> __uninitialized = new HashSet<String>(); + + private String name = null; + private Integer index = null; + + public Builder() { } + + public Builder(Inner1 config) { + name(config.name()); + index(config.index()); + } + + private Builder override(Builder __superior) { + if (__superior.name != null) + name(__superior.name); + if (__superior.index != null) + index(__superior.index); + return this; + } + + public Builder name(String __value) { + if (__value == null) throw new IllegalArgumentException("Null value is not allowed."); + name = __value; + return this; + } + + + public Builder index(int __value) { + index = __value; + return this; + } + + private Builder index(String __value) { + return index(Integer.valueOf(__value)); + } + } + + private final StringNode name; + private final IntegerNode index; + + public Inner1(Builder builder) { + this(builder, true); + } + + private Inner1(Builder builder, boolean throwIfUninitialized) { + if (throwIfUninitialized && ! builder.__uninitialized.isEmpty()) + throw new IllegalArgumentException("The following builder parameters for " + + "allfeatures.struct_of_struct.inner1 must be initialized: " + builder.__uninitialized); + + name = (builder.name == null) ? + new StringNode("inner1") : new StringNode(builder.name); + index = (builder.index == null) ? + new IntegerNode(1) : new IntegerNode(builder.index); + } + + /** + * @return allfeatures.struct_of_struct.inner1.name + */ + public String name() { + return name.value(); + } + + /** + * @return allfeatures.struct_of_struct.inner1.index + */ + public int index() { + return index.value(); + } + + private ChangesRequiringRestart getChangesRequiringRestart(Inner1 newConfig) { + ChangesRequiringRestart changes = new ChangesRequiringRestart("inner1"); + return changes; + } + } + } + + /** + * This class represents allfeatures.myArray[] + */ + public final static class MyArray extends InnerNode { + + public static class Builder implements ConfigBuilder { + private Set<String> __uninitialized = new HashSet<String>(Arrays.asList( + "refVal" + )); + + private Integer intVal = null; + public List<String> stringVal = new ArrayList<>(); + private EnumVal.Enum enumVal = null; + private String refVal = null; + public List<AnotherArray.Builder> anotherArray = new ArrayList<>(); + + public Builder() { } + + public Builder(MyArray config) { + intVal(config.intVal()); + stringVal(config.stringVal()); + enumVal(config.enumVal()); + refVal(config.refVal()); + for (AnotherArray a : config.anotherArray()) { + anotherArray(new AnotherArray.Builder(a)); + } + } + + private Builder override(Builder __superior) { + if (__superior.intVal != null) + intVal(__superior.intVal); + if (!__superior.stringVal.isEmpty()) + stringVal.addAll(__superior.stringVal); + if (__superior.enumVal != null) + enumVal(__superior.enumVal); + if (__superior.refVal != null) + refVal(__superior.refVal); + if (!__superior.anotherArray.isEmpty()) + anotherArray.addAll(__superior.anotherArray); + return this; + } + + public Builder intVal(int __value) { + intVal = __value; + return this; + } + + private Builder intVal(String __value) { + return intVal(Integer.valueOf(__value)); + } + + public Builder stringVal(String __value) { + stringVal.add(__value); + return this; + } + + public Builder stringVal(Collection<String> __values) { + stringVal.addAll(__values); + return this; + } + + public Builder enumVal(EnumVal.Enum __value) { + if (__value == null) throw new IllegalArgumentException("Null value is not allowed."); + enumVal = __value; + return this; + } + + private Builder enumVal(String __value) { + return enumVal(EnumVal.Enum.valueOf(__value)); + } + + public Builder refVal(String __value) { + if (__value == null) throw new IllegalArgumentException("Null value is not allowed."); + refVal = __value; + __uninitialized.remove("refVal"); + return this; + } + + + /** + * Add the given builder to this builder's list of AnotherArray builders + * @param __builder a builder + * @return this builder + */ + public Builder anotherArray(AnotherArray.Builder __builder) { + anotherArray.add(__builder); + return this; + } + + /** + * Set the given list as this builder's list of AnotherArray builders + * @param __builders a list of builders + * @return this builder + */ + public Builder anotherArray(List<AnotherArray.Builder> __builders) { + anotherArray = __builders; + return this; + } + } + + private final IntegerNode intVal; + private final LeafNodeVector<String, StringNode> stringVal; + private final EnumVal enumVal; + private final ReferenceNode refVal; + private final InnerNodeVector<AnotherArray> anotherArray; + + public MyArray(Builder builder) { + this(builder, true); + } + + private MyArray(Builder builder, boolean throwIfUninitialized) { + if (throwIfUninitialized && ! builder.__uninitialized.isEmpty()) + throw new IllegalArgumentException("The following builder parameters for " + + "allfeatures.myArray[] must be initialized: " + builder.__uninitialized); + + intVal = (builder.intVal == null) ? + new IntegerNode(14) : new IntegerNode(builder.intVal); + stringVal = new LeafNodeVector<>(builder.stringVal, new StringNode()); + enumVal = (builder.enumVal == null) ? + new EnumVal(EnumVal.TYPE) : new EnumVal(builder.enumVal); + refVal = (builder.refVal == null) ? + new ReferenceNode() : new ReferenceNode(builder.refVal); + anotherArray = AnotherArray.createVector(builder.anotherArray); + } + + /** + * @return allfeatures.myArray[].intVal + */ + public int intVal() { + return intVal.value(); + } + + /** + * @return allfeatures.myArray[].stringVal[] + */ + public List<String> stringVal() { + return stringVal.asList(); + } + + /** + * @param i the index of the value to return + * @return allfeatures.myArray[].stringVal[] + */ + public String stringVal(int i) { + return stringVal.get(i).value(); + } + + /** + * @return allfeatures.myArray[].enumVal + */ + public EnumVal.Enum enumVal() { + return enumVal.value(); + } + + /** + * @return allfeatures.myArray[].refVal + */ + public String refVal() { + return refVal.value(); + } + + /** + * @return allfeatures.myArray[].anotherArray[] + */ + public List<AnotherArray> anotherArray() { + return anotherArray; + } + + /** + * @param i the index of the value to return + * @return allfeatures.myArray[].anotherArray[] + */ + public AnotherArray anotherArray(int i) { + return anotherArray.get(i); + } + + private ChangesRequiringRestart getChangesRequiringRestart(MyArray newConfig) { + ChangesRequiringRestart changes = new ChangesRequiringRestart("myArray"); + return changes; + } + + private static InnerNodeVector<MyArray> createVector(List<Builder> builders) { + List<MyArray> elems = new ArrayList<>(); + for (Builder b : builders) { + elems.add(new MyArray(b)); + } + return new InnerNodeVector<MyArray>(elems); + } + + /** + * This class represents allfeatures.myArray[].enumVal + */ + public final static class EnumVal extends EnumNode<EnumVal.Enum> { + + public EnumVal(){ + this.value = null; + } + + public EnumVal(Enum enumValue) { + super(enumValue != null); + this.value = enumValue; + } + + public enum Enum {INNER, ENUM, TYPE} + public final static Enum INNER = Enum.INNER; + public final static Enum ENUM = Enum.ENUM; + public final static Enum TYPE = Enum.TYPE; + + @Override + protected boolean doSetValue(@NonNull String name) { + try { + value = Enum.valueOf(name); + return true; + } catch (IllegalArgumentException e) { + } + return false; + } + } + + /** + * This class represents allfeatures.myArray[].anotherArray[] + */ + public final static class AnotherArray extends InnerNode { + + public static class Builder implements ConfigBuilder { + private Set<String> __uninitialized = new HashSet<String>(); + + private Integer foo = null; + + public Builder() { } + + public Builder(AnotherArray config) { + foo(config.foo()); + } + + private Builder override(Builder __superior) { + if (__superior.foo != null) + foo(__superior.foo); + return this; + } + + public Builder foo(int __value) { + foo = __value; + return this; + } + + private Builder foo(String __value) { + return foo(Integer.valueOf(__value)); + } + } + + private final IntegerNode foo; + + public AnotherArray(Builder builder) { + this(builder, true); + } + + private AnotherArray(Builder builder, boolean throwIfUninitialized) { + if (throwIfUninitialized && ! builder.__uninitialized.isEmpty()) + throw new IllegalArgumentException("The following builder parameters for " + + "allfeatures.myArray[].anotherArray[] must be initialized: " + builder.__uninitialized); + + foo = (builder.foo == null) ? + new IntegerNode(-4) : new IntegerNode(builder.foo); + } + + /** + * @return allfeatures.myArray[].anotherArray[].foo + */ + public int foo() { + return foo.value(); + } + + private ChangesRequiringRestart getChangesRequiringRestart(AnotherArray newConfig) { + ChangesRequiringRestart changes = new ChangesRequiringRestart("anotherArray"); + return changes; + } + + private static InnerNodeVector<AnotherArray> createVector(List<Builder> builders) { + List<AnotherArray> elems = new ArrayList<>(); + for (Builder b : builders) { + elems.add(new AnotherArray(b)); + } + return new InnerNodeVector<AnotherArray>(elems); + } + } + } + + /** + * This class represents allfeatures.myMap{} + */ + public final static class MyMap extends InnerNode { + + public static class Builder implements ConfigBuilder { + private Set<String> __uninitialized = new HashSet<String>(Arrays.asList( + "refVal" + )); + + private Integer intVal = null; + public List<String> stringVal = new ArrayList<>(); + private EnumVal.Enum enumVal = null; + private String refVal = null; + public List<AnotherArray.Builder> anotherArray = new ArrayList<>(); + + public Builder() { } + + public Builder(MyMap config) { + intVal(config.intVal()); + stringVal(config.stringVal()); + enumVal(config.enumVal()); + refVal(config.refVal()); + for (AnotherArray a : config.anotherArray()) { + anotherArray(new AnotherArray.Builder(a)); + } + } + + private Builder override(Builder __superior) { + if (__superior.intVal != null) + intVal(__superior.intVal); + if (!__superior.stringVal.isEmpty()) + stringVal.addAll(__superior.stringVal); + if (__superior.enumVal != null) + enumVal(__superior.enumVal); + if (__superior.refVal != null) + refVal(__superior.refVal); + if (!__superior.anotherArray.isEmpty()) + anotherArray.addAll(__superior.anotherArray); + return this; + } + + public Builder intVal(int __value) { + intVal = __value; + return this; + } + + private Builder intVal(String __value) { + return intVal(Integer.valueOf(__value)); + } + + public Builder stringVal(String __value) { + stringVal.add(__value); + return this; + } + + public Builder stringVal(Collection<String> __values) { + stringVal.addAll(__values); + return this; + } + + public Builder enumVal(EnumVal.Enum __value) { + if (__value == null) throw new IllegalArgumentException("Null value is not allowed."); + enumVal = __value; + return this; + } + + private Builder enumVal(String __value) { + return enumVal(EnumVal.Enum.valueOf(__value)); + } + + public Builder refVal(String __value) { + if (__value == null) throw new IllegalArgumentException("Null value is not allowed."); + refVal = __value; + __uninitialized.remove("refVal"); + return this; + } + + + /** + * Add the given builder to this builder's list of AnotherArray builders + * @param __builder a builder + * @return this builder + */ + public Builder anotherArray(AnotherArray.Builder __builder) { + anotherArray.add(__builder); + return this; + } + + /** + * Set the given list as this builder's list of AnotherArray builders + * @param __builders a list of builders + * @return this builder + */ + public Builder anotherArray(List<AnotherArray.Builder> __builders) { + anotherArray = __builders; + return this; + } + } + + private final IntegerNode intVal; + private final LeafNodeVector<String, StringNode> stringVal; + private final EnumVal enumVal; + private final ReferenceNode refVal; + private final InnerNodeVector<AnotherArray> anotherArray; + + public MyMap(Builder builder) { + this(builder, true); + } + + private MyMap(Builder builder, boolean throwIfUninitialized) { + if (throwIfUninitialized && ! builder.__uninitialized.isEmpty()) + throw new IllegalArgumentException("The following builder parameters for " + + "allfeatures.myMap{} must be initialized: " + builder.__uninitialized); + + intVal = (builder.intVal == null) ? + new IntegerNode(15) : new IntegerNode(builder.intVal); + stringVal = new LeafNodeVector<>(builder.stringVal, new StringNode()); + enumVal = (builder.enumVal == null) ? + new EnumVal(EnumVal.ENUM) : new EnumVal(builder.enumVal); + refVal = (builder.refVal == null) ? + new ReferenceNode() : new ReferenceNode(builder.refVal); + anotherArray = AnotherArray.createVector(builder.anotherArray); + } + + /** + * @return allfeatures.myMap{}.intVal + */ + public int intVal() { + return intVal.value(); + } + + /** + * @return allfeatures.myMap{}.stringVal[] + */ + public List<String> stringVal() { + return stringVal.asList(); + } + + /** + * @param i the index of the value to return + * @return allfeatures.myMap{}.stringVal[] + */ + public String stringVal(int i) { + return stringVal.get(i).value(); + } + + /** + * @return allfeatures.myMap{}.enumVal + */ + public EnumVal.Enum enumVal() { + return enumVal.value(); + } + + /** + * @return allfeatures.myMap{}.refVal + */ + public String refVal() { + return refVal.value(); + } + + /** + * @return allfeatures.myMap{}.anotherArray[] + */ + public List<AnotherArray> anotherArray() { + return anotherArray; + } + + /** + * @param i the index of the value to return + * @return allfeatures.myMap{}.anotherArray[] + */ + public AnotherArray anotherArray(int i) { + return anotherArray.get(i); + } + + private ChangesRequiringRestart getChangesRequiringRestart(MyMap newConfig) { + ChangesRequiringRestart changes = new ChangesRequiringRestart("myMap"); + return changes; + } + + private static Map<String, MyMap> createMap(Map<String, Builder> builders) { + Map<String, MyMap> ret = new LinkedHashMap<>(); + for(String key : builders.keySet()) { + ret.put(key, new MyMap(builders.get(key))); + } + return Collections.unmodifiableMap(ret); + } + + /** + * This class represents allfeatures.myMap{}.enumVal + */ + public final static class EnumVal extends EnumNode<EnumVal.Enum> { + + public EnumVal(){ + this.value = null; + } + + public EnumVal(Enum enumValue) { + super(enumValue != null); + this.value = enumValue; + } + + public enum Enum {INNER, ENUM, TYPE} + public final static Enum INNER = Enum.INNER; + public final static Enum ENUM = Enum.ENUM; + public final static Enum TYPE = Enum.TYPE; + + @Override + protected boolean doSetValue(@NonNull String name) { + try { + value = Enum.valueOf(name); + return true; + } catch (IllegalArgumentException e) { + } + return false; + } + } + + /** + * This class represents allfeatures.myMap{}.anotherArray[] + */ + public final static class AnotherArray extends InnerNode { + + public static class Builder implements ConfigBuilder { + private Set<String> __uninitialized = new HashSet<String>(); + + private Integer foo = null; + + public Builder() { } + + public Builder(AnotherArray config) { + foo(config.foo()); + } + + private Builder override(Builder __superior) { + if (__superior.foo != null) + foo(__superior.foo); + return this; + } + + public Builder foo(int __value) { + foo = __value; + return this; + } + + private Builder foo(String __value) { + return foo(Integer.valueOf(__value)); + } + } + + private final IntegerNode foo; + + public AnotherArray(Builder builder) { + this(builder, true); + } + + private AnotherArray(Builder builder, boolean throwIfUninitialized) { + if (throwIfUninitialized && ! builder.__uninitialized.isEmpty()) + throw new IllegalArgumentException("The following builder parameters for " + + "allfeatures.myMap{}.anotherArray[] must be initialized: " + builder.__uninitialized); + + foo = (builder.foo == null) ? + new IntegerNode(-5) : new IntegerNode(builder.foo); + } + + /** + * @return allfeatures.myMap{}.anotherArray[].foo + */ + public int foo() { + return foo.value(); + } + + private ChangesRequiringRestart getChangesRequiringRestart(AnotherArray newConfig) { + ChangesRequiringRestart changes = new ChangesRequiringRestart("anotherArray"); + return changes; + } + + private static InnerNodeVector<AnotherArray> createVector(List<Builder> builders) { + List<AnotherArray> elems = new ArrayList<>(); + for (Builder b : builders) { + elems.add(new AnotherArray(b)); + } + return new InnerNodeVector<AnotherArray>(elems); + } + } + } + +} diff --git a/configgen/src/test/scala/com/yahoo/config/codegen/JavaClassBuilderTest.scala b/configgen/src/test/scala/com/yahoo/config/codegen/JavaClassBuilderTest.scala deleted file mode 100644 index c1a5eb2dd6a..00000000000 --- a/configgen/src/test/scala/com/yahoo/config/codegen/JavaClassBuilderTest.scala +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.config.codegen - -import org.junit.Assert.assertThat -import org.junit.Assert.assertTrue -import org.hamcrest.CoreMatchers.is -import java.io.StringReader -import ConfiggenUtil.createClassName -import JavaClassBuilder.createUniqueSymbol -import org.junit.{Ignore, Test} - -/** - * @author gjoranv - */ -class JavaClassBuilderTest { - - @Ignore - @Test - def visual_inspection_of_generated_class() { - val testDefinition = - """version=1 - |namespace=test - |p path - |pathArr[] path - |f file - |fileArr[] file - |i int default=0 - |# A long value - |l long default=0 - |s string default="" - |b bool - |# An enum value - |e enum {A, B, C} - |intArr[] int - |boolArr[] bool - |enumArr[] enum {FOO, BAR} - |intMap{} int - |# A struct - |# with multi-line - |# comment and "quotes". - |myStruct.i int - |myStruct.s string - |# An inner array - |myArr[].i int - |myArr[].newStruct.s string - |myArr[].newStruct.b bool - |myArr[].intArr[] int - |# An inner map - |myMap{}.i int - |myMap{}.newStruct.s string - |myMap{}.newStruct.b bool - |myMap{}.intArr[] int - |intMap{} int - |""".stripMargin - - val parser = new DefParser("test", new StringReader(testDefinition)) - val root = parser.getTree - val builder = new JavaClassBuilder(root, parser.getNormalizedDefinition, null, null) - val configClass = builder.getConfigClass("TestConfig") - print(configClass) - } - - @Test - def testCreateUniqueSymbol() { - val testDefinition = - """version=1 - |namespace=test - |m int - |n int - """.stripMargin - val root = new DefParser("test", new StringReader(testDefinition)).getTree - - assertThat(createUniqueSymbol(root, "foo"), is("f")) - assertThat(createUniqueSymbol(root, "name"), is("na")) - assertTrue(createUniqueSymbol(root, "m").startsWith(ReservedWords.INTERNAL_PREFIX + "m")) - - // The basis string is not a legal return value, even if unique, to avoid multiple symbols - // with the same name if the same basis string is given twice. - assertTrue(createUniqueSymbol(root, "my").startsWith(ReservedWords.INTERNAL_PREFIX + "my")) - } - - @Test - def testCreateClassName() { - assertThat(createClassName("simple"), is("SimpleConfig")) - assertThat(createClassName("a"), is("AConfig")) - assertThat(createClassName("a-b-c"), is("ABCConfig")) - assertThat(createClassName("a-1-2b"), is("A12bConfig")) - assertThat(createClassName("my-app"), is("MyAppConfig")) - assertThat(createClassName("MyApp"), is("MyAppConfig")) - } - - @Test(expected=classOf[CodegenRuntimeException]) - def testIllegalClassName() { - createClassName("+illegal") - } - -} |