]>
Commit | Line | Data |
---|---|---|
1b2798f6 EK |
1 | /******************************************************************************* |
2 | * Copyright (c) 2006, 2012 IBM Corporation and others. | |
3 | * All rights reserved. This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License v1.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * http://www.eclipse.org/legal/epl-v10.html | |
7 | * | |
8 | * Contributors: | |
9 | * IBM Corporation - initial API and implementation | |
10 | *******************************************************************************/ | |
11 | package org.eclipse.jdt.internal.corext.refactoring.binary; | |
12 | ||
13 | import java.util.ArrayList; | |
14 | import java.util.List; | |
15 | import java.util.Map; | |
16 | import java.util.TreeMap; | |
17 | ||
18 | import org.eclipse.core.runtime.Assert; | |
19 | import org.eclipse.core.runtime.IProgressMonitor; | |
20 | import org.eclipse.core.runtime.NullProgressMonitor; | |
21 | import org.eclipse.core.runtime.SubProgressMonitor; | |
22 | ||
23 | import org.eclipse.jdt.core.Flags; | |
24 | import org.eclipse.jdt.core.IAnnotatable; | |
25 | import org.eclipse.jdt.core.IAnnotation; | |
26 | import org.eclipse.jdt.core.IField; | |
27 | import org.eclipse.jdt.core.IJavaElement; | |
28 | import org.eclipse.jdt.core.IMember; | |
29 | import org.eclipse.jdt.core.IMemberValuePair; | |
30 | import org.eclipse.jdt.core.IMethod; | |
31 | import org.eclipse.jdt.core.IType; | |
32 | import org.eclipse.jdt.core.ITypeParameter; | |
33 | import org.eclipse.jdt.core.JavaModelException; | |
34 | import org.eclipse.jdt.core.Signature; | |
35 | ||
36 | import org.eclipse.jdt.internal.corext.refactoring.Checks; | |
37 | import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; | |
38 | ||
39 | public class StubCreator { | |
40 | ||
41 | /** The internal string buffer */ | |
42 | protected StringBuffer fBuffer; | |
43 | ||
44 | /** Should stubs for private member be generated as well? */ | |
45 | protected final boolean fStubInvisible; | |
46 | ||
47 | public StubCreator(final boolean stubInvisible) { | |
48 | fStubInvisible= stubInvisible; | |
49 | } | |
50 | ||
51 | protected void appendEnumConstants(final IType type) throws JavaModelException { | |
52 | final IField[] fields= type.getFields(); | |
53 | final List<IField> list= new ArrayList<IField>(fields.length); | |
54 | for (int index= 0; index < fields.length; index++) { | |
55 | final IField field= fields[index]; | |
56 | if (Flags.isEnum(field.getFlags())) | |
57 | list.add(field); | |
58 | } | |
59 | for (int index= 0; index < list.size(); index++) { | |
60 | if (index > 0) | |
61 | fBuffer.append(","); //$NON-NLS-1$ | |
62 | fBuffer.append(list.get(index).getElementName()); | |
63 | } | |
64 | fBuffer.append(";"); //$NON-NLS-1$ | |
65 | } | |
66 | ||
67 | protected void appendExpression(final String signature) { | |
68 | appendExpression(signature, false); | |
69 | } | |
70 | ||
71 | protected void appendExpression(String signature, boolean castNull) { | |
72 | switch (signature.charAt(0)) { | |
73 | case Signature.C_BOOLEAN: | |
74 | fBuffer.append("false"); //$NON-NLS-1$ | |
75 | break; | |
76 | case Signature.C_BYTE: | |
77 | case Signature.C_CHAR: | |
78 | case Signature.C_DOUBLE: | |
79 | case Signature.C_FLOAT: | |
80 | case Signature.C_INT: | |
81 | case Signature.C_LONG: | |
82 | case Signature.C_SHORT: | |
83 | fBuffer.append("0"); //$NON-NLS-1$ | |
84 | break; | |
85 | default: | |
86 | if (castNull) { | |
87 | fBuffer.append("("); //$NON-NLS-1$ | |
88 | fBuffer.append(Signature.toString(signature)); | |
89 | fBuffer.append(")"); //$NON-NLS-1$ | |
90 | } | |
91 | fBuffer.append("null"); //$NON-NLS-1$ | |
92 | break; | |
93 | } | |
94 | } | |
95 | ||
96 | protected void appendFieldDeclaration(final IField field) throws JavaModelException { | |
97 | appendFlags(field); | |
98 | fBuffer.append(" "); //$NON-NLS-1$ | |
99 | final String signature= field.getTypeSignature(); | |
100 | fBuffer.append(Signature.toString(signature)); | |
101 | fBuffer.append(" "); //$NON-NLS-1$ | |
102 | fBuffer.append(field.getElementName()); | |
103 | if (Flags.isFinal(field.getFlags())) { | |
104 | fBuffer.append("="); //$NON-NLS-1$ | |
105 | appendExpression(signature); | |
106 | } | |
107 | fBuffer.append(";"); //$NON-NLS-1$ | |
108 | } | |
109 | ||
110 | protected void appendFlags(final IMember member) throws JavaModelException { | |
111 | if (member instanceof IAnnotatable) | |
112 | for (IAnnotation annotation : ((IAnnotatable) member).getAnnotations()) { | |
113 | appendAnnotation(annotation); | |
114 | } | |
115 | ||
116 | int flags= member.getFlags(); | |
117 | final int kind= member.getElementType(); | |
118 | if (kind == IJavaElement.TYPE) { | |
119 | flags&= ~Flags.AccSuper; | |
120 | final IType type= (IType) member; | |
121 | if (!type.isMember()) | |
122 | flags&= ~Flags.AccPrivate; | |
123 | if (Flags.isEnum(flags)) | |
124 | flags&= ~Flags.AccAbstract; | |
125 | } | |
126 | if (Flags.isEnum(flags)) | |
127 | flags&= ~Flags.AccFinal; | |
128 | if (kind == IJavaElement.METHOD) { | |
129 | flags&= ~Flags.AccVarargs; | |
130 | flags&= ~Flags.AccBridge; | |
131 | } | |
132 | if (flags != 0) | |
133 | fBuffer.append(Flags.toString(flags)); | |
134 | } | |
135 | ||
136 | private void appendAnnotation(IAnnotation annotation) throws JavaModelException { | |
137 | fBuffer.append('@'); | |
138 | fBuffer.append(annotation.getElementName()); | |
139 | fBuffer.append('('); | |
140 | ||
141 | IMemberValuePair[] memberValuePairs= annotation.getMemberValuePairs(); | |
142 | for (IMemberValuePair pair : memberValuePairs) { | |
143 | fBuffer.append(pair.getMemberName()); | |
144 | fBuffer.append('='); | |
145 | appendAnnotationValue(pair.getValue(), pair.getValueKind()); | |
146 | fBuffer.append(','); | |
147 | } | |
148 | if (memberValuePairs.length > 0) | |
149 | fBuffer.deleteCharAt(fBuffer.length() - 1); | |
150 | ||
151 | fBuffer.append(')').append('\n'); | |
152 | } | |
153 | ||
154 | private void appendAnnotationValue(Object value, int valueKind) throws JavaModelException { | |
155 | if (value instanceof Object[]) { | |
156 | Object[] objects= (Object[]) value; | |
157 | fBuffer.append('{'); | |
158 | for (Object object : objects) { | |
159 | appendAnnotationValue(object, valueKind); | |
160 | fBuffer.append(','); | |
161 | } | |
162 | if (objects.length > 0) | |
163 | fBuffer.deleteCharAt(fBuffer.length() - 1); | |
164 | fBuffer.append('}'); | |
165 | ||
166 | } else { | |
167 | switch (valueKind) { | |
168 | case IMemberValuePair.K_ANNOTATION: | |
169 | appendAnnotation((IAnnotation) value); | |
170 | break; | |
171 | case IMemberValuePair.K_STRING: | |
172 | fBuffer.append('"').append(value).append('"'); | |
173 | break; | |
174 | ||
175 | default: | |
176 | fBuffer.append(value); | |
177 | break; | |
178 | } | |
179 | } | |
180 | } | |
181 | ||
182 | protected void appendMembers(final IType type, final IProgressMonitor monitor) throws JavaModelException { | |
183 | try { | |
184 | monitor.beginTask(RefactoringCoreMessages.StubCreationOperation_creating_type_stubs, 1); | |
185 | final IJavaElement[] children= type.getChildren(); | |
186 | for (int index= 0; index < children.length; index++) { | |
187 | final IMember child= (IMember) children[index]; | |
188 | final int flags= child.getFlags(); | |
189 | final boolean isPrivate= Flags.isPrivate(flags); | |
190 | final boolean isDefault= !Flags.isPublic(flags) && !Flags.isProtected(flags) && !isPrivate; | |
191 | final boolean stub= fStubInvisible || (!isPrivate && !isDefault); | |
192 | if (child instanceof IType) { | |
193 | if (stub) | |
194 | appendTypeDeclaration((IType) child, new SubProgressMonitor(monitor, 1)); | |
195 | } else if (child instanceof IField) { | |
196 | if (stub && !Flags.isEnum(flags) && !Flags.isSynthetic(flags)) | |
197 | appendFieldDeclaration((IField) child); | |
198 | } else if (child instanceof IMethod) { | |
199 | final IMethod method= (IMethod) child; | |
200 | final String name= method.getElementName(); | |
201 | if (method.getDeclaringType().isEnum()) { | |
202 | final int count= method.getNumberOfParameters(); | |
203 | if (count == 0 && "values".equals(name)) //$NON-NLS-1$ | |
204 | continue; | |
205 | if (count == 1 && "valueOf".equals(name) && "Ljava.lang.String;".equals(method.getParameterTypes()[0])) //$NON-NLS-1$ //$NON-NLS-2$ | |
206 | continue; | |
207 | if (method.isConstructor()) | |
208 | continue; | |
209 | } | |
210 | boolean skip= !stub || name.equals("<clinit>"); //$NON-NLS-1$ | |
211 | if (method.isConstructor()) | |
212 | skip= false; | |
213 | skip= skip || Flags.isSynthetic(flags) || Flags.isBridge(flags); | |
214 | if (!skip) | |
215 | appendMethodDeclaration(method); | |
216 | } | |
217 | fBuffer.append("\n"); //$NON-NLS-1$ | |
218 | } | |
219 | } finally { | |
220 | monitor.done(); | |
221 | } | |
222 | } | |
223 | ||
224 | @SuppressWarnings("boxing") | |
225 | protected void appendMethodBody(final IMethod method) throws JavaModelException { | |
226 | if (method.isConstructor()) { | |
227 | final IType declaringType= method.getDeclaringType(); | |
228 | String superSignature= declaringType.getSuperclassTypeSignature(); | |
229 | if (superSignature != null) { | |
230 | superSignature= Signature.getTypeErasure(superSignature); | |
231 | final IType superclass= declaringType.getJavaProject().findType(Signature.getSignatureQualifier(superSignature), Signature.getSignatureSimpleName(superSignature)); | |
232 | if (superclass != null) { | |
233 | final IMethod[] superMethods= superclass.getMethods(); | |
234 | ||
235 | // collect super constructors by parameter count | |
236 | Map<Integer, List<IMethod>> superConstructorsByParamCount= new TreeMap<Integer, List<IMethod>>(); | |
237 | boolean multi= false; | |
238 | IMethod superConstructor= null; | |
239 | for (int i= 0; i < superMethods.length; i++) { | |
240 | IMethod superMethod= superMethods[i]; | |
241 | if (superMethod.isConstructor() | |
242 | && !Flags.isPrivate(superMethod.getFlags()) | |
243 | && !(Flags.isPackageDefault(superMethod.getFlags()) && !declaringType.getPackageFragment().equals(superclass.getPackageFragment())) | |
244 | ) { | |
245 | int paramCount= superMethod.getNumberOfParameters(); | |
246 | if (paramCount == 0) { | |
247 | superConstructor= superMethod; | |
248 | break; | |
249 | } | |
250 | List<IMethod> constructors= superConstructorsByParamCount.get(paramCount); | |
251 | if (constructors == null) { | |
252 | constructors= new ArrayList<IMethod>(); | |
253 | superConstructorsByParamCount.put(paramCount, constructors); | |
254 | } | |
255 | constructors.add(superMethod); | |
256 | } | |
257 | } | |
258 | if (superConstructor == null && superConstructorsByParamCount.size() > 0) { | |
259 | // look for constructors without exceptions and without parameters | |
260 | done: for (List<IMethod> constructors : superConstructorsByParamCount.values()) { | |
261 | for (IMethod constructor : constructors) { | |
262 | if (constructor.getExceptionTypes().length == 0) { | |
263 | superConstructor= constructor; | |
264 | multi= constructors.size() != 1; | |
265 | if (multi) | |
266 | break; | |
267 | else | |
268 | break done; | |
269 | } | |
270 | if (superConstructor == null) { | |
271 | superConstructor= constructor; | |
272 | multi= constructors.size() != 1; | |
273 | } | |
274 | } | |
275 | } | |
276 | if (superConstructor == null) { | |
277 | // give up, get first | |
278 | superConstructor= superConstructorsByParamCount.values().iterator().next().get(0); | |
279 | multi= true; | |
280 | } | |
281 | } | |
282 | if (superConstructor != null) { | |
283 | final String[] superParameters= superConstructor.getParameterTypes(); | |
284 | final int paramLength= superParameters.length; | |
285 | fBuffer.append("super("); //$NON-NLS-1$ | |
286 | if (paramLength != 0) { | |
287 | for (int index= 0; index < paramLength; index++) { | |
288 | if (index > 0) | |
289 | fBuffer.append(","); //$NON-NLS-1$ | |
290 | appendExpression(superParameters[index], multi); | |
291 | } | |
292 | } | |
293 | fBuffer.append(");"); //$NON-NLS-1$ | |
294 | } | |
295 | } | |
296 | } | |
297 | } else { | |
298 | String returnType= method.getReturnType(); | |
299 | if (!Signature.SIG_VOID.equals(returnType)) { | |
300 | fBuffer.append("return "); //$NON-NLS-1$ | |
301 | appendExpression(returnType); | |
302 | fBuffer.append(";"); //$NON-NLS-1$ | |
303 | } | |
304 | } | |
305 | } | |
306 | ||
307 | protected void appendMethodDeclaration(final IMethod method) throws JavaModelException { | |
308 | appendFlags(method); | |
309 | fBuffer.append(" "); //$NON-NLS-1$ | |
310 | final ITypeParameter[] parameters= method.getTypeParameters(); | |
311 | if (parameters.length > 0) { | |
312 | appendTypeParameters(parameters); | |
313 | fBuffer.append(" "); //$NON-NLS-1$ | |
314 | } | |
315 | final String returnType= method.getReturnType(); | |
316 | if (!method.isConstructor()) { | |
317 | fBuffer.append(Signature.toString(returnType)); | |
318 | fBuffer.append(" "); //$NON-NLS-1$ | |
319 | } | |
320 | fBuffer.append(method.getElementName()); | |
321 | fBuffer.append("("); //$NON-NLS-1$ | |
322 | final String[] parameterTypes= method.getParameterTypes(); | |
323 | final int flags= method.getFlags(); | |
324 | final boolean varargs= Flags.isVarargs(flags); | |
325 | final int parameterLength= parameterTypes.length; | |
326 | for (int index= 0; index < parameterLength; index++) { | |
327 | if (index > 0) | |
328 | fBuffer.append(","); //$NON-NLS-1$ | |
329 | fBuffer.append(Signature.toString(parameterTypes[index])); | |
330 | if (varargs && index == parameterLength - 1) { | |
331 | final int length= fBuffer.length(); | |
332 | if (length >= 2 && fBuffer.indexOf("[]", length - 2) >= 0) //$NON-NLS-1$ | |
333 | fBuffer.setLength(length - 2); | |
334 | fBuffer.append("..."); //$NON-NLS-1$ | |
335 | } | |
336 | fBuffer.append(" "); //$NON-NLS-1$ | |
337 | appendMethodParameterName(method, index); | |
338 | } | |
339 | fBuffer.append(")"); //$NON-NLS-1$ | |
340 | final String[] exceptionTypes= method.getExceptionTypes(); | |
341 | final int exceptionLength= exceptionTypes.length; | |
342 | if (exceptionLength > 0) | |
343 | fBuffer.append(" throws "); //$NON-NLS-1$ | |
344 | for (int index= 0; index < exceptionLength; index++) { | |
345 | if (index > 0) | |
346 | fBuffer.append(","); //$NON-NLS-1$ | |
347 | fBuffer.append(Signature.toString(exceptionTypes[index])); | |
348 | } | |
349 | if (Flags.isAbstract(flags) || Flags.isNative(flags)) | |
350 | fBuffer.append(";"); //$NON-NLS-1$ | |
351 | else { | |
352 | fBuffer.append("{\n"); //$NON-NLS-1$ | |
353 | appendMethodBody(method); | |
354 | fBuffer.append("}"); //$NON-NLS-1$ | |
355 | } | |
356 | } | |
357 | ||
358 | /** | |
359 | * Appends a parameter name | |
360 | * | |
361 | * @param method the method | |
362 | * @param index the index of the parameter | |
363 | */ | |
364 | protected void appendMethodParameterName(IMethod method, int index) { | |
365 | fBuffer.append("a"); //$NON-NLS-1$ | |
366 | fBuffer.append(index); | |
367 | } | |
368 | ||
369 | protected void appendSuperInterfaceTypes(final IType type) throws JavaModelException { | |
370 | final String[] signatures= type.getSuperInterfaceTypeSignatures(); | |
371 | if (signatures.length > 0) { | |
372 | if (type.isInterface()) | |
373 | fBuffer.append(" extends "); //$NON-NLS-1$ | |
374 | else | |
375 | fBuffer.append(" implements "); //$NON-NLS-1$ | |
376 | } | |
377 | for (int index= 0; index < signatures.length; index++) { | |
378 | if (index > 0) | |
379 | fBuffer.append(","); //$NON-NLS-1$ | |
380 | fBuffer.append(Signature.toString(signatures[index])); | |
381 | } | |
382 | } | |
383 | ||
384 | protected void appendTopLevelType(final IType type, IProgressMonitor subProgressMonitor) throws JavaModelException { | |
385 | String packageName= type.getPackageFragment().getElementName(); | |
386 | if (packageName.length() > 0) { | |
387 | fBuffer.append("package "); //$NON-NLS-1$ | |
388 | fBuffer.append(packageName); | |
389 | fBuffer.append(";\n"); //$NON-NLS-1$ | |
390 | } | |
391 | appendTypeDeclaration(type, subProgressMonitor); | |
392 | } | |
393 | ||
394 | protected void appendTypeDeclaration(final IType type, final IProgressMonitor monitor) throws JavaModelException { | |
395 | try { | |
396 | monitor.beginTask(RefactoringCoreMessages.StubCreationOperation_creating_type_stubs, 1); | |
397 | if (type.isAnnotation()) { | |
398 | appendFlags(type); | |
399 | fBuffer.append(" @interface "); //$NON-NLS-1$ | |
400 | fBuffer.append(type.getElementName()); | |
401 | fBuffer.append("{\n"); //$NON-NLS-1$ | |
402 | appendMembers(type, new SubProgressMonitor(monitor, 1)); | |
403 | fBuffer.append("}"); //$NON-NLS-1$ | |
404 | } else if (type.isInterface()) { | |
405 | appendFlags(type); | |
406 | fBuffer.append(" interface "); //$NON-NLS-1$ | |
407 | fBuffer.append(type.getElementName()); | |
408 | appendTypeParameters(type.getTypeParameters()); | |
409 | appendSuperInterfaceTypes(type); | |
410 | fBuffer.append("{\n"); //$NON-NLS-1$ | |
411 | appendMembers(type, new SubProgressMonitor(monitor, 1)); | |
412 | fBuffer.append("}"); //$NON-NLS-1$ | |
413 | } else if (type.isClass()) { | |
414 | appendFlags(type); | |
415 | fBuffer.append(" class "); //$NON-NLS-1$ | |
416 | fBuffer.append(type.getElementName()); | |
417 | appendTypeParameters(type.getTypeParameters()); | |
418 | final String signature= type.getSuperclassTypeSignature(); | |
419 | if (signature != null) { | |
420 | fBuffer.append(" extends "); //$NON-NLS-1$ | |
421 | fBuffer.append(Signature.toString(signature)); | |
422 | } | |
423 | appendSuperInterfaceTypes(type); | |
424 | fBuffer.append("{\n"); //$NON-NLS-1$ | |
425 | appendMembers(type, new SubProgressMonitor(monitor, 1)); | |
426 | fBuffer.append("}"); //$NON-NLS-1$ | |
427 | } else if (type.isEnum()) { | |
428 | appendFlags(type); | |
429 | fBuffer.append(" enum "); //$NON-NLS-1$ | |
430 | fBuffer.append(type.getElementName()); | |
431 | appendSuperInterfaceTypes(type); | |
432 | fBuffer.append("{\n"); //$NON-NLS-1$ | |
433 | appendEnumConstants(type); | |
434 | appendMembers(type, new SubProgressMonitor(monitor, 1)); | |
435 | fBuffer.append("}"); //$NON-NLS-1$ | |
436 | } | |
437 | } finally { | |
438 | monitor.done(); | |
439 | } | |
440 | } | |
441 | ||
442 | protected void appendTypeParameters(final ITypeParameter[] parameters) throws JavaModelException { | |
443 | final int length= parameters.length; | |
444 | if (length > 0) | |
445 | fBuffer.append("<"); //$NON-NLS-1$ | |
446 | for (int index= 0; index < length; index++) { | |
447 | if (index > 0) | |
448 | fBuffer.append(","); //$NON-NLS-1$ | |
449 | final ITypeParameter parameter= parameters[index]; | |
450 | fBuffer.append(parameter.getElementName()); | |
451 | final String[] bounds= parameter.getBounds(); | |
452 | final int size= bounds.length; | |
453 | if (size > 0) | |
454 | fBuffer.append(" extends "); //$NON-NLS-1$ | |
455 | for (int offset= 0; offset < size; offset++) { | |
456 | if (offset > 0) | |
457 | fBuffer.append(" & "); //$NON-NLS-1$ | |
458 | fBuffer.append(bounds[offset]); | |
459 | } | |
460 | } | |
461 | if (length > 0) | |
462 | fBuffer.append(">"); //$NON-NLS-1$ | |
463 | } | |
464 | ||
465 | /** | |
466 | * Creates and returns a stub for the given top-level type. | |
467 | * | |
468 | * @param topLevelType the top-level type | |
469 | * @param monitor the progress monitor, can be <code>null</code> | |
470 | * @return the source stub | |
471 | * @throws JavaModelException if this element does not exist or if an exception occurs while | |
472 | * accessing its corresponding resource | |
473 | */ | |
474 | public String createStub(IType topLevelType, IProgressMonitor monitor) throws JavaModelException { | |
475 | Assert.isTrue(Checks.isTopLevel(topLevelType)); | |
476 | if (monitor == null) | |
477 | monitor= new NullProgressMonitor(); | |
478 | ||
479 | fBuffer= new StringBuffer(2046); | |
480 | appendTopLevelType(topLevelType, monitor); | |
481 | String result= fBuffer.toString(); | |
482 | fBuffer= null; | |
483 | return result; | |
484 | } | |
485 | ||
486 | } |