001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.tools;
003
004import java.io.InputStream;
005import java.net.URL;
006import java.util.Collection;
007import java.util.Collections;
008import java.util.HashSet;
009import java.util.Set;
010import java.util.function.Function;
011
012/**
013 * Unified provider that looks up for resource in various classloaders (josm, plugins, etc.).
014 * @since 15416
015 */
016public final class ResourceProvider {
017
018    /** set of class loaders to take resources from */
019    private static final Set<ClassLoader> classLoaders = Collections.synchronizedSet(new HashSet<>());
020    static {
021        try {
022            classLoaders.add(ClassLoader.getSystemClassLoader());
023        } catch (SecurityException e) {
024            Logging.log(Logging.LEVEL_ERROR, "Unable to get system classloader", e);
025        }
026        try {
027            classLoaders.add(ResourceProvider.class.getClassLoader());
028        } catch (SecurityException e) {
029            Logging.log(Logging.LEVEL_ERROR, "Unable to get application classloader", e);
030        }
031    }
032
033    private ResourceProvider() {
034        // Hide default constructor for utilities classes
035    }
036
037    /**
038     * Add an additional class loader to search image for.
039     * @param additionalClassLoader class loader to add to the internal set
040     * @return {@code true} if the set changed as a result of the call
041     */
042    public static boolean addAdditionalClassLoader(ClassLoader additionalClassLoader) {
043        return classLoaders.add(additionalClassLoader);
044    }
045
046    /**
047     * Add a collection of additional class loaders to search image for.
048     * @param additionalClassLoaders class loaders to add to the internal set
049     * @return {@code true} if the set changed as a result of the call
050     */
051    public static boolean addAdditionalClassLoaders(Collection<ClassLoader> additionalClassLoaders) {
052        return classLoaders.addAll(additionalClassLoaders);
053    }
054
055    private static <T> T getFirstNotNull(Function<ClassLoader, T> function) {
056        synchronized (classLoaders) {
057            for (ClassLoader source : classLoaders) {
058                T res = function.apply(source);
059                if (res != null)
060                    return res;
061            }
062        }
063        return null;
064    }
065
066    /**
067     * Finds the resource with the given name.
068     * @param name The resource name
069     * @return A {@code URL} object for reading the resource, or {@code null} if the resource could not be found
070     *         or the invoker doesn't have adequate  privileges to get the resource.
071     * @see ClassLoader#getResource
072     */
073    public static URL getResource(String name) {
074        return getFirstNotNull(x -> x.getResource(name));
075    }
076
077    /**
078     * Finds a resource with a given name, with robustness to known JDK bugs.
079     * @param name name of the desired resource
080     * @return  A {@link java.io.InputStream} object or {@code null} if no resource with this name is found
081     */
082    public static InputStream getResourceAsStream(String name) {
083        return getFirstNotNull(x -> Utils.getResourceAsStream(x, name));
084    }
085}