split 'ResolveSource' from SubstitutionResolver and ConfigSubstitution.

this is part of a plan to remove SubstitutionResolver (moving the
memoized hash to ResolveContext) and to move the "replacements"
functionality to ResolveSource. Should be clearer.
This commit is contained in:
Havoc Pennington 2012-03-30 19:24:44 -04:00
parent e438dfc8c2
commit d6b27c839a
4 changed files with 87 additions and 65 deletions

View File

@ -133,54 +133,7 @@ final class ConfigSubstitution extends AbstractConfigValue implements
return pieces;
}
/** resolver is null if we should not have refs */
private AbstractConfigValue findInObject(final AbstractConfigObject root,
final SubstitutionResolver resolver, final SubstitutionExpression subst,
final ResolveContext context) throws NotPossibleToResolve {
return context.traversing(this, subst, new ResolveContext.Resolver() {
@Override
public AbstractConfigValue call() throws NotPossibleToResolve {
return root.peekPath(subst.path(), resolver, context);
}
});
}
private AbstractConfigValue resolve(final SubstitutionResolver resolver,
final SubstitutionExpression subst, final ResolveContext context)
throws NotPossibleToResolve {
// 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, context);
if (result == null) {
// Then we want to check relative to the root file. We don't
// want the prefix we were included at to be used when looking
// up env variables either.
SubstitutionExpression unprefixed = subst
.changePath(subst.path().subPath(prefixLength));
if (result == null && prefixLength > 0) {
result = findInObject(resolver.root(), resolver, unprefixed, context);
}
if (result == null && context.options().getUseSystemEnvironment()) {
result = findInObject(ConfigImpl.envVariablesAsConfigObject(), null, unprefixed,
context);
}
}
if (result != null) {
final AbstractConfigValue unresolved = result;
result = context.traversing(this, subst, new ResolveContext.Resolver() {
@Override
public AbstractConfigValue call() throws NotPossibleToResolve {
return resolver.resolve(unresolved, context);
}
});
}
return result;
}
private static ResolveReplacer undefinedReplacer = new ResolveReplacer() {
@Override
@ -201,7 +154,8 @@ final class ConfigSubstitution extends AbstractConfigValue implements
// to concat into a string we have to do a full resolve,
// so unrestrict the context
AbstractConfigValue v = resolve(resolver, exp, context.unrestricted());
AbstractConfigValue v = context.source().lookupSubst(resolver,
context.unrestricted(), this, exp, prefixLength);
if (v == null) {
if (exp.optional()) {
@ -233,7 +187,8 @@ final class ConfigSubstitution extends AbstractConfigValue implements
"ConfigSubstitution should never contain a single String piece");
SubstitutionExpression exp = (SubstitutionExpression) pieces.get(0);
AbstractConfigValue v = resolve(resolver, exp, context);
AbstractConfigValue v = context.source().lookupSubst(resolver, context, this, exp,
prefixLength);
if (v == null && !exp.optional()) {
throw new ConfigException.UnresolvedSubstitution(origin(), exp.toString());

View File

@ -15,6 +15,9 @@ import com.typesafe.config.impl.AbstractConfigValue.SelfReferential;
import com.typesafe.config.impl.ResolveReplacer.Undefined;
final class ResolveContext {
// this is unfortunately mutable so should only be shared among
// ResolveContext in the same traversal.
final private ResolveSource source;
// Resolves that we have already begun (for cycle detection).
// SubstitutionResolver separately memoizes completed resolves.
// this set is unfortunately mutable and the user of ResolveContext
@ -30,19 +33,21 @@ final class ResolveContext {
// given replacement instead.
final private Map<MemoKey, LinkedList<ResolveReplacer>> replacements;
ResolveContext(LinkedList<Set<MemoKey>> traversedStack, ConfigResolveOptions options,
Path restrictToChild,
ResolveContext(ResolveSource source, LinkedList<Set<MemoKey>> traversedStack,
ConfigResolveOptions options, Path restrictToChild,
Map<MemoKey, LinkedList<ResolveReplacer>> replacements) {
this.source = source;
this.traversedStack = traversedStack;
this.options = options;
this.restrictToChild = restrictToChild;
this.replacements = replacements;
}
ResolveContext(ConfigResolveOptions options, Path restrictToChild) {
ResolveContext(AbstractConfigObject root, ConfigResolveOptions options, Path restrictToChild) {
// LinkedHashSet keeps the traversal order which is at least useful
// in error messages if nothing else
this(new LinkedList<Set<MemoKey>>(Collections.singletonList(new LinkedHashSet<MemoKey>())),
this(new ResolveSource(root), new LinkedList<Set<MemoKey>>(
Collections.singletonList(new LinkedHashSet<MemoKey>())),
options, restrictToChild, new HashMap<MemoKey, LinkedList<ResolveReplacer>>());
}
@ -125,6 +130,10 @@ final class ResolveContext {
return stack.peek().replace();
}
ResolveSource source() {
return source;
}
ConfigResolveOptions options() {
return options;
}
@ -141,7 +150,7 @@ final class ResolveContext {
if (restrictTo == restrictToChild)
return this;
else
return new ResolveContext(traversedStack, options, restrictTo, replacements);
return new ResolveContext(source, traversedStack, options, restrictTo, replacements);
}
ResolveContext unrestricted() {

View File

@ -0,0 +1,64 @@
package com.typesafe.config.impl;
import com.typesafe.config.impl.AbstractConfigValue.NotPossibleToResolve;
/**
* This class is the source for values for a substitution like ${foo}.
*/
final class ResolveSource {
final private AbstractConfigObject root;
ResolveSource(AbstractConfigObject root) {
this.root = root;
}
/** resolver is null if we should not have refs */
static private AbstractConfigValue findInObject(final AbstractConfigObject obj,
final SubstitutionResolver resolver, final ResolveContext context,
ConfigSubstitution traversed, final SubstitutionExpression subst)
throws NotPossibleToResolve {
return context.traversing(traversed, subst, new ResolveContext.Resolver() {
@Override
public AbstractConfigValue call() throws NotPossibleToResolve {
return obj.peekPath(subst.path(), resolver, context);
}
});
}
AbstractConfigValue lookupSubst(final SubstitutionResolver resolver,
final ResolveContext context, ConfigSubstitution traversed,
final SubstitutionExpression subst, int prefixLength) throws NotPossibleToResolve {
// First we look up the full path, which means relative to the
// included file if we were not a root file
AbstractConfigValue result = findInObject(root, resolver, context, traversed, subst);
if (result == null) {
// Then we want to check relative to the root file. We don't
// want the prefix we were included at to be used when looking
// up env variables either.
SubstitutionExpression unprefixed = subst
.changePath(subst.path().subPath(prefixLength));
if (result == null && prefixLength > 0) {
result = findInObject(root, resolver, context, traversed, unprefixed);
}
if (result == null && context.options().getUseSystemEnvironment()) {
result = findInObject(ConfigImpl.envVariablesAsConfigObject(), null, context,
traversed, unprefixed);
}
}
if (result != null) {
final AbstractConfigValue unresolved = result;
result = context.traversing(traversed, subst, new ResolveContext.Resolver() {
@Override
public AbstractConfigValue call() throws NotPossibleToResolve {
return resolver.resolve(unresolved, context);
}
});
}
return result;
}
}

View File

@ -17,13 +17,11 @@ import com.typesafe.config.impl.ResolveReplacer.Undefined;
* of values or whole trees of values as we follow chains of substitutions.
*/
final class SubstitutionResolver {
final private AbstractConfigObject root;
// note that we can resolve things to undefined (represented as Java null,
// rather than ConfigNull) so this map can have null values.
final private Map<MemoKey, AbstractConfigValue> memos;
SubstitutionResolver(AbstractConfigObject root) {
this.root = root;
SubstitutionResolver() {
this.memos = new HashMap<MemoKey, AbstractConfigValue>();
}
@ -97,22 +95,18 @@ final class SubstitutionResolver {
}
}
AbstractConfigObject root() {
return this.root;
}
static AbstractConfigValue resolve(AbstractConfigValue value, AbstractConfigObject root,
ConfigResolveOptions options, Path restrictToChildOrNull) throws NotPossibleToResolve {
SubstitutionResolver resolver = new SubstitutionResolver(root);
ResolveContext context = new ResolveContext(options, restrictToChildOrNull);
SubstitutionResolver resolver = new SubstitutionResolver();
ResolveContext context = new ResolveContext(root, options, restrictToChildOrNull);
return resolver.resolve(value, context);
}
static AbstractConfigValue resolveWithExternalExceptions(AbstractConfigValue value,
AbstractConfigObject root, ConfigResolveOptions options) {
SubstitutionResolver resolver = new SubstitutionResolver(root);
ResolveContext context = new ResolveContext(options, null /* restrictToChild */);
SubstitutionResolver resolver = new SubstitutionResolver();
ResolveContext context = new ResolveContext(root, options, null /* restrictToChild */);
try {
return resolver.resolve(value, context);