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 package com.lhkbob.entreri.impl;
28
29 import com.lhkbob.entreri.Component;
30 import com.lhkbob.entreri.property.ObjectProperty;
31
32 import javax.lang.model.SourceVersion;
33 import java.nio.ByteBuffer;
34 import java.nio.charset.Charset;
35 import java.security.MessageDigest;
36 import java.security.NoSuchAlgorithmException;
37 import java.text.DateFormat;
38 import java.text.SimpleDateFormat;
39 import java.util.*;
40
41
42
43
44
45
46
47
48
49
50
51 public abstract class ComponentFactoryProvider {
52
53
54
55
56
57
58
59
60
61 public static interface Factory<T extends Component> {
62
63
64
65
66
67
68
69
70
71
72 public AbstractComponent<T> newInstance(ComponentRepository<T> forRepository);
73
74
75
76
77
78
79
80
81 public ComponentSpecification getSpecification();
82 }
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101 public abstract <T extends Component> Factory<T> getFactory(Class<T> componentType);
102
103
104
105
106
107
108
109 public static ComponentFactoryProvider getInstance() {
110 return INSTANCE;
111 }
112
113 private static final ComponentFactoryProvider INSTANCE = new CachingDelegatingFactoryProvider();
114
115
116
117
118
119
120
121
122
123
124
125
126
127 public static String getImplementationClassName(ComponentSpecification spec,
128 boolean includePackage) {
129
130
131 String scrubbed = spec.getType().replace("[\\.]", "");
132
133
134
135 int hash;
136 try {
137 MessageDigest md = MessageDigest.getInstance("MD5");
138 md.update((spec.getPackage() + "." + spec.getType()).getBytes(CHARSET));
139
140 ByteBuffer md5 = ByteBuffer.wrap(md.digest());
141 hash = Math.abs(md5.getInt() ^ md5.getInt() ^ md5.getInt() ^ md5.getInt());
142 } catch (NoSuchAlgorithmException e) {
143 throw new RuntimeException("JVM does not support MD5", e);
144 }
145
146 StringBuilder sb = new StringBuilder();
147 if (includePackage && !spec.getPackage().isEmpty()) {
148 sb.append(spec.getPackage()).append('.');
149 }
150 sb.append(scrubbed).append("Impl").append(hash);
151
152 return sb.toString();
153 }
154
155 private static final Charset CHARSET = Charset.forName("UTF-8");
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173 public static String generateJavaCode(ComponentSpecification spec,
174 SourceVersion targetVersion) {
175 boolean use15 = targetVersion.compareTo(SourceVersion.RELEASE_5) >= 0;
176 String implName = getImplementationClassName(spec, false);
177
178
179
180 StringBuilder sb = new StringBuilder();
181
182 if (!spec.getPackage().isEmpty()) {
183 sb.append("package ").append(spec.getPackage()).append(";\n\n");
184 }
185
186 if (use15) {
187
188
189 TimeZone tz = TimeZone.getTimeZone("UTC");
190 DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'");
191 df.setTimeZone(tz);
192 String nowAsISO = df.format(new Date());
193
194 sb.append("import javax.annotation.Generated;\n\n");
195 sb.append("@Generated(value={\"")
196 .append(ComponentFactoryProvider.class.getCanonicalName())
197 .append("\"}, date=\"").append(nowAsISO).append("\")\n");
198 sb.append("@SuppressWarnings(\"unchecked\")\n");
199 }
200
201 sb.append("public class ").append(implName).append(" extends ")
202 .append(ABSTRACT_COMPONENT_NAME);
203 if (use15) {
204 sb.append('<').append(spec.getType()).append('>');
205 }
206 sb.append(" implements ").append(spec.getType()).append(" {\n");
207
208
209
210 int property = 0;
211 for (PropertyDeclaration s : spec.getProperties()) {
212 sb.append("\tprivate final ").append(s.getPropertyImplementation())
213 .append(' ').append(PROPERTY_FIELD_PREFIX).append(property).append(";\n");
214 if (s.isShared()) {
215 sb.append("\tprivate final ").append(s.getType()).append(' ')
216 .append(SHARED_FIELD_PREFIX).append(property).append(";\n");
217 }
218 property++;
219 }
220
221
222
223 sb.append("\n\tpublic ").append(implName).append("(").append(COMPONENT_REPO_NAME);
224 if (use15) {
225 sb.append('<').append(spec.getType()).append('>');
226 }
227
228 sb.append(" ").append(REPO_FIELD_NAME).append(") {\n").append("\t\tsuper(")
229 .append(REPO_FIELD_NAME).append(");\n");
230 property = 0;
231 for (PropertyDeclaration s : spec.getProperties()) {
232 sb.append("\t\t").append(PROPERTY_FIELD_PREFIX).append(property)
233 .append(" = (").append(s.getPropertyImplementation()).append(") ")
234 .append(REPO_FIELD_NAME).append('.').append(GET_PROPERTY_METHOD).append('(')
235 .append(property).append(");\n");
236 if (s.isShared()) {
237 sb.append("\t\t").append(SHARED_FIELD_PREFIX).append(property)
238 .append(" = ").append(PROPERTY_FIELD_PREFIX).append(property)
239 .append('.').append(CREATE_SHARE_METHOD).append("();\n");
240 }
241 property++;
242 }
243 sb.append("\t}\n");
244
245
246 Map<String, List<PropertyDeclaration>> setters = new HashMap<>();
247 property = 0;
248 for (PropertyDeclaration s : spec.getProperties()) {
249 if (use15) {
250 sb.append("\n\t@Override");
251 }
252 appendGetter(s, property, sb);
253 List<PropertyDeclaration> setterParams = setters.get(s.getSetterMethod());
254 if (setterParams == null) {
255 setterParams = new ArrayList<>();
256 setters.put(s.getSetterMethod(), setterParams);
257 }
258 setterParams.add(s);
259
260 property++;
261 }
262
263
264 for (List<PropertyDeclaration> setter : setters.values()) {
265 if (use15) {
266 sb.append("\n\t@Override");
267 }
268 appendSetter(setter, spec, sb);
269 }
270
271 sb.append("}\n");
272 return sb.toString();
273 }
274
275
276 private static final String ABSTRACT_COMPONENT_NAME = AbstractComponent.class
277 .getName();
278 private static final String COMPONENT_REPO_NAME = ComponentRepository.class.getName();
279 private static final String OBJECT_PROP_NAME = ObjectProperty.class.getName();
280
281 private static final String REPO_FIELD_NAME = "owner";
282 private static final String INDEX_FIELD_NAME = "index";
283 private static final String GET_PROPERTY_METHOD = "getProperty";
284 private static final String CREATE_SHARE_METHOD = "createShareableInstance";
285 private static final String UPDATE_VERSION_METHOD = "incrementVersion";
286
287 private static final String PROPERTY_FIELD_PREFIX = "property";
288 private static final String SHARED_FIELD_PREFIX = "sharedInstance";
289 private static final String SETTER_PARAM_PREFIX = "param";
290
291
292
293
294
295
296
297
298
299
300 private static void appendGetter(PropertyDeclaration forProperty, int index,
301 StringBuilder sb) {
302
303 sb.append("\n\tpublic ").append(forProperty.getType()).append(" ")
304 .append(forProperty.getGetterMethod()).append("() {\n\t\t");
305
306
307 if (forProperty.isShared()) {
308 sb.append(PROPERTY_FIELD_PREFIX).append(index).append(".get(")
309 .append(INDEX_FIELD_NAME).append(", ").append(SHARED_FIELD_PREFIX)
310 .append(index).append(");\n\t\treturn ").append(SHARED_FIELD_PREFIX)
311 .append(index).append(";");
312 } else {
313 if (forProperty.getPropertyImplementation().equals(OBJECT_PROP_NAME)) {
314
315
316
317 sb.append("return (").append(forProperty.getType()).append(") ")
318 .append(PROPERTY_FIELD_PREFIX).append(index).append(".get(")
319 .append(INDEX_FIELD_NAME).append(");");
320 } else {
321 sb.append("return ").append(PROPERTY_FIELD_PREFIX).append(index)
322 .append(".get(").append(INDEX_FIELD_NAME).append(");");
323 }
324 }
325
326 sb.append("\n\t}\n");
327 }
328
329
330
331
332
333
334
335
336
337
338 private static void appendSetter(List<PropertyDeclaration> params,
339 ComponentSpecification spec, StringBuilder sb) {
340
341 Collections.sort(params, new Comparator<PropertyDeclaration>() {
342 @Override
343 public int compare(PropertyDeclaration o1, PropertyDeclaration o2) {
344 return o1.getSetterParameter() - o2.getSetterParameter();
345 }
346 });
347
348 List<? extends PropertyDeclaration> properties = spec.getProperties();
349 String name = params.get(0).getSetterMethod();
350 boolean returnComponent = params.get(0).getSetterReturnsComponent();
351
352
353 if (returnComponent) {
354 sb.append("\n\tpublic ").append(spec.getType());
355 } else {
356 sb.append("\n\tpublic void");
357 }
358 sb.append(' ').append(name).append('(');
359
360
361 boolean first = true;
362 for (PropertyDeclaration p : params) {
363 if (first) {
364 first = false;
365 } else {
366 sb.append(", ");
367 }
368 sb.append(p.getType()).append(' ').append(SETTER_PARAM_PREFIX)
369 .append(properties.indexOf(p));
370 }
371 sb.append(") {\n");
372
373
374 for (PropertyDeclaration p : params) {
375 int idx = properties.indexOf(p);
376 sb.append("\t\t").append(PROPERTY_FIELD_PREFIX).append(idx).append(".set(")
377 .append(INDEX_FIELD_NAME).append(", ").append(SETTER_PARAM_PREFIX)
378 .append(idx).append(");\n");
379 }
380
381 sb.append("\t\t").append(REPO_FIELD_NAME).append('.')
382 .append(UPDATE_VERSION_METHOD).append("(").append(INDEX_FIELD_NAME)
383 .append(");\n");
384
385
386 if (returnComponent) {
387 sb.append("\t\treturn this;\n");
388 }
389 sb.append("\t}\n");
390 }
391 }