|
48 | 48 | import java.util.ArrayList; |
49 | 49 | import java.util.Calendar; |
50 | 50 | import java.util.Collection; |
| 51 | +import java.util.Collections; |
51 | 52 | import java.util.Date; |
| 53 | +import java.util.HashMap; |
| 54 | +import java.util.Map; |
52 | 55 | import java.util.jar.JarEntry; |
53 | 56 | import java.util.jar.JarFile; |
54 | 57 | import java.util.regex.Matcher; |
@@ -607,6 +610,82 @@ else if (protocol.equals("jar")) { |
607 | 610 | return result; |
608 | 611 | } |
609 | 612 |
|
| 613 | + /** |
| 614 | + * Finds {@link URL}s of available resources. Both JAR files and files on disk |
| 615 | + * are searched, according to the following mechanism: |
| 616 | + * <ol> |
| 617 | + * <li>Resources at the given {@code pathPrefix} are discovered using |
| 618 | + * {@link ClassLoader#getResources(String)} with the current thread's context |
| 619 | + * class loader. In particular, this invocation discovers resources in JAR |
| 620 | + * files beneath the given {@code pathPrefix}.</li> |
| 621 | + * <li>The directory named {@code pathPrefix} beneath the given |
| 622 | + * {@code baseDirectory} is scanned last, so that users can more easily |
| 623 | + * override resources provided inside JAR files by placing a resource of the |
| 624 | + * same name within that directory.</li> |
| 625 | + * </ol> |
| 626 | + * <p> |
| 627 | + * In both cases, resources are then recursively scanned using |
| 628 | + * {@link #listContents(URL)}, and anything matching the given {@code regex} |
| 629 | + * pattern is added to the output map. |
| 630 | + * </p> |
| 631 | + * |
| 632 | + * @param regex The regex to use when matching resources, or null to match |
| 633 | + * everything. |
| 634 | + * @param pathPrefix The path to search for resources. |
| 635 | + * @param baseDirectory The {@code baseDirectory/pathPrefix} directory to scan |
| 636 | + * <em>after</em> the URL resources. |
| 637 | + * @return A map of URLs referencing the matched resources. |
| 638 | + * @see AppUtils#getBaseDirectory |
| 639 | + */ |
| 640 | + public static Map<String, URL> findResources(final String regex, |
| 641 | + final String pathPrefix, final File baseDirectory) |
| 642 | + { |
| 643 | + // scan URL resource paths first |
| 644 | + final ClassLoader loader = Thread.currentThread().getContextClassLoader(); |
| 645 | + final ArrayList<URL> urls = new ArrayList<>(); |
| 646 | + try { |
| 647 | + urls.addAll(Collections.list(loader.getResources(pathPrefix + "/"))); |
| 648 | + } |
| 649 | + catch (final IOException exc) { |
| 650 | + // error loading resources; proceed with an empty list |
| 651 | + } |
| 652 | + |
| 653 | + // scan directory second; user can thus override resources from JARs |
| 654 | + if (baseDirectory != null) { |
| 655 | + try { |
| 656 | + urls.add(new File(baseDirectory, pathPrefix).toURI().toURL()); |
| 657 | + } |
| 658 | + catch (final MalformedURLException exc) { |
| 659 | + // error adding directory; proceed without it |
| 660 | + } |
| 661 | + } |
| 662 | + |
| 663 | + return findResources(regex, urls); |
| 664 | + } |
| 665 | + |
| 666 | + /** |
| 667 | + * Finds {@link URL}s of resources known to ImageJ. |
| 668 | + * <p> |
| 669 | + * Each of the given {@link URL}s is recursively scanned using |
| 670 | + * {@link #listContents(URL)}, and anything matching the given {@code regex} |
| 671 | + * pattern is added to the output map.</li> |
| 672 | + * |
| 673 | + * @param regex The regex to use when matching resources, or null to match |
| 674 | + * everything. |
| 675 | + * @param urls Paths to search for resources. |
| 676 | + * @return A map of URLs referencing the matched resources. |
| 677 | + */ |
| 678 | + public static Map<String, URL> findResources(final String regex, |
| 679 | + final Iterable<URL> urls) |
| 680 | + { |
| 681 | + final HashMap<String, URL> result = new HashMap<>(); |
| 682 | + final Pattern pattern = regex == null ? null : Pattern.compile(regex); |
| 683 | + for (final URL url : urls) { |
| 684 | + getResources(pattern, result, url); |
| 685 | + } |
| 686 | + return result; |
| 687 | + } |
| 688 | + |
610 | 689 | // -- Helper methods -- |
611 | 690 |
|
612 | 691 | /** Builds the {@link #VERSION_PATTERN} constant. */ |
@@ -639,6 +718,45 @@ private static String classifiers() { |
639 | 718 | return sb.toString(); |
640 | 719 | } |
641 | 720 |
|
| 721 | + /** Helper method of {@link #findResources(String, Iterable)}. */ |
| 722 | + private static void getResources(final Pattern pattern, |
| 723 | + final Map<String, URL> result, final URL base) |
| 724 | + { |
| 725 | + final String prefix = urlPath(base); |
| 726 | + if (prefix == null) return; // unsupported base URL |
| 727 | + |
| 728 | + for (final URL url : FileUtils.listContents(base)) { |
| 729 | + final String s = urlPath(url); |
| 730 | + if (s == null || !s.startsWith(prefix)) continue; |
| 731 | + |
| 732 | + if (pattern == null || pattern.matcher(s).matches()) { |
| 733 | + // this resource matches the pattern |
| 734 | + final String key = urlPath(s.substring(prefix.length())); |
| 735 | + if (key != null) result.put(key, url); |
| 736 | + } |
| 737 | + } |
| 738 | + } |
| 739 | + |
| 740 | + /** Helper method of {@link #getResources(Pattern, Map, URL)}. */ |
| 741 | + private static String urlPath(final URL url) { |
| 742 | + try { |
| 743 | + return url.toURI().toString(); |
| 744 | + } |
| 745 | + catch (final URISyntaxException exc) { |
| 746 | + return null; |
| 747 | + } |
| 748 | + } |
| 749 | + |
| 750 | + /** Helper method of {@link #getResources(Pattern, Map, URL)}. */ |
| 751 | + private static String urlPath(final String path) { |
| 752 | + try { |
| 753 | + return new URI(path).getPath(); |
| 754 | + } |
| 755 | + catch (final URISyntaxException exc) { |
| 756 | + return null; |
| 757 | + } |
| 758 | + } |
| 759 | + |
642 | 760 | // -- Deprecated methods -- |
643 | 761 |
|
644 | 762 | /** |
|
0 commit comments