|
16 | 16 | */
|
17 | 17 | package com.comphenix.protocol.wrappers;
|
18 | 18 |
|
| 19 | +import com.comphenix.protocol.reflect.accessors.Accessors; |
| 20 | +import com.comphenix.protocol.reflect.accessors.ConstructorAccessor; |
| 21 | +import com.comphenix.protocol.reflect.accessors.FieldAccessor; |
| 22 | +import com.comphenix.protocol.reflect.instances.DefaultInstances; |
| 23 | +import com.google.common.base.Defaults; |
| 24 | +import java.lang.reflect.Constructor; |
19 | 25 | import java.lang.reflect.Field;
|
20 | 26 | import java.lang.reflect.Modifier;
|
21 | 27 | import java.util.Arrays;
|
|
41 | 47 | * @author dmulloy2
|
42 | 48 | */
|
43 | 49 | public class AutoWrapper<T> implements EquivalentConverter<T> {
|
| 50 | + private static final Object[] NO_ARGS = new Object[0]; |
| 51 | + |
44 | 52 | private Map<Integer, Function<Object, Object>> wrappers = new HashMap<>();
|
45 | 53 | private Map<Integer, Function<Object, Object>> unwrappers = new HashMap<>();
|
46 | 54 |
|
| 55 | + // lazy |
| 56 | + private FieldAccessor[] nmsAccessors; |
| 57 | + private FieldAccessor[] wrapperAccessors; |
| 58 | + |
| 59 | + private Object[] nmsDefaultArgs; |
| 60 | + private ConstructorAccessor nmsInstanceCreator; |
| 61 | + |
47 | 62 | private Class<T> wrapperClass;
|
48 | 63 | private Class<?> nmsClass;
|
49 | 64 |
|
@@ -79,75 +94,77 @@ public T wrap(Object nmsObject) {
|
79 | 94 | throw new InvalidWrapperException(wrapperClass.getSimpleName() + " is not accessible!", ex);
|
80 | 95 | }
|
81 | 96 |
|
82 |
| - Field[] wrapperFields = wrapperClass.getDeclaredFields(); |
83 |
| - Field[] nmsFields = Arrays |
84 |
| - .stream(nmsClass.getDeclaredFields()) |
85 |
| - .filter(field -> !Modifier.isStatic(field.getModifiers())) |
86 |
| - .toArray(Field[]::new); |
| 97 | + // ensures that all accessors are present |
| 98 | + computeFieldAccessors(); |
87 | 99 |
|
88 |
| - for (int i = 0; i < wrapperFields.length; i++) { |
89 |
| - try { |
90 |
| - Field wrapperField = wrapperFields[i]; |
| 100 | + for (int i = 0; i < wrapperAccessors.length; i++) { |
| 101 | + FieldAccessor source = nmsAccessors[i]; |
| 102 | + FieldAccessor target = wrapperAccessors[i]; |
91 | 103 |
|
92 |
| - Field nmsField = nmsFields[i]; |
93 |
| - if (!nmsField.isAccessible()) |
94 |
| - nmsField.setAccessible(true); |
| 104 | + Object value = source.get(nmsObject); |
| 105 | + if (wrappers.containsKey(i)) |
| 106 | + value = wrappers.get(i).apply(value); |
95 | 107 |
|
96 |
| - Object value = nmsField.get(nmsObject); |
97 |
| - if (wrappers.containsKey(i)) |
98 |
| - value = wrappers.get(i).apply(value); |
99 |
| - |
100 |
| - wrapperField.set(instance, value); |
101 |
| - } catch (Exception ex) { |
102 |
| - throw new InvalidWrapperException("Failed to wrap field at index " + i, ex); |
103 |
| - } |
| 108 | + target.set(instance, value); |
104 | 109 | }
|
105 | 110 |
|
106 | 111 | return instance;
|
107 | 112 | }
|
108 | 113 |
|
109 | 114 | public Object unwrap(Object wrapper) {
|
110 |
| - Object instance; |
| 115 | + // ensures that all accessors are present |
| 116 | + computeFieldAccessors(); |
| 117 | + computeNmsConstructorAccess(); |
111 | 118 |
|
112 |
| - try { |
113 |
| - instance = nmsClass.newInstance(); |
114 |
| - } catch (ReflectiveOperationException ex) { |
115 |
| - throw new InvalidWrapperException("Failed to construct new " + nmsClass.getSimpleName(), ex); |
116 |
| - } |
| 119 | + Object instance = nmsInstanceCreator.invoke(nmsDefaultArgs); |
117 | 120 |
|
118 |
| - Field[] wrapperFields = wrapperClass.getDeclaredFields(); |
119 |
| - Field[] nmsFields = Arrays |
120 |
| - .stream(nmsClass.getDeclaredFields()) |
121 |
| - .filter(field -> !Modifier.isStatic(field.getModifiers())) |
122 |
| - .toArray(Field[]::new); |
123 |
| - |
124 |
| - for (int i = 0; i < wrapperFields.length; i++) { |
125 |
| - try { |
126 |
| - Field wrapperField = wrapperFields[i]; |
127 |
| - |
128 |
| - Field nmsField = nmsFields[i]; |
129 |
| - if (!nmsField.isAccessible()) |
130 |
| - nmsField.setAccessible(true); |
131 |
| - if (Modifier.isFinal(nmsField.getModifiers())) |
132 |
| - unsetFinal(nmsField); |
133 |
| - |
134 |
| - Object value = wrapperField.get(wrapper); |
135 |
| - if (unwrappers.containsKey(i)) |
136 |
| - value = unwrappers.get(i).apply(value); |
137 |
| - |
138 |
| - nmsField.set(instance, value); |
139 |
| - } catch (ReflectiveOperationException ex) { |
140 |
| - throw new InvalidWrapperException("Failed to unwrap field", ex); |
141 |
| - } |
| 121 | + for (int i = 0; i < wrapperAccessors.length; i++) { |
| 122 | + FieldAccessor source = wrapperAccessors[i]; |
| 123 | + FieldAccessor target = nmsAccessors[i]; |
| 124 | + |
| 125 | + Object value = source.get(wrapper); |
| 126 | + if (unwrappers.containsKey(i)) |
| 127 | + value = unwrappers.get(i).apply(value); |
| 128 | + |
| 129 | + target.set(instance, value); |
142 | 130 | }
|
143 | 131 |
|
144 | 132 | return instance;
|
145 | 133 | }
|
146 | 134 |
|
147 |
| - private void unsetFinal(Field field) throws ReflectiveOperationException { |
148 |
| - Field modifiers = Field.class.getDeclaredField("modifiers"); |
149 |
| - modifiers.setAccessible(true); |
150 |
| - modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL); |
| 135 | + private void computeFieldAccessors() { |
| 136 | + if (nmsAccessors == null) { |
| 137 | + nmsAccessors = Arrays |
| 138 | + .stream(nmsClass.getDeclaredFields()) |
| 139 | + .filter(field -> !Modifier.isStatic(field.getModifiers())) |
| 140 | + .map(field -> Accessors.getFieldAccessor(field, true)) |
| 141 | + .toArray(FieldAccessor[]::new); |
| 142 | + } |
| 143 | + |
| 144 | + if (wrapperAccessors == null) { |
| 145 | + wrapperAccessors = Arrays |
| 146 | + .stream(wrapperClass.getDeclaredFields()) |
| 147 | + .map(field -> Accessors.getFieldAccessor(field, true)) |
| 148 | + .toArray(FieldAccessor[]::new); |
| 149 | + } |
| 150 | + } |
| 151 | + |
| 152 | + private void computeNmsConstructorAccess() { |
| 153 | + if (nmsInstanceCreator == null) { |
| 154 | + ConstructorAccessor noArgs = Accessors.getConstructorAccessorOrNull(nmsClass); |
| 155 | + if (noArgs != null) { |
| 156 | + // no args constructor is available - use it |
| 157 | + nmsInstanceCreator = noArgs; |
| 158 | + nmsDefaultArgs = NO_ARGS; |
| 159 | + } else { |
| 160 | + // use the first constructor of the class |
| 161 | + nmsInstanceCreator = Accessors.getConstructorAccessor(nmsClass.getDeclaredConstructors()[0]); |
| 162 | + nmsDefaultArgs = Arrays |
| 163 | + .stream(nmsInstanceCreator.getConstructor().getParameterTypes()) |
| 164 | + .map(type -> type.isPrimitive() ? Defaults.defaultValue(type) : null) |
| 165 | + .toArray(Object[]::new); |
| 166 | + } |
| 167 | + } |
151 | 168 | }
|
152 | 169 |
|
153 | 170 | // ---- Equivalent conversion
|
|
0 commit comments