Add path to ConfigNodeComplexValue if nonexistent

Add the desired path passed into the setValueOnPath method in
ConfigNodeComplexValue if that path does not exist in the node.
This commit is contained in:
Preben Ingvaldsen 2015-03-09 16:52:41 -07:00
parent faf8d42a6c
commit fa0aaeabbf
5 changed files with 56 additions and 38 deletions

View File

@ -1,7 +0,0 @@
package com.typesafe.config.impl;
final class BasicConfigNode extends AbstractConfigNode{
BasicConfigNode(Token t) {
super(t);
}
}

View File

@ -0,0 +1,7 @@
package com.typesafe.config.impl;
final class ConfigNodeBasic extends AbstractConfigNode{
ConfigNodeBasic(Token t) {
super(t);
}
}

View File

@ -35,7 +35,7 @@ final class ConfigNodeComplexValue implements ConfigNode, ConfigNodeValue {
return renderedText.toString();
}
private ConfigNodeComplexValue changeValueOnPath(Path desiredPath, ConfigNodeValue value) {
protected ConfigNodeComplexValue changeValueOnPath(Path desiredPath, ConfigNodeValue value) {
ArrayList<ConfigNode> childrenCopy = (ArrayList<ConfigNode>)(children.clone());
boolean replaced = value == null;
ConfigNodeKeyValue node;
@ -54,7 +54,7 @@ final class ConfigNodeComplexValue implements ConfigNode, ConfigNodeValue {
if (node.value() instanceof ConfigNodeComplexValue) {
Path remainingPath = desiredPath.subPath(key.length());
if (!replaced) {
node = node.replaceValue(((ConfigNodeComplexValue) node.value()).setValueOnPath(remainingPath, value));
node = node.replaceValue(((ConfigNodeComplexValue) node.value()).changeValueOnPath(remainingPath, value));
if (node.render() != children.get(keyValIndex.intValue()).render())
replaced = true;
childrenCopy.set(keyValIndex.intValue(), node);
@ -69,7 +69,23 @@ final class ConfigNodeComplexValue implements ConfigNode, ConfigNodeValue {
}
public ConfigNodeComplexValue setValueOnPath(Path desiredPath, ConfigNodeValue value) {
return changeValueOnPath(desiredPath, value);
ConfigNodeComplexValue node = changeValueOnPath(desiredPath, value);
// If the desired Path did not exist, add it
if (node.render().equals(render())) {
ArrayList<ConfigNode> childrenCopy = (ArrayList<ConfigNode>)children.clone();
ArrayList<ConfigNode> newNodes = new ArrayList<ConfigNode>();
newNodes.add(new ConfigNodeBasic(Tokens.newLine(null)));
newNodes.add(new ConfigNodeKey(Tokens.newUnquotedText(null, desiredPath.render())));
newNodes.add(new ConfigNodeBasic(Tokens.newIgnoredWhitespace(null, " ")));
newNodes.add(new ConfigNodeBasic(Tokens.COLON));
newNodes.add(new ConfigNodeBasic(Tokens.newIgnoredWhitespace(null, " ")));
newNodes.add(value);
newNodes.add(new ConfigNodeBasic(Tokens.newLine(null)));
childrenCopy.add(new ConfigNodeKeyValue(newNodes));
node = new ConfigNodeComplexValue(childrenCopy);
}
return node;
}
public ConfigNodeComplexValue removeValueOnPath(Path desiredPath) {

View File

@ -21,7 +21,7 @@ class ConfigNodeTest extends TestUtils {
assertEquals(node.render(), token.tokenText())
}
private def keyValueNodeTest(key: ConfigNodeKey, value: ConfigNodeValue, trailingWhitespace: BasicConfigNode, newValue: ConfigNodeValue) {
private def keyValueNodeTest(key: ConfigNodeKey, value: ConfigNodeValue, trailingWhitespace: ConfigNodeBasic, newValue: ConfigNodeValue) {
val keyValNode = nodeKeyValuePair(key, value, trailingWhitespace)
assertEquals(key.render() + " : " + value.render() + trailingWhitespace.render(), keyValNode.render())
assertEquals(key.render, keyValNode.key().render())
@ -135,27 +135,26 @@ class ConfigNodeTest extends TestUtils {
@Test
def replaceInNestedMapComplexValue() {
val origText = "{\n\tfoo : bar\n\tbaz : {\n\t\t\"abc.def\" : 123\n\t\t//This is a comment about the below setting\n\n\t\tabc : {\n\t\t\t" +
"def : \"this is a string\"\n\t\t\tghi : ${\"a.b\"}\n\t\t}\n\t}\n\tbaz.abc.ghi : 52\n\tbaz.abc.ghi : 53\n}"
val lowestLevelMap = configNodeComplexValue(List(nodeOpenBrace, nodeLine(7),
nodeKeyValuePair(nodeWhitespace("\t\t\t"), nodeUnquotedKey("def"), configNodeSimpleValue(tokenString("this is a string")), nodeLine(8)),
nodeKeyValuePair(nodeWhitespace("\t\t\t"), nodeUnquotedKey("ghi"), configNodeSimpleValue(tokenKeySubstitution("a.b")), nodeLine(9)),
nodeWhitespace("\t\t"), nodeCloseBrace))
val higherLevelMap = configNodeComplexValue(List(nodeOpenBrace, nodeLine(3),
nodeKeyValuePair(nodeWhitespace("\t\t"), configNodeKey(tokenString("abc.def")), configNodeSimpleValue(tokenInt(123)), nodeLine(4)),
nodeWhitespace("\t\t"), configNodeBasic(tokenCommentDoubleSlash("This is a comment about the below setting")),
nodeLine(5), nodeLine(6),
nodeKeyValuePair(nodeWhitespace("\t\t"), nodeUnquotedKey("abc"), lowestLevelMap, nodeLine(10)), nodeWhitespace("\t"),
val origText = "foo : bar\nbaz : {\n\t\"abc.def\" : 123\n\t//This is a comment about the below setting\n\n\tabc : {\n\t\t" +
"def : \"this is a string\"\n\t\tghi : ${\"a.b\"}\n\t}\n}\nbaz.abc.ghi : 52\nbaz.abc.ghi : 53\n}"
val lowestLevelMap = configNodeComplexValue(List(nodeOpenBrace, nodeLine(6),
nodeKeyValuePair(nodeWhitespace("\t\t"), nodeUnquotedKey("def"), configNodeSimpleValue(tokenString("this is a string")), nodeLine(7)),
nodeKeyValuePair(nodeWhitespace("\t\t"), nodeUnquotedKey("ghi"), configNodeSimpleValue(tokenKeySubstitution("a.b")), nodeLine(8)),
nodeWhitespace("\t"), nodeCloseBrace))
val higherLevelMap = configNodeComplexValue(List(nodeOpenBrace, nodeLine(2),
nodeKeyValuePair(nodeWhitespace("\t"), configNodeKey(tokenString("abc.def")), configNodeSimpleValue(tokenInt(123)), nodeLine(3)),
nodeWhitespace("\t"), configNodeBasic(tokenCommentDoubleSlash("This is a comment about the below setting")),
nodeLine(4), nodeLine(5),
nodeKeyValuePair(nodeWhitespace("\t"), nodeUnquotedKey("abc"), lowestLevelMap, nodeLine(9)), nodeWhitespace(""),
nodeCloseBrace))
val origNode = configNodeComplexValue(List(nodeOpenBrace, nodeLine(1),
nodeKeyValuePair(nodeWhitespace("\t"), nodeUnquotedKey("foo"), configNodeSimpleValue(tokenUnquoted("bar")), nodeLine(2)),
nodeKeyValuePair(nodeWhitespace("\t"), nodeUnquotedKey("baz"), higherLevelMap, nodeLine(11)),
nodeKeyValuePair(nodeWhitespace("\t"), nodeUnquotedKey("baz.abc.ghi"), configNodeSimpleValue(tokenInt(52)), nodeLine(12)),
nodeKeyValuePair(nodeWhitespace("\t"), nodeUnquotedKey("baz.abc.ghi"), configNodeSimpleValue(tokenInt(53)), nodeLine(13)),
val origNode = configNodeComplexValue(List(nodeKeyValuePair(nodeUnquotedKey("foo"), configNodeSimpleValue(tokenUnquoted("bar")), nodeLine(1)),
nodeKeyValuePair(nodeUnquotedKey("baz"), higherLevelMap, nodeLine(10)),
nodeKeyValuePair(nodeUnquotedKey("baz.abc.ghi"), configNodeSimpleValue(tokenInt(52)), nodeLine(11)),
nodeKeyValuePair(nodeUnquotedKey("baz.abc.ghi"), configNodeSimpleValue(tokenInt(53)), nodeLine(12)),
nodeCloseBrace))
assertEquals(origText, origNode.render())
val finalText = "{\n\tfoo : bar\n\tbaz : {\n\t\t\"abc.def\" : true\n\t\t//This is a comment about the below setting\n\n\t\tabc : {\n\t\t\t" +
"def : false\n\t\t}\n\t}\n\tbaz.abc.ghi : randomunquotedString\n}"
val finalText = "foo : bar\nbaz : {\n\t\"abc.def\" : true\n\t//This is a comment about the below setting\n\n\tabc : {\n\t\t" +
"def : false\n\t}\n}\nbaz.abc.ghi : randomunquotedString\n}\nbaz.abc.this.does.not.exist : doesnotexist\n"
//Can replace settings in nested maps
// Paths with quotes in the name are treated as a single Path, rather than multiple sub-paths
@ -165,6 +164,9 @@ class ConfigNodeTest extends TestUtils {
// Repeats are removed
newNode = newNode.setValueOnPath(Path.newPath("baz.abc.ghi"), configNodeSimpleValue(tokenUnquoted("randomunquotedString")))
// Missing paths are added to the top level
newNode = newNode.setValueOnPath(Path.newPath("baz.abc.this.does.not.exist"), configNodeSimpleValue(tokenUnquoted("doesnotexist")))
// The above operations cause the resultant map to be rendered properly
assertEquals(finalText, newNode.render())
}

View File

@ -672,26 +672,26 @@ abstract trait TestUtils {
}
def configNodeBasic(value: Token) = {
new BasicConfigNode(value: Token)
new ConfigNodeBasic(value: Token)
}
def configNodeComplexValue(nodes: List[ConfigNode]) = {
new ConfigNodeComplexValue(nodes.asJavaCollection)
}
def nodeColon = new BasicConfigNode(Tokens.COLON)
def nodeSpace = new BasicConfigNode(tokenUnquoted(" "))
def nodeOpenBrace = new BasicConfigNode(Tokens.OPEN_CURLY)
def nodeCloseBrace = new BasicConfigNode(Tokens.CLOSE_CURLY)
def nodeLine(line: Integer) = new BasicConfigNode(tokenLine(line))
def nodeWhitespace(whitespace: String) = new BasicConfigNode(tokenWhitespace(whitespace))
def nodeColon = new ConfigNodeBasic(Tokens.COLON)
def nodeSpace = new ConfigNodeBasic(tokenUnquoted(" "))
def nodeOpenBrace = new ConfigNodeBasic(Tokens.OPEN_CURLY)
def nodeCloseBrace = new ConfigNodeBasic(Tokens.CLOSE_CURLY)
def nodeLine(line: Integer) = new ConfigNodeBasic(tokenLine(line))
def nodeWhitespace(whitespace: String) = new ConfigNodeBasic(tokenWhitespace(whitespace))
def nodeQuotedKey(key: String) = configNodeKey(tokenString(key))
def nodeUnquotedKey(key: String) = configNodeKey(tokenUnquoted(key))
def nodeKeyValuePair(key: ConfigNodeKey, value: ConfigNodeValue, trailingWhitespace: BasicConfigNode) = {
def nodeKeyValuePair(key: ConfigNodeKey, value: ConfigNodeValue, trailingWhitespace: ConfigNodeBasic) = {
val nodes = List(key, nodeSpace, nodeColon, nodeSpace, value, trailingWhitespace)
new ConfigNodeKeyValue(nodes.asJavaCollection)
}
def nodeKeyValuePair(leadingWhitespace: BasicConfigNode, key: ConfigNodeKey, value: ConfigNodeValue, trailingWhitespace: BasicConfigNode) = {
def nodeKeyValuePair(leadingWhitespace: ConfigNodeBasic, key: ConfigNodeKey, value: ConfigNodeValue, trailingWhitespace: ConfigNodeBasic) = {
val nodes = List(leadingWhitespace, key, nodeSpace, nodeColon, nodeSpace, value, trailingWhitespace)
new ConfigNodeKeyValue(nodes.asJavaCollection);
}