]> git.uio.no Git - ifi-stolz-refaktor.git/blame - case-study/jdt-before/core extension/org/eclipse/jdt/internal/corext/codemanipulation/StubUtility.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-before / core extension / org / eclipse / jdt / internal / corext / codemanipulation / StubUtility.java
CommitLineData
1b2798f6
EK
1/*******************************************************************************
2 * Copyright (c) 2000, 2011 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 * John Kaplan, johnkaplantech@gmail.com - 108071 [code templates] template for body of newly created class
11 *******************************************************************************/
12package org.eclipse.jdt.internal.corext.codemanipulation;
13
14import java.io.IOException;
15import java.lang.reflect.Modifier;
16import java.util.AbstractList;
17import java.util.ArrayList;
18import java.util.Collection;
19import java.util.Collections;
20import java.util.HashSet;
21import java.util.LinkedHashSet;
22import java.util.List;
23import java.util.Set;
24import java.util.StringTokenizer;
25
26import org.eclipse.core.runtime.CoreException;
27import org.eclipse.core.runtime.IStatus;
28import org.eclipse.core.runtime.Platform;
29import org.eclipse.core.runtime.Status;
30import org.eclipse.core.runtime.preferences.IScopeContext;
31import org.eclipse.core.runtime.preferences.InstanceScope;
32
33import org.eclipse.core.resources.IProject;
34import org.eclipse.core.resources.ProjectScope;
35
36import org.eclipse.text.edits.DeleteEdit;
37import org.eclipse.text.edits.MalformedTreeException;
38import org.eclipse.text.edits.MultiTextEdit;
39
40import org.eclipse.jface.text.BadLocationException;
41import org.eclipse.jface.text.Document;
42import org.eclipse.jface.text.IDocument;
43import org.eclipse.jface.text.IRegion;
44import org.eclipse.jface.text.templates.Template;
45import org.eclipse.jface.text.templates.TemplateBuffer;
46import org.eclipse.jface.text.templates.TemplateException;
47import org.eclipse.jface.text.templates.TemplateVariable;
48import org.eclipse.jface.text.templates.persistence.TemplatePersistenceData;
49import org.eclipse.jface.text.templates.persistence.TemplateStore;
50
51import org.eclipse.jdt.core.Flags;
52import org.eclipse.jdt.core.IBuffer;
53import org.eclipse.jdt.core.ICompilationUnit;
54import org.eclipse.jdt.core.IField;
55import org.eclipse.jdt.core.IJavaElement;
56import org.eclipse.jdt.core.IJavaProject;
57import org.eclipse.jdt.core.IMethod;
58import org.eclipse.jdt.core.IOpenable;
59import org.eclipse.jdt.core.IPackageFragment;
60import org.eclipse.jdt.core.IParent;
61import org.eclipse.jdt.core.ISourceReference;
62import org.eclipse.jdt.core.IType;
63import org.eclipse.jdt.core.ITypeParameter;
64import org.eclipse.jdt.core.ITypeRoot;
65import org.eclipse.jdt.core.JavaCore;
66import org.eclipse.jdt.core.JavaModelException;
67import org.eclipse.jdt.core.NamingConventions;
68import org.eclipse.jdt.core.Signature;
69import org.eclipse.jdt.core.dom.AST;
70import org.eclipse.jdt.core.dom.ASTNode;
71import org.eclipse.jdt.core.dom.ASTParser;
72import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
73import org.eclipse.jdt.core.dom.ArrayType;
74import org.eclipse.jdt.core.dom.CastExpression;
75import org.eclipse.jdt.core.dom.ClassInstanceCreation;
76import org.eclipse.jdt.core.dom.CompilationUnit;
77import org.eclipse.jdt.core.dom.ConstructorInvocation;
78import org.eclipse.jdt.core.dom.Expression;
79import org.eclipse.jdt.core.dom.FieldAccess;
80import org.eclipse.jdt.core.dom.IBinding;
81import org.eclipse.jdt.core.dom.IMethodBinding;
82import org.eclipse.jdt.core.dom.ITypeBinding;
83import org.eclipse.jdt.core.dom.IVariableBinding;
84import org.eclipse.jdt.core.dom.MethodDeclaration;
85import org.eclipse.jdt.core.dom.MethodInvocation;
86import org.eclipse.jdt.core.dom.Name;
87import org.eclipse.jdt.core.dom.NumberLiteral;
88import org.eclipse.jdt.core.dom.ParameterizedType;
89import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
90import org.eclipse.jdt.core.dom.StringLiteral;
91import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
92import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
93import org.eclipse.jdt.core.dom.SuperMethodInvocation;
94import org.eclipse.jdt.core.dom.Type;
95import org.eclipse.jdt.core.dom.TypeParameter;
96import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
97import org.eclipse.jdt.core.formatter.IndentManipulation;
98
99import org.eclipse.jdt.internal.corext.dom.ASTNodes;
100import org.eclipse.jdt.internal.corext.dom.Bindings;
101import org.eclipse.jdt.internal.corext.template.java.CodeTemplateContext;
102import org.eclipse.jdt.internal.corext.template.java.CodeTemplateContextType;
103import org.eclipse.jdt.internal.corext.util.Strings;
104
105import org.eclipse.jdt.ui.CodeStyleConfiguration;
106import org.eclipse.jdt.ui.PreferenceConstants;
107
108import org.eclipse.jdt.internal.ui.JavaPlugin;
109import org.eclipse.jdt.internal.ui.JavaUIStatus;
110import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
111import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;
112import org.eclipse.jdt.internal.ui.viewsupport.ProjectTemplateStore;
113
114public class StubUtility {
115
116 private static final String[] EMPTY= new String[0];
117
118 private static final Set<String> VALID_TYPE_BODY_TEMPLATES;
119 static {
120 VALID_TYPE_BODY_TEMPLATES= new HashSet<String>();
121 VALID_TYPE_BODY_TEMPLATES.add(CodeTemplateContextType.CLASSBODY_ID);
122 VALID_TYPE_BODY_TEMPLATES.add(CodeTemplateContextType.INTERFACEBODY_ID);
123 VALID_TYPE_BODY_TEMPLATES.add(CodeTemplateContextType.ENUMBODY_ID);
124 VALID_TYPE_BODY_TEMPLATES.add(CodeTemplateContextType.ANNOTATIONBODY_ID);
125 }
126
127 /*
128 * Don't use this method directly, use CodeGeneration.
129 */
130 public static String getMethodBodyContent(boolean isConstructor, IJavaProject project, String destTypeName, String methodName, String bodyStatement, String lineDelimiter) throws CoreException {
131 String templateName= isConstructor ? CodeTemplateContextType.CONSTRUCTORSTUB_ID : CodeTemplateContextType.METHODSTUB_ID;
132 Template template= getCodeTemplate(templateName, project);
133 if (template == null) {
134 return bodyStatement;
135 }
136 CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), project, lineDelimiter);
137 context.setVariable(CodeTemplateContextType.ENCLOSING_METHOD, methodName);
138 context.setVariable(CodeTemplateContextType.ENCLOSING_TYPE, destTypeName);
139 context.setVariable(CodeTemplateContextType.BODY_STATEMENT, bodyStatement);
140 String str= evaluateTemplate(context, template, new String[] { CodeTemplateContextType.BODY_STATEMENT });
141 if (str == null && !Strings.containsOnlyWhitespaces(bodyStatement)) {
142 return bodyStatement;
143 }
144 return str;
145 }
146
147 /*
148 * Don't use this method directly, use CodeGeneration.
149 */
150 public static String getGetterMethodBodyContent(IJavaProject project, String destTypeName, String methodName, String fieldName, String lineDelimiter) throws CoreException {
151 String templateName= CodeTemplateContextType.GETTERSTUB_ID;
152 Template template= getCodeTemplate(templateName, project);
153 if (template == null) {
154 return null;
155 }
156 CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), project, lineDelimiter);
157 context.setVariable(CodeTemplateContextType.ENCLOSING_METHOD, methodName);
158 context.setVariable(CodeTemplateContextType.ENCLOSING_TYPE, destTypeName);
159 context.setVariable(CodeTemplateContextType.FIELD, fieldName);
160
161 return evaluateTemplate(context, template);
162 }
163
164 /*
165 * Don't use this method directly, use CodeGeneration.
166 */
167 public static String getSetterMethodBodyContent(IJavaProject project, String destTypeName, String methodName, String fieldName, String paramName, String lineDelimiter) throws CoreException {
168 String templateName= CodeTemplateContextType.SETTERSTUB_ID;
169 Template template= getCodeTemplate(templateName, project);
170 if (template == null) {
171 return null;
172 }
173 CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), project, lineDelimiter);
174 context.setVariable(CodeTemplateContextType.ENCLOSING_METHOD, methodName);
175 context.setVariable(CodeTemplateContextType.ENCLOSING_TYPE, destTypeName);
176 context.setVariable(CodeTemplateContextType.FIELD, fieldName);
177 context.setVariable(CodeTemplateContextType.FIELD_TYPE, fieldName);
178 context.setVariable(CodeTemplateContextType.PARAM, paramName);
179
180 return evaluateTemplate(context, template);
181 }
182
183 public static String getCatchBodyContent(ICompilationUnit cu, String exceptionType, String variableName, ASTNode locationInAST, String lineDelimiter) throws CoreException {
184 String enclosingType= ""; //$NON-NLS-1$
185 String enclosingMethod= ""; //$NON-NLS-1$
186
187 if (locationInAST != null) {
188 MethodDeclaration parentMethod= ASTResolving.findParentMethodDeclaration(locationInAST);
189 if (parentMethod != null) {
190 enclosingMethod= parentMethod.getName().getIdentifier();
191 locationInAST= parentMethod;
192 }
193 ASTNode parentType= ASTResolving.findParentType(locationInAST);
194 if (parentType instanceof AbstractTypeDeclaration) {
195 enclosingType= ((AbstractTypeDeclaration)parentType).getName().getIdentifier();
196 }
197 }
198 return getCatchBodyContent(cu, exceptionType, variableName, enclosingType, enclosingMethod, lineDelimiter);
199 }
200
201
202 public static String getCatchBodyContent(ICompilationUnit cu, String exceptionType, String variableName, String enclosingType, String enclosingMethod, String lineDelimiter) throws CoreException {
203 Template template= getCodeTemplate(CodeTemplateContextType.CATCHBLOCK_ID, cu.getJavaProject());
204 if (template == null) {
205 return null;
206 }
207
208 CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), cu.getJavaProject(), lineDelimiter);
209 context.setVariable(CodeTemplateContextType.ENCLOSING_TYPE, enclosingType);
210 context.setVariable(CodeTemplateContextType.ENCLOSING_METHOD, enclosingMethod);
211 context.setVariable(CodeTemplateContextType.EXCEPTION_TYPE, exceptionType);
212 context.setVariable(CodeTemplateContextType.EXCEPTION_VAR, variableName);
213 return evaluateTemplate(context, template);
214 }
215
216 /*
217 * Don't use this method directly, use CodeGeneration.
218 * @see org.eclipse.jdt.ui.CodeGeneration#getCompilationUnitContent(ICompilationUnit, String, String, String, String)
219 */
220 public static String getCompilationUnitContent(ICompilationUnit cu, String fileComment, String typeComment, String typeContent, String lineDelimiter) throws CoreException {
221 IPackageFragment pack= (IPackageFragment)cu.getParent();
222 String packDecl= pack.isDefaultPackage() ? "" : "package " + pack.getElementName() + ';'; //$NON-NLS-1$ //$NON-NLS-2$
223 return getCompilationUnitContent(cu, packDecl, fileComment, typeComment, typeContent, lineDelimiter);
224 }
225
226 public static String getCompilationUnitContent(ICompilationUnit cu, String packDecl, String fileComment, String typeComment, String typeContent, String lineDelimiter) throws CoreException {
227 Template template= getCodeTemplate(CodeTemplateContextType.NEWTYPE_ID, cu.getJavaProject());
228 if (template == null) {
229 return null;
230 }
231
232 IJavaProject project= cu.getJavaProject();
233 CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), project, lineDelimiter);
234 context.setCompilationUnitVariables(cu);
235 context.setVariable(CodeTemplateContextType.PACKAGE_DECLARATION, packDecl);
236 context.setVariable(CodeTemplateContextType.TYPE_COMMENT, typeComment != null ? typeComment : ""); //$NON-NLS-1$
237 context.setVariable(CodeTemplateContextType.FILE_COMMENT, fileComment != null ? fileComment : ""); //$NON-NLS-1$
238 context.setVariable(CodeTemplateContextType.TYPE_DECLARATION, typeContent);
239 context.setVariable(CodeTemplateContextType.TYPENAME, JavaCore.removeJavaLikeExtension(cu.getElementName()));
240
241 String[] fullLine= { CodeTemplateContextType.PACKAGE_DECLARATION, CodeTemplateContextType.FILE_COMMENT, CodeTemplateContextType.TYPE_COMMENT };
242 return evaluateTemplate(context, template, fullLine);
243 }
244
245
246 /*
247 * Don't use this method directly, use CodeGeneration.
248 * @see org.eclipse.jdt.ui.CodeGeneration#getFileComment(ICompilationUnit, String)
249 */
250 public static String getFileComment(ICompilationUnit cu, String lineDelimiter) throws CoreException {
251 Template template= getCodeTemplate(CodeTemplateContextType.FILECOMMENT_ID, cu.getJavaProject());
252 if (template == null) {
253 return null;
254 }
255
256 IJavaProject project= cu.getJavaProject();
257 CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), project, lineDelimiter);
258 context.setCompilationUnitVariables(cu);
259 context.setVariable(CodeTemplateContextType.TYPENAME, JavaCore.removeJavaLikeExtension(cu.getElementName()));
260 return evaluateTemplate(context, template);
261 }
262
263 /*
264 * Don't use this method directly, use CodeGeneration.
265 * @see org.eclipse.jdt.ui.CodeGeneration#getTypeComment(ICompilationUnit, String, String[], String)
266 */
267 public static String getTypeComment(ICompilationUnit cu, String typeQualifiedName, String[] typeParameterNames, String lineDelim) throws CoreException {
268 Template template= getCodeTemplate(CodeTemplateContextType.TYPECOMMENT_ID, cu.getJavaProject());
269 if (template == null) {
270 return null;
271 }
272 CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), cu.getJavaProject(), lineDelim);
273 context.setCompilationUnitVariables(cu);
274 context.setVariable(CodeTemplateContextType.ENCLOSING_TYPE, Signature.getQualifier(typeQualifiedName));
275 context.setVariable(CodeTemplateContextType.TYPENAME, Signature.getSimpleName(typeQualifiedName));
276
277 TemplateBuffer buffer;
278 try {
279 buffer= context.evaluate(template);
280 } catch (BadLocationException e) {
281 throw new CoreException(Status.CANCEL_STATUS);
282 } catch (TemplateException e) {
283 throw new CoreException(Status.CANCEL_STATUS);
284 }
285 String str= buffer.getString();
286 if (Strings.containsOnlyWhitespaces(str)) {
287 return null;
288 }
289
290 TemplateVariable position= findVariable(buffer, CodeTemplateContextType.TAGS); // look if Javadoc tags have to be added
291 if (position == null) {
292 return str;
293 }
294
295 IDocument document= new Document(str);
296 int[] tagOffsets= position.getOffsets();
297 for (int i= tagOffsets.length - 1; i >= 0; i--) { // from last to first
298 try {
299 insertTag(document, tagOffsets[i], position.getLength(), EMPTY, EMPTY, null, typeParameterNames, false, lineDelim);
300 } catch (BadLocationException e) {
301 throw new CoreException(JavaUIStatus.createError(IStatus.ERROR, e));
302 }
303 }
304 return document.get();
305 }
306
307 /*
308 * Returns the parameters type names used in see tags. Currently, these are always fully qualified.
309 */
310 public static String[] getParameterTypeNamesForSeeTag(IMethodBinding binding) {
311 ITypeBinding[] typeBindings= binding.getParameterTypes();
312 String[] result= new String[typeBindings.length];
313 for (int i= 0; i < result.length; i++) {
314 ITypeBinding curr= typeBindings[i];
315 if (curr.isTypeVariable()) {
316 curr= curr.getErasure(); // in Javadoc only use type variable erasure
317 }
318 curr= curr.getTypeDeclaration(); // no parameterized types
319 result[i]= curr.getQualifiedName();
320 }
321 return result;
322 }
323
324 /*
325 * Returns the parameters type names used in see tags. Currently, these are always fully qualified.
326 */
327 private static String[] getParameterTypeNamesForSeeTag(IMethod overridden) {
328 try {
329 ASTParser parser= ASTParser.newParser(ASTProvider.SHARED_AST_LEVEL);
330 parser.setProject(overridden.getJavaProject());
331 IBinding[] bindings= parser.createBindings(new IJavaElement[] { overridden }, null);
332 if (bindings.length == 1 && bindings[0] instanceof IMethodBinding) {
333 return getParameterTypeNamesForSeeTag((IMethodBinding)bindings[0]);
334 }
335 } catch (IllegalStateException e) {
336 // method does not exist
337 }
338 // fall back code. Not good for generic methods!
339 String[] paramTypes= overridden.getParameterTypes();
340 String[] paramTypeNames= new String[paramTypes.length];
341 for (int i= 0; i < paramTypes.length; i++) {
342 paramTypeNames[i]= Signature.toString(Signature.getTypeErasure(paramTypes[i]));
343 }
344 return paramTypeNames;
345 }
346
347 private static String getSeeTag(String declaringClassQualifiedName, String methodName, String[] parameterTypesQualifiedNames) {
348 StringBuffer buf= new StringBuffer();
349 buf.append("@see "); //$NON-NLS-1$
350 buf.append(declaringClassQualifiedName);
351 buf.append('#');
352 buf.append(methodName);
353 buf.append('(');
354 for (int i= 0; i < parameterTypesQualifiedNames.length; i++) {
355 if (i > 0) {
356 buf.append(", "); //$NON-NLS-1$
357 }
358 buf.append(parameterTypesQualifiedNames[i]);
359 }
360 buf.append(')');
361 return buf.toString();
362 }
363
364 public static String[] getTypeParameterNames(ITypeParameter[] typeParameters) {
365 String[] typeParametersNames= new String[typeParameters.length];
366 for (int i= 0; i < typeParameters.length; i++) {
367 typeParametersNames[i]= typeParameters[i].getElementName();
368 }
369 return typeParametersNames;
370 }
371
372 /**
373 * Don't use this method directly, use CodeGeneration.
374 *
375 * @param templateID the template id of the type body to get. Valid id's are
376 * {@link CodeTemplateContextType#CLASSBODY_ID},
377 * {@link CodeTemplateContextType#INTERFACEBODY_ID},
378 * {@link CodeTemplateContextType#ENUMBODY_ID},
379 * {@link CodeTemplateContextType#ANNOTATIONBODY_ID},
380 * @param cu the compilation unit to which the template is added
381 * @param typeName the type name
382 * @param lineDelim the line delimiter to use
383 * @return return the type body template or <code>null</code>
384 * @throws CoreException thrown if the template could not be evaluated
385 * @see org.eclipse.jdt.ui.CodeGeneration#getTypeBody(String, ICompilationUnit, String, String)
386 */
387 public static String getTypeBody(String templateID, ICompilationUnit cu, String typeName, String lineDelim) throws CoreException {
388 if (!VALID_TYPE_BODY_TEMPLATES.contains(templateID)) {
389 throw new IllegalArgumentException("Invalid code template ID: " + templateID); //$NON-NLS-1$
390 }
391
392 Template template= getCodeTemplate(templateID, cu.getJavaProject());
393 if (template == null) {
394 return null;
395 }
396 CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), cu.getJavaProject(), lineDelim);
397 context.setCompilationUnitVariables(cu);
398 context.setVariable(CodeTemplateContextType.TYPENAME, typeName);
399
400 return evaluateTemplate(context, template);
401 }
402
403 /*
404 * Don't use this method directly, use CodeGeneration.
405 * @see org.eclipse.jdt.ui.CodeGeneration#getMethodComment(ICompilationUnit, String, String, String[], String[], String, String[], IMethod, String)
406 */
407 public static String getMethodComment(ICompilationUnit cu, String typeName, String methodName, String[] paramNames, String[] excTypeSig, String retTypeSig, String[] typeParameterNames,
408 IMethod target, boolean delegate, String lineDelimiter) throws CoreException {
409 String templateName= CodeTemplateContextType.METHODCOMMENT_ID;
410 if (retTypeSig == null) {
411 templateName= CodeTemplateContextType.CONSTRUCTORCOMMENT_ID;
412 } else if (target != null) {
413 if (delegate)
414 templateName= CodeTemplateContextType.DELEGATECOMMENT_ID;
415 else
416 templateName= CodeTemplateContextType.OVERRIDECOMMENT_ID;
417 }
418 Template template= getCodeTemplate(templateName, cu.getJavaProject());
419 if (template == null) {
420 return null;
421 }
422 CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), cu.getJavaProject(), lineDelimiter);
423 context.setCompilationUnitVariables(cu);
424 context.setVariable(CodeTemplateContextType.ENCLOSING_TYPE, typeName);
425 context.setVariable(CodeTemplateContextType.ENCLOSING_METHOD, methodName);
426
427 if (retTypeSig != null) {
428 context.setVariable(CodeTemplateContextType.RETURN_TYPE, Signature.toString(retTypeSig));
429 }
430 if (target != null) {
431 String targetTypeName= target.getDeclaringType().getFullyQualifiedName('.');
432 String[] targetParamTypeNames= getParameterTypeNamesForSeeTag(target);
433 if (delegate)
434 context.setVariable(CodeTemplateContextType.SEE_TO_TARGET_TAG, getSeeTag(targetTypeName, methodName, targetParamTypeNames));
435 else
436 context.setVariable(CodeTemplateContextType.SEE_TO_OVERRIDDEN_TAG, getSeeTag(targetTypeName, methodName, targetParamTypeNames));
437 }
438 TemplateBuffer buffer;
439 try {
440 buffer= context.evaluate(template);
441 } catch (BadLocationException e) {
442 throw new CoreException(Status.CANCEL_STATUS);
443 } catch (TemplateException e) {
444 throw new CoreException(Status.CANCEL_STATUS);
445 }
446 if (buffer == null) {
447 return null;
448 }
449
450 String str= buffer.getString();
451 if (Strings.containsOnlyWhitespaces(str)) {
452 return null;
453 }
454 TemplateVariable position= findVariable(buffer, CodeTemplateContextType.TAGS); // look if Javadoc tags have to be added
455 if (position == null) {
456 return str;
457 }
458
459 IDocument document= new Document(str);
460 String[] exceptionNames= new String[excTypeSig.length];
461 for (int i= 0; i < excTypeSig.length; i++) {
462 exceptionNames[i]= Signature.toString(excTypeSig[i]);
463 }
464 String returnType= retTypeSig != null ? Signature.toString(retTypeSig) : null;
465 int[] tagOffsets= position.getOffsets();
466 for (int i= tagOffsets.length - 1; i >= 0; i--) { // from last to first
467 try {
468 insertTag(document, tagOffsets[i], position.getLength(), paramNames, exceptionNames, returnType, typeParameterNames, false, lineDelimiter);
469 } catch (BadLocationException e) {
470 throw new CoreException(JavaUIStatus.createError(IStatus.ERROR, e));
471 }
472 }
473 return document.get();
474 }
475
476 // remove lines for empty variables
477 private static String fixEmptyVariables(TemplateBuffer buffer, String[] variables) throws MalformedTreeException, BadLocationException {
478 IDocument doc= new Document(buffer.getString());
479 int nLines= doc.getNumberOfLines();
480 MultiTextEdit edit= new MultiTextEdit();
481 HashSet<Integer> removedLines= new HashSet<Integer>();
482 for (int i= 0; i < variables.length; i++) {
483 TemplateVariable position= findVariable(buffer, variables[i]); // look if Javadoc tags have to be added
484 if (position == null || position.getLength() > 0) {
485 continue;
486 }
487 int[] offsets= position.getOffsets();
488 for (int k= 0; k < offsets.length; k++) {
489 int line= doc.getLineOfOffset(offsets[k]);
490 IRegion lineInfo= doc.getLineInformation(line);
491 int offset= lineInfo.getOffset();
492 String str= doc.get(offset, lineInfo.getLength());
493 if (Strings.containsOnlyWhitespaces(str) && nLines > line + 1 && removedLines.add(new Integer(line))) {
494 int nextStart= doc.getLineOffset(line + 1);
495 edit.addChild(new DeleteEdit(offset, nextStart - offset));
496 }
497 }
498 }
499 edit.apply(doc, 0);
500 return doc.get();
501 }
502
503 /*
504 * Don't use this method directly, use CodeGeneration.
505 */
506 public static String getFieldComment(ICompilationUnit cu, String typeName, String fieldName, String lineDelimiter) throws CoreException {
507 Template template= getCodeTemplate(CodeTemplateContextType.FIELDCOMMENT_ID, cu.getJavaProject());
508 if (template == null) {
509 return null;
510 }
511 CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), cu.getJavaProject(), lineDelimiter);
512 context.setCompilationUnitVariables(cu);
513 context.setVariable(CodeTemplateContextType.FIELD_TYPE, typeName);
514 context.setVariable(CodeTemplateContextType.FIELD, fieldName);
515
516 return evaluateTemplate(context, template);
517 }
518
519
520 /*
521 * Don't use this method directly, use CodeGeneration.
522 * @see org.eclipse.jdt.ui.CodeGeneration#getSetterComment(ICompilationUnit, String, String, String, String, String, String, String)
523 */
524 public static String getSetterComment(ICompilationUnit cu, String typeName, String methodName, String fieldName, String fieldType, String paramName, String bareFieldName, String lineDelimiter)
525 throws CoreException {
526 String templateName= CodeTemplateContextType.SETTERCOMMENT_ID;
527 Template template= getCodeTemplate(templateName, cu.getJavaProject());
528 if (template == null) {
529 return null;
530 }
531
532 CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), cu.getJavaProject(), lineDelimiter);
533 context.setCompilationUnitVariables(cu);
534 context.setVariable(CodeTemplateContextType.ENCLOSING_TYPE, typeName);
535 context.setVariable(CodeTemplateContextType.ENCLOSING_METHOD, methodName);
536 context.setVariable(CodeTemplateContextType.FIELD, fieldName);
537 context.setVariable(CodeTemplateContextType.FIELD_TYPE, fieldType);
538 context.setVariable(CodeTemplateContextType.BARE_FIELD_NAME, bareFieldName);
539 context.setVariable(CodeTemplateContextType.PARAM, paramName);
540
541 return evaluateTemplate(context, template);
542 }
543
544 /*
545 * Don't use this method directly, use CodeGeneration.
546 * @see org.eclipse.jdt.ui.CodeGeneration#getGetterComment(ICompilationUnit, String, String, String, String, String, String)
547 */
548 public static String getGetterComment(ICompilationUnit cu, String typeName, String methodName, String fieldName, String fieldType, String bareFieldName, String lineDelimiter) throws CoreException {
549 String templateName= CodeTemplateContextType.GETTERCOMMENT_ID;
550 Template template= getCodeTemplate(templateName, cu.getJavaProject());
551 if (template == null) {
552 return null;
553 }
554 CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), cu.getJavaProject(), lineDelimiter);
555 context.setCompilationUnitVariables(cu);
556 context.setVariable(CodeTemplateContextType.ENCLOSING_TYPE, typeName);
557 context.setVariable(CodeTemplateContextType.ENCLOSING_METHOD, methodName);
558 context.setVariable(CodeTemplateContextType.FIELD, fieldName);
559 context.setVariable(CodeTemplateContextType.FIELD_TYPE, fieldType);
560 context.setVariable(CodeTemplateContextType.BARE_FIELD_NAME, bareFieldName);
561
562 return evaluateTemplate(context, template);
563 }
564
565 private static String evaluateTemplate(CodeTemplateContext context, Template template) throws CoreException {
566 TemplateBuffer buffer;
567 try {
568 buffer= context.evaluate(template);
569 } catch (BadLocationException e) {
570 throw new CoreException(Status.CANCEL_STATUS);
571 } catch (TemplateException e) {
572 throw new CoreException(Status.CANCEL_STATUS);
573 }
574 if (buffer == null)
575 return null;
576 String str= buffer.getString();
577 if (Strings.containsOnlyWhitespaces(str)) {
578 return null;
579 }
580 return str;
581 }
582
583 private static String evaluateTemplate(CodeTemplateContext context, Template template, String[] fullLineVariables) throws CoreException {
584 TemplateBuffer buffer;
585 try {
586 buffer= context.evaluate(template);
587 if (buffer == null)
588 return null;
589 String str= fixEmptyVariables(buffer, fullLineVariables);
590 if (Strings.containsOnlyWhitespaces(str)) {
591 return null;
592 }
593 return str;
594 } catch (BadLocationException e) {
595 throw new CoreException(Status.CANCEL_STATUS);
596 } catch (TemplateException e) {
597 throw new CoreException(Status.CANCEL_STATUS);
598 }
599 }
600
601
602 /*
603 * Don't use this method directly, use CodeGeneration.
604 * @see org.eclipse.jdt.ui.CodeGeneration#getMethodComment(ICompilationUnit, String, MethodDeclaration, boolean, String, String[], String)
605 */
606 public static String getMethodComment(ICompilationUnit cu, String typeName, MethodDeclaration decl, boolean isDeprecated, String targetName, String targetMethodDeclaringTypeName,
607 String[] targetMethodParameterTypeNames, boolean delegate, String lineDelimiter) throws CoreException {
608 boolean needsTarget= targetMethodDeclaringTypeName != null && targetMethodParameterTypeNames != null;
609 String templateName= CodeTemplateContextType.METHODCOMMENT_ID;
610 if (decl.isConstructor()) {
611 templateName= CodeTemplateContextType.CONSTRUCTORCOMMENT_ID;
612 } else if (needsTarget) {
613 if (delegate)
614 templateName= CodeTemplateContextType.DELEGATECOMMENT_ID;
615 else
616 templateName= CodeTemplateContextType.OVERRIDECOMMENT_ID;
617 }
618 Template template= getCodeTemplate(templateName, cu.getJavaProject());
619 if (template == null) {
620 return null;
621 }
622 CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), cu.getJavaProject(), lineDelimiter);
623 context.setCompilationUnitVariables(cu);
624 context.setVariable(CodeTemplateContextType.ENCLOSING_TYPE, typeName);
625 context.setVariable(CodeTemplateContextType.ENCLOSING_METHOD, decl.getName().getIdentifier());
626 if (!decl.isConstructor()) {
627 context.setVariable(CodeTemplateContextType.RETURN_TYPE, ASTNodes.asString(getReturnType(decl)));
628 }
629 if (needsTarget) {
630 if (delegate)
631 context.setVariable(CodeTemplateContextType.SEE_TO_TARGET_TAG, getSeeTag(targetMethodDeclaringTypeName, targetName, targetMethodParameterTypeNames));
632 else
633 context.setVariable(CodeTemplateContextType.SEE_TO_OVERRIDDEN_TAG, getSeeTag(targetMethodDeclaringTypeName, targetName, targetMethodParameterTypeNames));
634 }
635
636 TemplateBuffer buffer;
637 try {
638 buffer= context.evaluate(template);
639 } catch (BadLocationException e) {
640 throw new CoreException(Status.CANCEL_STATUS);
641 } catch (TemplateException e) {
642 throw new CoreException(Status.CANCEL_STATUS);
643 }
644 if (buffer == null)
645 return null;
646 String str= buffer.getString();
647 if (Strings.containsOnlyWhitespaces(str)) {
648 return null;
649 }
650 TemplateVariable position= findVariable(buffer, CodeTemplateContextType.TAGS); // look if Javadoc tags have to be added
651 if (position == null) {
652 return str;
653 }
654
655 IDocument textBuffer= new Document(str);
656 List<TypeParameter> typeParams= shouldGenerateMethodTypeParameterTags(cu.getJavaProject()) ? decl.typeParameters() : Collections.emptyList();
657 String[] typeParamNames= new String[typeParams.size()];
658 for (int i= 0; i < typeParamNames.length; i++) {
659 TypeParameter elem= typeParams.get(i);
660 typeParamNames[i]= elem.getName().getIdentifier();
661 }
662 List<SingleVariableDeclaration> params= decl.parameters();
663 String[] paramNames= new String[params.size()];
664 for (int i= 0; i < paramNames.length; i++) {
665 SingleVariableDeclaration elem= params.get(i);
666 paramNames[i]= elem.getName().getIdentifier();
667 }
668 List<Name> exceptions= decl.thrownExceptions();
669 String[] exceptionNames= new String[exceptions.size()];
670 for (int i= 0; i < exceptionNames.length; i++) {
671 exceptionNames[i]= ASTNodes.getSimpleNameIdentifier(exceptions.get(i));
672 }
673
674 String returnType= null;
675 if (!decl.isConstructor()) {
676 returnType= ASTNodes.asString(getReturnType(decl));
677 }
678 int[] tagOffsets= position.getOffsets();
679 for (int i= tagOffsets.length - 1; i >= 0; i--) { // from last to first
680 try {
681 insertTag(textBuffer, tagOffsets[i], position.getLength(), paramNames, exceptionNames, returnType, typeParamNames, isDeprecated, lineDelimiter);
682 } catch (BadLocationException e) {
683 throw new CoreException(JavaUIStatus.createError(IStatus.ERROR, e));
684 }
685 }
686 return textBuffer.get();
687 }
688
689 public static boolean shouldGenerateMethodTypeParameterTags(IJavaProject project) {
690 return JavaCore.ENABLED.equals(project.getOption(JavaCore.COMPILER_PB_MISSING_JAVADOC_TAGS_METHOD_TYPE_PARAMETERS, true));
691 }
692
693 /**
694 * @param decl the method declaration
695 * @return the return type
696 * @deprecated Deprecated to avoid deprecated warnings
697 */
698 private static ASTNode getReturnType(MethodDeclaration decl) {
699 // used from API, can't eliminate
700 return decl.getAST().apiLevel() == AST.JLS2 ? decl.getReturnType() : decl.getReturnType2();
701 }
702
703
704 private static TemplateVariable findVariable(TemplateBuffer buffer, String variable) {
705 TemplateVariable[] positions= buffer.getVariables();
706 for (int i= 0; i < positions.length; i++) {
707 TemplateVariable curr= positions[i];
708 if (variable.equals(curr.getType())) {
709 return curr;
710 }
711 }
712 return null;
713 }
714
715 private static void insertTag(IDocument textBuffer, int offset, int length, String[] paramNames, String[] exceptionNames, String returnType, String[] typeParameterNames, boolean isDeprecated,
716 String lineDelimiter) throws BadLocationException {
717 IRegion region= textBuffer.getLineInformationOfOffset(offset);
718 if (region == null) {
719 return;
720 }
721 String lineStart= textBuffer.get(region.getOffset(), offset - region.getOffset());
722
723 StringBuffer buf= new StringBuffer();
724 for (int i= 0; i < typeParameterNames.length; i++) {
725 if (buf.length() > 0) {
726 buf.append(lineDelimiter).append(lineStart);
727 }
728 buf.append("@param <").append(typeParameterNames[i]).append('>'); //$NON-NLS-1$
729 }
730 for (int i= 0; i < paramNames.length; i++) {
731 if (buf.length() > 0) {
732 buf.append(lineDelimiter).append(lineStart);
733 }
734 buf.append("@param ").append(paramNames[i]); //$NON-NLS-1$
735 }
736 if (returnType != null && !returnType.equals("void")) { //$NON-NLS-1$
737 if (buf.length() > 0) {
738 buf.append(lineDelimiter).append(lineStart);
739 }
740 buf.append("@return"); //$NON-NLS-1$
741 }
742 if (exceptionNames != null) {
743 for (int i= 0; i < exceptionNames.length; i++) {
744 if (buf.length() > 0) {
745 buf.append(lineDelimiter).append(lineStart);
746 }
747 buf.append("@throws ").append(exceptionNames[i]); //$NON-NLS-1$
748 }
749 }
750 if (isDeprecated) {
751 if (buf.length() > 0) {
752 buf.append(lineDelimiter).append(lineStart);
753 }
754 buf.append("@deprecated"); //$NON-NLS-1$
755 }
756 if (buf.length() == 0 && isAllCommentWhitespace(lineStart)) {
757 int prevLine= textBuffer.getLineOfOffset(offset) - 1;
758 if (prevLine > 0) {
759 IRegion prevRegion= textBuffer.getLineInformation(prevLine);
760 int prevLineEnd= prevRegion.getOffset() + prevRegion.getLength();
761 // clear full line
762 textBuffer.replace(prevLineEnd, offset + length - prevLineEnd, ""); //$NON-NLS-1$
763 return;
764 }
765 }
766 textBuffer.replace(offset, length, buf.toString());
767 }
768
769 private static boolean isAllCommentWhitespace(String lineStart) {
770 for (int i= 0; i < lineStart.length(); i++) {
771 char ch= lineStart.charAt(i);
772 if (!Character.isWhitespace(ch) && ch != '*') {
773 return false;
774 }
775 }
776 return true;
777 }
778
779 /**
780 * Returns the line delimiter which is used in the specified project.
781 *
782 * @param project the java project, or <code>null</code>
783 * @return the used line delimiter
784 */
785 public static String getLineDelimiterUsed(IJavaProject project) {
786 return getProjectLineDelimiter(project);
787 }
788
789 private static String getProjectLineDelimiter(IJavaProject javaProject) {
790 IProject project= null;
791 if (javaProject != null)
792 project= javaProject.getProject();
793
794 String lineDelimiter= getLineDelimiterPreference(project);
795 if (lineDelimiter != null)
796 return lineDelimiter;
797
798 return System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
799 }
800
801 public static String getLineDelimiterPreference(IProject project) {
802 IScopeContext[] scopeContext;
803 if (project != null) {
804 // project preference
805 scopeContext= new IScopeContext[] { new ProjectScope(project) };
806 String lineDelimiter= Platform.getPreferencesService().getString(Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, null, scopeContext);
807 if (lineDelimiter != null)
808 return lineDelimiter;
809 }
810 // workspace preference
811 scopeContext= new IScopeContext[] { InstanceScope.INSTANCE };
812 String platformDefault= System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
813 return Platform.getPreferencesService().getString(Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, platformDefault, scopeContext);
814 }
815
816 /**
817 * Examines a string and returns the first line delimiter found.
818 *
819 * @param elem the element
820 * @return the line delimiter used for the element
821 */
822 public static String getLineDelimiterUsed(IJavaElement elem) {
823 IOpenable openable= elem.getOpenable();
824 if (openable instanceof ITypeRoot) {
825 try {
826 return openable.findRecommendedLineSeparator();
827 } catch (JavaModelException exception) {
828 // Use project setting
829 }
830 }
831 IJavaProject project= elem.getJavaProject();
832 return getProjectLineDelimiter(project.exists() ? project : null);
833 }
834
835 /**
836 * Evaluates the indentation used by a Java element. (in tabulators)
837 *
838 * @param elem the element to get the indent of
839 * @return return the indent unit
840 * @throws JavaModelException thrown if the element could not be accessed
841 */
842 public static int getIndentUsed(IJavaElement elem) throws JavaModelException {
843 IOpenable openable= elem.getOpenable();
844 if (openable instanceof ITypeRoot) {
845 IBuffer buf= openable.getBuffer();
846 if (buf != null) {
847 int offset= ((ISourceReference)elem).getSourceRange().getOffset();
848 return getIndentUsed(buf, offset, elem.getJavaProject());
849 }
850 }
851 return 0;
852 }
853
854 public static int getIndentUsed(IBuffer buffer, int offset, IJavaProject project) {
855 int i= offset;
856 // find beginning of line
857 while (i > 0 && !IndentManipulation.isLineDelimiterChar(buffer.getChar(i - 1))) {
858 i--;
859 }
860 return Strings.computeIndentUnits(buffer.getText(i, offset - i), project);
861 }
862
863
864
865 /**
866 * Returns the element after the give element.
867 *
868 * @param member a Java element
869 * @return the next sibling of the given element or <code>null</code>
870 * @throws JavaModelException thrown if the element could not be accessed
871 */
872 public static IJavaElement findNextSibling(IJavaElement member) throws JavaModelException {
873 IJavaElement parent= member.getParent();
874 if (parent instanceof IParent) {
875 IJavaElement[] elements= ((IParent)parent).getChildren();
876 for (int i= elements.length - 2; i >= 0; i--) {
877 if (member.equals(elements[i])) {
878 return elements[i + 1];
879 }
880 }
881 }
882 return null;
883 }
884
885 public static String getTodoTaskTag(IJavaProject project) {
886 String markers= null;
887 if (project == null) {
888 markers= JavaCore.getOption(JavaCore.COMPILER_TASK_TAGS);
889 } else {
890 markers= project.getOption(JavaCore.COMPILER_TASK_TAGS, true);
891 }
892
893 if (markers != null && markers.length() > 0) {
894 int idx= markers.indexOf(',');
895 if (idx == -1) {
896 return markers;
897 } else {
898 return markers.substring(0, idx);
899 }
900 }
901 return null;
902 }
903
904 private static String removeTypeArguments(String baseName) {
905 int idx= baseName.indexOf('<');
906 if (idx != -1) {
907 return baseName.substring(0, idx);
908 }
909 return baseName;
910 }
911
912
913 // --------------------------- name suggestions --------------------------
914
915 public static String[] getVariableNameSuggestions(int variableKind, IJavaProject project, ITypeBinding expectedType, Expression assignedExpression, Collection<String> excluded) {
916 LinkedHashSet<String> res= new LinkedHashSet<String>(); // avoid duplicates but keep order
917
918 if (assignedExpression != null) {
919 String nameFromExpression= getBaseNameFromExpression(project, assignedExpression, variableKind);
920 if (nameFromExpression != null) {
921 add(getVariableNameSuggestions(variableKind, project, nameFromExpression, 0, excluded, false), res); // pass 0 as dimension, base name already contains plural.
922 }
923
924 String nameFromParent= getBaseNameFromLocationInParent(assignedExpression);
925 if (nameFromParent != null) {
926 add(getVariableNameSuggestions(variableKind, project, nameFromParent, 0, excluded, false), res); // pass 0 as dimension, base name already contains plural.
927 }
928 }
929 if (expectedType != null) {
930 expectedType= Bindings.normalizeTypeBinding(expectedType);
931 if (expectedType != null) {
932 int dim= 0;
933 if (expectedType.isArray()) {
934 dim= expectedType.getDimensions();
935 expectedType= expectedType.getElementType();
936 }
937 if (expectedType.isParameterizedType()) {
938 expectedType= expectedType.getTypeDeclaration();
939 }
940 String typeName= expectedType.getName();
941 if (typeName.length() > 0) {
942 add(getVariableNameSuggestions(variableKind, project, typeName, dim, excluded, false), res);
943 }
944 }
945 }
946 if (res.isEmpty()) {
947 return getDefaultVariableNameSuggestions(variableKind, excluded);
948 }
949 return res.toArray(new String[res.size()]);
950 }
951
952 public static String[] getVariableNameSuggestions(int variableKind, IJavaProject project, Type expectedType, Expression assignedExpression, Collection<String> excluded) {
953 LinkedHashSet<String> res= new LinkedHashSet<String>(); // avoid duplicates but keep order
954
955 if (assignedExpression != null) {
956 String nameFromExpression= getBaseNameFromExpression(project, assignedExpression, variableKind);
957 if (nameFromExpression != null) {
958 add(getVariableNameSuggestions(variableKind, project, nameFromExpression, 0, excluded, false), res); // pass 0 as dimension, base name already contains plural.
959 }
960
961 String nameFromParent= getBaseNameFromLocationInParent(assignedExpression);
962 if (nameFromParent != null) {
963 add(getVariableNameSuggestions(variableKind, project, nameFromParent, 0, excluded, false), res); // pass 0 as dimension, base name already contains plural.
964 }
965 }
966 if (expectedType != null) {
967 String[] names= getVariableNameSuggestions(variableKind, project, expectedType, excluded, false);
968 for (int i= 0; i < names.length; i++) {
969 res.add(names[i]);
970 }
971 }
972 if (res.isEmpty()) {
973 return getDefaultVariableNameSuggestions(variableKind, excluded);
974 }
975 return res.toArray(new String[res.size()]);
976 }
977
978 private static String[] getVariableNameSuggestions(int variableKind, IJavaProject project, Type expectedType, Collection<String> excluded, boolean evaluateDefault) {
979 int dim= 0;
980 if (expectedType.isArrayType()) {
981 ArrayType arrayType= (ArrayType)expectedType;
982 dim= arrayType.getDimensions();
983 expectedType= arrayType.getElementType();
984 }
985 if (expectedType.isParameterizedType()) {
986 expectedType= ((ParameterizedType)expectedType).getType();
987 }
988 String typeName= ASTNodes.getTypeName(expectedType);
989
990 if (typeName.length() > 0) {
991 return getVariableNameSuggestions(variableKind, project, typeName, dim, excluded, evaluateDefault);
992 }
993 return EMPTY;
994 }
995
996 private static String[] getDefaultVariableNameSuggestions(int variableKind, Collection<String> excluded) {
997 String prop= variableKind == NamingConventions.VK_STATIC_FINAL_FIELD ? "X" : "x"; //$NON-NLS-1$//$NON-NLS-2$
998 String name= prop;
999 int i= 1;
1000 while (excluded.contains(name)) {
1001 name= prop + i++;
1002 }
1003 return new String[] { name };
1004 }
1005
1006 /**
1007 * Returns variable name suggestions for the given base name. This is a layer over the JDT.Core
1008 * NamingConventions API to fix its shortcomings. JDT UI code should only use this API.
1009 *
1010 * @param variableKind specifies what type the variable is: {@link NamingConventions#VK_LOCAL},
1011 * {@link NamingConventions#VK_PARAMETER}, {@link NamingConventions#VK_STATIC_FIELD},
1012 * {@link NamingConventions#VK_INSTANCE_FIELD}, or
1013 * {@link NamingConventions#VK_STATIC_FINAL_FIELD}.
1014 * @param project the current project
1015 * @param baseName the base name to make a suggestion on. The base name is expected to be a name
1016 * without any pre- or suffixes in singular form. Type name are accepted as well.
1017 * @param dimensions if greater than 0, the resulting name will be in plural form
1018 * @param excluded a collection containing all excluded names or <code>null</code> if no names
1019 * are excluded
1020 * @param evaluateDefault if set, the result is guaranteed to contain at least one result. If
1021 * not, the result can be an empty array.
1022 *
1023 * @return the name suggestions sorted by relevance (best proposal first). If
1024 * <code>evaluateDefault</code> is set to true, the returned array is never empty. If
1025 * <code>evaluateDefault</code> is set to false, an empty array is returned if there is
1026 * no good suggestion for the given base name.
1027 */
1028 public static String[] getVariableNameSuggestions(int variableKind, IJavaProject project, String baseName, int dimensions, Collection<String> excluded, boolean evaluateDefault) {
1029 return NamingConventions.suggestVariableNames(variableKind, NamingConventions.BK_TYPE_NAME, removeTypeArguments(baseName), project, dimensions, getExcludedArray(excluded), evaluateDefault);
1030 }
1031
1032 private static String[] getExcludedArray(Collection<String> excluded) {
1033 if (excluded == null) {
1034 return null;
1035 } else if (excluded instanceof ExcludedCollection) {
1036 return ((ExcludedCollection)excluded).getExcludedArray();
1037 }
1038 return excluded.toArray(new String[excluded.size()]);
1039 }
1040
1041
1042 private static final String[] KNOWN_METHOD_NAME_PREFIXES= { "get", "is", "to" }; //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-1$
1043
1044
1045 private static void add(String[] names, Set<String> result) {
1046 for (int i= 0; i < names.length; i++) {
1047 result.add(names[i]);
1048 }
1049 }
1050
1051 private static String getBaseNameFromExpression(IJavaProject project, Expression assignedExpression, int variableKind) {
1052 String name= null;
1053 if (assignedExpression instanceof CastExpression) {
1054 assignedExpression= ((CastExpression)assignedExpression).getExpression();
1055 }
1056 if (assignedExpression instanceof Name) {
1057 Name simpleNode= (Name)assignedExpression;
1058 IBinding binding= simpleNode.resolveBinding();
1059 if (binding instanceof IVariableBinding)
1060 return getBaseName((IVariableBinding)binding, project);
1061
1062 return ASTNodes.getSimpleNameIdentifier(simpleNode);
1063 } else if (assignedExpression instanceof MethodInvocation) {
1064 name= ((MethodInvocation)assignedExpression).getName().getIdentifier();
1065 } else if (assignedExpression instanceof SuperMethodInvocation) {
1066 name= ((SuperMethodInvocation)assignedExpression).getName().getIdentifier();
1067 } else if (assignedExpression instanceof FieldAccess) {
1068 return ((FieldAccess)assignedExpression).getName().getIdentifier();
1069 } else if (variableKind == NamingConventions.VK_STATIC_FINAL_FIELD && (assignedExpression instanceof StringLiteral || assignedExpression instanceof NumberLiteral)) {
1070 String string= assignedExpression instanceof StringLiteral ? ((StringLiteral)assignedExpression).getLiteralValue() : ((NumberLiteral)assignedExpression).getToken();
1071 StringBuffer res= new StringBuffer();
1072 boolean needsUnderscore= false;
1073 for (int i= 0; i < string.length(); i++) {
1074 char ch= string.charAt(i);
1075 if (Character.isJavaIdentifierPart(ch)) {
1076 if (res.length() == 0 && !Character.isJavaIdentifierStart(ch) || needsUnderscore) {
1077 res.append('_');
1078 }
1079 res.append(ch);
1080 needsUnderscore= false;
1081 } else {
1082 needsUnderscore= res.length() > 0;
1083 }
1084 }
1085 if (res.length() > 0) {
1086 return res.toString();
1087 }
1088 }
1089 if (name != null) {
1090 for (int i= 0; i < KNOWN_METHOD_NAME_PREFIXES.length; i++) {
1091 String curr= KNOWN_METHOD_NAME_PREFIXES[i];
1092 if (name.startsWith(curr)) {
1093 if (name.equals(curr)) {
1094 return null; // don't suggest 'get' as variable name
1095 } else if (Character.isUpperCase(name.charAt(curr.length()))) {
1096 return name.substring(curr.length());
1097 }
1098 }
1099 }
1100 }
1101 return name;
1102 }
1103
1104 private static String getBaseNameFromLocationInParent(Expression assignedExpression, List<Expression> arguments, IMethodBinding binding) {
1105 if (binding == null)
1106 return null;
1107
1108 ITypeBinding[] parameterTypes= binding.getParameterTypes();
1109 if (parameterTypes.length != arguments.size()) // beware of guessed method bindings
1110 return null;
1111
1112 int index= arguments.indexOf(assignedExpression);
1113 if (index == -1)
1114 return null;
1115
1116 ITypeBinding expressionBinding= assignedExpression.resolveTypeBinding();
1117 if (expressionBinding != null && !expressionBinding.isAssignmentCompatible(parameterTypes[index]))
1118 return null;
1119
1120 try {
1121 IJavaElement javaElement= binding.getJavaElement();
1122 if (javaElement instanceof IMethod) {
1123 IMethod method= (IMethod)javaElement;
1124 if (method.getOpenable().getBuffer() != null) { // avoid dummy names and lookup from Javadoc
1125 String[] parameterNames= method.getParameterNames();
1126 if (index < parameterNames.length) {
1127 return NamingConventions.getBaseName(NamingConventions.VK_PARAMETER, parameterNames[index], method.getJavaProject());
1128 }
1129 }
1130 }
1131 } catch (JavaModelException e) {
1132 // ignore
1133 }
1134 return null;
1135 }
1136
1137
1138 private static String getBaseNameFromLocationInParent(Expression assignedExpression) {
1139 StructuralPropertyDescriptor location= assignedExpression.getLocationInParent();
1140 if (location == MethodInvocation.ARGUMENTS_PROPERTY) {
1141 MethodInvocation parent= (MethodInvocation)assignedExpression.getParent();
1142 return getBaseNameFromLocationInParent(assignedExpression, parent.arguments(), parent.resolveMethodBinding());
1143 } else if (location == ClassInstanceCreation.ARGUMENTS_PROPERTY) {
1144 ClassInstanceCreation parent= (ClassInstanceCreation)assignedExpression.getParent();
1145 return getBaseNameFromLocationInParent(assignedExpression, parent.arguments(), parent.resolveConstructorBinding());
1146 } else if (location == SuperMethodInvocation.ARGUMENTS_PROPERTY) {
1147 SuperMethodInvocation parent= (SuperMethodInvocation)assignedExpression.getParent();
1148 return getBaseNameFromLocationInParent(assignedExpression, parent.arguments(), parent.resolveMethodBinding());
1149 } else if (location == ConstructorInvocation.ARGUMENTS_PROPERTY) {
1150 ConstructorInvocation parent= (ConstructorInvocation)assignedExpression.getParent();
1151 return getBaseNameFromLocationInParent(assignedExpression, parent.arguments(), parent.resolveConstructorBinding());
1152 } else if (location == SuperConstructorInvocation.ARGUMENTS_PROPERTY) {
1153 SuperConstructorInvocation parent= (SuperConstructorInvocation)assignedExpression.getParent();
1154 return getBaseNameFromLocationInParent(assignedExpression, parent.arguments(), parent.resolveConstructorBinding());
1155 }
1156 return null;
1157 }
1158
1159 public static String[] getArgumentNameSuggestions(IType type, String[] excluded) {
1160 return getVariableNameSuggestions(NamingConventions.VK_PARAMETER, type.getJavaProject(), type.getElementName(), 0, new ExcludedCollection(excluded), true);
1161 }
1162
1163 public static String[] getArgumentNameSuggestions(IJavaProject project, Type type, String[] excluded) {
1164 return getVariableNameSuggestions(NamingConventions.VK_PARAMETER, project, type, new ExcludedCollection(excluded), true);
1165 }
1166
1167 public static String[] getArgumentNameSuggestions(IJavaProject project, ITypeBinding binding, String[] excluded) {
1168 return getVariableNameSuggestions(NamingConventions.VK_PARAMETER, project, binding, null, new ExcludedCollection(excluded));
1169 }
1170
1171 public static String[] getArgumentNameSuggestions(IJavaProject project, String baseName, int dimensions, String[] excluded) {
1172 return getVariableNameSuggestions(NamingConventions.VK_PARAMETER, project, baseName, dimensions, new ExcludedCollection(excluded), true);
1173 }
1174
1175 public static String[] getFieldNameSuggestions(IType type, int fieldModifiers, String[] excluded) {
1176 return getFieldNameSuggestions(type.getJavaProject(), type.getElementName(), 0, fieldModifiers, excluded);
1177 }
1178
1179 public static String[] getFieldNameSuggestions(IJavaProject project, String baseName, int dimensions, int modifiers, String[] excluded) {
1180 if (Flags.isFinal(modifiers) && Flags.isStatic(modifiers)) {
1181 return getVariableNameSuggestions(NamingConventions.VK_STATIC_FINAL_FIELD, project, baseName, dimensions, new ExcludedCollection(excluded), true);
1182 } else if (Flags.isStatic(modifiers)) {
1183 return getVariableNameSuggestions(NamingConventions.VK_STATIC_FIELD, project, baseName, dimensions, new ExcludedCollection(excluded), true);
1184 }
1185 return getVariableNameSuggestions(NamingConventions.VK_INSTANCE_FIELD, project, baseName, dimensions, new ExcludedCollection(excluded), true);
1186 }
1187
1188 public static String[] getLocalNameSuggestions(IJavaProject project, String baseName, int dimensions, String[] excluded) {
1189 return getVariableNameSuggestions(NamingConventions.VK_LOCAL, project, baseName, dimensions, new ExcludedCollection(excluded), true);
1190 }
1191
1192 public static String suggestArgumentName(IJavaProject project, String baseName, String[] excluded) {
1193 return suggestVariableName(NamingConventions.VK_PARAMETER, project, baseName, 0, excluded);
1194 }
1195
1196 private static String suggestVariableName(int varKind, IJavaProject project, String baseName, int dimension, String[] excluded) {
1197 return getVariableNameSuggestions(varKind, project, baseName, dimension, new ExcludedCollection(excluded), true)[0];
1198 }
1199
1200
1201 public static String[][] suggestArgumentNamesWithProposals(IJavaProject project, String[] paramNames) {
1202 String[][] newNames= new String[paramNames.length][];
1203 ArrayList<String> takenNames= new ArrayList<String>();
1204
1205 // Ensure that the code generation preferences are respected
1206 for (int i= 0; i < paramNames.length; i++) {
1207 String curr= paramNames[i];
1208 String baseName= NamingConventions.getBaseName(NamingConventions.VK_PARAMETER, curr, project);
1209
1210 String[] proposedNames= getVariableNameSuggestions(NamingConventions.VK_PARAMETER, project, curr, 0, takenNames, true);
1211 if (!curr.equals(baseName)) {
1212 // make the existing name to favorite
1213 LinkedHashSet<String> updatedNames= new LinkedHashSet<String>();
1214 updatedNames.add(curr);
1215 for (int k= 0; k < proposedNames.length; k++) {
1216 updatedNames.add(proposedNames[k]);
1217 }
1218 proposedNames= updatedNames.toArray(new String[updatedNames.size()]);
1219 }
1220 newNames[i]= proposedNames;
1221 takenNames.add(proposedNames[0]);
1222 }
1223 return newNames;
1224 }
1225
1226 public static String[][] suggestArgumentNamesWithProposals(IJavaProject project, IMethodBinding binding) {
1227 int nParams= binding.getParameterTypes().length;
1228 if (nParams > 0) {
1229 try {
1230 IMethod method= (IMethod)binding.getMethodDeclaration().getJavaElement();
1231 if (method != null) {
1232 String[] parameterNames= method.getParameterNames();
1233 if (parameterNames.length == nParams) {
1234 return suggestArgumentNamesWithProposals(project, parameterNames);
1235 }
1236 }
1237 } catch (JavaModelException e) {
1238 // ignore
1239 }
1240 }
1241 String[][] names= new String[nParams][];
1242 for (int i= 0; i < names.length; i++) {
1243 names[i]= new String[] { "arg" + i }; //$NON-NLS-1$
1244 }
1245 return names;
1246 }
1247
1248
1249 public static String[] suggestArgumentNames(IJavaProject project, IMethodBinding binding) {
1250 int nParams= binding.getParameterTypes().length;
1251
1252 if (nParams > 0) {
1253 try {
1254 IMethod method= (IMethod)binding.getMethodDeclaration().getJavaElement();
1255 if (method != null) {
1256 String[] paramNames= method.getParameterNames();
1257 if (paramNames.length == nParams) {
1258 String[] namesArray= EMPTY;
1259 ArrayList<String> newNames= new ArrayList<String>(paramNames.length);
1260 // Ensure that the code generation preferences are respected
1261 for (int i= 0; i < paramNames.length; i++) {
1262 String curr= paramNames[i];
1263 String baseName= NamingConventions.getBaseName(NamingConventions.VK_PARAMETER, curr, method.getJavaProject());
1264 if (!curr.equals(baseName)) {
1265 // make the existing name the favorite
1266 newNames.add(curr);
1267 } else {
1268 newNames.add(suggestArgumentName(project, curr, namesArray));
1269 }
1270 namesArray= newNames.toArray(new String[newNames.size()]);
1271 }
1272 return namesArray;
1273 }
1274 }
1275 } catch (JavaModelException e) {
1276 // ignore
1277 }
1278 }
1279 String[] names= new String[nParams];
1280 for (int i= 0; i < names.length; i++) {
1281 names[i]= "arg" + i; //$NON-NLS-1$
1282 }
1283 return names;
1284 }
1285
1286 public static String getBaseName(IField field) throws JavaModelException {
1287 return NamingConventions.getBaseName(getFieldKind(field.getFlags()), field.getElementName(), field.getJavaProject());
1288 }
1289
1290 public static String getBaseName(IVariableBinding binding, IJavaProject project) {
1291 return NamingConventions.getBaseName(getKind(binding), binding.getName(), project);
1292 }
1293
1294 /**
1295 * Returns the kind of the given binding.
1296 *
1297 * @param binding variable binding
1298 * @return one of the <code>NamingConventions.VK_*</code> constants
1299 * @since 3.5
1300 */
1301 private static int getKind(IVariableBinding binding) {
1302 if (binding.isField())
1303 return getFieldKind(binding.getModifiers());
1304
1305 if (binding.isParameter())
1306 return NamingConventions.VK_PARAMETER;
1307
1308 return NamingConventions.VK_LOCAL;
1309 }
1310
1311 private static int getFieldKind(int modifiers) {
1312 if (!Modifier.isStatic(modifiers))
1313 return NamingConventions.VK_INSTANCE_FIELD;
1314
1315 if (!Modifier.isFinal(modifiers))
1316 return NamingConventions.VK_STATIC_FIELD;
1317
1318 return NamingConventions.VK_STATIC_FINAL_FIELD;
1319 }
1320
1321 private static class ExcludedCollection extends AbstractList<String> {
1322 private String[] fExcluded;
1323
1324 public ExcludedCollection(String[] excluded) {
1325 fExcluded= excluded;
1326 }
1327
1328 public String[] getExcludedArray() {
1329 return fExcluded;
1330 }
1331
1332 @Override
1333 public int size() {
1334 return fExcluded.length;
1335 }
1336
1337 @Override
1338 public String get(int index) {
1339 return fExcluded[index];
1340 }
1341
1342 @Override
1343 public int indexOf(Object o) {
1344 if (o instanceof String) {
1345 for (int i= 0; i < fExcluded.length; i++) {
1346 if (o.equals(fExcluded[i]))
1347 return i;
1348 }
1349 }
1350 return -1;
1351 }
1352
1353 @Override
1354 public boolean contains(Object o) {
1355 return indexOf(o) != -1;
1356 }
1357 }
1358
1359
1360 public static boolean hasFieldName(IJavaProject project, String name) {
1361 String prefixes= project.getOption(JavaCore.CODEASSIST_FIELD_PREFIXES, true);
1362 String suffixes= project.getOption(JavaCore.CODEASSIST_FIELD_SUFFIXES, true);
1363 String staticPrefixes= project.getOption(JavaCore.CODEASSIST_STATIC_FIELD_PREFIXES, true);
1364 String staticSuffixes= project.getOption(JavaCore.CODEASSIST_STATIC_FIELD_SUFFIXES, true);
1365
1366
1367 return hasPrefixOrSuffix(prefixes, suffixes, name)
1368 || hasPrefixOrSuffix(staticPrefixes, staticSuffixes, name);
1369 }
1370
1371 public static boolean hasParameterName(IJavaProject project, String name) {
1372 String prefixes= project.getOption(JavaCore.CODEASSIST_ARGUMENT_PREFIXES, true);
1373 String suffixes= project.getOption(JavaCore.CODEASSIST_ARGUMENT_SUFFIXES, true);
1374 return hasPrefixOrSuffix(prefixes, suffixes, name);
1375 }
1376
1377 public static boolean hasLocalVariableName(IJavaProject project, String name) {
1378 String prefixes= project.getOption(JavaCore.CODEASSIST_LOCAL_PREFIXES, true);
1379 String suffixes= project.getOption(JavaCore.CODEASSIST_LOCAL_SUFFIXES, true);
1380 return hasPrefixOrSuffix(prefixes, suffixes, name);
1381 }
1382
1383 public static boolean hasConstantName(IJavaProject project, String name) {
1384 if (Character.isUpperCase(name.charAt(0)))
1385 return true;
1386 String prefixes= project.getOption(JavaCore.CODEASSIST_STATIC_FINAL_FIELD_PREFIXES, true);
1387 String suffixes= project.getOption(JavaCore.CODEASSIST_STATIC_FINAL_FIELD_SUFFIXES, true);
1388 return hasPrefixOrSuffix(prefixes, suffixes, name);
1389 }
1390
1391
1392 private static boolean hasPrefixOrSuffix(String prefixes, String suffixes, String name) {
1393 final String listSeparartor= ","; //$NON-NLS-1$
1394
1395 StringTokenizer tok= new StringTokenizer(prefixes, listSeparartor);
1396 while (tok.hasMoreTokens()) {
1397 String curr= tok.nextToken();
1398 if (name.startsWith(curr)) {
1399 return true;
1400 }
1401 }
1402
1403 tok= new StringTokenizer(suffixes, listSeparartor);
1404 while (tok.hasMoreTokens()) {
1405 String curr= tok.nextToken();
1406 if (name.endsWith(curr)) {
1407 return true;
1408 }
1409 }
1410 return false;
1411 }
1412
1413 // -------------------- preference access -----------------------
1414
1415 public static boolean useThisForFieldAccess(IJavaProject project) {
1416 return Boolean.valueOf(PreferenceConstants.getPreference(PreferenceConstants.CODEGEN_KEYWORD_THIS, project)).booleanValue();
1417 }
1418
1419 public static boolean useIsForBooleanGetters(IJavaProject project) {
1420 return Boolean.valueOf(PreferenceConstants.getPreference(PreferenceConstants.CODEGEN_IS_FOR_GETTERS, project)).booleanValue();
1421 }
1422
1423 public static String getExceptionVariableName(IJavaProject project) {
1424 return PreferenceConstants.getPreference(PreferenceConstants.CODEGEN_EXCEPTION_VAR_NAME, project);
1425 }
1426
1427 public static boolean doAddComments(IJavaProject project) {
1428 return Boolean.valueOf(PreferenceConstants.getPreference(PreferenceConstants.CODEGEN_ADD_COMMENTS, project)).booleanValue();
1429 }
1430
1431 /**
1432 * Only to be used by tests
1433 *
1434 * @param templateId the template id
1435 * @param pattern the new pattern
1436 * @param project not used
1437 */
1438 public static void setCodeTemplate(String templateId, String pattern, IJavaProject project) {
1439 TemplateStore codeTemplateStore= JavaPlugin.getDefault().getCodeTemplateStore();
1440 TemplatePersistenceData data= codeTemplateStore.getTemplateData(templateId);
1441 Template orig= data.getTemplate();
1442 Template copy= new Template(orig.getName(), orig.getDescription(), orig.getContextTypeId(), pattern, true);
1443 data.setTemplate(copy);
1444 }
1445
1446 public static Template getCodeTemplate(String id, IJavaProject project) {
1447 if (project == null)
1448 return JavaPlugin.getDefault().getCodeTemplateStore().findTemplateById(id);
1449 ProjectTemplateStore projectStore= new ProjectTemplateStore(project.getProject());
1450 try {
1451 projectStore.load();
1452 } catch (IOException e) {
1453 JavaPlugin.log(e);
1454 }
1455 return projectStore.findTemplateById(id);
1456 }
1457
1458
1459 public static ImportRewrite createImportRewrite(ICompilationUnit cu, boolean restoreExistingImports) throws JavaModelException {
1460 return CodeStyleConfiguration.createImportRewrite(cu, restoreExistingImports);
1461 }
1462
1463 /**
1464 * Returns a {@link ImportRewrite} using {@link ImportRewrite#create(CompilationUnit, boolean)} and
1465 * configures the rewriter with the settings as specified in the JDT UI preferences.
1466 * <p>
1467 * This method sets {@link ImportRewrite#setUseContextToFilterImplicitImports(boolean)} to <code>true</code>
1468 * iff the given AST has been resolved with bindings. Clients should always supply a context
1469 * when they call one of the <code>addImport(...)</code> methods.
1470 * </p>
1471 *
1472 * @param astRoot the AST root to create the rewriter on
1473 * @param restoreExistingImports specifies if the existing imports should be kept or removed.
1474 * @return the new rewriter configured with the settings as specified in the JDT UI preferences.
1475 *
1476 * @see ImportRewrite#create(CompilationUnit, boolean)
1477 */
1478 public static ImportRewrite createImportRewrite(CompilationUnit astRoot, boolean restoreExistingImports) {
1479 ImportRewrite rewrite= CodeStyleConfiguration.createImportRewrite(astRoot, restoreExistingImports);
1480 if (astRoot.getAST().hasResolvedBindings()) {
1481 rewrite.setUseContextToFilterImplicitImports(true);
1482 }
1483 return rewrite;
1484 }
1485
1486}