mirror of
https://github.com/lightbend/config.git
synced 2025-03-22 23:30:27 +08:00
Support variable substitution in includes
This commit is contained in:
parent
aac122ae01
commit
730ad27628
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,6 +1,7 @@
|
|||||||
.classpath
|
.classpath
|
||||||
.project
|
.project
|
||||||
.cache
|
.cache
|
||||||
|
.DS_Store
|
||||||
.settings
|
.settings
|
||||||
.idea
|
.idea
|
||||||
.idea_modules
|
.idea_modules
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
# Submitting pull requests
|
|
||||||
|
|
||||||
Pull requests should go via GitHub; there are some nice
|
|
||||||
[general guidelines for contributing on GitHub](https://guides.github.com/activities/contributing-to-open-source/)
|
|
||||||
if you haven't done it before.
|
|
||||||
|
|
||||||
Unless your fix is trivial, it's a good idea to look for related
|
|
||||||
discussions and maybe open an issue to discuss, before making a
|
|
||||||
pull request. Discussion in advance may keep you from wasting time
|
|
||||||
on an approach that won't work out. However, if you communicate
|
|
||||||
better in code than in words, starting with a patch is
|
|
||||||
fine... just be willing to revise it!
|
|
||||||
|
|
||||||
Before we can accept pull requests, you will need to agree to the
|
|
||||||
Typesafe Contributor License Agreement online, using your GitHub
|
|
||||||
account - it takes 30 seconds. You can do this at
|
|
||||||
http://www.typesafe.com/contribute/cla
|
|
||||||
|
|
||||||
Expect that most PRs will need revision before merge. If people
|
|
||||||
suggest revisions, you can make them yourself or wait for a
|
|
||||||
maintainer to do it on their own timeline. The larger the PR, the
|
|
||||||
more revision will likely be needed.
|
|
||||||
|
|
||||||
# Making a release
|
|
||||||
|
|
||||||
To make a release you'll need to be a maintainer with GitHub
|
|
||||||
permissions to push to the master and gh-pages branches, and
|
|
||||||
Sonatype permissions to publish.
|
|
||||||
|
|
||||||
Here are the steps, which should be automated but aren't (PR
|
|
||||||
welcome!):
|
|
||||||
|
|
||||||
1. write release notes in NEWS.md following the format
|
|
||||||
already in there. commit.
|
|
||||||
2. create a signed git tag "vX.Y.Z"
|
|
||||||
3. start sbt; `show version` should confirm that the version was
|
|
||||||
taken from the tag
|
|
||||||
4. clean
|
|
||||||
5. test (double check that release works)
|
|
||||||
6. doc (double check that docs build, plus build docs
|
|
||||||
to be copied to gh-pages later)
|
|
||||||
7. if test or doc fails, delete the tag, fix it, start over.
|
|
||||||
8. publishSigned
|
|
||||||
9. make a separate clone of the repo in another directory and
|
|
||||||
check out the gh-pages branch
|
|
||||||
10. /bin/rm -rf latest/api on gh-pages checkout
|
|
||||||
11. copy config/target/api from master checkout to vX.Y.Z in
|
|
||||||
gh-pages so you have vX.Y.Z/index.html
|
|
||||||
12. copy config/target/api from master checkout into latest/
|
|
||||||
so you have latest/api/index.html
|
|
||||||
13. commit all that to gh-pages, check the diff for sanity
|
|
||||||
(latest/api should be mostly just changed timestamps)
|
|
||||||
14. push gh-pages
|
|
||||||
15. log into sonatype website and go through the usual hoops
|
|
||||||
(search for com.typesafe, verify the artifacts in it, close,
|
|
||||||
release)
|
|
||||||
16. push the "vX.Y.Z" tag
|
|
||||||
17. announce release, possibly wait for maven central to sync
|
|
||||||
first
|
|
13
HOCON.md
13
HOCON.md
@ -1025,6 +1025,19 @@ Then the `${x}` in "foo.conf", which has been fixed up to
|
|||||||
`${a.x}`, would evaluate to `42` rather than to `10`.
|
`${a.x}`, would evaluate to `42` rather than to `10`.
|
||||||
Substitution happens _after_ parsing the whole configuration.
|
Substitution happens _after_ parsing the whole configuration.
|
||||||
|
|
||||||
|
Includes inside of lists use the list index as a path segment when
|
||||||
|
fixing up substitutions.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
{
|
||||||
|
a : [ { include "foo.conf" } ]
|
||||||
|
a.0.x : 42
|
||||||
|
}
|
||||||
|
|
||||||
|
Since the `${x}` in "foo.conf" would be fixed up to `${a.0.x}' we
|
||||||
|
get our expected value of 42.
|
||||||
|
|
||||||
However, there are plenty of cases where the included file might
|
However, there are plenty of cases where the included file might
|
||||||
intend to refer to the application's root config. For example, to
|
intend to refer to the application's root config. For example, to
|
||||||
get a value from a system property or from the reference
|
get a value from a system property or from the reference
|
||||||
|
10
NEWS.md
10
NEWS.md
@ -1,13 +1,3 @@
|
|||||||
# 1.3.0: May 8, 2015
|
|
||||||
|
|
||||||
- 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
|
# 1.3.0-M3: April 21, 2015
|
||||||
|
|
||||||
- this is an ABI-not-guaranteed beta release in advance
|
- this is an ABI-not-guaranteed beta release in advance
|
||||||
|
21
README.md
21
README.md
@ -93,19 +93,19 @@ The license is Apache 2.0, see LICENSE-2.0.txt.
|
|||||||
### Binary Releases
|
### Binary Releases
|
||||||
|
|
||||||
Version 1.2.1 and earlier were built for Java 6, while newer
|
Version 1.2.1 and earlier were built for Java 6, while newer
|
||||||
versions (1.3.0 and above) will be built for Java 8.
|
versions (1.3.0-M1 is the current beta) will be built for Java 8.
|
||||||
|
|
||||||
You can find published releases on Maven Central.
|
You can find published releases on Maven Central.
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.typesafe</groupId>
|
<groupId>com.typesafe</groupId>
|
||||||
<artifactId>config</artifactId>
|
<artifactId>config</artifactId>
|
||||||
<version>1.3.0</version>
|
<version>1.2.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
sbt dependency:
|
sbt dependency:
|
||||||
|
|
||||||
libraryDependencies += "com.typesafe" % "config" % "1.3.0"
|
libraryDependencies += "com.typesafe" % "config" % "1.2.1"
|
||||||
|
|
||||||
Link for direct download if you don't use a dependency manager:
|
Link for direct download if you don't use a dependency manager:
|
||||||
|
|
||||||
@ -135,10 +135,6 @@ Typesafe Contributor License Agreement online, using your GitHub
|
|||||||
account - it takes 30 seconds. You can do this at
|
account - it takes 30 seconds. You can do this at
|
||||||
http://www.typesafe.com/contribute/cla
|
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
|
### Build
|
||||||
|
|
||||||
The build uses sbt and the tests are written in Scala; however,
|
The build uses sbt and the tests are written in Scala; however,
|
||||||
@ -752,14 +748,6 @@ If you have trouble with your configuration, some useful tips.
|
|||||||
Currently the library is maintained against Java 8, but
|
Currently the library is maintained against Java 8, but
|
||||||
version 1.2.1 and earlier will work with Java 6.
|
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
|
### Rationale for Supported File Formats
|
||||||
|
|
||||||
(For the curious.)
|
(For the curious.)
|
||||||
@ -812,9 +800,6 @@ 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
|
love to know what you're doing with this library or with the HOCON
|
||||||
format.
|
format.
|
||||||
|
|
||||||
#### Guice integration
|
|
||||||
* Typesafe Config Guice https://github.com/racc/typesafeconfig-guice
|
|
||||||
|
|
||||||
#### Scala wrappers for the Java library
|
#### Scala wrappers for the Java library
|
||||||
|
|
||||||
* Ficus https://github.com/ceedubs/ficus
|
* Ficus https://github.com/ceedubs/ficus
|
||||||
|
@ -193,13 +193,6 @@ public class ConfigBeanImpl {
|
|||||||
return config.getObjectList(configPropName);
|
return config.getObjectList(configPropName);
|
||||||
} else if (elementType == ConfigValue.class) {
|
} else if (elementType == ConfigValue.class) {
|
||||||
return config.getList(configPropName);
|
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 {
|
} else {
|
||||||
throw new ConfigException.BadBean("Bean property '" + configPropName + "' of class " + beanClass.getName() + " has unsupported list element type " + elementType);
|
throw new ConfigException.BadBean("Bean property '" + configPropName + "' of class " + beanClass.getName() + " has unsupported list element type " + elementType);
|
||||||
}
|
}
|
||||||
|
@ -270,11 +270,6 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements Unmergeabl
|
|||||||
render(stack, sb, indent, atRoot, atKey, options);
|
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 method also used by ConfigDelayedMergeObject.
|
||||||
static void render(List<AbstractConfigValue> stack, StringBuilder sb, int indent, boolean atRoot, String atKey,
|
static void render(List<AbstractConfigValue> stack, StringBuilder sb, int indent, boolean atRoot, String atKey,
|
||||||
ConfigRenderOptions options) {
|
ConfigRenderOptions options) {
|
||||||
|
@ -32,10 +32,6 @@ final public class ConfigImplUtil {
|
|||||||
return a.equals(b);
|
return a.equals(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isC0Control(int codepoint) {
|
|
||||||
return (codepoint >= 0x0000 && codepoint <= 0x001F);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String renderJsonString(String s) {
|
public static String renderJsonString(String s) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append('"');
|
sb.append('"');
|
||||||
@ -64,7 +60,7 @@ final public class ConfigImplUtil {
|
|||||||
sb.append("\\t");
|
sb.append("\\t");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (isC0Control(c))
|
if (Character.isISOControl(c))
|
||||||
sb.append(String.format("\\u%04x", (int) c));
|
sb.append(String.format("\\u%04x", (int) c));
|
||||||
else
|
else
|
||||||
sb.append(c);
|
sb.append(c);
|
||||||
|
@ -121,9 +121,6 @@ final class ConfigNodeObject extends ConfigNodeComplexValue {
|
|||||||
private Collection<AbstractConfigNode> indentation() {
|
private Collection<AbstractConfigNode> indentation() {
|
||||||
boolean seenNewLine = false;
|
boolean seenNewLine = false;
|
||||||
ArrayList<AbstractConfigNode> indentation = new ArrayList<AbstractConfigNode>();
|
ArrayList<AbstractConfigNode> indentation = new ArrayList<AbstractConfigNode>();
|
||||||
if (children.isEmpty()) {
|
|
||||||
return indentation;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < children.size(); i++) {
|
for (int i = 0; i < children.size(); i++) {
|
||||||
if (!seenNewLine) {
|
if (!seenNewLine) {
|
||||||
if (children.get(i) instanceof ConfigNodeSingleToken &&
|
if (children.get(i) instanceof ConfigNodeSingleToken &&
|
||||||
@ -144,18 +141,19 @@ final class ConfigNodeObject extends ConfigNodeComplexValue {
|
|||||||
}
|
}
|
||||||
if (indentation.isEmpty()) {
|
if (indentation.isEmpty()) {
|
||||||
indentation.add(new ConfigNodeSingleToken(Tokens.newIgnoredWhitespace(null, " ")));
|
indentation.add(new ConfigNodeSingleToken(Tokens.newIgnoredWhitespace(null, " ")));
|
||||||
|
return indentation;
|
||||||
} else {
|
} else {
|
||||||
// Calculate the indentation of the ending curly-brace to get the indentation of the root object
|
// Calculate the indentation of the ending curly-brace to get the indentation of the root object
|
||||||
AbstractConfigNode last = children.get(children.size() - 1);
|
AbstractConfigNode last = children.get(children.size() - 1);
|
||||||
if (last instanceof ConfigNodeSingleToken && ((ConfigNodeSingleToken) last).token() == Tokens.CLOSE_CURLY) {
|
if (last instanceof ConfigNodeSingleToken && ((ConfigNodeSingleToken) last).token() == Tokens.CLOSE_CURLY) {
|
||||||
AbstractConfigNode beforeLast = children.get(children.size() - 2);
|
AbstractConfigNode beforeLast = children.get(children.size() - 2);
|
||||||
String indent = "";
|
|
||||||
if (beforeLast instanceof ConfigNodeSingleToken &&
|
if (beforeLast instanceof ConfigNodeSingleToken &&
|
||||||
Tokens.isIgnoredWhitespace(((ConfigNodeSingleToken) beforeLast).token()))
|
Tokens.isIgnoredWhitespace(((ConfigNodeSingleToken) beforeLast).token())) {
|
||||||
indent = ((ConfigNodeSingleToken) beforeLast).token().tokenText();
|
String indent = ((ConfigNodeSingleToken) beforeLast).token().tokenText();
|
||||||
indent += " ";
|
indent += " ";
|
||||||
indentation.add(new ConfigNodeSingleToken(Tokens.newIgnoredWhitespace(null, indent)));
|
indentation.add(new ConfigNodeSingleToken(Tokens.newIgnoredWhitespace(null, indent)));
|
||||||
return indentation;
|
return indentation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,12 +168,12 @@ final class ConfigNodeObject extends ConfigNodeComplexValue {
|
|||||||
|
|
||||||
// If the value we're inserting is a complex value, we'll need to indent it for insertion
|
// If the value we're inserting is a complex value, we'll need to indent it for insertion
|
||||||
AbstractConfigNodeValue indentedValue;
|
AbstractConfigNodeValue indentedValue;
|
||||||
if (value instanceof ConfigNodeComplexValue && !indentation.isEmpty()) {
|
if (value instanceof ConfigNodeComplexValue) {
|
||||||
indentedValue = ((ConfigNodeComplexValue) value).indentText(indentation.get(indentation.size() - 1));
|
indentedValue = ((ConfigNodeComplexValue) value).indentText(indentation.get(indentation.size() - 1));
|
||||||
} else {
|
} else {
|
||||||
indentedValue = value;
|
indentedValue = value;
|
||||||
}
|
}
|
||||||
boolean sameLine = !(indentation.size() > 0 && indentation.get(0) instanceof ConfigNodeSingleToken &&
|
boolean sameLine = !(indentation.get(0) instanceof ConfigNodeSingleToken &&
|
||||||
Tokens.isNewline(((ConfigNodeSingleToken) indentation.get(0)).token()));
|
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
|
// If the path is of length greater than one, see if the value needs to be added further down
|
||||||
@ -211,10 +209,7 @@ final class ConfigNodeObject extends ConfigNodeComplexValue {
|
|||||||
// If the path is of length greater than one add the required new objects along the path
|
// If the path is of length greater than one add the required new objects along the path
|
||||||
ArrayList<AbstractConfigNode> newObjectNodes = new ArrayList<AbstractConfigNode>();
|
ArrayList<AbstractConfigNode> newObjectNodes = new ArrayList<AbstractConfigNode>();
|
||||||
newObjectNodes.add(new ConfigNodeSingleToken(Tokens.OPEN_CURLY));
|
newObjectNodes.add(new ConfigNodeSingleToken(Tokens.OPEN_CURLY));
|
||||||
if (indentation.isEmpty()) {
|
newObjectNodes.add(new ConfigNodeSingleToken(Tokens.newIgnoredWhitespace(null, " ")));
|
||||||
newObjectNodes.add(new ConfigNodeSingleToken(Tokens.newLine(null)));
|
|
||||||
}
|
|
||||||
newObjectNodes.addAll(indentation);
|
|
||||||
newObjectNodes.add(new ConfigNodeSingleToken(Tokens.CLOSE_CURLY));
|
newObjectNodes.add(new ConfigNodeSingleToken(Tokens.CLOSE_CURLY));
|
||||||
ConfigNodeObject newObject = new ConfigNodeObject(newObjectNodes);
|
ConfigNodeObject newObject = new ConfigNodeObject(newObjectNodes);
|
||||||
newNodes.add(newObject.addValueOnPath(desiredPath.subPath(1), indentedValue, flavor));
|
newNodes.add(newObject.addValueOnPath(desiredPath.subPath(1), indentedValue, flavor));
|
||||||
|
@ -34,11 +34,6 @@ final class ConfigParser {
|
|||||||
final private ConfigOrigin baseOrigin;
|
final private ConfigOrigin baseOrigin;
|
||||||
final private LinkedList<Path> pathStack;
|
final private LinkedList<Path> pathStack;
|
||||||
|
|
||||||
// the number of lists we are inside; this is used to detect the "cannot
|
|
||||||
// generate a reference to a list element" problem, and once we fix that
|
|
||||||
// problem we should be able to get rid of this variable.
|
|
||||||
int arrayCount;
|
|
||||||
|
|
||||||
ParseContext(ConfigSyntax flavor, ConfigOrigin origin, ConfigNodeRoot document,
|
ParseContext(ConfigSyntax flavor, ConfigOrigin origin, ConfigNodeRoot document,
|
||||||
FullIncluder includer, ConfigIncludeContext includeContext) {
|
FullIncluder includer, ConfigIncludeContext includeContext) {
|
||||||
lineNumber = 1;
|
lineNumber = 1;
|
||||||
@ -48,7 +43,6 @@ final class ConfigParser {
|
|||||||
this.includer = includer;
|
this.includer = includer;
|
||||||
this.includeContext = includeContext;
|
this.includeContext = includeContext;
|
||||||
this.pathStack = new LinkedList<Path>();
|
this.pathStack = new LinkedList<Path>();
|
||||||
this.arrayCount = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// merge a bunch of adjacent values into one
|
// merge a bunch of adjacent values into one
|
||||||
@ -95,8 +89,6 @@ final class ConfigParser {
|
|||||||
private AbstractConfigValue parseValue(AbstractConfigNodeValue n, List<String> comments) {
|
private AbstractConfigValue parseValue(AbstractConfigNodeValue n, List<String> comments) {
|
||||||
AbstractConfigValue v;
|
AbstractConfigValue v;
|
||||||
|
|
||||||
int startingArrayCount = arrayCount;
|
|
||||||
|
|
||||||
if (n instanceof ConfigNodeSimpleValue) {
|
if (n instanceof ConfigNodeSimpleValue) {
|
||||||
v = ((ConfigNodeSimpleValue) n).value();
|
v = ((ConfigNodeSimpleValue) n).value();
|
||||||
} else if (n instanceof ConfigNodeObject) {
|
} else if (n instanceof ConfigNodeObject) {
|
||||||
@ -114,9 +106,6 @@ final class ConfigParser {
|
|||||||
comments.clear();
|
comments.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arrayCount != startingArrayCount)
|
|
||||||
throw new ConfigException.BugOrBroken("Bug in config parser: unbalanced array count");
|
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,14 +176,6 @@ final class ConfigParser {
|
|||||||
throw new ConfigException.BugOrBroken("should not be reached");
|
throw new ConfigException.BugOrBroken("should not be reached");
|
||||||
}
|
}
|
||||||
|
|
||||||
// we really should make this work, but for now throwing an
|
|
||||||
// exception is better than producing an incorrect result.
|
|
||||||
// See https://github.com/typesafehub/config/issues/160
|
|
||||||
if (arrayCount > 0 && obj.resolveStatus() != ResolveStatus.RESOLVED)
|
|
||||||
throw parseError("Due to current limitations of the config parser, when an include statement is nested inside a list value, "
|
|
||||||
+ "${} substitutions inside the included file cannot be resolved correctly. Either move the include outside of the list value or "
|
|
||||||
+ "remove the ${} statements from the included file.");
|
|
||||||
|
|
||||||
if (!pathStack.isEmpty()) {
|
if (!pathStack.isEmpty()) {
|
||||||
Path prefix = fullCurrentPath();
|
Path prefix = fullCurrentPath();
|
||||||
obj = obj.relativized(prefix);
|
obj = obj.relativized(prefix);
|
||||||
@ -240,21 +221,6 @@ final class ConfigParser {
|
|||||||
|
|
||||||
// path must be on-stack while we parse the value
|
// path must be on-stack while we parse the value
|
||||||
pathStack.push(path);
|
pathStack.push(path);
|
||||||
if (((ConfigNodeField) node).separator() == Tokens.PLUS_EQUALS) {
|
|
||||||
// we really should make this work, but for now throwing
|
|
||||||
// an exception is better than producing an incorrect
|
|
||||||
// result. See
|
|
||||||
// https://github.com/typesafehub/config/issues/160
|
|
||||||
if (arrayCount > 0)
|
|
||||||
throw parseError("Due to current limitations of the config parser, += does not work nested inside a list. "
|
|
||||||
+ "+= expands to a ${} substitution and the path in ${} cannot currently refer to list elements. "
|
|
||||||
+ "You might be able to move the += outside of the list and then refer to it from inside the list with ${}.");
|
|
||||||
|
|
||||||
// because we will put it in an array after the fact so
|
|
||||||
// we want this to be incremented during the parseValue
|
|
||||||
// below in order to throw the above exception.
|
|
||||||
arrayCount += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
AbstractConfigNodeValue valueNode;
|
AbstractConfigNodeValue valueNode;
|
||||||
AbstractConfigValue newValue;
|
AbstractConfigValue newValue;
|
||||||
@ -265,7 +231,6 @@ final class ConfigParser {
|
|||||||
newValue = parseValue(valueNode, comments);
|
newValue = parseValue(valueNode, comments);
|
||||||
|
|
||||||
if (((ConfigNodeField) node).separator() == Tokens.PLUS_EQUALS) {
|
if (((ConfigNodeField) node).separator() == Tokens.PLUS_EQUALS) {
|
||||||
arrayCount -= 1;
|
|
||||||
|
|
||||||
List<AbstractConfigValue> concat = new ArrayList<AbstractConfigValue>(2);
|
List<AbstractConfigValue> concat = new ArrayList<AbstractConfigValue>(2);
|
||||||
AbstractConfigValue previousRef = new ConfigReference(newValue.origin(),
|
AbstractConfigValue previousRef = new ConfigReference(newValue.origin(),
|
||||||
@ -346,7 +311,6 @@ final class ConfigParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SimpleConfigList parseArray(ConfigNodeArray n) {
|
private SimpleConfigList parseArray(ConfigNodeArray n) {
|
||||||
arrayCount += 1;
|
|
||||||
|
|
||||||
SimpleConfigOrigin arrayOrigin = lineOrigin();
|
SimpleConfigOrigin arrayOrigin = lineOrigin();
|
||||||
List<AbstractConfigValue> values = new ArrayList<AbstractConfigValue>();
|
List<AbstractConfigValue> values = new ArrayList<AbstractConfigValue>();
|
||||||
@ -356,6 +320,8 @@ final class ConfigParser {
|
|||||||
|
|
||||||
AbstractConfigValue v = null;
|
AbstractConfigValue v = null;
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
for (AbstractConfigNode node : n.children()) {
|
for (AbstractConfigNode node : n.children()) {
|
||||||
if (node instanceof ConfigNodeComment) {
|
if (node instanceof ConfigNodeComment) {
|
||||||
comments.add(((ConfigNodeComment) node).commentText());
|
comments.add(((ConfigNodeComment) node).commentText());
|
||||||
@ -376,14 +342,16 @@ final class ConfigParser {
|
|||||||
values.add(v.withOrigin(v.origin().appendComments(new ArrayList<String>(comments))));
|
values.add(v.withOrigin(v.origin().appendComments(new ArrayList<String>(comments))));
|
||||||
comments.clear();
|
comments.clear();
|
||||||
}
|
}
|
||||||
|
pathStack.push(new Path(Integer.toString(index)));
|
||||||
|
index ++;
|
||||||
v = parseValue((AbstractConfigNodeValue)node, comments);
|
v = parseValue((AbstractConfigNodeValue)node, comments);
|
||||||
|
pathStack.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// There shouldn't be any comments at this point, but add them just in case
|
// There shouldn't be any comments at this point, but add them just in case
|
||||||
if (v != null) {
|
if (v != null) {
|
||||||
values.add(v.withOrigin(v.origin().appendComments(new ArrayList<String>(comments))));
|
values.add(v.withOrigin(v.origin().appendComments(new ArrayList<String>(comments))));
|
||||||
}
|
}
|
||||||
arrayCount -= 1;
|
|
||||||
return new SimpleConfigList(arrayOrigin, values);
|
return new SimpleConfigList(arrayOrigin, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ final class ResolveSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static private ValueWithPath findInObject(AbstractConfigObject obj, Path path) {
|
static private ValueWithPath findInObject(AbstractConfigValue obj, Path path) {
|
||||||
try {
|
try {
|
||||||
// we'll fail if anything along the path can't
|
// we'll fail if anything along the path can't
|
||||||
// be looked at without resolving.
|
// be looked at without resolving.
|
||||||
@ -65,19 +65,34 @@ final class ResolveSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static private ValueWithPath findInObject(AbstractConfigObject obj, Path path, Node<Container> parents) {
|
static private ValueWithPath findInObject(AbstractConfigValue obj, Path path, Node<Container> parents) {
|
||||||
String key = path.first();
|
String key = path.first();
|
||||||
Path next = path.remainder();
|
Path next = path.remainder();
|
||||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
ConfigImpl.trace("*** looking up '" + key + "' in " + obj);
|
ConfigImpl.trace("*** looking up '" + key + "' in " + obj);
|
||||||
AbstractConfigValue v = obj.attemptPeekWithPartialResolve(key);
|
|
||||||
Node<Container> newParents = parents == null ? new Node<Container>(obj) : parents.prepend(obj);
|
AbstractConfigValue v = null;
|
||||||
|
Node<Container> newParents = null;
|
||||||
|
|
||||||
|
if (obj instanceof AbstractConfigObject) {
|
||||||
|
v = ((AbstractConfigObject) obj).attemptPeekWithPartialResolve(key);
|
||||||
|
newParents = parents == null ? new Node<Container>((AbstractConfigObject) obj) : parents.prepend((AbstractConfigObject) obj);
|
||||||
|
} else if (obj instanceof SimpleConfigList) {
|
||||||
|
int ix;
|
||||||
|
try {
|
||||||
|
ix = Integer.parseInt(key);
|
||||||
|
} catch(Exception e) {
|
||||||
|
throw new ConfigException.BadPath(key, "path for list must be numeric index");
|
||||||
|
}
|
||||||
|
v = ((SimpleConfigList) obj).get(ix);
|
||||||
|
newParents = parents == null ? new Node<Container>((SimpleConfigList) obj) : parents.prepend((SimpleConfigList) obj);
|
||||||
|
}
|
||||||
|
|
||||||
if (next == null) {
|
if (next == null) {
|
||||||
return new ValueWithPath(v, newParents);
|
return new ValueWithPath(v, newParents);
|
||||||
} else {
|
} else {
|
||||||
if (v instanceof AbstractConfigObject) {
|
if (v instanceof AbstractConfigObject || v instanceof SimpleConfigList) {
|
||||||
return findInObject((AbstractConfigObject) v, next, newParents);
|
return findInObject(v, next, newParents);
|
||||||
} else {
|
} else {
|
||||||
return new ValueWithPath(null, newParents);
|
return new ValueWithPath(null, newParents);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package com.typesafe.config.impl;
|
package com.typesafe.config.impl;
|
||||||
|
|
||||||
import com.typesafe.config.*;
|
|
||||||
import com.typesafe.config.parser.ConfigDocument;
|
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.io.StringReader;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@ -32,9 +34,7 @@ final class SimpleConfigDocument implements ConfigDocument {
|
|||||||
public ConfigDocument withValue(String path, ConfigValue newValue) {
|
public ConfigDocument withValue(String path, ConfigValue newValue) {
|
||||||
if (newValue == null)
|
if (newValue == null)
|
||||||
throw new ConfigException.BugOrBroken("null value for " + path + " passed to withValue");
|
throw new ConfigException.BugOrBroken("null value for " + path + " passed to withValue");
|
||||||
ConfigRenderOptions options = ConfigRenderOptions.defaults();
|
return withValueText(path, newValue.render().trim());
|
||||||
options = options.setOriginComments(false);
|
|
||||||
return withValueText(path, newValue.render(options).trim());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -38,7 +38,7 @@ final class Tokenizer {
|
|||||||
return "tab";
|
return "tab";
|
||||||
else if (codepoint == -1)
|
else if (codepoint == -1)
|
||||||
return "end of file";
|
return "end of file";
|
||||||
else if (ConfigImplUtil.isC0Control(codepoint))
|
else if (Character.isISOControl(codepoint))
|
||||||
return String.format("control character 0x%x", codepoint);
|
return String.format("control character 0x%x", codepoint);
|
||||||
else
|
else
|
||||||
return String.format("%c", codepoint);
|
return String.format("%c", codepoint);
|
||||||
@ -498,7 +498,7 @@ final class Tokenizer {
|
|||||||
} else if (c == '"') {
|
} else if (c == '"') {
|
||||||
sbOrig.appendCodePoint(c);
|
sbOrig.appendCodePoint(c);
|
||||||
break;
|
break;
|
||||||
} else if (ConfigImplUtil.isC0Control(c)) {
|
} else if (Character.isISOControl(c)) {
|
||||||
throw problem(asString(c), "JSON does not allow unescaped " + asString(c)
|
throw problem(asString(c), "JSON does not allow unescaped " + asString(c)
|
||||||
+ " in quoted strings, use a backslash escape");
|
+ " in quoted strings, use a backslash escape");
|
||||||
} else {
|
} else {
|
||||||
|
@ -23,7 +23,6 @@ public class ArraysConfig {
|
|||||||
List<ConfigValue> ofConfigValue;
|
List<ConfigValue> ofConfigValue;
|
||||||
List<Duration> ofDuration;
|
List<Duration> ofDuration;
|
||||||
List<ConfigMemorySize> ofMemorySize;
|
List<ConfigMemorySize> ofMemorySize;
|
||||||
List<StringsConfig> ofStringBean;
|
|
||||||
|
|
||||||
public List<Integer> getEmpty() {
|
public List<Integer> getEmpty() {
|
||||||
return empty;
|
return empty;
|
||||||
@ -128,12 +127,4 @@ public class ArraysConfig {
|
|||||||
public void setOfMemorySize(List<ConfigMemorySize> ofMemorySize) {
|
public void setOfMemorySize(List<ConfigMemorySize> ofMemorySize) {
|
||||||
this.ofMemorySize = ofMemorySize;
|
this.ofMemorySize = ofMemorySize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<StringsConfig> getOfStringBean() {
|
|
||||||
return ofStringBean;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOfStringBean(List<StringsConfig> ofStringBean) {
|
|
||||||
this.ofStringBean = ofStringBean;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -20,26 +20,4 @@ public class StringsConfig {
|
|||||||
public void setYes(String s) {
|
public void setYes(String s) {
|
||||||
yes = 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 + ")";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -52,17 +52,7 @@
|
|||||||
"ofConfigObject" : [${numbers}, ${booleans}, ${strings}],
|
"ofConfigObject" : [${numbers}, ${booleans}, ${strings}],
|
||||||
"ofConfigValue" : [1, 2, "a"],
|
"ofConfigValue" : [1, 2, "a"],
|
||||||
"ofDuration" : [1, 2h, 3 days],
|
"ofDuration" : [1, 2h, 3 days],
|
||||||
"ofMemorySize" : [1024, 1M, 1G],
|
"ofMemorySize" : [1024, 1M, 1G]
|
||||||
"ofStringBean" : [
|
|
||||||
{
|
|
||||||
abcd : "testAbcdOne"
|
|
||||||
yes : "testYesOne"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
abcd : "testAbcdTwo"
|
|
||||||
yes : "testYesTwo"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"bytes" : {
|
"bytes" : {
|
||||||
"kilobyte" : "1kB",
|
"kilobyte" : "1kB",
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
// The {} inside the [] is needed because
|
// The {} inside the [] is needed because
|
||||||
// just [ include ] means an array with the
|
// just [ include ] means an array with the
|
||||||
// string "include" in it.
|
// string "include" in it.
|
||||||
a = [ { include "test01.conf" } ]
|
a = [
|
||||||
|
{
|
||||||
|
include "test12.conf"
|
||||||
|
replace-key: "replaced"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
3
config/src/test/resources/test12.conf
Normal file
3
config/src/test/resources/test12.conf
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// This file is included by include-from-list.conf to verify that includes with substitutions work in arrays
|
||||||
|
|
||||||
|
replace-me: ${replace-key}
|
@ -345,26 +345,19 @@ class ConcatenationTest extends TestUtils {
|
|||||||
assertEquals(Seq(1, 2, 3), conf.getObjectList("x.a").asScala.toList.map(_.toConfig.getInt("b")))
|
assertEquals(Seq(1, 2, 3), conf.getObjectList("x.a").asScala.toList.map(_.toConfig.getInt("b")))
|
||||||
}
|
}
|
||||||
|
|
||||||
// We would ideally make this case NOT throw an exception but we need to do some work
|
|
||||||
// to get there, see https://github.com/typesafehub/config/issues/160
|
|
||||||
@Test
|
@Test
|
||||||
def plusEqualsMultipleTimesNestedInArray() {
|
def plusEqualsMultipleTimesNestedInArray() {
|
||||||
val e = intercept[ConfigException.Parse] {
|
val conf = parseConfig("""x = [ { a += 1, a += 2, a += 3 } ] """).resolve()
|
||||||
val conf = parseConfig("""x = [ { a += 1, a += 2, a += 3 } ] """).resolve()
|
assertEquals(Seq(1, 2, 3), conf.getObjectList("x").asScala.toVector(0).toConfig.getIntList("a").asScala.toList)
|
||||||
assertEquals(Seq(1, 2, 3), conf.getObjectList("x").asScala.toVector(0).toConfig.getIntList("a").asScala.toList)
|
|
||||||
}
|
|
||||||
assertTrue(e.getMessage.contains("limitation"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We would ideally make this case NOT throw an exception but we need to do some work
|
|
||||||
// to get there, see https://github.com/typesafehub/config/issues/160
|
|
||||||
@Test
|
@Test
|
||||||
def plusEqualsMultipleTimesNestedInPlusEquals() {
|
def plusEqualsMultipleTimesNestedInPlusEquals() {
|
||||||
val e = intercept[ConfigException.Parse] {
|
val e = intercept[ConfigException.BugOrBroken] {
|
||||||
val conf = parseConfig("""x += { a += 1, a += 2, a += 3 } """).resolve()
|
val conf = parseConfig("""x += { a += 1, a += 2, a += 3 } """).resolve()
|
||||||
assertEquals(Seq(1, 2, 3), conf.getObjectList("x").asScala.toVector(0).toConfig.getIntList("a").asScala.toList)
|
assertEquals(Seq(1, 2, 3), conf.getObjectList("x").asScala.toVector(0).toConfig.getIntList("a").asScala.toList)
|
||||||
}
|
}
|
||||||
assertTrue(e.getMessage.contains("limitation"))
|
assertTrue(e.getMessage.contains("did not find"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// from https://github.com/typesafehub/config/issues/177
|
// from https://github.com/typesafehub/config/issues/177
|
||||||
|
@ -801,6 +801,14 @@ class ConfParserTest extends TestUtils {
|
|||||||
assertTrue("including basename URL doesn't load anything", conf.isEmpty())
|
assertTrue("including basename URL doesn't load anything", conf.isEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def includeWithSubstitutionsFromList() {
|
||||||
|
val conf = ConfigFactory.parseString("include file(" + jsonQuotedResourceFile("include-from-list") + ")")
|
||||||
|
|
||||||
|
val resolved = conf.resolve()
|
||||||
|
assertEquals(resolved.getConfigList("a").get(0).getString("replace-me"), "replaced")
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
def acceptBOMStartingFile() {
|
def acceptBOMStartingFile() {
|
||||||
// BOM at start of file should be ignored
|
// BOM at start of file should be ignored
|
||||||
|
@ -46,10 +46,10 @@ class ConfigBeanFactoryTest extends TestUtils {
|
|||||||
ConfigBeanFactory.create(config, classOf[ValidationBeanConfig])
|
ConfigBeanFactory.create(config, classOf[ValidationBeanConfig])
|
||||||
}
|
}
|
||||||
|
|
||||||
val expecteds = Seq(Missing("propNotListedInConfig", 77, "string"),
|
val expecteds = Seq(Missing("propNotListedInConfig", 67, "string"),
|
||||||
WrongType("shouldBeInt", 78, "number", "boolean"),
|
WrongType("shouldBeInt", 68, "number", "boolean"),
|
||||||
WrongType("should-be-boolean", 79, "boolean", "number"),
|
WrongType("should-be-boolean", 69, "boolean", "number"),
|
||||||
WrongType("should-be-list", 80, "list", "string"))
|
WrongType("should-be-list", 70, "list", "string"))
|
||||||
|
|
||||||
checkValidationException(e, expecteds)
|
checkValidationException(e, expecteds)
|
||||||
}
|
}
|
||||||
@ -111,15 +111,6 @@ class ConfigBeanFactoryTest extends TestUtils {
|
|||||||
ConfigMemorySize.ofBytes(1048576),
|
ConfigMemorySize.ofBytes(1048576),
|
||||||
ConfigMemorySize.ofBytes(1073741824)),
|
ConfigMemorySize.ofBytes(1073741824)),
|
||||||
beanConfig.getOfMemorySize.asScala)
|
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
|
@Test
|
||||||
|
@ -171,14 +171,14 @@ class ConfigDocumentTest extends TestUtils {
|
|||||||
@Test
|
@Test
|
||||||
def configDocumentSetNewValueMultiLevelConf {
|
def configDocumentSetNewValueMultiLevelConf {
|
||||||
val origText = "a:b\nc:d"
|
val origText = "a:b\nc:d"
|
||||||
val finalText = "a:b\nc:d\ne : {\n f : {\n g : 12\n }\n}"
|
val finalText = "a:b\nc:d\ne : { f : { g : 12 } }"
|
||||||
configDocumentReplaceConfTest(origText, finalText, "12", "e.f.g")
|
configDocumentReplaceConfTest(origText, finalText, "12", "e.f.g")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
def configDocumentSetNewValueMultiLevelJson {
|
def configDocumentSetNewValueMultiLevelJson {
|
||||||
val origText = "{\"a\":\"b\",\n\"c\":\"d\"}"
|
val origText = "{\"a\":\"b\",\n\"c\":\"d\"}"
|
||||||
val finalText = "{\"a\":\"b\",\n\"c\":\"d\",\n \"e\" : {\n \"f\" : {\n \"g\" : 12\n }\n }}"
|
val finalText = "{\"a\":\"b\",\n\"c\":\"d\",\n\"e\" : { \"f\" : { \"g\" : 12 } }}"
|
||||||
configDocumentReplaceJsonTest(origText, finalText, "12", "e.f.g")
|
configDocumentReplaceJsonTest(origText, finalText, "12", "e.f.g")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,13 +342,13 @@ class ConfigDocumentTest extends TestUtils {
|
|||||||
var configDocument = ConfigDocumentFactory.parseString(origText)
|
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 e : f\n}", configDocument.withValueText("a.e", "f").render())
|
||||||
|
|
||||||
assertEquals("a {\n b: c\n d : {\n e : {\n f : g\n }\n }\n}", configDocument.withValueText("a.d.e.f", "g").render())
|
assertEquals("a {\n b: c\n d : { e : { f : g } }\n}", configDocument.withValueText("a.d.e.f", "g").render())
|
||||||
|
|
||||||
origText = "a {\n b: c\n}\n"
|
origText = "a {\n b: c\n}\n"
|
||||||
configDocument = ConfigDocumentFactory.parseString(origText)
|
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\n", configDocument.withValueText("d", "e").render())
|
||||||
|
|
||||||
assertEquals("a {\n b: c\n}\nd : {\n e : {\n f : g\n }\n}\n", configDocument.withValueText("d.e.f", "g").render())
|
assertEquals("a {\n b: c\n}\nd : { e : { f : g } }\n", configDocument.withValueText("d.e.f", "g").render())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -435,14 +435,7 @@ class ConfigDocumentTest extends TestUtils {
|
|||||||
val origText = ""
|
val origText = ""
|
||||||
val configDocument = ConfigDocumentFactory.parseString(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
|
@Test
|
||||||
@ -452,7 +445,9 @@ class ConfigDocumentTest extends TestUtils {
|
|||||||
|
|
||||||
val configVal = ConfigValueFactory.fromAnyRef(Map("a" -> 1, "b" -> 2).asJava)
|
val configVal = ConfigValueFactory.fromAnyRef(Map("a" -> 1, "b" -> 2).asJava)
|
||||||
|
|
||||||
assertEquals("{ a : {\n \"a\" : 1,\n \"b\" : 2\n } }",
|
assertEquals("{ a : {\n # hardcoded value\n \"a\" : 1,\n # hardcoded value\n \"b\" : 2\n } }",
|
||||||
configDocument.withValue("a", configVal).render)
|
configDocument.withValue("a", configVal).render)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -212,7 +212,7 @@ class ConfigNodeTest extends TestUtils {
|
|||||||
nodeCloseBrace))
|
nodeCloseBrace))
|
||||||
assertEquals(origText, origNode.render())
|
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" +
|
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@@@+$#\" : {\n\t\t end : doesnotexist\n\t\t}\n\t}\n}\n\nbaz.abc.ghi : randomunquotedString\n}"
|
"def : false\n\t\t\n\t\t\"this.does.not.exist@@@+$#\" : { end : doesnotexist }\n\t}\n}\n\nbaz.abc.ghi : randomunquotedString\n}"
|
||||||
|
|
||||||
//Can replace settings in nested maps
|
//Can replace settings in nested maps
|
||||||
// Paths with quotes in the name are treated as a single Path, rather than multiple sub-paths
|
// Paths with quotes in the name are treated as a single Path, rather than multiple sub-paths
|
||||||
|
@ -875,17 +875,6 @@ class PublicApiTest extends TestUtils {
|
|||||||
assertTrue("wrong exception: " + e.getMessage, e.getMessage.contains("include statements nested"))
|
assertTrue("wrong exception: " + e.getMessage, e.getMessage.contains("include statements nested"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// We would ideally make this case NOT throw an exception but we need to do some work
|
|
||||||
// to get there, see https://github.com/typesafehub/config/issues/160
|
|
||||||
@Test
|
|
||||||
def detectIncludeFromList() {
|
|
||||||
val e = intercept[ConfigException.Parse] {
|
|
||||||
ConfigFactory.load("include-from-list.conf")
|
|
||||||
}
|
|
||||||
|
|
||||||
assertTrue("wrong exception: " + e.getMessage, e.getMessage.contains("limitation"))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
def missingOverrideResourceFails() {
|
def missingOverrideResourceFails() {
|
||||||
assertEquals("config.file is not set", null, System.getProperty("config.file"))
|
assertEquals("config.file is not set", null, System.getProperty("config.file"))
|
||||||
|
Loading…
Reference in New Issue
Block a user