aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/gtPlusPlus/core/util/reflect/ReflectionUtils.java
blob: 6bd0ad638d22fd74e65d02e5cea043786721fd6d (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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package gtPlusPlus.core.util.reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

import com.gtnewhorizon.gtnhlib.reflect.Fields;

import gtPlusPlus.api.objects.Logger;

@SuppressWarnings({ "unchecked", "rawtypes" })
public class ReflectionUtils {

    public static Map<String, CachedField> mCachedFields = new HashMap<>();
    public static Map<Field, Fields.ClassFields.Field> mCachedFieldAccessors = new HashMap<>();

    private static class CachedField {

        private final Field FIELD;

        public CachedField(Field aField, boolean isStatic) {
            FIELD = aField;
        }

        public Field get() {
            return FIELD;
        }

    }

    private static Fields.ClassFields.Field cacheAccessor(Field f) {
        return mCachedFieldAccessors.computeIfAbsent(
            f,
            (field) -> Fields.ofClass(field.getDeclaringClass())
                .getUntypedField(Fields.LookupType.DECLARED_IN_HIERARCHY, field.getName()));
    }

    private static boolean cacheField(Class<?> aClass, Field aField) {
        if (aField == null) {
            return false;
        }
        boolean isStatic = Modifier.isStatic(aField.getModifiers());
        CachedField y = mCachedFields.get(aClass.getName() + "." + aField.getName());
        if (y == null) {
            mCachedFields.put(aClass.getName() + "." + aField.getName(), new CachedField(aField, isStatic));
            return true;
        }
        return false;
    }

    /**
     * Returns a cached {@link Field} object.
     *
     * @param aClass     - Class containing the Method.
     * @param aFieldName - Field name in {@link String} form.
     * @return - Valid, non-final, {@link Field} object, or {@link null}.
     */
    public static Field getField(final Class<?> aClass, final String aFieldName) {
        if (aClass == null || aFieldName == null || aFieldName.length() <= 0) {
            return null;
        }
        CachedField y = mCachedFields.get(aClass.getName() + "." + aFieldName);
        if (y == null) {
            Field u;
            try {
                u = getField_Internal(aClass, aFieldName);
                if (u != null) {
                    Logger.REFLECTION("Caching Field '" + aFieldName + "' from " + aClass.getName());
                    cacheField(aClass, u);
                    return u;
                }
            } catch (NoSuchFieldException e) {}
            return null;

        } else {
            return y.get();
        }
    }

    /**
     * Returns a cached {@link Field} object.
     *
     * @param aInstance  - {@link Object} to get the field instance from.
     * @param aFieldName - Field name in {@link String} form.
     * @return - Valid, non-final, {@link Field} object, or {@link null}.
     */
    public static <T> T getField(final Object aInstance, final String aFieldName) {
        try {
            return (T) getField(aInstance.getClass(), aFieldName).get(aInstance);
        } catch (IllegalArgumentException | IllegalAccessException e) {
            return null;
        }
    }

    /*
     * Utility Functions
     */

    public static void makeFieldAccessible(final Field field) {
        if (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(
            field.getDeclaringClass()
                .getModifiers())) {
            field.setAccessible(true);
        }
    }

    public static boolean setField(final Object object, final Field field, final Object fieldValue) {
        if (field == null) return false;
        final Class<?> clazz;
        if (object instanceof Class) {
            clazz = (Class<?>) object;
        } else {
            clazz = object.getClass();
        }
        try {
            final Field field2 = getField(clazz, field.getName());
            if (field2 != null) {
                setFieldValue_Internal(object, field, fieldValue);
                return true;
            }
        } catch (final Exception e) {
            Logger.REFLECTION("setField(" + object + ", " + field.getName() + ") failed.");
            throw new IllegalStateException(e);
        }
        return false;
    }

    /*
     * Below Code block is used for determining generic types associated with type<E>
     */

    /*
     * End of Generics Block
     */

    private static Field getField_Internal(final Class<?> clazz, final String fieldName) throws NoSuchFieldException {
        try {
            Logger.REFLECTION("Field: Internal Lookup: " + fieldName);
            Field k = clazz.getDeclaredField(fieldName);
            makeFieldAccessible(k);
            return k;
        } catch (final NoSuchFieldException e) {
            Logger.REFLECTION("Field: Internal Lookup Failed: " + fieldName);
            final Class<?> superClass = clazz.getSuperclass();
            if (superClass == null) {
                Logger.REFLECTION("Unable to find field '" + fieldName + "'");
                throw e;
            }
            Logger.REFLECTION("Method: Recursion Lookup: " + fieldName + " - Checking in " + superClass.getName());
            return getField_Internal(superClass, fieldName);
        }
    }

    /**
     *
     * Set the value of a field reflectively.
     */
    private static void setFieldValue_Internal(Object owner, Field field, Object value) {
        cacheAccessor(field).setValue(owner, value);
    }

    public static <T> T getFieldValue(Field field, Object instance) {
        try {
            return (T) field.get(instance);
        } catch (IllegalArgumentException | IllegalAccessException e) {}
        return null;
    }

}