mirror of
https://github.com/lightbend/config.git
synced 2025-01-16 07:10:21 +08:00
Split ConfigSubstitution into ConfigConcatenation and ConfigReference
This makes the code a good bit simpler to reason about, the old ConfigSubstitution really mixed two things into one class. Unfortunately old ConfigSubstitution is hanging around as a compat shim so we can deserialize stuff from the old version of the library. The old version will not be able to deserialize unresolved configs from this new version. In retrospect it might have been better to forbid serialization of unresolved configs and only support serializing resolved configs.
This commit is contained in:
parent
27d92bec46
commit
006777c062
@ -0,0 +1,228 @@
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.typesafe.config.ConfigException;
|
||||
import com.typesafe.config.ConfigOrigin;
|
||||
import com.typesafe.config.ConfigValueType;
|
||||
|
||||
/**
|
||||
* A ConfigConcatenation represents a list of values to be concatenated (see the
|
||||
* spec). It only has to exist if at least one value is an unresolved
|
||||
* substitution, otherwise we could go ahead and collapse the list into a single
|
||||
* value.
|
||||
*
|
||||
* Right now this is always a list of strings and ${} references, but in the
|
||||
* future should support a list of ConfigList. We may also support
|
||||
* concatenations of objects, but ConfigDelayedMerge should be used for that
|
||||
* since a concat of objects really will merge, not concatenate.
|
||||
*/
|
||||
final class ConfigConcatenation extends AbstractConfigValue implements Unmergeable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
final private List<AbstractConfigValue> pieces;
|
||||
|
||||
ConfigConcatenation(ConfigOrigin origin, List<AbstractConfigValue> pieces) {
|
||||
super(origin);
|
||||
this.pieces = pieces;
|
||||
}
|
||||
|
||||
private ConfigException.NotResolved notResolved() {
|
||||
return new ConfigException.NotResolved(
|
||||
"need to Config#resolve(), see the API docs for Config#resolve(); substitution not resolved: "
|
||||
+ this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigValueType valueType() {
|
||||
throw notResolved();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object unwrapped() {
|
||||
throw notResolved();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigConcatenation newCopy(boolean ignoresFallbacks, ConfigOrigin newOrigin) {
|
||||
return new ConfigConcatenation(newOrigin, pieces);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean ignoresFallbacks() {
|
||||
// we can never ignore fallbacks because if a child ConfigReference
|
||||
// is self-referential we have to look lower in the merge stack
|
||||
// for its value.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractConfigValue mergedWithTheUnmergeable(Unmergeable fallback) {
|
||||
// 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());
|
||||
}
|
||||
|
||||
protected AbstractConfigValue mergedLater(AbstractConfigValue fallback) {
|
||||
List<AbstractConfigValue> newStack = new ArrayList<AbstractConfigValue>();
|
||||
newStack.add(this);
|
||||
newStack.add(fallback);
|
||||
return new ConfigDelayedMerge(AbstractConfigObject.mergeOrigins(newStack), newStack,
|
||||
fallback.ignoresFallbacks());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractConfigValue mergedWithObject(AbstractConfigObject fallback) {
|
||||
// if we turn out to be an object, and the fallback also does,
|
||||
// then a merge may be required; delay until we resolve.
|
||||
return mergedLater(fallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractConfigValue mergedWithNonObject(AbstractConfigValue fallback) {
|
||||
// We may need the fallback if we contain a self-referential
|
||||
// ConfigReference.
|
||||
//
|
||||
// we can't easily detect the self-referential case since the cycle
|
||||
// may involve more than one step, so we have to wait and
|
||||
// merge later when resolving.
|
||||
return mergedLater(fallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ConfigConcatenation> unmergedValues() {
|
||||
return Collections.singleton(this);
|
||||
}
|
||||
|
||||
private static ResolveReplacer undefinedReplacer = new ResolveReplacer() {
|
||||
@Override
|
||||
protected AbstractConfigValue makeReplacement() throws Undefined {
|
||||
throw new Undefined();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
AbstractConfigValue resolveSubstitutions(ResolveContext context) throws NotPossibleToResolve {
|
||||
List<AbstractConfigValue> resolved = new ArrayList<AbstractConfigValue>(pieces.size());
|
||||
// if you have "foo = ${?foo}bar" then we will
|
||||
// self-referentially look up foo and we need to
|
||||
// get undefined, rather than "bar"
|
||||
context.replace(this, undefinedReplacer);
|
||||
try {
|
||||
for (AbstractConfigValue p : pieces) {
|
||||
// to concat into a string we have to do a full resolve,
|
||||
// so unrestrict the context
|
||||
AbstractConfigValue r = context.unrestricted().resolve(p);
|
||||
if (r == null) {
|
||||
// it was optional... omit
|
||||
} else {
|
||||
switch (r.valueType()) {
|
||||
case LIST:
|
||||
case OBJECT:
|
||||
// cannot substitute lists and objects into strings
|
||||
// we know p was a ConfigReference since it wasn't
|
||||
// a ConfigString
|
||||
String pathString = ((ConfigReference) p).expression().toString();
|
||||
throw new ConfigException.WrongType(r.origin(), pathString, "not a list or object", r.valueType().name());
|
||||
default:
|
||||
resolved.add(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
context.unreplace(this);
|
||||
}
|
||||
|
||||
// now need to concat everything
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (AbstractConfigValue r : resolved) {
|
||||
sb.append(r.transformToString());
|
||||
}
|
||||
|
||||
return new ConfigString(origin(), sb.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
ResolveStatus resolveStatus() {
|
||||
return ResolveStatus.UNRESOLVED;
|
||||
}
|
||||
|
||||
// when you graft a substitution into another object,
|
||||
// you have to prefix it with the location in that object
|
||||
// where you grafted it; but save prefixLength so
|
||||
// system property and env variable lookups don't get
|
||||
// broken.
|
||||
@Override
|
||||
ConfigConcatenation relativized(Path prefix) {
|
||||
List<AbstractConfigValue> newPieces = new ArrayList<AbstractConfigValue>();
|
||||
for (AbstractConfigValue p : pieces) {
|
||||
newPieces.add(p.relativized(prefix));
|
||||
}
|
||||
return new ConfigConcatenation(origin(), newPieces);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
protected boolean canEqual(Object other) {
|
||||
return other instanceof ConfigConcatenation || other instanceof ConfigSubstitution;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
// note that "origin" is deliberately NOT part of equality
|
||||
if (other instanceof ConfigConcatenation) {
|
||||
return canEqual(other) && this.pieces.equals(((ConfigConcatenation) other).pieces);
|
||||
} else if (other instanceof ConfigSubstitution) {
|
||||
return equals(((ConfigSubstitution) other).delegate());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// note that "origin" is deliberately NOT part of equality
|
||||
return pieces.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void render(StringBuilder sb, int indent, boolean formatted) {
|
||||
for (AbstractConfigValue p : pieces) {
|
||||
p.render(sb, indent, formatted);
|
||||
}
|
||||
}
|
||||
|
||||
// This ridiculous hack is because some JDK versions apparently can't
|
||||
// serialize an array, which is used to implement ArrayList and EmptyList.
|
||||
// maybe
|
||||
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6446627
|
||||
private Object writeReplace() throws ObjectStreamException {
|
||||
// switch to LinkedList
|
||||
return new ConfigConcatenation(origin(), new java.util.LinkedList<AbstractConfigValue>(
|
||||
pieces));
|
||||
}
|
||||
|
||||
static List<AbstractConfigValue> valuesFromPieces(ConfigOrigin origin, List<Object> pieces) {
|
||||
List<AbstractConfigValue> values = new ArrayList<AbstractConfigValue>(pieces.size());
|
||||
for (Object p : pieces) {
|
||||
if (p instanceof SubstitutionExpression) {
|
||||
values.add(new ConfigReference(origin, (SubstitutionExpression) p));
|
||||
} else if (p instanceof String) {
|
||||
values.add(new ConfigString(origin, (String) p));
|
||||
} else {
|
||||
throw new ConfigException.BugOrBroken("Unexpected piece " + p);
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
@ -0,0 +1,165 @@
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.typesafe.config.ConfigException;
|
||||
import com.typesafe.config.ConfigOrigin;
|
||||
import com.typesafe.config.ConfigValueType;
|
||||
|
||||
/**
|
||||
* ConfigReference replaces ConfigReference (the older class kept for back
|
||||
* compat) and represents the ${} substitution syntax. It can resolve to any
|
||||
* kind of value.
|
||||
*/
|
||||
final class ConfigReference extends AbstractConfigValue implements Unmergeable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
final private SubstitutionExpression expr;
|
||||
// the length of any prefixes added with relativized()
|
||||
final private int prefixLength;
|
||||
|
||||
ConfigReference(ConfigOrigin origin, SubstitutionExpression expr) {
|
||||
this(origin, expr, 0);
|
||||
}
|
||||
|
||||
private ConfigReference(ConfigOrigin origin, SubstitutionExpression expr, int prefixLength) {
|
||||
super(origin);
|
||||
this.expr = expr;
|
||||
this.prefixLength = prefixLength;
|
||||
}
|
||||
|
||||
private ConfigException.NotResolved notResolved() {
|
||||
return new ConfigException.NotResolved(
|
||||
"need to Config#resolve(), see the API docs for Config#resolve(); substitution not resolved: "
|
||||
+ this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigValueType valueType() {
|
||||
throw notResolved();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object unwrapped() {
|
||||
throw notResolved();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigReference newCopy(boolean ignoresFallbacks, ConfigOrigin newOrigin) {
|
||||
if (ignoresFallbacks)
|
||||
throw new ConfigException.BugOrBroken("Cannot ignore fallbacks for " + this);
|
||||
return new ConfigReference(newOrigin, expr, prefixLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean ignoresFallbacks() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractConfigValue mergedWithTheUnmergeable(Unmergeable fallback) {
|
||||
// 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());
|
||||
}
|
||||
|
||||
protected AbstractConfigValue mergedLater(AbstractConfigValue fallback) {
|
||||
List<AbstractConfigValue> newStack = new ArrayList<AbstractConfigValue>();
|
||||
newStack.add(this);
|
||||
newStack.add(fallback);
|
||||
return new ConfigDelayedMerge(AbstractConfigObject.mergeOrigins(newStack), newStack,
|
||||
fallback.ignoresFallbacks());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractConfigValue mergedWithObject(AbstractConfigObject fallback) {
|
||||
// if we turn out to be an object, and the fallback also does,
|
||||
// then a merge may be required; delay until we resolve.
|
||||
return mergedLater(fallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractConfigValue mergedWithNonObject(AbstractConfigValue fallback) {
|
||||
// We may need the fallback for two reasons:
|
||||
// 1. if an optional substitution ends up getting deleted
|
||||
// because it is not defined
|
||||
// 2. if the substitution is self-referential
|
||||
//
|
||||
// we can't easily detect the self-referential case since the cycle
|
||||
// may involve more than one step, so we have to wait and
|
||||
// merge later when resolving.
|
||||
return mergedLater(fallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ConfigReference> unmergedValues() {
|
||||
return Collections.singleton(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
AbstractConfigValue resolveSubstitutions(ResolveContext context) throws NotPossibleToResolve {
|
||||
AbstractConfigValue v = context.source().lookupSubst(context, this, expr, prefixLength);
|
||||
|
||||
if (v == null && !expr.optional()) {
|
||||
throw new ConfigException.UnresolvedSubstitution(origin(), expr.toString());
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
ResolveStatus resolveStatus() {
|
||||
return ResolveStatus.UNRESOLVED;
|
||||
}
|
||||
|
||||
// when you graft a substitution into another object,
|
||||
// you have to prefix it with the location in that object
|
||||
// where you grafted it; but save prefixLength so
|
||||
// system property and env variable lookups don't get
|
||||
// broken.
|
||||
@Override
|
||||
ConfigReference relativized(Path prefix) {
|
||||
SubstitutionExpression newExpr = expr.changePath(expr.path().prepend(prefix));
|
||||
return new ConfigReference(origin(), newExpr, prefixLength + prefix.length());
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
protected boolean canEqual(Object other) {
|
||||
return other instanceof ConfigReference || other instanceof ConfigSubstitution;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
// note that "origin" is deliberately NOT part of equality
|
||||
if (other instanceof ConfigReference) {
|
||||
return canEqual(other) && this.expr.equals(((ConfigReference) other).expr);
|
||||
} else if (other instanceof ConfigSubstitution) {
|
||||
return equals(((ConfigSubstitution) other).delegate());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// note that "origin" is deliberately NOT part of equality
|
||||
return expr.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void render(StringBuilder sb, int indent, boolean formatted) {
|
||||
sb.append(expr.toString());
|
||||
}
|
||||
|
||||
SubstitutionExpression expression() {
|
||||
return expr;
|
||||
}
|
||||
}
|
@ -3,10 +3,9 @@
|
||||
*/
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.typesafe.config.ConfigException;
|
||||
@ -14,10 +13,11 @@ import com.typesafe.config.ConfigOrigin;
|
||||
import com.typesafe.config.ConfigValueType;
|
||||
|
||||
/**
|
||||
* A ConfigSubstitution represents a value with one or more substitutions in it;
|
||||
* it can resolve to a value of any type, though if the substitution has more
|
||||
* than one piece it always resolves to a string via value concatenation.
|
||||
* ConfigSubstitution is now a shim for back-compat with serialization. i.e. an
|
||||
* old version may unserialize one of these. We now split into ConfigReference
|
||||
* and ConfigConcatenation.
|
||||
*/
|
||||
@Deprecated
|
||||
final class ConfigSubstitution extends AbstractConfigValue implements
|
||||
Unmergeable {
|
||||
|
||||
@ -27,224 +27,109 @@ final class ConfigSubstitution extends AbstractConfigValue implements
|
||||
// SubstitutionExpression has to be resolved to values, then if there's more
|
||||
// than one piece everything is stringified and concatenated
|
||||
final private List<Object> pieces;
|
||||
// the length of any prefixes added with relativized()
|
||||
final private int prefixLength;
|
||||
|
||||
// this is just here to avoid breaking serialization;
|
||||
// it is always false at the moment.
|
||||
// this is just here to avoid breaking serialization
|
||||
@SuppressWarnings("unused")
|
||||
@Deprecated
|
||||
final private int prefixLength = 0;
|
||||
|
||||
// this is just here to avoid breaking serialization
|
||||
@SuppressWarnings("unused")
|
||||
@Deprecated
|
||||
final private boolean ignoresFallbacks = false;
|
||||
|
||||
ConfigSubstitution(ConfigOrigin origin, List<Object> pieces) {
|
||||
this(origin, pieces, 0, false);
|
||||
// we chain the ConfigSubstitution back-compat stub to a new value
|
||||
private transient AbstractConfigValue delegate = null;
|
||||
|
||||
private void createDelegate() {
|
||||
if (delegate != null)
|
||||
throw new ConfigException.BugOrBroken("creating delegate twice: " + this);
|
||||
|
||||
List<AbstractConfigValue> values = ConfigConcatenation.valuesFromPieces(origin(), pieces);
|
||||
|
||||
if (values.size() == 1) {
|
||||
delegate = values.get(0);
|
||||
} else {
|
||||
delegate = new ConfigConcatenation(origin(), values);
|
||||
}
|
||||
|
||||
private ConfigSubstitution(ConfigOrigin origin, List<Object> pieces,
|
||||
int prefixLength, boolean ignoresFallbacks) {
|
||||
if (!(delegate instanceof Unmergeable))
|
||||
throw new ConfigException.BugOrBroken("delegate must be Unmergeable: " + this
|
||||
+ " delegate was: " + delegate);
|
||||
}
|
||||
|
||||
AbstractConfigValue delegate() {
|
||||
if (delegate == null)
|
||||
throw new NullPointerException("failed to create delegate " + this);
|
||||
return delegate;
|
||||
}
|
||||
|
||||
ConfigSubstitution(ConfigOrigin origin, List<Object> pieces) {
|
||||
super(origin);
|
||||
this.pieces = pieces;
|
||||
this.prefixLength = prefixLength;
|
||||
|
||||
for (Object p : pieces) {
|
||||
if (p instanceof Path)
|
||||
throw new RuntimeException("broken here");
|
||||
}
|
||||
|
||||
if (ignoresFallbacks)
|
||||
throw new ConfigException.BugOrBroken("ConfigSubstitution may never ignore fallbacks");
|
||||
}
|
||||
|
||||
private ConfigException.NotResolved notResolved() {
|
||||
return new ConfigException.NotResolved(
|
||||
"need to Config#resolve(), see the API docs for Config#resolve(); substitution not resolved: "
|
||||
+ this);
|
||||
createDelegate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigValueType valueType() {
|
||||
throw notResolved();
|
||||
return delegate().valueType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object unwrapped() {
|
||||
throw notResolved();
|
||||
return delegate().unwrapped();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigSubstitution newCopy(boolean ignoresFallbacks, ConfigOrigin newOrigin) {
|
||||
return new ConfigSubstitution(newOrigin, pieces, prefixLength, ignoresFallbacks);
|
||||
protected AbstractConfigValue newCopy(boolean ignoresFallbacks, ConfigOrigin newOrigin) {
|
||||
return delegate().newCopy(ignoresFallbacks, newOrigin);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean ignoresFallbacks() {
|
||||
return ignoresFallbacks;
|
||||
return delegate().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());
|
||||
}
|
||||
|
||||
protected AbstractConfigValue mergedLater(AbstractConfigValue fallback) {
|
||||
if (ignoresFallbacks)
|
||||
throw new ConfigException.BugOrBroken("should not be reached");
|
||||
|
||||
List<AbstractConfigValue> newStack = new ArrayList<AbstractConfigValue>();
|
||||
newStack.add(this);
|
||||
newStack.add(fallback);
|
||||
return new ConfigDelayedMerge(AbstractConfigObject.mergeOrigins(newStack), newStack,
|
||||
fallback.ignoresFallbacks());
|
||||
return delegate().mergedWithTheUnmergeable(fallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractConfigValue mergedWithObject(AbstractConfigObject fallback) {
|
||||
// if we turn out to be an object, and the fallback also does,
|
||||
// then a merge may be required; delay until we resolve.
|
||||
return mergedLater(fallback);
|
||||
return delegate().mergedWithObject(fallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractConfigValue mergedWithNonObject(AbstractConfigValue fallback) {
|
||||
// We may need the fallback for two reasons:
|
||||
// 1. if an optional substitution ends up getting deleted
|
||||
// because it is not defined
|
||||
// 2. if the substitution is self-referential
|
||||
//
|
||||
// we can't easily detect the self-referential case since the cycle
|
||||
// may involve more than one step, so we have to wait and
|
||||
// merge later when resolving.
|
||||
return mergedLater(fallback);
|
||||
return delegate().mergedWithNonObject(fallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ConfigSubstitution> unmergedValues() {
|
||||
return Collections.singleton(this);
|
||||
}
|
||||
|
||||
List<Object> pieces() {
|
||||
return pieces;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static ResolveReplacer undefinedReplacer = new ResolveReplacer() {
|
||||
@Override
|
||||
protected AbstractConfigValue makeReplacement() throws Undefined {
|
||||
throw new Undefined();
|
||||
}
|
||||
};
|
||||
|
||||
private AbstractConfigValue resolveValueConcat(ResolveContext context)
|
||||
throws NotPossibleToResolve {
|
||||
// need to concat everything into a string
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Object p : pieces) {
|
||||
if (p instanceof String) {
|
||||
sb.append((String) p);
|
||||
} else {
|
||||
SubstitutionExpression exp = (SubstitutionExpression) p;
|
||||
|
||||
// to concat into a string we have to do a full resolve,
|
||||
// so unrestrict the context
|
||||
AbstractConfigValue v = context.source().lookupSubst(context.unrestricted(), this,
|
||||
exp, prefixLength);
|
||||
|
||||
if (v == null) {
|
||||
if (exp.optional()) {
|
||||
// append nothing to StringBuilder
|
||||
} else {
|
||||
throw new ConfigException.UnresolvedSubstitution(origin(), exp.toString());
|
||||
}
|
||||
} else {
|
||||
switch (v.valueType()) {
|
||||
case LIST:
|
||||
case OBJECT:
|
||||
// cannot substitute lists and objects into strings
|
||||
throw new ConfigException.WrongType(v.origin(), exp.path().render(),
|
||||
"not a list or object", v.valueType().name());
|
||||
default:
|
||||
sb.append(v.transformToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ConfigString(origin(), sb.toString());
|
||||
}
|
||||
|
||||
private AbstractConfigValue resolveSingleSubst(ResolveContext context)
|
||||
throws NotPossibleToResolve {
|
||||
|
||||
if (!(pieces.get(0) instanceof SubstitutionExpression))
|
||||
throw new ConfigException.BugOrBroken(
|
||||
"ConfigSubstitution should never contain a single String piece");
|
||||
|
||||
SubstitutionExpression exp = (SubstitutionExpression) pieces.get(0);
|
||||
AbstractConfigValue v = context.source().lookupSubst(context, this, exp,
|
||||
prefixLength);
|
||||
|
||||
if (v == null && !exp.optional()) {
|
||||
throw new ConfigException.UnresolvedSubstitution(origin(), exp.toString());
|
||||
}
|
||||
return v;
|
||||
public Collection<? extends AbstractConfigValue> unmergedValues() {
|
||||
return ((Unmergeable) delegate()).unmergedValues();
|
||||
}
|
||||
|
||||
@Override
|
||||
AbstractConfigValue resolveSubstitutions(ResolveContext context)
|
||||
throws NotPossibleToResolve {
|
||||
AbstractConfigValue resolved;
|
||||
if (pieces.size() > 1) {
|
||||
// if you have "foo = ${?foo}bar" then we will
|
||||
// self-referentially look up foo and we need to
|
||||
// get undefined, rather than "bar"
|
||||
context.replace(this, undefinedReplacer);
|
||||
try {
|
||||
resolved = resolveValueConcat(context);
|
||||
} finally {
|
||||
context.unreplace(this);
|
||||
}
|
||||
} else {
|
||||
resolved = resolveSingleSubst(context);
|
||||
}
|
||||
return resolved;
|
||||
AbstractConfigValue resolveSubstitutions(ResolveContext context) throws NotPossibleToResolve {
|
||||
return context.resolve(delegate());
|
||||
}
|
||||
|
||||
@Override
|
||||
ResolveStatus resolveStatus() {
|
||||
return ResolveStatus.UNRESOLVED;
|
||||
return delegate().resolveStatus();
|
||||
}
|
||||
|
||||
// when you graft a substitution into another object,
|
||||
// you have to prefix it with the location in that object
|
||||
// where you grafted it; but save prefixLength so
|
||||
// system property and env variable lookups don't get
|
||||
// broken.
|
||||
@Override
|
||||
ConfigSubstitution relativized(Path prefix) {
|
||||
List<Object> newPieces = new ArrayList<Object>();
|
||||
for (Object p : pieces) {
|
||||
if (p instanceof SubstitutionExpression) {
|
||||
SubstitutionExpression exp = (SubstitutionExpression) p;
|
||||
|
||||
newPieces.add(exp.changePath(exp.path().prepend(prefix)));
|
||||
} else {
|
||||
newPieces.add(p);
|
||||
}
|
||||
}
|
||||
return new ConfigSubstitution(origin(), newPieces, prefixLength
|
||||
+ prefix.length(), ignoresFallbacks);
|
||||
AbstractConfigValue relativized(Path prefix) {
|
||||
return delegate().relativized(prefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canEqual(Object other) {
|
||||
return other instanceof ConfigSubstitution;
|
||||
return other instanceof ConfigSubstitution || other instanceof ConfigReference
|
||||
|| other instanceof ConfigConcatenation;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -254,25 +139,18 @@ final class ConfigSubstitution extends AbstractConfigValue implements
|
||||
return canEqual(other)
|
||||
&& this.pieces.equals(((ConfigSubstitution) other).pieces);
|
||||
} else {
|
||||
return false;
|
||||
return delegate().equals(other);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// note that "origin" is deliberately NOT part of equality
|
||||
return pieces.hashCode();
|
||||
return delegate().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void render(StringBuilder sb, int indent, boolean formatted) {
|
||||
for (Object p : pieces) {
|
||||
if (p instanceof SubstitutionExpression) {
|
||||
sb.append(p.toString());
|
||||
} else {
|
||||
sb.append(ConfigImplUtil.renderJsonString((String) p));
|
||||
}
|
||||
}
|
||||
delegate().render(sb, indent, formatted);
|
||||
}
|
||||
|
||||
// This ridiculous hack is because some JDK versions apparently can't
|
||||
@ -281,7 +159,20 @@ final class ConfigSubstitution extends AbstractConfigValue implements
|
||||
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6446627
|
||||
private Object writeReplace() throws ObjectStreamException {
|
||||
// switch to LinkedList
|
||||
return new ConfigSubstitution(origin(), new java.util.LinkedList<Object>(pieces),
|
||||
prefixLength, ignoresFallbacks);
|
||||
return new ConfigSubstitution(origin(), new java.util.LinkedList<Object>(pieces));
|
||||
}
|
||||
|
||||
// generate the delegate when we deserialize to avoid thread safety
|
||||
// issues later
|
||||
private void readObject(java.io.ObjectInputStream in) throws IOException,
|
||||
ClassNotFoundException {
|
||||
in.defaultReadObject();
|
||||
createDelegate();
|
||||
}
|
||||
|
||||
// this is a little cleaner but just causes compat issues probably
|
||||
// private Object readResolve() throws ObjectStreamException {
|
||||
// replace ourselves on deserialize
|
||||
// return delegate();
|
||||
// }
|
||||
}
|
||||
|
@ -335,10 +335,15 @@ final class Parser {
|
||||
if (minimized.size() == 1 && minimized.get(0) instanceof String) {
|
||||
consolidated = Tokens.newString(firstOrigin,
|
||||
(String) minimized.get(0));
|
||||
} else if (minimized.size() == 1 && minimized.get(0) instanceof SubstitutionExpression) {
|
||||
// a substitution expression ${}
|
||||
consolidated = Tokens.newValue(new ConfigReference(firstOrigin,
|
||||
(SubstitutionExpression) minimized.get(0)));
|
||||
} else {
|
||||
// there's some substitution to do later (post-parse step)
|
||||
consolidated = Tokens.newValue(new ConfigSubstitution(
|
||||
firstOrigin, minimized));
|
||||
// a value concatenation with a substitution expression in it
|
||||
List<AbstractConfigValue> vs = ConfigConcatenation.valuesFromPieces(
|
||||
firstOrigin, minimized);
|
||||
consolidated = Tokens.newValue(new ConfigConcatenation(firstOrigin, vs));
|
||||
}
|
||||
|
||||
putBack(new TokenWithComments(consolidated, firstValueWithComments.comments));
|
||||
|
@ -51,7 +51,7 @@ final class ResolveContext {
|
||||
Collections.singletonList(new LinkedHashSet<MemoKey>())), options, restrictToChild);
|
||||
}
|
||||
|
||||
private void traverse(ConfigSubstitution value, SubstitutionExpression via)
|
||||
private void traverse(ConfigReference value, SubstitutionExpression via)
|
||||
throws SelfReferential {
|
||||
Set<MemoKey> traversed = traversedStack.peekFirst();
|
||||
|
||||
@ -63,7 +63,7 @@ final class ResolveContext {
|
||||
traversed.add(key);
|
||||
}
|
||||
|
||||
private void untraverse(ConfigSubstitution value) {
|
||||
private void untraverse(ConfigReference value) {
|
||||
Set<MemoKey> traversed = traversedStack.peekFirst();
|
||||
|
||||
MemoKey key = new MemoKey(value, restrictToChild);
|
||||
@ -78,7 +78,7 @@ final class ResolveContext {
|
||||
AbstractConfigValue call() throws NotPossibleToResolve;
|
||||
}
|
||||
|
||||
AbstractConfigValue traversing(ConfigSubstitution value, SubstitutionExpression subst,
|
||||
AbstractConfigValue traversing(ConfigReference value, SubstitutionExpression subst,
|
||||
Resolver callable) throws NotPossibleToResolve {
|
||||
try {
|
||||
traverse(value, subst);
|
||||
|
@ -26,7 +26,7 @@ final class ResolveSource {
|
||||
}
|
||||
|
||||
static private AbstractConfigValue findInObject(final AbstractConfigObject obj,
|
||||
final ResolveContext context, ConfigSubstitution traversed,
|
||||
final ResolveContext context, ConfigReference traversed,
|
||||
final SubstitutionExpression subst) throws NotPossibleToResolve {
|
||||
return context.traversing(traversed, subst, new ResolveContext.Resolver() {
|
||||
@Override
|
||||
@ -36,7 +36,7 @@ final class ResolveSource {
|
||||
});
|
||||
}
|
||||
|
||||
AbstractConfigValue lookupSubst(final ResolveContext context, ConfigSubstitution traversed,
|
||||
AbstractConfigValue lookupSubst(final ResolveContext context, ConfigReference 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
|
||||
|
@ -77,10 +77,8 @@ class ConfParserTest extends TestUtils {
|
||||
tree match {
|
||||
case list: ConfigList =>
|
||||
list.get(0) match {
|
||||
case subst: ConfigSubstitution =>
|
||||
subst.pieces().get(0) match {
|
||||
case exp: SubstitutionExpression => exp.path()
|
||||
}
|
||||
case ref: ConfigReference =>
|
||||
ref.expression().path()
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
|
@ -705,7 +705,7 @@ class ConfigSubstitutionTest extends TestUtils {
|
||||
}
|
||||
|
||||
@Test
|
||||
def serializeUnresolvedObject() {
|
||||
def deserializeOldUnresolvedObject() {
|
||||
val expectedSerialization = "" +
|
||||
"aced00057372002b636f6d2e74797065736166652e636f6e6669672e696d706c2e53696d706c6543" +
|
||||
"6f6e6669674f626a65637400000000000000010200035a001069676e6f72657346616c6c6261636b" +
|
||||
@ -784,6 +784,83 @@ class ConfigSubstitutionTest extends TestUtils {
|
||||
"070000000c0000000c7071007e000c71007e000f7000000000007371007e001a7704000000017371" +
|
||||
"007e001c007371007e001f740008707472546f4172727078787878"
|
||||
|
||||
checkSerializableOldFormat(expectedSerialization, substComplexObject)
|
||||
}
|
||||
|
||||
@Test
|
||||
def serializeUnresolvedObject() {
|
||||
val expectedSerialization = "" +
|
||||
"aced00057372002b636f6d2e74797065736166652e636f6e6669672e696d706c2e53696d706c6543" +
|
||||
"6f6e6669674f626a65637400000000000000010200035a001069676e6f72657346616c6c6261636b" +
|
||||
"735a00087265736f6c7665644c000576616c756574000f4c6a6176612f7574696c2f4d61703b7872" +
|
||||
"002d636f6d2e74797065736166652e636f6e6669672e696d706c2e4162737472616374436f6e6669" +
|
||||
"674f626a65637400000000000000010200014c0006636f6e6669677400274c636f6d2f7479706573" +
|
||||
"6166652f636f6e6669672f696d706c2f53696d706c65436f6e6669673b7872002c636f6d2e747970" +
|
||||
"65736166652e636f6e6669672e696d706c2e4162737472616374436f6e66696756616c7565000000" +
|
||||
"00000000010200014c00066f726967696e74002d4c636f6d2f74797065736166652f636f6e666967" +
|
||||
"2f696d706c2f53696d706c65436f6e6669674f726967696e3b78707372002b636f6d2e7479706573" +
|
||||
"6166652e636f6e6669672e696d706c2e53696d706c65436f6e6669674f726967696e000000000000" +
|
||||
"000102000649000d656e644c696e654e756d62657249000a6c696e654e756d6265724c000e636f6d" +
|
||||
"6d656e74734f724e756c6c7400104c6a6176612f7574696c2f4c6973743b4c000b64657363726970" +
|
||||
"74696f6e7400124c6a6176612f6c616e672f537472696e673b4c000a6f726967696e547970657400" +
|
||||
"254c636f6d2f74797065736166652f636f6e6669672f696d706c2f4f726967696e547970653b4c00" +
|
||||
"0975726c4f724e756c6c71007e0009787000000002000000027074000b7465737420737472696e67" +
|
||||
"7e720023636f6d2e74797065736166652e636f6e6669672e696d706c2e4f726967696e5479706500" +
|
||||
"000000000000001200007872000e6a6176612e6c616e672e456e756d000000000000000012000078" +
|
||||
"7074000747454e455249437073720025636f6d2e74797065736166652e636f6e6669672e696d706c" +
|
||||
"2e53696d706c65436f6e66696700000000000000010200014c00066f626a65637474002f4c636f6d" +
|
||||
"2f74797065736166652f636f6e6669672f696d706c2f4162737472616374436f6e6669674f626a65" +
|
||||
"63743b787071007e00060000737200116a6176612e7574696c2e486173684d61700507dac1c31660" +
|
||||
"d103000246000a6c6f6164466163746f724900097468726573686f6c6478703f4000000000000c77" +
|
||||
"08000000100000000a7400046f626a4573720028636f6d2e74797065736166652e636f6e6669672e" +
|
||||
"696d706c2e436f6e6669675265666572656e6365000000000000000102000249000c707265666978" +
|
||||
"4c656e6774684c0004657870727400314c636f6d2f74797065736166652f636f6e6669672f696d70" +
|
||||
"6c2f537562737469747574696f6e45787072657373696f6e3b7871007e00047371007e0007000000" +
|
||||
"08000000087071007e000c71007e000f70000000007372002f636f6d2e74797065736166652e636f" +
|
||||
"6e6669672e696d706c2e537562737469747574696f6e45787072657373696f6e0000000000000001" +
|
||||
"0200025a00086f7074696f6e616c4c00047061746874001f4c636f6d2f74797065736166652f636f" +
|
||||
"6e6669672f696d706c2f506174683b7870007372001d636f6d2e74797065736166652e636f6e6669" +
|
||||
"672e696d706c2e5061746800000000000000010200024c0005666972737471007e00094c00097265" +
|
||||
"6d61696e64657271007e001c7870740001617371007e001e740001627371007e001e740001657074" +
|
||||
"0007666f6f2e62617273720022636f6d2e74797065736166652e636f6e6669672e696d706c2e436f" +
|
||||
"6e666967496e74000000000000000102000149000576616c756578720025636f6d2e747970657361" +
|
||||
"66652e636f6e6669672e696d706c2e436f6e6669674e756d62657200000000000000010200014c00" +
|
||||
"0c6f726967696e616c5465787471007e00097871007e00047371007e000700000009000000097071" +
|
||||
"007e000c71007e000f707400023337000000257400046f626a427371007e00177371007e00070000" +
|
||||
"0007000000077071007e000c71007e000f70000000007371007e001b007371007e001e7400016173" +
|
||||
"71007e001e740001627074000361727273720029636f6d2e74797065736166652e636f6e6669672e" +
|
||||
"696d706c2e53696d706c65436f6e6669674c69737400000000000000010200025a00087265736f6c" +
|
||||
"7665644c000576616c756571007e00087871007e00047371007e00070000000a0000000a7071007e" +
|
||||
"000c71007e000f7000737200146a6176612e7574696c2e4c696e6b65644c6973740c29535d4a6088" +
|
||||
"2203000078707704000000067371007e00177371007e00070000000a0000000a7071007e000c7100" +
|
||||
"7e000f70000000007371007e001b007371007e001e740003666f6f707371007e001771007e003a00" +
|
||||
"0000007371007e001b007371007e001e740001617371007e001e740001627371007e001e74000163" +
|
||||
"707371007e001771007e003a000000007371007e001b007371007e001e740007666f6f2e62617270" +
|
||||
"7371007e001771007e003a000000007371007e001b007371007e001e7400046f626a427371007e00" +
|
||||
"1e74000164707371007e001771007e003a000000007371007e001b007371007e001e7400046f626a" +
|
||||
"417371007e001e740001627371007e001e740001657371007e001e74000166707371007e00177100" +
|
||||
"7e003a000000007371007e001b007371007e001e7400046f626a457371007e001e74000166707874" +
|
||||
"00046f626a417371007e00177371007e000700000006000000067071007e000c71007e000f700000" +
|
||||
"00007371007e001b007371007e001e7400016170740001617371007e00007371007e000700000005" +
|
||||
"000000057071007e000c71007e000f707371007e001171007e006700007371007e00143f40000000" +
|
||||
"00000c77080000001000000001740001627371007e00007371007e00070000000500000005707100" +
|
||||
"7e000c71007e000f707371007e001171007e006c00007371007e00143f4000000000000c77080000" +
|
||||
"001000000003740001647371007e00177371007e000700000005000000057071007e000c71007e00" +
|
||||
"0f70000000007371007e001b007371007e001e740003666f6f70740001657371007e00007371007e" +
|
||||
"000700000005000000057071007e000c71007e000f707371007e001171007e007700007371007e00" +
|
||||
"143f4000000000000c77080000001000000001740001667371007e001771007e0072000000007371" +
|
||||
"007e001b007371007e001e740003666f6f7078740001637371007e002671007e0072740002353700" +
|
||||
"0000397878740003666f6f7371007e00177371007e000700000003000000037071007e000c71007e" +
|
||||
"000f70000000007371007e001b007371007e001e74000362617270740008707472546f4172727371" +
|
||||
"007e00177371007e00070000000b0000000b7071007e000c71007e000f70000000007371007e001b" +
|
||||
"007371007e001e740003617272707400036261727371007e00177371007e00070000000400000004" +
|
||||
"7071007e000c71007e000f70000000007371007e001b007371007e001e740001617371007e001e74" +
|
||||
"0001627371007e001e7400016370740001787371007e00007371007e00070000000c0000000c7071" +
|
||||
"007e000c71007e000f707371007e001171007e009a00007371007e00143f4000000000000c770800" +
|
||||
"00001000000001740001797371007e00007371007e00070000000c0000000c7071007e000c71007e" +
|
||||
"000f707371007e001171007e009f00007371007e00143f4000000000000c77080000001000000001" +
|
||||
"74000d707472546f507472546f4172727371007e00177371007e00070000000c0000000c7071007e" +
|
||||
"000c71007e000f70000000007371007e001b007371007e001e740008707472546f41727270787878"
|
||||
checkSerializable(expectedSerialization, substComplexObject)
|
||||
}
|
||||
|
||||
|
@ -237,6 +237,9 @@ class ConfigValueTest extends TestUtils {
|
||||
val sameAsA = subst("foo")
|
||||
val b = subst("bar")
|
||||
|
||||
assertTrue("wrong type " + a, a.isInstanceOf[ConfigSubstitution])
|
||||
assertTrue("wrong type " + b, b.isInstanceOf[ConfigSubstitution])
|
||||
|
||||
checkEqualObjects(a, a)
|
||||
checkEqualObjects(a, sameAsA)
|
||||
checkNotEqualObjects(a, b)
|
||||
@ -271,6 +274,122 @@ class ConfigValueTest extends TestUtils {
|
||||
val b = checkSerializable(expectedSerialization, a)
|
||||
}
|
||||
|
||||
@Test
|
||||
def configReferenceEquality() {
|
||||
val a = subst("foo").delegate()
|
||||
val sameAsA = subst("foo").delegate()
|
||||
val b = subst("bar").delegate()
|
||||
val c = subst("foo", optional = true).delegate()
|
||||
|
||||
assertTrue("wrong type " + a, a.isInstanceOf[ConfigReference])
|
||||
assertTrue("wrong type " + b, b.isInstanceOf[ConfigReference])
|
||||
assertTrue("wrong type " + c, c.isInstanceOf[ConfigReference])
|
||||
|
||||
checkEqualObjects(a, a)
|
||||
checkEqualObjects(a, sameAsA)
|
||||
checkNotEqualObjects(a, b)
|
||||
checkNotEqualObjects(a, c)
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
def configReferenceSerializable() {
|
||||
val expectedSerialization = "" +
|
||||
"aced000573720028636f6d2e74797065736166652e636f6e6669672e696d706c2e436f6e66696752" +
|
||||
"65666572656e6365000000000000000102000249000c7072656669784c656e6774684c0004657870" +
|
||||
"727400314c636f6d2f74797065736166652f636f6e6669672f696d706c2f53756273746974757469" +
|
||||
"6f6e45787072657373696f6e3b7872002c636f6d2e74797065736166652e636f6e6669672e696d70" +
|
||||
"6c2e4162737472616374436f6e66696756616c756500000000000000010200014c00066f72696769" +
|
||||
"6e74002d4c636f6d2f74797065736166652f636f6e6669672f696d706c2f53696d706c65436f6e66" +
|
||||
"69674f726967696e3b78707372002b636f6d2e74797065736166652e636f6e6669672e696d706c2e" +
|
||||
"53696d706c65436f6e6669674f726967696e000000000000000102000649000d656e644c696e654e" +
|
||||
"756d62657249000a6c696e654e756d6265724c000e636f6d6d656e74734f724e756c6c7400104c6a" +
|
||||
"6176612f7574696c2f4c6973743b4c000b6465736372697074696f6e7400124c6a6176612f6c616e" +
|
||||
"672f537472696e673b4c000a6f726967696e547970657400254c636f6d2f74797065736166652f63" +
|
||||
"6f6e6669672f696d706c2f4f726967696e547970653b4c000975726c4f724e756c6c71007e000778" +
|
||||
"70ffffffffffffffff7074000b66616b65206f726967696e7e720023636f6d2e7479706573616665" +
|
||||
"2e636f6e6669672e696d706c2e4f726967696e5479706500000000000000001200007872000e6a61" +
|
||||
"76612e6c616e672e456e756d0000000000000000120000787074000747454e455249437000000000" +
|
||||
"7372002f636f6d2e74797065736166652e636f6e6669672e696d706c2e537562737469747574696f" +
|
||||
"6e45787072657373696f6e00000000000000010200025a00086f7074696f6e616c4c000470617468" +
|
||||
"74001f4c636f6d2f74797065736166652f636f6e6669672f696d706c2f506174683b787000737200" +
|
||||
"1d636f6d2e74797065736166652e636f6e6669672e696d706c2e5061746800000000000000010200" +
|
||||
"024c0005666972737471007e00074c000972656d61696e64657271007e00107870740003666f6f70"
|
||||
|
||||
val a = subst("foo").delegate()
|
||||
assertTrue("wrong type " + a, a.isInstanceOf[ConfigReference])
|
||||
val b = checkSerializable(expectedSerialization, a)
|
||||
assertTrue("wrong type " + b, b.isInstanceOf[ConfigReference])
|
||||
}
|
||||
|
||||
@Test
|
||||
def configConcatenationEquality() {
|
||||
val a = substInString("foo").delegate()
|
||||
val sameAsA = substInString("foo").delegate()
|
||||
val b = substInString("bar").delegate()
|
||||
val c = substInString("foo", optional = true).delegate()
|
||||
|
||||
assertTrue("wrong type " + a, a.isInstanceOf[ConfigConcatenation])
|
||||
assertTrue("wrong type " + b, b.isInstanceOf[ConfigConcatenation])
|
||||
assertTrue("wrong type " + c, c.isInstanceOf[ConfigConcatenation])
|
||||
|
||||
checkEqualObjects(a, a)
|
||||
checkEqualObjects(a, sameAsA)
|
||||
checkNotEqualObjects(a, b)
|
||||
checkNotEqualObjects(a, c)
|
||||
}
|
||||
|
||||
@Test
|
||||
def configConcatenationSerializable() {
|
||||
val expectedSerialization = "" +
|
||||
"aced00057372002c636f6d2e74797065736166652e636f6e6669672e696d706c2e436f6e66696743" +
|
||||
"6f6e636174656e6174696f6e00000000000000010200014c00067069656365737400104c6a617661" +
|
||||
"2f7574696c2f4c6973743b7872002c636f6d2e74797065736166652e636f6e6669672e696d706c2e" +
|
||||
"4162737472616374436f6e66696756616c756500000000000000010200014c00066f726967696e74" +
|
||||
"002d4c636f6d2f74797065736166652f636f6e6669672f696d706c2f53696d706c65436f6e666967" +
|
||||
"4f726967696e3b78707372002b636f6d2e74797065736166652e636f6e6669672e696d706c2e5369" +
|
||||
"6d706c65436f6e6669674f726967696e000000000000000102000649000d656e644c696e654e756d" +
|
||||
"62657249000a6c696e654e756d6265724c000e636f6d6d656e74734f724e756c6c71007e00014c00" +
|
||||
"0b6465736372697074696f6e7400124c6a6176612f6c616e672f537472696e673b4c000a6f726967" +
|
||||
"696e547970657400254c636f6d2f74797065736166652f636f6e6669672f696d706c2f4f72696769" +
|
||||
"6e547970653b4c000975726c4f724e756c6c71007e00067870ffffffffffffffff7074000b66616b" +
|
||||
"65206f726967696e7e720023636f6d2e74797065736166652e636f6e6669672e696d706c2e4f7269" +
|
||||
"67696e5479706500000000000000001200007872000e6a6176612e6c616e672e456e756d00000000" +
|
||||
"00000000120000787074000747454e4552494370737200146a6176612e7574696c2e4c696e6b6564" +
|
||||
"4c6973740c29535d4a608822030000787077040000000373720025636f6d2e74797065736166652e" +
|
||||
"636f6e6669672e696d706c2e436f6e666967537472696e6700000000000000010200014c00057661" +
|
||||
"6c756571007e00067871007e000271007e000874000673746172743c73720028636f6d2e74797065" +
|
||||
"736166652e636f6e6669672e696d706c2e436f6e6669675265666572656e63650000000000000001" +
|
||||
"02000249000c7072656669784c656e6774684c0004657870727400314c636f6d2f74797065736166" +
|
||||
"652f636f6e6669672f696d706c2f537562737469747574696f6e45787072657373696f6e3b787100" +
|
||||
"7e000271007e0008000000007372002f636f6d2e74797065736166652e636f6e6669672e696d706c" +
|
||||
"2e537562737469747574696f6e45787072657373696f6e00000000000000010200025a00086f7074" +
|
||||
"696f6e616c4c00047061746874001f4c636f6d2f74797065736166652f636f6e6669672f696d706c" +
|
||||
"2f506174683b7870007372001d636f6d2e74797065736166652e636f6e6669672e696d706c2e5061" +
|
||||
"746800000000000000010200024c0005666972737471007e00064c000972656d61696e6465727100" +
|
||||
"7e00177870740003666f6f707371007e001071007e00087400043e656e6478"
|
||||
|
||||
val a = substInString("foo").delegate()
|
||||
assertTrue("wrong type " + a, a.isInstanceOf[ConfigConcatenation])
|
||||
val b = checkSerializable(expectedSerialization, a)
|
||||
assertTrue("wrong type " + b, b.isInstanceOf[ConfigConcatenation])
|
||||
}
|
||||
|
||||
@Test
|
||||
def configSubstitutionEqualsItsDelegates() {
|
||||
val a = subst("foo")
|
||||
assertTrue("wrong type " + a, a.isInstanceOf[ConfigSubstitution])
|
||||
val aD = a.delegate()
|
||||
assertTrue("wrong type " + aD, aD.isInstanceOf[ConfigReference])
|
||||
val b = substInString("bar")
|
||||
assertTrue("wrong type " + b, b.isInstanceOf[ConfigSubstitution])
|
||||
val bD = b.delegate()
|
||||
assertTrue("wrong type " + bD, bD.isInstanceOf[ConfigConcatenation])
|
||||
|
||||
checkEqualObjects(a, aD)
|
||||
checkEqualObjects(b, bD)
|
||||
}
|
||||
|
||||
@Test
|
||||
def configDelayedMergeEquality() {
|
||||
val s1 = subst("foo")
|
||||
|
@ -103,7 +103,7 @@ abstract trait TestUtils {
|
||||
copy
|
||||
}
|
||||
|
||||
protected def checkSerializationCompat[T: Manifest](expectedHex: String, o: T): Unit = {
|
||||
protected def checkSerializationCompat[T: Manifest](expectedHex: String, o: T, changedOK: Boolean = false): Unit = {
|
||||
// be sure we can still deserialize the old one
|
||||
val inStream = new ByteArrayInputStream(Hex.decodeHex(expectedHex.toCharArray()))
|
||||
val inObjectStream = new ObjectInputStream(inStream)
|
||||
@ -146,6 +146,7 @@ abstract trait TestUtils {
|
||||
o, deserialized)
|
||||
assertFalse(failure.isDefined) // should have thrown if we had a failure
|
||||
|
||||
if (!changedOK)
|
||||
assertEquals(o.getClass.getSimpleName + " serialization has changed (though we still deserialized the old serialization)",
|
||||
expectedHex, hex)
|
||||
} catch {
|
||||
@ -161,6 +162,12 @@ abstract trait TestUtils {
|
||||
t
|
||||
}
|
||||
|
||||
protected def checkSerializableOldFormat[T: Manifest](expectedHex: String, o: T): T = {
|
||||
val t = checkSerializable(o)
|
||||
checkSerializationCompat(expectedHex, o, changedOK = true)
|
||||
t
|
||||
}
|
||||
|
||||
protected def checkSerializable[T: Manifest](o: T): T = {
|
||||
checkEqualObjects(o, o)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user