src/share/classes/com/sun/tools/jdeps/ClassFileReader.java

Fri, 28 Dec 2012 22:25:21 -0800

author
mchung
date
Fri, 28 Dec 2012 22:25:21 -0800
changeset 1472
0c244701188e
child 1638
fd3fdaff0257
permissions
-rw-r--r--

8003562: Provide a CLI tool to analyze class dependencies
Reviewed-by: jjg, alanb, ulfzibis, erikj

     1 /*
     2  * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    25 package com.sun.tools.jdeps;
    27 import com.sun.tools.classfile.ClassFile;
    28 import com.sun.tools.classfile.ConstantPoolException;
    29 import com.sun.tools.classfile.Dependencies.ClassFileError;
    30 import java.io.*;
    31 import java.nio.file.FileVisitResult;
    32 import java.nio.file.Files;
    33 import java.nio.file.Path;
    34 import java.nio.file.SimpleFileVisitor;
    35 import java.nio.file.attribute.BasicFileAttributes;
    36 import java.util.*;
    37 import java.util.jar.JarEntry;
    38 import java.util.jar.JarFile;
    40 /**
    41  * ClassFileReader reads ClassFile(s) of a given path that can be
    42  * a .class file, a directory, or a JAR file.
    43  */
    44 public class ClassFileReader {
    45     /**
    46      * Returns a ClassFileReader instance of a given path.
    47      */
    48     public static ClassFileReader newInstance(File path) throws IOException {
    49         if (!path.exists()) {
    50             throw new FileNotFoundException(path.getAbsolutePath());
    51         }
    53         if (path.isDirectory()) {
    54             return new DirectoryReader(path.toPath());
    55         } else if (path.getName().endsWith(".jar")) {
    56             return new JarFileReader(path.toPath());
    57         } else {
    58             return new ClassFileReader(path.toPath());
    59         }
    60     }
    62     protected final Path path;
    63     protected final String baseFileName;
    64     private ClassFileReader(Path path) {
    65         this.path = path;
    66         this.baseFileName = path.getFileName() != null
    67                                 ? path.getFileName().toString()
    68                                 : path.toString();
    69     }
    71     public String getFileName() {
    72         return baseFileName;
    73     }
    75     /**
    76      * Returns the ClassFile matching the given binary name
    77      * or a fully-qualified class name.
    78      */
    79     public ClassFile getClassFile(String name) throws IOException {
    80         if (name.indexOf('.') > 0) {
    81             int i = name.lastIndexOf('.');
    82             String pathname = name.replace('.', File.separatorChar) + ".class";
    83             if (baseFileName.equals(pathname) ||
    84                     baseFileName.equals(pathname.substring(0, i) + "$" +
    85                                         pathname.substring(i+1, pathname.length()))) {
    86                 return readClassFile(path);
    87             }
    88         } else {
    89             if (baseFileName.equals(name.replace('/', File.separatorChar) + ".class")) {
    90                 return readClassFile(path);
    91             }
    92         }
    93         return null;
    94     }
    96     public Iterable<ClassFile> getClassFiles() throws IOException {
    97         return new Iterable<ClassFile>() {
    98             public Iterator<ClassFile> iterator() {
    99                 return new FileIterator();
   100             }
   101         };
   102     }
   104     protected ClassFile readClassFile(Path p) throws IOException {
   105         InputStream is = null;
   106         try {
   107             is = Files.newInputStream(p);
   108             return ClassFile.read(is);
   109         } catch (ConstantPoolException e) {
   110             throw new ClassFileError(e);
   111         } finally {
   112             if (is != null) {
   113                 is.close();
   114             }
   115         }
   116     }
   118     class FileIterator implements Iterator<ClassFile> {
   119         int count;
   120         FileIterator() {
   121             this.count = 0;
   122         }
   123         public boolean hasNext() {
   124             return count == 0 && baseFileName.endsWith(".class");
   125         }
   127         public ClassFile next() {
   128             if (!hasNext()) {
   129                 throw new NoSuchElementException();
   130             }
   131             try {
   132                 ClassFile cf = readClassFile(path);
   133                 count++;
   134                 return cf;
   135             } catch (IOException e) {
   136                 throw new ClassFileError(e);
   137             }
   138         }
   140         public void remove() {
   141             throw new UnsupportedOperationException("Not supported yet.");
   142         }
   143     }
   145     public String toString() {
   146         return path.toString();
   147     }
   149     private static class DirectoryReader extends ClassFileReader {
   150         DirectoryReader(Path path) throws IOException {
   151             super(path);
   152         }
   154         public ClassFile getClassFile(String name) throws IOException {
   155             if (name.indexOf('.') > 0) {
   156                 int i = name.lastIndexOf('.');
   157                 String pathname = name.replace('.', File.separatorChar) + ".class";
   158                 Path p = path.resolve(pathname);
   159                 if (!p.toFile().exists()) {
   160                     p = path.resolve(pathname.substring(0, i) + "$" +
   161                                      pathname.substring(i+1, pathname.length()));
   162                 }
   163                 if (p.toFile().exists()) {
   164                     return readClassFile(p);
   165                 }
   166             } else {
   167                 Path p = path.resolve(name + ".class");
   168                 if (p.toFile().exists()) {
   169                     return readClassFile(p);
   170                 }
   171             }
   172             return null;
   173         }
   175         public Iterable<ClassFile> getClassFiles() throws IOException {
   176             final Iterator<ClassFile> iter = new DirectoryIterator();
   177             return new Iterable<ClassFile>() {
   178                 public Iterator<ClassFile> iterator() {
   179                     return iter;
   180                 }
   181             };
   182         }
   184         private List<Path> walkTree(Path dir) throws IOException {
   185             final List<Path> files = new ArrayList<Path>();
   186             Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
   187                 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
   188                         throws IOException {
   189                     if (file.toFile().getName().endsWith(".class")) {
   190                         files.add(file);
   191                     }
   192                     return FileVisitResult.CONTINUE;
   193                 }
   194             });
   195             return files;
   196         }
   198         class DirectoryIterator implements Iterator<ClassFile> {
   199             private List<Path> entries;
   200             private int index = 0;
   201             DirectoryIterator() throws IOException {
   202                 entries = walkTree(path);
   203                 index = 0;
   204             }
   206             public boolean hasNext() {
   207                 return index != entries.size();
   208             }
   210             public ClassFile next() {
   211                 if (!hasNext()) {
   212                     throw new NoSuchElementException();
   213                 }
   214                 Path path = entries.get(index++);
   215                 try {
   216                     return readClassFile(path);
   217                 } catch (IOException e) {
   218                     throw new ClassFileError(e);
   219                 }
   220             }
   222             public void remove() {
   223                 throw new UnsupportedOperationException("Not supported yet.");
   224             }
   225         }
   226     }
   228     private static class JarFileReader extends ClassFileReader {
   229         final JarFile jarfile;
   230         JarFileReader(Path path) throws IOException {
   231             super(path);
   232             this.jarfile = new JarFile(path.toFile());
   233         }
   235         public ClassFile getClassFile(String name) throws IOException {
   236             if (name.indexOf('.') > 0) {
   237                 int i = name.lastIndexOf('.');
   238                 String entryName = name.replace('.', '/') + ".class";
   239                 JarEntry e = jarfile.getJarEntry(entryName);
   240                 if (e == null) {
   241                     e = jarfile.getJarEntry(entryName.substring(0, i) + "$"
   242                             + entryName.substring(i + 1, entryName.length()));
   243                 }
   244                 if (e != null) {
   245                     return readClassFile(e);
   246                 }
   247             } else {
   248                 JarEntry e = jarfile.getJarEntry(name + ".class");
   249                 if (e != null) {
   250                     return readClassFile(e);
   251                 }
   252             }
   253             return null;
   254         }
   256         private ClassFile readClassFile(JarEntry e) throws IOException {
   257             InputStream is = null;
   258             try {
   259                 is = jarfile.getInputStream(e);
   260                 return ClassFile.read(is);
   261             } catch (ConstantPoolException ex) {
   262                 throw new ClassFileError(ex);
   263             } finally {
   264                 if (is != null)
   265                     is.close();
   266             }
   267         }
   269         public Iterable<ClassFile> getClassFiles() throws IOException {
   270             final Iterator<ClassFile> iter = new JarFileIterator();
   271             return new Iterable<ClassFile>() {
   272                 public Iterator<ClassFile> iterator() {
   273                     return iter;
   274                 }
   275             };
   276         }
   278         class JarFileIterator implements Iterator<ClassFile> {
   279             private Enumeration<JarEntry> entries;
   280             private JarEntry nextEntry;
   281             JarFileIterator() {
   282                 this.entries = jarfile.entries();
   283                 while (entries.hasMoreElements()) {
   284                     JarEntry e = entries.nextElement();
   285                     String name = e.getName();
   286                     if (name.endsWith(".class")) {
   287                         this.nextEntry = e;
   288                         break;
   289                     }
   290                 }
   291             }
   293             public boolean hasNext() {
   294                 return nextEntry != null;
   295             }
   297             public ClassFile next() {
   298                 if (!hasNext()) {
   299                     throw new NoSuchElementException();
   300                 }
   302                 ClassFile cf;
   303                 try {
   304                     cf = readClassFile(nextEntry);
   305                 } catch (IOException ex) {
   306                     throw new ClassFileError(ex);
   307                 }
   308                 JarEntry entry = nextEntry;
   309                 nextEntry = null;
   310                 while (entries.hasMoreElements()) {
   311                     JarEntry e = entries.nextElement();
   312                     String name = e.getName();
   313                     if (name.endsWith(".class")) {
   314                         nextEntry = e;
   315                         break;
   316                     }
   317                 }
   318                 return cf;
   319             }
   321             public void remove() {
   322                 throw new UnsupportedOperationException("Not supported yet.");
   323             }
   324         }
   325     }
   326 }

mercurial