package org.simpleflatmapper.reflect.meta;
import org.simpleflatmapper.reflect.Getter;
import org.simpleflatmapper.reflect.Setter;
import org.simpleflatmapper.reflect.getter.IndexedBooleanArrayGetter;
import org.simpleflatmapper.reflect.getter.IndexedByteArrayGetter;
import org.simpleflatmapper.reflect.getter.IndexedCharArrayGetter;
import org.simpleflatmapper.reflect.getter.IndexedDoubleArrayGetter;
import org.simpleflatmapper.reflect.getter.IndexedFloatArrayGetter;
import org.simpleflatmapper.reflect.getter.IndexedIntArrayGetter;
import org.simpleflatmapper.reflect.getter.IndexedListGetter;
import org.simpleflatmapper.reflect.getter.IndexedLongArrayGetter;
import org.simpleflatmapper.reflect.getter.IndexedObjectArrayGetter;
import org.simpleflatmapper.reflect.getter.IndexedShortArrayGetter;
import org.simpleflatmapper.reflect.getter.NullGetter;
import org.simpleflatmapper.reflect.instantiator.ExecutableInstantiatorDefinition;
import org.simpleflatmapper.reflect.InstantiatorDefinition;
import org.simpleflatmapper.reflect.ReflectionService;
import org.simpleflatmapper.reflect.setter.AppendCollectionSetter;
import org.simpleflatmapper.reflect.setter.IndexedBooleanArraySetter;
import org.simpleflatmapper.reflect.setter.IndexedByteArraySetter;
import org.simpleflatmapper.reflect.setter.IndexedCharArraySetter;
import org.simpleflatmapper.reflect.setter.IndexedDoubleArraySetter;
import org.simpleflatmapper.reflect.setter.IndexedFloatArraySetter;
import org.simpleflatmapper.reflect.setter.IndexedIntArraySetter;
import org.simpleflatmapper.reflect.setter.IndexedListSetter;
import org.simpleflatmapper.reflect.setter.IndexedLongArraySetter;
import org.simpleflatmapper.reflect.setter.IndexedObjectArraySetter;
import org.simpleflatmapper.reflect.setter.IndexedShortArraySetter;
import org.simpleflatmapper.reflect.setter.NullSetter;
import org.simpleflatmapper.util.BooleanSupplier;
import org.simpleflatmapper.util.Consumer;
import org.simpleflatmapper.util.IntFactory;
import org.simpleflatmapper.util.Predicate;
import org.simpleflatmapper.util.TypeHelper;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.*;
public class ArrayClassMeta<T, E> implements ClassMeta<T> {
private final ReflectionService reflectionService;
private final Type elementTarget;
private final ClassMeta<E> elementClassMeta;
private final Type type;
private final Class<?> implementationType;
private final InstantiatorDefinition constructor;
public ArrayClassMeta(Type type, Type elementTarget, ReflectionService reflectionService) {
this.type = type;
this.elementTarget = elementTarget;
this.reflectionService = reflectionService;
this.elementClassMeta = reflectionService.getClassMeta(elementTarget);
this.implementationType = findImpl(type);
this.constructor = getConstructor(type);
}
private InstantiatorDefinition getConstructor(Type type) {
if (TypeHelper.isArray(type)) {
return null;
} else {
try {
return new ExecutableInstantiatorDefinition(implementationType.getDeclaredConstructor());
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("No empty constructor for " + implementationType);
}
}
}
private Class<?> findImpl(Type type) {
Class<?> clazz = TypeHelper.toClass(type);
if (clazz.isArray()) {
return clazz;
}
if (clazz.isInterface()) {
if (List.class.equals(clazz) || Collection.class.equals(clazz) || Iterable.class.equals(clazz)) {
return ArrayList.class;
} else if (Set.class.equals(clazz)) {
return HashSet.class;
}
} else if (!Modifier.isAbstract(clazz.getModifiers())) {
return clazz;
}
throw new IllegalArgumentException("Unknown List impl for " + type);
}
public ClassMeta<E> getElementClassMeta() {
return elementClassMeta;
}
public Type getElementTarget() {
return elementTarget;
}
@Override
public ReflectionService getReflectionService() {
return reflectionService;
}
@Override
public PropertyFinder<T> newPropertyFinder(Predicate<PropertyMeta<?, ?>> propertyFilter) {
return new ArrayPropertyFinder<T, E>(this, propertyFilter);
}
public Type getType() {
return type;
}
public boolean isArray() {
return TypeHelper.isArray(type);
}
@Override
public List<InstantiatorDefinition> getInstantiatorDefinitions() {
if (constructor != null) {
return Arrays.asList(constructor);
} else {
return Collections.emptyList();
}
}
@Override
public void forEachProperties(Consumer<? super PropertyMeta<T, ?>> consumer) {
throw new UnsupportedOperationException("Cannot forEach property on array as variable");
}
@SuppressWarnings("unchecked")
public <T, E> IntFactory<Setter<T, E>> newSetterFactory(final BooleanSupplier appendSetter) {
if (TypeHelper.isArray(type)) {
Type elementType = TypeHelper.getComponentTypeOfListOrArray(type);
if (TypeHelper.isPrimitive(elementType)) {
if (boolean.class.equals(elementType)) {
return new IntFactory<Setter<T, E>>() {
@Override
public Setter<T, E> newInstance(int i) {
return (Setter<T, E>) new IndexedBooleanArraySetter(i);
}
};
} else if (byte.class.equals(elementType)) {
return new IntFactory<Setter<T, E>>() {
@Override
public Setter<T, E> newInstance(int i) {
return (Setter<T, E>) new IndexedByteArraySetter(i);
}
};
} else if (char.class.equals(elementType)) {
return new IntFactory<Setter<T, E>>() {
@Override
public Setter<T, E> newInstance(int i) {
return (Setter<T, E>) new IndexedCharArraySetter(i);
}
};
} else if (short.class.equals(elementType)) {
return new IntFactory<Setter<T, E>>() {
@Override
public Setter<T, E> newInstance(int i) {
return (Setter<T, E>) new IndexedShortArraySetter(i);
}
};
} else if (int.class.equals(elementType)) {
return new IntFactory<Setter<T, E>>() {
@Override
public Setter<T, E> newInstance(int i) {
return (Setter<T, E>) new IndexedIntArraySetter(i);
}
};
} else if (long.class.equals(elementType)) {
return new IntFactory<Setter<T, E>>() {
@Override
public Setter<T, E> newInstance(int i) {
return (Setter<T, E>) new IndexedLongArraySetter(i);
}
};
} else if (float.class.equals(elementType)) {
return new IntFactory<Setter<T, E>>() {
@Override
public Setter<T, E> newInstance(int i) {
return (Setter<T, E>) new IndexedFloatArraySetter(i);
}
};
} else if (double.class.equals(elementType)) {
return new IntFactory<Setter<T, E>>() {
@Override
public Setter<T, E> newInstance(int i) {
return (Setter<T, E>) new IndexedDoubleArraySetter(i);
}
};
}
} else {
return new IntFactory<Setter<T, E>>() {
@Override
public Setter<T, E> newInstance(int i) {
return new IndexedObjectArraySetter(i);
}
};
}
} else if (TypeHelper.isAssignable(List.class, implementationType)){
return new IntFactory<Setter<T, E>>() {
@Override
public Setter<T, E> newInstance(int i) {
if (appendSetter.getAsBoolean() && i == 0) {
return AppendCollectionSetter.INSTANCE;
} else {
return new IndexedListSetter(i);
}
}
};
} else if (TypeHelper.isAssignable(Collection.class, implementationType)) {
return new IntFactory<Setter<T, E>>() {
@Override
public Setter<T, E> newInstance(int i) {
return AppendCollectionSetter.INSTANCE;
}
};
}
return new IntFactory<Setter<T, E>>() {
@Override
public Setter<T, E> newInstance(int i) {
return (Setter<T, E>) NullSetter.NULL_SETTER;
}
};
}
@SuppressWarnings("unchecked")
public <T, E> IntFactory<Getter<T, E>> newGetterFactory() {
if (TypeHelper.isArray(type)) {
Type elementType = TypeHelper.getComponentTypeOfListOrArray(type);
if (TypeHelper.isPrimitive(elementType)) {
if (boolean.class.equals(elementType)) {
return new IntFactory<Getter<T, E>>() {
@Override
public Getter<T, E> newInstance(int i) {
return (Getter<T, E>) new IndexedBooleanArrayGetter(i);
}
};
} else if (byte.class.equals(elementType)) {
return new IntFactory<Getter<T, E>>() {
@Override
public Getter<T, E> newInstance(int i) {
return (Getter<T, E>) new IndexedByteArrayGetter(i);
}
};
} else if (char.class.equals(elementType)) {
return new IntFactory<Getter<T, E>>() {
@Override
public Getter<T, E> newInstance(int i) {
return (Getter<T, E>) new IndexedCharArrayGetter(i);
}
};
} else if (short.class.equals(elementType)) {
return new IntFactory<Getter<T, E>>() {
@Override
public Getter<T, E> newInstance(int i) {
return (Getter<T, E>) new IndexedShortArrayGetter(i);
}
};
} else if (int.class.equals(elementType)) {
return new IntFactory<Getter<T, E>>() {
@Override
public Getter<T, E> newInstance(int i) {
return (Getter<T, E>) new IndexedIntArrayGetter(i);
}
};
} else if (long.class.equals(elementType)) {
return new IntFactory<Getter<T, E>>() {
@Override
public Getter<T, E> newInstance(int i) {
return (Getter<T, E>) new IndexedLongArrayGetter(i);
}
};
} else if (float.class.equals(elementType)) {
return new IntFactory<Getter<T, E>>() {
@Override
public Getter<T, E> newInstance(int i) {
return (Getter<T, E>) new IndexedFloatArrayGetter(i);
}
};
} else if (double.class.equals(elementType)) {
return new IntFactory<Getter<T, E>>() {
@Override
public Getter<T, E> newInstance(int i) {
return (Getter<T, E>) new IndexedDoubleArrayGetter(i);
}
};
}
} else {
return new IntFactory<Getter<T, E>>() {
@Override
public Getter<T, E> newInstance(int i) {
return new IndexedObjectArrayGetter(i);
}
};
}
} else if (TypeHelper.isAssignable(List.class, implementationType)) {
return new IntFactory<Getter<T, E>>() {
@Override
public Getter<T, E> newInstance(int i) {
return new IndexedListGetter(i);
}
};
}
return new IntFactory<Getter<T, E>>() {
@Override
public Getter<T, E> newInstance(int i) {
return (Getter<T, E>) NullGetter.getter();
}
};
}
}