diff options
Diffstat (limited to 'support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/ClassLoadingUtils.kt')
-rw-r--r-- | support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/ClassLoadingUtils.kt | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/ClassLoadingUtils.kt b/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/ClassLoadingUtils.kt new file mode 100644 index 0000000000..05a1031a8a --- /dev/null +++ b/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/ClassLoadingUtils.kt @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: BSD-3-Clause +package org.islandoftex.arara.utils + +import java.io.File +import java.lang.reflect.InvocationTargetException +import java.net.MalformedURLException +import java.net.URLClassLoader + +/** + * Implements utilitary methods for classloading and object instantiation. + * + * @author Island of TeX + * @version 5.0 + * @since 4.0 + */ +object ClassLoadingUtils { + /** + * Indicator of success or failure of class loading. + */ + enum class ClassLoadingStatus { + SUCCESS, + FILE_NOT_FOUND, + MALFORMED_URL, + CLASS_NOT_FOUND, + ILLEGAL_ACCESS, + INSTANTIATION_EXCEPTION + } + + /** + * Loads a class from the provided file, potentially a Java archive. + * @param file File containing the Java bytecode (namely, a JAR). + * @param name The canonical name of the class. + * @return A pair representing the status and the class. + */ + fun loadClass(file: File, name: String): + Pair<ClassLoadingStatus, Class<*>> { + // status and class to be returned, + // it defaults to an object class + var value: Class<*> = Any::class.java + + // if file does not exist, nothing + // can be done, status is changed + val status = if (!file.exists()) { + ClassLoadingStatus.FILE_NOT_FOUND + } else { + // classloading involves defining + // a classloader and fetching the + // desired class from it, based on + // the provided file archive + try { + // creates a new classloader with + // the provided file (potentially + // a JAR file) + val classloader = URLClassLoader(arrayOf(file.toURI().toURL()), + ClassLoadingUtils::class.java.classLoader) + + // fetches the class from the + // instantiated classloader + value = Class.forName(name, true, classloader) + ClassLoadingStatus.SUCCESS + } catch (_: MalformedURLException) { + ClassLoadingStatus.MALFORMED_URL + } catch (_: ClassNotFoundException) { + ClassLoadingStatus.CLASS_NOT_FOUND + } + } + + // return a new pair based on the + // current status and class holder + return status to value + } + + /** + * Loads a class from the provided file, instantiating it. + * @param file File containing the Java bytecode (namely, a JAR). + * @param name The canonical name of the class. + * @return A pair representing the status and the class object. + */ + fun loadObject(file: File, name: String): Pair<ClassLoadingStatus, Any> { + // load the corresponding class + // based on the qualified name + val pair = loadClass(file, name) + + // status and object to be returned, + // it defaults to an object + var status = pair.first + var value = Any() + + // checks if the class actually + // exists, otherwise simply + // ignore instantiation + if (status == ClassLoadingStatus.SUCCESS) { + // object instantiation relies + // on the default constructor + // (without arguments), class + // must implement it + + // OBS: constructors with arguments + // must be invoked through reflection + try { + // get the class reference from + // the pair and instantiate it + // by invoking the default + // constructor (without arguments) + value = pair.second.getDeclaredConstructor().newInstance() + } catch (_: IllegalAccessException) { + status = ClassLoadingStatus.ILLEGAL_ACCESS + } catch (_: InstantiationException) { + // the user wanted to instantiate an abstract class + status = ClassLoadingStatus.INSTANTIATION_EXCEPTION + } catch (_: InvocationTargetException) { + // the underlying constructor caused an exception + status = ClassLoadingStatus.INSTANTIATION_EXCEPTION + } + } + + // return a new pair based on the + // current status and object holder + return status to value + } +} |