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 */ 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 */ 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 */ 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 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 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 */ 177 public ClassFile getDirectory(String name) { 178 return getFile(name, true); 179 } 180 181 /** 182 * Load the specified file from the class path 183 */ 184 public ClassFile getFile(String name) { 185 return getFile(name, false); 186 } 187 188 private final String fileSeparatorChar = "" + File.separatorChar; 189 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 */ 236 public Enumeration<ClassFile> getFiles(String pkg, String ext) { 237 Hashtable<String, ClassFile> files = new Hashtable<>(); 238 for (int i = path.length; --i >= 0; ) { 239 if (path[i].zip != null) { 240 Enumeration<? extends ZipEntry> 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 */ 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 } 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<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 }