|
17 | 17 |
|
18 | 18 | package com.lambda.util.reflections |
19 | 19 |
|
20 | | -import com.lambda.Lambda.LOG |
21 | 20 | import com.lambda.util.extension.isObject |
22 | 21 | import com.lambda.util.extension.objectInstance |
23 | 22 | import org.reflections.Reflections |
24 | 23 | import org.reflections.util.ConfigurationBuilder |
25 | 24 | import java.lang.reflect.Modifier |
26 | | -import java.util.regex.Pattern |
| 25 | +import java.util.Objects |
27 | 26 |
|
28 | | -/** |
29 | | - * Retrieves instances of objects of the specified type using a DSL configuration. |
30 | | - * |
31 | | - * @param T The type of objects to retrieve instances for. |
32 | | - * @param block A DSL block used to configure the [ReflectionConfigDsl]. |
33 | | - * @return A [List] of object instances of type [T], or null if no instances can be found. |
34 | | - */ |
35 | | -inline fun <reified T : Any> getInstances(block: ReflectionConfigDsl.() -> Unit): List<T> = |
36 | | - getInstances<T>(getConfiguration(block)) |
| 27 | +val cache = mutableMapOf<Int, Reflections>() |
37 | 28 |
|
38 | 29 | /** |
39 | | - * Retrieves instances of objects of the specified type using a provided configuration. |
| 30 | + * Retrieves all instances of the specified type `T`. |
40 | 31 | * |
41 | | - * @param T The type of objects to retrieve instances for. |
42 | | - * @param config A pre-configured [ConfigurationBuilder]. |
43 | | - * @return A [List] of object instances of type [T], or null if no instances can be found. |
44 | | - */ |
45 | | -inline fun <reified T : Any> getInstances(config: ConfigurationBuilder): List<T> = |
46 | | - getSubTypesOf<T>(config).mapNotNull { clazz: Class<*> -> |
47 | | - if (!clazz.isInterface |
48 | | - && !clazz.isEnum |
49 | | - && !clazz.isAnnotation |
50 | | - && !clazz.isObject |
51 | | - ) clazz.constructors.filter { !Modifier.isAbstract(clazz.modifiers) }.firstOrNull { it.parameterCount == 0 } |
52 | | - ?.newInstance() as? T |
53 | | - ?: null.also { LOG.debug("Could not find a proper no-arg constructor for the class ${clazz.simpleName}.") } |
54 | | - else clazz.objectInstance as? T |
55 | | - // We're doomed at this point, now I am become death, the destroyer of worlds |
56 | | - ?: null.also { |
57 | | - LOG.debug( |
58 | | - "No instance of type ${T::class.java.simpleName} could be found on the class ${clazz.simpleName}." + |
59 | | - " Ensure that the class has an INSTANCE field or a no-arg constructor." |
60 | | - ) |
61 | | - } |
62 | | - } |
63 | | - |
64 | | -/** |
65 | | - * Finds all subtypes of a specified class |
| 32 | + * The function caches the results based on the configuration provided via the [block] lambda to avoid redundant |
| 33 | + * reflection calls. |
66 | 34 | * |
67 | | - * @param T The type for which to find subtypes. |
68 | | - * @param config A pre-configured [ConfigurationBuilder]. |
69 | | - * @return A [Set] of [Class] objects representing all subtypes of the specified type. |
| 35 | + * @param T The type of instances to retrieve. |
| 36 | + * @param block A configuration lambda to customize the [ConfigurationBuilder] used to configure Reflections. |
| 37 | + * |
| 38 | + * @return A list of instances of type `T` |
70 | 39 | */ |
71 | | -inline fun <reified T : Any> getSubTypesOf(config: ConfigurationBuilder): Set<Class<*>> = |
72 | | - Reflections(config).getSubTypesOf(T::class.java) |
| 40 | +inline fun <reified T : Any> getInstances(block: ConfigurationBuilder.() -> Unit = { forPackage("com.lambda") }): List<T> { |
| 41 | + val config = ConfigurationBuilder().apply(block) |
| 42 | + val cacheKey = Objects.hash(config.classLoaders, config.urls, config.scanners, config.inputsFilter) |
73 | 43 |
|
| 44 | + // Use previously scanned classes or create a new reflections |
| 45 | + val reflections = cache.getOrPut(cacheKey) { Reflections(config) } |
74 | 46 |
|
75 | | -/** |
76 | | - * Retrieves resources matching a specified pattern |
77 | | - * |
78 | | - * @param pattern The pattern to match resources against. |
79 | | - * @param config A pre-configured [ConfigurationBuilder]. |
80 | | - * @return A [Set] of [String] representing resources matching the specified pattern. |
81 | | - */ |
82 | | -fun getResources(pattern: String, config: ConfigurationBuilder): Set<String> = |
83 | | - Reflections(config).getResources(pattern) |
| 47 | + return reflections.getSubTypesOf(T::class.java) |
| 48 | + .mapNotNull { clazz -> createInstance<T>(clazz) } |
| 49 | +} |
84 | 50 |
|
85 | 51 | /** |
86 | | - * Retrieves resources matching a specified [Pattern] |
| 52 | + * Retrieves all resource paths that match the given pattern. |
| 53 | + * |
| 54 | + * The function caches the results based on the configuration provided via the [block] lambda to avoid redundant |
| 55 | + * reflection calls. |
| 56 | + * |
| 57 | + * @param pattern The resource pattern to search for. |
| 58 | + * @param block A configuration lambda to customize the [ConfigurationBuilder] used to configure Reflections. |
87 | 59 | * |
88 | | - * @param pattern The [Pattern] to match resources against. |
89 | | - * @param block A DSL block used to configure the [ReflectionConfigDsl]. |
90 | | - * @return A [Set] of [String] representing resources matching the specified pattern. |
| 60 | + * @return A set of resource paths that match the specified pattern. |
91 | 61 | */ |
92 | | -inline fun getResources(pattern: Pattern, block: ReflectionConfigDsl.() -> Unit): Set<String> = |
93 | | - getResources(pattern.pattern(), config = getConfiguration(block)) |
| 62 | +inline fun getResources(pattern: String, block: ConfigurationBuilder.() -> Unit = { forPackage("com.lambda") }): Set<String> { |
| 63 | + val config = ConfigurationBuilder().apply(block) |
| 64 | + val cacheKey = Objects.hash(config.classLoaders, config.urls, config.scanners, config.inputsFilter) |
| 65 | + |
| 66 | + // Use previously scanned classes or create a new reflections |
| 67 | + val reflections = cache.getOrPut(cacheKey) { Reflections(config) } |
| 68 | + |
| 69 | + return reflections.getResources(pattern) |
| 70 | +} |
| 71 | + |
| 72 | +inline fun <reified T : Any> createInstance(clazz: Class<*>): T? { |
| 73 | + return when { |
| 74 | + clazz.isInterface || clazz.isEnum || clazz.isAnnotation || clazz.isObject -> { |
| 75 | + // Handle objects (singletons) or invalid types |
| 76 | + clazz.objectInstance as? T |
| 77 | + } |
| 78 | + else -> { |
| 79 | + // Look for a constructor with no parameters |
| 80 | + clazz.constructors |
| 81 | + .filterNot { Modifier.isAbstract(it.declaringClass.modifiers) } // Avoid abstract constructors |
| 82 | + .firstOrNull { it.parameterCount == 0 }?.newInstance() as? T |
| 83 | + } |
| 84 | + } |
| 85 | +} |
0 commit comments