in core/src/main/java/org/apache/brooklyn/core/objs/BrooklynDynamicType.java [161:270]
protected static void buildConfigKeys(Class<? extends BrooklynObject> clazz, AbstractBrooklynObject optionalInstance,
Map<String, FieldAndValue<ConfigKey<?>>> configKeys) {
// TODO remove or document overlap with ConfigUtilsInternal.findConfigKeys
ListMultimap<String,FieldAndValue<ConfigKey<?>>> configKeysAll =
ArrayListMultimap.<String, FieldAndValue<ConfigKey<?>>>create();
for (Field f : FlagUtils.getAllFields(clazz)) {
boolean isConfigKey = ConfigKey.class.isAssignableFrom(f.getType());
if (!isConfigKey) {
if (!HasConfigKey.class.isAssignableFrom(f.getType())) {
// neither ConfigKey nor HasConfigKey
continue;
}
}
if (!Modifier.isStatic(f.getModifiers())) {
// require it to be static or we have an instance
LOG.warn("Discouraged use of non-static config key "+f+" defined in " + (optionalInstance!=null ? optionalInstance : clazz));
if (optionalInstance==null) continue;
}
try {
Object v = f.get(optionalInstance);
if (v == null) {
LOG.warn("no value defined for config key field (skipping): "+f);
} else {
ConfigKey<?> k = isConfigKey ? (ConfigKey<?>) v : ((HasConfigKey<?>) v).getConfigKey();
configKeysAll.put(k.getName(), new FieldAndValue<ConfigKey<?>>(f, k));
}
} catch (IllegalAccessException e) {
LOG.warn("cannot access config key (skipping): "+f);
}
}
Collection<Class<?>> interfaces = MutableSet.copyOf(Arrays.asList(clazz.getInterfaces()));
LinkedHashSet<String> keys = new LinkedHashSet<String>(configKeysAll.keys());
for (String kn: keys) {
List<FieldAndValue<ConfigKey<?>>> kk = Lists.newArrayList(configKeysAll.get(kn));
// remove anything which isn't reinheritable, or which is overridden
for (FieldAndValue<ConfigKey<?>> k: kk) {
ConfigKey<?> key = value(k);
if (!ConfigInheritances.isKeyReinheritable(key, InheritanceContext.TYPE_DEFINITION)) {
if (k.field.getDeclaringClass().equals(clazz)) {
// proceed if key is declared on this class
} else if (interfaces.contains(k.field.getDeclaringClass())) {
// proceed if key is declared on an exlicitly declared interface
} else {
// key not re-inheritable from parent so not visible here
configKeysAll.remove(k.value.getName(), k);
}
}
if (key instanceof BasicConfigKeyOverwriting) {
ConfigKey<?> parent = ((BasicConfigKeyOverwriting<?>)key).getParentKey();
// find and remove the parent from consideration
for (FieldAndValue<ConfigKey<?>> k2: kk) {
if (value(k2) == parent)
configKeysAll.remove(kn, k2);
}
}
}
kk = Lists.newArrayList(configKeysAll.get(kn));
// multiple keys, not overwriting; if their values are the same then we don't mind
FieldAndValue<ConfigKey<?>> best = null;
for (FieldAndValue<ConfigKey<?>> k: kk) {
if (best==null) {
best=k;
} else {
Field lower = Reflections.inferSubbestField(k.field, best.field);
ConfigKey<? extends Object> lowerV = lower==null ? null : lower.equals(k.field) ? k.value : best.value;
if (best.value == k.value) {
// same value doesn't matter which we take (but take lower if there is one)
if (LOG.isTraceEnabled())
LOG.trace("multiple definitions for config key {} on {}; same value {}; " +
"from {} and {}, preferring {}",
new Object[] {
best.value.getName(), optionalInstance!=null ? optionalInstance : clazz,
best.value.getDefaultValue(),
k.field, best.field, lower});
best = new FieldAndValue<ConfigKey<?>>(lower!=null ? lower : best.field, best.value);
} else if (lower!=null) {
// different value, but one clearly lower (in type hierarchy)
if (LOG.isTraceEnabled())
LOG.trace("multiple definitions for config key {} on {}; " +
"from {} and {}, preferring lower {}, value {}",
new Object[] {
best.value.getName(), optionalInstance!=null ? optionalInstance : clazz,
k.field, best.field, lower,
lowerV.getDefaultValue() });
best = new FieldAndValue<ConfigKey<?>>(lower, lowerV);
} else {
// different value, neither one lower than another in hierarchy
LOG.warn("multiple ambiguous definitions for config key {} on {}; " +
"from {} and {}, values {} and {}; " +
"keeping latter (arbitrarily)",
new Object[] {
best.value.getName(), optionalInstance!=null ? optionalInstance : clazz,
k.field, best.field,
k.value.getDefaultValue(), best.value.getDefaultValue() });
// (no change)
}
}
}
if (best==null) {
// means key was not reinheritable
continue;
} else {
configKeys.put(best.value.getName(), best);
}
}
}