package com.codepoetics.phantompojo.impl;
import com.codepoetics.phantompojo.PhantomPojo;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
final class PhantomBuilderProxy<P extends PhantomPojo<B>, B extends Supplier<P>> implements DispatchingInvocationHandler, Supplier<P> {
private final PhantomBuilderClassPair<P, B> classPair;
private final PropertyStore store;
PhantomBuilderProxy(PhantomBuilderClassPair<P, B> classPair, PropertyStore store) {
this.classPair = classPair;
this.store = store;
}
@Override
public Object invokeMissing(Object proxy, Method method, Object[] args) {
Type targetType = store.getTargetType(method);
store.write(method, reify(args[0], targetType));
return proxy;
}
private Object reify(Object arg, Type targetType) {
if (arg == null) {
return null;
}
if (arg instanceof Collection) {
return reifyStream(targetType, ((Collection<Object>) arg).stream());
}
if (arg.getClass().isArray()) {
return reifyStream(targetType, Stream.of(ReflectionUtils.toObjectArray(arg)));
}
return arg instanceof Supplier
? ((Supplier<?>) arg).get()
: arg;
}
private Object reifyStream(Type targetType, Stream<Object> argStream) {
Class<?> rawType = ReflectionUtils.rawTypeOf(targetType);
Type itemType = ReflectionUtils.getFirstTypeArgument(targetType);
Stream<Object> reifiedStream = argStream.map(a -> reify(a, itemType));
if (rawType.equals(List.class)) {
return reifiedStream.collect(Collectors.toList());
}
if (rawType.equals(Set.class)) {
return reifiedStream.collect(Collectors.toSet());
}
throw new IllegalArgumentException("Unable to convert collection to " + rawType.getSimpleName());
}
@Override
public P get() {
return classPair.createPhantom(store.copy());
}
}