KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > api > java > source > SourceUtils


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
20 package org.netbeans.api.java.source;
21
22 import java.io.File JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.net.MalformedURLException JavaDoc;
25 import java.net.URI JavaDoc;
26 import java.net.URL JavaDoc;
27 import java.util.*;
28
29 import javax.lang.model.element.*;
30 import javax.lang.model.element.VariableElement;
31 import javax.lang.model.type.ArrayType;
32 import javax.lang.model.type.DeclaredType;
33 import javax.lang.model.type.TypeKind;
34 import javax.lang.model.type.TypeMirror;
35 import javax.lang.model.util.ElementFilter;
36 import javax.tools.JavaFileObject.Kind;
37
38 import com.sun.source.tree.*;
39 import com.sun.source.util.TreePath;
40 import com.sun.source.util.TreePathScanner;
41 import com.sun.tools.javac.api.JavacTaskImpl;
42 import com.sun.tools.javac.api.JavacTrees;
43 import com.sun.tools.javac.code.Flags;
44 import com.sun.tools.javac.code.Kinds;
45 import com.sun.tools.javac.code.Symbol;
46 import com.sun.tools.javac.code.Symbol.*;
47 import com.sun.tools.javac.code.Symtab;
48 import com.sun.tools.javac.code.Type;
49 import com.sun.tools.javac.comp.Check;
50 import com.sun.tools.javac.model.JavacElements;
51 import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
52 import com.sun.tools.javac.util.Context;
53 import com.sun.tools.javac.util.Name;
54 import javax.lang.model.type.WildcardType;
55
56 import org.netbeans.api.java.classpath.ClassPath;
57 import org.netbeans.api.java.queries.JavadocForBinaryQuery;
58 import org.netbeans.api.java.queries.SourceForBinaryQuery;
59 import org.netbeans.api.java.source.CancellableTask;
60 import org.netbeans.api.java.source.ClasspathInfo.PathKind;
61 import org.netbeans.api.java.source.JavaSource.Phase;
62 import org.netbeans.api.java.source.WorkingCopy;
63 import org.netbeans.modules.java.JavaDataLoader;
64 import org.netbeans.modules.java.source.parsing.FileObjects;
65 import org.netbeans.modules.java.source.usages.Index;
66 import org.netbeans.modules.java.source.usages.RepositoryUpdater;
67 import org.netbeans.spi.java.classpath.support.ClassPathSupport;
68
69 import org.openide.filesystems.FileObject;
70 import org.openide.filesystems.FileStateInvalidException;
71 import org.openide.filesystems.URLMapper;
72 import org.openide.util.Exceptions;
73 import org.openide.util.RequestProcessor;
74
75 /**
76  *
77  * @author Dusan Balek
78  */

79 public class SourceUtils {
80      
81     private static final String JavaDoc PACKAGE_SUMMARY = "package-summary"; //NOI18N
82

83     private SourceUtils() {}
84     
85     public static Tree treeFor(CompilationInfo info, Element element) {
86         Context ctx = getSourceContextFor(info.getClasspathInfo(), Phase.ELEMENTS_RESOLVED, element);
87         if (ctx != null) {
88             Element e = getSourceElementFor(element, ctx);
89             if (e != null)
90                 return JavacElements.instance(ctx).getTree((Symbol)e);
91         }
92         return null;
93     }
94
95     public static TreePath pathFor(CompilationInfo info, Element element) {
96         Context ctx = getSourceContextFor(info.getClasspathInfo(), Phase.ELEMENTS_RESOLVED, element);
97         if (ctx != null) {
98             Element e = getSourceElementFor(element, ctx);
99             if (e != null)
100                 return JavacTrees.instance(ctx).getPath(e);
101         }
102         return null;
103     }
104     
105     public static Element getImplementationOf(CompilationInfo info, ExecutableElement method, TypeElement origin) {
106         Context c = ((JavacTaskImpl) info.getJavacTask()).getContext();
107         return ((MethodSymbol)method).implementation((TypeSymbol)origin, com.sun.tools.javac.code.Types.instance(c), true);
108     }
109
110     public static boolean checkTypesAssignable(CompilationInfo info, TypeMirror from, TypeMirror to) {
111         Context c = ((JavacTaskImpl) info.getJavacTask()).getContext();
112         if (from.getKind() == TypeKind.DECLARED) {
113             com.sun.tools.javac.util.List<Type> typeVars = com.sun.tools.javac.util.List.nil();
114             for (TypeMirror tm : ((DeclaredType)from).getTypeArguments()) {
115                 if (tm.getKind() == TypeKind.TYPEVAR)
116                     typeVars = typeVars.append((Type)tm);
117             }
118             if (!typeVars.isEmpty())
119                 from = new Type.ForAll(typeVars, (Type)from);
120         } else if (from.getKind() == TypeKind.WILDCARD) {
121             WildcardType wt = (WildcardType)from;
122             from = wt.getExtendsBound();
123             if (from == null)
124                 from = Symtab.instance(c).objectType;
125         }
126         return Check.instance(c).checkType(null, (Type)from, (Type)to).getKind() != TypeKind.ERROR;
127     }
128
129     /**
130      * Returns the type element within which this member or constructor
131      * is declared. Does not accept pakages
132      * If this is the declaration of a top-level type (a non-nested class
133      * or interface), returns null.
134      *
135      * @return the type declaration within which this member or constructor
136      * is declared, or null if there is none
137      * @throws IllegalArgumentException if the provided element is a package element
138      */

139     public static TypeElement getEnclosingTypeElement( Element element ) throws IllegalArgumentException JavaDoc {
140     
141     if( element.getKind() == ElementKind.PACKAGE ) {
142         throw new IllegalArgumentException JavaDoc();
143     }
144     
145         if (element.getEnclosingElement().getKind() == ElementKind.PACKAGE) {
146             //element is a top level class, returning null according to the contract:
147
return null;
148         }
149         
150     while( !(element.getEnclosingElement().getKind().isClass() ||
151            element.getEnclosingElement().getKind().isInterface()) ) {
152         element = element.getEnclosingElement();
153     }
154     
155     return (TypeElement)element.getEnclosingElement(); // Wrong
156
}
157     
158     public static TypeElement getOutermostEnclosingTypeElement( Element element ) {
159     
160     Element ec = getEnclosingTypeElement( element );
161     if (ec == null) {
162         ec = element;
163     }
164     
165     while( ec.getEnclosingElement().getKind().isClass() ||
166            ec.getEnclosingElement().getKind().isInterface() ) {
167     
168         ec = ec.getEnclosingElement();
169     }
170         
171     return (TypeElement)ec;
172     }
173     
174     private static EnumSet JAVA_JFO_KIND = EnumSet.of(Kind.CLASS, Kind.SOURCE);
175         
176     
177     /**Resolve full qualified name in the given context. Adds import statement as necessary.
178      * Returns name that resolved to a given FQN in given context (either simple name
179      * or full qualified name). Handles import conflicts.
180      *
181      * <br><b>Note:</b> if the <code>info</code> passed to this method is not an instance of {@link WorkingCopy},
182      * missing import statement is added from a separate modification task executed asynchronously.
183      * <br><b>Note:</b> after calling this method, it is not permitted to rewrite copy.getCompilationUnit().
184      *
185      * @param info CompilationInfo over which the method should work
186      * @param context in which the fully qualified should be resolved
187      * @param fqn the fully qualified name to resolve
188      * @return either a simple name or a FQN that will resolve to given fqn in given context
189      */

190     public static String JavaDoc resolveImport(final CompilationInfo info, final TreePath context, final String JavaDoc fqn) throws NullPointerException JavaDoc, IOException JavaDoc {
191         if (info == null)
192             throw new NullPointerException JavaDoc();
193         if (context == null)
194             throw new NullPointerException JavaDoc();
195         if (fqn == null)
196             throw new NullPointerException JavaDoc();
197         
198         CompilationUnitTree cut = info.getCompilationUnit();
199         Scope scope = info.getTrees().getScope(context);
200         String JavaDoc qName = fqn;
201         StringBuilder JavaDoc sqName = new StringBuilder JavaDoc();
202         String JavaDoc sName = null;
203         boolean clashing = false;
204         ElementUtilities eu = info.getElementUtilities();
205         ElementUtilities.ElementAcceptor acceptor = new ElementUtilities.ElementAcceptor() {
206             public boolean accept(Element e, TypeMirror type) {
207                 return e.getKind().isClass() || e.getKind().isInterface();
208             }
209         };
210         while(qName != null && qName.length() > 0) {
211             int lastDot = qName.lastIndexOf('.');
212             String JavaDoc simple = qName.substring(lastDot < 0 ? 0 : lastDot + 1);
213             if (sName == null)
214                 sName = simple;
215             else
216                 sqName.insert(0, '.');
217             sqName.insert(0, simple);
218             if (info.getElements().getTypeElement(qName) != null) {
219                 boolean matchFound = false;
220                 for(Element e : eu.getLocalMembersAndVars(scope, acceptor)) {
221                     if (simple.contentEquals(e.getSimpleName())) {
222                         //either a clash or already imported:
223
if (qName.contentEquals(((TypeElement)e).getQualifiedName())) {
224                             return sqName.toString();
225                         } else if (fqn == qName) {
226                             clashing = true;
227                         }
228                         matchFound = true;
229                         break;
230                     }
231                 }
232                 if (!matchFound) {
233                     for(Element e : eu.getGlobalTypes(acceptor)) {
234                         if (simple.contentEquals(e.getSimpleName())) {
235                             //either a clash or already imported:
236
if (qName.contentEquals(((TypeElement)e).getQualifiedName())) {
237                                 return sqName.toString();
238                             } else if (fqn == qName) {
239                                 clashing = true;
240                             }
241                             break;
242                         }
243                     }
244                 }
245             }
246             qName = lastDot < 0 ? null : qName.substring(0, lastDot);
247         }
248         if (clashing)
249             return fqn;
250         
251         //not imported/visible so far by any means:
252
if (info instanceof WorkingCopy) {
253             CompilationUnitTree nue = (CompilationUnitTree) ((WorkingCopy)info).getChangeSet().getChange(cut);
254             cut = nue != null ? nue : cut;
255             ((WorkingCopy)info).rewrite(info.getCompilationUnit(), addImports(cut, Collections.singletonList(fqn), ((WorkingCopy)info).getTreeMaker()));
256         } else {
257             RequestProcessor.getDefault().post(new Runnable JavaDoc() {
258                 public void run() {
259                     try {
260                         info.getJavaSource().runModificationTask(new CancellableTask<WorkingCopy>() {
261                             public void cancel() {
262                             }
263                             public void run(WorkingCopy copy) throws Exception JavaDoc {
264                                 copy.toPhase(Phase.ELEMENTS_RESOLVED);
265                                 copy.rewrite(copy.getCompilationUnit(), addImports(copy.getCompilationUnit(), Collections.singletonList(fqn), copy.getTreeMaker()));
266                             }
267                         }).commit();
268                     } catch (IOException JavaDoc ioe) {
269                         Exceptions.printStackTrace(ioe);
270                     }
271                 }
272             });
273         }
274         TypeElement te = info.getElements().getTypeElement(fqn);
275         if (te != null) {
276             ((JCCompilationUnit) info.getCompilationUnit()).namedImportScope.enterIfAbsent((Symbol) te);
277         }
278         
279         return sName;
280     }
281     
282     /**
283      *
284      *
285      */

286     public static CompilationUnitTree addImports(CompilationUnitTree cut, List<String JavaDoc> toImport, TreeMaker make)
287         throws IOException JavaDoc {
288         // do not modify the list given by the caller (may be reused or immutable).
289
toImport = new ArrayList<String JavaDoc>(toImport);
290         Collections.sort(toImport);
291
292         List<ImportTree> imports = new ArrayList<ImportTree>(cut.getImports());
293         int currentToImport = toImport.size() - 1;
294         int currentExisting = imports.size() - 1;
295         
296         while (currentToImport >= 0 && currentExisting >= 0) {
297             String JavaDoc currentToImportText = toImport.get(currentToImport);
298             
299             while (currentExisting >= 0 && (imports.get(currentExisting).isStatic() || imports.get(currentExisting).getQualifiedIdentifier().toString().compareTo(currentToImportText) > 0))
300                 currentExisting--;
301             
302             if (currentExisting >= 0) {
303                 imports.add(currentExisting+1, make.Import(make.Identifier(currentToImportText), false));
304                 currentToImport--;
305             }
306         }
307         // we are at the head of import section and we still have some imports
308
// to add, put them to the very beginning
309
while (currentToImport >= 0) {
310             String JavaDoc importText = toImport.get(currentToImport);
311             imports.add(0, make.Import(make.Identifier(importText), false));
312             currentToImport--;
313         }
314         // return a copy of the unit with changed imports section
315
return make.CompilationUnit(cut.getPackageName(), imports, cut.getTypeDecls(), cut.getSourceFile());
316     }
317
318     /**
319      * Returns a {@link FileObject} in which the Element is defined.
320      * Element must be defined in source file
321      * @param element for which the {@link FileObject} should be located
322      * @return the defining {@link FileObject} or null if it cannot be
323      * found in any source file.
324      */

325     public static FileObject getFile(Element element) {
326         if (element == null) {
327             throw new IllegalArgumentException JavaDoc ("Cannot pass null as an argument of the SourceUtils.getFile"); //NOI18N
328
}
329         Element prev = null;
330         while (element.getKind() != ElementKind.PACKAGE) {
331             prev = element;
332             element = element.getEnclosingElement();
333         }
334         if (prev == null || (!prev.getKind().isClass() && !prev.getKind().isInterface()))
335             return null;
336         ClassSymbol clsSym = (ClassSymbol)prev;
337         URI JavaDoc uri;
338         if (clsSym.completer != null)
339             clsSym.complete();
340         if (clsSym.sourcefile != null && (uri=clsSym.sourcefile.toUri())!= null && uri.isAbsolute()) {
341             try {
342                 return URLMapper.findFileObject(uri.toURL());
343             } catch (MalformedURLException JavaDoc ex) {
344                 ex.printStackTrace();
345             }
346         }
347         return null;
348     }
349     
350     /**
351      * Returns a {@link FileObject} in which the Element is defined.
352      * @param element for which the {@link FileObject} should be located
353      * @param cpInfo the classpaths context
354      * @return the defining {@link FileObject} or null if it cannot be
355      * found
356      */

357     public static FileObject getFile (Element element, ClasspathInfo cpInfo) {
358         try {
359         if (element == null || cpInfo == null) {
360             throw new IllegalArgumentException JavaDoc ("Cannot pass null as an argument of the SourceUtils.getFile"); //NOI18N
361
}
362         Element prev = null;
363         while (element.getKind() != ElementKind.PACKAGE) {
364             prev = element;
365             element = element.getEnclosingElement();
366         }
367         if (prev == null || (!prev.getKind().isClass() && !prev.getKind().isInterface()))
368             return null;
369         ClassSymbol clsSym = (ClassSymbol)prev;
370         URI JavaDoc uri;
371         if (clsSym.completer != null)
372             clsSym.complete();
373         if (clsSym.sourcefile != null && (uri=clsSym.sourcefile.toUri())!= null && uri.isAbsolute()) {
374             return URLMapper.findFileObject(uri.toURL());
375         }
376         else {
377             if (clsSym.classfile == null)
378                 return null;
379             uri = clsSym.classfile.toUri();
380             if (uri == null || !uri.isAbsolute()) {
381                 return null;
382             }
383             FileObject classFo = URLMapper.findFileObject(uri.toURL());
384             if (classFo == null) {
385                 return null;
386             }
387             ClassPath cp = ClassPathSupport.createProxyClassPath(
388                 new ClassPath[] {
389                     createClassPath(cpInfo,ClasspathInfo.PathKind.BOOT),
390                     createClassPath(cpInfo,ClasspathInfo.PathKind.OUTPUT),
391                     createClassPath(cpInfo,ClasspathInfo.PathKind.COMPILE),
392             });
393             FileObject root = cp.findOwnerRoot(classFo);
394             if (root == null) {
395                 return null;
396             }
397             String JavaDoc parentResName = cp.getResourceName(classFo.getParent(),'/',false); //NOI18N
398
SourceForBinaryQuery.Result result = SourceForBinaryQuery.findSourceRoots(root.getURL());
399             FileObject[] sourceRoots = result.getRoots();
400             ClassPath sourcePath = ClassPathSupport.createClassPath(sourceRoots);
401             List<FileObject> folders = (List<FileObject>) sourcePath.findAllResources(parentResName);
402             boolean caseSensitive = isCaseSensitive ();
403             final String JavaDoc sourceFileName = getSourceFileName (classFo.getName());
404             for (FileObject folder : folders) {
405                 FileObject[] children = folder.getChildren();
406                 for (FileObject child : children) {
407                     if (((caseSensitive && child.getName().equals (sourceFileName)) ||
408                         (!caseSensitive && child.getName().equalsIgnoreCase (sourceFileName)))
409                         &&
410                     JavaDataLoader.JAVA_EXTENSION.equalsIgnoreCase(child.getExt())) {
411                         return child;
412                     }
413                 }
414             }
415         }
416         } catch (MalformedURLException JavaDoc e) {
417             Exceptions.printStackTrace(e);
418         }
419         catch (FileStateInvalidException e) {
420             Exceptions.printStackTrace(e);
421         }
422         return null;
423     }
424     
425     /**
426      * Returns a {@link FileObject} of the source file in which the handle is declared.
427      * @param handle to find the {@link FileObject} for
428      * @param cpInfo classpaths for resolving handle
429      * @return {@link FileObject} or null when the source file cannot be found
430      */

431     public static FileObject getFile (final ElementHandle<? extends Element> handle, final ClasspathInfo cpInfo) {
432         if (handle == null || cpInfo == null) {
433             throw new IllegalArgumentException JavaDoc ("Cannot pass null as an argument of the SourceUtils.getFile"); //NOI18N
434
}
435         try {
436             boolean pkg = handle.getKind() == ElementKind.PACKAGE;
437             String JavaDoc[] signature = handle.getSignature();
438             assert signature.length >= 1;
439             ClassPath cp = ClassPathSupport.createProxyClassPath(
440                 new ClassPath[] {
441                     createClassPath(cpInfo,ClasspathInfo.PathKind.BOOT),
442                     createClassPath(cpInfo,ClasspathInfo.PathKind.OUTPUT),
443                     createClassPath(cpInfo,ClasspathInfo.PathKind.COMPILE),
444                 });
445             String JavaDoc pkgName, className = null;
446             if (pkg) {
447                 pkgName = FileObjects.convertPackage2Folder(signature[0]);
448             }
449             else {
450                 int index = signature[0].lastIndexOf('.'); //NOI18N
451
if (index<0) {
452                     pkgName = ""; //NOI18N
453
className = signature[0];
454                 }
455                 else {
456                     pkgName = FileObjects.convertPackage2Folder(signature[0].substring(0,index));
457                     className = signature[0].substring(index+1);
458                 }
459             }
460             List<FileObject> fos = cp.findAllResources(pkgName);
461             for (FileObject fo : fos) {
462                 FileObject root = cp.findOwnerRoot(fo);
463                 assert root != null;
464                 FileObject[] sourceRoots = SourceForBinaryQuery.findSourceRoots(root.getURL()).getRoots();
465                 ClassPath sourcePath = ClassPathSupport.createClassPath(sourceRoots);
466                 LinkedList<FileObject> folders = new LinkedList<FileObject>(sourcePath.findAllResources(pkgName));
467                 if (pkg) {
468                     return folders.isEmpty() ? fo : folders.get(0);
469                 }
470                 else {
471                     boolean caseSensitive = isCaseSensitive ();
472                     String JavaDoc sourceFileName = getSourceFileName (className);
473                     folders.addFirst(fo);
474                     for (FileObject folder : folders) {
475                         FileObject[] children = folder.getChildren();
476                         for (FileObject child : children) {
477                             if (((caseSensitive && child.getName().equals (sourceFileName)) ||
478                                 (!caseSensitive && child.getName().equalsIgnoreCase (sourceFileName))) &&
479                                 (child.isData() && JavaDataLoader.JAVA_EXTENSION.equalsIgnoreCase(child.getExt()))) {
480                                 return child;
481                             }
482                         }
483                     }
484                 }
485             }
486         } catch (MalformedURLException JavaDoc e) {
487             Exceptions.printStackTrace(e);
488         }
489         catch (FileStateInvalidException e) {
490             Exceptions.printStackTrace(e);
491         }
492         return null;
493     }
494     
495     /**
496      * Finds {@link URL} of a javadoc page for given element when available. This method
497      * uses {@link JavadocForBinaryQuery} to find the javadoc page for the give element.
498      * For {@link TypeElement} or {@link Element}s enclosed by the {@link TypeElement}
499      * it returns the {@link URL} of the javadoc for top level {@link TypeElement}.
500      * For {@link PackageElement} it returns the package-summary.html for given package.
501      * @param element to find the Javadoc for
502      * @param cpInfo classpaths used to resolve
503      * @return the URL of the javadoc page or null when the javadoc is not available.
504      */

505     public static URL JavaDoc getJavadoc (Element element, final ClasspathInfo cpInfo) {
506         if (element == null || cpInfo == null) {
507             throw new IllegalArgumentException JavaDoc ("Cannot pass null as an argument of the SourceUtils.getJavadoc"); //NOI18N
508
}
509         
510         ClassSymbol clsSym = null;
511         String JavaDoc pkgName;
512         String JavaDoc pageName;
513         if (element.getKind() == ElementKind.PACKAGE) {
514             List<? extends Element> els = element.getEnclosedElements();
515             for (Element e :els) {
516                 if (e.getKind().isClass() || e.getKind().isInterface()) {
517                     clsSym = (ClassSymbol) e;
518                     break;
519                 }
520             }
521             if (clsSym == null) {
522                 return null;
523             }
524             pkgName = FileObjects.convertPackage2Folder(((PackageElement)element).getQualifiedName().toString());
525             pageName = PACKAGE_SUMMARY;
526         }
527         else {
528             Element prev = null;
529             while (element.getKind() != ElementKind.PACKAGE) {
530                 prev = element;
531                 element = element.getEnclosingElement();
532             }
533             if (prev == null || (!prev.getKind().isClass() && !prev.getKind().isInterface())) {
534                 return null;
535             }
536             clsSym = (ClassSymbol)prev;
537             pkgName = FileObjects.convertPackage2Folder(clsSym.getEnclosingElement().getQualifiedName().toString());
538             pageName = clsSym.getSimpleName().toString();
539         }
540         
541         if (clsSym.completer != null) {
542             clsSym.complete();
543         }
544         
545         URL JavaDoc sourceRoot = null;
546         Set<URL JavaDoc> binaries = new HashSet<URL JavaDoc>();
547         try {
548             if (clsSym.classfile != null) {
549                 FileObject fo = URLMapper.findFileObject(clsSym.classfile.toUri().toURL());
550                 StringTokenizer tk = new StringTokenizer(pkgName,"/"); //NOI18N
551
for (int i=0 ;fo != null && i<=tk.countTokens(); i++) {
552                     fo = fo.getParent();
553                 }
554                 if (fo != null) {
555                     URL JavaDoc url = fo.getURL();
556                     sourceRoot = Index.getSourceRootForClassFolder(url);
557                     if (sourceRoot == null) {
558                         binaries.add(url);
559                     }
560                 }
561             }
562             if (sourceRoot == null && binaries.isEmpty() && clsSym.sourcefile != null) {
563                 sourceRoot = clsSym.sourcefile.toUri().toURL();
564             }
565             if (sourceRoot != null) {
566                 FileObject sourceFo = URLMapper.findFileObject(sourceRoot);
567                 if (sourceFo != null) {
568                     ClassPath exec = ClassPath.getClassPath(sourceFo, ClassPath.EXECUTE);
569                     ClassPath compile = ClassPath.getClassPath(sourceFo, ClassPath.COMPILE);
570                     ClassPath source = ClassPath.getClassPath(sourceFo, ClassPath.SOURCE);
571                     if (exec == null) {
572                         exec = compile;
573                         compile = null;
574                     }
575                     if (exec == null || source == null) {
576                         return null;
577                     }
578                     Set<URL JavaDoc> roots = new HashSet<URL JavaDoc>();
579                     for (ClassPath.Entry e : exec.entries()) {
580                         roots.add(e.getURL());
581                     }
582                     if (compile != null) {
583                         for (ClassPath.Entry e : compile.entries()) {
584                             roots.remove(e.getURL());
585                         }
586                     }
587                     List<FileObject> sourceRoots = Arrays.asList(source.getRoots());
588 out: for (URL JavaDoc e : roots) {
589                         FileObject[] res = SourceForBinaryQuery.findSourceRoots(e).getRoots();
590                         for (FileObject fo : res) {
591                             if (sourceRoots.contains(fo)) {
592                                 binaries.add(e);
593                                 continue out;
594                             }
595                         }
596                     }
597                 }
598             }
599             for (URL JavaDoc binary : binaries) {
600                 URL JavaDoc[] result = JavadocForBinaryQuery.findJavadoc(binary).getRoots();
601                 ClassPath cp = ClassPathSupport.createClassPath(result);
602                 FileObject fo = cp.findResource(pkgName);
603                 if (fo != null) {
604                     for (FileObject child : fo.getChildren()) {
605                         if (pageName.equals(child.getName()) && FileObjects.HTML.equalsIgnoreCase(child.getExt())) {
606                             return child.getURL();
607                         }
608                     }
609                 }
610             }
611             
612         } catch (MalformedURLException JavaDoc e) {
613             Exceptions.printStackTrace(e);
614         }
615         catch (FileStateInvalidException e) {
616             Exceptions.printStackTrace(e);
617         }
618         return null;
619     }
620     
621     
622     /**
623      * Tests whether the initial scan is in progress.
624      */

625     public static boolean isScanInProgress () {
626         return RepositoryUpdater.getDefault().isScanInProgress();
627     }
628
629     /**
630      * Waits for the end of the initial scan, this helper method
631      * is designed for tests which require to wait for end of initial scan.
632      * @throws InterruptedException is thrown when the waiting thread is interrupted.
633      */

634     public static void waitScanFinished () throws InterruptedException JavaDoc {
635         RepositoryUpdater.getDefault().waitScanFinished();
636     }
637     
638     
639     //Helper methods
640

641     /**
642      * Returns classes declared in the given source file which have the main method.
643      * @param fo source file
644      * @return the classes containing main method
645      * @throws IllegalArgumentException when file does not exist or is not a java source file.
646      */

647     public static Collection<ElementHandle<TypeElement>> getMainClasses (final FileObject fo) {
648         if (fo == null || !fo.isValid() || fo.isVirtual()) {
649             throw new IllegalArgumentException JavaDoc ();
650         }
651         final JavaSource js = JavaSource.forFileObject(fo);
652         if (js == null) {
653             throw new IllegalArgumentException JavaDoc ();
654         }
655         try {
656             final List<ElementHandle<TypeElement>> result = new LinkedList<ElementHandle<TypeElement>>();
657             js.runUserActionTask(new CancellableTask<CompilationController>() {
658                 public void run(final CompilationController control) throws Exception JavaDoc {
659                     if (control.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED).compareTo (JavaSource.Phase.ELEMENTS_RESOLVED)>=0) {
660                         new TreePathScanner<Void JavaDoc,Void JavaDoc> () {
661                            public Void JavaDoc visitMethod(MethodTree node, Void JavaDoc p) {
662                                ExecutableElement method = (ExecutableElement) control.getTrees().getElement(getCurrentPath());
663                                if (method != null && SourceUtils.isMainMethod(method) && isAccessible(method.getEnclosingElement())) {
664                                    result.add (ElementHandle.create((TypeElement)method.getEnclosingElement()));
665                                }
666                                return null;
667                            }
668                         }.scan(control.getCompilationUnit(), null);
669                     }
670                 }
671
672                 private boolean isAccessible (Element element) {
673                     ElementKind kind = element.getKind();
674                     while (kind != ElementKind.PACKAGE) {
675                         if (!kind.isClass() && !kind.isInterface()) {
676                             return false;
677                         }
678                         Set<Modifier> modifiers = ((TypeElement)element).getModifiers();
679                         if (!modifiers.contains(Modifier.PUBLIC)) {
680                             return false;
681                         }
682                         Element parent = element.getEnclosingElement();
683                         if (parent.getKind() != ElementKind.PACKAGE && !modifiers.contains(Modifier.STATIC)) {
684                             return false;
685                         }
686                         element = parent;
687                         kind = element.getKind();
688                     }
689                     return true;
690                 }
691
692                 public void cancel() {}
693
694             }, true);
695             return result;
696         } catch (IOException JavaDoc ioe) {
697             Exceptions.printStackTrace(ioe);
698             return Collections.<ElementHandle<TypeElement>>emptySet();
699         }
700     }
701     
702     /**
703      * Returns true when the class contains main method.
704      * @param qualifiedName the fully qualified name of class
705      * @param cpInfo the classpath used to resolve the class
706      * @return true when the class contains a main method
707      */

708     public static boolean isMainClass (final String JavaDoc qualifiedName, ClasspathInfo cpInfo) {
709         if (qualifiedName == null || cpInfo == null) {
710             throw new IllegalArgumentException JavaDoc ();
711         }
712         final boolean[] result = new boolean[]{false};
713         JavaSource js = JavaSource.create(cpInfo);
714         try {
715             js.runUserActionTask(new CancellableTask<CompilationController>() {
716
717                 public void run(CompilationController control) throws Exception JavaDoc {
718                     TypeElement type = control.getElements().getTypeElement(qualifiedName);
719                     if (type == null) {
720                         return;
721                     }
722                     List<? extends ExecutableElement> methods = ElementFilter.methodsIn(type.getEnclosedElements());
723                     for (ExecutableElement method : methods) {
724                         if (SourceUtils.isMainMethod(method)) {
725                             result[0] = true;
726                             break;
727                         }
728                     }
729                 }
730
731                 public void cancel() {}
732
733             }, true);
734         } catch (IOException JavaDoc ioe) {
735             Exceptions.printStackTrace(ioe);
736         }
737         return result[0];
738     }
739     
740     /**
741      * Returns true if the method is a main method
742      * @param method to be checked
743      * @return true when the method is a main method
744      */

745     public static boolean isMainMethod (final ExecutableElement method) {
746         if (!"main".contentEquals(method.getSimpleName())) { //NOI18N
747
return false;
748         }
749         long flags = ((Symbol.MethodSymbol)method).flags(); //faster
750
if (((flags & Flags.PUBLIC) == 0) || ((flags & Flags.STATIC) == 0)) {
751             return false;
752         }
753         if (method.getReturnType().getKind() != TypeKind.VOID) {
754             return false;
755         }
756         List<? extends VariableElement> params = method.getParameters();
757         if (params.size() != 1) {
758             return false;
759         }
760         TypeMirror param = params.get(0).asType();
761         if (param.getKind() != TypeKind.ARRAY) {
762             return false;
763         }
764         ArrayType array = (ArrayType) param;
765         TypeMirror compound = array.getComponentType();
766         if (compound.getKind() != TypeKind.DECLARED) {
767             return false;
768         }
769         if (!"java.lang.String".contentEquals(((TypeElement)((DeclaredType)compound).asElement()).getQualifiedName())) { //NOI18N
770
return false;
771         }
772         return true;
773     }
774     
775     /**
776      * Returns classes declared under the given source roots which have the main method.
777      * @param sourceRoots the source roots
778      * @return the classes containing the main methods
779      * Currently this method is not optimized and may be slow
780      */

781     public static Collection<ElementHandle<TypeElement>> getMainClasses (final FileObject[] sourceRoots) {
782         final List<ElementHandle<TypeElement>> result = new LinkedList<ElementHandle<TypeElement>> ();
783         for (FileObject root : sourceRoots) {
784             try {
785                 ClassPath bootPath = ClassPath.getClassPath(root, ClassPath.BOOT);
786                 ClassPath compilePath = ClassPath.getClassPath(root, ClassPath.COMPILE);
787                 ClassPath srcPath = ClassPathSupport.createClassPath(new FileObject[] {root});
788                 ClasspathInfo cpInfo = ClasspathInfo.create(bootPath, compilePath, srcPath);
789                 final Set<ElementHandle<TypeElement>> classes = cpInfo.getClassIndex().getDeclaredTypes("", ClassIndex.NameKind.PREFIX, EnumSet.of(ClassIndex.SearchScope.SOURCE));
790                 JavaSource js = JavaSource.create(cpInfo);
791                 js.runUserActionTask(new CancellableTask<CompilationController>() {
792                     public void run(CompilationController control) throws Exception JavaDoc {
793                         for (ElementHandle<TypeElement> cls : classes) {
794                             TypeElement te = cls.resolve(control);
795                             if (te != null) {
796                                 Iterable JavaDoc<? extends ExecutableElement> methods = ElementFilter.methodsIn(te.getEnclosedElements());
797                                 for (ExecutableElement method : methods) {
798                                     if (isMainMethod(method)) {
799                                         result.add (cls);
800                                     }
801                                 }
802                             }
803                         }
804                     }
805
806                     public void cancel() {}
807                 }, false);
808             } catch (IOException JavaDoc ioe) {
809                 Exceptions.printStackTrace(ioe);
810                 return Collections.<ElementHandle<TypeElement>>emptySet();
811             }
812         }
813         return result;
814     }
815     
816     private static boolean isCaseSensitive () {
817         return ! new File JavaDoc ("a").equals (new File JavaDoc ("A")); //NOI18N
818
}
819     
820     private static String JavaDoc getSourceFileName (String JavaDoc classFileName) {
821         int index = classFileName.indexOf('$'); //NOI18N
822
return index == -1 ? classFileName : classFileName.substring(0,index);
823     }
824     
825     // --------------- Helper methods of getFile () -----------------------------
826
private static ClassPath createClassPath (ClasspathInfo cpInfo, PathKind kind) throws MalformedURLException JavaDoc {
827     return cpInfo.getClassPath (kind);
828     }
829     
830     // --------------- End of getFile () helper methods ------------------------------
831

832     private static Context getSourceContextFor(ClasspathInfo cpInfo, final JavaSource.Phase phase, Element element) {
833         try {
834             FileObject fo = getFile(element, cpInfo);
835             if (fo != null) {
836                 JavaSource js = JavaSource.forFileObject(fo);
837                 if (js != null) {
838                     final Context[] ret = new Context[1];
839                     js.runUserActionTask(new CancellableTask<CompilationController>() {
840                         public void cancel() {
841                         }
842                         public void run(CompilationController controller) throws Exception JavaDoc {
843                             controller.toPhase(phase);
844                             ret[0] = controller.getJavacTask().getContext();
845                         }
846                     },true);
847                     return ret[0];
848                 }
849             }
850         } catch (IOException JavaDoc ex) {
851             Exceptions.printStackTrace(ex);
852         }
853         return null;
854     }
855     
856     private static Element getSourceElementFor(Element element, Context ctx) {
857         Symbol sym = (Symbol)element;
858         Symtab symbolTable = Symtab.instance(ctx);
859         Name.Table nameTable = Name.Table.instance(ctx);
860         Symbol owner = sym.owner;
861         ClassSymbol enclCls = sym.enclClass();
862         Name name = nameTable.fromString(enclCls.flatname.toString());
863         ClassSymbol cls = symbolTable.classes.get(name);
864         if (enclCls == sym)
865             return cls;
866         if (cls != null && owner == enclCls) {
867             com.sun.tools.javac.code.Scope.Entry e = cls.members().lookup(nameTable.fromString(sym.name.toString()));
868             while (e.scope != null) {
869                 if (e.sym.kind == sym.kind && (e.sym.flags_field & Flags.SYNTHETIC) == 0 &&
870                         e.sym.type.toString().equals(sym.type.toString()))
871                     return e.sym;
872                 e = e.next();
873             }
874         } else if (cls != null && owner.kind == Kinds.MTH && sym.kind == Kinds.VAR) {
875             com.sun.tools.javac.code.Scope.Entry e = cls.members().lookup(nameTable.fromString(owner.name.toString()));
876             Symbol newOwner = null;
877             while (e.scope != null) {
878                 if (e.sym.kind == owner.kind && (e.sym.flags_field & Flags.SYNTHETIC) == 0 &&
879                         e.sym.type.toString().equals(owner.type.toString())) {
880                     newOwner = e.sym;
881                     break;
882                 }
883                 e = e.next();
884             }
885             if (newOwner != null && newOwner.kind == Kinds.MTH) {
886                 int i = 0;
887                 for (com.sun.tools.javac.util.List<VarSymbol> l = ((MethodSymbol)owner).params; l.nonEmpty(); l = l.tail) {
888                     i++;
889                     if (sym == l.head)
890                         break;
891                 }
892                 for (com.sun.tools.javac.util.List<VarSymbol> l = ((MethodSymbol)newOwner).params; l.nonEmpty(); l = l.tail) {
893                     if (--i == 0)
894                         return l.head;
895                 }
896             }
897         }
898         return null;
899     }
900 }
901
Popular Tags