1 package org.springframework.roo.classpath.itd;
2
3 import java.lang.reflect.Modifier;
4 import java.util.List;
5 import java.util.SortedSet;
6 import java.util.TreeSet;
7
8 import org.springframework.roo.classpath.details.AnnotationMetadataUtils;
9 import org.springframework.roo.classpath.details.ConstructorMetadata;
10 import org.springframework.roo.classpath.details.DeclaredFieldAnnotationDetails;
11 import org.springframework.roo.classpath.details.DeclaredMethodAnnotationDetails;
12 import org.springframework.roo.classpath.details.FieldMetadata;
13 import org.springframework.roo.classpath.details.ItdTypeDetails;
14 import org.springframework.roo.classpath.details.MethodMetadata;
15 import org.springframework.roo.classpath.details.annotations.AnnotatedJavaType;
16 import org.springframework.roo.classpath.details.annotations.AnnotationMetadata;
17 import org.springframework.roo.model.ImportRegistrationResolver;
18 import org.springframework.roo.model.ImportRegistrationResolverImpl;
19 import org.springframework.roo.model.JavaSymbolName;
20 import org.springframework.roo.model.JavaType;
21 import org.springframework.roo.support.util.Assert;
22
23
24
25
26
27
28
29
30
31 public class ItdSourceFileComposer {
32
33 private int indentLevel = 0;
34
35 private JavaType introductionTo;
36 private StringBuilder pw = new StringBuilder();
37 private boolean content;
38 private ItdTypeDetails itdTypeDetails;
39 private ImportRegistrationResolver resolver;
40 private JavaType aspect;
41
42
43
44
45
46
47
48 public ItdSourceFileComposer(ItdTypeDetails itdTypeDetails) {
49 Assert.notNull(itdTypeDetails, "ITD type details required");
50
51 this.itdTypeDetails = itdTypeDetails;
52 Assert.notNull(itdTypeDetails.getName(), "Introduction to is required");
53 this.introductionTo = itdTypeDetails.getName();
54
55 this.aspect = itdTypeDetails.getAspect();
56
57
58 resolver = new ImportRegistrationResolverImpl(itdTypeDetails.getAspect().getPackage());
59
60 for (JavaType registeredImport : itdTypeDetails.getRegisteredImports()) {
61
62 if (resolver.isAdditionLegal(registeredImport)) {
63 resolver.addImport(registeredImport);
64 }
65 }
66
67 appendTypeDeclaration();
68 appendExtendsTypes();
69 appendImplementsTypes();
70 appendTypeAnnotations();
71 appendFieldAnnotations();
72 appendMethodAnnotations();
73 appendFields();
74 appendConstructors();
75 appendMethods();
76 appendTerminator();
77
78
79
80 prependCompilationUnitDetails();
81 }
82
83 private void prependCompilationUnitDetails() {
84 StringBuilder topOfFile = new StringBuilder();
85
86
87 if (!aspect.isDefaultPackage()) {
88 topOfFile.append("package " + aspect.getPackage().getFullyQualifiedPackageName() + ";").append(getNewLine());
89 topOfFile.append(getNewLine());
90 }
91
92
93 SortedSet<JavaType> types = new TreeSet<JavaType>();
94 types.addAll(resolver.getRegisteredImports());
95 if (types.size() > 0) {
96 for (JavaType importType : types) {
97 topOfFile.append("import " + importType.getFullyQualifiedTypeName() + ";").append(getNewLine());
98 }
99
100 topOfFile.append(getNewLine());
101 }
102
103
104 topOfFile.append(pw.toString());
105
106
107 this.pw = topOfFile;
108 }
109
110 private void appendTypeDeclaration() {
111 Assert.isTrue(introductionTo.getPackage().equals(aspect.getPackage()), "Aspect and introduction must be in identical packages");
112
113 this.appendIndent();
114 if (itdTypeDetails.isPrivilegedAspect()) {
115 this.append("privileged ");
116 }
117 this.append("aspect " + aspect.getSimpleTypeName() + " {");
118 this.newLine(false);
119 this.indent();
120 this.newLine();
121
122
123 content = false;
124 }
125
126 private void outputAnnotation(AnnotationMetadata annotation) {
127 this.append(AnnotationMetadataUtils.toSourceForm(annotation, resolver));
128 }
129
130 private void appendTypeAnnotations() {
131 List<? extends AnnotationMetadata> typeAnnotations = itdTypeDetails.getTypeAnnotations();
132 if (typeAnnotations == null || typeAnnotations.size() == 0) {
133 return;
134 }
135
136 content = true;
137
138 for (AnnotationMetadata typeAnnotation : typeAnnotations) {
139 this.appendIndent();
140 this.append("declare @type: ");
141 this.append(introductionTo.getSimpleTypeName());
142 this.append(": ");
143 outputAnnotation(typeAnnotation);
144 this.append(";");
145 this.newLine(false);
146 this.newLine();
147 }
148 }
149
150 private void appendFieldAnnotations() {
151 List<DeclaredFieldAnnotationDetails> fieldAnnotations = itdTypeDetails.getFieldAnnotations();
152 if (fieldAnnotations == null || fieldAnnotations.size() == 0) {
153 return;
154 }
155
156 content = true;
157
158 for (DeclaredFieldAnnotationDetails fieldDetails : fieldAnnotations) {
159 this.appendIndent();
160 this.append("declare @field: * ");
161 this.append(introductionTo.getSimpleTypeName());
162 this.append(".");
163 this.append(fieldDetails.getFieldMetadata().getFieldName().getSymbolName());
164 this.append(": ");
165 outputAnnotation(fieldDetails.getFieldAnnotation());
166 this.append(";");
167 this.newLine(false);
168 this.newLine();
169 }
170 }
171
172 private void appendMethodAnnotations() {
173 List<DeclaredMethodAnnotationDetails> methodAnnotations = itdTypeDetails.getMethodAnnotations();
174 if (methodAnnotations == null || methodAnnotations.size() == 0) {
175 return;
176 }
177
178 content = true;
179
180 for (DeclaredMethodAnnotationDetails methodDetails : methodAnnotations) {
181 this.appendIndent();
182 this.append("declare @method: ");
183 this.append(Modifier.toString(methodDetails.getMethodMetadata().getModifier()));
184 this.append(" ");
185 this.append(methodDetails.getMethodMetadata().getReturnType().getNameIncludingTypeParameters());
186 this.append(" ");
187 this.append(introductionTo.getSimpleTypeName());
188 this.append(".");
189 this.append(methodDetails.getMethodMetadata().getMethodName().getSymbolName());
190 this.append("(");
191 for (int i = 0; i < methodDetails.getMethodMetadata().getParameterTypes().size(); i++) {
192 this.append(methodDetails.getMethodMetadata().getParameterTypes().get(i).getJavaType().getNameIncludingTypeParameters(false, resolver));
193 if (i != methodDetails.getMethodMetadata().getParameterTypes().size() - 1) {
194 this.append(",");
195 }
196 }
197 this.append("): ");
198 outputAnnotation(methodDetails.getMethodAnnotation());
199 this.append(";");
200 this.newLine(false);
201 this.newLine();
202 }
203 }
204
205 private void appendExtendsTypes() {
206 List<JavaType> extendsTypes = itdTypeDetails.getExtendsTypes();
207 if (extendsTypes == null || extendsTypes.size() == 0) {
208 return;
209 }
210
211 content = true;
212
213 for (JavaType extendsType : extendsTypes) {
214 this.appendIndent();
215 this.append("declare parents: ");
216 this.append(introductionTo.getSimpleTypeName());
217 this.append(" extends ");
218 if (resolver.isFullyQualifiedFormRequiredAfterAutoImport(extendsType)) {
219 this.append(extendsType.getNameIncludingTypeParameters());
220 } else {
221 this.append(extendsType.getNameIncludingTypeParameters(false, resolver));
222 }
223 this.append(";");
224 this.newLine(false);
225 this.newLine();
226 }
227 }
228
229 private void appendImplementsTypes() {
230 List<JavaType> implementsTypes = itdTypeDetails.getImplementsTypes();
231
232 if (implementsTypes == null || implementsTypes.size() == 0) {
233 return;
234 }
235
236 content = true;
237
238 for (JavaType extendsType : implementsTypes) {
239 this.appendIndent();
240 this.append("declare parents: ");
241 this.append(introductionTo.getSimpleTypeName());
242 this.append(" implements ");
243 if (resolver.isFullyQualifiedFormRequiredAfterAutoImport(extendsType)) {
244 this.append(extendsType.getNameIncludingTypeParameters());
245 } else {
246 this.append(extendsType.getNameIncludingTypeParameters(false, resolver));
247 }
248 this.append(";");
249 this.newLine(false);
250 this.newLine();
251 }
252 }
253
254 private void appendConstructors() {
255 List<? extends ConstructorMetadata> constructors = itdTypeDetails.getDeclaredConstructors();
256 if (constructors == null || constructors.size() == 0) {
257 return;
258 }
259 content = true;
260 for (ConstructorMetadata constructor : constructors) {
261 Assert.isTrue(constructor.getParameterTypes().size() == constructor.getParameterNames().size(), "Mismatched parameter names against parameter types");
262
263
264 for (AnnotationMetadata annotation : constructor.getAnnotations()) {
265 this.appendIndent();
266 outputAnnotation(annotation);
267 this.newLine(false);
268 }
269
270
271 this.appendIndent();
272 if (constructor.getModifier() != 0) {
273 this.append(Modifier.toString(constructor.getModifier()));
274 this.append(" ");
275 }
276 this.append(introductionTo.getSimpleTypeName());
277 this.append(".");
278 this.append("new");
279
280
281 this.append("(");
282 List<AnnotatedJavaType> paramTypes = constructor.getParameterTypes();
283 List<JavaSymbolName> paramNames = constructor.getParameterNames();
284 for (int i = 0 ; i < paramTypes.size(); i++) {
285 AnnotatedJavaType paramType = paramTypes.get(i);
286 JavaSymbolName paramName = paramNames.get(i);
287 for (AnnotationMetadata methodParameterAnnotation : paramType.getAnnotations()) {
288 this.append(AnnotationMetadataUtils.toSourceForm(methodParameterAnnotation));
289 this.append(" ");
290 }
291 this.append(paramType.getJavaType().getNameIncludingTypeParameters(false, resolver));
292 this.append(" ");
293 this.append(paramName.getSymbolName());
294 if (i < paramTypes.size() - 1) {
295 this.append(", ");
296 }
297 }
298 this.append(") {");
299 this.newLine(false);
300 this.indent();
301
302
303 this.append(constructor.getBody());
304 this.indentRemove();
305 this.appendFormalLine("}");
306 this.newLine(false);
307 }
308 }
309
310 private void appendMethods() {
311 List<? extends MethodMetadata> methods = itdTypeDetails.getDeclaredMethods();
312 if (methods == null || methods.size() == 0) {
313 return;
314 }
315 content = true;
316 for (MethodMetadata method : methods) {
317 Assert.isTrue(method.getParameterTypes().size() == method.getParameterNames().size(), "Mismatched parameter names against parameter types");
318
319
320 for (AnnotationMetadata annotation : method.getAnnotations()) {
321 this.appendIndent();
322 outputAnnotation(annotation);
323 this.newLine(false);
324 }
325
326
327 this.appendIndent();
328 if (method.getModifier() != 0) {
329 this.append(Modifier.toString(method.getModifier()));
330 this.append(" ");
331 }
332
333
334 boolean staticMethod = Modifier.isStatic(method.getModifier());
335 this.append(method.getReturnType().getNameIncludingTypeParameters(staticMethod, resolver));
336 this.append(" ");
337 this.append(introductionTo.getSimpleTypeName());
338 this.append(".");
339 this.append(method.getMethodName().getSymbolName());
340
341
342 this.append("(");
343 List<AnnotatedJavaType> paramTypes = method.getParameterTypes();
344 List<JavaSymbolName> paramNames = method.getParameterNames();
345 for (int i = 0 ; i < paramTypes.size(); i++) {
346 AnnotatedJavaType paramType = paramTypes.get(i);
347 JavaSymbolName paramName = paramNames.get(i);
348 for (AnnotationMetadata methodParameterAnnotation : paramType.getAnnotations()) {
349 outputAnnotation(methodParameterAnnotation);
350 this.append(" ");
351 }
352 this.append(paramType.getJavaType().getNameIncludingTypeParameters(false, resolver));
353 this.append(" ");
354 this.append(paramName.getSymbolName());
355 if (i < paramTypes.size() - 1) {
356 this.append(", ");
357 }
358 }
359
360
361 List<JavaType> throwsTypes = method.getThrowsTypes();
362 if (throwsTypes.size() > 0) {
363 this.append(") throws ");
364 for (int i = 0; i < throwsTypes.size(); i++) {
365 this.append(throwsTypes.get(i).getNameIncludingTypeParameters(false, resolver));
366 if (throwsTypes.size() > (i+1)) {
367 this.append(", ");
368 }
369 }
370 this.append(" {");
371 } else {
372 this.append(") {");
373 }
374
375 this.newLine(false);
376 this.indent();
377
378
379 this.append(method.getBody());
380 this.indentRemove();
381 this.appendFormalLine("}");
382 this.newLine();
383 }
384 }
385
386 private void appendFields() {
387 List<? extends FieldMetadata> fields = itdTypeDetails.getDeclaredFields();
388 if (fields == null || fields.size() == 0) {
389 return;
390 }
391 content = true;
392 for (FieldMetadata field : fields) {
393
394
395 for (AnnotationMetadata annotation : field.getAnnotations()) {
396 this.appendIndent();
397 outputAnnotation(annotation);
398 this.newLine(false);
399 }
400
401
402 this.appendIndent();
403 if (field.getModifier() != 0) {
404 this.append(Modifier.toString(field.getModifier()));
405 this.append(" ");
406 }
407 this.append(field.getFieldType().getNameIncludingTypeParameters(false, resolver));
408 this.append(" ");
409 this.append(introductionTo.getSimpleTypeName());
410 this.append(".");
411 this.append(field.getFieldName().getSymbolName());
412
413
414 if (field.getFieldInitializer() != null) {
415 this.append(" = ");
416 this.append(field.getFieldInitializer());
417 }
418
419
420 this.append(";");
421 this.newLine(false);
422 this.newLine();
423 }
424 }
425
426
427
428
429 private ItdSourceFileComposer indent() {
430 indentLevel++;
431 return this;
432 }
433
434
435
436
437 private ItdSourceFileComposer indentRemove() {
438 indentLevel--;
439 return this;
440 }
441
442
443
444
445 private ItdSourceFileComposer newLine() {
446 return newLine(true);
447 }
448
449
450
451
452 private ItdSourceFileComposer newLine(boolean indent) {
453 if (indent) appendIndent();
454
455 pw.append(getNewLine());
456
457 return this;
458 }
459
460 private String getNewLine() {
461
462 return ("\n");
463 }
464
465
466
467
468 private ItdSourceFileComposer append(String message) {
469 if (message != null && !"".equals(message)) {
470 pw.append(message);
471 content = true;
472 }
473 return this;
474 }
475
476
477
478
479 private ItdSourceFileComposer appendFormalLine(String message) {
480 appendIndent();
481 if (message != null && !"".equals(message)) {
482 pw.append(message);
483 content = true;
484 }
485 return newLine(false);
486 }
487
488
489
490
491 private ItdSourceFileComposer appendIndent() {
492 for (int i = 0 ; i < indentLevel; i++) {
493 pw.append(" ");
494 }
495 return this;
496 }
497
498 private void appendTerminator() {
499 Assert.isTrue(this.indentLevel == 1, "Indent level must be 1 (not " + indentLevel + ") to conclude!");
500 this.indentRemove();
501
502
503 boolean contentBefore = content;
504 this.appendFormalLine("}");
505 content = contentBefore;
506
507 }
508
509 public String getOutput() {
510 return pw.toString();
511 }
512
513
514
515
516
517
518 public boolean isContent() {
519 return content;
520 }
521 }