All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.simpleflatmapper.reflect.meta.ArrayClassMeta Maven / Gradle / Ivy

package org.simpleflatmapper.reflect.meta;

import org.simpleflatmapper.reflect.BuilderInstantiatorDefinition;
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.ErrorHelper;
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 implements ClassMeta {

	private final ReflectionService reflectionService;
	private final Type elementTarget;
	private final ClassMeta elementClassMeta;
	private final Type type;
	private final InstantiatorDefinitionAndIntermediatType instInfo;
	private final boolean needTransformer;

	public ArrayClassMeta(Type type, Type elementTarget, ReflectionService reflectionService, InstantiatorDefinitionAndIntermediatType typeInfo) {
		this.type = type;
		this.elementTarget = elementTarget;
		this.reflectionService = reflectionService;
		this.needTransformer = typeInfo.needTransform;
		this.elementClassMeta = reflectionService.getClassMeta(elementTarget);
		this.instInfo = typeInfo;
	}

	public ArrayClassMeta(Type type, Type elementTarget, ReflectionService reflectionService, boolean needTransformer, ClassMeta elementClassMeta, InstantiatorDefinitionAndIntermediatType instInfo) {
		this.reflectionService = reflectionService;
		this.elementTarget = elementTarget;
		this.elementClassMeta = elementClassMeta;
		this.type = type;
		this.instInfo = instInfo;
		this.needTransformer = needTransformer;
	}

	@Override
	public ArrayClassMeta withReflectionService(ReflectionService reflectionService) {
		return new ArrayClassMeta(type, elementTarget, reflectionService, needTransformer, reflectionService.getClassMeta(elementClassMeta.getType()), instInfo);
	}
	
	public ClassMeta getElementClassMeta() {
		return elementClassMeta;
	}

	public Type getElementTarget() {
		return elementTarget;
	}

	@Override
	public ReflectionService getReflectionService() {
		return reflectionService;
	}

	@Override
	public PropertyFinder newPropertyFinder() {
		return new ArrayPropertyFinder(this, reflectionService.selfScoreFullName());
	}

	public Type getType() {
		return type;
	}

	@Override
	public List getInstantiatorDefinitions() {
		if (instInfo.instantiatorDefinition != null) {
			return Arrays.asList(instInfo.instantiatorDefinition);
		} else {
			return Collections.emptyList();
		}
	}

	@Override
	public void forEachProperties(Consumer> consumer) {
		throw new UnsupportedOperationException("Cannot forEach property on array as variable");
	}

	@Override
	public int getNumberOfProperties() {
		return 10000;
	}

	@Override
	public boolean needTransformer() {
		return needTransformer;
	}



	@SuppressWarnings("unchecked")
	public  IntFactory> newSetterFactory(final BooleanSupplier appendSetter) {
		if (TypeHelper.isArray(type)) {
			if (TypeHelper.isPrimitive(elementTarget)) {
				if (boolean.class.equals(elementTarget)) {
					return new IntFactory>() {
						@Override
						public Setter newInstance(int i) {
							return (Setter) new IndexedBooleanArraySetter(i);
						}
					};
				} else if (byte.class.equals(elementTarget)) {
					return new IntFactory>() {
						@Override
						public Setter newInstance(int i) {
							return (Setter) new IndexedByteArraySetter(i);
						}
					};
				} else if (char.class.equals(elementTarget)) {
					return new IntFactory>() {
						@Override
						public Setter newInstance(int i) {
							return (Setter) new IndexedCharArraySetter(i);
						}
					};
				} else if (short.class.equals(elementTarget)) {
					return new IntFactory>() {
						@Override
						public Setter newInstance(int i) {
							return (Setter) new IndexedShortArraySetter(i);
						}
					};
				} else if (int.class.equals(elementTarget)) {
					return new IntFactory>() {
						@Override
						public Setter newInstance(int i) {
							return (Setter) new IndexedIntArraySetter(i);
						}
					};
				} else if (long.class.equals(elementTarget)) {
					return new IntFactory>() {
						@Override
						public Setter newInstance(int i) {
							return (Setter) new IndexedLongArraySetter(i);
						}
					};
				} else if (float.class.equals(elementTarget)) {
					return new IntFactory>() {
						@Override
						public Setter newInstance(int i) {
							return (Setter) new IndexedFloatArraySetter(i);
						}
					};
				} else if (double.class.equals(elementTarget)) {
					return new IntFactory>() {
						@Override
						public Setter newInstance(int i) {
							return (Setter) new IndexedDoubleArraySetter(i);
						}
					};
				}
			} else {
				return new IntFactory>() {
					@Override
					public Setter newInstance(int i) {
						return new IndexedObjectArraySetter(i);
					}
				};
			}
		} else if (TypeHelper.isAssignable(List.class, instInfo.intermediateType)){
			return new IntFactory>() {
				@Override
				public Setter newInstance(int i) {
					if (appendSetter.getAsBoolean() && i == 0) {
						return AppendCollectionSetter.INSTANCE;
					} else {
						return new IndexedListSetter(i);
					}
				}
			};
		} else if (TypeHelper.isAssignable(Collection.class, instInfo.intermediateType)) {
			return new IntFactory>() {
				@Override
				public Setter newInstance(int i) {
					return AppendCollectionSetter.INSTANCE;
				}
			};
		}
		return new IntFactory>() {
			@Override
			public Setter newInstance(int i) {
				return (Setter) NullSetter.NULL_SETTER;
			}
		};
	}

	@SuppressWarnings("unchecked")
	public  IntFactory> newGetterFactory() {
		if (TypeHelper.isArray(type)) {
			Type elementType = TypeHelper.getComponentTypeOfListOrArray(type);

			if (TypeHelper.isPrimitive(elementType)) {
				if (boolean.class.equals(elementType)) {
					return new IntFactory>() {
						@Override
						public Getter newInstance(int i) {
							return (Getter) new IndexedBooleanArrayGetter(i);
						}
					};
				} else if (byte.class.equals(elementType)) {
					return new IntFactory>() {
						@Override
						public Getter newInstance(int i) {
							return (Getter) new IndexedByteArrayGetter(i);
						}
					};
				} else if (char.class.equals(elementType)) {
					return new IntFactory>() {
						@Override
						public Getter newInstance(int i) {
							return (Getter) new IndexedCharArrayGetter(i);
						}
					};
				} else if (short.class.equals(elementType)) {
					return new IntFactory>() {
						@Override
						public Getter newInstance(int i) {
							return (Getter) new IndexedShortArrayGetter(i);
						}
					};
				} else if (int.class.equals(elementType)) {
					return new IntFactory>() {
						@Override
						public Getter newInstance(int i) {
							return (Getter) new IndexedIntArrayGetter(i);
						}
					};
				} else if (long.class.equals(elementType)) {
					return new IntFactory>() {
						@Override
						public Getter newInstance(int i) {
							return (Getter) new IndexedLongArrayGetter(i);
						}
					};
				} else if (float.class.equals(elementType)) {
					return new IntFactory>() {
						@Override
						public Getter newInstance(int i) {
							return (Getter) new IndexedFloatArrayGetter(i);
						}
					};
				} else if (double.class.equals(elementType)) {
					return new IntFactory>() {
						@Override
						public Getter newInstance(int i) {
							return (Getter) new IndexedDoubleArrayGetter(i);
						}
					};
				}
			} else {
				return new IntFactory>() {
					@Override
					public Getter newInstance(int i) {
						return new IndexedObjectArrayGetter(i);
					}
				};
			}

		} else if (TypeHelper.isAssignable(List.class, instInfo.intermediateType)) {
			return new IntFactory>() {
				@Override
				public Getter newInstance(int i) {
					return new IndexedListGetter(i);
				}
			};
		}
		return new IntFactory>() {
			@Override
			public Getter newInstance(int i) {
				return (Getter) NullGetter.getter();
			}
		};
	}

	public boolean isIndexedAccessible() {
		return TypeHelper.isAssignable(List.class, instInfo.intermediateType) || TypeHelper.isArray(type);
	}


	public static class InstantiatorDefinitionAndIntermediatType {
		public final InstantiatorDefinition instantiatorDefinition;
		public final Type intermediateType;
		public final boolean needTransform;

		public InstantiatorDefinitionAndIntermediatType(InstantiatorDefinition instantiatorDefinition, Type intermediateType, boolean needTransform) {
			this.instantiatorDefinition = instantiatorDefinition;
			this.intermediateType = intermediateType;
			this.needTransform = needTransform;
		}
	}
	
	public interface InstantiatorDefinitionAndIntermediatTypeFactory {
		boolean supports(Type type);
		InstantiatorDefinitionAndIntermediatType newTypeInfo(Type type);
		
	}
	
	
	private static final List intermediatTypeFactories;
	static {
		intermediatTypeFactories = new ArrayList();


		intermediatTypeFactories.add(new InstantiatorDefinitionAndIntermediatTypeFactory() {
			@Override
			public boolean supports(Type type) {
				return TypeHelper.isArray(type);
			}

			@Override
			public InstantiatorDefinitionAndIntermediatType newTypeInfo(Type type) {
				return new InstantiatorDefinitionAndIntermediatType(null, type, false);
			}
		});


		intermediatTypeFactories.add(new InstantiatorDefinitionAndIntermediatTypeFactory() {
			@Override
			public boolean supports(Type type) {
				return TypeHelper.areEquals(type, List.class)
						|| TypeHelper.areEquals(type, Collection.class)
						|| TypeHelper.areEquals(type, Iterable.class)
						;
			}

			@Override
			public InstantiatorDefinitionAndIntermediatType newTypeInfo(Type type) {
				return new InstantiatorDefinitionAndIntermediatType(getConstructor(ArrayList.class), ArrayList.class, false);
			}
		});

		intermediatTypeFactories.add(new InstantiatorDefinitionAndIntermediatTypeFactory() {
			@Override
			public boolean supports(Type type) {
				return TypeHelper.areEquals(type, Set.class)
						;
			}
			@Override
			public InstantiatorDefinitionAndIntermediatType newTypeInfo(Type type) {
				return new InstantiatorDefinitionAndIntermediatType(getConstructor(HashSet.class), HashSet.class, false);
			}
		});

		intermediatTypeFactories.add(new InstantiatorDefinitionAndIntermediatTypeFactory() {
			@Override
			public boolean supports(Type type) {
				return "org.eclipse.collections.api.list.MutableList".equals(TypeHelper.toClass(type).getName());
			}
			@Override
			public InstantiatorDefinitionAndIntermediatType newTypeInfo(Type type) {
				try {
					Class clazz = TypeHelper.toClass(type).getClassLoader().loadClass("org.eclipse.collections.impl.list.mutable.FastList");
					return new InstantiatorDefinitionAndIntermediatType(getConstructor(clazz), clazz, false);
				} catch (ClassNotFoundException e) {
					return ErrorHelper.rethrow(e);
				}

			}
		});

		intermediatTypeFactories.add(new InstantiatorDefinitionAndIntermediatTypeFactory() {
			@Override
			public boolean supports(Type type) {
				return "org.eclipse.collections.api.list.ImmutableList".equals(TypeHelper.toClass(type).getName());
			}
			@Override
			public InstantiatorDefinitionAndIntermediatType newTypeInfo(Type type) {
				try {
					Class clazz = TypeHelper.toClass(type).getClassLoader().loadClass("org.eclipse.collections.impl.list.mutable.FastList");

					return new InstantiatorDefinitionAndIntermediatType(
							new BuilderInstantiatorDefinition(getConstructor(clazz),
									new HashMap(),
									clazz.getMethod("toImmutable")),
							clazz, true);
				} catch (ClassNotFoundException e) {
					return ErrorHelper.rethrow(e);
				} catch (NoSuchMethodException e) {
					return ErrorHelper.rethrow(e);
				}
			}
		});

		intermediatTypeFactories.add(new InstantiatorDefinitionAndIntermediatTypeFactory() {
			@Override
			public boolean supports(Type type) {
				return "com.google.common.collect.ImmutableList".equals(TypeHelper.toClass(type).getName());
			}
			@Override
			public InstantiatorDefinitionAndIntermediatType newTypeInfo(Type type) {
				try {
					Class builderClass = TypeHelper.toClass(type).getClassLoader().loadClass("com.google.common.collect.ImmutableList");
					return new InstantiatorDefinitionAndIntermediatType(
							new BuilderInstantiatorDefinition(getConstructor(ArrayList.class),
									new HashMap(),
									builderClass.getMethod("copyOf", Collection.class)),
							ArrayList.class, true);
				} catch (ClassNotFoundException e) {
					return ErrorHelper.rethrow(e);
				} catch (NoSuchMethodException e) {
					return ErrorHelper.rethrow(e);
				}
			}
		});
	}


	public static boolean supports(Type target) {
		Class clazz = TypeHelper.toClass(target);

		for(InstantiatorDefinitionAndIntermediatTypeFactory factory  :intermediatTypeFactories) {
			if (factory.supports(target)) {
				return true;
			}
		}

		return (Collection.class.isAssignableFrom(clazz) || Iterable.class.equals(clazz));
	}

	public static  ArrayClassMeta of(Type type, Type elementTarget, ReflectionService reflectionService) {
		InstantiatorDefinitionAndIntermediatType typeInfo = getTypeInfo(type);
		return new ArrayClassMeta(type, elementTarget, reflectionService, typeInfo);
	}
	
	
	public static InstantiatorDefinitionAndIntermediatType getTypeInfo(Type type) {
		for(InstantiatorDefinitionAndIntermediatTypeFactory factory  :intermediatTypeFactories) {
			if (factory.supports(type)) {
				return factory.newTypeInfo(type);
			}
		}

		Class clazz = TypeHelper.toClass(type);

		if (!Modifier.isAbstract(clazz.getModifiers())) {
			return new InstantiatorDefinitionAndIntermediatType(getConstructor(type), type, false);
		}
		throw new IllegalArgumentException("Unknown List impl for " + type);
	}


	private static InstantiatorDefinition getConstructor(Type type) {
		if (TypeHelper.isArray(type)) {
			return null;
		} else {
			try {
				return new ExecutableInstantiatorDefinition(TypeHelper.toClass(type).getDeclaredConstructor());
			} catch (NoSuchMethodException e) {
				throw new IllegalArgumentException("No empty constructor for " + type);
			}
		}
	}


}