summaryrefslogtreecommitdiff
path: root/src/main/java/au/id/zancanaro/javacheck/object/GeneratorProvider.java
blob: c9759333fe500df12077d7209bc92f7fa40e77b9 (about) (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
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> T getAnnotation(Annotation[] annotations, Class<T> 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<Type, Generator<?>> generators = helper.computeGenerators();
        return (type, annotations, provider) -> {
            if (generators.containsKey(type)) {
                return generators.get(type);
            } else {
                return this.getGenerator(type, annotations, provider);
            }
        };
    }
}