KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > corext > codemanipulation > AddImportsOperation


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.corext.codemanipulation;
12
13 import java.util.ArrayList JavaDoc;
14
15 import org.eclipse.text.edits.MultiTextEdit;
16 import org.eclipse.text.edits.ReplaceEdit;
17 import org.eclipse.text.edits.TextEdit;
18
19 import org.eclipse.core.runtime.Assert;
20 import org.eclipse.core.runtime.CoreException;
21 import org.eclipse.core.runtime.IProgressMonitor;
22 import org.eclipse.core.runtime.IStatus;
23 import org.eclipse.core.runtime.NullProgressMonitor;
24 import org.eclipse.core.runtime.OperationCanceledException;
25 import org.eclipse.core.runtime.Status;
26 import org.eclipse.core.runtime.SubProgressMonitor;
27 import org.eclipse.core.runtime.jobs.ISchedulingRule;
28
29 import org.eclipse.core.resources.IWorkspaceRunnable;
30
31 import org.eclipse.jface.text.BadLocationException;
32
33 import org.eclipse.jdt.core.Flags;
34 import org.eclipse.jdt.core.IBuffer;
35 import org.eclipse.jdt.core.ICompilationUnit;
36 import org.eclipse.jdt.core.IJavaElement;
37 import org.eclipse.jdt.core.JavaModelException;
38 import org.eclipse.jdt.core.Signature;
39 import org.eclipse.jdt.core.dom.ASTNode;
40 import org.eclipse.jdt.core.dom.Annotation;
41 import org.eclipse.jdt.core.dom.CompilationUnit;
42 import org.eclipse.jdt.core.dom.IBinding;
43 import org.eclipse.jdt.core.dom.IMethodBinding;
44 import org.eclipse.jdt.core.dom.ITypeBinding;
45 import org.eclipse.jdt.core.dom.IVariableBinding;
46 import org.eclipse.jdt.core.dom.MarkerAnnotation;
47 import org.eclipse.jdt.core.dom.MethodInvocation;
48 import org.eclipse.jdt.core.dom.Modifier;
49 import org.eclipse.jdt.core.dom.Name;
50 import org.eclipse.jdt.core.dom.QualifiedName;
51 import org.eclipse.jdt.core.dom.QualifiedType;
52 import org.eclipse.jdt.core.dom.SimpleName;
53 import org.eclipse.jdt.core.dom.Type;
54 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
55 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
56 import org.eclipse.jdt.core.search.IJavaSearchConstants;
57 import org.eclipse.jdt.core.search.IJavaSearchScope;
58 import org.eclipse.jdt.core.search.SearchEngine;
59 import org.eclipse.jdt.core.search.SearchPattern;
60 import org.eclipse.jdt.core.search.TypeNameMatch;
61
62 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
63 import org.eclipse.jdt.internal.corext.dom.NodeFinder;
64 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
65 import org.eclipse.jdt.internal.corext.util.Messages;
66 import org.eclipse.jdt.internal.corext.util.TypeNameMatchCollector;
67
68 import org.eclipse.jdt.internal.ui.JavaPlugin;
69 import org.eclipse.jdt.internal.ui.JavaUIStatus;
70 import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
71 import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;
72 import org.eclipse.jdt.internal.ui.text.correction.SimilarElementsRequestor;
73
74 /**
75  * Add imports to a compilation unit.
76  * The input is an array of full qualified type names. No elimination of unnecessary
77  * imports is done (use ImportStructure for this). Duplicates are not added.
78  * If the compilation unit is open in an editor, be sure to pass over its working copy.
79  */

80 public class AddImportsOperation implements IWorkspaceRunnable {
81     
82     public static interface IChooseImportQuery {
83         /**
84          * Selects an import from a list of choices.
85          * @param openChoices Array of found types
86          * @param containerName Name type to be imported
87          * @return Returns <code>null</code> to cancel the operation, or the
88          * selected imports.
89          */

90         TypeNameMatch chooseImport(TypeNameMatch[] openChoices, String JavaDoc containerName);
91     }
92     
93     private ICompilationUnit fCompilationUnit;
94     private final int fSelectionOffset;
95     private final int fSelectionLength;
96     private final IChooseImportQuery fQuery;
97     private IStatus fStatus;
98     private boolean fDoSave;
99     
100     
101     /**
102      * Generate import statements for the passed java elements
103      * Elements must be of type IType (-> single import) or IPackageFragment
104      * (on-demand-import). Other JavaElements are ignored
105      * @param cu The compilation unit
106      * @param selectionOffset Start of the current text selection
107      * @param selectionLength End of the current text selection
108      * @param query Query element to be used for UI interaction or <code>null</code> to not select anything
109      * when multiple possibilities are available
110      * @param save If set, the result will be saved
111      */

112     public AddImportsOperation(ICompilationUnit cu, int selectionOffset, int selectionLength, IChooseImportQuery query, boolean save) {
113         super();
114         Assert.isNotNull(cu);
115         
116         fCompilationUnit= cu;
117         fSelectionOffset= selectionOffset;
118         fSelectionLength= selectionLength;
119         fQuery= query;
120         fStatus= Status.OK_STATUS;
121         fDoSave= save;
122     }
123     
124     /**
125      * @return Returns the status.
126      */

127     public IStatus getStatus() {
128         return fStatus;
129     }
130
131     /**
132      * Runs the operation.
133      * @param monitor The progress monitor
134      * @throws CoreException
135      * @throws OperationCanceledException Runtime error thrown when operation is canceled.
136      */

137     public void run(IProgressMonitor monitor) throws CoreException, OperationCanceledException {
138         if (monitor == null) {
139             monitor= new NullProgressMonitor();
140         }
141         try {
142             monitor.beginTask(CodeGenerationMessages.AddImportsOperation_description, 4);
143             
144             CompilationUnit astRoot= JavaPlugin.getDefault().getASTProvider().getAST(fCompilationUnit, ASTProvider.WAIT_YES, new SubProgressMonitor(monitor, 1));
145
146             ImportRewrite importRewrite= StubUtility.createImportRewrite(astRoot, true);
147             
148             MultiTextEdit res= new MultiTextEdit();
149             
150             TextEdit edit= evaluateEdits(astRoot, importRewrite, fSelectionOffset, fSelectionLength, new SubProgressMonitor(monitor, 1));
151             if (edit == null) {
152                 return;
153             }
154             res.addChild(edit);
155                 
156             TextEdit importsEdit= importRewrite.rewriteImports(new SubProgressMonitor(monitor, 1));
157             res.addChild(importsEdit);
158             
159             JavaModelUtil.applyEdit(fCompilationUnit, res, fDoSave, new SubProgressMonitor(monitor, 1));
160         } catch (BadLocationException e) {
161             throw new CoreException(JavaUIStatus.createError(IStatus.ERROR, e));
162         } finally {
163             monitor.done();
164         }
165     }
166     
167     private TextEdit evaluateEdits(CompilationUnit root, ImportRewrite importRewrite, int offset, int length, IProgressMonitor monitor) throws BadLocationException, JavaModelException {
168         SimpleName nameNode= null;
169         if (root != null) { // got an AST
170
ASTNode node= NodeFinder.perform(root, offset, length);
171             if (node instanceof MarkerAnnotation) {
172                 node= ((Annotation) node).getTypeName();
173             }
174             if (node instanceof QualifiedName) {
175                 nameNode= ((QualifiedName) node).getName();
176             } else if (node instanceof SimpleName) {
177                 nameNode= (SimpleName) node;
178             }
179         }
180         
181         String JavaDoc name, simpleName, containerName;
182         int qualifierStart;
183         int simpleNameStart;
184         if (nameNode != null) {
185             simpleName= nameNode.getIdentifier();
186             simpleNameStart= nameNode.getStartPosition();
187             if (nameNode.getLocationInParent() == QualifiedName.NAME_PROPERTY) {
188                 Name qualifier= ((QualifiedName) nameNode.getParent()).getQualifier();
189                 containerName= qualifier.getFullyQualifiedName();
190                 name= JavaModelUtil.concatenateName(containerName, simpleName);
191                 qualifierStart= qualifier.getStartPosition();
192             } else if (nameNode.getParent().getLocationInParent() == QualifiedType.NAME_PROPERTY) {
193                 Type qualifier= ((QualifiedType) nameNode.getParent().getParent()).getQualifier();
194                 containerName= ASTNodes.asString(qualifier);
195                 name= JavaModelUtil.concatenateName(containerName, simpleName);
196                 qualifierStart= qualifier.getStartPosition();
197             } else if (nameNode.getLocationInParent() == MethodInvocation.NAME_PROPERTY) {
198                 ASTNode qualifier= ((MethodInvocation) nameNode.getParent()).getExpression();
199                 if (qualifier instanceof Name) {
200                     containerName= ASTNodes.asString(qualifier);
201                     name= JavaModelUtil.concatenateName(containerName, simpleName);
202                     qualifierStart= qualifier.getStartPosition();
203                 } else {
204                     return null;
205                 }
206             } else {
207                 containerName= ""; //$NON-NLS-1$
208
name= simpleName;
209                 qualifierStart= simpleNameStart;
210             }
211             
212             IBinding binding= nameNode.resolveBinding();
213             if (binding != null && !binding.isRecovered()) {
214                 if (binding instanceof ITypeBinding) {
215                     ITypeBinding typeBinding= (ITypeBinding) binding;
216                     String JavaDoc qualifiedBindingName= typeBinding.getQualifiedName();
217                     if (containerName.length() > 0 && !qualifiedBindingName.equals(name)) {
218                         return null;
219                     }
220                     
221                     String JavaDoc res= importRewrite.addImport(typeBinding);
222                     if (containerName.length() > 0 && !res.equals(simpleName)) {
223                         // adding import failed
224
return null;
225                     }
226                     return new ReplaceEdit(qualifierStart, simpleNameStart - qualifierStart, new String JavaDoc());
227                 } else if (binding instanceof IVariableBinding || binding instanceof IMethodBinding) {
228                     boolean isField= binding instanceof IVariableBinding;
229                     ITypeBinding declaringClass= isField ? ((IVariableBinding) binding).getDeclaringClass() : ((IMethodBinding) binding).getDeclaringClass();
230                     if (declaringClass == null) {
231                         return null; // variableBinding.getDeclaringClass() is null for array.length
232
}
233                     if (Modifier.isStatic(binding.getModifiers())) {
234                         if (Modifier.isPrivate(declaringClass.getModifiers())) {
235                             fStatus= JavaUIStatus.createError(IStatus.ERROR, Messages.format(CodeGenerationMessages.AddImportsOperation_error_not_visible_class, declaringClass.getName()), null);
236                             return null;
237                         }
238                         
239                         if (containerName.length() > 0) {
240                             if (containerName.equals(declaringClass.getName()) || containerName.equals(declaringClass.getQualifiedName()) ) {
241                                 String JavaDoc res= importRewrite.addStaticImport(declaringClass.getQualifiedName(), binding.getName(), isField);
242                                 if (!res.equals(simpleName)) {
243                                     // adding import failed
244
return null;
245                                 }
246                                 return new ReplaceEdit(qualifierStart, simpleNameStart - qualifierStart, ""); //$NON-NLS-1$
247
}
248                         }
249                     }
250                     return null; // no static imports for packages
251
} else {
252                     return null;
253                 }
254             }
255             if (binding != null && binding.getKind() != IBinding.TYPE) {
256                 // recovered binding
257
return null;
258             }
259             
260         } else {
261             IBuffer buffer= fCompilationUnit.getBuffer();
262             
263             qualifierStart= getNameStart(buffer, offset);
264             int nameEnd= getNameEnd(buffer, offset + length);
265             int len= nameEnd - qualifierStart;
266             name= buffer.getText(qualifierStart, len).trim();
267             if (name.length() == 0) {
268                 return null;
269             }
270             
271             simpleName= Signature.getSimpleName(name);
272             containerName= Signature.getQualifier(name);
273             
274             simpleNameStart= getSimpleNameStart(buffer, qualifierStart, containerName);
275             
276             int res= importRewrite.getDefaultImportRewriteContext().findInContext(containerName, simpleName, ImportRewriteContext.KIND_TYPE);
277             if (res == ImportRewriteContext.RES_NAME_CONFLICT) {
278                 fStatus= JavaUIStatus.createError(IStatus.ERROR, CodeGenerationMessages.AddImportsOperation_error_importclash, null);
279                 return null;
280             } else if (res == ImportRewriteContext.RES_NAME_FOUND) {
281                 return new ReplaceEdit(qualifierStart, simpleNameStart - qualifierStart, ""); //$NON-NLS-1$
282
}
283         }
284         IJavaSearchScope searchScope= SearchEngine.createJavaSearchScope(new IJavaElement[] { fCompilationUnit.getJavaProject() });
285         
286         TypeNameMatch[] types= findAllTypes(simpleName, searchScope, nameNode, new SubProgressMonitor(monitor, 1));
287         if (types.length == 0) {
288             fStatus= JavaUIStatus.createError(IStatus.ERROR, Messages.format(CodeGenerationMessages.AddImportsOperation_error_notresolved_message, simpleName), null);
289             return null;
290         }
291         
292         if (monitor.isCanceled()) {
293             throw new OperationCanceledException();
294         }
295         TypeNameMatch chosen;
296         if (types.length > 1 && fQuery != null) {
297             chosen= fQuery.chooseImport(types, containerName);
298             if (chosen == null) {
299                 throw new OperationCanceledException();
300             }
301         } else {
302             chosen= types[0];
303         }
304         importRewrite.addImport(chosen.getFullyQualifiedName());
305         return new ReplaceEdit(qualifierStart, simpleNameStart - qualifierStart, ""); //$NON-NLS-1$
306
}
307
308     
309     private int getNameStart(IBuffer buffer, int pos) throws BadLocationException {
310         while (pos > 0) {
311             char ch= buffer.getChar(pos - 1);
312             if (!Character.isJavaIdentifierPart(ch) && ch != '.') {
313                 return pos;
314             }
315             pos--;
316         }
317         return pos;
318     }
319     
320     private int getNameEnd(IBuffer doc, int pos) throws BadLocationException {
321         if (pos > 0) {
322             if (Character.isWhitespace(doc.getChar(pos - 1))) {
323                 return pos;
324             }
325         }
326         int len= doc.getLength();
327         while (pos < len) {
328             char ch= doc.getChar(pos);
329             if (!Character.isJavaIdentifierPart(ch)) {
330                 return pos;
331             }
332             pos++;
333         }
334         return pos;
335     }
336     
337     private int getSimpleNameStart(IBuffer buffer, int nameStart, String JavaDoc containerName) throws BadLocationException {
338         int containerLen= containerName.length();
339         int docLen= buffer.getLength();
340         if ((containerLen > 0) && (nameStart + containerLen + 1 < docLen)) {
341             for (int k= 0; k < containerLen; k++) {
342                 if (buffer.getChar(nameStart + k) != containerName.charAt(k)) {
343                     return nameStart;
344                 }
345             }
346             if (buffer.getChar(nameStart + containerLen) == '.') {
347                 return nameStart + containerLen + 1;
348             }
349         }
350         return nameStart;
351     }
352     
353     private int getSearchForConstant(int typeKinds) {
354         final int CLASSES= SimilarElementsRequestor.CLASSES;
355         final int INTERFACES= SimilarElementsRequestor.INTERFACES;
356         final int ENUMS= SimilarElementsRequestor.ENUMS;
357         final int ANNOTATIONS= SimilarElementsRequestor.ANNOTATIONS;
358
359         switch (typeKinds & (CLASSES | INTERFACES | ENUMS | ANNOTATIONS)) {
360             case CLASSES: return IJavaSearchConstants.CLASS;
361             case INTERFACES: return IJavaSearchConstants.INTERFACE;
362             case ENUMS: return IJavaSearchConstants.ENUM;
363             case ANNOTATIONS: return IJavaSearchConstants.ANNOTATION_TYPE;
364             case CLASSES | INTERFACES: return IJavaSearchConstants.CLASS_AND_INTERFACE;
365             case CLASSES | ENUMS: return IJavaSearchConstants.CLASS_AND_ENUM;
366             default: return IJavaSearchConstants.TYPE;
367         }
368     }
369     
370     
371     /*
372      * Finds a type by the simple name.
373      */

374     private TypeNameMatch[] findAllTypes(String JavaDoc simpleTypeName, IJavaSearchScope searchScope, SimpleName nameNode, IProgressMonitor monitor) throws JavaModelException {
375         boolean is50OrHigher= JavaModelUtil.is50OrHigher(fCompilationUnit.getJavaProject());
376         
377         int typeKinds= SimilarElementsRequestor.ALL_TYPES;
378         if (nameNode != null) {
379             typeKinds= ASTResolving.getPossibleTypeKinds(nameNode, is50OrHigher);
380         }
381         
382         ArrayList JavaDoc typeInfos= new ArrayList JavaDoc();
383         TypeNameMatchCollector requestor= new TypeNameMatchCollector(typeInfos);
384         int matchMode= SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE;
385         new SearchEngine().searchAllTypeNames(null, matchMode, simpleTypeName.toCharArray(), matchMode, getSearchForConstant(typeKinds), searchScope, requestor, IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, monitor);
386
387         ArrayList JavaDoc typeRefsFound= new ArrayList JavaDoc(typeInfos.size());
388         for (int i= 0, len= typeInfos.size(); i < len; i++) {
389             TypeNameMatch curr= (TypeNameMatch) typeInfos.get(i);
390             if (curr.getPackageName().length() > 0) { // do not suggest imports from the default package
391
if (isOfKind(curr, typeKinds, is50OrHigher) && isVisible(curr)) {
392                     typeRefsFound.add(curr);
393                 }
394             }
395         }
396         return (TypeNameMatch[]) typeRefsFound.toArray(new TypeNameMatch[typeRefsFound.size()]);
397     }
398     
399     private boolean isOfKind(TypeNameMatch curr, int typeKinds, boolean is50OrHigher) {
400         int flags= curr.getModifiers();
401         if (Flags.isAnnotation(flags)) {
402             return is50OrHigher && ((typeKinds & SimilarElementsRequestor.ANNOTATIONS) != 0);
403         }
404         if (Flags.isEnum(flags)) {
405             return is50OrHigher && ((typeKinds & SimilarElementsRequestor.ENUMS) != 0);
406         }
407         if (Flags.isInterface(flags)) {
408             return (typeKinds & SimilarElementsRequestor.INTERFACES) != 0;
409         }
410         return (typeKinds & SimilarElementsRequestor.CLASSES) != 0;
411     }
412
413     
414     private boolean isVisible(TypeNameMatch curr) {
415         int flags= curr.getModifiers();
416         if (Flags.isPrivate(flags)) {
417             return false;
418         }
419         if (Flags.isPublic(flags) || Flags.isProtected(flags)) {
420             return true;
421         }
422         return curr.getPackageName().equals(fCompilationUnit.getParent().getElementName());
423     }
424     
425     
426     /**
427      * @return Returns the scheduling rule for this operation
428      */

429     public ISchedulingRule getScheduleRule() {
430         return fCompilationUnit.getJavaProject().getResource();
431     }
432         
433 }
434
Popular Tags