1 /*
2  * Copyright (c) 1994, 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 
26 package sun.tools.java;
27 
28 import java.util.Enumeration;
29 import java.util.Hashtable;
30 import java.io.File;
31 import java.io.IOException;
32 import java.util.zip.*;
33 
34 /**
35  * This class is used to represent a class path, which can contain both
36  * directories and zip files.
37  *
38  * WARNING: The contents of this source file are not part of any
39  * supported API.  Code that depends on them does so at its own risk:
40  * they are subject to change or removal without notice.
41  */
42 public
43 class ClassPath {
44     static final char dirSeparator = File.pathSeparatorChar;
45 
46     /**
47      * The original class path string
48      */
49     String pathstr;
50 
51     /**
52      * List of class path entries
53      */
54     private ClassPathEntry[] path;
55 
56     /**
57      * Build a class path from the specified path string
58      */
ClassPath(String pathstr)59     public ClassPath(String pathstr) {
60         init(pathstr);
61     }
62 
63     /**
64      * Build a class path from the specified array of class path
65      * element strings.  This constructor, and the corresponding
66      * "init" method, were added as part of the fix for 6473331, which
67      * adds support for Class-Path manifest entries in JAR files to
68      * rmic.  It is conceivable that the value of a Class-Path
69      * manifest entry will contain a path separator, which would cause
70      * incorrect behavior if the expanded path were passed to the
71      * previous constructor as a single path-separator-delimited
72      * string; use of this constructor avoids that problem.
73      */
ClassPath(String[] patharray)74     public ClassPath(String[] patharray) {
75         init(patharray);
76     }
77 
78     /**
79      * Build a default class path from the path strings specified by
80      * the properties sun.boot.class.path and env.class.path, in that
81      * order.
82      */
ClassPath()83     public ClassPath() {
84         String syscp = System.getProperty("sun.boot.class.path");
85         String envcp = System.getProperty("env.class.path");
86         if (envcp == null) envcp = ".";
87         String cp = syscp + File.pathSeparator + envcp;
88         init(cp);
89     }
90 
init(String pathstr)91     private void init(String pathstr) {
92         int i, j, n;
93         // Save original class path string
94         this.pathstr = pathstr;
95 
96         if (pathstr.length() == 0) {
97             this.path = new ClassPathEntry[0];
98         }
99 
100         // Count the number of path separators
101         i = n = 0;
102         while ((i = pathstr.indexOf(dirSeparator, i)) != -1) {
103             n++; i++;
104         }
105         // Build the class path
106         ClassPathEntry[] path = new ClassPathEntry[n+1];
107         int len = pathstr.length();
108         for (i = n = 0; i < len; i = j + 1) {
109             if ((j = pathstr.indexOf(dirSeparator, i)) == -1) {
110                 j = len;
111             }
112             if (i == j) {
113                 path[n] = new ClassPathEntry();
114                 path[n++].dir = new File(".");
115             } else {
116                 File file = new File(pathstr.substring(i, j));
117                 if (file.isFile()) {
118                     try {
119                         ZipFile zip = new ZipFile(file);
120                         path[n] = new ClassPathEntry();
121                         path[n++].zip = zip;
122                     } catch (ZipException e) {
123                     } catch (IOException e) {
124                         // Ignore exceptions, at least for now...
125                     }
126                 } else {
127                     path[n] = new ClassPathEntry();
128                     path[n++].dir = file;
129                 }
130             }
131         }
132         // Trim class path to exact size
133         this.path = new ClassPathEntry[n];
134         System.arraycopy((Object)path, 0, (Object)this.path, 0, n);
135     }
136 
init(String[] patharray)137     private void init(String[] patharray) {
138         // Save original class path string
139         if (patharray.length == 0) {
140             this.pathstr = "";
141         } else {
142             StringBuilder sb = new StringBuilder(patharray[0]);
143             for (int i = 1; i < patharray.length; i++) {
144                 sb.append(File.pathSeparatorChar);
145                 sb.append(patharray[i]);
146             }
147             this.pathstr = sb.toString();
148         }
149 
150         // Build the class path
151         ClassPathEntry[] path = new ClassPathEntry[patharray.length];
152         int n = 0;
153         for (String name : patharray) {
154             File file = new File(name);
155             if (file.isFile()) {
156                 try {
157                     ZipFile zip = new ZipFile(file);
158                     path[n] = new ClassPathEntry();
159                     path[n++].zip = zip;
160                 } catch (ZipException e) {
161                 } catch (IOException e) {
162                     // Ignore exceptions, at least for now...
163                 }
164             } else {
165                 path[n] = new ClassPathEntry();
166                 path[n++].dir = file;
167             }
168         }
169         // Trim class path to exact size
170         this.path = new ClassPathEntry[n];
171         System.arraycopy((Object)path, 0, (Object)this.path, 0, n);
172     }
173 
174     /**
175      * Find the specified directory in the class path
176      */
getDirectory(String name)177     public ClassFile getDirectory(String name) {
178         return getFile(name, true);
179     }
180 
181     /**
182      * Load the specified file from the class path
183      */
getFile(String name)184     public ClassFile getFile(String name) {
185         return getFile(name, false);
186     }
187 
188     private final String fileSeparatorChar = "" + File.separatorChar;
189 
getFile(String name, boolean isDirectory)190     private ClassFile getFile(String name, boolean isDirectory) {
191         String subdir = name;
192         String basename = "";
193         if (!isDirectory) {
194             int i = name.lastIndexOf(File.separatorChar);
195             subdir = name.substring(0, i + 1);
196             basename = name.substring(i + 1);
197         } else if (!subdir.equals("")
198                    && !subdir.endsWith(fileSeparatorChar)) {
199             // zip files are picky about "foo" vs. "foo/".
200             // also, the getFiles caches are keyed with a trailing /
201             subdir = subdir + File.separatorChar;
202             name = subdir;      // Note: isDirectory==true & basename==""
203         }
204         for (int i = 0; i < path.length; i++) {
205             if (path[i].zip != null) {
206                 String newname = name.replace(File.separatorChar, '/');
207                 ZipEntry entry = path[i].zip.getEntry(newname);
208                 if (entry != null) {
209                     return new ClassFile(path[i].zip, entry);
210                 }
211             } else {
212                 File file = new File(path[i].dir.getPath(), name);
213                 String list[] = path[i].getFiles(subdir);
214                 if (isDirectory) {
215                     if (list.length > 0) {
216                         return new ClassFile(file);
217                     }
218                 } else {
219                     for (int j = 0; j < list.length; j++) {
220                         if (basename.equals(list[j])) {
221                             // Don't bother checking !file.isDir,
222                             // since we only look for names which
223                             // cannot already be packages (foo.java, etc).
224                             return new ClassFile(file);
225                         }
226                     }
227                 }
228             }
229         }
230         return null;
231     }
232 
233     /**
234      * Returns list of files given a package name and extension.
235      */
getFiles(String pkg, String ext)236     public Enumeration getFiles(String pkg, String ext) {
237         Hashtable files = new Hashtable();
238         for (int i = path.length; --i >= 0; ) {
239             if (path[i].zip != null) {
240                 Enumeration e = path[i].zip.entries();
241                 while (e.hasMoreElements()) {
242                     ZipEntry entry = (ZipEntry)e.nextElement();
243                     String name = entry.getName();
244                     name = name.replace('/', File.separatorChar);
245                     if (name.startsWith(pkg) && name.endsWith(ext)) {
246                         files.put(name, new ClassFile(path[i].zip, entry));
247                     }
248                 }
249             } else {
250                 String[] list = path[i].getFiles(pkg);
251                 for (int j = 0; j < list.length; j++) {
252                     String name = list[j];
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      */
close()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      */
toString()278     public String toString() {
279         return pathstr;
280     }
281 }
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 subdirs = new Hashtable(29); // cache of sub-directory listings
getFiles(String subdir)291     String[] getFiles(String subdir) {
292         String files[] = (String[]) 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 }
315