1 package org.springframework.roo.classpath.javaparser.details;
2
3 import japa.parser.JavaParser;
4 import japa.parser.ParseException;
5 import japa.parser.ast.CompilationUnit;
6 import japa.parser.ast.TypeParameter;
7 import japa.parser.ast.body.BodyDeclaration;
8 import japa.parser.ast.body.MethodDeclaration;
9 import japa.parser.ast.body.Parameter;
10 import japa.parser.ast.body.TypeDeclaration;
11 import japa.parser.ast.body.VariableDeclaratorId;
12 import japa.parser.ast.expr.AnnotationExpr;
13 import japa.parser.ast.expr.NameExpr;
14 import japa.parser.ast.stmt.BlockStmt;
15 import japa.parser.ast.type.ClassOrInterfaceType;
16 import japa.parser.ast.type.ReferenceType;
17 import japa.parser.ast.type.Type;
18
19 import java.io.ByteArrayInputStream;
20 import java.lang.reflect.Modifier;
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Set;
26
27 import org.springframework.roo.classpath.details.MethodMetadata;
28 import org.springframework.roo.classpath.details.annotations.AnnotatedJavaType;
29 import org.springframework.roo.classpath.details.annotations.AnnotationMetadata;
30 import org.springframework.roo.classpath.javaparser.CompilationUnitServices;
31 import org.springframework.roo.classpath.javaparser.JavaParserUtils;
32 import org.springframework.roo.model.JavaSymbolName;
33 import org.springframework.roo.model.JavaType;
34 import org.springframework.roo.support.style.ToStringCreator;
35 import org.springframework.roo.support.util.Assert;
36
37
38
39
40
41
42
43
44 public class JavaParserMethodMetadata implements MethodMetadata {
45
46 private List<AnnotationMetadata> annotations = new ArrayList<AnnotationMetadata>();
47 private List<AnnotatedJavaType> parameterTypes = new ArrayList<AnnotatedJavaType>();
48 private List<JavaSymbolName> parameterNames = new ArrayList<JavaSymbolName>();
49 private List<JavaType> throwsTypes = new ArrayList<JavaType>();
50 private JavaType returnType;
51 private JavaSymbolName methodName;
52 private String body;
53 private String declaredByMetadataId;
54 private int modifier;
55
56 public JavaParserMethodMetadata(String declaredByMetadataId, MethodDeclaration methodDeclaration, CompilationUnitServices compilationUnitServices, Set<JavaSymbolName> typeParameters) {
57 Assert.hasText(declaredByMetadataId, "Declared by metadata ID required");
58 Assert.notNull(methodDeclaration, "Method declaration is mandatory");
59 Assert.notNull(compilationUnitServices, "Compilation unit services are required");
60
61
62 this.modifier = JavaParserUtils.getJdkModifier(methodDeclaration.getModifiers());
63
64
65 Set<JavaSymbolName> fullTypeParameters = new HashSet<JavaSymbolName>();
66 fullTypeParameters.addAll(typeParameters);
67 List<TypeParameter> params = methodDeclaration.getTypeParameters();
68 if (params != null) {
69 for (TypeParameter candidate : params) {
70 JavaSymbolName currentTypeParam = new JavaSymbolName(candidate.getName());
71 fullTypeParameters.add(currentTypeParam);
72 }
73 }
74
75
76 Type rt = methodDeclaration.getType();
77 this.returnType = JavaParserUtils.getJavaType(compilationUnitServices, rt, fullTypeParameters);
78
79
80 this.methodName = new JavaSymbolName(methodDeclaration.getName());
81
82
83 this.body = methodDeclaration.getBody() == null ? null : methodDeclaration.getBody().toString();
84
85
86 if (methodDeclaration.getParameters() != null) {
87 for (Parameter p : methodDeclaration.getParameters()) {
88 Type pt = p.getType();
89 JavaType parameterType = JavaParserUtils.getJavaType(compilationUnitServices, pt, fullTypeParameters);
90
91 List<AnnotationExpr> annotationsList = p.getAnnotations();
92 List<AnnotationMetadata> annotations = new ArrayList<AnnotationMetadata>();
93 if (annotationsList != null) {
94 for (AnnotationExpr candidate : annotationsList) {
95 JavaParserAnnotationMetadata md = new JavaParserAnnotationMetadata(candidate, compilationUnitServices);
96 annotations.add(md);
97 }
98 }
99
100 parameterTypes.add(new AnnotatedJavaType(parameterType, annotations));
101 parameterNames.add(new JavaSymbolName(p.getId().getName()));
102 }
103 }
104
105 if (methodDeclaration.getThrows() != null) {
106 for (NameExpr throwsType: methodDeclaration.getThrows()) {
107 throwsTypes.add(new JavaType(JavaParserUtils.getClassOrInterfaceType(throwsType).getName()));
108 }
109 }
110
111 if (methodDeclaration.getAnnotations() != null) {
112 for (AnnotationExpr annotation : methodDeclaration.getAnnotations()) {
113 this.annotations.add(new JavaParserAnnotationMetadata(annotation, compilationUnitServices));
114 }
115 }
116
117 }
118
119 public int getModifier() {
120 return modifier;
121 }
122
123 public String getDeclaredByMetadataId() {
124 return declaredByMetadataId;
125 }
126
127 public List<AnnotationMetadata> getAnnotations() {
128 return Collections.unmodifiableList(annotations);
129 }
130
131 public List<JavaSymbolName> getParameterNames() {
132 return Collections.unmodifiableList(parameterNames);
133 }
134
135 public List<AnnotatedJavaType> getParameterTypes() {
136 return Collections.unmodifiableList(parameterTypes);
137 }
138
139 public JavaType getReturnType() {
140 return returnType;
141 }
142
143 public JavaSymbolName getMethodName() {
144 return methodName;
145 }
146
147 public List<JavaType> getThrowsTypes() {
148 return throwsTypes;
149 }
150
151 public String getBody() {
152 return body;
153 }
154
155 public String toString() {
156 ToStringCreator tsc = new ToStringCreator(this);
157 tsc.append("declaredByMetadataId", declaredByMetadataId);
158 tsc.append("modifier", Modifier.toString(modifier));
159 tsc.append("methodName", methodName);
160 tsc.append("parameterTypes", parameterTypes);
161 tsc.append("parameterNames", parameterNames);
162 tsc.append("returnType", returnType);
163 tsc.append("annotations", annotations);
164 tsc.append("body", body);
165 return tsc.toString();
166 }
167
168 public static void addMethod(CompilationUnitServices compilationUnitServices, List<BodyDeclaration> members, MethodMetadata method, boolean permitFlush, Set<JavaSymbolName> typeParameters) {
169 Assert.notNull(compilationUnitServices, "Compilation unit services required");
170 Assert.notNull(members, "Members required");
171 Assert.notNull(method, "Method required");
172
173 if (typeParameters == null) {
174 typeParameters = new HashSet<JavaSymbolName>();
175 }
176
177
178 Type returnType = null;
179 if (method.getReturnType().isPrimitive()) {
180 returnType = JavaParserUtils.getType(method.getReturnType());
181 } else {
182 NameExpr importedType = JavaParserUtils.importTypeIfRequired(compilationUnitServices.getEnclosingTypeName(), compilationUnitServices.getImports(), method.getReturnType());
183 ClassOrInterfaceType cit = JavaParserUtils.getClassOrInterfaceType(importedType);
184
185
186 if (method.getReturnType().getParameters().size() > 0) {
187 List<Type> typeArgs = new ArrayList<Type>();
188 cit.setTypeArgs(typeArgs);
189 for (JavaType parameter : method.getReturnType().getParameters()) {
190
191
192 typeArgs.add(JavaParserUtils.importParametersForType(compilationUnitServices.getEnclosingTypeName(), compilationUnitServices.getImports(), parameter));
193 }
194 }
195
196
197 if (method.getReturnType().isArray()) {
198 ReferenceType rt = new ReferenceType();
199 rt.setArrayCount(method.getReturnType().getArray());
200 rt.setType(cit);
201 returnType = rt;
202 } else {
203 returnType = cit;
204 }
205 }
206
207
208 MethodDeclaration d = new MethodDeclaration();
209 d.setModifiers(JavaParserUtils.getJavaParserModifier(method.getModifier()));
210 d.setName(method.getMethodName().getSymbolName());
211 d.setType(returnType);
212
213
214 List<AnnotationExpr> annotations = new ArrayList<AnnotationExpr>();
215 d.setAnnotations(annotations);
216 for (AnnotationMetadata annotation : method.getAnnotations()) {
217 JavaParserAnnotationMetadata.addAnnotationToList(compilationUnitServices, annotations, annotation, false);
218 }
219
220
221 List<Parameter> parameters = new ArrayList<Parameter>();
222 d.setParameters(parameters);
223 int index = -1;
224 for (AnnotatedJavaType methodParameter : method.getParameterTypes()) {
225 index++;
226
227
228 List<AnnotationExpr> parameterAnnotations = new ArrayList<AnnotationExpr>();
229
230 for (AnnotationMetadata parameterAnnotation : methodParameter.getAnnotations()) {
231 JavaParserAnnotationMetadata.addAnnotationToList(compilationUnitServices, parameterAnnotations, parameterAnnotation, false);
232 }
233
234
235 String parameterName = method.getParameterNames().get(index).getSymbolName();
236
237
238 Type parameterType = null;
239 if (methodParameter.getJavaType().isPrimitive()) {
240 parameterType = JavaParserUtils.getType(methodParameter.getJavaType());
241 } else {
242 NameExpr importedType = JavaParserUtils.importTypeIfRequired(compilationUnitServices.getEnclosingTypeName(), compilationUnitServices.getImports(), methodParameter.getJavaType());
243 ClassOrInterfaceType cit = JavaParserUtils.getClassOrInterfaceType(importedType);
244
245
246 if (methodParameter.getJavaType().getParameters().size() > 0) {
247 List<Type> typeArgs = new ArrayList<Type>();
248 cit.setTypeArgs(typeArgs);
249 for (JavaType parameter : methodParameter.getJavaType().getParameters()) {
250 NameExpr importedParameterType = JavaParserUtils.importTypeIfRequired(compilationUnitServices.getEnclosingTypeName(), compilationUnitServices.getImports(), parameter);
251 typeArgs.add(JavaParserUtils.getReferenceType(importedParameterType));
252 }
253
254 }
255 parameterType = cit;
256 }
257
258
259 if (method.getThrowsTypes().size() > 0) {
260 List<NameExpr> throwsTypes = new ArrayList<NameExpr>();
261 for (JavaType javaType: method.getThrowsTypes()) {
262 NameExpr importedType = JavaParserUtils.importTypeIfRequired(compilationUnitServices.getEnclosingTypeName(), compilationUnitServices.getImports(), javaType);
263 throwsTypes.add(importedType);
264 }
265 d.setThrows(throwsTypes);
266 }
267
268
269 Parameter p = new Parameter(parameterType, new VariableDeclaratorId(parameterName));
270 p.setAnnotations(parameterAnnotations);
271 parameters.add(p);
272 }
273
274
275 if (method.getBody() == null || method.getBody().length() == 0) {
276
277 if (!Modifier.isAbstract(method.getModifier())) {
278 d.setBody(new BlockStmt());
279 }
280 } else {
281
282
283
284 StringBuilder sb = new StringBuilder();
285 sb.append("class TemporaryClass {\n");
286 sb.append(" public void temporaryMethod() {\n");
287 sb.append(method.getBody());
288 sb.append("\n");
289 sb.append(" }\n");
290 sb.append("}\n");
291 ByteArrayInputStream bais = new ByteArrayInputStream(sb.toString().getBytes());
292 CompilationUnit ci;
293 try {
294 ci = JavaParser.parse(bais);
295 } catch (ParseException pe) {
296 throw new IllegalStateException("Illegal state: JavaParser did not parse correctly", pe);
297 }
298 List<TypeDeclaration> types = ci.getTypes();
299 if (types == null || types.size() != 1) {
300 throw new IllegalArgumentException("Method body invalid");
301 }
302 TypeDeclaration td = types.get(0);
303 List<BodyDeclaration> bodyDeclarations = td.getMembers();
304 if (bodyDeclarations == null || bodyDeclarations.size() != 1) {
305 throw new IllegalStateException("Illegal state: JavaParser did not return body declarations correctly");
306 }
307 BodyDeclaration bd = bodyDeclarations.get(0);
308 if (!(bd instanceof MethodDeclaration)) {
309 throw new IllegalStateException("Illegal state: JavaParser did not return a method declaration correctly");
310 }
311 MethodDeclaration md = (MethodDeclaration) bd;
312 d.setBody(md.getBody());
313 }
314
315
316 for (BodyDeclaration bd : members) {
317 if (bd instanceof MethodDeclaration) {
318
319 MethodDeclaration md = (MethodDeclaration) bd;
320 if (md.getName().equals(d.getName()) && md.getParameters().size() == d.getParameters().size()) {
321
322 JavaParserMethodMetadata jpmm = new JavaParserMethodMetadata(method.getDeclaredByMetadataId(), md, compilationUnitServices, typeParameters);
323 boolean matchesFully = true;
324 for (AnnotatedJavaType existingParameter : jpmm.getParameterTypes()) {
325 if (!existingParameter.getJavaType().equals(method.getParameterTypes().get(index))) {
326 matchesFully = false;
327 break;
328 }
329 }
330 if (matchesFully) {
331 throw new IllegalStateException("Method '" + method.getMethodName().getSymbolName() + "' already exists with identical parameters");
332 }
333 }
334 }
335 }
336
337
338 members.add(d);
339
340 if (permitFlush) {
341 compilationUnitServices.flush();
342 }
343 }
344 }