1/* 2 * Javassist, a Java-bytecode translator toolkit. 3 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. 4 * 5 * The contents of this file are subject to the Mozilla Public License Version 6 * 1.1 (the "License"); you may not use this file except in compliance with 7 * the License. Alternatively, the contents of this file may be used under 8 * the terms of the GNU Lesser General Public License Version 2.1 or later. 9 * 10 * Software distributed under the License is distributed on an "AS IS" basis, 11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 12 * for the specific language governing rights and limitations under the 13 * License. 14 */ 15 16package javassist; 17 18import java.io.*; 19import java.util.jar.*; 20import java.net.MalformedURLException; 21import java.net.URL; 22import java.util.Hashtable; 23 24final class ClassPathList { 25 ClassPathList next; 26 ClassPath path; 27 28 ClassPathList(ClassPath p, ClassPathList n) { 29 next = n; 30 path = p; 31 } 32} 33 34final class DirClassPath implements ClassPath { 35 String directory; 36 37 DirClassPath(String dirName) { 38 directory = dirName; 39 } 40 41 public InputStream openClassfile(String classname) { 42 try { 43 char sep = File.separatorChar; 44 String filename = directory + sep 45 + classname.replace('.', sep) + ".class"; 46 return new FileInputStream(filename.toString()); 47 } 48 catch (FileNotFoundException e) {} 49 catch (SecurityException e) {} 50 return null; 51 } 52 53 public URL find(String classname) { 54 char sep = File.separatorChar; 55 String filename = directory + sep 56 + classname.replace('.', sep) + ".class"; 57 File f = new File(filename); 58 if (f.exists()) 59 try { 60 return f.getCanonicalFile().toURI().toURL(); 61 } 62 catch (MalformedURLException e) {} 63 catch (IOException e) {} 64 65 return null; 66 } 67 68 public void close() {} 69 70 public String toString() { 71 return directory; 72 } 73} 74 75final class JarDirClassPath implements ClassPath { 76 JarClassPath[] jars; 77 78 JarDirClassPath(String dirName) throws NotFoundException { 79 File[] files = new File(dirName).listFiles(new FilenameFilter() { 80 public boolean accept(File dir, String name) { 81 name = name.toLowerCase(); 82 return name.endsWith(".jar") || name.endsWith(".zip"); 83 } 84 }); 85 86 if (files != null) { 87 jars = new JarClassPath[files.length]; 88 for (int i = 0; i < files.length; i++) 89 jars[i] = new JarClassPath(files[i].getPath()); 90 } 91 } 92 93 public InputStream openClassfile(String classname) throws NotFoundException { 94 if (jars != null) 95 for (int i = 0; i < jars.length; i++) { 96 InputStream is = jars[i].openClassfile(classname); 97 if (is != null) 98 return is; 99 } 100 101 return null; // not found 102 } 103 104 public URL find(String classname) { 105 if (jars != null) 106 for (int i = 0; i < jars.length; i++) { 107 URL url = jars[i].find(classname); 108 if (url != null) 109 return url; 110 } 111 112 return null; // not found 113 } 114 115 public void close() { 116 if (jars != null) 117 for (int i = 0; i < jars.length; i++) 118 jars[i].close(); 119 } 120} 121 122final class JarClassPath implements ClassPath { 123 JarFile jarfile; 124 String jarfileURL; 125 126 JarClassPath(String pathname) throws NotFoundException { 127 try { 128 jarfile = new JarFile(pathname); 129 jarfileURL = new File(pathname).getCanonicalFile() 130 .toURI().toURL().toString(); 131 return; 132 } 133 catch (IOException e) {} 134 throw new NotFoundException(pathname); 135 } 136 137 public InputStream openClassfile(String classname) 138 throws NotFoundException 139 { 140 try { 141 String jarname = classname.replace('.', '/') + ".class"; 142 JarEntry je = jarfile.getJarEntry(jarname); 143 if (je != null) 144 return jarfile.getInputStream(je); 145 else 146 return null; // not found 147 } 148 catch (IOException e) {} 149 throw new NotFoundException("broken jar file?: " 150 + jarfile.getName()); 151 } 152 153 public URL find(String classname) { 154 String jarname = classname.replace('.', '/') + ".class"; 155 JarEntry je = jarfile.getJarEntry(jarname); 156 if (je != null) 157 try { 158 return new URL("jar:" + jarfileURL + "!/" + jarname); 159 } 160 catch (MalformedURLException e) {} 161 162 return null; // not found 163 } 164 165 public void close() { 166 try { 167 jarfile.close(); 168 jarfile = null; 169 } 170 catch (IOException e) {} 171 } 172 173 public String toString() { 174 return jarfile == null ? "<null>" : jarfile.toString(); 175 } 176} 177 178final class ClassPoolTail { 179 protected ClassPathList pathList; 180 private Hashtable packages; // should be synchronized. 181 182 public ClassPoolTail() { 183 pathList = null; 184 packages = new Hashtable(); 185 } 186 187 public String toString() { 188 StringBuffer buf = new StringBuffer(); 189 buf.append("[class path: "); 190 ClassPathList list = pathList; 191 while (list != null) { 192 buf.append(list.path.toString()); 193 buf.append(File.pathSeparatorChar); 194 list = list.next; 195 } 196 197 buf.append(']'); 198 return buf.toString(); 199 } 200 201 public synchronized ClassPath insertClassPath(ClassPath cp) { 202 pathList = new ClassPathList(cp, pathList); 203 return cp; 204 } 205 206 public synchronized ClassPath appendClassPath(ClassPath cp) { 207 ClassPathList tail = new ClassPathList(cp, null); 208 ClassPathList list = pathList; 209 if (list == null) 210 pathList = tail; 211 else { 212 while (list.next != null) 213 list = list.next; 214 215 list.next = tail; 216 } 217 218 return cp; 219 } 220 221 public synchronized void removeClassPath(ClassPath cp) { 222 ClassPathList list = pathList; 223 if (list != null) 224 if (list.path == cp) 225 pathList = list.next; 226 else { 227 while (list.next != null) 228 if (list.next.path == cp) 229 list.next = list.next.next; 230 else 231 list = list.next; 232 } 233 234 cp.close(); 235 } 236 237 public ClassPath appendSystemPath() { 238 return appendClassPath(new ClassClassPath()); 239 } 240 241 public ClassPath insertClassPath(String pathname) 242 throws NotFoundException 243 { 244 return insertClassPath(makePathObject(pathname)); 245 } 246 247 public ClassPath appendClassPath(String pathname) 248 throws NotFoundException 249 { 250 return appendClassPath(makePathObject(pathname)); 251 } 252 253 private static ClassPath makePathObject(String pathname) 254 throws NotFoundException 255 { 256 String lower = pathname.toLowerCase(); 257 if (lower.endsWith(".jar") || lower.endsWith(".zip")) 258 return new JarClassPath(pathname); 259 260 int len = pathname.length(); 261 if (len > 2 && pathname.charAt(len - 1) == '*' 262 && (pathname.charAt(len - 2) == '/' 263 || pathname.charAt(len - 2) == File.separatorChar)) { 264 String dir = pathname.substring(0, len - 2); 265 return new JarDirClassPath(dir); 266 } 267 268 return new DirClassPath(pathname); 269 } 270 271 /** 272 * You can record "System" so that java.lang.System can be quickly 273 * found although "System" is not a package name. 274 */ 275 public void recordInvalidClassName(String name) { 276 packages.put(name, name); 277 } 278 279 /** 280 * This method does not close the output stream. 281 */ 282 void writeClassfile(String classname, OutputStream out) 283 throws NotFoundException, IOException, CannotCompileException 284 { 285 InputStream fin = openClassfile(classname); 286 if (fin == null) 287 throw new NotFoundException(classname); 288 289 try { 290 copyStream(fin, out); 291 } 292 finally { 293 fin.close(); 294 } 295 } 296 297 /* 298 -- faster version -- 299 void checkClassName(String classname) throws NotFoundException { 300 if (find(classname) == null) 301 throw new NotFoundException(classname); 302 } 303 304 -- slower version -- 305 306 void checkClassName(String classname) throws NotFoundException { 307 InputStream fin = openClassfile(classname); 308 try { 309 fin.close(); 310 } 311 catch (IOException e) {} 312 } 313 */ 314 315 316 /** 317 * Opens the class file for the class specified by 318 * <code>classname</code>. 319 * 320 * @param classname a fully-qualified class name 321 * @return null if the file has not been found. 322 * @throws NotFoundException if any error is reported by ClassPath. 323 */ 324 InputStream openClassfile(String classname) 325 throws NotFoundException 326 { 327 if (packages.get(classname) != null) 328 return null; // not found 329 330 ClassPathList list = pathList; 331 InputStream ins = null; 332 NotFoundException error = null; 333 while (list != null) { 334 try { 335 ins = list.path.openClassfile(classname); 336 } 337 catch (NotFoundException e) { 338 if (error == null) 339 error = e; 340 } 341 342 if (ins == null) 343 list = list.next; 344 else 345 return ins; 346 } 347 348 if (error != null) 349 throw error; 350 else 351 return null; // not found 352 } 353 354 /** 355 * Searches the class path to obtain the URL of the class file 356 * specified by classname. It is also used to determine whether 357 * the class file exists. 358 * 359 * @param classname a fully-qualified class name. 360 * @return null if the class file could not be found. 361 */ 362 public URL find(String classname) { 363 if (packages.get(classname) != null) 364 return null; 365 366 ClassPathList list = pathList; 367 URL url = null; 368 while (list != null) { 369 url = list.path.find(classname); 370 if (url == null) 371 list = list.next; 372 else 373 return url; 374 } 375 376 return null; 377 } 378 379 /** 380 * Reads from an input stream until it reaches the end. 381 * 382 * @return the contents of that input stream 383 */ 384 public static byte[] readStream(InputStream fin) throws IOException { 385 byte[][] bufs = new byte[8][]; 386 int bufsize = 4096; 387 388 for (int i = 0; i < 8; ++i) { 389 bufs[i] = new byte[bufsize]; 390 int size = 0; 391 int len = 0; 392 do { 393 len = fin.read(bufs[i], size, bufsize - size); 394 if (len >= 0) 395 size += len; 396 else { 397 byte[] result = new byte[bufsize - 4096 + size]; 398 int s = 0; 399 for (int j = 0; j < i; ++j) { 400 System.arraycopy(bufs[j], 0, result, s, s + 4096); 401 s = s + s + 4096; 402 } 403 404 System.arraycopy(bufs[i], 0, result, s, size); 405 return result; 406 } 407 } while (size < bufsize); 408 bufsize *= 2; 409 } 410 411 throw new IOException("too much data"); 412 } 413 414 /** 415 * Reads from an input stream and write to an output stream 416 * until it reaches the end. This method does not close the 417 * streams. 418 */ 419 public static void copyStream(InputStream fin, OutputStream fout) 420 throws IOException 421 { 422 int bufsize = 4096; 423 for (int i = 0; i < 8; ++i) { 424 byte[] buf = new byte[bufsize]; 425 int size = 0; 426 int len = 0; 427 do { 428 len = fin.read(buf, size, bufsize - size); 429 if (len >= 0) 430 size += len; 431 else { 432 fout.write(buf, 0, size); 433 return; 434 } 435 } while (size < bufsize); 436 fout.write(buf); 437 bufsize *= 2; 438 } 439 440 throw new IOException("too much data"); 441 } 442} 443