mirror of
https://github.com/lightbend/config.git
synced 2025-03-14 19:30:25 +08:00
merge
This commit is contained in:
parent
f086a0902a
commit
7a423d3824
28
NEWS.md
28
NEWS.md
@ -1,22 +1,12 @@
|
||||
# 1.3.0-M3: April 21, 2015
|
||||
# 1.3.0: May 8, 2015
|
||||
|
||||
- this is an ABI-not-guaranteed beta release in advance
|
||||
of 1.3.0. Please see the notes for 1.3.0-M1 below for warnings,
|
||||
caveats, and the bulk of what's changed since 1.2.1.
|
||||
|
||||
API changes (since 1.3.0-M2):
|
||||
|
||||
- renamed some methods in the new ConfigDocument for consistency
|
||||
with Config (breaks ABI vs. 1.3.0-M2, but not vs. any stable
|
||||
release)
|
||||
|
||||
Fixes:
|
||||
|
||||
- couple of bugfixes in ConfigDocument
|
||||
|
||||
Thank you to contributors with commits since v1.3.0-M2 tag:
|
||||
|
||||
- Preben Ingvaldsen
|
||||
- no changes from 1.3.0-M3
|
||||
- this is an ABI-guaranteed stable release.
|
||||
- 1.3.0 should be ABI-compatible with 1.2.x for most applications,
|
||||
though there are enough changes to corner cases and
|
||||
implementation that some obscure things could break. Please see
|
||||
the notes for the 1.3.0 milestones below for details, especially
|
||||
for 1.3.0-M1.
|
||||
|
||||
# 1.3.0-M3: April 21, 2015
|
||||
|
||||
@ -392,4 +382,4 @@ Thank you to contributors with commits since v1.2.1 tag:
|
||||
`${foo}` syntax) without getting confused; just about anything
|
||||
that makes conceptual sense should now work. Only inherently
|
||||
circular config files should fail.
|
||||
- some minor documentation fixes.
|
||||
- some minor documentation fixes.
|
21
README.md
21
README.md
@ -93,19 +93,19 @@ The license is Apache 2.0, see LICENSE-2.0.txt.
|
||||
### Binary Releases
|
||||
|
||||
Version 1.2.1 and earlier were built for Java 6, while newer
|
||||
versions (1.3.0-M1 is the current beta) will be built for Java 8.
|
||||
versions (1.3.0 and above) will be built for Java 8.
|
||||
|
||||
You can find published releases on Maven Central.
|
||||
|
||||
<dependency>
|
||||
<groupId>com.typesafe</groupId>
|
||||
<artifactId>config</artifactId>
|
||||
<version>1.2.1</version>
|
||||
<version>1.3.0</version>
|
||||
</dependency>
|
||||
|
||||
sbt dependency:
|
||||
|
||||
libraryDependencies += "com.typesafe" % "config" % "1.2.1"
|
||||
libraryDependencies += "com.typesafe" % "config" % "1.3.0"
|
||||
|
||||
Link for direct download if you don't use a dependency manager:
|
||||
|
||||
@ -135,6 +135,10 @@ Typesafe Contributor License Agreement online, using your GitHub
|
||||
account - it takes 30 seconds. You can do this at
|
||||
http://www.typesafe.com/contribute/cla
|
||||
|
||||
Please see
|
||||
[CONTRIBUTING](https://github.com/typesafehub/config/blob/master/CONTRIBUTING.md)
|
||||
for more including how to make a release.
|
||||
|
||||
### Build
|
||||
|
||||
The build uses sbt and the tests are written in Scala; however,
|
||||
@ -748,6 +752,14 @@ If you have trouble with your configuration, some useful tips.
|
||||
Currently the library is maintained against Java 8, but
|
||||
version 1.2.1 and earlier will work with Java 6.
|
||||
|
||||
Please use 1.2.1 if you need Java 6 support, though some people
|
||||
have expressed interest in a branch off of 1.3.0 supporting
|
||||
Java 7. If you want to work on that branch you might bring it up
|
||||
on [chat](https://gitter.im/typesafehub/config). We can release a
|
||||
jar for Java 7 if someone(s) steps up to maintain the branch. The
|
||||
master branch does not use Java 8 "gratuitously" but some APIs
|
||||
that use Java 8 types will need to be removed.
|
||||
|
||||
### Rationale for Supported File Formats
|
||||
|
||||
(For the curious.)
|
||||
@ -800,6 +812,9 @@ your wrapper, just send a pull request for this README. We would
|
||||
love to know what you're doing with this library or with the HOCON
|
||||
format.
|
||||
|
||||
#### Guice integration
|
||||
* Typesafe Config Guice https://github.com/racc/typesafeconfig-guice
|
||||
|
||||
#### Scala wrappers for the Java library
|
||||
|
||||
* Ficus https://github.com/ceedubs/ficus
|
||||
|
@ -193,6 +193,13 @@ public class ConfigBeanImpl {
|
||||
return config.getObjectList(configPropName);
|
||||
} else if (elementType == ConfigValue.class) {
|
||||
return config.getList(configPropName);
|
||||
} else if (hasAtLeastOneBeanProperty((Class<?>) elementType)) {
|
||||
List<Object> beanList = new ArrayList<Object>();
|
||||
List<? extends Config> configList = config.getConfigList(configPropName);
|
||||
for (Config listMember : configList) {
|
||||
beanList.add(createInternal(listMember, (Class<?>) elementType));
|
||||
}
|
||||
return beanList;
|
||||
} else {
|
||||
throw new ConfigException.BadBean("Bean property '" + configPropName + "' of class " + beanClass.getName() + " has unsupported list element type " + elementType);
|
||||
}
|
||||
@ -245,4 +252,4 @@ public class ConfigBeanImpl {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -270,6 +270,11 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements Unmergeabl
|
||||
render(stack, sb, indent, atRoot, atKey, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
|
||||
render(sb, indent, atRoot, null, options);
|
||||
}
|
||||
|
||||
// static method also used by ConfigDelayedMergeObject.
|
||||
static void render(List<AbstractConfigValue> stack, StringBuilder sb, int indent, boolean atRoot, String atKey,
|
||||
ConfigRenderOptions options) {
|
||||
@ -334,4 +339,4 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements Unmergeabl
|
||||
sb.append("# ) end of unresolved merge\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -32,6 +32,10 @@ final public class ConfigImplUtil {
|
||||
return a.equals(b);
|
||||
}
|
||||
|
||||
static boolean isC0Control(int codepoint) {
|
||||
return (codepoint >= 0x0000 && codepoint <= 0x001F);
|
||||
}
|
||||
|
||||
public static String renderJsonString(String s) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('"');
|
||||
@ -60,7 +64,7 @@ final public class ConfigImplUtil {
|
||||
sb.append("\\t");
|
||||
break;
|
||||
default:
|
||||
if (Character.isISOControl(c))
|
||||
if (isC0Control(c))
|
||||
sb.append(String.format("\\u%04x", (int) c));
|
||||
else
|
||||
sb.append(c);
|
||||
@ -229,4 +233,4 @@ final public class ConfigImplUtil {
|
||||
}
|
||||
return nameBuilder.toString();
|
||||
}
|
||||
}
|
||||
}
|
@ -121,6 +121,9 @@ final class ConfigNodeObject extends ConfigNodeComplexValue {
|
||||
private Collection<AbstractConfigNode> indentation() {
|
||||
boolean seenNewLine = false;
|
||||
ArrayList<AbstractConfigNode> indentation = new ArrayList<AbstractConfigNode>();
|
||||
if (children.isEmpty()) {
|
||||
return indentation;
|
||||
}
|
||||
for (int i = 0; i < children.size(); i++) {
|
||||
if (!seenNewLine) {
|
||||
if (children.get(i) instanceof ConfigNodeSingleToken &&
|
||||
@ -141,19 +144,18 @@ final class ConfigNodeObject extends ConfigNodeComplexValue {
|
||||
}
|
||||
if (indentation.isEmpty()) {
|
||||
indentation.add(new ConfigNodeSingleToken(Tokens.newIgnoredWhitespace(null, " ")));
|
||||
return indentation;
|
||||
} else {
|
||||
// Calculate the indentation of the ending curly-brace to get the indentation of the root object
|
||||
AbstractConfigNode last = children.get(children.size() - 1);
|
||||
if (last instanceof ConfigNodeSingleToken && ((ConfigNodeSingleToken) last).token() == Tokens.CLOSE_CURLY) {
|
||||
AbstractConfigNode beforeLast = children.get(children.size() - 2);
|
||||
String indent = "";
|
||||
if (beforeLast instanceof ConfigNodeSingleToken &&
|
||||
Tokens.isIgnoredWhitespace(((ConfigNodeSingleToken) beforeLast).token())) {
|
||||
String indent = ((ConfigNodeSingleToken) beforeLast).token().tokenText();
|
||||
indent += " ";
|
||||
indentation.add(new ConfigNodeSingleToken(Tokens.newIgnoredWhitespace(null, indent)));
|
||||
return indentation;
|
||||
}
|
||||
Tokens.isIgnoredWhitespace(((ConfigNodeSingleToken) beforeLast).token()))
|
||||
indent = ((ConfigNodeSingleToken) beforeLast).token().tokenText();
|
||||
indent += " ";
|
||||
indentation.add(new ConfigNodeSingleToken(Tokens.newIgnoredWhitespace(null, indent)));
|
||||
return indentation;
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,12 +170,12 @@ final class ConfigNodeObject extends ConfigNodeComplexValue {
|
||||
|
||||
// If the value we're inserting is a complex value, we'll need to indent it for insertion
|
||||
AbstractConfigNodeValue indentedValue;
|
||||
if (value instanceof ConfigNodeComplexValue) {
|
||||
if (value instanceof ConfigNodeComplexValue && !indentation.isEmpty()) {
|
||||
indentedValue = ((ConfigNodeComplexValue) value).indentText(indentation.get(indentation.size() - 1));
|
||||
} else {
|
||||
indentedValue = value;
|
||||
}
|
||||
boolean sameLine = !(indentation.get(0) instanceof ConfigNodeSingleToken &&
|
||||
boolean sameLine = !(indentation.size() > 0 && indentation.get(0) instanceof ConfigNodeSingleToken &&
|
||||
Tokens.isNewline(((ConfigNodeSingleToken) indentation.get(0)).token()));
|
||||
|
||||
// If the path is of length greater than one, see if the value needs to be added further down
|
||||
@ -209,7 +211,10 @@ final class ConfigNodeObject extends ConfigNodeComplexValue {
|
||||
// If the path is of length greater than one add the required new objects along the path
|
||||
ArrayList<AbstractConfigNode> newObjectNodes = new ArrayList<AbstractConfigNode>();
|
||||
newObjectNodes.add(new ConfigNodeSingleToken(Tokens.OPEN_CURLY));
|
||||
newObjectNodes.add(new ConfigNodeSingleToken(Tokens.newIgnoredWhitespace(null, " ")));
|
||||
if (indentation.isEmpty()) {
|
||||
newObjectNodes.add(new ConfigNodeSingleToken(Tokens.newLine(null)));
|
||||
}
|
||||
newObjectNodes.addAll(indentation);
|
||||
newObjectNodes.add(new ConfigNodeSingleToken(Tokens.CLOSE_CURLY));
|
||||
ConfigNodeObject newObject = new ConfigNodeObject(newObjectNodes);
|
||||
newNodes.add(newObject.addValueOnPath(desiredPath.subPath(1), indentedValue, flavor));
|
||||
@ -273,4 +278,4 @@ final class ConfigNodeObject extends ConfigNodeComplexValue {
|
||||
Path path = PathParser.parsePathNode(desiredPath, flavor).value();
|
||||
return changeValueOnPath(path, null, flavor);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,7 @@
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import com.typesafe.config.*;
|
||||
import com.typesafe.config.parser.ConfigDocument;
|
||||
import com.typesafe.config.ConfigParseOptions;
|
||||
import com.typesafe.config.ConfigValue;
|
||||
import com.typesafe.config.ConfigException;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.Iterator;
|
||||
@ -34,7 +32,9 @@ final class SimpleConfigDocument implements ConfigDocument {
|
||||
public ConfigDocument withValue(String path, ConfigValue newValue) {
|
||||
if (newValue == null)
|
||||
throw new ConfigException.BugOrBroken("null value for " + path + " passed to withValue");
|
||||
return withValueText(path, newValue.render().trim());
|
||||
ConfigRenderOptions options = ConfigRenderOptions.defaults();
|
||||
options = options.setOriginComments(false);
|
||||
return withValueText(path, newValue.render(options).trim());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -60,4 +60,4 @@ final class SimpleConfigDocument implements ConfigDocument {
|
||||
public int hashCode() {
|
||||
return render().hashCode();
|
||||
}
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@ final class Tokenizer {
|
||||
return "tab";
|
||||
else if (codepoint == -1)
|
||||
return "end of file";
|
||||
else if (Character.isISOControl(codepoint))
|
||||
else if (ConfigImplUtil.isC0Control(codepoint))
|
||||
return String.format("control character 0x%x", codepoint);
|
||||
else
|
||||
return String.format("%c", codepoint);
|
||||
@ -498,7 +498,7 @@ final class Tokenizer {
|
||||
} else if (c == '"') {
|
||||
sbOrig.appendCodePoint(c);
|
||||
break;
|
||||
} else if (Character.isISOControl(c)) {
|
||||
} else if (ConfigImplUtil.isC0Control(c)) {
|
||||
throw problem(asString(c), "JSON does not allow unescaped " + asString(c)
|
||||
+ " in quoted strings, use a backslash escape");
|
||||
} else {
|
||||
@ -692,4 +692,4 @@ final class Tokenizer {
|
||||
"Does not make sense to remove items from token stream");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -23,6 +23,7 @@ public class ArraysConfig {
|
||||
List<ConfigValue> ofConfigValue;
|
||||
List<Duration> ofDuration;
|
||||
List<ConfigMemorySize> ofMemorySize;
|
||||
List<StringsConfig> ofStringBean;
|
||||
|
||||
public List<Integer> getEmpty() {
|
||||
return empty;
|
||||
@ -127,4 +128,12 @@ public class ArraysConfig {
|
||||
public void setOfMemorySize(List<ConfigMemorySize> ofMemorySize) {
|
||||
this.ofMemorySize = ofMemorySize;
|
||||
}
|
||||
}
|
||||
|
||||
public List<StringsConfig> getOfStringBean() {
|
||||
return ofStringBean;
|
||||
}
|
||||
|
||||
public void setOfStringBean(List<StringsConfig> ofStringBean) {
|
||||
this.ofStringBean = ofStringBean;
|
||||
}
|
||||
}
|
@ -20,4 +20,26 @@ public class StringsConfig {
|
||||
public void setYes(String s) {
|
||||
yes = s;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof StringsConfig) {
|
||||
StringsConfig sc = (StringsConfig) o;
|
||||
return sc.abcd.equals(abcd) &&
|
||||
sc.yes.equals(yes);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = 41 * (41 + abcd.hashCode());
|
||||
return h + yes.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StringsConfig(" + abcd + "," + yes + ")";
|
||||
}
|
||||
}
|
@ -46,10 +46,10 @@ class ConfigBeanFactoryTest extends TestUtils {
|
||||
ConfigBeanFactory.create(config, classOf[ValidationBeanConfig])
|
||||
}
|
||||
|
||||
val expecteds = Seq(Missing("propNotListedInConfig", 67, "string"),
|
||||
WrongType("shouldBeInt", 68, "number", "boolean"),
|
||||
WrongType("should-be-boolean", 69, "boolean", "number"),
|
||||
WrongType("should-be-list", 70, "list", "string"))
|
||||
val expecteds = Seq(Missing("propNotListedInConfig", 77, "string"),
|
||||
WrongType("shouldBeInt", 78, "number", "boolean"),
|
||||
WrongType("should-be-boolean", 79, "boolean", "number"),
|
||||
WrongType("should-be-list", 80, "list", "string"))
|
||||
|
||||
checkValidationException(e, expecteds)
|
||||
}
|
||||
@ -111,6 +111,15 @@ class ConfigBeanFactoryTest extends TestUtils {
|
||||
ConfigMemorySize.ofBytes(1048576),
|
||||
ConfigMemorySize.ofBytes(1073741824)),
|
||||
beanConfig.getOfMemorySize.asScala)
|
||||
|
||||
val stringsConfigOne = new StringsConfig();
|
||||
stringsConfigOne.setAbcd("testAbcdOne")
|
||||
stringsConfigOne.setYes("testYesOne")
|
||||
val stringsConfigTwo = new StringsConfig();
|
||||
stringsConfigTwo.setAbcd("testAbcdTwo")
|
||||
stringsConfigTwo.setYes("testYesTwo")
|
||||
|
||||
assertEquals(List(stringsConfigOne, stringsConfigTwo).asJava, beanConfig.getOfStringBean)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -200,4 +209,4 @@ class ConfigBeanFactoryTest extends TestUtils {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -171,14 +171,14 @@ class ConfigDocumentTest extends TestUtils {
|
||||
@Test
|
||||
def configDocumentSetNewValueMultiLevelConf {
|
||||
val origText = "a:b\nc:d"
|
||||
val finalText = "a:b\nc:d\ne : { f : { g : 12 } }"
|
||||
val finalText = "a:b\nc:d\ne : {\n f : {\n g : 12\n }\n}"
|
||||
configDocumentReplaceConfTest(origText, finalText, "12", "e.f.g")
|
||||
}
|
||||
|
||||
@Test
|
||||
def configDocumentSetNewValueMultiLevelJson {
|
||||
val origText = "{\"a\":\"b\",\n\"c\":\"d\"}"
|
||||
val finalText = "{\"a\":\"b\",\n\"c\":\"d\",\n\"e\" : { \"f\" : { \"g\" : 12 } }}"
|
||||
val finalText = "{\"a\":\"b\",\n\"c\":\"d\",\n \"e\" : {\n \"f\" : {\n \"g\" : 12\n }\n }}"
|
||||
configDocumentReplaceJsonTest(origText, finalText, "12", "e.f.g")
|
||||
}
|
||||
|
||||
@ -342,13 +342,13 @@ class ConfigDocumentTest extends TestUtils {
|
||||
var configDocument = ConfigDocumentFactory.parseString(origText)
|
||||
assertEquals("a {\n b: c\n e : f\n}", configDocument.withValueText("a.e", "f").render())
|
||||
|
||||
assertEquals("a {\n b: c\n d : { e : { f : g } }\n}", configDocument.withValueText("a.d.e.f", "g").render())
|
||||
assertEquals("a {\n b: c\n d : {\n e : {\n f : g\n }\n }\n}", configDocument.withValueText("a.d.e.f", "g").render())
|
||||
|
||||
origText = "a {\n b: c\n}\n"
|
||||
configDocument = ConfigDocumentFactory.parseString(origText)
|
||||
assertEquals("a {\n b: c\n}\nd : e\n", configDocument.withValueText("d", "e").render())
|
||||
|
||||
assertEquals("a {\n b: c\n}\nd : { e : { f : g } }\n", configDocument.withValueText("d.e.f", "g").render())
|
||||
assertEquals("a {\n b: c\n}\nd : {\n e : {\n f : g\n }\n}\n", configDocument.withValueText("d.e.f", "g").render())
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -435,7 +435,14 @@ class ConfigDocumentTest extends TestUtils {
|
||||
val origText = ""
|
||||
val configDocument = ConfigDocumentFactory.parseString(origText)
|
||||
|
||||
assertEquals(" a : 1", configDocument.withValueText("a", "1").render)
|
||||
assertEquals("a : 1", configDocument.withValueText("a", "1").render)
|
||||
|
||||
val mapVal = ConfigValueFactory.fromAnyRef(Map("a" -> 1, "b" -> 2).asJava)
|
||||
assertEquals("a : {\n \"a\" : 1,\n \"b\" : 2\n}",
|
||||
configDocument.withValue("a", mapVal).render)
|
||||
|
||||
val arrayVal = ConfigValueFactory.fromAnyRef(List(1, 2).asJava)
|
||||
assertEquals("a : [\n 1,\n 2\n]", configDocument.withValue("a", arrayVal).render)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -445,9 +452,7 @@ class ConfigDocumentTest extends TestUtils {
|
||||
|
||||
val configVal = ConfigValueFactory.fromAnyRef(Map("a" -> 1, "b" -> 2).asJava)
|
||||
|
||||
assertEquals("{ a : {\n # hardcoded value\n \"a\" : 1,\n # hardcoded value\n \"b\" : 2\n } }",
|
||||
assertEquals("{ a : {\n \"a\" : 1,\n \"b\" : 2\n } }",
|
||||
configDocument.withValue("a", configVal).render)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -212,7 +212,7 @@ class ConfigNodeTest extends TestUtils {
|
||||
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\t\n\t\t\"this.does.not.exist@@@+$#\" : { end : doesnotexist }\n\t}\n}\n\nbaz.abc.ghi : randomunquotedString\n}"
|
||||
"def : false\n\t\t\n\t\t\"this.does.not.exist@@@+$#\" : {\n\t\t end : doesnotexist\n\t\t}\n\t}\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
|
||||
@ -229,4 +229,4 @@ class ConfigNodeTest extends TestUtils {
|
||||
assertEquals(finalText, newNode.render())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user