All Downloads are FREE. Search and download functionalities are using the official Maven repository.

it.unibo.alchemist.util.ClassPathScanner.kt Maven / Gradle / Ivy

Go to download

Abstract, incarnation independent implementations of the Alchemist's interfaces. Provides support for those who want to write incarnations.

There is a newer version: 35.0.1
Show newest version
/*
 * Copyright (C) 2010-2022, Danilo Pianini and contributors
 * listed, for each module, in the respective subproject's build.gradle.kts file.
 *
 * This file is part of Alchemist, and is distributed under the terms of the
 * GNU General Public License, with a linking exception,
 * as described in the file LICENSE in the Alchemist distribution's top directory.
 */

package it.unibo.alchemist.util

import com.github.benmanes.caffeine.cache.Caffeine
import com.google.common.base.Objects
import io.github.classgraph.ClassGraph
import java.io.InputStream
import java.lang.reflect.Modifier
import java.net.URL
import java.util.regex.Pattern

/**
 * An utility class providing support for loading arbitrary subclasses available in the classpath.
 */
object ClassPathScanner {

    private val loader = Caffeine.newBuilder().build>> { scanData ->
        classGraphForPackages(*scanData.inPackages)
            .enableClassInfo()
            .scan()
            .let { scanResult ->
                if (scanData.superClass.isInterface) {
                    scanResult.getClassesImplementing(scanData.superClass.name)
                } else {
                    scanResult.getSubclasses(scanData.superClass.name)
                }
            }
            .filter { !it.isAbstract }.loadClasses()
    }

    private fun classGraphForPackages(vararg inPackage: String): ClassGraph = ClassGraph()
        .apply {
            // WHITELIST package
            acceptPackages(*inPackage)
            // BLACKLIST package
            rejectPackages("org.gradle")
        }

    /**
     * This function loads all subtypes of the provided Java class that can be discovered on the current classpath.
     *
     * This function cannot use `reified` and `inline` (as it should have) due to Java being unaware of the required
     * transformation to use them.
     */
    @JvmStatic
    @Suppress("UNCHECKED_CAST")
    fun  subTypesOf(superClass: Class, vararg inPackage: String): List> = when {
        Modifier.isFinal(superClass.modifiers) -> listOf(superClass)
        else -> loader[ScanData(superClass, inPackage)] as List>
    }

    /**
     * This function loads all subtypes of the provided Java class that can be discovered on the current classpath.
     */
    inline fun  subTypesOf(vararg inPackage: String): List> =
        subTypesOf(T::class.java, *inPackage)

    /**
     * This function returns a list of all the resources in a certain (optional) package matching a regular expression.
     *
     * This function cannot use `reified` and `inline` (as it should have) due to Java being unaware of the required
     * transformation to use them.
     */
    @JvmStatic
    fun resourcesMatching(regex: String, vararg inPackage: String): List = classGraphForPackages(*inPackage)
        .scan().getResourcesMatchingPattern(Pattern.compile(regex))
        .urLs

    /**
     * This function returns a list of all the resources in a certain (optional) package matching a regular expression.
     */
    @JvmStatic
    fun resourcesMatchingAsStream(regex: String, vararg inPackage: String): List =
        resourcesMatching(regex, *inPackage).map { it.openStream() }

    private data class ScanData(val superClass: Class<*>, val inPackages: Array) {

        val hashCode = Objects.hashCode(superClass, *inPackages)

        override fun equals(other: Any?) = other === this ||
            other is ScanData && superClass == other.superClass && inPackages.contentEquals(other.inPackages)

        override fun hashCode(): Int = hashCode

        override fun toString(): String = "ScanData(${superClass.simpleName}, ${inPackages.asList()})"
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy