summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Musum <musum@yahooinc.com>2023-09-07 12:25:37 +0200
committerHarald Musum <musum@yahooinc.com>2023-09-07 12:25:37 +0200
commitcac205f35b56d0bd584013b79b88a6635dee5ab4 (patch)
treebcf8461b3091c44ae78e5d9355a2109ba7d6a77a
parentdaba552c567f1fcb9e300ae65825c1d97cedbb5e (diff)
Support optional 'path' config type
Initial work needed for this to work (config definition and config library)
-rw-r--r--config-lib/abi-spec.json23
-rw-r--r--config-lib/src/main/java/com/yahoo/config/LeafNodeMaps.java9
-rw-r--r--config-lib/src/main/java/com/yahoo/config/LeafNodeVector.java8
-rw-r--r--config-lib/src/main/java/com/yahoo/config/OptionalPathNode.java84
-rw-r--r--config/src/main/java/com/yahoo/vespa/config/ConfigPayloadApplier.java20
-rw-r--r--config/src/test/java/com/yahoo/config/subscription/CfgConfigPayloadBuilderTest.java1
-rw-r--r--config/src/test/java/com/yahoo/config/subscription/ConfigInstancePayloadTest.java2
-rw-r--r--config/src/test/java/com/yahoo/config/subscription/FunctionTest.java4
-rw-r--r--config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionBuilderTest.java6
-rw-r--r--config/src/test/resources/configs/def-files/function-test.def2
-rw-r--r--config/src/test/resources/configs/function-test/variableaccess.txt3
-rw-r--r--configgen/src/main/java/com/yahoo/config/codegen/BuilderGenerator.java45
-rw-r--r--configgen/src/main/java/com/yahoo/config/codegen/ConfigGenerator.java11
-rw-r--r--configgen/src/main/java/com/yahoo/config/codegen/DefLine.java14
-rw-r--r--configgen/src/main/java/com/yahoo/config/codegen/LeafCNode.java12
-rw-r--r--configgen/src/test/java/com/yahoo/config/codegen/DefLineParsingTest.java21
-rw-r--r--configgen/src/test/java/com/yahoo/config/codegen/DefParserTest.java4
-rw-r--r--configgen/src/test/java/com/yahoo/config/codegen/JavaClassBuilderTest.java2
-rw-r--r--configgen/src/test/java/com/yahoo/config/codegen/NormalizedDefinitionTest.java2
-rw-r--r--configgen/src/test/resources/allfeatures.reference28
-rw-r--r--configgen/src/test/resources/configgen.allfeatures.def1
21 files changed, 285 insertions, 17 deletions
diff --git a/config-lib/abi-spec.json b/config-lib/abi-spec.json
index caae2ebf1cf..07e61c63237 100644
--- a/config-lib/abi-spec.json
+++ b/config-lib/abi-spec.json
@@ -273,6 +273,7 @@
"public static java.util.Map asNodeMap(java.util.Map, com.yahoo.config.LeafNode)",
"public static java.util.Map asFileNodeMap(java.util.Map)",
"public static java.util.Map asPathNodeMap(java.util.Map)",
+ "public static java.util.Map asOptionalPathNodeMap(java.util.Map)",
"public static java.util.Map asUrlNodeMap(java.util.Map)",
"public static java.util.Map asModelNodeMap(java.util.Map)"
],
@@ -289,6 +290,7 @@
"public java.util.List asList()",
"public static com.yahoo.config.LeafNodeVector createFileNodeVector(java.util.Collection)",
"public static com.yahoo.config.LeafNodeVector createPathNodeVector(java.util.Collection)",
+ "public static com.yahoo.config.LeafNodeVector createOptionalPathNodeVector(java.util.Collection)",
"public static com.yahoo.config.LeafNodeVector createUrlNodeVector(java.util.Collection)",
"public static com.yahoo.config.LeafNodeVector createModelNodeVector(java.util.Collection)"
],
@@ -420,6 +422,27 @@
"protected final java.util.ArrayList vector"
]
},
+ "com.yahoo.config.OptionalPathNode" : {
+ "superClass" : "com.yahoo.config.LeafNode",
+ "interfaces" : [ ],
+ "attributes" : [
+ "public"
+ ],
+ "methods" : [
+ "public void <init>()",
+ "public void <init>(com.yahoo.config.FileReference)",
+ "public void <init>(java.util.Optional)",
+ "public java.util.Optional value()",
+ "public java.lang.String getValue()",
+ "public java.lang.String toString()",
+ "protected boolean doSetValue(java.lang.String)",
+ "public java.util.Optional getFileReference()",
+ "public static java.util.List toFileReferences(java.util.List)",
+ "public static java.util.Map toFileReferenceMap(java.util.Map)",
+ "public bridge synthetic java.lang.Object value()"
+ ],
+ "fields" : [ ]
+ },
"com.yahoo.config.PathNode" : {
"superClass" : "com.yahoo.config.LeafNode",
"interfaces" : [ ],
diff --git a/config-lib/src/main/java/com/yahoo/config/LeafNodeMaps.java b/config-lib/src/main/java/com/yahoo/config/LeafNodeMaps.java
index 82663fa8bfd..214d8c52caa 100644
--- a/config-lib/src/main/java/com/yahoo/config/LeafNodeMaps.java
+++ b/config-lib/src/main/java/com/yahoo/config/LeafNodeMaps.java
@@ -4,6 +4,7 @@ package com.yahoo.config;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.Optional;
import java.util.stream.Collectors;
/**
@@ -60,6 +61,14 @@ public class LeafNodeMaps {
return Collections.unmodifiableMap(pathNodeMap);
}
+ public static Map<String, OptionalPathNode> asOptionalPathNodeMap(Map<String, Optional<FileReference>> fileReferenceMap) {
+ Map<String, OptionalPathNode> pathNodeMap = new LinkedHashMap<>();
+ for (Map.Entry<String, Optional<FileReference>> e : fileReferenceMap.entrySet()) {
+ pathNodeMap.put(e.getKey(), new OptionalPathNode(e.getValue()));
+ }
+ return Collections.unmodifiableMap(pathNodeMap);
+ }
+
public static Map<String, UrlNode> asUrlNodeMap(Map<String, UrlReference> urlReferenceMap) {
return Collections.unmodifiableMap(
urlReferenceMap.entrySet().stream().collect(
diff --git a/config-lib/src/main/java/com/yahoo/config/LeafNodeVector.java b/config-lib/src/main/java/com/yahoo/config/LeafNodeVector.java
index a4fea95088d..cfb8cd4eebd 100644
--- a/config-lib/src/main/java/com/yahoo/config/LeafNodeVector.java
+++ b/config-lib/src/main/java/com/yahoo/config/LeafNodeVector.java
@@ -8,6 +8,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
/**
* A vector of leaf nodes.
@@ -71,6 +72,13 @@ public class LeafNodeVector<REAL, NODE extends LeafNode<REAL>> extends NodeVecto
return new LeafNodeVector<>(paths, new PathNode());
}
+ public static LeafNodeVector<Optional<Path>, OptionalPathNode> createOptionalPathNodeVector(Collection<Optional<FileReference>> values) {
+ List<Optional<Path>> paths = new ArrayList<>();
+ for (Optional<FileReference> fileReference : values)
+ paths.add(fileReference.map(reference -> Paths.get(reference.value())));
+ return new LeafNodeVector<>(paths, new OptionalPathNode());
+ }
+
public static LeafNodeVector<File, UrlNode> createUrlNodeVector(Collection<UrlReference> values) {
List<File> files = new ArrayList<>();
for (UrlReference urlReference : values)
diff --git a/config-lib/src/main/java/com/yahoo/config/OptionalPathNode.java b/config-lib/src/main/java/com/yahoo/config/OptionalPathNode.java
new file mode 100644
index 00000000000..8a6414d798f
--- /dev/null
+++ b/config-lib/src/main/java/com/yahoo/config/OptionalPathNode.java
@@ -0,0 +1,84 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.config;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Represents a 'path' in a {@link ConfigInstance}, usually a filename, can be optional
+ *
+ * @author hmusum
+ */
+public class OptionalPathNode extends LeafNode<Optional<Path>> {
+
+ private final Optional<FileReference> fileReference;
+
+ public OptionalPathNode() {
+ fileReference = Optional.empty();
+ }
+
+ public OptionalPathNode(FileReference fileReference) {
+ super(true);
+ this.value = Optional.of(Path.of(fileReference.value()));
+ this.fileReference = Optional.of(fileReference);
+ }
+
+ public OptionalPathNode(Optional<FileReference> fileReference) {
+ super(true);
+ this.value = fileReference.map(reference -> Path.of(reference.value()));
+ this.fileReference = fileReference;
+ }
+
+ public Optional<Path> value() {
+ return value;
+ }
+
+ @Override
+ public String getValue() {
+ return value.toString();
+ }
+
+ @Override
+ public String toString() {
+ return (value.isEmpty()) ? "(empty)" : '"' + value.get().toString() + '"';
+ }
+
+ @Override
+ protected boolean doSetValue(String stringVal) {
+ throw new UnsupportedOperationException("doSetValue should not be necessary anymore!");
+ }
+
+ @Override
+ void serialize(String name, Serializer serializer) {
+ value.ifPresent(path -> serializer.serialize(name, path.toString()));
+ }
+
+ @Override
+ void serialize(Serializer serializer) {
+ value.ifPresent(path -> serializer.serialize(path.toString()));
+ }
+
+ public Optional<FileReference> getFileReference() {
+ return fileReference;
+ }
+
+ public static List<Optional<FileReference>> toFileReferences(List<OptionalPathNode> pathNodes) {
+ List<Optional<FileReference>> fileReferences = new ArrayList<>();
+ for (OptionalPathNode pathNode : pathNodes)
+ fileReferences.add(pathNode.getFileReference());
+ return fileReferences;
+ }
+
+ public static Map<String, Optional<FileReference>> toFileReferenceMap(Map<String, OptionalPathNode> map) {
+ Map<String, Optional<FileReference>> ret = new LinkedHashMap<>();
+ for (Map.Entry<String, OptionalPathNode> e : map.entrySet()) {
+ ret.put(e.getKey(), e.getValue().getFileReference());
+ }
+ return ret;
+ }
+
+}
diff --git a/config/src/main/java/com/yahoo/vespa/config/ConfigPayloadApplier.java b/config/src/main/java/com/yahoo/vespa/config/ConfigPayloadApplier.java
index e86b13b2c98..0dbc40a246c 100644
--- a/config/src/main/java/com/yahoo/vespa/config/ConfigPayloadApplier.java
+++ b/config/src/main/java/com/yahoo/vespa/config/ConfigPayloadApplier.java
@@ -21,6 +21,7 @@ import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.logging.Logger;
@@ -213,7 +214,10 @@ public class ConfigPayloadApplier<T extends ConfigInstance.Builder> {
Inspector value = (Inspector)rawValue;
if (isPathField(builder, methodName))
return resolvePath(value.asString());
- else if (isUrlField(builder, methodName))
+ if (isOptionalPathField(builder, methodName)) {
+ String v = value.asString();
+ return resolvePath(v.isEmpty() ? Optional.empty() : Optional.of(v));
+ } else if (isUrlField(builder, methodName))
return value.asString().isEmpty() ? "" : resolveUrl(value.asString());
else if (isModelField(builder, methodName))
return value.asString().isEmpty() ? "" : resolveModel(value.asString());
@@ -234,6 +238,10 @@ public class ConfigPayloadApplier<T extends ConfigInstance.Builder> {
return new FileReference(path.toString());
}
+ private Optional<FileReference> resolvePath(Optional<String> value) {
+ return value.isEmpty() ? Optional.empty() : Optional.of(resolvePath(value.get()));
+ }
+
private UrlReference resolveUrl(String url) {
if ( ! isClientside()) return new UrlReference(url);
File file = urlDownloader.waitFor(new UrlReference(url), 60 * 60);
@@ -319,6 +327,16 @@ public class ConfigPayloadApplier<T extends ConfigInstance.Builder> {
return isFieldType(pathFieldSet, builder, methodName, FileReference.class);
}
+ /**
+ * Checks if this field is of type 'path', in which
+ * case some special handling might be needed. Caches the result.
+ */
+ private final Set<String> optionalPathFieldSet = new HashSet<>();
+ private boolean isOptionalPathField(Object builder, String methodName) {
+ // Paths are stored as Optional<FileReference> in Builder.
+ return isFieldType(optionalPathFieldSet, builder, methodName, Optional.class);
+ }
+
private final Set<String> urlFieldSet = new HashSet<>();
private boolean isUrlField(Object builder, String methodName) {
// Urls are stored as UrlReference in Builder.
diff --git a/config/src/test/java/com/yahoo/config/subscription/CfgConfigPayloadBuilderTest.java b/config/src/test/java/com/yahoo/config/subscription/CfgConfigPayloadBuilderTest.java
index a6273ad5ccb..0e50be83e7a 100644
--- a/config/src/test/java/com/yahoo/config/subscription/CfgConfigPayloadBuilderTest.java
+++ b/config/src/test/java/com/yahoo/config/subscription/CfgConfigPayloadBuilderTest.java
@@ -34,6 +34,7 @@ public class CfgConfigPayloadBuilderTest {
" 'parent:'",
" ],",
" 'pathVal': 'src/test/resources/configs/def-files/function-test.def',",
+ " 'optionalPathVal': 'src/test/resources/configs/def-files/function-test.def',",
" 'string_val': 'foo',",
" 'myStructMap': {",
" 'one': {",
diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigInstancePayloadTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigInstancePayloadTest.java
index c656bfe1a60..f09462eb634 100644
--- a/config/src/test/java/com/yahoo/config/subscription/ConfigInstancePayloadTest.java
+++ b/config/src/test/java/com/yahoo/config/subscription/ConfigInstancePayloadTest.java
@@ -13,6 +13,7 @@ import org.junit.Test;
import java.io.File;
import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
import static com.yahoo.foo.FunctionTestConfig.*;
import static org.junit.Assert.assertNotNull;
@@ -46,6 +47,7 @@ public class ConfigInstancePayloadTest {
refwithdef(":parent:").
fileVal("etc").
pathVal(FileReference.mockFileReferenceForUnitTesting(new File("src/test/resources/configs/def-files/function-test.def"))).
+ optionalPathVal(Optional.of(FileReference.mockFileReferenceForUnitTesting(new File("src/test/resources/configs/def-files/function-test.def")))).
boolarr(false).
longarr(9223372036854775807L).
longarr(-9223372036854775808L).
diff --git a/config/src/test/java/com/yahoo/config/subscription/FunctionTest.java b/config/src/test/java/com/yahoo/config/subscription/FunctionTest.java
index 8656c0e945f..7a3b0e437f2 100644
--- a/config/src/test/java/com/yahoo/config/subscription/FunctionTest.java
+++ b/config/src/test/java/com/yahoo/config/subscription/FunctionTest.java
@@ -30,7 +30,7 @@ public class FunctionTest {
public static final String PATH = "src/test/resources/configs/function-test/";
private FunctionTestConfig config;
- private ConfigSourceSet sourceSet = new ConfigSourceSet("function-test");
+ private final ConfigSourceSet sourceSet = new ConfigSourceSet("function-test");
public void configure(FunctionTestConfig config, ConfigSourceSet sourceSet) {
this.config = config;
@@ -222,6 +222,8 @@ public class FunctionTest {
assertEquals(":parent", config.refarr(1));
assertEquals("parent:", config.refarr(2));
assertEquals("bin", config.fileArr(0).value());
+ assertEquals("function-test.def", config.pathVal().toFile().getName());
+ assertEquals("function-test.def", config.optionalPathVal().get().toFile().getName()); // TODO
assertEquals("pom.xml", config.pathArr(0).toString());
assertEquals("pom.xml", config.pathMap("one").toString());
diff --git a/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionBuilderTest.java b/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionBuilderTest.java
index a9f09951d7e..fa1ec815047 100644
--- a/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionBuilderTest.java
+++ b/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionBuilderTest.java
@@ -77,10 +77,14 @@ public class ConfigDefinitionBuilderTest {
assertThat(def.getFileDefs().size(), is(1));
assertNotNull(def.getFileDefs().get("fileVal"));
- assertThat(def.getArrayDefs().size(), is(9));
+ // An array does not have to have any elements set
+ assertThat(def.getArrayDefs().size(), is(10));
assertNotNull(def.getArrayDefs().get("boolarr"));
assertThat(def.getArrayDefs().get("boolarr").getTypeSpec().getType(), is("bool"));
+ assertNotNull(def.getArrayDefs().get("boolarrEmpty"));
+ assertThat(def.getArrayDefs().get("boolarrEmpty").getTypeSpec().getType(), is("bool"));
+
assertNotNull(def.getArrayDefs().get("enumarr"));
assertThat(def.getArrayDefs().get("enumarr").getTypeSpec().getType(), is("enum"));
assertThat(def.getArrayDefs().get("enumarr").getTypeSpec().getEnumVals().toString(), is("[ARRAY, VALUES]"));
diff --git a/config/src/test/resources/configs/def-files/function-test.def b/config/src/test/resources/configs/def-files/function-test.def
index 4c4cb6bf08b..b97713b18f3 100644
--- a/config/src/test/resources/configs/def-files/function-test.def
+++ b/config/src/test/resources/configs/def-files/function-test.def
@@ -42,8 +42,10 @@ refval reference
refwithdef reference default=":parent:"
fileVal file
pathVal path
+optionalPathVal path optional
boolarr[] bool
+boolarrEmpty[] bool
intarr[] int
longarr[] long
doublearr[] double
diff --git a/config/src/test/resources/configs/function-test/variableaccess.txt b/config/src/test/resources/configs/function-test/variableaccess.txt
index 997de21750d..8c2cadcdbbc 100644
--- a/config/src/test/resources/configs/function-test/variableaccess.txt
+++ b/config/src/test/resources/configs/function-test/variableaccess.txt
@@ -14,7 +14,8 @@ enumwithdef BAR2
refval :parent:
refwithdef ":parent:"
fileVal "etc"
-pathVal "pom.xml"
+pathVal "function-test.def"
+optionalPathVal "function-test.def"
boolarr[1]
boolarr[0] false
intarr[0]
diff --git a/configgen/src/main/java/com/yahoo/config/codegen/BuilderGenerator.java b/configgen/src/main/java/com/yahoo/config/codegen/BuilderGenerator.java
index 6cd344466e4..12469d7a3ef 100644
--- a/configgen/src/main/java/com/yahoo/config/codegen/BuilderGenerator.java
+++ b/configgen/src/main/java/com/yahoo/config/codegen/BuilderGenerator.java
@@ -4,10 +4,12 @@ package com.yahoo.config.codegen;
import com.yahoo.config.codegen.LeafCNode.FileLeaf;
import com.yahoo.config.codegen.LeafCNode.ModelLeaf;
import com.yahoo.config.codegen.LeafCNode.PathLeaf;
+import com.yahoo.config.codegen.LeafCNode.OptionalPathLeaf;
import com.yahoo.config.codegen.LeafCNode.UrlLeaf;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import java.util.stream.Collectors;
import static com.yahoo.config.codegen.ConfigGenerator.boxedDataType;
@@ -89,7 +91,9 @@ public class BuilderGenerator {
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)) {
+ if (child instanceof LeafCNode
+ && (!child.isArray && !child.isMap && ((LeafCNode) child).getDefaultValue() == null)
+ && (! (child instanceof OptionalPathLeaf))) {
scalarsWithoutDefault.add("\"" + child.getName() + "\"");
}
}
@@ -109,7 +113,11 @@ public class BuilderGenerator {
} 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());
+ String boxedBuilderType = boxedBuilderType((LeafCNode) node);
+ if (boxedBuilderType.startsWith("Optional<"))
+ return String.format("private %s %s = Optional.empty();", boxedBuilderType, node.getName());
+ else
+ return String.format("private %s %s = null;", boxedBuilderType, node.getName());
} else {
throw new IllegalStateException("Cannot produce builder field definition for node"); // Should not happen
}
@@ -207,6 +215,11 @@ public class BuilderGenerator {
private static String privateLeafNodeSetter(LeafCNode n) {
if ("String".equals(builderType(n)) || "FileReference".equals(builderType(n))) {
return "";
+ } else if ("Optional<FileReference>".equals(builderType(n))) {
+ return "\n\n" + //
+ "private Builder " + n.getName() + "(String " + INTERNAL_PREFIX + "value) {\n" + //
+ " return " + n.getName() + "(" + builderType(n) + ".of(" + INTERNAL_PREFIX + "value));\n" + //
+ "}";
} else {
return "\n\n" + //
"private Builder " + n.getName() + "(String " + INTERNAL_PREFIX + "value) {\n" + //
@@ -270,14 +283,24 @@ public class BuilderGenerator {
: "";
String bType = builderType(n);
- String stringSetter = "";
- if ( ! "String".equals(bType) && ! "FileReference".equals(bType) && ! "ModelReference".equals(bType)) {
+ String privateSetter = "";
+ if ( ! Set.of("String", "FileReference", "ModelReference", "Optional<FileReference>").contains(bType)) {
String type = boxedDataType(n);
if ("UrlReference".equals(bType))
type = bType;
- stringSetter = String.format("\nprivate Builder %s(String %svalue) {\n" +
- " return %s(%s.valueOf(%svalue));\n" + //
- "}", name, INTERNAL_PREFIX, name, type, INTERNAL_PREFIX);
+ //
+ privateSetter = String.format("""
+
+ private Builder %s(String %svalue) {
+ return %s(%s.valueOf(%svalue));
+ }""", name, INTERNAL_PREFIX, name, type, INTERNAL_PREFIX);
+ } else if ("Optional<FileReference>".equals(bType)) {
+ //
+ privateSetter = String.format("""
+
+ private Builder %s(FileReference %svalue) {
+ return %s(Optional.of(%svalue));
+ }""", name, INTERNAL_PREFIX, name, INTERNAL_PREFIX);
}
String getNullGuard = bType.equals(boxedBuilderType(n)) ? String.format(
@@ -286,7 +309,7 @@ public class BuilderGenerator {
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;
+ " return this;" + "\n}\n" + privateSetter;
}
}
@@ -307,6 +330,8 @@ public class BuilderGenerator {
return name + "(" + nodeClass(child) + ".toFileReferenceMap(config." + name + "));";
} else if (child instanceof PathLeaf) {
return name + "(config." + name + ".getFileReference());";
+ } else if (child instanceof OptionalPathLeaf) {
+ return name + "(config." + name + ".getFileReference());";
} else if (child instanceof UrlLeaf && isArray) {
return name + "(" + nodeClass(child) + ".toUrlReferences(config." + name + "));";
} else if (child instanceof UrlLeaf && isMap) {
@@ -408,6 +433,8 @@ public class BuilderGenerator {
return "String";
} else if (node instanceof PathLeaf) {
return "FileReference";
+ } else if (node instanceof OptionalPathLeaf) {
+ return "Optional<FileReference>";
} else if (node instanceof UrlLeaf) {
return "UrlReference";
} else if (node instanceof ModelLeaf) {
@@ -424,6 +451,8 @@ public class BuilderGenerator {
return "String";
} else if (node instanceof PathLeaf) {
return "FileReference";
+ } else if (node instanceof OptionalPathLeaf) {
+ return "Optional<FileReference>";
} else if (node instanceof UrlLeaf) {
return "UrlReference";
} else if (node instanceof ModelLeaf) {
diff --git a/configgen/src/main/java/com/yahoo/config/codegen/ConfigGenerator.java b/configgen/src/main/java/com/yahoo/config/codegen/ConfigGenerator.java
index cb10ffdc2be..903d8dc0865 100644
--- a/configgen/src/main/java/com/yahoo/config/codegen/ConfigGenerator.java
+++ b/configgen/src/main/java/com/yahoo/config/codegen/ConfigGenerator.java
@@ -7,6 +7,7 @@ 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.OptionalPathLeaf;
import com.yahoo.config.codegen.LeafCNode.PathLeaf;
import com.yahoo.config.codegen.LeafCNode.ReferenceLeaf;
import com.yahoo.config.codegen.LeafCNode.StringLeaf;
@@ -165,6 +166,8 @@ public class ConfigGenerator {
return name + " = LeafNodeVector.createFileNodeVector(builder." + name + ");";
} else if (child instanceof PathLeaf && isArray) {
return name + " = LeafNodeVector.createPathNodeVector(builder." + name + ");";
+ } else if (child instanceof OptionalPathLeaf && isArray) {
+ return name + " = LeafNodeVector.createOptionalPathNodeVector(builder." + name + ");";
} else if (child instanceof UrlLeaf && isArray) {
return name + " = LeafNodeVector.createUrlNodeVector(builder." + name + ");";
} else if (child instanceof ModelLeaf && isArray) {
@@ -175,6 +178,8 @@ public class ConfigGenerator {
return name + " = LeafNodeMaps.asFileNodeMap(builder." + name + ");";
} else if (child instanceof PathLeaf && isMap) {
return name + " = LeafNodeMaps.asPathNodeMap(builder." + name + ");";
+ } else if (child instanceof OptionalPathLeaf && isMap) {
+ return name + " = LeafNodeMaps.asOptionalPathNodeMap(builder." + name + ");";
} else if (child instanceof UrlLeaf && isMap) {
return name + " = LeafNodeMaps.asUrlNodeMap(builder." + name + ");";
} else if (child instanceof ModelLeaf && isMap) {
@@ -401,6 +406,8 @@ public class ConfigGenerator {
return "FileNode";
} else if (node instanceof PathLeaf) {
return "PathNode";
+ } else if (node instanceof OptionalPathLeaf) {
+ return "OptionalPathNode";
} else if (node instanceof UrlLeaf) {
return "UrlNode";
} else if (node instanceof ModelLeaf) {
@@ -431,6 +438,8 @@ public class ConfigGenerator {
return "FileReference";
} else if (node instanceof PathLeaf) {
return "Path";
+ } else if (node instanceof OptionalPathLeaf) {
+ return "Optional<Path>";
} else if (node instanceof UrlLeaf) {
return "File";
} else if (node instanceof ModelLeaf) {
@@ -456,6 +465,8 @@ public class ConfigGenerator {
return "Integer";
} else if (rawType.toLowerCase().equals(rawType)) {
return ConfiggenUtil.capitalize(rawType);
+ } else if (rawType.startsWith("Optional<")) {
+ return "Optional";
} else {
return rawType;
}
diff --git a/configgen/src/main/java/com/yahoo/config/codegen/DefLine.java b/configgen/src/main/java/com/yahoo/config/codegen/DefLine.java
index 385c7f1979e..d6bffe349a8 100644
--- a/configgen/src/main/java/com/yahoo/config/codegen/DefLine.java
+++ b/configgen/src/main/java/com/yahoo/config/codegen/DefLine.java
@@ -7,6 +7,7 @@ import java.util.regex.Pattern;
public class DefLine {
private final static Pattern defaultPattern = Pattern.compile("^\\s*default\\s*=\\s*(\\S+)");
+ private final static Pattern optionalPattern = Pattern.compile("^\\s*optional\\s*");
private final static Pattern rangePattern = Pattern.compile("^\\s*range\\s*=\\s*([\\(\\[].*?[\\)\\]])");
private final static Pattern restartPattern = Pattern.compile("^\\s*restart\\s*");
private final static Pattern wordPattern = Pattern.compile("\\S+");
@@ -21,6 +22,7 @@ public class DefLine {
private final Type type = new Type();
private DefaultValue defaultValue = null;
+ private boolean optional = false;
private String range = null;
private boolean restart = false;
@@ -74,6 +76,9 @@ public class DefLine {
}
public Type getType() {
+ if (optional && type.name.equals("path"))
+ type.name = "optionalPath";
+
return type;
}
@@ -89,6 +94,8 @@ public class DefLine {
return enumArray;
}
+ public boolean isOptional() { return optional; }
+
/**
* Special function that searches through s and returns the index
* of the first occurrence of " that is not escaped.
@@ -114,6 +121,7 @@ public class DefLine {
private int parseOptions(CharSequence string) {
Matcher defaultNullMatcher = defaultNullPattern.matcher(string);
Matcher defaultMatcher = defaultPattern.matcher(string);
+ Matcher optionalMatcher = optionalPattern.matcher(string);
Matcher rangeMatcher = rangePattern.matcher(string);
Matcher restartMatcher = restartPattern.matcher(string);
@@ -133,6 +141,12 @@ public class DefLine {
defaultValue = new DefaultValue(deflt, type);
}
return defaultMatcher.end();
+ } else if (optionalMatcher.find()) {
+ if ( ! type.name.equals("path"))
+ throw new IllegalArgumentException("optional can only be used for 'path'");
+ optional = true;
+ type.name = "optionalPath";
+ return optionalMatcher.end();
} else if (rangeMatcher.find()) {
range = rangeMatcher.group(1);
return rangeMatcher.end();
diff --git a/configgen/src/main/java/com/yahoo/config/codegen/LeafCNode.java b/configgen/src/main/java/com/yahoo/config/codegen/LeafCNode.java
index afd6acfbabf..c2470b0c703 100644
--- a/configgen/src/main/java/com/yahoo/config/codegen/LeafCNode.java
+++ b/configgen/src/main/java/com/yahoo/config/codegen/LeafCNode.java
@@ -26,6 +26,7 @@ public abstract class LeafCNode extends CNode {
case "reference" -> new ReferenceLeaf(parent, name);
case "file" -> new FileLeaf(parent, name);
case "path" -> new PathLeaf(parent, name);
+ case "optionalPath" -> new OptionalPathLeaf(parent, name);
case "enum" -> new EnumLeaf(parent, name, type.enumArray);
case "url" -> new UrlLeaf(parent, name);
case "model" -> new ModelLeaf(parent, name);
@@ -217,6 +218,17 @@ public abstract class LeafCNode extends CNode {
}
}
+ public static class OptionalPathLeaf extends NoClassLeafCNode {
+ OptionalPathLeaf(InnerCNode parent, String name) {
+ super(parent, name);
+ }
+
+ @Override
+ public String getType() {
+ return "optionalPath";
+ }
+ }
+
public static class UrlLeaf extends NoClassLeafCNode {
UrlLeaf(InnerCNode parent, String name) {
super(parent, name);
diff --git a/configgen/src/test/java/com/yahoo/config/codegen/DefLineParsingTest.java b/configgen/src/test/java/com/yahoo/config/codegen/DefLineParsingTest.java
index 0e2f6cc4d05..bb6b8eb64b4 100644
--- a/configgen/src/test/java/com/yahoo/config/codegen/DefLineParsingTest.java
+++ b/configgen/src/test/java/com/yahoo/config/codegen/DefLineParsingTest.java
@@ -1,6 +1,7 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.config.codegen;
+import java.util.Optional;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
@@ -246,4 +247,24 @@ public class DefLineParsingTest {
assertTrue(r1.getRestart());
}
+ @Test
+ void testParseOptionalPathWithDefault() {
+ DefLine l = new DefLine("pathWithDef path optional");
+
+ assertEquals("pathWithDef", l.getName());
+ assertNull(l.getDefault());
+ assertTrue(l.isOptional());
+ assertEquals("optionalPath", l.getType().getName());
+ }
+
+ @Test
+ void testParsPathWithDefault() {
+ DefLine l = new DefLine("pathWithDef path");
+
+ assertEquals("pathWithDef", l.getName());
+ assertNull(l.getDefault());
+ assertFalse(l.isOptional());
+ assertEquals("path", l.getType().getName());
+ }
+
}
diff --git a/configgen/src/test/java/com/yahoo/config/codegen/DefParserTest.java b/configgen/src/test/java/com/yahoo/config/codegen/DefParserTest.java
index 45d1f21763c..e5227282c05 100644
--- a/configgen/src/test/java/com/yahoo/config/codegen/DefParserTest.java
+++ b/configgen/src/test/java/com/yahoo/config/codegen/DefParserTest.java
@@ -28,7 +28,7 @@ public class DefParserTest {
CNode root = new DefParser("test", new FileReader(defFile)).getTree();
assertNotNull(root);
CNode[] children = root.getChildren();
- assertEquals(37, children.length);
+ assertEquals(38, children.length);
int numGrandChildren = 0;
int numGreatGrandChildren = 0;
@@ -70,7 +70,7 @@ public class DefParserTest {
void testMd5Sum() throws IOException {
File defFile = new File(DEF_NAME);
CNode root = new DefParser("test", new FileReader(defFile)).getTree();
- assertEquals("0501f9e2c4ecc8c283e100e0b1178ca4", root.defMd5);
+ assertEquals("ee37973499305fde315da46256e64b2e", root.defMd5);
}
@Test
diff --git a/configgen/src/test/java/com/yahoo/config/codegen/JavaClassBuilderTest.java b/configgen/src/test/java/com/yahoo/config/codegen/JavaClassBuilderTest.java
index 428576e340f..c3145c03fff 100644
--- a/configgen/src/test/java/com/yahoo/config/codegen/JavaClassBuilderTest.java
+++ b/configgen/src/test/java/com/yahoo/config/codegen/JavaClassBuilderTest.java
@@ -120,7 +120,7 @@ public class JavaClassBuilderTest {
}
for (int i = 0; i < referenceClassLines.size(); i++) {
if (configClassLines.length <= i)
- fail("Missing lines i generated config class. First missing line:\n" + referenceClassLines.get(i));
+ fail("Missing lines in generated config class. First missing line:\n" + referenceClassLines.get(i));
assertEquals(referenceClassLines.get(i), configClassLines[i], "Line " + i);
}
}
diff --git a/configgen/src/test/java/com/yahoo/config/codegen/NormalizedDefinitionTest.java b/configgen/src/test/java/com/yahoo/config/codegen/NormalizedDefinitionTest.java
index 57b3ed962eb..18608102ffa 100644
--- a/configgen/src/test/java/com/yahoo/config/codegen/NormalizedDefinitionTest.java
+++ b/configgen/src/test/java/com/yahoo/config/codegen/NormalizedDefinitionTest.java
@@ -70,7 +70,7 @@ public class NormalizedDefinitionTest {
}
assertNotNull(out);
- assertEquals(75, out.size());
+ assertEquals(76, out.size());
assertNotNull(fileReader);
fileReader.close();
diff --git a/configgen/src/test/resources/allfeatures.reference b/configgen/src/test/resources/allfeatures.reference
index b7a79f663e7..79508b3a25f 100644
--- a/configgen/src/test/resources/allfeatures.reference
+++ b/configgen/src/test/resources/allfeatures.reference
@@ -35,7 +35,7 @@ import com.yahoo.config.*;
*/
public final class AllfeaturesConfig extends ConfigInstance {
- public final static String CONFIG_DEF_MD5 = "0501f9e2c4ecc8c283e100e0b1178ca4";
+ public final static String CONFIG_DEF_MD5 = "ee37973499305fde315da46256e64b2e";
public final static String CONFIG_DEF_NAME = "allfeatures";
public final static String CONFIG_DEF_NAMESPACE = "configgen";
public final static String[] CONFIG_DEF_SCHEMA = {
@@ -56,6 +56,7 @@ public final class AllfeaturesConfig extends ConfigInstance {
"refwithdef reference default=\":parent:\"",
"fileVal file",
"pathVal path",
+ "optionalPathVal path optional",
"urlVal url",
"modelVal model",
"boolarr[] bool",
@@ -130,6 +131,7 @@ public final class AllfeaturesConfig extends ConfigInstance {
private String refwithdef = null;
private String fileVal = null;
private FileReference pathVal = null;
+ private Optional<FileReference> optionalPathVal = Optional.empty();
private UrlReference urlVal = null;
private ModelReference modelVal = null;
public List<Boolean> boolarr = new ArrayList<>();
@@ -171,6 +173,7 @@ public final class AllfeaturesConfig extends ConfigInstance {
refwithdef(config.refwithdef());
fileVal(config.fileVal().value());
pathVal(config.pathVal.getFileReference());
+ optionalPathVal(config.optionalPathVal.getFileReference());
urlVal(config.urlVal.getUrlReference());
modelVal(config.modelVal.getModelReference());
boolarr(config.boolarr());
@@ -231,6 +234,8 @@ public final class AllfeaturesConfig extends ConfigInstance {
fileVal(__superior.fileVal);
if (__superior.pathVal != null)
pathVal(__superior.pathVal);
+ if (__superior.optionalPathVal != null)
+ optionalPathVal(__superior.optionalPathVal);
if (__superior.urlVal != null)
urlVal(__superior.urlVal);
if (__superior.modelVal != null)
@@ -412,6 +417,17 @@ public final class AllfeaturesConfig extends ConfigInstance {
}
+ public Builder optionalPathVal(Optional<FileReference> __value) {
+ if (__value == null) throw new IllegalArgumentException("Null value is not allowed.");
+ optionalPathVal = __value;
+ __uninitialized.remove("optionalPathVal");
+ return this;
+ }
+
+ private Builder optionalPathVal(FileReference __value) {
+ return optionalPathVal(Optional.of(__value));
+ }
+
public Builder urlVal(UrlReference __value) {
if (__value == null) throw new IllegalArgumentException("Null value is not allowed.");
urlVal = __value;
@@ -759,6 +775,7 @@ public final class AllfeaturesConfig extends ConfigInstance {
private final ReferenceNode refwithdef;
private final FileNode fileVal;
private final PathNode pathVal;
+ private final OptionalPathNode optionalPathVal;
private final UrlNode urlVal;
private final ModelNode modelVal;
private final LeafNodeVector<Boolean, BooleanNode> boolarr;
@@ -822,6 +839,8 @@ public final class AllfeaturesConfig extends ConfigInstance {
new FileNode() : new FileNode(builder.fileVal);
pathVal = (builder.pathVal == null) ?
new PathNode() : new PathNode(builder.pathVal);
+ optionalPathVal = (builder.optionalPathVal == null) ?
+ new OptionalPathNode() : new OptionalPathNode(builder.optionalPathVal);
urlVal = (builder.urlVal == null) ?
new UrlNode() : new UrlNode(builder.urlVal);
modelVal = (builder.modelVal == null) ?
@@ -960,6 +979,13 @@ public final class AllfeaturesConfig extends ConfigInstance {
}
/**
+ * @return allfeatures.optionalPathVal
+ */
+ public Optional<Path> optionalPathVal() {
+ return optionalPathVal.value();
+ }
+
+ /**
* @return allfeatures.urlVal
*/
public File urlVal() {
diff --git a/configgen/src/test/resources/configgen.allfeatures.def b/configgen/src/test/resources/configgen.allfeatures.def
index 1f93e29b73b..eee39dc18f3 100644
--- a/configgen/src/test/resources/configgen.allfeatures.def
+++ b/configgen/src/test/resources/configgen.allfeatures.def
@@ -39,6 +39,7 @@ refVal reference
refwithdef reference default=":parent:"
fileVal file
pathVal path
+optionalPathVal path optional
urlVal url
modelVal model