diff options
Diffstat (limited to 'configgen/src/main/scala/com/yahoo/config/codegen')
3 files changed, 0 insertions, 959 deletions
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 - } - -} |