// 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> { // 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 { // 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 } }