package au.id.zancanaro.javacheck.object; import au.id.zancanaro.javacheck.DataSourceHelper; import au.id.zancanaro.javacheck.Generator; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import static au.id.zancanaro.javacheck.Generators.*; public interface GeneratorProvider { Generator getGenerator(Type type, Annotation[] annotations, GeneratorProvider topLevel); final static GeneratorProvider DEFAULT_PROVIDER = new GeneratorProvider() { @SuppressWarnings("unchecked") private T getAnnotation(Annotation[] annotations, Class type) { for (Annotation ann : annotations) { if (type.isAssignableFrom(ann.getClass())) { return (T) ann; } } return null; } @Override public Generator getGenerator(Type type, Annotation[] annotations, GeneratorProvider provider) { UseGenerator generator = getAnnotation(annotations, UseGenerator.class); if (generator != null) { try { return generator.value().getConstructor().newInstance(); } catch (InvocationTargetException | NoSuchMethodException | InstantiationException | IllegalAccessException ex) { throw new ObjectGenerationException("Could not construct generator of type " + generator.value(), ex); } } else if (type == Integer.TYPE || type == Integer.class) { IntRange range = getAnnotation(annotations, IntRange.class); if (range == null) { return integer(); } else { return integer(range.min(), range.max()); } } else if (type == Long.TYPE || type == Long.class) { LongRange range = getAnnotation(annotations, LongRange.class); if (range == null) { return longInteger(); } else { return longInteger(range.min(), range.max()); } } else if (type == Double.TYPE || type == Double.class) { DoubleRange range = getAnnotation(annotations, DoubleRange.class); if (range == null) { return doublePrecision(); } else { return doublePrecision(range.min(), range.max()); } } else if (type == String.class) { CharType charType = getAnnotation(annotations, CharType.class); if (charType == null) { return string(); } else { switch (charType.value()) { case ALPHA: return stringOf(alphaCharacter()); case ALPHA_NUMERIC: return stringOf(alphaNumericCharacter()); case ASCII: return stringOf(asciiCharacter()); case ALL: return stringOf(character()); default: return string(); } } } else if (type == List.class) { return listOf(provider.getGenerator( List.class.getTypeParameters()[0], new Annotation[0], provider)); } else if (type == Set.class) { return setOf(provider.getGenerator( Set.class.getTypeParameters()[0], new Annotation[0], provider)); } else if (type == Map.class) { TypeVariable[] params = Map.class.getTypeParameters(); TypeVariable keyType = params[0], valueType = params[1]; return mapOf( provider.getGenerator(keyType, new Annotation[0], provider), provider.getGenerator(valueType, new Annotation[0], provider)); } else if (type instanceof Class) { Class clazz = (Class) type; if (Enum.class.isAssignableFrom(clazz)) { return elements(clazz.getEnumConstants()); } else { return ofType((Class) type, provider); } } else if (type instanceof ParameterizedType) { ParameterizedType param = (ParameterizedType) type; if (param.getRawType() instanceof Class) { Class container = (Class) param.getRawType(); TypeVariable[] variables = container.getTypeParameters(); Type[] types = param.getActualTypeArguments(); return provider.getGenerator( container, new Annotation[0], provider.withSubstitutedTypeVariables(variables, types)); } throw new ObjectGenerationException("Cannot generate object of type " + type); } else { throw new ObjectGenerationException("Cannot generate object of type " + type); } } }; default GeneratorProvider withSubstitutedTypeVariables(TypeVariable[] variables, Type[] types) { GeneratorProvider fallback = this; return new GeneratorProvider() { @Override public Generator getGenerator(Type type, Annotation[] annotations, GeneratorProvider provider) { for (int i = 0; i < variables.length; ++i) { if (Objects.equals(variables[i], type)) { return provider.getGenerator( types[i], new Annotation[0], this); } } return fallback.getGenerator(type, annotations, provider); } }; } default GeneratorProvider withDataSources(Class wrappingType) { DataSourceHelper helper = new DataSourceHelper(wrappingType); Map> generators = helper.computeGenerators(); return (type, annotations, provider) -> { if (generators.containsKey(type)) { return generators.get(type); } else { return this.getGenerator(type, annotations, provider); } }; } }