langtools/src/jdk.dev/share/classes/com/sun/tools/jdeps/ClassFileReader.java
changeset 25874 83c19f00452c
parent 25442 755ff386d1ac
child 27579 d1a63c99cdd5
equal deleted inserted replaced
25873:024ed9c9ed13 25874:83c19f00452c
       
     1 /*
       
     2  * Copyright (c) 2012, 2014, 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;
       
    26 
       
    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;
       
    39 
       
    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(Path path) throws IOException {
       
    49         if (!Files.exists(path)) {
       
    50             throw new FileNotFoundException(path.toString());
       
    51         }
       
    52 
       
    53         if (Files.isDirectory(path)) {
       
    54             return new DirectoryReader(path);
       
    55         } else if (path.getFileName().toString().endsWith(".jar")) {
       
    56             return new JarFileReader(path);
       
    57         } else {
       
    58             return new ClassFileReader(path);
       
    59         }
       
    60     }
       
    61 
       
    62     /**
       
    63      * Returns a ClassFileReader instance of a given JarFile.
       
    64      */
       
    65     public static ClassFileReader newInstance(Path path, JarFile jf) throws IOException {
       
    66         return new JarFileReader(path, jf);
       
    67     }
       
    68 
       
    69     protected final Path path;
       
    70     protected final String baseFileName;
       
    71     protected final List<String> skippedEntries = new ArrayList<>();
       
    72     protected ClassFileReader(Path path) {
       
    73         this.path = path;
       
    74         this.baseFileName = path.getFileName() != null
       
    75                                 ? path.getFileName().toString()
       
    76                                 : path.toString();
       
    77     }
       
    78 
       
    79     public String getFileName() {
       
    80         return baseFileName;
       
    81     }
       
    82 
       
    83     public List<String> skippedEntries() {
       
    84         return skippedEntries;
       
    85     }
       
    86 
       
    87     /**
       
    88      * Returns the ClassFile matching the given binary name
       
    89      * or a fully-qualified class name.
       
    90      */
       
    91     public ClassFile getClassFile(String name) throws IOException {
       
    92         if (name.indexOf('.') > 0) {
       
    93             int i = name.lastIndexOf('.');
       
    94             String pathname = name.replace('.', File.separatorChar) + ".class";
       
    95             if (baseFileName.equals(pathname) ||
       
    96                     baseFileName.equals(pathname.substring(0, i) + "$" +
       
    97                                         pathname.substring(i+1, pathname.length()))) {
       
    98                 return readClassFile(path);
       
    99             }
       
   100         } else {
       
   101             if (baseFileName.equals(name.replace('/', File.separatorChar) + ".class")) {
       
   102                 return readClassFile(path);
       
   103             }
       
   104         }
       
   105         return null;
       
   106     }
       
   107 
       
   108     public Iterable<ClassFile> getClassFiles() throws IOException {
       
   109         return new Iterable<ClassFile>() {
       
   110             public Iterator<ClassFile> iterator() {
       
   111                 return new FileIterator();
       
   112             }
       
   113         };
       
   114     }
       
   115 
       
   116     protected ClassFile readClassFile(Path p) throws IOException {
       
   117         InputStream is = null;
       
   118         try {
       
   119             is = Files.newInputStream(p);
       
   120             return ClassFile.read(is);
       
   121         } catch (ConstantPoolException e) {
       
   122             throw new ClassFileError(e);
       
   123         } finally {
       
   124             if (is != null) {
       
   125                 is.close();
       
   126             }
       
   127         }
       
   128     }
       
   129 
       
   130     class FileIterator implements Iterator<ClassFile> {
       
   131         int count;
       
   132         FileIterator() {
       
   133             this.count = 0;
       
   134         }
       
   135         public boolean hasNext() {
       
   136             return count == 0 && baseFileName.endsWith(".class");
       
   137         }
       
   138 
       
   139         public ClassFile next() {
       
   140             if (!hasNext()) {
       
   141                 throw new NoSuchElementException();
       
   142             }
       
   143             try {
       
   144                 ClassFile cf = readClassFile(path);
       
   145                 count++;
       
   146                 return cf;
       
   147             } catch (IOException e) {
       
   148                 throw new ClassFileError(e);
       
   149             }
       
   150         }
       
   151 
       
   152         public void remove() {
       
   153             throw new UnsupportedOperationException("Not supported yet.");
       
   154         }
       
   155     }
       
   156 
       
   157     public String toString() {
       
   158         return path.toString();
       
   159     }
       
   160 
       
   161     private static class DirectoryReader extends ClassFileReader {
       
   162         DirectoryReader(Path path) throws IOException {
       
   163             super(path);
       
   164         }
       
   165 
       
   166         public ClassFile getClassFile(String name) throws IOException {
       
   167             if (name.indexOf('.') > 0) {
       
   168                 int i = name.lastIndexOf('.');
       
   169                 String pathname = name.replace('.', File.separatorChar) + ".class";
       
   170                 Path p = path.resolve(pathname);
       
   171                 if (!Files.exists(p)) {
       
   172                     p = path.resolve(pathname.substring(0, i) + "$" +
       
   173                                      pathname.substring(i+1, pathname.length()));
       
   174                 }
       
   175                 if (Files.exists(p)) {
       
   176                     return readClassFile(p);
       
   177                 }
       
   178             } else {
       
   179                 Path p = path.resolve(name + ".class");
       
   180                 if (Files.exists(p)) {
       
   181                     return readClassFile(p);
       
   182                 }
       
   183             }
       
   184             return null;
       
   185         }
       
   186 
       
   187         public Iterable<ClassFile> getClassFiles() throws IOException {
       
   188             final Iterator<ClassFile> iter = new DirectoryIterator();
       
   189             return new Iterable<ClassFile>() {
       
   190                 public Iterator<ClassFile> iterator() {
       
   191                     return iter;
       
   192                 }
       
   193             };
       
   194         }
       
   195 
       
   196         private List<Path> walkTree(Path dir) throws IOException {
       
   197             final List<Path> files = new ArrayList<>();
       
   198             Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
       
   199                 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
       
   200                         throws IOException {
       
   201                     if (file.getFileName().toString().endsWith(".class")) {
       
   202                         files.add(file);
       
   203                     }
       
   204                     return FileVisitResult.CONTINUE;
       
   205                 }
       
   206             });
       
   207             return files;
       
   208         }
       
   209 
       
   210         class DirectoryIterator implements Iterator<ClassFile> {
       
   211             private List<Path> entries;
       
   212             private int index = 0;
       
   213             DirectoryIterator() throws IOException {
       
   214                 entries = walkTree(path);
       
   215                 index = 0;
       
   216             }
       
   217 
       
   218             public boolean hasNext() {
       
   219                 return index != entries.size();
       
   220             }
       
   221 
       
   222             public ClassFile next() {
       
   223                 if (!hasNext()) {
       
   224                     throw new NoSuchElementException();
       
   225                 }
       
   226                 Path path = entries.get(index++);
       
   227                 try {
       
   228                     return readClassFile(path);
       
   229                 } catch (IOException e) {
       
   230                     throw new ClassFileError(e);
       
   231                 }
       
   232             }
       
   233 
       
   234             public void remove() {
       
   235                 throw new UnsupportedOperationException("Not supported yet.");
       
   236             }
       
   237         }
       
   238     }
       
   239 
       
   240     static class JarFileReader extends ClassFileReader {
       
   241         private final JarFile jarfile;
       
   242         JarFileReader(Path path) throws IOException {
       
   243             this(path, new JarFile(path.toFile(), false));
       
   244         }
       
   245 
       
   246         JarFileReader(Path path, JarFile jf) throws IOException {
       
   247             super(path);
       
   248             this.jarfile = jf;
       
   249         }
       
   250 
       
   251         public ClassFile getClassFile(String name) throws IOException {
       
   252             if (name.indexOf('.') > 0) {
       
   253                 int i = name.lastIndexOf('.');
       
   254                 String entryName = name.replace('.', '/') + ".class";
       
   255                 JarEntry e = jarfile.getJarEntry(entryName);
       
   256                 if (e == null) {
       
   257                     e = jarfile.getJarEntry(entryName.substring(0, i) + "$"
       
   258                             + entryName.substring(i + 1, entryName.length()));
       
   259                 }
       
   260                 if (e != null) {
       
   261                     return readClassFile(jarfile, e);
       
   262                 }
       
   263             } else {
       
   264                 JarEntry e = jarfile.getJarEntry(name + ".class");
       
   265                 if (e != null) {
       
   266                     return readClassFile(jarfile, e);
       
   267                 }
       
   268             }
       
   269             return null;
       
   270         }
       
   271 
       
   272         protected ClassFile readClassFile(JarFile jarfile, JarEntry e) throws IOException {
       
   273             InputStream is = null;
       
   274             try {
       
   275                 is = jarfile.getInputStream(e);
       
   276                 return ClassFile.read(is);
       
   277             } catch (ConstantPoolException ex) {
       
   278                 throw new ClassFileError(ex);
       
   279             } finally {
       
   280                 if (is != null)
       
   281                     is.close();
       
   282             }
       
   283         }
       
   284 
       
   285         public Iterable<ClassFile> getClassFiles() throws IOException {
       
   286             final Iterator<ClassFile> iter = new JarFileIterator(this, jarfile);
       
   287             return new Iterable<ClassFile>() {
       
   288                 public Iterator<ClassFile> iterator() {
       
   289                     return iter;
       
   290                 }
       
   291             };
       
   292         }
       
   293     }
       
   294 
       
   295     class JarFileIterator implements Iterator<ClassFile> {
       
   296         protected final JarFileReader reader;
       
   297         protected Enumeration<JarEntry> entries;
       
   298         protected JarFile jf;
       
   299         protected JarEntry nextEntry;
       
   300         protected ClassFile cf;
       
   301         JarFileIterator(JarFileReader reader) {
       
   302             this(reader, null);
       
   303         }
       
   304         JarFileIterator(JarFileReader reader, JarFile jarfile) {
       
   305             this.reader = reader;
       
   306             setJarFile(jarfile);
       
   307         }
       
   308 
       
   309         void setJarFile(JarFile jarfile) {
       
   310             if (jarfile == null) return;
       
   311 
       
   312             this.jf = jarfile;
       
   313             this.entries = jf.entries();
       
   314             this.nextEntry = nextEntry();
       
   315         }
       
   316 
       
   317         public boolean hasNext() {
       
   318             if (nextEntry != null && cf != null) {
       
   319                 return true;
       
   320             }
       
   321             while (nextEntry != null) {
       
   322                 try {
       
   323                     cf = reader.readClassFile(jf, nextEntry);
       
   324                     return true;
       
   325                 } catch (ClassFileError | IOException ex) {
       
   326                     skippedEntries.add(nextEntry.getName());
       
   327                 }
       
   328                 nextEntry = nextEntry();
       
   329             }
       
   330             return false;
       
   331         }
       
   332 
       
   333         public ClassFile next() {
       
   334             if (!hasNext()) {
       
   335                 throw new NoSuchElementException();
       
   336             }
       
   337             ClassFile classFile = cf;
       
   338             cf = null;
       
   339             nextEntry = nextEntry();
       
   340             return classFile;
       
   341         }
       
   342 
       
   343         protected JarEntry nextEntry() {
       
   344             while (entries.hasMoreElements()) {
       
   345                 JarEntry e = entries.nextElement();
       
   346                 String name = e.getName();
       
   347                 if (name.endsWith(".class")) {
       
   348                     return e;
       
   349                 }
       
   350             }
       
   351             return null;
       
   352         }
       
   353 
       
   354         public void remove() {
       
   355             throw new UnsupportedOperationException("Not supported yet.");
       
   356         }
       
   357     }
       
   358 }