mirror of
https://github.com/lightbend/config.git
synced 2025-03-28 13:01:09 +08:00
Keep tokens in Path
Save the list of Tokens from which a Path was created when a Path is parsed from a string in Parser.parsePath. Change ConfigNodeKey to store a Path instead of a token.
This commit is contained in:
parent
e695543bf1
commit
cce8c204ac
config/src
main/java/com/typesafe/config/impl
test/scala/com/typesafe/config/impl
@ -33,7 +33,7 @@ final class ConfigNodeComplexValue extends AbstractConfigNodeValue {
|
||||
continue;
|
||||
}
|
||||
node = (ConfigNodeKeyValue)children.get(i);
|
||||
key = Path.newPath(node.key().render());
|
||||
key = node.key().value();
|
||||
if (key.equals(desiredPath)) {
|
||||
if (!replaced) {
|
||||
childrenCopy.set(i, node.replaceValue(value));
|
||||
@ -67,7 +67,7 @@ final class ConfigNodeComplexValue extends AbstractConfigNodeValue {
|
||||
ArrayList<AbstractConfigNode> childrenCopy = new ArrayList<AbstractConfigNode>(children);
|
||||
ArrayList<AbstractConfigNode> newNodes = new ArrayList();
|
||||
newNodes.add(new ConfigNodeSingleToken(Tokens.newLine(null)));
|
||||
newNodes.add(new ConfigNodeKey(Tokens.newUnquotedText(null, desiredPath.render())));
|
||||
newNodes.add(new ConfigNodeKey(desiredPath));
|
||||
newNodes.add(new ConfigNodeSingleToken(Tokens.newIgnoredWhitespace(null, " ")));
|
||||
newNodes.add(new ConfigNodeSingleToken(Tokens.COLON));
|
||||
newNodes.add(new ConfigNodeSingleToken(Tokens.newIgnoredWhitespace(null, " ")));
|
||||
|
@ -1,15 +1,18 @@
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
final class ConfigNodeKey extends AbstractConfigNode {
|
||||
Token token;
|
||||
ConfigNodeKey(Token t) {
|
||||
token = t;
|
||||
final private Path key;
|
||||
ConfigNodeKey(Path key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
protected Collection<Token> tokens() {
|
||||
return Collections.singletonList(token);
|
||||
return key.tokens();
|
||||
}
|
||||
|
||||
protected Path value() {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
@ -1063,6 +1063,7 @@ final class Parser {
|
||||
private static Path parsePathExpression(Iterator<Token> expression,
|
||||
ConfigOrigin origin, String originalText) {
|
||||
// each builder in "buf" is an element in the path.
|
||||
ArrayList<Token> pathTokens = new ArrayList<Token>();
|
||||
List<Element> buf = new ArrayList<Element>();
|
||||
buf.add(new Element("", false));
|
||||
|
||||
@ -1073,6 +1074,7 @@ final class Parser {
|
||||
|
||||
while (expression.hasNext()) {
|
||||
Token t = expression.next();
|
||||
pathTokens.add(t);
|
||||
|
||||
// Ignore all IgnoredWhitespace tokens
|
||||
if (Tokens.isIgnoredWhitespace(t))
|
||||
@ -1119,7 +1121,7 @@ final class Parser {
|
||||
}
|
||||
}
|
||||
|
||||
PathBuilder pb = new PathBuilder();
|
||||
PathBuilder pb = new PathBuilder(pathTokens);
|
||||
for (Element e : buf) {
|
||||
if (e.sb.length() == 0 && !e.canBeEmpty) {
|
||||
throw new ConfigException.BadPath(
|
||||
@ -1192,8 +1194,10 @@ final class Parser {
|
||||
private static Path fastPathBuild(Path tail, String s, int end) {
|
||||
// lastIndexOf takes last index it should look at, end - 1 not end
|
||||
int splitAt = s.lastIndexOf('.', end - 1);
|
||||
ArrayList<Token> tokens = new ArrayList<Token>();
|
||||
tokens.add(Tokens.newUnquotedText(null, s));
|
||||
// this works even if splitAt is -1; then we start the substring at 0
|
||||
Path withOneMoreElement = new Path(s.substring(splitAt + 1, end), tail);
|
||||
Path withOneMoreElement = new Path(s.substring(splitAt + 1, end), tail, tokens);
|
||||
if (splitAt < 0) {
|
||||
return withOneMoreElement;
|
||||
} else {
|
||||
|
@ -3,6 +3,8 @@
|
||||
*/
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
@ -13,12 +15,22 @@ final class Path {
|
||||
final private String first;
|
||||
final private Path remainder;
|
||||
|
||||
// We only need to keep track of this for top-level paths created with
|
||||
// parsePath, so this will be empty or null for all other cases
|
||||
final private ArrayList<Token> tokens;
|
||||
|
||||
Path(String first, Path remainder) {
|
||||
this(first, remainder, new ArrayList<Token>());
|
||||
}
|
||||
|
||||
Path(String first, Path remainder, Collection<Token> tokens) {
|
||||
this.first = first;
|
||||
this.remainder = remainder;
|
||||
this.tokens = new ArrayList<Token>(tokens);
|
||||
}
|
||||
|
||||
Path(String... elements) {
|
||||
this.tokens = null;
|
||||
if (elements.length == 0)
|
||||
throw new ConfigException.BugOrBroken("empty path");
|
||||
this.first = elements[0];
|
||||
@ -40,6 +52,7 @@ final class Path {
|
||||
|
||||
// append all the paths in the iterator together into one path
|
||||
Path(Iterator<Path> i) {
|
||||
this.tokens = null;
|
||||
if (!i.hasNext())
|
||||
throw new ConfigException.BugOrBroken("empty path");
|
||||
|
||||
@ -204,6 +217,10 @@ final class Path {
|
||||
}
|
||||
}
|
||||
|
||||
protected Collection<Token> tokens() {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
@ -3,6 +3,8 @@
|
||||
*/
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Stack;
|
||||
|
||||
import com.typesafe.config.ConfigException;
|
||||
@ -12,8 +14,17 @@ final class PathBuilder {
|
||||
final private Stack<String> keys;
|
||||
private Path result;
|
||||
|
||||
// the tokens only matter for top-level paths created with parsePath, in all
|
||||
// other cases this will be empty
|
||||
final private ArrayList<Token> tokens;
|
||||
|
||||
PathBuilder() {
|
||||
this(new ArrayList<Token>());
|
||||
}
|
||||
|
||||
PathBuilder(Collection<Token> tokens) {
|
||||
keys = new Stack<String>();
|
||||
this.tokens = new ArrayList<Token>(tokens);
|
||||
}
|
||||
|
||||
private void checkCanAppend() {
|
||||
@ -51,7 +62,7 @@ final class PathBuilder {
|
||||
Path remainder = null;
|
||||
while (!keys.isEmpty()) {
|
||||
String key = keys.pop();
|
||||
remainder = new Path(key, remainder);
|
||||
remainder = new Path(key, remainder, tokens);
|
||||
}
|
||||
result = remainder;
|
||||
}
|
||||
|
@ -11,9 +11,9 @@ class ConfigNodeTest extends TestUtils {
|
||||
assertEquals(node.render(), token.tokenText())
|
||||
}
|
||||
|
||||
private def keyNodeTest(token: Token) {
|
||||
val node = configNodeKey(token)
|
||||
assertEquals(node.render(), token.tokenText())
|
||||
private def keyNodeTest(path: String) {
|
||||
val node = configNodeKey(path)
|
||||
assertEquals(path, node.render())
|
||||
}
|
||||
|
||||
private def simpleValueNodeTest(token: Token) {
|
||||
@ -32,21 +32,21 @@ class ConfigNodeTest extends TestUtils {
|
||||
assertEquals(newValue.render(), newKeyValNode.value().render())
|
||||
}
|
||||
|
||||
private def topLevelValueReplaceTest(value: AbstractConfigNodeValue, newValue: AbstractConfigNodeValue, key: Token = tokenString("foo")) {
|
||||
private def topLevelValueReplaceTest(value: AbstractConfigNodeValue, newValue: AbstractConfigNodeValue, key: String = "foo") {
|
||||
val complexNodeChildren = List(nodeOpenBrace,
|
||||
nodeKeyValuePair(nodeWhitespace(" "), configNodeKey(key),value, nodeWhitespace(" ")),
|
||||
nodeCloseBrace)
|
||||
val complexNode = configNodeComplexValue(complexNodeChildren)
|
||||
val newNode = complexNode.setValueOnPath(Path.newPath(key.tokenText()), newValue)
|
||||
val origText = "{ " + key.tokenText() + " : " + value.render() + " }"
|
||||
val finalText = "{ " + key.tokenText() + " : " + newValue.render() + " }"
|
||||
val newNode = complexNode.setValueOnPath(Path.newPath(key), newValue)
|
||||
val origText = "{ " + key + " : " + value.render() + " }"
|
||||
val finalText = "{ " + key + " : " + newValue.render() + " }"
|
||||
|
||||
assertEquals(origText, complexNode.render())
|
||||
assertEquals(finalText, newNode.render())
|
||||
}
|
||||
|
||||
private def replaceDuplicatesTest(value1: AbstractConfigNodeValue, value2: AbstractConfigNodeValue, value3: AbstractConfigNodeValue) {
|
||||
val key = nodeUnquotedKey("foo")
|
||||
val key = configNodeKey("foo")
|
||||
val keyValPair1 = nodeKeyValuePair(key, value1)
|
||||
val keyValPair2 = nodeKeyValuePair(key, value2)
|
||||
val keyValPair3 = nodeKeyValuePair(key, value3)
|
||||
@ -59,7 +59,7 @@ class ConfigNodeTest extends TestUtils {
|
||||
}
|
||||
|
||||
private def nonExistentPathTest(value: AbstractConfigNodeValue) {
|
||||
val node = configNodeComplexValue(List(nodeKeyValuePair(nodeUnquotedKey("bar"), nodeInt(15))))
|
||||
val node = configNodeComplexValue(List(nodeKeyValuePair(configNodeKey("bar"), nodeInt(15))))
|
||||
assertEquals("bar : 15", node.render())
|
||||
val newNode = node.setValueOnPath(Path.newPath("foo"), value)
|
||||
val finalText = "bar : 15\nfoo : " + value.render() + "\n"
|
||||
@ -89,8 +89,8 @@ class ConfigNodeTest extends TestUtils {
|
||||
@Test
|
||||
def createConfigNodeSetting() {
|
||||
//Ensure a ConfigNodeSetting can handle the normal key types
|
||||
keyNodeTest(tokenUnquoted("foo"))
|
||||
keyNodeTest(tokenString("Hello I am a key how are you today"))
|
||||
keyNodeTest("foo")
|
||||
keyNodeTest("\"Hello I am a key how are you today\"")
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -112,12 +112,12 @@ class ConfigNodeTest extends TestUtils {
|
||||
@Test
|
||||
def createConfigNodeKeyValue() {
|
||||
// Supports Quoted and Unquoted keys
|
||||
keyValueNodeTest(nodeQuotedKey("abc"), nodeInt(123), nodeLine(1), nodeInt(245))
|
||||
keyValueNodeTest(nodeUnquotedKey("abc"), nodeInt(123), nodeLine(1), nodeInt(245))
|
||||
keyValueNodeTest(configNodeKey("\"abc\""), nodeInt(123), nodeLine(1), nodeInt(245))
|
||||
keyValueNodeTest(configNodeKey("abc"), nodeInt(123), nodeLine(1), nodeInt(245))
|
||||
|
||||
// Can replace value with values of different types
|
||||
keyValueNodeTest(nodeQuotedKey("abc"), nodeInt(123), nodeLine(1), nodeString("I am a string"))
|
||||
keyValueNodeTest(nodeQuotedKey("abc"), nodeInt(123), nodeLine(1), configNodeComplexValue(List(nodeOpenBrace, nodeCloseBrace)))
|
||||
keyValueNodeTest(configNodeKey("\"abc\""), nodeInt(123), nodeLine(1), nodeString("I am a string"))
|
||||
keyValueNodeTest(configNodeKey("\"abc\""), nodeInt(123), nodeLine(1), configNodeComplexValue(List(nodeOpenBrace, nodeCloseBrace)))
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -142,7 +142,7 @@ class ConfigNodeTest extends TestUtils {
|
||||
topLevelValueReplaceTest(array, configNodeComplexValue(List(nodeOpenBrace, nodeCloseBrace)))
|
||||
|
||||
//Ensure maps can be replaced
|
||||
val nestedMap = configNodeComplexValue(List(nodeOpenBrace, nodeUnquotedKey("abc"),
|
||||
val nestedMap = configNodeComplexValue(List(nodeOpenBrace, configNodeKey("abc"),
|
||||
nodeColon, configNodeSimpleValue(tokenString("a string")),
|
||||
nodeCloseBrace))
|
||||
topLevelValueReplaceTest(nestedMap, nodeInt(10))
|
||||
@ -152,7 +152,7 @@ class ConfigNodeTest extends TestUtils {
|
||||
topLevelValueReplaceTest(nestedMap, configNodeComplexValue(List(nodeOpenBrace, nodeCloseBrace)))
|
||||
|
||||
//Ensure a key with format "a.b" will be properly replaced
|
||||
topLevelValueReplaceTest(nodeInt(10), nestedMap, tokenUnquoted("foo.bar"))
|
||||
topLevelValueReplaceTest(nodeInt(10), nestedMap, "foo.bar")
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -170,7 +170,7 @@ class ConfigNodeTest extends TestUtils {
|
||||
def addNonExistentPaths() {
|
||||
nonExistentPathTest(nodeInt(10))
|
||||
nonExistentPathTest(configNodeComplexValue(List(nodeOpenBracket, nodeInt(15), nodeCloseBracket)))
|
||||
nonExistentPathTest(configNodeComplexValue(List(nodeOpenBrace, nodeKeyValuePair(nodeUnquotedKey("foo"), nodeDouble(3.14), nodeSpace))))
|
||||
nonExistentPathTest(configNodeComplexValue(List(nodeOpenBrace, nodeKeyValuePair(configNodeKey("foo"), nodeDouble(3.14), nodeSpace))))
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -179,23 +179,23 @@ class ConfigNodeTest extends TestUtils {
|
||||
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)),
|
||||
nodeKeyValuePair(nodeWhitespace("\t\t"), configNodeKey("def"), configNodeSimpleValue(tokenString("this is a string")), nodeLine(7)),
|
||||
nodeKeyValuePair(nodeWhitespace("\t\t"), configNodeKey("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)),
|
||||
nodeKeyValuePair(nodeWhitespace("\t"), configNodeKey("\"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(""),
|
||||
nodeKeyValuePair(nodeWhitespace("\t"), configNodeKey("abc"), lowestLevelMap, nodeLine(9)), nodeWhitespace(""),
|
||||
nodeCloseBrace))
|
||||
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)),
|
||||
val origNode = configNodeComplexValue(List(nodeKeyValuePair(configNodeKey("foo"), configNodeSimpleValue(tokenUnquoted("bar")), nodeLine(1)),
|
||||
nodeKeyValuePair(configNodeKey("baz"), higherLevelMap, nodeLine(10)),
|
||||
nodeKeyValuePair(configNodeKey("baz.abc.ghi"), configNodeSimpleValue(tokenInt(52)), nodeLine(11)),
|
||||
nodeKeyValuePair(configNodeKey("baz.abc.ghi"), configNodeSimpleValue(tokenInt(53)), nodeLine(12)),
|
||||
nodeCloseBrace))
|
||||
assertEquals(origText, origNode.render())
|
||||
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"
|
||||
"def : false\n\t}\n}\nbaz.abc.ghi : randomunquotedString\n}\nbaz.abc.\"this.does.not.exist@@@+$#\".end : 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
|
||||
@ -206,7 +206,7 @@ class ConfigNodeTest extends TestUtils {
|
||||
newNode = newNode.setValueOnPath(Path.newPath("baz.abc.ghi"), configNodeSimpleValue(tokenUnquoted("randomunquotedString")))
|
||||
|
||||
// Missing paths are added to the top level if they don't appear anywhere, including in nested maps
|
||||
newNode = newNode.setValueOnPath(Path.newPath("baz.abc.this.does.not.exist"), configNodeSimpleValue(tokenUnquoted("doesnotexist")))
|
||||
newNode = newNode.setValueOnPath(Path.newPath("baz.abc.\"this.does.not.exist@@@+$#\".end"), configNodeSimpleValue(tokenUnquoted("doesnotexist")))
|
||||
|
||||
// The above operations cause the resultant map to be rendered properly
|
||||
assertEquals(finalText, newNode.render())
|
||||
|
@ -667,8 +667,9 @@ abstract trait TestUtils {
|
||||
new ConfigNodeSimpleValue(value)
|
||||
}
|
||||
|
||||
def configNodeKey(value: Token) = {
|
||||
new ConfigNodeKey(value)
|
||||
def configNodeKey(path: String) = {
|
||||
val parsedPath = Parser.parsePath(path)
|
||||
new ConfigNodeKey(parsedPath)
|
||||
}
|
||||
|
||||
def configNodeBasic(value: Token) = {
|
||||
@ -688,8 +689,6 @@ abstract trait TestUtils {
|
||||
def nodeComma = new ConfigNodeSingleToken(Tokens.COMMA)
|
||||
def nodeLine(line: Integer) = new ConfigNodeSingleToken(tokenLine(line))
|
||||
def nodeWhitespace(whitespace: String) = new ConfigNodeSingleToken(tokenWhitespace(whitespace))
|
||||
def nodeQuotedKey(key: String) = configNodeKey(tokenString(key))
|
||||
def nodeUnquotedKey(key: String) = configNodeKey(tokenUnquoted(key))
|
||||
def nodeKeyValuePair(key: ConfigNodeKey, value: AbstractConfigNodeValue) = {
|
||||
val nodes = List(key, nodeSpace, nodeColon, nodeSpace, value)
|
||||
new ConfigNodeKeyValue(nodes.asJavaCollection)
|
||||
|
Loading…
Reference in New Issue
Block a user