1 | |
package org.truth0.codegen; |
2 | |
|
3 | |
import static java.lang.reflect.Modifier.isFinal; |
4 | |
import static java.lang.reflect.Modifier.isPrivate; |
5 | |
import static java.lang.reflect.Modifier.isStatic; |
6 | |
|
7 | |
import java.lang.annotation.Annotation; |
8 | |
import java.lang.reflect.Method; |
9 | |
import java.lang.reflect.Modifier; |
10 | |
import java.util.Arrays; |
11 | |
import java.util.List; |
12 | |
|
13 | |
import org.truth0.subjects.Subject; |
14 | |
import org.truth0.subjects.SubjectFactory; |
15 | |
import org.truth0.util.ReflectionUtil; |
16 | |
|
17 | |
|
18 | |
|
19 | |
|
20 | |
|
21 | |
|
22 | |
|
23 | |
|
24 | |
|
25 | |
public class IteratingWrapperClassBuilder { |
26 | |
|
27 | |
|
28 | |
|
29 | |
|
30 | |
|
31 | |
|
32 | |
|
33 | |
|
34 | |
|
35 | |
|
36 | |
|
37 | |
|
38 | |
|
39 | |
|
40 | |
private static final String CLASS_TEMPLATE = |
41 | |
"package %1$s;%n" + |
42 | |
"%n" + |
43 | |
"import org.truth0.FailureStrategy;%n" + |
44 | |
"import org.truth0.subjects.SubjectFactory;%n" + |
45 | |
"%n" + |
46 | |
"public class %2$sIteratingWrapper extends %2$s {%n" + |
47 | |
"%n" + |
48 | |
" private final SubjectFactory subjectFactory;%n" + |
49 | |
" private final Iterable<%3$s> data;%n" + |
50 | |
"%n" + |
51 | |
" public %2$sIteratingWrapper(%n" + |
52 | |
" FailureStrategy failureStrategy,%n" + |
53 | |
" SubjectFactory<?, ?> subjectFactory,%n" + |
54 | |
" Iterable<%3$s> data%n" + |
55 | |
" ) {%n" + |
56 | |
" super(failureStrategy, (%3$s)null);%n" + |
57 | |
" this.subjectFactory = subjectFactory;%n" + |
58 | |
" this.data = data;%n" + |
59 | |
" }%n" + |
60 | |
"%n" + |
61 | |
"%4$s" + |
62 | |
"}%n"; |
63 | |
|
64 | |
|
65 | |
|
66 | |
|
67 | |
|
68 | |
|
69 | |
|
70 | |
|
71 | |
|
72 | |
|
73 | |
|
74 | |
|
75 | |
|
76 | |
|
77 | |
|
78 | |
|
79 | |
|
80 | |
private static final String WRAPPER_METHOD_TEMPLATE = |
81 | |
" %1$s %2$s %3$s(%4$s) {%n" + |
82 | |
" for (%5$s item : data) {%n" + |
83 | |
" %6$s subject = (%6$s)subjectFactory.getSubject(failureStrategy, item);%n" + |
84 | |
" subject.%3$s(%7$s);%n" + |
85 | |
" }%n" + |
86 | |
" }%n"; |
87 | |
|
88 | |
|
89 | |
private static final int TARGET_TYPE_PARAMETER = 1; |
90 | |
|
91 | |
private static final String ITERATING_WRAPPER = "IteratingWrapper"; |
92 | |
|
93 | |
private final SubjectFactory<?, ?> subjectFactory; |
94 | |
|
95 | |
public final String className; |
96 | |
|
97 | 4 | public IteratingWrapperClassBuilder(SubjectFactory<?,?> subjectFactory) { |
98 | 4 | this.subjectFactory = subjectFactory; |
99 | 4 | this.className = subjectFactory.getSubjectClass().getCanonicalName() + ITERATING_WRAPPER; |
100 | 4 | } |
101 | |
|
102 | |
public String build() { |
103 | 4 | Class<?> subjectClass = subjectFactory.getSubjectClass(); |
104 | 4 | List<Method> methods = Arrays.asList(subjectClass.getMethods()); |
105 | 4 | Class<?> targetType = ReflectionUtil.typeParameter(subjectClass, TARGET_TYPE_PARAMETER); |
106 | |
|
107 | 4 | StringBuilder methodWrappers = new StringBuilder(); |
108 | 4 | for (Method m : methods) { |
109 | 82 | appendMethodWrapper(methodWrappers, subjectClass, targetType, m); |
110 | |
} |
111 | 4 | String code = String.format( |
112 | |
CLASS_TEMPLATE, |
113 | |
subjectClass.getPackage().getName(), |
114 | |
subjectClass.getSimpleName(), |
115 | |
targetType.getCanonicalName(), |
116 | |
methodWrappers.toString()); |
117 | |
|
118 | 4 | return code; |
119 | |
} |
120 | |
|
121 | |
private void appendMethodWrapper( |
122 | |
StringBuilder code, |
123 | |
Class<?> subjectType, |
124 | |
Class<?> targetType, |
125 | |
Method method) { |
126 | 82 | int modifiers = method.getModifiers(); |
127 | 82 | boolean shouldWrap = |
128 | |
!method.getDeclaringClass().equals(Subject.class) && |
129 | |
!method.getDeclaringClass().equals(Object.class) && |
130 | |
!(isFinal(modifiers) || isPrivate(modifiers) || isStatic(modifiers)); |
131 | |
|
132 | 82 | if (shouldWrap) { |
133 | 14 | code.append(String.format( |
134 | |
WRAPPER_METHOD_TEMPLATE, |
135 | |
stringVisibility(modifiers), |
136 | |
method.getReturnType().getCanonicalName(), |
137 | |
method.getName(), |
138 | |
methodSignature( |
139 | |
method.getParameterTypes(), |
140 | |
method.getParameterAnnotations()).toString(), |
141 | |
targetType.getCanonicalName(), |
142 | |
subjectType.getCanonicalName(), |
143 | |
methodParameterList(method.getParameterTypes().length) |
144 | |
)); |
145 | |
} |
146 | 82 | } |
147 | |
|
148 | |
private static StringBuilder methodParameterList(int length) { |
149 | 14 | StringBuilder builder = new StringBuilder(); |
150 | 30 | for (int i = 0; i < length; i++) { |
151 | 16 | if (i > 0) builder.append(", "); |
152 | 16 | builder.append("arg").append(0); |
153 | |
} |
154 | 14 | return builder; |
155 | |
} |
156 | |
|
157 | |
|
158 | |
private static StringBuilder methodSignature(Class<?>[] parameters, Annotation[][] annotations) { |
159 | 14 | StringBuilder builder = new StringBuilder(); |
160 | 30 | for (int i = 0, iLen = parameters.length; i < iLen; i++) { |
161 | 16 | if (i > 0) builder.append(", "); |
162 | 17 | for (int j = 0, jLen = annotations[i].length; j < jLen; j++) { |
163 | 1 | if (j > 0) builder.append(" "); |
164 | 1 | builder.append("@").append(annotations[i][j].annotationType().getCanonicalName()); |
165 | 1 | builder.append(" "); |
166 | |
} |
167 | 16 | builder.append(parameters[i].getCanonicalName()); |
168 | 16 | builder.append(" arg").append(i); |
169 | |
} |
170 | 14 | return builder; |
171 | |
} |
172 | |
|
173 | |
private static String stringVisibility(int modifiers) { |
174 | 14 | if (Modifier.isProtected(modifiers)) { |
175 | 0 | return "protected"; |
176 | 14 | } else if (Modifier.isPublic(modifiers)) { |
177 | 14 | return "public"; |
178 | |
} else { |
179 | 0 | return ""; |
180 | |
} |
181 | |
} |
182 | |
|
183 | |
} |