se.fortnox.reactivewizard.util.PropertyResolver Maven / Gradle / Ivy
package se.fortnox.reactivewizard.util;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
public class PropertyResolver {
private final Type genericType;
private final Property[] properties;
private final Class> type;
private PropertyResolver(Class> type, Type genericType, Property[] properties) {
this.type = type;
this.genericType = genericType;
this.properties = properties;
}
/**
* Get property resolver for type.
* @param type the type
* @param propertyNames the property names
* @return an optional of property resolver
*/
public static Optional from(Type type, String[] propertyNames) {
Property[] property = new Property[propertyNames.length];
Class> cls = ReflectionUtil.getRawType(type);
for (int i = 0; i < propertyNames.length; i++) {
property[i] = Property.from(cls, propertyNames[i]);
if (property[i] == null) {
// prop not found
return Optional.empty();
}
cls = property[i].getType();
}
Type genericType = propertyNames.length == 0 ? type : property[propertyNames.length - 1].getGenericType();
return Optional.of(new PropertyResolver(cls, genericType, property));
}
public Class> getPropertyType() {
return type;
}
public Type getPropertyGenericType() {
return genericType;
}
/**
* Get property resolver for subpath.
* @param subPath the subpath
* @return an optional of property resolver
*/
public Optional subPath(String[] subPath) {
Optional propsToAppend = from(getPropertyType(), subPath);
if (propsToAppend.isPresent()) {
PropertyResolver otherPropertyResolver = propsToAppend.get();
Property[] propertiesToAppend = otherPropertyResolver.properties;
Property[] newProperties = new Property[this.properties.length + propertiesToAppend.length];
System.arraycopy(properties, 0, newProperties, 0, properties.length);
System.arraycopy(propertiesToAppend, 0, newProperties, properties.length, propertiesToAppend.length);
return Optional.of(new PropertyResolver(otherPropertyResolver.getPropertyType(), otherPropertyResolver.getPropertyGenericType(), newProperties));
}
return Optional.empty();
}
@Override
public String toString() {
return Arrays.toString(properties);
}
/**
* Get getter.
* @return the getter
*/
public Function getter() {
if (properties.length == 0) {
return (Function) Function.identity();
}
if (properties.length == 1) {
return properties[0].getter();
}
Function[] functionChain = new Function[properties.length];
for (int i = 0; i < properties.length; i++) {
functionChain[i] = properties[i].getter();
}
return new FunctionChain<>(functionChain);
}
public BiConsumer setter() {
if (properties.length == 0) {
throw new IllegalArgumentException("No properties found");
}
if (properties.length == 1) {
return properties[0].setter();
}
Function[] getterChain = new Function[properties.length - 1];
BiConsumer[] setterChain = new BiConsumer[properties.length - 1];
Supplier[] instantiators = new Supplier[properties.length - 1];
for (int i = 0; i < properties.length - 1; i++) {
getterChain[i] = properties[i].getter();
setterChain[i] = properties[i].setter();
instantiators[i] = ReflectionUtil.instantiator(properties[i].getType());
}
return new SetterChain(getterChain, setterChain, instantiators, properties[properties.length - 1].setter());
}
private static class FunctionChain implements Function {
private final Function[] functionChain;
public FunctionChain(Function[] functionChain) {
this.functionChain = functionChain;
}
@Override
public T apply(I instance) {
Object currentInstance = instance;
for (int i = 0; i < functionChain.length && currentInstance != null; i++) {
currentInstance = functionChain[i].apply(currentInstance);
}
return (T)currentInstance;
}
}
private static class SetterChain implements BiConsumer {
private final Function[] getterChain;
private final BiConsumer[] setterChain;
private final Supplier[] instantiators;
private final BiConsumer setter;
public SetterChain(Function[] getterChain, BiConsumer[] setterChain, Supplier[] instantiators, BiConsumer setter) {
this.getterChain = getterChain;
this.setterChain = setterChain;
this.instantiators = instantiators;
this.setter = setter;
}
@Override
public void accept(I instance, T value) {
Object currentInstance = instance;
for (int i = 0; i < getterChain.length && currentInstance != null; i++) {
Object next = getterChain[i].apply(currentInstance);
if (next == null) {
next = instantiators[i].get();
setterChain[i].accept(currentInstance, next);
}
currentInstance = next;
}
setter.accept(currentInstance, value);
}
}
private static class Property {
private final String name;
private final Class> type;
private final Type genericType;
private final Getter getter;
private final Setter setter;
Property(String name, Class> type, Type genericType, Getter getter, Setter setter) {
this.name = name;
this.type = type;
this.genericType = genericType;
this.getter = getter;
this.setter = setter;
}
private static Property from(Class cls, String prop) {
final Getter getter = ReflectionUtil.getGetter(cls, prop);
final Setter setter = ReflectionUtil.getSetter(cls, prop);
if (getter != null) {
return new Property(prop, getter.getReturnType(), getter.getGenericReturnType(), getter, setter);
} else if (setter != null) {
return new Property(prop, setter.getParameterType(), setter.getGenericParameterType(), null, setter);
}
return null;
}
public Class> getType() {
return type;
}
Type getGenericType() {
return genericType;
}
@Override
public String toString() {
return name;
}
public Function getter() {
return getter.getterFunction();
}
public BiConsumer setter() {
return setter.setterFunction();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy