KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > editor > imports > ComputeImports


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.netbeans.modules.java.editor.imports;
20
21 import com.sun.source.tree.IdentifierTree;
22 import com.sun.source.tree.MemberSelectTree;
23 import com.sun.source.tree.Scope;
24 import com.sun.source.tree.Tree.Kind;
25 import com.sun.source.tree.VariableTree;
26 import com.sun.source.util.TreePath;
27 import com.sun.source.util.TreePathScanner;
28 import java.awt.Dialog JavaDoc;
29 import java.io.IOException JavaDoc;
30 import java.util.ArrayList JavaDoc;
31 import java.util.Collections JavaDoc;
32 import java.util.EnumSet JavaDoc;
33 import java.util.HashMap JavaDoc;
34 import java.util.HashSet JavaDoc;
35 import java.util.List JavaDoc;
36 import java.util.Map JavaDoc;
37 import java.util.Set JavaDoc;
38 import java.util.logging.Level JavaDoc;
39 import java.util.logging.Logger JavaDoc;
40 import javax.lang.model.element.Element;
41 import javax.lang.model.element.ElementKind;
42 import javax.lang.model.element.PackageElement;
43 import javax.lang.model.element.TypeElement;
44 import javax.lang.model.type.DeclaredType;
45 import javax.lang.model.type.TypeKind;
46 import javax.lang.model.type.TypeMirror;
47 import javax.lang.model.util.Types;
48 import javax.swing.text.BadLocationException JavaDoc;
49 import org.netbeans.api.java.source.CancellableTask;
50 import org.netbeans.api.java.source.ClassIndex;
51 import org.netbeans.api.java.source.CompilationController;
52 import org.netbeans.api.java.source.CompilationInfo;
53 import org.netbeans.api.java.source.JavaSource;
54 import org.netbeans.api.java.source.JavaSource.Phase;
55 import org.netbeans.api.java.source.SourceUtils;
56 import org.netbeans.api.java.source.ClassIndex.NameKind;
57 import org.netbeans.api.java.source.ElementHandle;
58 import org.netbeans.api.java.source.support.CancellableTreePathScanner;
59 import org.openide.DialogDescriptor;
60 import org.openide.DialogDisplayer;
61 import org.openide.ErrorManager;
62 import org.openide.filesystems.FileObject;
63 import org.openide.util.Union2;
64
65 /**
66  *
67  * @author Jan Lahoda
68  */

69 public class ComputeImports {
70     
71     private static final String JavaDoc ERROR = "<error>";
72     
73     /** Creates a new instance of JavaFixAllImports */
74     public ComputeImports() {
75     }
76     
77     private boolean cancelled;
78     
79     public synchronized void cancel() {
80         cancelled = true;
81         
82         if (visitor != null)
83             visitor.cancel();
84     }
85     
86     private synchronized boolean isCancelled() {
87         return cancelled;
88     }
89     
90     public Pair<Map JavaDoc<String JavaDoc, List JavaDoc<TypeElement>>, Map JavaDoc<String JavaDoc, List JavaDoc<TypeElement>>> computeCandidates(CompilationInfo info) {
91         return computeCandidates(info, Collections.<String JavaDoc>emptySet());
92     }
93     
94     private TreeVisitorImpl visitor;
95     
96     private synchronized void setVisitor(TreeVisitorImpl visitor) {
97         this.visitor = visitor;
98     }
99     
100     Pair<Map JavaDoc<String JavaDoc, List JavaDoc<TypeElement>>, Map JavaDoc<String JavaDoc, List JavaDoc<TypeElement>>> computeCandidates(CompilationInfo info, Set JavaDoc<String JavaDoc> forcedUnresolved) {
101         Map JavaDoc<String JavaDoc, List JavaDoc<TypeElement>> candidates = new HashMap JavaDoc<String JavaDoc, List JavaDoc<TypeElement>>();
102         Map JavaDoc<String JavaDoc, List JavaDoc<TypeElement>> notFilteredCandidates = new HashMap JavaDoc<String JavaDoc, List JavaDoc<TypeElement>>();
103         TreeVisitorImpl v = new TreeVisitorImpl(info);
104
105         setVisitor(v);
106         
107         v.scan(info.getCompilationUnit(), new HashMap JavaDoc<String JavaDoc, Object JavaDoc>());
108         
109         setVisitor(null);
110         
111         Set JavaDoc<String JavaDoc> unresolvedNames = new HashSet JavaDoc<String JavaDoc>(v.unresolved);
112         
113         unresolvedNames.addAll(forcedUnresolved);
114         
115         for (String JavaDoc unresolved : unresolvedNames) {
116             if (isCancelled())
117                 return new Pair(Collections.emptyMap(), Collections.emptyMap());
118             
119             List JavaDoc<TypeElement> classes = new ArrayList JavaDoc<TypeElement>();
120             
121             for (ElementHandle<TypeElement> typeNames : info.getJavaSource().getClasspathInfo().getClassIndex().getDeclaredTypes(unresolved, NameKind.SIMPLE_NAME,EnumSet.allOf(ClassIndex.SearchScope.class))) {
122                 TypeElement te = info.getElements().getTypeElement(typeNames.getQualifiedName());
123                 
124                 if (te == null) {
125                     Logger.getLogger(ComputeImports.class.getName()).log(Level.INFO, "Cannot resolve type element \"" + typeNames + "\".");
126                     continue;
127                 }
128                 
129                 classes.add(te);
130             }
131             
132             candidates.put(unresolved, new ArrayList JavaDoc(classes));
133             notFilteredCandidates.put(unresolved, classes);
134         }
135         
136         boolean wasChanged = true;
137         
138         while (wasChanged) {
139             if (isCancelled())
140                 return new Pair(Collections.emptyMap(), Collections.emptyMap());
141             
142             wasChanged = false;
143             
144             for (Hint hint: v.hints) {
145                 wasChanged |= hint.filter(info, notFilteredCandidates, candidates);
146             }
147         }
148             
149         return new Pair<Map JavaDoc<String JavaDoc, List JavaDoc<TypeElement>>, Map JavaDoc<String JavaDoc, List JavaDoc<TypeElement>>>(candidates, notFilteredCandidates);
150     }
151     
152     private static boolean filter(Types types, List JavaDoc<TypeElement> left, List JavaDoc<TypeElement> right, boolean leftReadOnly, boolean rightReadOnly) {
153         boolean changed = false;
154         Map JavaDoc<TypeElement, List JavaDoc<TypeElement>> validPairs = new HashMap JavaDoc<TypeElement, List JavaDoc<TypeElement>>();
155         
156         for (TypeElement l : left) {
157             List JavaDoc<TypeElement> valid = new ArrayList JavaDoc<TypeElement>();
158             
159             for (TypeElement r : right) {
160                 TypeMirror t1 = types.erasure(l.asType());
161                 TypeMirror t2 = types.erasure(r.asType());
162                 
163 // System.err.println("t2 = " + t2);
164
// System.err.println("t1 = " + t1);
165
// System.err.println("types= " + types.getClass());
166
// System.err.println("types.isAssignable(t2, t1) = " + types.isAssignable(t2, t1));
167
// System.err.println("types.isSubtype(t2, t1) = " + types.isSubtype(t2, t1));
168
// System.err.println("types.isAssignable(t1,t2) = " + types.isAssignable(t1,t2));
169
// System.err.println("types.isSubtype(t1, t2) = " + types.isSubtype(t1, t2));
170
if (types.isAssignable(t2, t1))
171                     valid.add(r);
172             }
173             
174 // System.err.println("l = " + l );
175
// System.err.println("valid = " + valid );
176
validPairs.put(l, valid);
177         }
178         
179         Set JavaDoc<TypeElement> validRights = new HashSet JavaDoc<TypeElement>();
180         
181         for (TypeElement l : validPairs.keySet()) {
182             List JavaDoc<TypeElement> valid = validPairs.get(l);
183             
184             if (valid.isEmpty() && !leftReadOnly) {
185                 //invalid left:
186
left.remove(l);
187                 changed = true;
188             }
189             
190             validRights.addAll(valid);
191         }
192         
193         if (!rightReadOnly)
194             changed = right.retainAll(validRights) | changed;
195         
196         return changed;
197     }
198     
199     private static EnumSet JavaDoc<TypeKind> INVALID_TYPES = EnumSet.of(TypeKind.NULL, TypeKind.NONE, TypeKind.OTHER, TypeKind.ERROR);
200     
201     private static class TreeVisitorImpl extends CancellableTreePathScanner<Void JavaDoc, Map JavaDoc<String JavaDoc, Object JavaDoc>> {
202         
203         private CompilationInfo info;
204         private Set JavaDoc<String JavaDoc> unresolved;
205         
206         private List JavaDoc<Hint> hints;
207         
208         public TreeVisitorImpl(CompilationInfo info) {
209             this.info = info;
210             unresolved = new HashSet JavaDoc<String JavaDoc>();
211             hints = new ArrayList JavaDoc<Hint>();
212         }
213         
214         @Override JavaDoc
215         public Void JavaDoc visitMemberSelect(MemberSelectTree tree, Map JavaDoc<String JavaDoc, Object JavaDoc> p) {
216             if (tree.getExpression().getKind() == Kind.IDENTIFIER) {
217                 p.put("request", null);
218             }
219             
220             scan(tree.getExpression(), p);
221             
222             Union2<String JavaDoc, DeclaredType> leftSide = (Union2<String JavaDoc, DeclaredType>) p.remove("result");
223             
224             p.remove("request");
225             
226             if (leftSide != null && leftSide.hasFirst()) {
227                 String JavaDoc rightSide = tree.getIdentifier().toString();
228                 
229                 if (ERROR.equals(rightSide))
230                     rightSide = "";
231                 
232                 boolean isMethodInvocation = getCurrentPath().getParentPath().getLeaf().getKind() == Kind.METHOD_INVOCATION;
233                 
234                 //Ignore .class (which will not help us much):
235
if (!"class".equals(rightSide))
236                     hints.add(new EnclosedHint(leftSide.first(), rightSide, !isMethodInvocation));
237             }
238             
239             return null;
240         }
241         
242         @Override JavaDoc
243         public Void JavaDoc visitVariable(VariableTree tree, Map JavaDoc<String JavaDoc, Object JavaDoc> p) {
244             scan(tree.getModifiers(), p);
245             
246             if (tree.getType().getKind() == Kind.IDENTIFIER) {
247                 p.put("request", null);
248             }
249             
250             scan(tree.getType(), p);
251             
252             Union2<String JavaDoc, DeclaredType> leftSide = (Union2<String JavaDoc, DeclaredType>) p.remove("result");
253             
254             p.remove("request");
255             
256             Union2<String JavaDoc, DeclaredType> rightSide = null;
257             
258             if (leftSide != null && tree.getInitializer() != null) {
259                 Element el = info.getTrees().getElement(new TreePath(getCurrentPath(),tree.getInitializer()));
260                 TypeMirror rightType = el != null ? el.asType() : null;
261                 
262 // System.err.println("rightType = " + rightType );
263
// System.err.println("tree.getInitializer()=" + tree.getInitializer());
264
// System.err.println("rightType.getKind()=" + rightType.getKind());
265
// System.err.println("INVALID_TYPES.contains(rightType.getKind())=" + INVALID_TYPES.contains(rightType.getKind()));
266
if (rightType != null && rightType.getKind() == TypeKind.DECLARED) {
267                     rightSide = Union2.<String JavaDoc, DeclaredType>createSecond((DeclaredType) rightType);
268                 } else {
269                     if (tree.getInitializer().getKind() == Kind.NEW_CLASS || tree.getInitializer().getKind() == Kind.NEW_ARRAY) {
270                         p.put("request", null);
271                     }
272                 }
273             }
274             
275             scan(tree.getInitializer(), p);
276             
277             rightSide = rightSide == null ? (Union2<String JavaDoc, DeclaredType>) p.remove("result") : rightSide;
278             
279             p.remove("result");
280             
281 // System.err.println("rightSide = " + rightSide );
282

283             p.remove("request");
284             
285             if (leftSide != null && rightSide != null) {
286                 if (!(leftSide instanceof TypeMirror) || !(rightSide instanceof TypeMirror)) {
287                     hints.add(new TypeHint(leftSide, rightSide));
288                 }
289             }
290             
291             return null;
292         }
293         
294         @Override JavaDoc
295         public Void JavaDoc visitIdentifier(IdentifierTree tree, Map JavaDoc<String JavaDoc, Object JavaDoc> p) {
296             super.visitIdentifier(tree, p);
297             
298 // System.err.println("tree=" + tree);
299
Element el = info.getTrees().getElement(getCurrentPath());
300             if (el != null && (el.getKind().isClass() || el.getKind().isInterface() || el.getKind() == ElementKind.PACKAGE)) {
301                 TypeMirror type = el.asType();
302                 String JavaDoc simpleName = null;
303                 
304                 if (type.getKind() == TypeKind.ERROR) {
305                     simpleName = el.getSimpleName().toString();
306                 }
307                 
308                 if (type.getKind() == TypeKind.PACKAGE) {
309                     //does the package really exists?
310
String JavaDoc s = ((PackageElement) el).getQualifiedName().toString();
311                     if (info.getElements().getPackageElement(s) == null) {
312                         //probably situation like:
313
//Map.Entry e;
314
//where Map is not imported
315
simpleName = el.getSimpleName().toString();
316                     }
317                 }
318                 
319                 if (ERROR.equals(simpleName)) {
320                     simpleName = null;
321                 }
322                 
323                 if (simpleName != null) {
324                     unresolved.add(simpleName);
325                     
326                     Scope currentScope = info.getTrees().getScope(getCurrentPath());
327                     
328                     hints.add(new AccessibleHint(simpleName, currentScope));
329                     
330                     if (p.containsKey("request")) {
331                         p.put("result", Union2.<String JavaDoc, DeclaredType>createFirst(simpleName));
332                     }
333                 } else {
334                     if (p.containsKey("request") && type.getKind() == TypeKind.DECLARED) {
335                         p.put("result", Union2.<String JavaDoc, DeclaredType>createSecond((DeclaredType) type));
336                     }
337                 }
338             }
339             
340             p.remove("request");
341             
342             return null;
343         }
344         
345     }
346     
347     public static interface Hint {
348         
349         public abstract boolean filter(CompilationInfo info, Map JavaDoc<String JavaDoc, List JavaDoc<TypeElement>> rawCandidates, Map JavaDoc<String JavaDoc, List JavaDoc<TypeElement>> candidates);
350         
351     }
352     
353     public static final class TypeHint implements Hint {
354         
355         private Union2<String JavaDoc, DeclaredType> left;
356         private Union2<String JavaDoc, DeclaredType> right;
357         
358         public TypeHint(Union2<String JavaDoc, DeclaredType> left, Union2<String JavaDoc, DeclaredType> right) {
359             this.left = left;
360             this.right = right;
361         }
362         
363         public boolean filter(CompilationInfo info, Map JavaDoc<String JavaDoc, List JavaDoc<TypeElement>> rawCandidates, Map JavaDoc<String JavaDoc, List JavaDoc<TypeElement>> candidates) {
364             List JavaDoc<TypeElement> left = null;
365             List JavaDoc<TypeElement> right = null;
366             boolean leftReadOnly = false;
367             boolean rightReadOnly = false;
368             
369             if (this.left.hasSecond()) {
370                 Element el = this.left.second().asElement();
371                 
372                 //TODO do not use instanceof!
373
if (el instanceof TypeElement) {
374                     left = Collections.singletonList((TypeElement) el);
375                     leftReadOnly = true;
376                 }
377             } else {
378                 left = candidates.get(this.left.first());
379             }
380             
381             if (this.right.hasSecond()) {
382                 Element el = this.right.second().asElement();
383                 
384                 //TODO do not use instanceof!
385
if (el instanceof TypeElement) {
386                     right = Collections.singletonList((TypeElement) el);
387                     rightReadOnly = true;
388                 }
389             } else {
390                 right = candidates.get(this.right.first());
391             }
392             
393             if (left != null && right != null && !left.isEmpty() && !right.isEmpty()) {
394                 return ComputeImports.filter(info.getTypes(), left, right, leftReadOnly, rightReadOnly);
395             }
396             
397             return false;
398         }
399         
400     }
401     
402     public static final class EnclosedHint implements Hint {
403         
404         private String JavaDoc simpleName;
405         private String JavaDoc methodName;
406         private boolean allowPrefix;
407         
408         public EnclosedHint(String JavaDoc simpleName, String JavaDoc methodName, boolean allowPrefix) {
409             this.simpleName = simpleName;
410             this.methodName = methodName;
411             this.allowPrefix = allowPrefix;
412         }
413         
414         public boolean filter(CompilationInfo info, Map JavaDoc<String JavaDoc, List JavaDoc<TypeElement>> rawCandidates, Map JavaDoc<String JavaDoc, List JavaDoc<TypeElement>> candidates) {
415             List JavaDoc<TypeElement> cands = candidates.get(simpleName);
416             
417             if (cands == null || cands.isEmpty())
418                 return false;
419             
420             List JavaDoc<TypeElement> toRemove = new ArrayList JavaDoc<TypeElement>();
421             
422             for (TypeElement te : cands) {
423                 boolean found = false;
424                 
425                 for (Element e : te.getEnclosedElements()) {
426                     String JavaDoc simpleName = e.getSimpleName().toString();
427                     
428                     if (methodName.contentEquals(simpleName)) {
429                         found = true;
430                         break;
431                     }
432                     
433                     if (allowPrefix && simpleName.startsWith(methodName)) {
434                         found = true;
435                         break;
436                     }
437                 }
438                 
439                 if (!found) {
440                     toRemove.add(te);
441                 }
442             }
443             
444             return cands.removeAll(toRemove);
445         }
446         
447     }
448     
449     public static final class AccessibleHint implements Hint {
450         
451         private String JavaDoc simpleName;
452         private Scope scope;
453         
454         public AccessibleHint(String JavaDoc simpleName, Scope scope) {
455             this.simpleName = simpleName;
456             this.scope = scope;
457         }
458         
459         public boolean filter(CompilationInfo info, Map JavaDoc<String JavaDoc, List JavaDoc<TypeElement>> rawCandidates, Map JavaDoc<String JavaDoc, List JavaDoc<TypeElement>> candidates) {
460             List JavaDoc<TypeElement> cands = candidates.get(simpleName);
461             
462             if (cands == null || cands.isEmpty())
463                 return false;
464             
465             List JavaDoc<TypeElement> toRemove = new ArrayList JavaDoc<TypeElement>();
466             
467             for (TypeElement te : cands) {
468                 if (!info.getTrees().isAccessible(scope, te)) {
469                     toRemove.add(te);
470                 }
471             }
472             
473             //remove it from the raw candidates too:
474
rawCandidates.get(simpleName).removeAll(toRemove);
475             
476             return cands.removeAll(toRemove);
477         }
478         
479     }
480     
481     public static class Pair<A, B> {
482         
483         public A a;
484         public B b;
485         
486         public Pair(A a, B b) {
487             this.a = a;
488             this.b = b;
489         }
490     }
491     
492 }
493
Popular Tags