summaryrefslogtreecommitdiff
path: root/support/arara/source/src/main/kotlin/org/islandoftex/arara/utils/ClassLoadingUtils.kt
diff options
context:
space:
mode:
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.kt121
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
+ }
+}