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