Latest config lib

This commit is contained in:
Patrik Nordwall 2011-11-19 09:23:01 +01:00
parent a9217cec7b
commit b8be8f3869
50 changed files with 449 additions and 246 deletions

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config;
import java.util.List;
@ -53,9 +56,6 @@ public interface Config extends ConfigMergeable {
@Override
Config withFallback(ConfigMergeable other);
@Override
Config withFallbacks(ConfigMergeable... others);
@Override
ConfigObject toValue();

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config;
/**

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config;
import java.io.File;
@ -74,7 +77,7 @@ public final class ConfigFactory {
Config referenceFiles = parseResourcesForPath(rootPath + ".reference",
options);
return system.withFallbacks(mainFiles, referenceFiles);
return system.withFallback(mainFiles).withFallback(referenceFiles);
}
public static ConfigRoot emptyRoot(String rootPath) {

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config;

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config;
/**

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config;
import java.util.List;

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config;
/**
@ -18,35 +21,16 @@ public interface ConfigMergeable {
* Config instances do anything in this method (they need to merge the
* fallback keys into themselves). All other values just return the original
* value, since they automatically override any fallback.
*
*
* The semantics of merging are described in
* https://github.com/havocp/config/blob/master/HOCON.md
*
* Prefer <code>withFallbacks()</code>, listing all your fallbacks at once,
* over this method.
*
* <i>When using this method, there is an easy way to write a wrong
* loop.</i> Even if you don't loop, it's easy to do the equivalent wrong
* thing.
*
* <code>
* // WRONG
* for (ConfigMergeable fallback : fallbacks) {
* // DO NOT DO THIS
* result = result.withFallback(fallback);
* }
* </code>
*
* This is wrong because when <code>result</code> is an object and
* <code>fallback</code> is a non-object,
* <code>result.withFallback(fallback)</code> returns an object. Then if
* there are more objects, they are merged into that object. But the correct
* semantics are that a non-object will block merging any more objects later
* in the list. To get it right, you need to iterate backward. Simplest
* solution: prefer <code>withFallbacks()</code> which is harder to get
* wrong, and merge all your fallbacks in one call to
* <code>withFallbacks()</code>.
*
*
* Note that objects do not merge "across" non-objects; if you do
* <code>object.withFallback(nonObject).withFallback(otherObject)</code>,
* then <code>otherObject</code> will simply be ignored. This is an
* intentional part of how merging works. Both non-objects, and any object
* which has fallen back to a non-object, block subsequent fallbacks.
*
* @param other
* an object whose keys should be used if the keys are not
* present in this one
@ -54,14 +38,4 @@ public interface ConfigMergeable {
* used)
*/
ConfigMergeable withFallback(ConfigMergeable other);
/**
* Convenience method just calls withFallback() on each of the values;
* earlier values in the list win over later ones. The semantics of merging
* are described in https://github.com/havocp/config/blob/master/HOCON.md
*
* @param fallbacks
* @return a version of the object with the requested fallbacks merged in
*/
ConfigMergeable withFallbacks(ConfigMergeable... others);
}

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config;
import java.util.Map;
@ -58,9 +61,6 @@ public interface ConfigObject extends ConfigValue, Map<String, ConfigValue> {
@Override
ConfigObject withFallback(ConfigMergeable other);
@Override
ConfigObject withFallbacks(ConfigMergeable... others);
/**
* Gets a ConfigValue at the given key, or returns null if there is no
* value. The returned ConfigValue may have ConfigValueType.NULL or any

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config;
/**

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config;

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config;
import java.net.URL;

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config;
public final class ConfigResolveOptions {

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config;
/**
@ -21,9 +24,6 @@ public interface ConfigRoot extends Config {
@Override
ConfigRoot withFallback(ConfigMergeable fallback);
@Override
ConfigRoot withFallbacks(ConfigMergeable... fallbacks);
/**
* Gets the global app name that this root represents.
*

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config;
public enum ConfigSyntax {

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config;
/**
@ -29,7 +32,4 @@ public interface ConfigValue extends ConfigMergeable {
@Override
ConfigValue withFallback(ConfigMergeable other);
@Override
ConfigValue withFallbacks(ConfigMergeable... fallbacks);
}

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config;
import java.util.Collection;

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config;
/**

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import java.util.ArrayList;
@ -99,54 +102,61 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
return ConfigValueType.OBJECT;
}
protected abstract AbstractConfigObject newCopy(ResolveStatus status);
protected abstract AbstractConfigObject newCopy(ResolveStatus status,
boolean ignoresFallbacks);
@Override
public AbstractConfigObject withFallbacks(ConfigMergeable... others) {
return (AbstractConfigObject) super.withFallbacks(others);
protected AbstractConfigObject newCopy(boolean ignoresFallbacks) {
return newCopy(resolveStatus(), ignoresFallbacks);
}
@Override
protected final AbstractConfigObject mergedWithTheUnmergeable(Unmergeable fallback) {
if (ignoresFallbacks())
throw new ConfigException.BugOrBroken("should not be reached");
List<AbstractConfigValue> stack = new ArrayList<AbstractConfigValue>();
if (this instanceof Unmergeable) {
stack.addAll(((Unmergeable) this).unmergedValues());
} else {
stack.add(this);
}
stack.addAll(fallback.unmergedValues());
return new ConfigDelayedMergeObject(mergeOrigins(stack), stack,
((AbstractConfigValue) fallback).ignoresFallbacks());
}
@Override
protected AbstractConfigObject mergedWithObject(AbstractConfigObject fallback) {
if (ignoresFallbacks())
throw new ConfigException.BugOrBroken("should not be reached");
boolean allResolved = true;
Map<String, AbstractConfigValue> merged = new HashMap<String, AbstractConfigValue>();
Set<String> allKeys = new HashSet<String>();
allKeys.addAll(this.keySet());
allKeys.addAll(fallback.keySet());
for (String key : allKeys) {
AbstractConfigValue first = this.peek(key);
AbstractConfigValue second = fallback.peek(key);
AbstractConfigValue kept;
if (first == null)
kept = second;
else if (second == null)
kept = first;
else
kept = first.withFallback(second);
merged.put(key, kept);
if (kept.resolveStatus() == ResolveStatus.UNRESOLVED)
allResolved = false;
}
return new SimpleConfigObject(mergeOrigins(this, fallback), merged,
ResolveStatus.fromBoolean(allResolved), fallback.ignoresFallbacks());
}
@Override
public AbstractConfigObject withFallback(ConfigMergeable mergeable) {
ConfigValue other = mergeable.toValue();
if (other instanceof Unmergeable) {
List<AbstractConfigValue> stack = new ArrayList<AbstractConfigValue>();
stack.add(this);
stack.addAll(((Unmergeable) other).unmergedValues());
return new ConfigDelayedMergeObject(mergeOrigins(stack), stack);
} else if (other instanceof AbstractConfigObject) {
AbstractConfigObject fallback = (AbstractConfigObject) other;
if (fallback.isEmpty()) {
return this; // nothing to do
} else {
boolean allResolved = true;
Map<String, AbstractConfigValue> merged = new HashMap<String, AbstractConfigValue>();
Set<String> allKeys = new HashSet<String>();
allKeys.addAll(this.keySet());
allKeys.addAll(fallback.keySet());
for (String key : allKeys) {
AbstractConfigValue first = this.peek(key);
AbstractConfigValue second = fallback.peek(key);
AbstractConfigValue kept;
if (first == null)
kept = second;
else if (second == null)
kept = first;
else
kept = first.withFallback(second);
merged.put(key, kept);
if (kept.resolveStatus() == ResolveStatus.UNRESOLVED)
allResolved = false;
}
return new SimpleConfigObject(mergeOrigins(this, fallback),
merged, ResolveStatus.fromBoolean(allResolved));
}
} else {
// falling back to a non-object has no effect, we just override
// primitive values.
return this;
}
return (AbstractConfigObject) super.withFallback(mergeable);
}
static ConfigOrigin mergeOrigins(
@ -166,7 +176,9 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
if (desc.startsWith(prefix))
desc = desc.substring(prefix.length());
if (v instanceof ConfigObject && ((ConfigObject) v).isEmpty()) {
if (v instanceof AbstractConfigObject
&& ((AbstractConfigObject) v).resolveStatus() == ResolveStatus.RESOLVED
&& ((ConfigObject) v).isEmpty()) {
// don't include empty files or the .empty()
// config in the description, since they are
// likely to be "implementation details"
@ -206,7 +218,7 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
}
}
if (changes == null) {
return newCopy(newResolveStatus);
return newCopy(newResolveStatus, ignoresFallbacks());
} else {
Map<String, AbstractConfigValue> modified = new HashMap<String, AbstractConfigValue>();
for (String k : keySet()) {
@ -216,8 +228,8 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
modified.put(k, peek(k));
}
}
return new SimpleConfigObject(origin(), modified,
newResolveStatus);
return new SimpleConfigObject(origin(), modified, newResolveStatus,
ignoresFallbacks());
}
}
@ -311,7 +323,8 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
@Override
public boolean equals(Object other) {
// note that "origin" is deliberately NOT part of equality
// note that "origin" is deliberately NOT part of equality.
// neither are other "extras" like ignoresFallbacks or resolve status.
if (other instanceof ConfigObject) {
// optimization to avoid unwrapped() for two ConfigObject,
// which is what AbstractConfigValue does.
@ -324,6 +337,7 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
@Override
public int hashCode() {
// note that "origin" is deliberately NOT part of equality
// neither are other "extras" like ignoresFallbacks or resolve status.
return mapHash(this);
}

View file

@ -1,5 +1,9 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import com.typesafe.config.ConfigException;
import com.typesafe.config.ConfigMergeable;
import com.typesafe.config.ConfigOrigin;
import com.typesafe.config.ConfigResolveOptions;
@ -72,17 +76,61 @@ abstract class AbstractConfigValue implements ConfigValue {
return this;
}
@Override
public AbstractConfigValue withFallback(ConfigMergeable other) {
protected AbstractConfigValue newCopy(boolean ignoresFallbacks) {
return this;
}
// this is virtualized rather than a field because only some subclasses
// really need to store the boolean, and they may be able to pack it
// with another boolean to save space.
protected boolean ignoresFallbacks() {
return true;
}
private ConfigException badMergeException() {
if (ignoresFallbacks())
throw new ConfigException.BugOrBroken(
"method should not have been called with ignoresFallbacks=true"
+ getClass().getSimpleName());
else
throw new ConfigException.BugOrBroken("should override this in "
+ getClass().getSimpleName());
}
protected AbstractConfigValue mergedWithTheUnmergeable(Unmergeable fallback) {
throw badMergeException();
}
protected AbstractConfigValue mergedWithObject(AbstractConfigObject fallback) {
throw badMergeException();
}
@Override
public AbstractConfigValue withFallbacks(ConfigMergeable... fallbacks) {
// note: this is a no-op unless the subclass overrides withFallback().
// But we need to do this because subclass withFallback() may not
// just "return this"
return ConfigImpl.merge(AbstractConfigValue.class, this, fallbacks);
public AbstractConfigValue withFallback(ConfigMergeable mergeable) {
if (ignoresFallbacks()) {
return this;
} else {
ConfigValue other = mergeable.toValue();
if (other instanceof Unmergeable) {
return mergedWithTheUnmergeable((Unmergeable) other);
} else if (other instanceof AbstractConfigObject) {
AbstractConfigObject fallback = (AbstractConfigObject) other;
if (fallback.resolveStatus() == ResolveStatus.RESOLVED && fallback.isEmpty()) {
if (fallback.ignoresFallbacks())
return newCopy(true /* ignoresFallbacks */);
else
return this;
} else {
return mergedWithObject((AbstractConfigObject) other);
}
} else {
// falling back to a non-object doesn't merge anything, and also
// prohibits merging any objects that we fall back to later.
// so we have to switch to ignoresFallbacks mode.
return newCopy(true /* ignoresFallbacks */);
}
}
}
protected boolean canEqual(Object other) {

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import com.typesafe.config.ConfigOrigin;

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import java.util.ArrayList;
@ -5,10 +8,8 @@ import java.util.Collection;
import java.util.List;
import com.typesafe.config.ConfigException;
import com.typesafe.config.ConfigMergeable;
import com.typesafe.config.ConfigOrigin;
import com.typesafe.config.ConfigResolveOptions;
import com.typesafe.config.ConfigValue;
import com.typesafe.config.ConfigValueType;
/**
@ -24,14 +25,26 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements
// earlier items in the stack win
final private List<AbstractConfigValue> stack;
final private boolean ignoresFallbacks;
ConfigDelayedMerge(ConfigOrigin origin, List<AbstractConfigValue> stack) {
ConfigDelayedMerge(ConfigOrigin origin, List<AbstractConfigValue> stack,
boolean ignoresFallbacks) {
super(origin);
this.stack = stack;
this.ignoresFallbacks = ignoresFallbacks;
if (stack.isEmpty())
throw new ConfigException.BugOrBroken(
"creating empty delayed merge value");
for (AbstractConfigValue v : stack) {
if (v instanceof ConfigDelayedMerge || v instanceof ConfigDelayedMergeObject)
throw new ConfigException.BugOrBroken(
"placed nested DelayedMerge in a ConfigDelayedMerge, should have consolidated stack");
}
}
ConfigDelayedMerge(ConfigOrigin origin, List<AbstractConfigValue> stack) {
this(origin, stack, false /* ignoresFallbacks */);
}
@Override
@ -57,18 +70,19 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements
List<AbstractConfigValue> stack, SubstitutionResolver resolver,
int depth, ConfigResolveOptions options) {
// to resolve substitutions, we need to recursively resolve
// the stack of stuff to merge, and then merge the stack.
List<AbstractConfigValue> toMerge = new ArrayList<AbstractConfigValue>();
// the stack of stuff to merge, and merge the stack so
// we won't be a delayed merge anymore.
AbstractConfigValue merged = null;
for (AbstractConfigValue v : stack) {
AbstractConfigValue resolved = resolver.resolve(v, depth, options);
toMerge.add(resolved);
if (merged == null)
merged = resolved;
else
merged = merged.withFallback(resolved);
}
// we shouldn't have a delayed merge object with an empty stack, so
// it should be safe to ignore the toMerge.isEmpty case.
return ConfigImpl.merge(AbstractConfigValue.class, toMerge.get(0),
toMerge.subList(1, toMerge.size()));
return merged;
}
@Override
@ -82,30 +96,40 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements
for (AbstractConfigValue o : stack) {
newStack.add(o.relativized(prefix));
}
return new ConfigDelayedMerge(origin(), newStack);
return new ConfigDelayedMerge(origin(), newStack, ignoresFallbacks);
}
@Override
public AbstractConfigValue withFallback(ConfigMergeable mergeable) {
ConfigValue other = mergeable.toValue();
protected boolean ignoresFallbacks() {
return ignoresFallbacks;
}
if (other instanceof AbstractConfigObject
|| other instanceof Unmergeable) {
// if we turn out to be an object, and the fallback also does,
// then a merge may be required; delay until we resolve.
List<AbstractConfigValue> newStack = new ArrayList<AbstractConfigValue>();
newStack.addAll(stack);
if (other instanceof Unmergeable)
newStack.addAll(((Unmergeable) other).unmergedValues());
else
newStack.add((AbstractConfigValue) other);
return new ConfigDelayedMerge(
AbstractConfigObject.mergeOrigins(newStack), newStack);
} else {
// if the other is not an object, there won't be anything
// to merge with, so we are it even if we are an object.
return this;
}
@Override
protected final ConfigDelayedMerge mergedWithTheUnmergeable(Unmergeable fallback) {
if (ignoresFallbacks)
throw new ConfigException.BugOrBroken("should not be reached");
// if we turn out to be an object, and the fallback also does,
// then a merge may be required; delay until we resolve.
List<AbstractConfigValue> newStack = new ArrayList<AbstractConfigValue>();
newStack.addAll(stack);
newStack.addAll(fallback.unmergedValues());
return new ConfigDelayedMerge(AbstractConfigObject.mergeOrigins(newStack), newStack,
((AbstractConfigValue) fallback).ignoresFallbacks());
}
@Override
protected final ConfigDelayedMerge mergedWithObject(AbstractConfigObject fallback) {
if (ignoresFallbacks)
throw new ConfigException.BugOrBroken("should not be reached");
// if we turn out to be an object, and the fallback also does,
// then a merge may be required; delay until we resolve.
List<AbstractConfigValue> newStack = new ArrayList<AbstractConfigValue>();
newStack.addAll(stack);
newStack.add(fallback);
return new ConfigDelayedMerge(AbstractConfigObject.mergeOrigins(newStack), newStack,
fallback.ignoresFallbacks());
}
@Override

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import java.util.ArrayList;
@ -18,25 +21,40 @@ class ConfigDelayedMergeObject extends AbstractConfigObject implements
Unmergeable {
final private List<AbstractConfigValue> stack;
final private boolean ignoresFallbacks;
ConfigDelayedMergeObject(ConfigOrigin origin,
List<AbstractConfigValue> stack) {
this(origin, stack, false /* ignoresFallbacks */);
}
ConfigDelayedMergeObject(ConfigOrigin origin, List<AbstractConfigValue> stack,
boolean ignoresFallbacks) {
super(origin);
this.stack = stack;
this.ignoresFallbacks = ignoresFallbacks;
if (stack.isEmpty())
throw new ConfigException.BugOrBroken(
"creating empty delayed merge object");
if (!(stack.get(0) instanceof AbstractConfigObject))
throw new ConfigException.BugOrBroken(
"created a delayed merge object not guaranteed to be an object");
for (AbstractConfigValue v : stack) {
if (v instanceof ConfigDelayedMerge || v instanceof ConfigDelayedMergeObject)
throw new ConfigException.BugOrBroken(
"placed nested DelayedMerge in a ConfigDelayedMergeObject, should have consolidated stack");
}
}
@Override
public ConfigDelayedMergeObject newCopy(ResolveStatus status) {
protected ConfigDelayedMergeObject newCopy(ResolveStatus status,
boolean ignoresFallbacks) {
if (status != resolveStatus())
throw new ConfigException.BugOrBroken(
"attempt to create resolved ConfigDelayedMergeObject");
return new ConfigDelayedMergeObject(origin(), stack);
return new ConfigDelayedMergeObject(origin(), stack, ignoresFallbacks);
}
@Override
@ -64,36 +82,32 @@ class ConfigDelayedMergeObject extends AbstractConfigObject implements
for (AbstractConfigValue o : stack) {
newStack.add(o.relativized(prefix));
}
return new ConfigDelayedMergeObject(origin(), newStack);
return new ConfigDelayedMergeObject(origin(), newStack,
ignoresFallbacks);
}
@Override
protected boolean ignoresFallbacks() {
return ignoresFallbacks;
}
@Override
protected ConfigDelayedMergeObject mergedWithObject(AbstractConfigObject fallback) {
if (ignoresFallbacks)
throw new ConfigException.BugOrBroken("should not be reached");
// since we are an object, and the fallback is, we'll need to
// merge the fallback once we resolve.
List<AbstractConfigValue> newStack = new ArrayList<AbstractConfigValue>();
newStack.addAll(stack);
newStack.add(fallback);
return new ConfigDelayedMergeObject(AbstractConfigObject.mergeOrigins(newStack), newStack,
fallback.ignoresFallbacks());
}
@Override
public ConfigDelayedMergeObject withFallback(ConfigMergeable mergeable) {
ConfigValue other = mergeable.toValue();
if (other instanceof AbstractConfigObject
|| other instanceof Unmergeable) {
// since we are an object, and the fallback could be,
// then a merge may be required; delay until we resolve.
List<AbstractConfigValue> newStack = new ArrayList<AbstractConfigValue>();
newStack.addAll(stack);
if (other instanceof Unmergeable)
newStack.addAll(((Unmergeable) other).unmergedValues());
else
newStack.add((AbstractConfigValue) other);
return new ConfigDelayedMergeObject(
AbstractConfigObject.mergeOrigins(newStack),
newStack);
} else {
// if the other is not an object, there won't be anything
// to merge with.
return this;
}
}
@Override
public ConfigDelayedMergeObject withFallbacks(ConfigMergeable... others) {
return (ConfigDelayedMergeObject) super.withFallbacks(others);
return (ConfigDelayedMergeObject) super.withFallback(mergeable);
}
@Override

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import com.typesafe.config.ConfigOrigin;

View file

@ -1,20 +1,20 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigException;
import com.typesafe.config.ConfigIncludeContext;
import com.typesafe.config.ConfigIncluder;
import com.typesafe.config.ConfigMergeable;
import com.typesafe.config.ConfigObject;
import com.typesafe.config.ConfigOrigin;
import com.typesafe.config.ConfigParseOptions;
@ -26,32 +26,6 @@ import com.typesafe.config.ConfigValue;
/** This is public but is only supposed to be used by the "config" package */
public class ConfigImpl {
static <T extends ConfigMergeable> T merge(Class<T> klass, T first,
ConfigMergeable... others) {
List<ConfigMergeable> stack = Arrays.asList(others);
return merge(klass, first, stack);
}
static <T extends ConfigMergeable> T merge(Class<T> klass, T first,
List<? extends ConfigMergeable> stack) {
if (stack.isEmpty()) {
return first;
} else {
// to be consistent with the semantics of duplicate keys
// in the same file, we have to go backward like this.
// importantly, a primitive value always permanently
// hides a previous object value.
ListIterator<? extends ConfigMergeable> i = stack
.listIterator(stack.size());
ConfigMergeable merged = i.previous();
while (i.hasPrevious()) {
merged = i.previous().withFallback(merged);
}
merged = first.withFallback(merged);
return klass.cast(merged);
}
}
private interface NameSource {
ConfigParseable nameToParseable(String name);
}
@ -425,7 +399,7 @@ public class ConfigImpl {
new SimpleConfigOrigin("env var " + key), entry.getValue()));
}
return new SimpleConfigObject(new SimpleConfigOrigin("env variables"),
m, ResolveStatus.RESOLVED);
m, ResolveStatus.RESOLVED, false /* ignoresFallbacks */);
}
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import com.typesafe.config.ConfigOrigin;

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import com.typesafe.config.ConfigOrigin;

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import com.typesafe.config.ConfigOrigin;

View file

@ -1,5 +1,9 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import com.typesafe.config.ConfigException;
import com.typesafe.config.ConfigOrigin;
abstract class ConfigNumber extends AbstractConfigValue {
@ -14,11 +18,23 @@ abstract class ConfigNumber extends AbstractConfigValue {
this.originalText = originalText;
}
@Override
public abstract Number unwrapped();
@Override
String transformToString() {
return originalText;
}
int intValueRangeChecked(String path) {
long l = longValue();
if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
throw new ConfigException.WrongType(origin(), path, "32-bit integer",
"out-of-range value " + l);
}
return (int) l;
}
protected abstract long longValue();
protected abstract double doubleValue();

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import com.typesafe.config.ConfigOrigin;

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import java.util.ArrayList;
@ -6,7 +9,6 @@ import java.util.Collections;
import java.util.List;
import com.typesafe.config.ConfigException;
import com.typesafe.config.ConfigMergeable;
import com.typesafe.config.ConfigOrigin;
import com.typesafe.config.ConfigResolveOptions;
import com.typesafe.config.ConfigValue;
@ -25,17 +27,19 @@ final class ConfigSubstitution extends AbstractConfigValue implements
// than one piece everything is stringified and concatenated
final private List<Object> pieces;
// the length of any prefixes added with relativized()
final int prefixLength;
final private int prefixLength;
final private boolean ignoresFallbacks;
ConfigSubstitution(ConfigOrigin origin, List<Object> pieces) {
this(origin, pieces, 0);
this(origin, pieces, 0, false);
}
private ConfigSubstitution(ConfigOrigin origin, List<Object> pieces,
int prefixLength) {
int prefixLength, boolean ignoresFallbacks) {
super(origin);
this.pieces = pieces;
this.prefixLength = prefixLength;
this.ignoresFallbacks = ignoresFallbacks;
}
@Override
@ -52,26 +56,41 @@ final class ConfigSubstitution extends AbstractConfigValue implements
}
@Override
public AbstractConfigValue withFallback(ConfigMergeable mergeable) {
ConfigValue other = mergeable.toValue();
protected ConfigSubstitution newCopy(boolean ignoresFallbacks) {
return new ConfigSubstitution(origin(), pieces, prefixLength, ignoresFallbacks);
}
if (other instanceof AbstractConfigObject
|| other instanceof Unmergeable) {
// if we turn out to be an object, and the fallback also does,
// then a merge may be required; delay until we resolve.
List<AbstractConfigValue> newStack = new ArrayList<AbstractConfigValue>();
newStack.add(this);
if (other instanceof Unmergeable)
newStack.addAll(((Unmergeable) other).unmergedValues());
else
newStack.add((AbstractConfigValue) other);
return new ConfigDelayedMerge(
AbstractConfigObject.mergeOrigins(newStack), newStack);
} else {
// if the other is not an object, there won't be anything
// to merge with, so we are it even if we are an object.
return this;
}
@Override
protected boolean ignoresFallbacks() {
return ignoresFallbacks;
}
@Override
protected AbstractConfigValue mergedWithTheUnmergeable(Unmergeable fallback) {
if (ignoresFallbacks)
throw new ConfigException.BugOrBroken("should not be reached");
// if we turn out to be an object, and the fallback also does,
// then a merge may be required; delay until we resolve.
List<AbstractConfigValue> newStack = new ArrayList<AbstractConfigValue>();
newStack.add(this);
newStack.addAll(fallback.unmergedValues());
return new ConfigDelayedMerge(AbstractConfigObject.mergeOrigins(newStack), newStack,
((AbstractConfigValue) fallback).ignoresFallbacks());
}
@Override
protected AbstractConfigValue mergedWithObject(AbstractConfigObject fallback) {
if (ignoresFallbacks)
throw new ConfigException.BugOrBroken("should not be reached");
// if we turn out to be an object, and the fallback also does,
// then a merge may be required; delay until we resolve.
List<AbstractConfigValue> newStack = new ArrayList<AbstractConfigValue>();
newStack.add(this);
newStack.add(fallback);
return new ConfigDelayedMerge(AbstractConfigObject.mergeOrigins(newStack), newStack,
fallback.ignoresFallbacks());
}
@Override
@ -198,7 +217,7 @@ final class ConfigSubstitution extends AbstractConfigValue implements
}
}
return new ConfigSubstitution(origin(), newPieces, prefixLength
+ prefix.length());
+ prefix.length(), ignoresFallbacks);
}
@Override

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import com.typesafe.config.ConfigValueType;

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
enum FromMapMode {

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import java.io.BufferedReader;

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import java.io.StringReader;

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import java.util.Iterator;

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import java.util.Stack;

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import java.io.IOException;
@ -177,11 +180,12 @@ final class PropertiesParser {
.get(parentPath) : root;
AbstractConfigObject o = new SimpleConfigObject(origin, scope,
ResolveStatus.RESOLVED);
ResolveStatus.RESOLVED, false /* ignoresFallbacks */);
parent.put(scopePath.last(), o);
}
// return root config object
return new SimpleConfigObject(origin, root, ResolveStatus.RESOLVED);
return new SimpleConfigObject(origin, root, ResolveStatus.RESOLVED,
false /* ignoresFallbacks */);
}
}

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import java.util.Collection;

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import com.typesafe.config.ConfigMergeable;
@ -32,20 +35,14 @@ final class RootConfig extends SimpleConfig implements ConfigRoot {
// if the object is already resolved then we should end up returning
// "this" here, since asRoot() should return this if the path
// is unchanged.
SimpleConfig resolved = resolvedObject(options).toConfig();
return resolved.asRoot(rootPath);
AbstractConfigObject resolved = resolvedObject(options);
return newRootIfObjectChanged(this, resolved);
}
@Override
public RootConfig withFallback(ConfigMergeable value) {
// this can return "this" if the withFallback does nothing
return super.withFallback(value).asRoot(rootPath);
}
@Override
public RootConfig withFallbacks(ConfigMergeable... values) {
// this can return "this" if the withFallbacks does nothing
return super.withFallbacks(values).asRoot(rootPath);
return newRootIfObjectChanged(this, super.withFallback(value).toObject());
}
Path rootPathObject() {

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import java.util.ArrayList;
@ -56,6 +59,13 @@ class SimpleConfig implements Config {
return new RootConfig(underlying, newRootPath);
}
static protected RootConfig newRootIfObjectChanged(RootConfig self, AbstractConfigObject underlying) {
if (underlying == self.object)
return self;
else
return new RootConfig(underlying, self.rootPathObject());
}
protected AbstractConfigObject resolvedObject(ConfigResolveOptions options) {
AbstractConfigValue resolved = SubstitutionResolver.resolve(object,
object, options);
@ -129,15 +139,20 @@ class SimpleConfig implements Config {
return (Boolean) v.unwrapped();
}
private ConfigNumber getConfigNumber(String path) {
ConfigValue v = find(path, ConfigValueType.NUMBER, path);
return (ConfigNumber) v;
}
@Override
public Number getNumber(String path) {
ConfigValue v = find(path, ConfigValueType.NUMBER, path);
return (Number) v.unwrapped();
return getConfigNumber(path).unwrapped();
}
@Override
public int getInt(String path) {
return getNumber(path).intValue();
ConfigNumber n = getConfigNumber(path);
return n.intValueRangeChecked(path);
}
@Override
@ -245,9 +260,9 @@ class SimpleConfig implements Config {
@Override
public List<Integer> getIntList(String path) {
List<Integer> l = new ArrayList<Integer>();
List<Number> numbers = getNumberList(path);
for (Number n : numbers) {
l.add(n.intValue());
List<AbstractConfigValue> numbers = getHomogeneousWrappedList(path, ConfigValueType.NUMBER);
for (AbstractConfigValue v : numbers) {
l.add(((ConfigNumber) v).intValueRangeChecked(path));
}
return l;
}
@ -385,13 +400,6 @@ class SimpleConfig implements Config {
return object.withFallback(other).toConfig();
}
@Override
public SimpleConfig withFallbacks(ConfigMergeable... others) {
// this can return "this" if the withFallbacks doesn't need a new
// ConfigObject
return object.withFallbacks(others).toConfig();
}
@Override
public final boolean equals(Object other) {
if (other instanceof SimpleConfig) {

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import java.util.ArrayList;

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import java.util.AbstractMap;
@ -17,21 +20,23 @@ final class SimpleConfigObject extends AbstractConfigObject {
// this map should never be modified - assume immutable
final private Map<String, AbstractConfigValue> value;
final private boolean resolved;
final private boolean ignoresFallbacks;
SimpleConfigObject(ConfigOrigin origin,
Map<String, AbstractConfigValue> value, ResolveStatus status) {
Map<String, AbstractConfigValue> value, ResolveStatus status,
boolean ignoresFallbacks) {
super(origin);
if (value == null)
throw new ConfigException.BugOrBroken(
"creating config object with null map");
this.value = value;
this.resolved = status == ResolveStatus.RESOLVED;
this.ignoresFallbacks = ignoresFallbacks;
}
SimpleConfigObject(ConfigOrigin origin,
Map<String, AbstractConfigValue> value) {
this(origin, value, ResolveStatus.fromValues(value
.values()));
this(origin, value, ResolveStatus.fromValues(value.values()), false /* ignoresFallbacks */);
}
@Override
@ -40,9 +45,8 @@ final class SimpleConfigObject extends AbstractConfigObject {
}
@Override
public SimpleConfigObject newCopy(ResolveStatus newStatus) {
return new SimpleConfigObject(origin(), value,
newStatus);
protected SimpleConfigObject newCopy(ResolveStatus newStatus, boolean newIgnoresFallbacks) {
return new SimpleConfigObject(origin(), value, newStatus, newIgnoresFallbacks);
}
@Override
@ -50,6 +54,11 @@ final class SimpleConfigObject extends AbstractConfigObject {
return ResolveStatus.fromBoolean(resolved);
}
@Override
protected boolean ignoresFallbacks() {
return ignoresFallbacks;
}
@Override
public Map<String, Object> unwrapped() {
Map<String, Object> m = new HashMap<String, Object>();

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import com.typesafe.config.ConfigOrigin;

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import java.util.IdentityHashMap;

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
class Token {

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
enum TokenType {

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import java.io.IOException;

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import java.util.List;

View file

@ -1,3 +1,6 @@
/**
* Copyright (C) 2011 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config.impl;
import java.util.Collection;