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}