KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > source > usages > BinaryAnalyser


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.source.usages;
21
22 import java.io.BufferedInputStream JavaDoc;
23 import java.io.File JavaDoc;
24 import java.io.FileInputStream JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.net.URI JavaDoc;
28 import java.net.URL JavaDoc;
29 import java.util.ArrayList JavaDoc;
30 import java.util.Collection JavaDoc;
31 import java.util.EnumSet JavaDoc;
32 import java.util.Enumeration 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.concurrent.atomic.AtomicBoolean JavaDoc;
39 import java.util.logging.Level JavaDoc;
40 import java.util.logging.Logger JavaDoc;
41 import java.util.zip.ZipEntry JavaDoc;
42 import java.util.zip.ZipFile JavaDoc;
43 import javax.lang.model.element.ElementKind;
44 import org.netbeans.api.progress.ProgressHandle;
45 import org.netbeans.modules.classfile.Access;
46 import org.netbeans.modules.classfile.CPClassInfo;
47 import org.netbeans.modules.classfile.CPFieldInfo;
48 import org.netbeans.modules.classfile.CPInterfaceMethodInfo;
49 import org.netbeans.modules.classfile.CPMethodInfo;
50 import org.netbeans.modules.classfile.ClassFile;
51 import org.netbeans.modules.classfile.ClassName;
52 import org.netbeans.modules.classfile.Code;
53 import org.netbeans.modules.classfile.ConstantPool;
54 import org.netbeans.modules.classfile.InvalidClassFormatException;
55 import org.netbeans.modules.classfile.LocalVariableTableEntry;
56 import org.netbeans.modules.classfile.LocalVariableTypeTableEntry;
57 import org.netbeans.modules.classfile.Method;
58 import org.netbeans.modules.classfile.Variable;
59 import org.netbeans.modules.classfile.Parameter;
60 import org.netbeans.modules.java.source.parsing.FileObjects;
61 import org.netbeans.modules.java.source.util.LowMemoryEvent;
62 import org.netbeans.modules.java.source.util.LowMemoryListener;
63 import org.netbeans.modules.java.source.util.LowMemoryNotifier;
64 import org.openide.filesystems.FileObject;
65 import org.openide.filesystems.FileUtil;
66 import org.openide.filesystems.URLMapper;
67 import org.openide.util.Exceptions;
68 import org.openide.util.NbBundle;
69
70
71
72
73 /**
74  *
75  * @author Petr Hrebejk, Tomas Zezula
76  */

77 public class BinaryAnalyser implements LowMemoryListener {
78     
79     static final String JavaDoc OBJECT = Object JavaDoc.class.getName();
80     
81     private final Index index;
82     private final Map JavaDoc<String JavaDoc,List JavaDoc<String JavaDoc>> refs = new HashMap JavaDoc<String JavaDoc,List JavaDoc<String JavaDoc>>();
83     private final Set JavaDoc<String JavaDoc> toDelete = new HashSet JavaDoc<String JavaDoc> ();
84     private final AtomicBoolean JavaDoc lowMemory;
85     private boolean cacheCleared;
86
87     public BinaryAnalyser (Index index) {
88        assert index != null;
89        this.index = index;
90        this.lowMemory = new AtomicBoolean JavaDoc (false);
91     }
92     
93         /** Analyses a classpath root.
94      * @param URL the classpath root, either a folder or an archive file.
95      *
96      */

97     public final void analyse (final URL JavaDoc root, final ProgressHandle handle) throws IOException JavaDoc, IllegalArgumentException JavaDoc {
98         assert root != null;
99             ClassIndexManager.getDefault().writeLock(new ClassIndexManager.ExceptionAction<Void JavaDoc> () {
100                 public Void JavaDoc run () throws IOException JavaDoc {
101                 LowMemoryNotifier.getDefault().addLowMemoryListener (BinaryAnalyser.this);
102                 try {
103                     String JavaDoc mainP = root.getProtocol();
104                     if ("jar".equals(mainP)) { //NOI18N
105
URL JavaDoc innerURL = FileUtil.getArchiveFile(root);
106                         if ("file".equals(innerURL.getProtocol())) { //NOI18N
107
//Fast way
108
File JavaDoc archive = new File JavaDoc (URI.create(innerURL.toExternalForm()));
109                             if (archive.exists() && archive.canRead()) {
110                                 if (handle != null) {
111                                     handle.setDisplayName(String.format (NbBundle.getMessage(BinaryAnalyser.class,"MSG_Scannig"),archive.getAbsolutePath()));
112                                 }
113                                 if (!isUpToDate(null,archive.lastModified())) {
114                                     index.clear();
115                                     if (handle != null) { //Tests don't provide handle
116
handle.setDisplayName (String.format(NbBundle.getMessage(RepositoryUpdater.class,"MSG_Analyzing"),archive.getAbsolutePath()));
117                                     }
118                                     final ZipFile JavaDoc zipFile = new ZipFile JavaDoc(archive);
119                                     try {
120                                         analyseArchive( zipFile );
121                                     }
122                                     finally {
123                                         zipFile.close();
124                                     }
125                                 }
126                             }
127                         }
128                         else {
129                             FileObject rootFo = URLMapper.findFileObject(root);
130                             if (rootFo != null) {
131                                 if (handle != null) {
132                                     handle.setDisplayName(String.format (NbBundle.getMessage(BinaryAnalyser.class,"MSG_Scannig"),FileUtil.getFileDisplayName(rootFo)));
133                                 }
134                                 if (!isUpToDate(null,rootFo.lastModified().getTime())) {
135                                     index.clear();
136                                     if (handle != null) { //Tests don't provide handle
137
handle.setDisplayName (String.format(NbBundle.getMessage(RepositoryUpdater.class,"MSG_Analyzing"),FileUtil.getFileDisplayName(rootFo)));
138                                     }
139                                     analyseFileObjects(rootFo);
140                                 }
141                             }
142                         }
143                     }
144                     else if ("file".equals(mainP)) { //NOI18N
145
//Fast way
146
File JavaDoc rootFile = new File JavaDoc (URI.create(root.toExternalForm()));
147                         if (rootFile.isDirectory()) {
148                             String JavaDoc path = rootFile.getAbsolutePath ();
149                             if (path.charAt(path.length()-1) != File.separatorChar) {
150                                 path = path + File.separatorChar;
151                             }
152                             if (handle != null) { //Tests don't provide handle
153
handle.setDisplayName (String.format(NbBundle.getMessage(RepositoryUpdater.class,"MSG_Analyzing"),rootFile.getAbsolutePath()));
154                             }
155                             cacheCleared = false;
156                             analyseFolder(rootFile, path);
157                         }
158                     }
159                     else {
160                         FileObject rootFo = URLMapper.findFileObject(root);
161                         if (rootFo != null) {
162                             if (handle != null) { //Tests don't provide handle
163
handle.setDisplayName (String.format(NbBundle.getMessage(RepositoryUpdater.class,"MSG_Analyzing"),FileUtil.getFileDisplayName(rootFo)));
164                             }
165                             index.clear();
166                             analyseFileObjects(rootFo);
167                         }
168                     }
169                 } finally {
170                     LowMemoryNotifier.getDefault().removeLowMemoryListener(BinaryAnalyser.this);
171                 }
172                 store();
173                 return null;
174             }});
175     }
176     
177         /** Analyses a folder
178      * @param folder to analyze
179      * @param rootURL the url of root, it would be nicer to pass an URL type,
180      * but the {@link URL#toExternalForm} from some strange reason does not cache result,
181      * the String type is faster.
182      */

183     private void analyseFolder (final File JavaDoc folder, final String JavaDoc rootPath) throws IOException JavaDoc {
184         if (folder.exists() && folder.canRead()) {
185             File JavaDoc[] children = folder.listFiles();
186             for (File JavaDoc file : children) {
187                 if (file.isDirectory()) {
188                     analyseFolder(file, rootPath);
189                 }
190                 else if (this.accepts(file.getName())) {
191                     String JavaDoc filePath = file.getAbsolutePath();
192                     long fileMTime = file.lastModified();
193                     int dotIndex = filePath.lastIndexOf('.');
194                     int slashIndex = filePath.lastIndexOf('/');
195                     int endPos;
196                     if (dotIndex>slashIndex) {
197                         endPos = dotIndex;
198                     }
199                     else {
200                         endPos = filePath.length();
201                     }
202                     String JavaDoc relativePath = FileObjects.convertFolder2Package (filePath.substring(rootPath.length(), endPos));
203                     if (this.accepts(file.getName()) && !isUpToDate (relativePath, fileMTime)) {
204                         if (!cacheCleared) {
205                             this.index.clear();
206                             cacheCleared = true;
207                         }
208                         InputStream JavaDoc in = new BufferedInputStream JavaDoc (new FileInputStream JavaDoc (file));
209                         try {
210                             analyse (in);
211                         } catch (InvalidClassFormatException icf) {
212                             Logger.getLogger(BinaryAnalyser.class.getName()).info("Invalid class file format: "+file.getAbsolutePath()); //NOI18N
213
}
214                         finally {
215                             in.close();
216                         }
217                         if (this.lowMemory.getAndSet(false)) {
218                             this.store();
219                         }
220                     }
221                 }
222             }
223         }
224     }
225     
226         //Private helper methods
227
/** Analyses a zip file */
228     private void analyseArchive ( ZipFile JavaDoc zipFile ) throws IOException JavaDoc {
229         for( Enumeration JavaDoc e = zipFile.entries(); e.hasMoreElements(); ) {
230             ZipEntry JavaDoc ze = (ZipEntry JavaDoc)e.nextElement();
231             if ( !ze.isDirectory() && this.accepts(ze.getName())) {
232                 InputStream JavaDoc in = zipFile.getInputStream( ze );
233                 try {
234                     analyse(in);
235                 } catch (InvalidClassFormatException icf) {
236                     Logger.getLogger(BinaryAnalyser.class.getName()).info("Invalid class file format: "+ new File JavaDoc(zipFile.getName()).toURI() + "!/" + ze.getName()); //NOI18N
237
} catch (IOException JavaDoc x) {
238                     Exceptions.attachMessage(x, "While scanning: " + ze.getName()); //NOI18N
239
throw x;
240                 }
241                 finally {
242                     in.close();
243                 }
244                 if (this.lowMemory.getAndSet(false)) {
245                     this.store();
246                 }
247             }
248         }
249     }
250     
251     private void analyseFileObjects (FileObject folder) throws IOException JavaDoc {
252         for (FileObject fo : folder.getChildren()) {
253             if (fo.isFolder()) {
254                 analyseFileObjects (fo);
255             }
256             else if (this.accepts(fo.getName())) {
257                 InputStream JavaDoc in = new BufferedInputStream JavaDoc (fo.getInputStream());
258                 try {
259                     analyse (in);
260                 } catch (InvalidClassFormatException icf) {
261                     Logger.getLogger(BinaryAnalyser.class.getName()).info("Invalid class file format: "+FileUtil.getFileDisplayName(fo)); //NOI18N
262
}
263                 finally {
264                     in.close();
265                 }
266                 if (this.lowMemory.getAndSet(false)) {
267                     this.store();
268                 }
269             }
270         }
271     }
272     
273     //Cleans up usages of deleted class
274
private final void delete (final String JavaDoc className) throws IOException JavaDoc {
275         assert className != null;
276         if (!this.index.isValid(false)) {
277             return;
278         }
279         this.toDelete.add(className);
280     }
281     
282     public void lowMemory (final LowMemoryEvent event) {
283         this.lowMemory.set(true);
284     }
285
286     // Implementation of StreamAnalyser ----------------------------------------
287

288     private boolean accepts(String JavaDoc name) {
289         int index = name.lastIndexOf('.');
290         if (index == -1 || (index+1) == name.length()) {
291             return false;
292         }
293         return "CLASS".equalsIgnoreCase(name.substring(index+1)); // NOI18N
294
}
295     
296     private void analyse (InputStream JavaDoc inputStream ) throws IOException JavaDoc {
297         final ClassFile classFile = new ClassFile(inputStream);
298         final ClassName className = classFile.getName ();
299         final String JavaDoc classNameStr = nameToString (className);
300         this.delete (classNameStr);
301         final Map JavaDoc <ClassName, Set JavaDoc<ClassIndexImpl.UsageType>> usages = performAnalyse(classFile, classNameStr);
302         ElementKind kind = ElementKind.CLASS;
303         if (classFile.isEnum()) {
304             kind = ElementKind.ENUM;
305         }
306         else if (classFile.isAnnotation()) {
307             kind = ElementKind.ANNOTATION_TYPE;
308         }
309         else if ((classFile.getAccess() & Access.INTERFACE) == Access.INTERFACE) {
310             kind = ElementKind.INTERFACE;
311         }
312         final String JavaDoc classNameType = classNameStr + DocumentUtil.encodeKind(kind);
313         final List JavaDoc <String JavaDoc> references = getClassReferences (classNameType);
314         for (Map.Entry JavaDoc<ClassName,Set JavaDoc<ClassIndexImpl.UsageType>> entry : usages.entrySet()) {
315             ClassName name = entry.getKey();
316             Set JavaDoc<ClassIndexImpl.UsageType> usage = entry.getValue();
317             references.add (DocumentUtil.encodeUsage( nameToString(name), usage));
318         }
319     }
320     
321     private void store() throws IOException JavaDoc {
322         try {
323             if (this.refs.size()>0 || this.toDelete.size()>0) {
324                 this.index.store(this.refs,this.toDelete);
325             }
326         } finally {
327             refs.clear();
328             toDelete.clear();
329         }
330     }
331     
332     private final boolean isUpToDate(String JavaDoc resourceName, long resourceMTime) throws IOException JavaDoc {
333         return this.index.isUpToDate(resourceName, resourceMTime);
334     }
335     
336     
337     //Private methods
338
@SuppressWarnings JavaDoc ("unchecked") //NOI18N, the classfile module is not generic
339
private Map JavaDoc <ClassName,Set JavaDoc<ClassIndexImpl.UsageType>> performAnalyse(final ClassFile classFile, final String JavaDoc className) throws IOException JavaDoc {
340         final Map JavaDoc <ClassName, Set JavaDoc<ClassIndexImpl.UsageType>> usages = new HashMap JavaDoc <ClassName, Set JavaDoc<ClassIndexImpl.UsageType>> ();
341         //Add type signature of this class
342
String JavaDoc signature = classFile.getTypeSignature();
343         if (signature != null) {
344             try {
345                 ClassName[] typeSigNames = ClassFileUtil.getTypesFromClassTypeSignature (signature);
346                 for (ClassName typeSigName : typeSigNames) {
347                     addUsage(usages, typeSigName, ClassIndexImpl.UsageType.TYPE_REFERENCE);
348                 }
349             } catch (final RuntimeException JavaDoc re) {
350                 final StringBuilder JavaDoc message = new StringBuilder JavaDoc ("BinaryAnalyser: Cannot read type: " + signature+" cause: " + re.getLocalizedMessage() + '\n'); //NOI18N
351
final StackTraceElement JavaDoc[] elements = re.getStackTrace();
352                 for (StackTraceElement JavaDoc e : elements) {
353                     message.append(e.toString());
354                     message.append('\n'); //NOI18N
355
}
356                 Logger.getLogger("global").log (Level.INFO, message.toString()); //NOI18N
357
}
358         }
359
360         // 0. Add the superclass
361
ClassName scName = classFile.getSuperClass();
362         if ( scName != null ) {
363             addUsage (usages, scName, ClassIndexImpl.UsageType.SUPER_CLASS);
364         }
365
366         // 1. Add interfaces
367
Collection JavaDoc<ClassName> interfaces = classFile.getInterfaces();
368         for( ClassName ifaceName : interfaces ) {
369             addUsage (usages, ifaceName, ClassIndexImpl.UsageType.SUPER_INTERFACE);
370         }
371
372         //2. Add filed usages
373
final ConstantPool constantPool = classFile.getConstantPool();
374         Collection JavaDoc<? extends CPFieldInfo> fields = constantPool.getAllConstants(CPFieldInfo.class);
375         for (CPFieldInfo field : fields) {
376             ClassName name = ClassFileUtil.getType(constantPool.getClass(field.getClassID()));
377             if (name != null) {
378                 addUsage (usages, name, ClassIndexImpl.UsageType.FIELD_REFERENCE);
379             }
380         }
381
382         //3. Add method usages
383
Collection JavaDoc<? extends CPMethodInfo> methodCalls = constantPool.getAllConstants(CPMethodInfo.class);
384         for (CPMethodInfo method : methodCalls) {
385             ClassName name = ClassFileUtil.getType(constantPool.getClass(method.getClassID()));
386             if (name != null) {
387                 addUsage (usages, name, ClassIndexImpl.UsageType.METHOD_REFERENCE);
388             }
389         }
390         methodCalls = constantPool.getAllConstants(CPInterfaceMethodInfo.class);
391         for (CPMethodInfo method : methodCalls) {
392             ClassName name = ClassFileUtil.getType(constantPool.getClass(method.getClassID()));
393             if (name != null) {
394                 addUsage (usages, name, ClassIndexImpl.UsageType.METHOD_REFERENCE);
395             }
396         }
397
398         //4, 5, 6, 8 Add method type refs (return types, param types, exception types) and local variables.
399
Collection JavaDoc<Method> methods = classFile.getMethods();
400         for (Method method : methods) {
401             String JavaDoc jvmTypeId = method.getReturnType();
402             ClassName type = ClassFileUtil.getType (jvmTypeId);
403             if (type != null) {
404                 addUsage(usages, type, ClassIndexImpl.UsageType.TYPE_REFERENCE);
405             }
406             List JavaDoc<Parameter> params = method.getParameters();
407             for (Parameter param : params) {
408                 jvmTypeId = param.getDescriptor();
409                 type = ClassFileUtil.getType (jvmTypeId);
410                 if (type != null) {
411                     addUsage(usages, type, ClassIndexImpl.UsageType.TYPE_REFERENCE);
412                 }
413             }
414             CPClassInfo[] classInfos = method.getExceptionClasses();
415             for (CPClassInfo classInfo : classInfos) {
416                 type = classInfo.getClassName();
417                 if (type != null) {
418                     addUsage(usages, type, ClassIndexImpl.UsageType.TYPE_REFERENCE);
419                 }
420             }
421             jvmTypeId = method.getTypeSignature();
422             if (jvmTypeId != null) {
423                 try {
424                     ClassName[] typeSigNames = ClassFileUtil.getTypesFromMethodTypeSignature (jvmTypeId);
425                     for (ClassName typeSigName : typeSigNames) {
426                         addUsage(usages, typeSigName, ClassIndexImpl.UsageType.TYPE_REFERENCE);
427                     }
428                 } catch (IllegalStateException JavaDoc is) {
429                     Logger.getLogger("global").warning("Invalid method signature: "+className+"::"+method.getName()+" signature is:" + jvmTypeId); // NOI18N
430
}
431             }
432             Code code = method.getCode();
433             if (code != null) {
434                 LocalVariableTableEntry[] vars = code.getLocalVariableTable();
435                 for (LocalVariableTableEntry var : vars) {
436                     type = ClassFileUtil.getType (var.getDescription());
437                     if (type != null) {
438                         addUsage(usages, type, ClassIndexImpl.UsageType.TYPE_REFERENCE);
439                     }
440                 }
441                 LocalVariableTypeTableEntry[] varTypes = method.getCode().getLocalVariableTypeTable();
442                 for (LocalVariableTypeTableEntry varType : varTypes) {
443                     try {
444                         ClassName[] typeSigNames = ClassFileUtil.getTypesFromFiledTypeSignature (varType.getSignature());
445                         for (ClassName typeSigName : typeSigNames) {
446                             addUsage(usages, typeSigName, ClassIndexImpl.UsageType.TYPE_REFERENCE);
447                         }
448                     } catch (IllegalStateException JavaDoc is) {
449                         Logger.getLogger("global").warning("Invalid local variable signature: "+className+"::"+method.getName()); // NOI18N
450
}
451                 }
452             }
453         }
454         //7. Add Filed Type References
455
Collection JavaDoc<Variable> vars = classFile.getVariables();
456         for (Variable var : vars) {
457             String JavaDoc jvmTypeId = var.getDescriptor();
458             ClassName type = ClassFileUtil.getType (jvmTypeId);
459             if (type != null) {
460                 addUsage (usages, type, ClassIndexImpl.UsageType.TYPE_REFERENCE);
461             }
462             jvmTypeId = var.getTypeSignature();
463             if (jvmTypeId != null) {
464                 try {
465                     ClassName[] typeSigNames = ClassFileUtil.getTypesFromFiledTypeSignature (jvmTypeId);
466                     for (ClassName typeSigName : typeSigNames) {
467                         addUsage(usages, typeSigName, ClassIndexImpl.UsageType.TYPE_REFERENCE);
468                     }
469                 } catch (IllegalStateException JavaDoc is) {
470                     Logger.getLogger("global").warning("Invalid field signature: "+className+"::"+var.getName()+" signature is: "+jvmTypeId); // NOI18N
471
}
472             }
473         }
474
475         //9. Remains
476
Collection JavaDoc<? extends CPClassInfo> cis = constantPool.getAllConstants(CPClassInfo.class);
477         for (CPClassInfo ci : cis) {
478             ClassName ciName = ClassFileUtil.getType(ci);
479             if (ciName != null && !usages.keySet().contains (ciName)) {
480                 addUsage(usages, ciName, ClassIndexImpl.UsageType.TYPE_REFERENCE);
481             }
482         }
483         
484         return usages;
485     }
486     
487     private List JavaDoc<String JavaDoc> getClassReferences (final String JavaDoc className) {
488         assert className != null;
489         List JavaDoc<String JavaDoc> cr = this.refs.get (className);
490         if (cr == null) {
491             cr = new ArrayList JavaDoc<String JavaDoc> ();
492             this.refs.put (className, cr);
493         }
494         return cr;
495     }
496     
497                 
498     // Static private methods ---------------------------------------------------------
499

500     private static String JavaDoc nameToString( ClassName name ) {
501         return name.getInternalName().replace('/', '.'); // NOI18N
502
}
503     
504     private static void addUsage (final Map JavaDoc<ClassName, Set JavaDoc<ClassIndexImpl.UsageType>> usages, final ClassName name, final ClassIndexImpl.UsageType usage) {
505         if (OBJECT.equals(name.getExternalName())) {
506             return;
507         }
508         Set JavaDoc<ClassIndexImpl.UsageType> uset = usages.get(name);
509         if (uset == null) {
510             uset = EnumSet.noneOf(ClassIndexImpl.UsageType.class);
511             usages.put(name, uset);
512         }
513         uset.add(usage);
514     }
515 }
516
Popular Tags