1 package at.rseiler.spbee.core.generator;
2
3 import at.rseiler.spbee.core.exception.MultipleObjectsReturned;
4 import at.rseiler.spbee.core.exception.ObjectDoesNotExist;
5 import at.rseiler.spbee.core.pojo.*;
6 import at.rseiler.spbee.core.util.CodeModelUtil;
7 import at.rseiler.spbee.core.util.StringUtil;
8 import com.sun.codemodel.*;
9
10 import javax.annotation.processing.ProcessingEnvironment;
11 import javax.sql.DataSource;
12 import java.io.IOException;
13 import java.util.*;
14
15
16
17
18
19
20 public class DtoGenerator extends AbstractGenerator {
21
22 private static final String SPRING_ANNOTATION_AUTOWIRED = "org.springframework.beans.factory.annotation.Autowired";
23 private static final String SPRING_ANNOTATION_SERVICE = "org.springframework.stereotype.Service";
24
25 private final Properties config;
26 private final Map<String, ResultSetClass> resultSetMap;
27
28 public DtoGenerator(ProcessingEnvironment processingEnv, Properties config, Map<String, ResultSetClass> resultSetMap) {
29 super(processingEnv);
30 this.config = config;
31 this.resultSetMap = resultSetMap;
32 }
33
34
35
36
37
38
39
40
41 public void generateDtoClasses(List<DtoClass> dtoClasses) throws JClassAlreadyExistsException, IOException {
42 for (DtoClass dtoClass : dtoClasses) {
43 DtoClassGeneratorInstance dtoClassGeneratorInstance = new DtoClassGeneratorInstance(config, dtoClass, resultSetMap)
44 .createClass()
45 .createConstructor()
46 .addStoredProcedureMethods();
47
48 generateClass(dtoClassGeneratorInstance.getModel(), dtoClass.getQualifiedClassName());
49 }
50 }
51
52
53
54
55 private static class DtoClassGeneratorInstance {
56
57 private final Properties config;
58 private final DtoClass dtoClass;
59 private final Map<String, ResultSetClass> resultSetMap;
60 private JCodeModel model = new JCodeModel();
61 private JDefinedClass dtoJClass;
62 private JMethod constructor;
63 private JVar dataSource;
64 private Map<String, JFieldVar> spFields = new HashMap<>();
65
66 public DtoClassGeneratorInstance(Properties config, DtoClass dtoClass, Map<String, ResultSetClass> resultSetMap) {
67 this.config = config;
68 this.dtoClass = dtoClass;
69 this.resultSetMap = resultSetMap;
70 }
71
72 public JCodeModel getModel() {
73 return model;
74 }
75
76
77
78
79
80
81
82
83 private DtoClassGeneratorInstance createClass() throws JClassAlreadyExistsException {
84 JPackage dtoJPackage = model._package(dtoClass.getPackage());
85 dtoJClass = dtoJPackage._class(dtoClass.getSimpleClassName());
86 CodeModelUtil.annotateGenerated(dtoJClass);
87 dtoJClass.annotate(model.directClass(SPRING_ANNOTATION_SERVICE));
88 addSuperClassOrInterface();
89 return this;
90 }
91
92
93
94
95
96
97
98 public DtoClassGeneratorInstance createConstructor() {
99 constructor = dtoJClass.constructor(JMod.PUBLIC);
100 constructor.annotate(model.directClass(SPRING_ANNOTATION_AUTOWIRED));
101 dataSource = constructor.param(DataSource.class, "dataSource");
102
103 if (dtoClass.hasDataSourceConstructor()) {
104 constructor.body().add(JExpr.invoke("super").arg(dataSource));
105 }
106
107 return this;
108 }
109
110
111
112
113
114 public DtoClassGeneratorInstance addStoredProcedureMethods() throws JClassAlreadyExistsException {
115 for (StoredProcedureMethod storedProcedureMethod : dtoClass.getStoredProcedureMethods()) {
116 String fieldName = storedProcedureMethod.getDtoFieldName();
117
118 if (!spFields.containsKey(fieldName)) {
119 spFields.put(fieldName, addConstructorField(storedProcedureMethod));
120 }
121
122 addDtoMethod(storedProcedureMethod, spFields.get(fieldName));
123 }
124 return this;
125 }
126
127
128
129
130
131
132
133
134 private void addSuperClassOrInterface() {
135 if (dtoClass.isAnInterface()) {
136 dtoJClass._implements(model.directClass(dtoClass.getSuperQualifiedClassName()));
137 } else {
138 dtoJClass._extends(model.directClass(dtoClass.getSuperQualifiedClassName()));
139 }
140 }
141
142
143
144
145
146
147
148
149
150
151
152
153 private JFieldVar addConstructorField(StoredProcedureMethod storedProcedureMethod) {
154 if (constructor == null) {
155 throw new RuntimeException("Invalid Usage. createConstructor() must be called first.");
156 }
157
158 JClass jClass = model.directClass(storedProcedureMethod.getQualifiedClassName());
159 JFieldVar field = dtoJClass.field(JMod.PRIVATE | JMod.FINAL, jClass, storedProcedureMethod.getDtoFieldName());
160 constructor.body().assign(field, JExpr._new(jClass).arg(dataSource));
161 return field;
162 }
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179 private void addDtoMethod(StoredProcedureMethod storedProcedureMethod, JFieldVar field) throws JClassAlreadyExistsException {
180 JClass returnClass = getReturnClass(storedProcedureMethod);
181 JMethod method = dtoJClass.method(JMod.PUBLIC, returnClass, storedProcedureMethod.getMethodName());
182 addAnnotations(storedProcedureMethod, method);
183 JInvocation execute = getExecute(storedProcedureMethod, field, method);
184 JVar interceptorIdObject = null;
185
186 if (config.containsKey("interceptor")) {
187 JInvocation before = model.ref(config.getProperty("interceptor")).staticInvoke("before");
188 before.arg(JExpr.lit(storedProcedureMethod.getStoredProcedureName()));
189
190 for (JExpression arg : execute.listArgs()) {
191 before.arg(arg);
192 }
193
194 interceptorIdObject = method.body().decl(model.directClass(Object.class.getCanonicalName()), "interceptorIdObject");
195 method.body().assign(interceptorIdObject, before);
196 }
197
198 if ("void".equals(storedProcedureMethod.getReturnTypeInfo().getType())) {
199 method.body().add(execute);
200 addInterceptorCallAfter(storedProcedureMethod, method, execute, interceptorIdObject);
201 } else if (resultSetMap.containsKey(storedProcedureMethod.getReturnTypeInfo().getType())) {
202 multipleResultSets(storedProcedureMethod, returnClass, method, execute, interceptorIdObject);
203 } else {
204 singleResultSet(storedProcedureMethod, returnClass, method, execute, interceptorIdObject);
205 }
206 }
207
208
209
210
211
212
213
214 private JInvocation getExecute(StoredProcedureMethod storedProcedureMethod, JFieldVar field, JMethod method) {
215 JInvocation execute = JExpr.invoke(field, "execute");
216
217 for (Variable variable : storedProcedureMethod.getArguments()) {
218 JVar param = method.param(model.directClass(variable.getTypeInfo().asString()), variable.getName());
219 execute.arg(param);
220 }
221
222 return execute;
223 }
224
225
226
227
228 private JClass getReturnClass(StoredProcedureMethod storedProcedureMethod) {
229 Optional<String> genericType = storedProcedureMethod.getReturnTypeInfo().getGenericType();
230 JClass returnClass = model.ref(storedProcedureMethod.getReturnTypeInfo().getType());
231
232 if (genericType.isPresent()) {
233 returnClass = returnClass.narrow(model.ref(genericType.get()));
234 }
235
236 return returnClass;
237 }
238
239
240
241
242 private void addAnnotations(StoredProcedureMethod storedProcedureMethod, JMethod method) throws JClassAlreadyExistsException {
243 for (AnnotationInfo annotationInfo : storedProcedureMethod.getAnnotations()) {
244 JAnnotationUse jAnnotationUse = method.annotate(model.ref(annotationInfo.getAnnotationType()));
245
246 for (AnnotationValueInfo annotationValueInfo : annotationInfo.getAnnotationValueInfos()) {
247 addAnnotationParam(jAnnotationUse, annotationValueInfo);
248 }
249 }
250 }
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327 private void multipleResultSets(StoredProcedureMethod storedProcedureMethod, JClass returnClass, JMethod method, JInvocation execute, JVar interceptorIdObject) {
328 JVar map = method.body().decl(CodeModelUtil.getMapStringObject(model), "map");
329 method.body().assign(map, execute);
330 addInterceptorCallAfter(storedProcedureMethod, method, execute, interceptorIdObject);
331 JInvocation resultSetsInvoke = JExpr._new(returnClass);
332
333 List<JVar> args = new ArrayList<>();
334 List<ResultSetVariable> variables = resultSetMap.get(storedProcedureMethod.getReturnTypeInfo().getType()).getResultSetVariables();
335
336 for (int i = 0; i < variables.size(); i++) {
337 ResultSetVariable variable = variables.get(i);
338 Optional<String> genericType = variable.getTypeInfo().getGenericType();
339
340 if (genericType.isPresent()) {
341 if (Optional.class.getCanonicalName().equals(variable.getTypeInfo().getType())) {
342 JClass varType = CodeModelUtil.getGenericList(model, variable.getTypeInfo().getGenericType().get());
343 JVar list = method.body().decl(varType, "list" + i);
344 method.body().assign(list, JExpr.cast(varType, map.invoke("get").arg("#result-set-" + i)));
345
346 JVar obj = method.body().decl(model.directClass(variable.getTypeInfo().getType()), "obj" + i);
347 JConditional condition = method.body()._if(list.invoke("size").eq(JExpr.lit(1)));
348 condition._then().assign(obj, model.directClass(Optional.class.getCanonicalName()).staticInvoke("of").arg(list.invoke("get").arg(JExpr.lit(0))));
349
350 condition = condition._elseif(list.invoke("size").eq(JExpr.lit(0)));
351 condition._then().assign(obj, model.directClass(Optional.class.getCanonicalName()).staticInvoke("empty"));
352 condition._else()._throw(JExpr._new(model.directClass(MultipleObjectsReturned.class.getCanonicalName())));
353
354 args.add(obj);
355 } else {
356 JClass varType = model.directClass(variable.getTypeInfo().getType()).narrow(model.directClass(genericType.get()));
357 JVar list = method.body().decl(varType, "list" + i);
358 method.body().assign(list, JExpr.cast(varType, map.invoke("get").arg("#result-set-" + i)));
359 args.add(list);
360 }
361 } else {
362 JClass varType = CodeModelUtil.getGenericList(model, variable.getTypeInfo().getType());
363 JVar list = method.body().decl(varType, "list" + i);
364 method.body().assign(list, JExpr.cast(varType, map.invoke("get").arg("#result-set-" + i)));
365
366 JVar obj = method.body().decl(model.directClass(variable.getTypeInfo().getType()), "obj" + i);
367 JConditional condition = method.body()._if(list.invoke("size").eq(JExpr.lit(1)));
368 condition._then().assign(obj, list.invoke("get").arg(JExpr.lit(0)));
369
370 if (variable.useNullInsteadOfAnException()) {
371 condition = condition._elseif(list.invoke("size").eq(JExpr.lit(0)));
372 condition._then().assign(obj, JExpr._null());
373 condition._else()._throw(JExpr._new(model.directClass(MultipleObjectsReturned.class.getCanonicalName())));
374 } else {
375 condition = condition._elseif(list.invoke("size").eq(JExpr.lit(0)));
376 condition._then()._throw(JExpr._new(model.directClass(ObjectDoesNotExist.class.getCanonicalName())));
377 condition._else()._throw(JExpr._new(model.directClass(MultipleObjectsReturned.class.getCanonicalName())));
378 }
379
380 args.add(obj);
381 }
382 }
383
384 args.forEach(resultSetsInvoke::arg);
385
386 method.body()._return(resultSetsInvoke);
387 }
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456 private void singleResultSet(StoredProcedureMethod storedProcedureMethod, JClass returnClass, JMethod method, JInvocation execute, JVar interceptorIdObject) {
457 if (storedProcedureMethod.getReturnTypeInfo().getGenericType().isPresent()) {
458 if (Optional.class.getCanonicalName().equals(storedProcedureMethod.getReturnTypeInfo().getType())) {
459 String genericClassType = storedProcedureMethod.getReturnTypeInfo().getGenericType().get();
460 JVar list = method.body().decl(CodeModelUtil.getGenericList(model, genericClassType), "list");
461 method.body().assign(list, JExpr.cast(model.ref(List.class).narrow(model.ref(genericClassType)), execute.invoke("get").arg("#result-set-0")));
462 addInterceptorCallAfter(storedProcedureMethod, method, execute, interceptorIdObject);
463 JConditional condition = method.body()._if(list.invoke("size").eq(JExpr.lit(1)));
464 condition._then()._return(model.directClass(Optional.class.getCanonicalName()).staticInvoke("of").arg(list.invoke("get").arg(JExpr.lit(0))));
465 condition = condition._elseif(list.invoke("size").eq(JExpr.lit(0)));
466 condition._then()._return(model.directClass(Optional.class.getCanonicalName()).staticInvoke("empty"));
467 condition._else()._throw(JExpr._new(model.directClass(MultipleObjectsReturned.class.getCanonicalName())));
468 } else {
469 JVar map = method.body().decl(CodeModelUtil.getMapStringObject(model), "map");
470 method.body().assign(map, execute);
471 addInterceptorCallAfter(storedProcedureMethod, method, execute, interceptorIdObject);
472 method.body()._return(JExpr.cast(returnClass, map.invoke("get").arg("#result-set-0")));
473 }
474 } else {
475 JVar list = method.body().decl(CodeModelUtil.getGenericList(model, storedProcedureMethod.getReturnTypeInfo().getType()), "list");
476 method.body().assign(list, JExpr.cast(model.ref(List.class).narrow(returnClass), execute.invoke("get").arg("#result-set-0")));
477 addInterceptorCallAfter(storedProcedureMethod, method, execute, interceptorIdObject);
478 JConditional condition = method.body()._if(list.invoke("size").eq(JExpr.lit(1)));
479 condition._then()._return(list.invoke("get").arg(JExpr.lit(0)));
480
481 if (storedProcedureMethod.useNullInsteadOfAnException()) {
482 condition = condition._elseif(list.invoke("size").eq(JExpr.lit(0)));
483 condition._then()._return(JExpr._null());
484 condition._else()._throw(JExpr._new(model.directClass(MultipleObjectsReturned.class.getCanonicalName())));
485 } else {
486 condition = condition._elseif(list.invoke("size").eq(JExpr.lit(0)));
487 condition._then()._throw(JExpr._new(model.directClass(ObjectDoesNotExist.class.getCanonicalName())));
488 condition._else()._throw(JExpr._new(model.directClass(MultipleObjectsReturned.class.getCanonicalName())));
489 }
490 }
491 }
492
493
494
495
496 private void addInterceptorCallAfter(StoredProcedureMethod storedProcedureMethod, JMethod method, JInvocation execute, JVar interceptorIdObject) {
497 if(interceptorIdObject != null) {
498 JInvocation after = model.ref(config.getProperty("interceptor")).staticInvoke("after");
499 after.arg(interceptorIdObject);
500 after.arg(JExpr.lit(storedProcedureMethod.getStoredProcedureName()));
501
502 for (JExpression arg : execute.listArgs()) {
503 after.arg(arg);
504 }
505
506 method.body().add(after);
507 }
508 }
509
510 private void addAnnotationParam(JAnnotationUse jAnnotationUse, AnnotationValueInfo annotationValueInfo) throws JClassAlreadyExistsException {
511 Object value = annotationValueInfo.getValue();
512 String annotationName = annotationValueInfo.getName();
513
514 switch (annotationValueInfo.getKind()) {
515 case BASIC:
516 if (value instanceof Boolean) {
517 jAnnotationUse.param(annotationName, (Boolean) value);
518 } else if (value instanceof Byte) {
519 jAnnotationUse.param(annotationName, (Byte) value);
520 } else if (value instanceof Character) {
521 jAnnotationUse.param(annotationName, (Character) value);
522 } else if (value instanceof Double) {
523 jAnnotationUse.param(annotationName, (Double) value);
524 } else if (value instanceof Float) {
525 jAnnotationUse.param(annotationName, (Float) value);
526 } else if (value instanceof Long) {
527 jAnnotationUse.param(annotationName, (Long) value);
528 } else if (value instanceof Short) {
529 jAnnotationUse.param(annotationName, (Short) value);
530 } else if (value instanceof Integer) {
531 jAnnotationUse.param(annotationName, (Integer) value);
532 } else if (value instanceof String) {
533 jAnnotationUse.param(annotationName, (String) value);
534 }
535 break;
536 case DECLARED_TYPE:
537 jAnnotationUse.param(annotationName, model.ref(value.toString()));
538 break;
539 case ELEMENT:
540 String qualifiedEnumName = StringUtil.getPackage(annotationValueInfo.getType());
541 JPackage dtoJPackage = new JCodeModel()._package(StringUtil.getPackage(qualifiedEnumName));
542 JDefinedClass definedClass = dtoJPackage._class(StringUtil.getSimpleClassName(qualifiedEnumName));
543 jAnnotationUse.param(annotationName, definedClass.enumConstant(value.toString()));
544 break;
545 case LIST:
546 JAnnotationArrayMember jAnnotationArrayMember = jAnnotationUse.paramArray(annotationName);
547 List<AnnotationValueInfo> list = (List<AnnotationValueInfo>) value;
548
549 for (AnnotationValueInfo valueInfo : list) {
550 addAnnotationArrayMemberParam(jAnnotationArrayMember, valueInfo);
551 }
552 break;
553 default:
554 throw new RuntimeException("Failed to addAnnotationParam, because the kind is unknown: " + annotationValueInfo.getKind());
555 }
556 }
557
558 private void addAnnotationArrayMemberParam(JAnnotationArrayMember jAnnotationArrayMember, AnnotationValueInfo annotationValueInfo) {
559 Object value = annotationValueInfo.getValue();
560
561 try {
562 switch (annotationValueInfo.getKind()) {
563 case BASIC:
564 if (value instanceof Boolean) {
565 jAnnotationArrayMember.param((Boolean) value);
566 } else if (value instanceof Byte) {
567 jAnnotationArrayMember.param((Byte) value);
568 } else if (value instanceof Character) {
569 jAnnotationArrayMember.param((Character) value);
570 } else if (value instanceof Double) {
571 jAnnotationArrayMember.param((Double) value);
572 } else if (value instanceof Float) {
573 jAnnotationArrayMember.param((Float) value);
574 } else if (value instanceof Long) {
575 jAnnotationArrayMember.param((Long) value);
576 } else if (value instanceof Short) {
577 jAnnotationArrayMember.param((Short) value);
578 } else if (value instanceof Integer) {
579 jAnnotationArrayMember.param((Integer) value);
580 } else if (value instanceof String) {
581 jAnnotationArrayMember.param((String) value);
582 }
583 break;
584 case DECLARED_TYPE:
585 jAnnotationArrayMember.param(model.ref(value.toString()));
586 break;
587 case ELEMENT:
588 String qualifiedEnumName = StringUtil.getPackage(value.toString());
589 JPackage dtoJPackage = model._package(StringUtil.getPackage(qualifiedEnumName));
590 JDefinedClass definedClass = dtoJPackage._class(StringUtil.getSimpleClassName(qualifiedEnumName));
591 jAnnotationArrayMember.param(definedClass.enumConstant(value.toString()));
592 break;
593 default:
594 throw new RuntimeException("Failed to addAnnotationParam, because the kind is unknown: " + annotationValueInfo.getKind());
595 }
596 } catch (JClassAlreadyExistsException e) {
597 throw new RuntimeException("JClassAlreadyExistsException", e);
598 }
599 }
600
601 }
602 }