aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni/utils/ReflectionUtils.kt
blob: 1fe8b882c79776c71f138e5d2be44cbd051a1228 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package at.hannibal2.skyhanni.utils

import net.minecraftforge.fml.common.Loader
import net.minecraftforge.fml.common.ModContainer
import java.lang.reflect.Constructor
import java.lang.reflect.Field
import java.lang.reflect.Modifier
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty0
import kotlin.reflect.KProperty1
import kotlin.reflect.full.isSubtypeOf
import kotlin.reflect.full.memberProperties
import kotlin.reflect.full.starProjectedType
import kotlin.reflect.jvm.isAccessible

object ReflectionUtils {

    // TODO nea?
//    fun <T> dynamic(block: () -> KMutableProperty0<T>?): ReadWriteProperty<Any?, T?> {
//        return object : ReadWriteProperty<Any?, T?> {
//            override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
//                return block()?.get()
//            }
//
//            override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
//                if (value != null)
//                    block()?.set(value)
//            }
//        }
//    }

    fun <T, R> dynamic(root: KProperty0<R?>, child: KMutableProperty1<R, T>) =
        object : ReadWriteProperty<Any?, T?> {
            override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
                val rootObj = root.get() ?: return null
                return child.get(rootObj)
            }

            override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
                if (value == null) return
                val rootObj = root.get() ?: return
                child.set(rootObj, value)
            }
        }

    inline fun <reified T : Any> Any.getPropertiesWithType() =
        this::class.memberProperties
            .filter { it.returnType.isSubtypeOf(T::class.starProjectedType) }
            .map {
                it.isAccessible = true
                (it as KProperty1<Any, T>).get(this)
            }

    fun Field.makeAccessible() = also { isAccessible = true }

    fun <T> Constructor<T>.makeAccessible() = also { isAccessible = true }

    fun Field.removeFinal(): Field {
        javaClass.getDeclaredField("modifiers").makeAccessible().set(this, modifiers and (Modifier.FINAL.inv()))
        return this
    }

    fun StackTraceElement.getClassInstance(): Class<*> {
        return Class.forName(this.className)
    }

    private val packageLookup by lazy {
        Loader.instance().modList
            .flatMap { mod -> mod.ownedPackages.map { it to mod } }
            .toMap()
    }

    val Class<*>.shPackageName
        get() =
            canonicalName?.substringBeforeLast('.')

    fun Class<*>.getModContainer(): ModContainer? {
        return packageLookup[shPackageName]
    }

    fun Class<*>.getDeclaredFieldOrNull(name: String): Field? = declaredFields.firstOrNull { it.name == name }
}