String stype = (String) typeValue.unwrapped();
try {
Class<T> normalType = (Class<T>) pluginMap.getClass(stype);
ConfigObject aliasDefaults = pluginMap.aliasDefaults(stype);
ConfigObject fieldValues = configObject.withoutKey(classField).withFallback(aliasDefaults);
CodableClassInfo normalInfo = getOrCreateClassInfo(normalType);
return createAndPopulate(normalInfo, normalType, fieldValues);
} catch (ClassNotFoundException e) {
String helpMessage = Plugins.classNameSuggestions(pluginRegistry, pluginMap, stype);
throw new ConfigException.UnresolvedSubstitution(configObject.origin(), helpMessage, e);
}
}
/** if no chance of instantiating current type, try to get a new type from various special syntax/ settings */
if ((type == null) || Modifier.isAbstract(type.getModifiers()) || Modifier.isInterface(type.getModifiers())) {
/** "type-value : {...}" syntax; ie. if there is only one key, see if it would be a valid type */
if (configObject.size() == 1) {
String sugarType = configObject.keySet().iterator().next();
try {
Class<T> singleKeyType = (Class<T>) pluginMap.getClass(sugarType);
ConfigObject aliasDefaults = pluginMap.aliasDefaults(sugarType);
ConfigValue configValue = configObject.get(sugarType);
// if value is not an object, try supporting _primary syntax to derive one
if (configValue.valueType() != ConfigValueType.OBJECT) {
if (aliasDefaults.get("_primary") != null) {
configValue = configValue.atPath((String) aliasDefaults.get("_primary").unwrapped()).root();
} else {
throw new ConfigException.WrongType(configValue.origin(), sugarType,
"OBJECT", configValue.valueType().toString());
}
}
ConfigObject fieldValues = ((ConfigObject) configValue).withFallback(aliasDefaults);
CodableClassInfo singleKeyInfo = getOrCreateClassInfo(singleKeyType);
return createAndPopulate(singleKeyInfo, singleKeyType, fieldValues);
} catch (ClassNotFoundException ignored) {
// expected when the single key is not a valid alias or class. could avoid exception if we dropped
// support for single-keys that are just classes (ie. anonymous aliases), but we'll leave it in
// until we have some, more concrete, reason to remove it.
}
}
/** inlined types syntax ie "{ type-value: some-value, some-field: some-other-value, ...}".
* Opt-in is on a per alias basis, and the target type must be unambiguous amongst aliases
* that have opted in. The recognized alias label is then replaced with the _primary field. */
String matched = null;
for (String alias : pluginMap.inlinedAliases()) {
if (configObject.get(alias) != null) {
if (matched != null) {
String message = String.format(
"no type specified, more than one key, and both %s and %s match for inlined types.",
matched, alias);
throw new ConfigException.Parse(configObject.origin(), message);
}
matched = alias;
}
}
if (matched != null) {
Class<T> inlinedType = (Class<T>) pluginMap.getClassIfConfigured(matched);
assert inlinedType != null : "matched is always a key from the pluginMap's inlinedAliases set";
CodableClassInfo inlinedInfo = getOrCreateClassInfo(inlinedType);
ConfigObject aliasDefaults = pluginMap.aliasDefaults(matched);
ConfigValue configValue = configObject.get(matched);
String primaryField = (String) aliasDefaults.get("_primary").unwrapped();
ConfigObject fieldValues = configObject.toConfig().withValue(primaryField, configValue).root()
.withoutKey(matched)
.withFallback(aliasDefaults);
return createAndPopulate(inlinedInfo, inlinedType, fieldValues);
}
/** lastly, check for a _default type. */
Class<T> defaultType = (Class<T>) pluginMap.defaultSugar();
if (defaultType != null) {
CodableClassInfo defaultInfo = getOrCreateClassInfo(defaultType);
ConfigObject aliasDefaults = pluginMap.aliasDefaults("_default");
ConfigObject fieldValues = configObject.withFallback(aliasDefaults);
return createAndPopulate(defaultInfo, defaultType, fieldValues);
}