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

Thu, 24 May 2018 18:02:46 +0800

author
aoqi
date
Thu, 24 May 2018 18:02:46 +0800
changeset 3446
e468915bad3a
parent 3368
f206126308bc
parent 2702
9ca8d8713094
permissions
-rw-r--r--

Merge

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

mercurial