combine three resolveSubstitutions() parameters into ResolveContext

This makes it easier to mess with the parameters needed without
changing every resolveSubstitutions() all over the place.

Really the SubstitutionResolver and ResolveContext should maybe
be merged, but keeping this patch more incremental.
This commit is contained in:
Havoc Pennington 2012-03-29 09:51:58 -04:00
parent 6de4a991d4
commit d9c9adc39f
10 changed files with 127 additions and 107 deletions

View File

@ -6,16 +6,13 @@ package com.typesafe.config.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.typesafe.config.ConfigException;
import com.typesafe.config.ConfigMergeable;
import com.typesafe.config.ConfigObject;
import com.typesafe.config.ConfigOrigin;
import com.typesafe.config.ConfigResolveOptions;
import com.typesafe.config.ConfigValue;
import com.typesafe.config.ConfigValueType;
@ -92,9 +89,8 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
* @throws NotPossibleToResolve
*/
protected AbstractConfigValue peekPath(Path path, SubstitutionResolver resolver,
Set<MemoKey> traversed, ConfigResolveOptions options)
throws NotPossibleToResolve, NeedsFullResolve {
return peekPath(this, path, resolver, traversed, options);
ResolveContext context) throws NotPossibleToResolve, NeedsFullResolve {
return peekPath(this, path, resolver, context);
}
/**
@ -103,7 +99,7 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
*/
AbstractConfigValue peekPathWithExternalExceptions(Path path) {
try {
return peekPath(this, path, null, Collections.<MemoKey> emptySet(), null);
return peekPath(this, path, null, null);
} catch (NotPossibleToResolve e) {
throw e.exportException(origin(), path.render());
} catch (NeedsFullResolve e) {
@ -116,16 +112,14 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
// child being peeked, but NOT the child itself. Caller has to resolve
// the child itself if needed.
private static AbstractConfigValue peekPath(AbstractConfigObject self, Path path,
SubstitutionResolver resolver, Set<MemoKey> traversed, ConfigResolveOptions options)
SubstitutionResolver resolver, ResolveContext context)
throws NotPossibleToResolve, NeedsFullResolve {
if (resolver != null) {
// walk down through the path resolving only things along that path,
// and then recursively call ourselves with no resolver.
AbstractConfigValue partiallyResolved = resolver
.resolve(self, traversed, options, path);
AbstractConfigValue partiallyResolved = resolver.resolve(self, context.restrict(path));
if (partiallyResolved instanceof AbstractConfigObject) {
return peekPath((AbstractConfigObject) partiallyResolved, path, null,
traversed, null);
return peekPath((AbstractConfigObject) partiallyResolved, path, null, null);
} else {
throw new ConfigException.BugOrBroken("resolved object to non-object " + self
+ " to " + partiallyResolved);
@ -140,8 +134,7 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
return v;
} else {
if (v instanceof AbstractConfigObject) {
return peekPath((AbstractConfigObject) v, next, null,
Collections.<MemoKey> emptySet(), null);
return peekPath((AbstractConfigObject) v, next, null, null);
} else {
return null;
}
@ -223,9 +216,8 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
}
@Override
abstract AbstractConfigObject resolveSubstitutions(final SubstitutionResolver resolver,
Set<MemoKey> traversed, ConfigResolveOptions options, Path restrictToChildOrNull)
throws NotPossibleToResolve, NeedsFullResolve;
abstract AbstractConfigObject resolveSubstitutions(SubstitutionResolver resolver,
ResolveContext context) throws NotPossibleToResolve, NeedsFullResolve;
@Override
abstract AbstractConfigObject relativized(final Path prefix);

View File

@ -4,12 +4,10 @@
package com.typesafe.config.impl;
import java.io.Serializable;
import java.util.Set;
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;
/**
@ -102,17 +100,12 @@ abstract class AbstractConfigValue implements ConfigValue, MergeableValue, Seria
*
* @param resolver
* the resolver doing the resolving
* @param traversed
* objects which have already been visited, will include this one
* @param options
* whether to look at system props and env vars
* @param restrictToChildOrNull
* if non-null, only recurse into this child path
* @param context
* state of the current resolve
* @return a new value if there were changes, or this if no changes
*/
AbstractConfigValue resolveSubstitutions(SubstitutionResolver resolver, Set<MemoKey> traversed,
ConfigResolveOptions options, Path restrictToChildOrNull) throws NotPossibleToResolve,
NeedsFullResolve {
AbstractConfigValue resolveSubstitutions(SubstitutionResolver resolver, ResolveContext context)
throws NotPossibleToResolve, NeedsFullResolve {
return this;
}

View File

@ -8,11 +8,9 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import com.typesafe.config.ConfigException;
import com.typesafe.config.ConfigOrigin;
import com.typesafe.config.ConfigResolveOptions;
import com.typesafe.config.ConfigValueType;
/**
@ -65,16 +63,15 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements
}
@Override
AbstractConfigValue resolveSubstitutions(SubstitutionResolver resolver, Set<MemoKey> traversed,
ConfigResolveOptions options, Path restrictToChildOrNull) throws NotPossibleToResolve,
NeedsFullResolve {
return resolveSubstitutions(stack, resolver, traversed, options, restrictToChildOrNull);
AbstractConfigValue resolveSubstitutions(SubstitutionResolver resolver, ResolveContext context)
throws NotPossibleToResolve, NeedsFullResolve {
return resolveSubstitutions(stack, resolver, context);
}
// static method also used by ConfigDelayedMergeObject
static AbstractConfigValue resolveSubstitutions(List<AbstractConfigValue> stack,
SubstitutionResolver resolver, Set<MemoKey> traversed, ConfigResolveOptions options,
Path restrictToChildOrNull) throws NotPossibleToResolve, NeedsFullResolve {
SubstitutionResolver resolver, ResolveContext context) throws NotPossibleToResolve,
NeedsFullResolve {
// to resolve substitutions, we need to recursively resolve
// the stack of stuff to merge, and merge the stack so
// we won't be a delayed merge anymore. If restrictToChildOrNull
@ -82,8 +79,7 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements
AbstractConfigValue merged = null;
for (AbstractConfigValue v : stack) {
AbstractConfigValue resolved = resolver.resolve(v, traversed, options,
restrictToChildOrNull);
AbstractConfigValue resolved = resolver.resolve(v, context);
if (resolved != null) {
if (merged == null)
merged = resolved;

View File

@ -13,7 +13,6 @@ import java.util.Set;
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;
// This is just like ConfigDelayedMerge except we know statically
@ -61,11 +60,10 @@ final class ConfigDelayedMergeObject extends AbstractConfigObject implements
}
@Override
AbstractConfigObject resolveSubstitutions(SubstitutionResolver resolver,
Set<MemoKey> traversed, ConfigResolveOptions options, Path restrictToChildOrNull)
AbstractConfigObject resolveSubstitutions(SubstitutionResolver resolver, ResolveContext context)
throws NotPossibleToResolve, NeedsFullResolve {
AbstractConfigValue merged = ConfigDelayedMerge.resolveSubstitutions(stack, resolver,
traversed, options, restrictToChildOrNull);
context);
if (merged instanceof AbstractConfigObject) {
return (AbstractConfigObject) merged;
} else {

View File

@ -8,11 +8,9 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import com.typesafe.config.ConfigException;
import com.typesafe.config.ConfigOrigin;
import com.typesafe.config.ConfigResolveOptions;
import com.typesafe.config.ConfigValue;
import com.typesafe.config.ConfigValueType;
@ -136,30 +134,25 @@ final class ConfigSubstitution extends AbstractConfigValue implements
private static AbstractConfigValue findInObject(AbstractConfigObject root,
SubstitutionResolver resolver, /* null if we should not have refs */
Path subst, Set<MemoKey> traversed, ConfigResolveOptions options)
Path subst, ResolveContext context)
throws NotPossibleToResolve, NeedsFullResolve {
AbstractConfigValue result = root.peekPath(subst, resolver, traversed, options);
AbstractConfigValue result = root.peekPath(subst, resolver, context);
return result;
}
private AbstractConfigValue resolve(SubstitutionResolver resolver,
SubstitutionExpression subst, Set<MemoKey> traversed, ConfigResolveOptions options,
Path restrictToChildOrNull) throws NotPossibleToResolve,
SubstitutionExpression subst, ResolveContext context) throws NotPossibleToResolve,
NeedsFullResolve {
MemoKey key = new MemoKey(this, restrictToChildOrNull);
if (traversed.contains(key))
throw new SelfReferential(origin(), subst.path().render());
traversed.add(key);
context.traverse(this, subst.path());
try {
// First we look up the full path, which means relative to the
// included file if we were not a root file
AbstractConfigValue result = findInObject(resolver.root(), resolver, subst.path(),
traversed, options);
context);
if (result == null) {
// Then we want to check relative to the root file. We don't
@ -169,28 +162,28 @@ final class ConfigSubstitution extends AbstractConfigValue implements
Path unprefixed = subst.path().subPath(prefixLength);
if (result == null && prefixLength > 0) {
result = findInObject(resolver.root(), resolver, unprefixed, traversed, options);
result = findInObject(resolver.root(), resolver, unprefixed, context);
}
if (result == null && options.getUseSystemEnvironment()) {
if (result == null && context.options().getUseSystemEnvironment()) {
result = findInObject(ConfigImpl.envVariablesAsConfigObject(), null,
unprefixed, traversed, options);
unprefixed, context);
}
}
if (result != null) {
result = resolver.resolve(result, traversed, options, restrictToChildOrNull);
result = resolver.resolve(result, context);
}
return result;
} finally {
traversed.remove(key);
context.untraverse(this);
}
}
private ConfigValue resolve(SubstitutionResolver resolver, Set<MemoKey> traversed,
ConfigResolveOptions options, Path restrictToChildOrNull) throws NotPossibleToResolve {
private ConfigValue resolve(SubstitutionResolver resolver, ResolveContext context)
throws NotPossibleToResolve {
if (pieces.size() > 1) {
// need to concat everything into a string
StringBuilder sb = new StringBuilder();
@ -202,8 +195,8 @@ final class ConfigSubstitution extends AbstractConfigValue implements
ConfigValue v;
try {
// to concat into a string we have to do a full resolve,
// so don't pass along restrictToChildOrNull
v = resolve(resolver, exp, traversed, options, null);
// so unrestrict the context
v = resolve(resolver, exp, context.unrestricted());
} catch (NeedsFullResolve e) {
throw new NotPossibleToResolve(null, exp.path().render(),
"Some kind of loop or interdependency prevents resolving " + exp, e);
@ -237,7 +230,7 @@ final class ConfigSubstitution extends AbstractConfigValue implements
SubstitutionExpression exp = (SubstitutionExpression) pieces.get(0);
ConfigValue v;
try {
v = resolve(resolver, exp, traversed, options, restrictToChildOrNull);
v = resolve(resolver, exp, context);
} catch (NeedsFullResolve e) {
throw new NotPossibleToResolve(null, exp.path().render(),
"Some kind of loop or interdependency prevents resolving " + exp, e);
@ -250,10 +243,9 @@ final class ConfigSubstitution extends AbstractConfigValue implements
}
@Override
AbstractConfigValue resolveSubstitutions(SubstitutionResolver resolver, Set<MemoKey> traversed,
ConfigResolveOptions options, Path restrictToChildOrNull) throws NotPossibleToResolve {
AbstractConfigValue resolved = (AbstractConfigValue) resolve(resolver, traversed, options,
restrictToChildOrNull);
AbstractConfigValue resolveSubstitutions(SubstitutionResolver resolver, ResolveContext context)
throws NotPossibleToResolve {
AbstractConfigValue resolved = (AbstractConfigValue) resolve(resolver, context);
return resolved;
}

View File

@ -0,0 +1,67 @@
package com.typesafe.config.impl;
import java.util.Set;
import java.util.LinkedHashSet;
import com.typesafe.config.ConfigException;
import com.typesafe.config.ConfigResolveOptions;
import com.typesafe.config.impl.AbstractConfigValue.SelfReferential;
final class ResolveContext {
// this set is unfortunately mutable and the user of ResolveContext
// has to be sure it's only shared between ResolveContext that
// are in the same traversal.
final private Set<MemoKey> traversed;
final private ConfigResolveOptions options;
final private Path restrictToChild; // can be null
ResolveContext(Set<MemoKey> traversed, ConfigResolveOptions options, Path restrictToChild) {
this.traversed = traversed;
this.options = options;
this.restrictToChild = restrictToChild;
}
ResolveContext(ConfigResolveOptions options, Path restrictToChild) {
// LinkedHashSet keeps the traversal order which is at least useful
// in error messages if nothing else
this(new LinkedHashSet<MemoKey>(), options, restrictToChild);
}
void traverse(ConfigSubstitution value, Path via) throws SelfReferential {
MemoKey key = new MemoKey(value, restrictToChild);
if (traversed.contains(key))
throw new SelfReferential(value.origin(), via.render());
traversed.add(key);
}
void untraverse(ConfigSubstitution value) {
MemoKey key = new MemoKey(value, restrictToChild);
if (!traversed.remove(key))
throw new ConfigException.BugOrBroken(
"untraverse() did not find the untraversed substitution " + value);
}
ConfigResolveOptions options() {
return options;
}
boolean isRestrictedToChild() {
return restrictToChild != null;
}
Path restrictToChild() {
return restrictToChild;
}
ResolveContext restrict(Path restrictTo) {
if (restrictTo == restrictToChild)
return this;
else
return new ResolveContext(traversed, options, restrictTo);
}
ResolveContext unrestricted() {
return restrict(null);
}
}

View File

@ -6,7 +6,6 @@ package com.typesafe.config.impl;
import java.io.Serializable;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -74,7 +73,7 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
Path path = Path.newPath(pathExpression);
ConfigValue peeked;
try {
peeked = object.peekPath(path, null, Collections.<MemoKey> emptySet(), null);
peeked = object.peekPath(path, null, null);
} catch (NotPossibleToResolve e) {
throw e.exportException(origin(), pathExpression);
} catch (NeedsFullResolve e) {

View File

@ -9,12 +9,10 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import com.typesafe.config.ConfigException;
import com.typesafe.config.ConfigList;
import com.typesafe.config.ConfigOrigin;
import com.typesafe.config.ConfigResolveOptions;
import com.typesafe.config.ConfigValue;
import com.typesafe.config.ConfigValueType;
@ -106,13 +104,11 @@ final class SimpleConfigList extends AbstractConfigValue implements ConfigList {
@Override
SimpleConfigList resolveSubstitutions(final SubstitutionResolver resolver,
final Set<MemoKey> traversed, final ConfigResolveOptions options,
Path restrictToChildOrNull)
throws NotPossibleToResolve, NeedsFullResolve {
final ResolveContext context) throws NotPossibleToResolve, NeedsFullResolve {
if (resolved)
return this;
if (restrictToChildOrNull != null) {
if (context.isRestrictedToChild()) {
// if a list restricts to a child path, then it has no child paths,
// so nothing to do.
return this;
@ -122,7 +118,7 @@ final class SimpleConfigList extends AbstractConfigValue implements ConfigList {
@Override
public AbstractConfigValue modifyChildMayThrow(String key, AbstractConfigValue v)
throws NotPossibleToResolve, NeedsFullResolve {
return resolver.resolve(v, traversed, options, null /* restrictToChild */);
return resolver.resolve(v, context);
}
}, ResolveStatus.RESOLVED);

View File

@ -16,7 +16,6 @@ import java.util.Set;
import com.typesafe.config.ConfigException;
import com.typesafe.config.ConfigObject;
import com.typesafe.config.ConfigOrigin;
import com.typesafe.config.ConfigResolveOptions;
import com.typesafe.config.ConfigValue;
final class SimpleConfigObject extends AbstractConfigObject {
@ -261,9 +260,7 @@ final class SimpleConfigObject extends AbstractConfigObject {
@Override
AbstractConfigObject resolveSubstitutions(final SubstitutionResolver resolver,
final Set<MemoKey> traversed, final ConfigResolveOptions options,
final Path restrictToChildOrNull)
throws NotPossibleToResolve, NeedsFullResolve {
final ResolveContext context) throws NotPossibleToResolve, NeedsFullResolve {
if (resolveStatus() == ResolveStatus.RESOLVED)
return this;
@ -273,11 +270,11 @@ final class SimpleConfigObject extends AbstractConfigObject {
@Override
public AbstractConfigValue modifyChildMayThrow(String key, AbstractConfigValue v)
throws NotPossibleToResolve, NeedsFullResolve {
if (restrictToChildOrNull != null) {
if (key.equals(restrictToChildOrNull.first())) {
Path remainder = restrictToChildOrNull.remainder();
if (context.isRestrictedToChild()) {
if (key.equals(context.restrictToChild().first())) {
Path remainder = context.restrictToChild().remainder();
if (remainder != null) {
return resolver.resolve(v, traversed, options, remainder);
return resolver.resolve(v, context.restrict(remainder));
} else {
// we don't want to resolve the leaf child.
return v;
@ -288,7 +285,7 @@ final class SimpleConfigObject extends AbstractConfigObject {
}
} else {
// no restrictToChild, resolve everything
return resolver.resolve(v, traversed, options, null);
return resolver.resolve(v, context.unrestricted());
}
}

View File

@ -4,9 +4,7 @@
package com.typesafe.config.impl;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import com.typesafe.config.ConfigException;
import com.typesafe.config.ConfigResolveOptions;
@ -29,9 +27,8 @@ final class SubstitutionResolver {
this.memos = new HashMap<MemoKey, AbstractConfigValue>();
}
AbstractConfigValue resolve(AbstractConfigValue original, Set<MemoKey> traversed,
ConfigResolveOptions options, Path restrictToChildOrNull) throws NotPossibleToResolve,
NeedsFullResolve {
AbstractConfigValue resolve(AbstractConfigValue original, ResolveContext context)
throws NotPossibleToResolve, NeedsFullResolve {
// a fully-resolved (no restrictToChild) object can satisfy a
// request for a restricted object, so always check that first.
@ -43,16 +40,15 @@ final class SubstitutionResolver {
// but if there was no fully-resolved object cached, we'll only
// compute the restrictToChild object so use a more limited
// memo key
if (cached == null && restrictToChildOrNull != null) {
restrictedKey = new MemoKey(original, restrictToChildOrNull);
if (cached == null && context.isRestrictedToChild()) {
restrictedKey = new MemoKey(original, context.restrictToChild());
cached = memos.get(restrictedKey);
}
if (cached != null) {
return cached;
} else {
AbstractConfigValue resolved = original.resolveSubstitutions(this, traversed, options,
restrictToChildOrNull);
AbstractConfigValue resolved = original.resolveSubstitutions(this, context);
if (resolved == null || resolved.resolveStatus() == ResolveStatus.RESOLVED) {
// if the resolved object is fully resolved by resolving
@ -64,15 +60,15 @@ final class SubstitutionResolver {
// if we have an unresolved object then either we did a
// partial resolve restricted to a certain child, or it's
// a bug.
if (restrictToChildOrNull == null) {
throw new ConfigException.BugOrBroken(
"resolveSubstitutions() did not give us a resolved object");
} else {
if (context.isRestrictedToChild()) {
if (restrictedKey == null) {
throw new ConfigException.BugOrBroken(
"restrictedKey should not be null here");
}
memos.put(restrictedKey, resolved);
} else {
throw new ConfigException.BugOrBroken(
"resolveSubstitutions() did not give us a resolved object");
}
}
@ -84,25 +80,19 @@ final class SubstitutionResolver {
return this.root;
}
private static Set<MemoKey> newTraversedSet() {
// using LinkedHashSet just to show the order of traversal
// in error messages.
return new LinkedHashSet<MemoKey>();
}
static AbstractConfigValue resolve(AbstractConfigValue value, AbstractConfigObject root,
ConfigResolveOptions options, Path restrictToChildOrNull) throws NotPossibleToResolve,
NeedsFullResolve {
SubstitutionResolver resolver = new SubstitutionResolver(root);
return resolver.resolve(value, newTraversedSet(), options, restrictToChildOrNull);
return resolver.resolve(value, new ResolveContext(options, restrictToChildOrNull));
}
static AbstractConfigValue resolveWithExternalExceptions(AbstractConfigValue value,
AbstractConfigObject root, ConfigResolveOptions options) {
SubstitutionResolver resolver = new SubstitutionResolver(root);
try {
return resolver.resolve(value, newTraversedSet(), options, null /* restrictToChild */);
return resolver.resolve(value, new ResolveContext(options, null /* restrictToChild */));
} catch (NotPossibleToResolve e) {
throw e.exportException(value.origin(), null);
} catch (NeedsFullResolve e) {