io.scalecube.config.ObjectConfigPropertyImpl Maven / Gradle / Ivy
package io.scalecube.config;
import io.scalecube.config.source.LoadedConfigProperty;
import io.scalecube.config.utils.ThrowableUtil;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* Implementation of {@link ObjectConfigProperty}.
*
* @param type of the property value
*/
class ObjectConfigPropertyImpl extends AbstractConfigProperty
implements ObjectConfigProperty {
@SuppressWarnings("rawtypes")
ObjectConfigPropertyImpl(
Map bindingMap,
Class cfgClass,
Map propertyMap,
Map> propertyCallbackMap) {
super(cfgClass.getName(), cfgClass);
List propertyFields = toPropertyFields(bindingMap, cfgClass);
setPropertyCallback(computePropertyCallback(cfgClass, propertyFields, propertyCallbackMap));
computeValue(
propertyFields.stream()
.map(ObjectPropertyField::getPropertyName)
.filter(propertyMap::containsKey)
.map(propertyMap::get)
.collect(Collectors.toList()));
}
@Override
public T value(T defaultValue) {
return value().orElse(defaultValue);
}
private List toPropertyFields(
Map bindingMap, Class cfgClass) {
List propertyFields = new ArrayList<>(bindingMap.size());
for (String fieldName : bindingMap.keySet()) {
Field field;
try {
field = cfgClass.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
throw ThrowableUtil.propagate(e);
}
int modifiers = field.getModifiers();
if (!Modifier.isStatic(modifiers) && !Modifier.isFinal(modifiers)) {
propertyFields.add(new ObjectPropertyField(field, bindingMap.get(fieldName)));
}
}
return propertyFields;
}
private PropertyCallback computePropertyCallback(
Class cfgClass,
List propertyFields,
Map> propertyCallbackMap) {
PropertyCallback propertyCallback =
new PropertyCallback<>(
list -> ObjectPropertyParser.parseObject(list, propertyFields, cfgClass));
List propertyNames =
propertyFields.stream()
.map(ObjectPropertyField::getPropertyName)
.collect(Collectors.toList());
// ensure that only one propertyCallback instance will be shared among instances of the same
// type
synchronized (propertyCallbackMap) {
propertyNames.forEach(
propName -> {
propertyCallbackMap.putIfAbsent(propName, new ConcurrentHashMap<>());
Map callbackMap = propertyCallbackMap.get(propName);
callbackMap.putIfAbsent(propertyClass, propertyCallback);
});
}
// noinspection unchecked
return propertyCallbackMap.entrySet().stream()
.filter(e -> propertyNames.contains(e.getKey()))
.map(Map.Entry::getValue)
.filter(callbackMap -> callbackMap.containsKey(propertyClass))
.map(callbackMap -> callbackMap.get(propertyClass))
.collect(Collectors.toSet())
.iterator()
.next();
}
}