diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ClassSource.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ClassSource.java index 78bd9d3b..658ff489 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ClassSource.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ClassSource.java @@ -1,5 +1,6 @@ package com.comphenix.protocol.utility; +import java.util.Collections; import java.util.Map; /** @@ -14,7 +15,7 @@ public abstract class ClassSource { public static ClassSource fromClassLoader() { return fromClassLoader(ClassSource.class.getClassLoader()); } - + /** * Construct a class source from the default class loader and package. * @param packageName - the package that is prepended to every lookup. @@ -23,7 +24,7 @@ public abstract class ClassSource { public static ClassSource fromPackage(String packageName) { return fromClassLoader().usingPackage(packageName); } - + /** * Construct a class source from the given class loader. * @param loader - the class loader. @@ -40,6 +41,7 @@ public abstract class ClassSource { /** * Construct a class source from a mapping of canonical names and the corresponding classes. + * If the map is null, it will be interpreted as an empty map. If the map does not contain a Class with the specified name, or that string maps to NULL explicitly, a {@link ClassNotFoundException} will be thrown. * @param map - map of class names and classes. * @return The class source. */ @@ -47,11 +49,45 @@ public abstract class ClassSource { return new ClassSource() { @Override public Class loadClass(String canonicalName) throws ClassNotFoundException { - return map.get(canonicalName); + Class loaded = map == null ? null : map.get(canonicalName); + if(loaded == null){ + // Throw the appropriate exception if we can't load the class + throw new ClassNotFoundException("The specified class could not be found by this ClassLoader."); + } + return loaded; } }; } + /** + * @return A ClassLoader which will never successfully load a class. + */ + public static ClassSource empty(){ + return fromMap(Collections.>emptyMap()); + } + + /** + * Retrieve a class source that will attempt lookups in each of the given sources in the order they are in the array, and return the first value that is found. + * If the sources array is null or composed of any null elements, an exception will be thrown. + * @param sources - the class sources. + * @return A new class source. + */ + public static ClassSource attemptLoadFrom(final ClassSource... sources) { + if(sources.length == 0){ // Throws NPE if sources is null, which is what we want + return ClassSource.empty(); + } + + ClassSource source = null; + for(int i = 0; i < sources.length; i++){ + if(sources[i] == null){ + throw new IllegalArgumentException("Null values are not permitted as ClassSources."); + } + + source = source == null ? sources[i] : source.retry(sources[i]); + } + return source; + } + /** * Retrieve a class source that will retry failed lookups in the given source. * @param other - the other class source. @@ -69,7 +105,7 @@ public abstract class ClassSource { } }; } - + /** * Retrieve a class source that prepends a specific package name to every lookup. * @param packageName - the package name to prepend. @@ -93,7 +129,7 @@ public abstract class ClassSource { protected static String append(String a, String b) { boolean left = a.endsWith("."); boolean right = b.endsWith("."); - + // Only add a dot if necessary if (left && right) return a.substring(0, a.length() - 1) + b; @@ -102,11 +138,11 @@ public abstract class ClassSource { else return a + "." + b; } - + /** * Retrieve a class by name. * @param canonicalName - the full canonical name of the class. - * @return The corresponding class + * @return The corresponding class. If the class is not found, NULL should not be returned, instead a {@code ClassNotFoundException} exception should be thrown. * @throws ClassNotFoundException If the class could not be found. */ public abstract Class loadClass(String canonicalName) throws ClassNotFoundException;