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 } |
|