]> git.uio.no Git - ifi-stolz-refaktor.git/blob - 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
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  *******************************************************************************/
12 package org.eclipse.jdt.internal.corext.codemanipulation;
13
14 import java.io.IOException;
15 import java.lang.reflect.Modifier;
16 import java.util.AbstractList;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.HashSet;
21 import java.util.LinkedHashSet;
22 import java.util.List;
23 import java.util.Set;
24 import java.util.StringTokenizer;
25
26 import org.eclipse.core.runtime.CoreException;
27 import org.eclipse.core.runtime.IStatus;
28 import org.eclipse.core.runtime.Platform;
29 import org.eclipse.core.runtime.Status;
30 import org.eclipse.core.runtime.preferences.IScopeContext;
31 import org.eclipse.core.runtime.preferences.InstanceScope;
32
33 import org.eclipse.core.resources.IProject;
34 import org.eclipse.core.resources.ProjectScope;
35
36 import org.eclipse.text.edits.DeleteEdit;
37 import org.eclipse.text.edits.MalformedTreeException;
38 import org.eclipse.text.edits.MultiTextEdit;
39
40 import org.eclipse.jface.text.BadLocationException;
41 import org.eclipse.jface.text.Document;
42 import org.eclipse.jface.text.IDocument;
43 import org.eclipse.jface.text.IRegion;
44 import org.eclipse.jface.text.templates.Template;
45 import org.eclipse.jface.text.templates.TemplateBuffer;
46 import org.eclipse.jface.text.templates.TemplateException;
47 import org.eclipse.jface.text.templates.TemplateVariable;
48 import org.eclipse.jface.text.templates.persistence.TemplatePersistenceData;
49 import org.eclipse.jface.text.templates.persistence.TemplateStore;
50
51 import org.eclipse.jdt.core.Flags;
52 import org.eclipse.jdt.core.IBuffer;
53 import org.eclipse.jdt.core.ICompilationUnit;
54 import org.eclipse.jdt.core.IField;
55 import org.eclipse.jdt.core.IJavaElement;
56 import org.eclipse.jdt.core.IJavaProject;
57 import org.eclipse.jdt.core.IMethod;
58 import org.eclipse.jdt.core.IOpenable;
59 import org.eclipse.jdt.core.IPackageFragment;
60 import org.eclipse.jdt.core.IParent;
61 import org.eclipse.jdt.core.ISourceReference;
62 import org.eclipse.jdt.core.IType;
63 import org.eclipse.jdt.core.ITypeParameter;
64 import org.eclipse.jdt.core.ITypeRoot;
65 import org.eclipse.jdt.core.JavaCore;
66 import org.eclipse.jdt.core.JavaModelException;
67 import org.eclipse.jdt.core.NamingConventions;
68 import org.eclipse.jdt.core.Signature;
69 import org.eclipse.jdt.core.dom.AST;
70 import org.eclipse.jdt.core.dom.ASTNode;
71 import org.eclipse.jdt.core.dom.ASTParser;
72 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
73 import org.eclipse.jdt.core.dom.ArrayType;
74 import org.eclipse.jdt.core.dom.CastExpression;
75 import org.eclipse.jdt.core.dom.ClassInstanceCreation;
76 import org.eclipse.jdt.core.dom.CompilationUnit;
77 import org.eclipse.jdt.core.dom.ConstructorInvocation;
78 import org.eclipse.jdt.core.dom.Expression;
79 import org.eclipse.jdt.core.dom.FieldAccess;
80 import org.eclipse.jdt.core.dom.IBinding;
81 import org.eclipse.jdt.core.dom.IMethodBinding;
82 import org.eclipse.jdt.core.dom.ITypeBinding;
83 import org.eclipse.jdt.core.dom.IVariableBinding;
84 import org.eclipse.jdt.core.dom.MethodDeclaration;
85 import org.eclipse.jdt.core.dom.MethodInvocation;
86 import org.eclipse.jdt.core.dom.Name;
87 import org.eclipse.jdt.core.dom.NumberLiteral;
88 import org.eclipse.jdt.core.dom.ParameterizedType;
89 import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
90 import org.eclipse.jdt.core.dom.StringLiteral;
91 import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
92 import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
93 import org.eclipse.jdt.core.dom.SuperMethodInvocation;
94 import org.eclipse.jdt.core.dom.Type;
95 import org.eclipse.jdt.core.dom.TypeParameter;
96 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
97 import org.eclipse.jdt.core.formatter.IndentManipulation;
98
99 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
100 import org.eclipse.jdt.internal.corext.dom.Bindings;
101 import org.eclipse.jdt.internal.corext.template.java.CodeTemplateContext;
102 import org.eclipse.jdt.internal.corext.template.java.CodeTemplateContextType;
103 import org.eclipse.jdt.internal.corext.util.Strings;
104
105 import org.eclipse.jdt.ui.CodeStyleConfiguration;
106 import org.eclipse.jdt.ui.PreferenceConstants;
107
108 import org.eclipse.jdt.internal.ui.JavaPlugin;
109 import org.eclipse.jdt.internal.ui.JavaUIStatus;
110 import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
111 import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;
112 import org.eclipse.jdt.internal.ui.viewsupport.ProjectTemplateStore;
113
114 public 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 }