move the "replacements" functionality to ResolveSource from Context

Conceptually, a "replacement" is a transformation of ResolveSource
This commit is contained in:
Havoc Pennington 2012-03-30 21:00:01 -04:00
parent a22924de6d
commit 15bcfc984d
2 changed files with 48 additions and 33 deletions

View File

@ -1,9 +1,7 @@
package com.typesafe.config.impl;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.LinkedHashSet;
import java.util.concurrent.Callable;
@ -36,27 +34,22 @@ final class ResolveContext {
// cause a cycle "by side effect"
// CAN BE NULL for a full resolve.
final private Path restrictToChild;
// if we try to resolve something in here, use the
// given replacement instead.
final private Map<MemoKey, LinkedList<ResolveReplacer>> replacements;
ResolveContext(ResolveSource source, ResolveMemos memos,
LinkedList<Set<MemoKey>> traversedStack, ConfigResolveOptions options,
Path restrictToChild, Map<MemoKey, LinkedList<ResolveReplacer>> replacements) {
Path restrictToChild) {
this.source = source;
this.memos = memos;
this.traversedStack = traversedStack;
this.options = options;
this.restrictToChild = restrictToChild;
this.replacements = replacements;
}
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 ResolveSource(root), new ResolveMemos(), new LinkedList<Set<MemoKey>>(
Collections.singletonList(new LinkedHashSet<MemoKey>())), options, restrictToChild,
new HashMap<MemoKey, LinkedList<ResolveReplacer>>());
Collections.singletonList(new LinkedHashSet<MemoKey>())), options, restrictToChild);
}
private void traverse(ConfigSubstitution value, SubstitutionExpression via)
@ -105,39 +98,22 @@ final class ResolveContext {
}
void replace(AbstractConfigValue value, ResolveReplacer replacer) {
MemoKey key = new MemoKey(value, null /* restrictToChild */);
LinkedList<ResolveReplacer> stack = replacements.get(key);
if (stack == null) {
stack = new LinkedList<ResolveReplacer>();
replacements.put(key, stack);
}
stack.addFirst(replacer);
source.replace(value, replacer);
// we have to reset the cycle detection because with the
// replacement, a cycle may be broken
// replacement, a cycle may not exist anymore.
traversedStack.addFirst(new LinkedHashSet<MemoKey>());
}
void unreplace(AbstractConfigValue value) {
MemoKey key = new MemoKey(value, null /* restrictToChild */);
LinkedList<ResolveReplacer> stack = replacements.get(key);
if (stack == null)
throw new ConfigException.BugOrBroken("unreplace() without replace(): " + value);
source.unreplace(value);
stack.removeFirst();
Set<MemoKey> oldTraversed = traversedStack.removeFirst();
if (!oldTraversed.isEmpty())
throw new ConfigException.BugOrBroken(
"unreplace() with stuff still in the traverse set: " + oldTraversed);
}
AbstractConfigValue replacement(MemoKey key) throws Undefined {
LinkedList<ResolveReplacer> stack = replacements.get(new MemoKey(key.value(), null));
if (stack == null || stack.isEmpty())
return key.value();
else
return stack.peek().replace();
}
ResolveSource source() {
return source;
}
@ -158,8 +134,7 @@ final class ResolveContext {
if (restrictTo == restrictToChild)
return this;
else
return new ResolveContext(source, memos, traversedStack, options, restrictTo,
replacements);
return new ResolveContext(source, memos, traversedStack, options, restrictTo);
}
ResolveContext unrestricted() {
@ -192,7 +167,7 @@ final class ResolveContext {
AbstractConfigValue replacement;
boolean forceUndefined = false;
try {
replacement = replacement(key);
replacement = source.replacement(key);
} catch (Undefined e) {
replacement = original;
forceUndefined = true;

View File

@ -1,15 +1,28 @@
package com.typesafe.config.impl;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import com.typesafe.config.ConfigException;
import com.typesafe.config.impl.AbstractConfigValue.NotPossibleToResolve;
import com.typesafe.config.impl.ResolveReplacer.Undefined;
/**
* This class is the source for values for a substitution like ${foo}.
*/
final class ResolveSource {
final private AbstractConfigObject root;
// Conceptually, we transform the ResolveSource whenever we traverse
// a substitution or delayed merge stack, in order to remove the
// traversed node and therefore avoid circular dependencies.
// We implement it with this somewhat hacky "patch a replacement"
// mechanism instead of actually transforming the tree.
final private Map<MemoKey, LinkedList<ResolveReplacer>> replacements;
ResolveSource(AbstractConfigObject root) {
this.root = root;
this.replacements = new HashMap<MemoKey, LinkedList<ResolveReplacer>>();
}
static private AbstractConfigValue findInObject(final AbstractConfigObject obj,
@ -58,4 +71,31 @@ final class ResolveSource {
return result;
}
void replace(AbstractConfigValue value, ResolveReplacer replacer) {
MemoKey key = new MemoKey(value, null /* restrictToChild */);
LinkedList<ResolveReplacer> stack = replacements.get(key);
if (stack == null) {
stack = new LinkedList<ResolveReplacer>();
replacements.put(key, stack);
}
stack.addFirst(replacer);
}
void unreplace(AbstractConfigValue value) {
MemoKey key = new MemoKey(value, null /* restrictToChild */);
LinkedList<ResolveReplacer> stack = replacements.get(key);
if (stack == null)
throw new ConfigException.BugOrBroken("unreplace() without replace(): " + value);
stack.removeFirst();
}
AbstractConfigValue replacement(MemoKey key) throws Undefined {
LinkedList<ResolveReplacer> stack = replacements.get(new MemoKey(key.value(), null));
if (stack == null || stack.isEmpty())
return key.value();
else
return stack.peek().replace();
}
}