jdk/src/jdk.rmic/share/classes/sun/tools/java/ClassPath.java
changeset 27565 729f9700483a
parent 25859 3317bb8137f4
child 31673 135283550686
equal deleted inserted replaced
27564:eaaa79b68cd5 27565:729f9700483a
    23  * questions.
    23  * questions.
    24  */
    24  */
    25 
    25 
    26 package sun.tools.java;
    26 package sun.tools.java;
    27 
    27 
    28 import java.util.Enumeration;
       
    29 import java.util.Hashtable;
       
    30 import java.io.File;
    28 import java.io.File;
    31 import java.io.IOException;
    29 import java.io.IOException;
       
    30 import java.io.UncheckedIOException;
    32 import java.util.zip.*;
    31 import java.util.zip.*;
       
    32 import java.util.Enumeration;
       
    33 import java.util.Map;
       
    34 import java.util.HashMap;
       
    35 import java.util.Hashtable;
       
    36 import java.util.Set;
       
    37 import java.util.LinkedHashSet;
       
    38 import java.net.URI;
       
    39 import java.nio.file.DirectoryStream;
       
    40 import java.nio.file.Files;
       
    41 import java.nio.file.FileSystem;
       
    42 import java.nio.file.FileSystems;
       
    43 import java.nio.file.Path;
       
    44 import java.nio.file.spi.FileSystemProvider;
    33 
    45 
    34 /**
    46 /**
    35  * This class is used to represent a class path, which can contain both
    47  * This class is used to represent a class path, which can contain both
    36  * directories and zip files.
    48  * directories and zip files.
    37  *
    49  *
    39  * supported API.  Code that depends on them does so at its own risk:
    51  * supported API.  Code that depends on them does so at its own risk:
    40  * they are subject to change or removal without notice.
    52  * they are subject to change or removal without notice.
    41  */
    53  */
    42 public
    54 public
    43 class ClassPath {
    55 class ClassPath {
       
    56     private static final String JIMAGE_EXT = ".jimage";
       
    57     private FileSystem getJrtFileSystem() {
       
    58         return FileSystems.getFileSystem(URI.create("jrt:/"));
       
    59     }
       
    60 
    44     static final char dirSeparator = File.pathSeparatorChar;
    61     static final char dirSeparator = File.pathSeparatorChar;
    45 
    62 
    46     /**
    63     /**
    47      * The original class path string
    64      * The original class path string
    48      */
    65      */
   103             n++; i++;
   120             n++; i++;
   104         }
   121         }
   105         // Build the class path
   122         // Build the class path
   106         ClassPathEntry[] path = new ClassPathEntry[n+1];
   123         ClassPathEntry[] path = new ClassPathEntry[n+1];
   107         int len = pathstr.length();
   124         int len = pathstr.length();
       
   125         boolean jrtAdded = false;
   108         for (i = n = 0; i < len; i = j + 1) {
   126         for (i = n = 0; i < len; i = j + 1) {
   109             if ((j = pathstr.indexOf(dirSeparator, i)) == -1) {
   127             if ((j = pathstr.indexOf(dirSeparator, i)) == -1) {
   110                 j = len;
   128                 j = len;
   111             }
   129             }
   112             if (i == j) {
   130             if (i == j) {
   113                 path[n] = new ClassPathEntry();
   131                 path[n++] = new DirClassPathEntry(new File("."));
   114                 path[n++].dir = new File(".");
       
   115             } else {
   132             } else {
   116                 File file = new File(pathstr.substring(i, j));
   133                 String filename = pathstr.substring(i, j);
       
   134                 File file = new File(filename);
   117                 if (file.isFile()) {
   135                 if (file.isFile()) {
   118                     try {
   136                     if (filename.endsWith(JIMAGE_EXT)) {
   119                         ZipFile zip = new ZipFile(file);
   137                         if (jrtAdded) continue;
   120                         path[n] = new ClassPathEntry();
   138                         FileSystem fs = getJrtFileSystem();
   121                         path[n++].zip = zip;
   139                         path[n++] = new JrtClassPathEntry(fs);
   122                     } catch (ZipException e) {
   140                         jrtAdded = true;
   123                     } catch (IOException e) {
   141                     } else {
   124                         // Ignore exceptions, at least for now...
   142                         try {
       
   143                             ZipFile zip = new ZipFile(file);
       
   144                             path[n++] = new ZipClassPathEntry(zip);
       
   145                         } catch (ZipException e) {
       
   146                         } catch (IOException e) {
       
   147                             // Ignore exceptions, at least for now...
       
   148                         }
   125                     }
   149                     }
   126                 } else {
   150                 } else {
   127                     path[n] = new ClassPathEntry();
   151                     path[n++] = new DirClassPathEntry(file);
   128                     path[n++].dir = file;
       
   129                 }
   152                 }
   130             }
   153             }
   131         }
   154         }
   132         // Trim class path to exact size
   155         // Trim class path to exact size
   133         this.path = new ClassPathEntry[n];
   156         this.path = new ClassPathEntry[n];
   148         }
   171         }
   149 
   172 
   150         // Build the class path
   173         // Build the class path
   151         ClassPathEntry[] path = new ClassPathEntry[patharray.length];
   174         ClassPathEntry[] path = new ClassPathEntry[patharray.length];
   152         int n = 0;
   175         int n = 0;
       
   176         boolean jrtAdded = false;
   153         for (String name : patharray) {
   177         for (String name : patharray) {
   154             File file = new File(name);
   178             File file = new File(name);
   155             if (file.isFile()) {
   179             if (file.isFile()) {
   156                 try {
   180                 if (name.endsWith(JIMAGE_EXT)) {
   157                     ZipFile zip = new ZipFile(file);
   181                     if (jrtAdded) continue;
   158                     path[n] = new ClassPathEntry();
   182                     FileSystem fs = getJrtFileSystem();
   159                     path[n++].zip = zip;
   183                     path[n++] = new JrtClassPathEntry(fs);
   160                 } catch (ZipException e) {
   184                     jrtAdded = true;
   161                 } catch (IOException e) {
   185                 } else {
   162                     // Ignore exceptions, at least for now...
   186                     try {
   163                 }
   187                         ZipFile zip = new ZipFile(file);
       
   188                         path[n++] = new ZipClassPathEntry(zip);
       
   189                     } catch (ZipException e) {
       
   190                     } catch (IOException e) {
       
   191                         // Ignore exceptions, at least for now...
       
   192                     }
       
   193                }
   164             } else {
   194             } else {
   165                 path[n] = new ClassPathEntry();
   195                 path[n++] = new DirClassPathEntry(file);
   166                 path[n++].dir = file;
       
   167             }
   196             }
   168         }
   197         }
   169         // Trim class path to exact size
   198         // Trim class path to exact size
   170         this.path = new ClassPathEntry[n];
   199         this.path = new ClassPathEntry[n];
   171         System.arraycopy((Object)path, 0, (Object)this.path, 0, n);
   200         System.arraycopy((Object)path, 0, (Object)this.path, 0, n);
   200             // also, the getFiles caches are keyed with a trailing /
   229             // also, the getFiles caches are keyed with a trailing /
   201             subdir = subdir + File.separatorChar;
   230             subdir = subdir + File.separatorChar;
   202             name = subdir;      // Note: isDirectory==true & basename==""
   231             name = subdir;      // Note: isDirectory==true & basename==""
   203         }
   232         }
   204         for (int i = 0; i < path.length; i++) {
   233         for (int i = 0; i < path.length; i++) {
   205             if (path[i].zip != null) {
   234             ClassFile cf = path[i].getFile(name, subdir, basename, isDirectory);
   206                 String newname = name.replace(File.separatorChar, '/');
   235             if (cf != null) {
   207                 ZipEntry entry = path[i].zip.getEntry(newname);
   236                 return cf;
   208                 if (entry != null) {
   237             }
   209                     return new ClassFile(path[i].zip, entry);
   238         }
       
   239         return null;
       
   240     }
       
   241 
       
   242     /**
       
   243      * Returns list of files given a package name and extension.
       
   244      */
       
   245     public Enumeration<ClassFile> getFiles(String pkg, String ext) {
       
   246         Hashtable<String, ClassFile> files = new Hashtable<>();
       
   247         for (int i = path.length; --i >= 0; ) {
       
   248             path[i].fillFiles(pkg, ext, files);
       
   249         }
       
   250         return files.elements();
       
   251     }
       
   252 
       
   253     /**
       
   254      * Release resources.
       
   255      */
       
   256     public void close() throws IOException {
       
   257         for (int i = path.length; --i >= 0; ) {
       
   258             path[i].close();
       
   259         }
       
   260     }
       
   261 
       
   262     /**
       
   263      * Returns original class path string
       
   264      */
       
   265     public String toString() {
       
   266         return pathstr;
       
   267     }
       
   268 }
       
   269 
       
   270 /**
       
   271  * A class path entry, which can either be a directory or an open zip file or an open jimage filesystem.
       
   272  */
       
   273 abstract class ClassPathEntry {
       
   274     abstract ClassFile getFile(String name, String subdir, String basename, boolean isDirectory);
       
   275     abstract void fillFiles(String pkg, String ext, Hashtable<String, ClassFile> files);
       
   276     abstract void close() throws IOException;
       
   277 }
       
   278 
       
   279 // a ClassPathEntry that represents a directory
       
   280 final class DirClassPathEntry extends ClassPathEntry {
       
   281     private final File dir;
       
   282 
       
   283     DirClassPathEntry(File dir) {
       
   284         this.dir = dir;
       
   285     }
       
   286 
       
   287     private final Hashtable<String, String[]> subdirs = new Hashtable<>(29); // cache of sub-directory listings:
       
   288     private String[] getFiles(String subdir) {
       
   289         String files[] = subdirs.get(subdir);
       
   290         if (files == null) {
       
   291             files = computeFiles(subdir);
       
   292             subdirs.put(subdir, files);
       
   293         }
       
   294         return files;
       
   295     }
       
   296 
       
   297     private String[] computeFiles(String subdir) {
       
   298         File sd = new File(dir.getPath(), subdir);
       
   299         String[] files = null;
       
   300         if (sd.isDirectory()) {
       
   301             files = sd.list();
       
   302             if (files == null) {
       
   303                 // should not happen, but just in case, fail silently
       
   304                 files = new String[0];
       
   305             }
       
   306             if (files.length == 0) {
       
   307                 String nonEmpty[] = { "" };
       
   308                 files = nonEmpty;
       
   309             }
       
   310         } else {
       
   311             files = new String[0];
       
   312         }
       
   313         return files;
       
   314     }
       
   315 
       
   316     ClassFile getFile(String name,  String subdir, String basename, boolean isDirectory) {
       
   317         File file = new File(dir.getPath(), name);
       
   318         String list[] = getFiles(subdir);
       
   319         if (isDirectory) {
       
   320             if (list.length > 0) {
       
   321                 return ClassFile.newClassFile(file);
       
   322             }
       
   323         } else {
       
   324             for (int j = 0; j < list.length; j++) {
       
   325                 if (basename.equals(list[j])) {
       
   326                     // Don't bother checking !file.isDir,
       
   327                     // since we only look for names which
       
   328                     // cannot already be packages (foo.java, etc).
       
   329                     return ClassFile.newClassFile(file);
   210                 }
   330                 }
   211             } else {
   331             }
   212                 File file = new File(path[i].dir.getPath(), name);
   332         }
   213                 String list[] = path[i].getFiles(subdir);
   333         return null;
   214                 if (isDirectory) {
   334     }
   215                     if (list.length > 0) {
   335 
   216                         return new ClassFile(file);
   336     void fillFiles(String pkg, String ext, Hashtable<String, ClassFile> files) {
   217                     }
   337         String[] list = getFiles(pkg);
   218                 } else {
   338         for (int j = 0; j < list.length; j++) {
   219                     for (int j = 0; j < list.length; j++) {
   339             String name = list[j];
   220                         if (basename.equals(list[j])) {
   340             if (name.endsWith(ext)) {
   221                             // Don't bother checking !file.isDir,
   341                 name = pkg + File.separatorChar + name;
   222                             // since we only look for names which
   342                 File file = new File(dir.getPath(), name);
   223                             // cannot already be packages (foo.java, etc).
   343                 files.put(name, ClassFile.newClassFile(file));
   224                             return new ClassFile(file);
   344             }
       
   345         }
       
   346     }
       
   347 
       
   348     void close() throws IOException {
       
   349     }
       
   350 }
       
   351 
       
   352 // a ClassPathEntry that represents a .zip or a .jar file
       
   353 final class ZipClassPathEntry extends ClassPathEntry {
       
   354     private final ZipFile zip;
       
   355 
       
   356     ZipClassPathEntry(ZipFile zip) {
       
   357         this.zip = zip;
       
   358     }
       
   359 
       
   360     void close() throws IOException {
       
   361         zip.close();
       
   362     }
       
   363 
       
   364     ClassFile getFile(String name, String subdir, String basename, boolean isDirectory) {
       
   365         String newname = name.replace(File.separatorChar, '/');
       
   366         ZipEntry entry = zip.getEntry(newname);
       
   367         return entry != null? ClassFile.newClassFile(zip, entry) : null;
       
   368     }
       
   369 
       
   370     void fillFiles(String pkg, String ext, Hashtable<String, ClassFile> files) {
       
   371         Enumeration<? extends ZipEntry> e = zip.entries();
       
   372         while (e.hasMoreElements()) {
       
   373             ZipEntry entry = (ZipEntry)e.nextElement();
       
   374             String name = entry.getName();
       
   375             name = name.replace('/', File.separatorChar);
       
   376             if (name.startsWith(pkg) && name.endsWith(ext)) {
       
   377                 files.put(name, ClassFile.newClassFile(zip, entry));
       
   378             }
       
   379         }
       
   380     }
       
   381 }
       
   382 
       
   383 // a ClassPathEntry that represents jrt file system
       
   384 final class JrtClassPathEntry extends ClassPathEntry {
       
   385     private final FileSystem fs;
       
   386     // module directory paths in jrt fs
       
   387     private final Set<Path> jrtModules;
       
   388     // package name to package directory path mapping (lazily filled)
       
   389     private final Map<String, Path> pkgDirs;
       
   390 
       
   391     JrtClassPathEntry(FileSystem fs) {
       
   392         this.fs = fs;
       
   393         this.jrtModules = new LinkedHashSet<>();
       
   394         this.pkgDirs = new HashMap<>();
       
   395 
       
   396         // fill in module directories at the root dir
       
   397         Path root = fs.getPath("/");
       
   398         try {
       
   399             try (DirectoryStream<Path> stream = Files.newDirectoryStream(root)) {
       
   400                 for (Path entry: stream) {
       
   401                     if (Files.isDirectory(entry))
       
   402                         jrtModules.add(entry);
       
   403                 }
       
   404             }
       
   405         } catch (IOException ioExp) {
       
   406             throw new UncheckedIOException(ioExp);
       
   407         }
       
   408     }
       
   409 
       
   410     void close() throws IOException {
       
   411     }
       
   412 
       
   413     // from pkgName (internal separator '/') to it's Path in jrtfs
       
   414     synchronized Path getPackagePath(String pkgName) throws IOException {
       
   415         // check the cache first
       
   416         if (pkgDirs.containsKey(pkgName)) {
       
   417             return pkgDirs.get(pkgName);
       
   418         }
       
   419 
       
   420         for (Path modPath : jrtModules) {
       
   421             Path pkgDir = fs.getPath(modPath.toString(), pkgName);
       
   422             // check if package directory is under any of the known modules
       
   423             if (Files.exists(pkgDir)) {
       
   424                 // it is a package directory only if contains atleast one .class file
       
   425                 try (DirectoryStream<Path> stream = Files.newDirectoryStream(pkgDir)) {
       
   426                     for (Path p : stream) {
       
   427                         if (Files.isRegularFile(p) && p.toString().endsWith(".class")) {
       
   428                             // cache package-to-package dir mapping for future
       
   429                             pkgDirs.put(pkgName, pkgDir);
       
   430                             return pkgDir;
   225                         }
   431                         }
   226                     }
   432                     }
   227                 }
   433                 }
   228             }
   434             }
   229         }
   435         }
       
   436 
   230         return null;
   437         return null;
   231     }
   438     }
   232 
   439 
   233     /**
   440     // fully qualified (internal) class name to it's Path in jrtfs
   234      * Returns list of files given a package name and extension.
   441     Path getClassPath(String clsName) throws IOException {
   235      */
   442         int index = clsName.lastIndexOf('/');
   236     public Enumeration<ClassFile> getFiles(String pkg, String ext) {
   443         if (index == -1) {
   237         Hashtable<String, ClassFile> files = new Hashtable<>();
   444             return null;
   238         for (int i = path.length; --i >= 0; ) {
   445         }
   239             if (path[i].zip != null) {
   446         Path pkgPath = getPackagePath(clsName.substring(0, index));
   240                 Enumeration<? extends ZipEntry> e = path[i].zip.entries();
   447         return pkgPath == null? null : fs.getPath(pkgPath + "/" + clsName.substring(index + 1));
   241                 while (e.hasMoreElements()) {
   448     }
   242                     ZipEntry entry = (ZipEntry)e.nextElement();
   449 
   243                     String name = entry.getName();
   450     ClassFile getFile(String name, String subdir, String basename, boolean isDirectory) {
   244                     name = name.replace('/', File.separatorChar);
   451         try {
   245                     if (name.startsWith(pkg) && name.endsWith(ext)) {
   452             name = name.replace(File.separatorChar, '/');
   246                         files.put(name, new ClassFile(path[i].zip, entry));
   453             Path cp = getClassPath(name);
   247                     }
   454             return cp == null? null : ClassFile.newClassFile(cp);
       
   455         } catch (IOException ioExp) {
       
   456             throw new UncheckedIOException(ioExp);
       
   457         }
       
   458     }
       
   459 
       
   460     void fillFiles(String pkg, String ext, Hashtable<String, ClassFile> files) {
       
   461         Path dir;
       
   462         try {
       
   463             dir = getPackagePath(pkg);
       
   464             if (dir == null) {
       
   465                 return;
       
   466             }
       
   467         } catch (IOException ioExp) {
       
   468             throw new UncheckedIOException(ioExp);
       
   469         }
       
   470 
       
   471         try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
       
   472             for (Path p : stream) {
       
   473                 String name = p.toString();
       
   474                 name = name.replace('/', File.separatorChar);
       
   475                 if (name.startsWith(pkg) && name.endsWith(ext)) {
       
   476                     files.put(name, ClassFile.newClassFile(p));
   248                 }
   477                 }
   249             } else {
   478             }
   250                 String[] list = path[i].getFiles(pkg);
   479         } catch (IOException ioExp) {
   251                 for (int j = 0; j < list.length; j++) {
   480             throw new UncheckedIOException(ioExp);
   252                     String name = list[j];
   481         }
   253                     if (name.endsWith(ext)) {
       
   254                         name = pkg + File.separatorChar + name;
       
   255                         File file = new File(path[i].dir.getPath(), name);
       
   256                         files.put(name, new ClassFile(file));
       
   257                     }
       
   258                 }
       
   259             }
       
   260         }
       
   261         return files.elements();
       
   262     }
       
   263 
       
   264     /**
       
   265      * Release resources.
       
   266      */
       
   267     public void close() throws IOException {
       
   268         for (int i = path.length; --i >= 0; ) {
       
   269             if (path[i].zip != null) {
       
   270                 path[i].zip.close();
       
   271             }
       
   272         }
       
   273     }
       
   274 
       
   275     /**
       
   276      * Returns original class path string
       
   277      */
       
   278     public String toString() {
       
   279         return pathstr;
       
   280     }
   482     }
   281 }
   483 }
   282 
       
   283 /**
       
   284  * A class path entry, which can either be a directory or an open zip file.
       
   285  */
       
   286 class ClassPathEntry {
       
   287     File dir;
       
   288     ZipFile zip;
       
   289 
       
   290     Hashtable<String, String[]> subdirs = new Hashtable<>(29); // cache of sub-directory listings:
       
   291     String[] getFiles(String subdir) {
       
   292         String files[] = subdirs.get(subdir);
       
   293         if (files == null) {
       
   294             // search the directory, exactly once
       
   295             File sd = new File(dir.getPath(), subdir);
       
   296             if (sd.isDirectory()) {
       
   297                 files = sd.list();
       
   298                 if (files == null) {
       
   299                     // should not happen, but just in case, fail silently
       
   300                     files = new String[0];
       
   301                 }
       
   302                 if (files.length == 0) {
       
   303                     String nonEmpty[] = { "" };
       
   304                     files = nonEmpty;
       
   305                 }
       
   306             } else {
       
   307                 files = new String[0];
       
   308             }
       
   309             subdirs.put(subdir, files);
       
   310         }
       
   311         return files;
       
   312     }
       
   313 
       
   314 }