mirror of
https://github.com/lightbend/config.git
synced 2025-03-22 23:30:27 +08:00
Address further PR feedback before merge
Implement an `equals()` method for AbstractConfigNodes. Add a test to ensure an exception is thrown when calling setValue() on a ConfigDocument when passing in a value with HOCON syntax when the document was parsed as JSON. Add a setValue() method to the ConfigDocument interface that takes a ConfigValue instead of a String. When parsing a single value, throw an exception if a concatenation is seen when parsing JSON.
This commit is contained in:
parent
c3a2e07c6a
commit
1639a91481
@ -25,12 +25,29 @@ public interface ConfigDocument {
|
||||
*
|
||||
* @param path the path at which to set the desired value
|
||||
* @param newValue the value to set at the desired path, represented as a string. This
|
||||
* string will be parsed into a ConfigNode, and the text will be inserted
|
||||
* string will be parsed into a ConfigNode using the same options used to
|
||||
* parse the entire document, and the text will be inserted
|
||||
* as-is into the document, with leading and trailing whitespace removed.
|
||||
* If a concatenation is passed in for newValue but the document was parsed
|
||||
* with JSON, the first value in the concatenation will be parsed and inserted
|
||||
* into the ConfigDocument.
|
||||
* @return a copy of the ConfigDocument with the desired value at the desired path
|
||||
*/
|
||||
ConfigDocument setValue(String path, String newValue);
|
||||
|
||||
/**
|
||||
* Returns a new ConfigDocument that is a copy of the current ConfigDocument,
|
||||
* but with the desired value set at the desired path as with {@link #setValue(String, String)},
|
||||
* but takes a ConfigValue instead of a string.
|
||||
*
|
||||
* @param path the path at which to set the desired value
|
||||
* @param newValue the value to set at the desired path, represented as a ConfigValue.
|
||||
* The rendered text of the ConfigValue will be inserted into the
|
||||
* ConfigDocument.
|
||||
* @return a copy of the ConfigDocument with the desired value at the desired path
|
||||
*/
|
||||
ConfigDocument setValue(String path, ConfigValue newValue);
|
||||
|
||||
/**
|
||||
* The original text of the input, modified if necessary with
|
||||
* any replaced or added values.
|
||||
|
@ -16,4 +16,9 @@ abstract class AbstractConfigNode implements ConfigNode {
|
||||
}
|
||||
return origText.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
final public boolean equals(Object other) {
|
||||
return other instanceof AbstractConfigNode && render().equals(((AbstractConfigNode)other).render());
|
||||
}
|
||||
}
|
||||
|
@ -644,7 +644,16 @@ final class ConfigDocumentParser {
|
||||
throw parseError("Empty value");
|
||||
}
|
||||
if (flavor == ConfigSyntax.JSON) {
|
||||
return parseValue(t);
|
||||
AbstractConfigNodeValue node = parseValue(t);
|
||||
t = nextToken();
|
||||
while (Tokens.isIgnoredWhitespace(t) || Tokens.isNewline(t) || isUnquotedWhitespace(t)) {
|
||||
t = nextToken();
|
||||
}
|
||||
if (t == Tokens.END) {
|
||||
return node;
|
||||
} else {
|
||||
throw parseError("Tried to parse a concatenation. Concatenations not allowed in valid JSON");
|
||||
}
|
||||
} else {
|
||||
putBack(t);
|
||||
ArrayList<AbstractConfigNode> nodes = new ArrayList();
|
||||
|
@ -33,7 +33,7 @@ final class ConfigNodeObject extends ConfigNodeComplexValue {
|
||||
if (node.value() instanceof ConfigNodeObject) {
|
||||
Path remainingPath = desiredPath.subPath(key.length());
|
||||
childrenCopy.set(i, node.replaceValue(((ConfigNodeObject)node.value()).changeValueOnPath(remainingPath, valueCopy)));
|
||||
if (valueCopy != null && node.render() != super.children.get(i).render())
|
||||
if (valueCopy != null && !node.equals(super.children.get(i)))
|
||||
valueCopy = null;
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,10 @@ final class SimpleConfigDocument implements ConfigDocument {
|
||||
return new SimpleConfigDocument(((ConfigNodeObject)configNodeTree).setValueOnPath(path, parsedValue, parseOptions.getSyntax()), parseOptions);
|
||||
}
|
||||
|
||||
public ConfigDocument setValue(String path, ConfigValue newValue) {
|
||||
return setValue(path, newValue.render());
|
||||
}
|
||||
|
||||
public String render() {
|
||||
return configNodeTree.render();
|
||||
}
|
||||
|
@ -1,76 +1,75 @@
|
||||
package com.typesafe.config.impl
|
||||
|
||||
import com.typesafe.config.{ConfigException, ConfigSyntax, ConfigParseOptions}
|
||||
import com.typesafe.config.{ ConfigException, ConfigSyntax, ConfigParseOptions }
|
||||
import org.junit.Assert._
|
||||
import org.junit.Test
|
||||
|
||||
class ConfigDocumentParserTest extends TestUtils {
|
||||
|
||||
private def parseTest(origText: String) {
|
||||
val node = ConfigDocumentParser.parse(tokenize(origText))
|
||||
assertEquals(origText, node.render())
|
||||
}
|
||||
private def parseTest(origText: String) {
|
||||
val node = ConfigDocumentParser.parse(tokenize(origText))
|
||||
assertEquals(origText, node.render())
|
||||
}
|
||||
|
||||
private def parseJSONFailuresTest(origText: String, containsMessage: String) {
|
||||
var exceptionThrown = false
|
||||
try {
|
||||
ConfigDocumentParser.parse(tokenize(origText), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
exceptionThrown = true
|
||||
assertTrue(e.isInstanceOf[ConfigException])
|
||||
assertTrue(e.getMessage.contains(containsMessage))
|
||||
}
|
||||
assertTrue(exceptionThrown)
|
||||
}
|
||||
private def parseJSONFailuresTest(origText: String, containsMessage: String) {
|
||||
var exceptionThrown = false
|
||||
try {
|
||||
ConfigDocumentParser.parse(tokenize(origText), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
exceptionThrown = true
|
||||
assertTrue(e.isInstanceOf[ConfigException])
|
||||
assertTrue(e.getMessage.contains(containsMessage))
|
||||
}
|
||||
assertTrue(exceptionThrown)
|
||||
}
|
||||
|
||||
private def parseSimpleValueTest(origText: String, finalText: String = null) {
|
||||
val expectedRenderedText = if (finalText == null) origText else finalText
|
||||
val node = ConfigDocumentParser.parseValue(tokenize(origText), ConfigParseOptions.defaults())
|
||||
assertEquals(expectedRenderedText, node.render())
|
||||
assertTrue(node.isInstanceOf[AbstractConfigNodeValue])
|
||||
private def parseSimpleValueTest(origText: String, finalText: String = null) {
|
||||
val expectedRenderedText = if (finalText == null) origText else finalText
|
||||
val node = ConfigDocumentParser.parseValue(tokenize(origText), ConfigParseOptions.defaults())
|
||||
assertEquals(expectedRenderedText, node.render())
|
||||
assertTrue(node.isInstanceOf[AbstractConfigNodeValue])
|
||||
|
||||
val nodeJSON = ConfigDocumentParser.parseValue(tokenize(origText), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
||||
assertEquals(expectedRenderedText, nodeJSON.render())
|
||||
assertTrue(nodeJSON.isInstanceOf[AbstractConfigNodeValue])
|
||||
}
|
||||
val nodeJSON = ConfigDocumentParser.parseValue(tokenize(origText), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
||||
assertEquals(expectedRenderedText, nodeJSON.render())
|
||||
assertTrue(nodeJSON.isInstanceOf[AbstractConfigNodeValue])
|
||||
}
|
||||
|
||||
private def parseComplexValueTest(origText: String) {
|
||||
val node = ConfigDocumentParser.parseValue(tokenize(origText), ConfigParseOptions.defaults())
|
||||
assertEquals(origText, node.render())
|
||||
assertTrue(node.isInstanceOf[AbstractConfigNodeValue])
|
||||
private def parseComplexValueTest(origText: String) {
|
||||
val node = ConfigDocumentParser.parseValue(tokenize(origText), ConfigParseOptions.defaults())
|
||||
assertEquals(origText, node.render())
|
||||
assertTrue(node.isInstanceOf[AbstractConfigNodeValue])
|
||||
|
||||
val nodeJSON = ConfigDocumentParser.parseValue(tokenize(origText), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
||||
assertEquals(origText, nodeJSON.render())
|
||||
assertTrue(nodeJSON.isInstanceOf[AbstractConfigNodeValue])
|
||||
}
|
||||
val nodeJSON = ConfigDocumentParser.parseValue(tokenize(origText), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
||||
assertEquals(origText, nodeJSON.render())
|
||||
assertTrue(nodeJSON.isInstanceOf[AbstractConfigNodeValue])
|
||||
}
|
||||
|
||||
private def parseSingleValueInvalidJSONTest(origText: String, containsMessage: String) {
|
||||
val node = ConfigDocumentParser.parseValue(tokenize(origText), ConfigParseOptions.defaults())
|
||||
assertEquals(origText, node.render())
|
||||
private def parseSingleValueInvalidJSONTest(origText: String, containsMessage: String) {
|
||||
val node = ConfigDocumentParser.parseValue(tokenize(origText), ConfigParseOptions.defaults())
|
||||
assertEquals(origText, node.render())
|
||||
|
||||
var exceptionThrown = false
|
||||
try {
|
||||
ConfigDocumentParser.parse(tokenize(origText), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
exceptionThrown = true
|
||||
assertTrue(e.isInstanceOf[ConfigException])
|
||||
assertTrue(e.getMessage.contains(containsMessage))
|
||||
}
|
||||
assertTrue(exceptionThrown)
|
||||
}
|
||||
var exceptionThrown = false
|
||||
try {
|
||||
ConfigDocumentParser.parse(tokenize(origText), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
exceptionThrown = true
|
||||
assertTrue(e.isInstanceOf[ConfigException])
|
||||
assertTrue(e.getMessage.contains(containsMessage))
|
||||
}
|
||||
assertTrue(exceptionThrown)
|
||||
}
|
||||
|
||||
@Test
|
||||
def parseSuccess {
|
||||
parseTest("foo:bar")
|
||||
parseTest(" foo : bar ")
|
||||
parseTest("""include "foo.conf" """)
|
||||
|
||||
@Test
|
||||
def parseSuccess {
|
||||
parseTest("foo:bar")
|
||||
parseTest(" foo : bar ")
|
||||
parseTest("""include "foo.conf" """)
|
||||
|
||||
// Can parse a map with all simple types
|
||||
parseTest(
|
||||
"""
|
||||
// Can parse a map with all simple types
|
||||
parseTest(
|
||||
"""
|
||||
aUnquoted : bar
|
||||
aString = "qux"
|
||||
aNum:123
|
||||
@ -81,15 +80,15 @@ class ConfigDocumentParserTest extends TestUtils {
|
||||
aSub = ${a.b}
|
||||
include "foo.conf"
|
||||
""")
|
||||
parseTest("{}")
|
||||
parseTest("{foo:bar}")
|
||||
parseTest("{ foo : bar }")
|
||||
parseTest("{foo:bar} ")
|
||||
parseTest("""{include "foo.conf"}""")
|
||||
parseTest("{}")
|
||||
parseTest("{foo:bar}")
|
||||
parseTest("{ foo : bar }")
|
||||
parseTest("{foo:bar} ")
|
||||
parseTest("""{include "foo.conf"}""")
|
||||
|
||||
//Can parse a map with all simple types
|
||||
parseTest(
|
||||
"""{
|
||||
//Can parse a map with all simple types
|
||||
parseTest(
|
||||
"""{
|
||||
aUnquoted : bar
|
||||
aString = "qux"
|
||||
aNum:123
|
||||
@ -101,9 +100,9 @@ class ConfigDocumentParserTest extends TestUtils {
|
||||
include "foo.conf"
|
||||
}""")
|
||||
|
||||
// Test that maps can be nested within other maps
|
||||
parseTest(
|
||||
"""
|
||||
// Test that maps can be nested within other maps
|
||||
parseTest(
|
||||
"""
|
||||
foo.bar.baz : {
|
||||
qux : "abcdefg"
|
||||
"abc".def."ghi" : 123
|
||||
@ -112,34 +111,34 @@ class ConfigDocumentParserTest extends TestUtils {
|
||||
qux = 123.456
|
||||
""")
|
||||
|
||||
// Test that comments can be parsed in maps
|
||||
parseTest(
|
||||
"""{
|
||||
// Test that comments can be parsed in maps
|
||||
parseTest(
|
||||
"""{
|
||||
foo: bar
|
||||
// This is a comment
|
||||
baz:qux // This is another comment
|
||||
}""")
|
||||
|
||||
// Basic array tests
|
||||
parseTest("[]")
|
||||
parseTest("[foo]")
|
||||
// Basic array tests
|
||||
parseTest("[]")
|
||||
parseTest("[foo]")
|
||||
|
||||
// Test trailing comment and whitespace
|
||||
parseTest("[foo,]")
|
||||
parseTest("[foo,] ")
|
||||
// Test trailing comment and whitespace
|
||||
parseTest("[foo,]")
|
||||
parseTest("[foo,] ")
|
||||
|
||||
// Can parse arrays with all simple types
|
||||
parseTest("""[foo, bar,"qux", 123,123.456, true,false, null, ${a.b}]""")
|
||||
parseTest("""[foo, bar,"qux" , 123 , 123.456, true,false, null, ${a.b} ]""")
|
||||
// Can parse arrays with all simple types
|
||||
parseTest("""[foo, bar,"qux", 123,123.456, true,false, null, ${a.b}]""")
|
||||
parseTest("""[foo, bar,"qux" , 123 , 123.456, true,false, null, ${a.b} ]""")
|
||||
|
||||
// Basic concatenation tests
|
||||
parseTest("[foo bar baz qux]")
|
||||
parseTest("{foo: foo bar baz qux}")
|
||||
parseTest("[abc 123 123.456 null true false [1, 2, 3] {a:b}, 2]")
|
||||
// Basic concatenation tests
|
||||
parseTest("[foo bar baz qux]")
|
||||
parseTest("{foo: foo bar baz qux}")
|
||||
parseTest("[abc 123 123.456 null true false [1, 2, 3] {a:b}, 2]")
|
||||
|
||||
// Complex node with all types test
|
||||
parseTest(
|
||||
"""{
|
||||
// Complex node with all types test
|
||||
parseTest(
|
||||
"""{
|
||||
foo: bar baz qux ernie
|
||||
// The above was a concatenation
|
||||
|
||||
@ -172,9 +171,9 @@ class ConfigDocumentParserTest extends TestUtils {
|
||||
// Did I cover everything?
|
||||
}""")
|
||||
|
||||
// Can correctly parse a JSON string
|
||||
val origText =
|
||||
"""{
|
||||
// Can correctly parse a JSON string
|
||||
val origText =
|
||||
"""{
|
||||
"foo": "bar",
|
||||
"baz": 123,
|
||||
"qux": true,
|
||||
@ -185,74 +184,82 @@ class ConfigDocumentParserTest extends TestUtils {
|
||||
]
|
||||
}
|
||||
"""
|
||||
val node = ConfigDocumentParser.parse(tokenize(origText), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
||||
assertEquals(origText, node.render())
|
||||
}
|
||||
val node = ConfigDocumentParser.parse(tokenize(origText), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
||||
assertEquals(origText, node.render())
|
||||
}
|
||||
|
||||
@Test
|
||||
def parseJSONFailures() {
|
||||
// JSON does not support concatenations
|
||||
parseJSONFailuresTest("""{ "foo": 123 456 789 } """, "Expecting close brace } or a comma")
|
||||
@Test
|
||||
def parseJSONFailures() {
|
||||
// JSON does not support concatenations
|
||||
parseJSONFailuresTest("""{ "foo": 123 456 789 } """, "Expecting close brace } or a comma")
|
||||
|
||||
// JSON must begin with { or [
|
||||
parseJSONFailuresTest(""""a": 123, "b": 456"""", "Document must have an object or array at root")
|
||||
// JSON must begin with { or [
|
||||
parseJSONFailuresTest(""""a": 123, "b": 456"""", "Document must have an object or array at root")
|
||||
|
||||
// JSON does not support unquoted text
|
||||
parseJSONFailuresTest("""{"foo": unquotedtext}""", "Token not allowed in valid JSON")
|
||||
// JSON does not support unquoted text
|
||||
parseJSONFailuresTest("""{"foo": unquotedtext}""", "Token not allowed in valid JSON")
|
||||
|
||||
// JSON does not support substitutions
|
||||
parseJSONFailuresTest("""{"foo": ${"a.b"}}""", "Substitutions (${} syntax) not allowed in JSON")
|
||||
// JSON does not support substitutions
|
||||
parseJSONFailuresTest("""{"foo": ${"a.b"}}""", "Substitutions (${} syntax) not allowed in JSON")
|
||||
|
||||
// JSON does not support multi-element paths
|
||||
parseJSONFailuresTest("""{"foo"."bar": 123}""", "Token not allowed in valid JSON")
|
||||
// JSON does not support multi-element paths
|
||||
parseJSONFailuresTest("""{"foo"."bar": 123}""", "Token not allowed in valid JSON")
|
||||
|
||||
// JSON does not support =
|
||||
parseJSONFailuresTest("""{"foo"=123}""", """Key '"foo"' may not be followed by token: '='""")
|
||||
// JSON does not support =
|
||||
parseJSONFailuresTest("""{"foo"=123}""", """Key '"foo"' may not be followed by token: '='""")
|
||||
|
||||
// JSON does not support +=
|
||||
parseJSONFailuresTest("""{"foo" += "bar"}""", """Key '"foo"' may not be followed by token: '+='""")
|
||||
// JSON does not support +=
|
||||
parseJSONFailuresTest("""{"foo" += "bar"}""", """Key '"foo"' may not be followed by token: '+='""")
|
||||
|
||||
// JSON does not support duplicate keys
|
||||
parseJSONFailuresTest("""{"foo" : 123, "foo": 456}""", "JSON does not allow duplicate fields")
|
||||
// JSON does not support duplicate keys
|
||||
parseJSONFailuresTest("""{"foo" : 123, "foo": 456}""", "JSON does not allow duplicate fields")
|
||||
|
||||
// JSON does not support trailing commas
|
||||
parseJSONFailuresTest("""{"foo" : 123,}""", "expecting a field name after a comma, got a close brace } instead")
|
||||
// JSON does not support trailing commas
|
||||
parseJSONFailuresTest("""{"foo" : 123,}""", "expecting a field name after a comma, got a close brace } instead")
|
||||
|
||||
// JSON does not support empty documents
|
||||
parseJSONFailuresTest("", "Empty document")
|
||||
// JSON does not support empty documents
|
||||
parseJSONFailuresTest("", "Empty document")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
def parseSingleValues() {
|
||||
// Parse simple values
|
||||
parseSimpleValueTest("123")
|
||||
parseSimpleValueTest("123.456")
|
||||
parseSimpleValueTest(""""a string"""")
|
||||
parseSimpleValueTest("true")
|
||||
parseSimpleValueTest("false")
|
||||
parseSimpleValueTest("null")
|
||||
@Test
|
||||
def parseSingleValues() {
|
||||
// Parse simple values
|
||||
parseSimpleValueTest("123")
|
||||
parseSimpleValueTest("123.456")
|
||||
parseSimpleValueTest(""""a string"""")
|
||||
parseSimpleValueTest("true")
|
||||
parseSimpleValueTest("false")
|
||||
parseSimpleValueTest("null")
|
||||
|
||||
// Parse Simple Value throws out trailing and leading whitespace
|
||||
parseSimpleValueTest(" 123", "123")
|
||||
parseSimpleValueTest("123 ", "123")
|
||||
parseSimpleValueTest(" 123 ", "123")
|
||||
// Parse Simple Value throws out trailing and leading whitespace
|
||||
parseSimpleValueTest(" 123", "123")
|
||||
parseSimpleValueTest("123 ", "123")
|
||||
parseSimpleValueTest(" 123 ", "123")
|
||||
|
||||
// Can parse complex values
|
||||
parseComplexValueTest("""{"a": "b"}""")
|
||||
parseComplexValueTest("""["a","b","c"]""")
|
||||
// Can parse complex values
|
||||
parseComplexValueTest("""{"a": "b"}""")
|
||||
parseComplexValueTest("""["a","b","c"]""")
|
||||
|
||||
parseSingleValueInvalidJSONTest("unquotedtext", "Token not allowed in valid JSON")
|
||||
parseSingleValueInvalidJSONTest("${a.b}", "Substitutions (${} syntax) not allowed in JSON")
|
||||
parseSingleValueInvalidJSONTest("unquotedtext", "Token not allowed in valid JSON")
|
||||
parseSingleValueInvalidJSONTest("${a.b}", "Substitutions (${} syntax) not allowed in JSON")
|
||||
|
||||
// Check that concatenations are handled by CONF parsing
|
||||
var origText = "123 456 unquotedtext abc"
|
||||
var node = ConfigDocumentParser.parseValue(tokenize(origText), ConfigParseOptions.defaults())
|
||||
assertEquals(origText, node.render())
|
||||
// Check that concatenations are handled by CONF parsing
|
||||
var origText = "123 456 unquotedtext abc"
|
||||
var node = ConfigDocumentParser.parseValue(tokenize(origText), ConfigParseOptions.defaults())
|
||||
assertEquals(origText, node.render())
|
||||
|
||||
// Check that concatenations in JSON will only return the first value passed in
|
||||
origText = "123 456 789"
|
||||
node = ConfigDocumentParser.parseValue(tokenize(origText), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
||||
assertEquals("123", node.render())
|
||||
}
|
||||
// Check that concatenations in JSON will throw an error
|
||||
origText = "123 456 789"
|
||||
var exceptionThrown = false
|
||||
try {
|
||||
node = ConfigDocumentParser.parseValue(tokenize(origText), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
exceptionThrown = true
|
||||
assertTrue(e.isInstanceOf[ConfigException])
|
||||
assertTrue(e.getMessage.contains("Tried to parse a concatenation. Concatenations not allowed in valid JSON"))
|
||||
}
|
||||
assertTrue(exceptionThrown)
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
package com.typesafe.config.impl
|
||||
|
||||
import java.io.{BufferedReader, FileReader}
|
||||
import java.io.{ BufferedReader, FileReader }
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.{Paths, Files}
|
||||
import java.nio.file.{ Paths, Files }
|
||||
|
||||
import com.typesafe.config.{ConfigException, ConfigSyntax, ConfigParseOptions, ConfigDocumentFactory}
|
||||
import com.typesafe.config._
|
||||
import org.junit.Assert._
|
||||
import org.junit.Test
|
||||
|
||||
@ -36,7 +36,7 @@ class ConfigDocumentTest extends TestUtils {
|
||||
|
||||
// Can handle parsing/replacement with a complicated map
|
||||
var origText =
|
||||
"""{
|
||||
"""{
|
||||
"a":123,
|
||||
"b": 123.456,
|
||||
"c": true,
|
||||
@ -53,7 +53,7 @@ class ConfigDocumentTest extends TestUtils {
|
||||
}
|
||||
}"""
|
||||
var finalText =
|
||||
"""{
|
||||
"""{
|
||||
"a":123,
|
||||
"b": 123.456,
|
||||
"c": true,
|
||||
@ -72,10 +72,9 @@ class ConfigDocumentTest extends TestUtils {
|
||||
configDocumentReplaceConfTest(origText, finalText, """"i am now a string"""", "h.b.a")
|
||||
configDocumentReplaceJsonTest(origText, finalText, """"i am now a string"""", "h.b.a")
|
||||
|
||||
|
||||
// Can handle replacing values with maps
|
||||
finalText =
|
||||
"""{
|
||||
"""{
|
||||
"a":123,
|
||||
"b": 123.456,
|
||||
"c": true,
|
||||
@ -96,7 +95,7 @@ class ConfigDocumentTest extends TestUtils {
|
||||
|
||||
// Can handle replacing values with arrays
|
||||
finalText =
|
||||
"""{
|
||||
"""{
|
||||
"a":123,
|
||||
"b": 123.456,
|
||||
"c": true,
|
||||
@ -116,7 +115,7 @@ class ConfigDocumentTest extends TestUtils {
|
||||
configDocumentReplaceJsonTest(origText, finalText, "[1,2,3,4,5]", "h.b.a")
|
||||
|
||||
finalText =
|
||||
"""{
|
||||
"""{
|
||||
"a":123,
|
||||
"b": 123.456,
|
||||
"c": true,
|
||||
@ -133,7 +132,7 @@ class ConfigDocumentTest extends TestUtils {
|
||||
}
|
||||
}"""
|
||||
configDocumentReplaceConfTest(origText, finalText,
|
||||
"this is a concatenation 123 456 {a:b} [1,2,3] {a: this is another 123 concatenation null true}", "h.b.a")
|
||||
"this is a concatenation 123 456 {a:b} [1,2,3] {a: this is another 123 concatenation null true}", "h.b.a")
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -167,7 +166,20 @@ class ConfigDocumentTest extends TestUtils {
|
||||
}
|
||||
|
||||
@Test
|
||||
def configDocumentReplaceFailure {
|
||||
def configDocumentSetNewConfigValue {
|
||||
val origText = "{\"a\": \"b\"}"
|
||||
val finalText = "{\"a\": 12}"
|
||||
val configDocHOCON = ConfigDocumentFactory.parseString(origText)
|
||||
val configDocJSON = ConfigDocumentFactory.parseString(origText, ConfigParseOptions.defaults.setSyntax(ConfigSyntax.JSON))
|
||||
val newValue = ConfigValueFactory.fromAnyRef(12)
|
||||
assertEquals(origText, configDocHOCON.render())
|
||||
assertEquals(origText, configDocJSON.render())
|
||||
assertEquals(finalText, configDocHOCON.setValue("a", newValue).render())
|
||||
assertEquals(finalText, configDocJSON.setValue("a", newValue).render())
|
||||
}
|
||||
|
||||
@Test
|
||||
def configDocumentArrayReplaceFailure {
|
||||
// Attempting a replace on a ConfigDocument parsed from an array throws an error
|
||||
val origText = "[1, 2, 3, 4, 5]"
|
||||
val document = ConfigDocumentFactory.parseString(origText)
|
||||
@ -175,10 +187,46 @@ class ConfigDocumentTest extends TestUtils {
|
||||
try {
|
||||
document.setValue("a", "1")
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
exceptionThrown = true
|
||||
assertTrue(e.isInstanceOf[ConfigException])
|
||||
assertTrue(e.getMessage.contains("ConfigDocument had an array at the root level"))
|
||||
case e: Exception =>
|
||||
exceptionThrown = true
|
||||
assertTrue(e.isInstanceOf[ConfigException])
|
||||
assertTrue(e.getMessage.contains("ConfigDocument had an array at the root level"))
|
||||
}
|
||||
assertTrue(exceptionThrown)
|
||||
}
|
||||
|
||||
@Test
|
||||
def configDocumentJSONReplaceFailure {
|
||||
// Attempting a replace on a ConfigDocument parsed from JSON with a value using HOCON syntax
|
||||
// will fail
|
||||
val origText = "{\"foo\": \"bar\", \"baz\": \"qux\"}"
|
||||
val document = ConfigDocumentFactory.parseString(origText, ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
||||
var exceptionThrown = false
|
||||
try {
|
||||
document.setValue("foo", "unquoted")
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
exceptionThrown = true
|
||||
assertTrue(e.isInstanceOf[ConfigException])
|
||||
assertTrue(e.getMessage.contains("Token not allowed in valid JSON"))
|
||||
}
|
||||
assertTrue(exceptionThrown)
|
||||
}
|
||||
|
||||
@Test
|
||||
def configDocumentJSONReplaceWithConcatenationFailure {
|
||||
// Attempting a replace on a ConfigDocument parsed from JSON with a concatenation will
|
||||
// fail
|
||||
val origText = "{\"foo\": \"bar\", \"baz\": \"qux\"}"
|
||||
val document = ConfigDocumentFactory.parseString(origText, ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
||||
var exceptionThrown = false
|
||||
try {
|
||||
document.setValue("foo", "1 2 3 concatenation")
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
exceptionThrown = true
|
||||
assertTrue(e.isInstanceOf[ConfigException])
|
||||
assertTrue(e.getMessage.contains("Tried to parse a concatenation. Concatenations not allowed in valid JSON"))
|
||||
}
|
||||
assertTrue(exceptionThrown)
|
||||
}
|
||||
@ -205,4 +253,5 @@ class ConfigDocumentTest extends TestUtils {
|
||||
val configDocumentFile = ConfigDocumentFactory.parseFile(resourceFile("/test03.conf"))
|
||||
assertEquals(configDocumentFile.render(), configDocument.render())
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -33,8 +33,8 @@ class ConfigNodeTest extends TestUtils {
|
||||
|
||||
private def topLevelValueReplaceTest(value: AbstractConfigNodeValue, newValue: AbstractConfigNodeValue, key: String = "foo") {
|
||||
val complexNodeChildren = List(nodeOpenBrace,
|
||||
nodeKeyValuePair(nodeWhitespace(" "), configNodeKey(key),value, nodeWhitespace(" ")),
|
||||
nodeCloseBrace)
|
||||
nodeKeyValuePair(nodeWhitespace(" "), configNodeKey(key), value, nodeWhitespace(" ")),
|
||||
nodeCloseBrace)
|
||||
val complexNode = configNodeObject(complexNodeChildren)
|
||||
val newNode = complexNode.setValueOnPath(key, newValue)
|
||||
val origText = "{ " + key + " : " + value.render() + " }"
|
||||
@ -151,8 +151,8 @@ class ConfigNodeTest extends TestUtils {
|
||||
|
||||
// Ensure maps can be replaced
|
||||
val nestedMap = configNodeObject(List(nodeOpenBrace, configNodeKey("abc"),
|
||||
nodeColon, configNodeSimpleValue(tokenString("a string")),
|
||||
nodeCloseBrace))
|
||||
nodeColon, configNodeSimpleValue(tokenString("a string")),
|
||||
nodeCloseBrace))
|
||||
topLevelValueReplaceTest(nestedMap, nodeInt(10))
|
||||
topLevelValueReplaceTest(nodeInt(10), nestedMap)
|
||||
topLevelValueReplaceTest(array, nestedMap)
|
||||
@ -168,7 +168,6 @@ class ConfigNodeTest extends TestUtils {
|
||||
topLevelValueReplaceTest(array, concatenation)
|
||||
topLevelValueReplaceTest(concatenation, array)
|
||||
|
||||
|
||||
//Ensure a key with format "a.b" will be properly replaced
|
||||
topLevelValueReplaceTest(nodeInt(10), nestedMap, "foo.bar")
|
||||
}
|
||||
@ -195,25 +194,25 @@ class ConfigNodeTest extends TestUtils {
|
||||
def replaceNestedNodes() {
|
||||
// Test that all features of node replacement in a map work in a complex map containing nested maps
|
||||
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}"
|
||||
"def : \"this is a string\"\n\t\tghi : ${\"a.b\"}\n\t}\n}\nbaz.abc.ghi : 52\nbaz.abc.ghi : 53\n}"
|
||||
val lowestLevelMap = configNodeObject(List(nodeOpenBrace, nodeLine(6),
|
||||
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))
|
||||
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 = configNodeObject(List(nodeOpenBrace, nodeLine(2),
|
||||
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"), configNodeKey("abc"), lowestLevelMap, nodeLine(9)), nodeWhitespace(""),
|
||||
nodeCloseBrace))
|
||||
val origNode = configNodeObject(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))
|
||||
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"), configNodeKey("abc"), lowestLevelMap, nodeLine(9)), nodeWhitespace(""),
|
||||
nodeCloseBrace))
|
||||
val origNode = configNodeObject(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\"this.does.not.exist@@@+$#\" : {\nend : doesnotexist\n}\n}\n}\nbaz.abc.ghi : randomunquotedString\n}"
|
||||
"def : false\n\t\n\"this.does.not.exist@@@+$#\" : {\nend : doesnotexist\n}\n}\n}\nbaz.abc.ghi : randomunquotedString\n}"
|
||||
|
||||
//Can replace settings in nested maps
|
||||
// Paths with quotes in the name are treated as a single Path, rather than multiple sub-paths
|
||||
|
Loading…
Reference in New Issue
Block a user