KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > ClassElementFinder


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.modules.java;
21
22 import java.util.*;
23
24 import org.openide.cookies.SourceCookie;
25
26 import org.openide.filesystems.FileObject;
27 import org.openide.filesystems.FileUtil;
28
29 import org.openide.loaders.DataObject;
30 import org.openide.loaders.DataObjectNotFoundException;
31
32 import org.openide.src.*;
33 import org.netbeans.api.java.classpath.ClassPath;
34 import org.netbeans.api.java.queries.SourceForBinaryQuery;
35 import org.netbeans.spi.java.classpath.support.ClassPathSupport;
36
37 /**
38  * This class implements Finder interface from org.openide.src and is used
39  * to look up ClassElement relevant for the given name. The function is as follows:
40  * The finder locates the first .java or .class file in the Repository, looking
41  * at filesystems with capability COMPILE. If .java file (source) is found,
42  * it is parsed and scanned for the element. The results are then returned.
43  * <P>
44  * If .class is located first, the scan continues and the finder queries all
45  * matching .java files whether they know about the .class that was found first.
46  * They might, because of cross-directory compilation.
47  * <P>
48  * If one of the .java sources claims ownership over the .class, the ClassElement
49  * is dug out of the source file, otherwise the finder returns <CODE>null</CODE>
50  * and leaves the work to other finders.
51  *
52  * @author <A HREF="mailto:sdedic@netbeans.org">Svatopluk Dedic</A>
53  * @version 0.1
54  */

55 class ClassElementFinder implements ClassElement.Finder {
56     /**
57      * Enables tons of debug messages for tracing bugs and weird results.
58      */

59     private static final boolean DEBUG = false;
60     
61     private static ClassElementFinder finder;
62     
63     /**
64      * Hidden to prevent construction from outside.
65      */

66     private ClassElementFinder() {
67     }
68     
69     /**
70      * Returns, creating it if necessary, an instance of Finder. The finder
71      * is implemented as a singleton.
72      */

73     public static ClassElementFinder getInstance() {
74         if (finder != null)
75             return finder;
76         synchronized (ClassElementFinder.class) {
77             if (finder == null)
78                 finder = new ClassElementFinder();
79         }
80         return finder;
81     }
82     
83     /**
84      * @param fullName name as passed to ClassElement.forName. Must contain $
85      * as inner class delimiters.
86      * @param packageDelimiter basically position of the last . in the fullName,
87      * or 0 if there is none.
88      * @param innerDelimiter index of the first $ in the fullName. Must be >
89      * packageDelimiter.
90      */

91     private ClassElement findSpecifiedInner(FileObject projectArtefact, String JavaDoc fullName, int packageDelimiter,
92         int innerDelimiter) {
93
94         String JavaDoc packName = packageDelimiter >= 0 ? fullName.substring(0, packageDelimiter) : "";
95         int classnameOffset = packageDelimiter + 1;
96         String JavaDoc topName = fullName.substring(classnameOffset, innerDelimiter);
97         FileObject fo = searchFile(projectArtefact, packName, topName);
98          if (fo == null)
99              return null;
100
101          StringTokenizer tt = new StringTokenizer(fullName.substring(classnameOffset),
102              "$"); // NOI18N
103
return findClassInFile(fo, tt);
104     }
105     
106     /**
107      * Finds the specified ClassElement within the source, travesing through inner
108      * classes. The Enumeration should contain Strings - beginning with the name
109      * of the top-level class, followed by names of inner classes up to the one
110      * the caller wants to locate.
111      */

112     private ClassElement findInSource(SourceElement s, Enumeration names) {
113         Identifier classID;
114         ClassElement c;
115
116         classID = Identifier.create((String JavaDoc)names.nextElement());
117         c = s.getClass(classID);
118         if (DEBUG) {
119             System.err.println("getClass(" + classID.getName() + ") = " + // NOI18N
120
(c == null ? "nothing" : c.getName().getFullName())); // NOI18N
121
}
122         if (!names.hasMoreElements() || c == null)
123             return c;
124         do {
125             classID = Identifier.create((String JavaDoc)names.nextElement());
126             c = c.getClass(classID);
127             if (DEBUG) {
128                 System.err.println("getClass(" + classID.getName() + ") = " + // NOI18N
129
(c == null ? "nothing" : c.getName().getFullName())); // NOI18N
130
}
131         } while (c != null && names.hasMoreElements());
132         return c;
133     }
134     
135     /**
136      * Tries to find ClassElement with the corresponding name.
137      */

138     public ClassElement find(String JavaDoc className, FileObject projectArtefact) {
139         int lastDot = className.length() - 1;
140         int innerDelimiter = -1;
141         ClassElement result;
142
143         if (lastDot < 0)
144             return null;
145
146         while (lastDot > 0 && className.charAt(lastDot) != '.') {
147             if (className.charAt(lastDot) == '$')
148                 innerDelimiter = lastDot;
149             lastDot--;
150         }
151         if (lastDot == 0) {
152             if (className.charAt(lastDot) == '.') {
153                 return null; //Invalid name
154
}
155             else {
156                 lastDot-=1;
157             }
158         }
159
160         if (DEBUG) {
161             System.err.println("[ClassFinder] Searching for " + className + // NOI18N
162
" lastDot = " + lastDot + " delimiter = " + innerDelimiter); // NOI18N
163
}
164
165         // innerDelimiter > -1 --> specified an inner class.
166
// we can find source with package className[0..lastDot - 1],
167
// name className[lastDot + 1..innerDelimiter - 1]
168
if (innerDelimiter > 0)
169             return findSpecifiedInner(projectArtefact, className, lastDot, innerDelimiter);
170
171         int nameEnd = className.length();
172         String JavaDoc baseName;
173         String JavaDoc packName;
174         FileObject f;
175
176         if (lastDot > 0) {
177             baseName = className.substring(lastDot + 1, nameEnd);
178             packName = className.substring(0, lastDot);
179         } else {
180             baseName = className;
181             packName = ""; // NOI18N
182
}
183         
184         while (true) {
185             FileObject fo = searchFile(projectArtefact, packName, baseName);
186             if (fo != null) {
187                 if (DEBUG) {
188                     System.err.println("Finally found file " + f); // NOI18N
189
}
190                 String JavaDoc partName = lastDot > 0? className.substring(lastDot + 1): className;
191                 result = findClassInFile(fo,new StringTokenizer(partName, ".")); // NOI18N
192
if (result != null)
193                     return result;
194             }
195             // dive out from the package:
196
if (lastDot <= 0)
197                 break;
198
199             nameEnd = lastDot;
200             lastDot = className.lastIndexOf('.', lastDot - 1);
201             if (lastDot > 0) {
202                 baseName = className.substring(lastDot + 1, nameEnd);
203                 packName = className.substring(0, lastDot);
204             } else {
205                 packName = ""; // NOI18N
206
baseName = className.substring(0, nameEnd);
207             }
208         }
209         return null;
210     }
211
212     /**
213      * Attempts to locate the named class within the file. The enumeration
214      * should produce String components of the class name, beginning with
215      * top-level class name, followed by inner class name(s), if applicable.
216      */

217     private ClassElement findClassInFile(FileObject f, Enumeration names) {
218         DataObject d;
219         SourceElement s;
220         try {
221             d = DataObject.find(f);
222             
223             SourceCookie sc = (SourceCookie)d.getCookie(SourceCookie.class);
224             if (DEBUG)
225                 System.err.println("SourceCookie = " + sc); // NOI18N
226
if (sc == null)
227                 return null;
228             s = sc.getSource();
229         } catch (DataObjectNotFoundException ex) {
230             return null;
231         }
232         return findInSource(s, names);
233     }
234     
235     /**
236      * Tries to find the designated class. It searches FileSystems in the
237      * order defined by the Repository. If it finds a .class file, it
238      * does not give up immediately, but tries to locate a matching .java
239      * file
240      * @param packName name of the package (folder) to look in.
241      */

242     private FileObject searchFile(FileObject projectArtefact, String JavaDoc packName, String JavaDoc baseName) {
243         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(packName.replace('.', '/'));
244         if (packName.length() != 0)
245             sb.append('/');
246         sb.append(baseName);
247         String JavaDoc resNameBase = sb.toString();
248         FileObject result = null;
249         ClassPath classPath = ClassPath.getClassPath(projectArtefact,ClassPath.SOURCE);
250         if (classPath != null) {
251             result = classPath.findResource(resNameBase+".java"); // NOI18N
252
if (result != null) {
253                 return result;
254             }
255         }
256         classPath = ClassPath.getClassPath (projectArtefact,ClassPath.COMPILE);
257         if (classPath != null) {
258             result = classPath.findResource(resNameBase+".class"); // NOI18N
259
if (result != null) {
260                 //Try to find source for it
261
ClassPath.Entry root = findRoot (classPath, result);
262                 FileObject[] sources = findSourceFile (root, resNameBase);
263                 if (sources.length == 1) {
264                     //And return it only if it is safe
265
return sources[0];
266                 }
267                 return result;
268             } else {
269                 // the COMPILE classpath items might not be built yet and
270
// so check the sources directly:
271
Iterator it = classPath.entries().iterator();
272                 while (it.hasNext()) {
273                     ClassPath.Entry entry = (ClassPath.Entry)it.next();
274                     FileObject[] sourceRoots = SourceForBinaryQuery.findSourceRoots(entry.getURL()).getRoots();
275                     if (sourceRoots.length > 0) {
276                         ClassPath cp = ClassPathSupport.createClassPath(sourceRoots);
277                         result = cp.findResource(resNameBase+".java"); // NOI18N
278
if (result != null) {
279                             return result;
280                         }
281                     }
282                 }
283             }
284         }
285         classPath = ClassPath.getClassPath (projectArtefact,ClassPath.BOOT);
286         if (classPath != null) {
287             result = classPath.findResource(resNameBase+".class"); // NOI18N
288
if (result != null) {
289                 //Try to find source for it
290
ClassPath.Entry root = findRoot (classPath, result);
291                 FileObject[] sources = findSourceFile (root, resNameBase);
292                 if (sources.length == 1) {
293                     //And return it only if it is safe
294
return sources[0];
295                 }
296                 return result;
297             }
298         }
299         return null;
300     }
301     
302     public ClassElement find(Class JavaDoc clazz, FileObject context) {
303         return null;
304     }
305
306
307
308     /**
309      * Returns the source file for given class file.
310      * @param root the ClassPath root containing the compiled file
311      * @param resourceName the resource name e.g. org/netbeans/modules/java/JavaDataObject.class
312      * @return FileObject or null
313      */

314     private static FileObject[] findSourceFile (ClassPath.Entry root, String JavaDoc resourceName) {
315         Set result = new HashSet ();
316         if (resourceName != null) {
317             resourceName+=".java"; //NOI18N
318
FileObject[] sourceRoots = SourceForBinaryQuery.findSourceRoots (root.getURL()).getRoots();
319             if (sourceRoots.length > 0) {
320                 ClassPath cp = ClassPathSupport.createClassPath (sourceRoots);
321                 result.addAll(cp.findAllResources (resourceName));
322             }
323         }
324         return (FileObject[]) result.toArray (new FileObject [result.size()]);
325     }
326
327     private static ClassPath.Entry findRoot (ClassPath cp, FileObject file) {
328         FileObject owner = cp.findOwnerRoot (file);
329         for (Iterator it = cp.entries().iterator(); it.hasNext();) {
330             ClassPath.Entry entry = ((ClassPath.Entry)it.next());
331             FileObject root = entry.getRoot();
332             if (root != null && root.equals(owner))
333                 return entry;
334         }
335         return null;
336     }
337 }
338
Popular Tags