package org.deephacks.confit.internal.core.property.typesafe.impl;
import java.util.Collection;
import java.util.Collections;
import org.deephacks.confit.internal.core.property.typesafe.ConfigRenderOptions;
import org.deephacks.confit.internal.core.property.typesafe.ConfigValueType;
import org.deephacks.confit.internal.core.property.typesafe.ConfigException;
import org.deephacks.confit.internal.core.property.typesafe.ConfigOrigin;
/**
* 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 {
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(ConfigOrigin newOrigin) {
return new ConfigReference(newOrigin, expr, prefixLength);
}
@Override
protected boolean ignoresFallbacks() {
return false;
}
@Override
public Collection<ConfigReference> unmergedValues() {
return Collections.singleton(this);
}
// ConfigReference should be a firewall against NotPossibleToResolve going
// further up the stack; it should convert everything to ConfigException.
// This way it's impossible for NotPossibleToResolve to "escape" since
// any failure to resolve has to start with a ConfigReference.
@Override
AbstractConfigValue resolveSubstitutions(ResolveContext context) {
context.source().replace(this, ResolveReplacer.cycleResolveReplacer);
try {
AbstractConfigValue v;
try {
v = context.source().lookupSubst(context, expr, prefixLength);
} catch (NotPossibleToResolve e) {
if (expr.optional())
v = null;
else
throw new ConfigException.UnresolvedSubstitution(origin(), expr
+ " was part of a cycle of substitutions involving " + e.traceString(),
e);
}
if (v == null && !expr.optional()) {
throw new ConfigException.UnresolvedSubstitution(origin(), expr.toString());
}
return v;
} finally {
context.source().unreplace(this);
}
}
@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 lookup
// broken.
@Override
ConfigReference relativized(Path prefix) {
SubstitutionExpression newExpr = expr.changePath(expr.path().prepend(prefix));
return new ConfigReference(origin(), newExpr, prefixLength + prefix.length());
}
@Override
protected boolean canEqual(Object other) {
return other instanceof ConfigReference;
}
@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 {
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, ConfigRenderOptions options) {
sb.append(expr.toString());
}
SubstitutionExpression expression() {
return expr;
}
}