fix error message for type errors mid-path

If you looked up "a.b.c" and b is not an object, it said
"a.b.c is a FOO and not an OBJECT"
now it says
"a.b is a FOO and not an OBJECT"
This commit is contained in:
Havoc Pennington 2012-04-05 14:02:37 -04:00
parent 683e72cbbe
commit 06ed4d24e1
4 changed files with 47 additions and 31 deletions

View File

@ -57,7 +57,7 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements Confi
* @param key * @param key
* @return the unmodified raw value or null * @return the unmodified raw value or null
*/ */
protected final AbstractConfigValue peekAssumingResolved(String key, String originalPath) { protected final AbstractConfigValue peekAssumingResolved(String key, Path originalPath) {
try { try {
return attemptPeekWithPartialResolve(key); return attemptPeekWithPartialResolve(key);
} catch (ConfigException.NotResolved e) { } catch (ConfigException.NotResolved e) {
@ -138,7 +138,7 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements Confi
} }
} }
} catch (ConfigException.NotResolved e) { } catch (ConfigException.NotResolved e) {
throw ConfigImpl.improveNotResolved(path.render(), e); throw ConfigImpl.improveNotResolved(path, e);
} }
} }

View File

@ -526,9 +526,10 @@ public class ConfigImpl {
// toplevel error message. the "original" exception may however have extra // toplevel error message. the "original" exception may however have extra
// detail about what happened. call this if you have a better "what" than // detail about what happened. call this if you have a better "what" than
// further down on the stack. // further down on the stack.
static ConfigException.NotResolved improveNotResolved(String what, static ConfigException.NotResolved improveNotResolved(Path what,
ConfigException.NotResolved original) { ConfigException.NotResolved original) {
String newMessage = what + " has not been resolved, you need to call Config#resolve()," String newMessage = what.render()
+ " has not been resolved, you need to call Config#resolve(),"
+ " see API docs for Config#resolve()"; + " see API docs for Config#resolve()";
if (newMessage.equals(original.getMessage())) if (newMessage.equals(original.getMessage()))
return original; return original;

View File

@ -123,6 +123,23 @@ final class Path implements Serializable {
return p; return p;
} }
Path subPath(int firstIndex, int lastIndex) {
if (lastIndex < firstIndex)
throw new ConfigException.BugOrBroken("bad call to subPath");
Path from = subPath(firstIndex);
PathBuilder pb = new PathBuilder();
int count = lastIndex - firstIndex;
while (count > 0) {
count -= 1;
pb.appendKey(from.first());
from = from.remainder();
if (from == null)
throw new ConfigException.BugOrBroken("subPath lastIndex out of range " + lastIndex);
}
return pb.result();
}
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if (other instanceof Path) { if (other instanceof Path) {

View File

@ -72,7 +72,7 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
try { try {
peeked = object.peekPath(path); peeked = object.peekPath(path);
} catch (ConfigException.NotResolved e) { } catch (ConfigException.NotResolved e) {
throw ConfigImpl.improveNotResolved(pathExpression, e); throw ConfigImpl.improveNotResolved(path, e);
} }
return peeked != null && peeked.valueType() != ConfigValueType.NULL; return peeked != null && peeked.valueType() != ConfigValueType.NULL;
} }
@ -107,33 +107,27 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
return entries; return entries;
} }
static private AbstractConfigValue find(AbstractConfigObject self,
String pathExpression, ConfigValueType expected, String originalPath) {
Path path = Path.newPath(pathExpression);
return find(self, path, expected, originalPath);
}
static private AbstractConfigValue findKey(AbstractConfigObject self, String key, static private AbstractConfigValue findKey(AbstractConfigObject self, String key,
ConfigValueType expected, String originalPath) { ConfigValueType expected, Path originalPath) {
AbstractConfigValue v = self.peekAssumingResolved(key, originalPath); AbstractConfigValue v = self.peekAssumingResolved(key, originalPath);
if (v == null) if (v == null)
throw new ConfigException.Missing(originalPath); throw new ConfigException.Missing(originalPath.render());
if (expected != null) if (expected != null)
v = DefaultTransformer.transform(v, expected); v = DefaultTransformer.transform(v, expected);
if (v.valueType() == ConfigValueType.NULL) if (v.valueType() == ConfigValueType.NULL)
throw new ConfigException.Null(v.origin(), originalPath, throw new ConfigException.Null(v.origin(), originalPath.render(),
expected != null ? expected.name() : null); expected != null ? expected.name() : null);
else if (expected != null && v.valueType() != expected) else if (expected != null && v.valueType() != expected)
throw new ConfigException.WrongType(v.origin(), originalPath, expected.name(), v throw new ConfigException.WrongType(v.origin(), originalPath.render(), expected.name(),
.valueType().name()); v.valueType().name());
else else
return v; return v;
} }
static private AbstractConfigValue find(AbstractConfigObject self, Path path, static private AbstractConfigValue find(AbstractConfigObject self, Path path,
ConfigValueType expected, String originalPath) { ConfigValueType expected, Path originalPath) {
try { try {
String key = path.first(); String key = path.first();
Path next = path.remainder(); Path next = path.remainder();
@ -141,33 +135,38 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
return findKey(self, key, expected, originalPath); return findKey(self, key, expected, originalPath);
} else { } else {
AbstractConfigObject o = (AbstractConfigObject) findKey(self, key, AbstractConfigObject o = (AbstractConfigObject) findKey(self, key,
ConfigValueType.OBJECT, originalPath); ConfigValueType.OBJECT,
originalPath.subPath(0, originalPath.length() - next.length()));
assert (o != null); // missing was supposed to throw assert (o != null); // missing was supposed to throw
return find(o, next, expected, originalPath); return find(o, next, expected, originalPath);
} }
} catch (ConfigException.NotResolved e) { } catch (ConfigException.NotResolved e) {
throw ConfigImpl.improveNotResolved(path.render(), e); throw ConfigImpl.improveNotResolved(path, e);
} }
} }
AbstractConfigValue find(String pathExpression, ConfigValueType expected, AbstractConfigValue find(Path pathExpression, ConfigValueType expected, Path originalPath) {
String originalPath) {
return find(object, pathExpression, expected, originalPath); return find(object, pathExpression, expected, originalPath);
} }
AbstractConfigValue find(String pathExpression, ConfigValueType expected) {
Path path = Path.newPath(pathExpression);
return find(path, expected, path);
}
@Override @Override
public AbstractConfigValue getValue(String path) { public AbstractConfigValue getValue(String path) {
return find(path, null, path); return find(path, null);
} }
@Override @Override
public boolean getBoolean(String path) { public boolean getBoolean(String path) {
ConfigValue v = find(path, ConfigValueType.BOOLEAN, path); ConfigValue v = find(path, ConfigValueType.BOOLEAN);
return (Boolean) v.unwrapped(); return (Boolean) v.unwrapped();
} }
private ConfigNumber getConfigNumber(String path) { private ConfigNumber getConfigNumber(String path) {
ConfigValue v = find(path, ConfigValueType.NUMBER, path); ConfigValue v = find(path, ConfigValueType.NUMBER);
return (ConfigNumber) v; return (ConfigNumber) v;
} }
@ -194,20 +193,19 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
@Override @Override
public String getString(String path) { public String getString(String path) {
ConfigValue v = find(path, ConfigValueType.STRING, path); ConfigValue v = find(path, ConfigValueType.STRING);
return (String) v.unwrapped(); return (String) v.unwrapped();
} }
@Override @Override
public ConfigList getList(String path) { public ConfigList getList(String path) {
AbstractConfigValue v = find(path, ConfigValueType.LIST, path); AbstractConfigValue v = find(path, ConfigValueType.LIST);
return (ConfigList) v; return (ConfigList) v;
} }
@Override @Override
public AbstractConfigObject getObject(String path) { public AbstractConfigObject getObject(String path) {
AbstractConfigObject obj = (AbstractConfigObject) find(path, AbstractConfigObject obj = (AbstractConfigObject) find(path, ConfigValueType.OBJECT);
ConfigValueType.OBJECT, path);
return obj; return obj;
} }
@ -218,7 +216,7 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
@Override @Override
public Object getAnyRef(String path) { public Object getAnyRef(String path) {
ConfigValue v = find(path, null, path); ConfigValue v = find(path, null);
return v.unwrapped(); return v.unwrapped();
} }
@ -228,7 +226,7 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
try { try {
size = getLong(path); size = getLong(path);
} catch (ConfigException.WrongType e) { } catch (ConfigException.WrongType e) {
ConfigValue v = find(path, ConfigValueType.STRING, path); ConfigValue v = find(path, ConfigValueType.STRING);
size = parseBytes((String) v.unwrapped(), size = parseBytes((String) v.unwrapped(),
v.origin(), path); v.origin(), path);
} }
@ -248,7 +246,7 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
try { try {
ns = TimeUnit.MILLISECONDS.toNanos(getLong(path)); ns = TimeUnit.MILLISECONDS.toNanos(getLong(path));
} catch (ConfigException.WrongType e) { } catch (ConfigException.WrongType e) {
ConfigValue v = find(path, ConfigValueType.STRING, path); ConfigValue v = find(path, ConfigValueType.STRING);
ns = parseDuration((String) v.unwrapped(), v.origin(), path); ns = parseDuration((String) v.unwrapped(), v.origin(), path);
} }
return ns; return ns;