Skip to content

Commit 179de87

Browse files
committed
FileUtils: add findResources methods
They will be useful for finding scripts inside JARs on the classpath. These methods were migrated from imagej-ui-swing; see: https://github.com/imagej/imagej-ui-swing/blob/imagej-ui-swing-0.18.0/src/main/java/net/imagej/ui/swing/script/FileFunctions.java
1 parent d389ff0 commit 179de87

File tree

1 file changed

+118
-0
lines changed

1 file changed

+118
-0
lines changed

src/main/java/org/scijava/util/FileUtils.java

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@
4848
import java.util.ArrayList;
4949
import java.util.Calendar;
5050
import java.util.Collection;
51+
import java.util.Collections;
5152
import java.util.Date;
53+
import java.util.HashMap;
54+
import java.util.Map;
5255
import java.util.jar.JarEntry;
5356
import java.util.jar.JarFile;
5457
import java.util.regex.Matcher;
@@ -607,6 +610,82 @@ else if (protocol.equals("jar")) {
607610
return result;
608611
}
609612

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+
610689
// -- Helper methods --
611690

612691
/** Builds the {@link #VERSION_PATTERN} constant. */
@@ -639,6 +718,45 @@ private static String classifiers() {
639718
return sb.toString();
640719
}
641720

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+
642760
// -- Deprecated methods --
643761

644762
/**

0 commit comments

Comments
 (0)