Skip to content

Commit 192ee26

Browse files
committed
Refactor compatibility utilities to use Try for method and class retrieval
1 parent 6c86c41 commit 192ee26

7 files changed

Lines changed: 313 additions & 101 deletions

File tree

src/main/java/org/mvplugins/multiverse/core/dynamiclistener/DynamicListenerRegistration.java

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public final class DynamicListenerRegistration {
3535
private static final boolean hasEventExecutorCreate;
3636

3737
static {
38-
hasEventExecutorCreate = ReflectHelper.getMethod(EventExecutor.class, "create", Method.class, Class.class) != null;
38+
hasEventExecutorCreate = ReflectHelper.hasMethod(EventExecutor.class, "create", Method.class, Class.class);
3939
}
4040

4141
private final EventPriorityMapper eventPriorityMapper;
@@ -98,18 +98,22 @@ private void registerAsEventClass(DynamicListener listener, Plugin plugin, Metho
9898
}
9999

100100
method.setAccessible(true);
101-
Object methodOutput = ReflectHelper.invokeMethod(listener, method);
102-
if (!(methodOutput instanceof EventRunnable eventRunnable)) {
103-
Logging.warning("Event method %s in %s did not return a SingleEventListener",
104-
method.getName(), listener.getClass().getName());
105-
return;
106-
}
107-
EventExecutor executor = new EventRunnableExecutor<>(eventClass, eventRunnable);
108-
EventPriority priority = getDynamicEventPriority(method);
109-
boolean ignoreCancelled = isIgnoreIfCancelled(method);
110101

111-
Logging.finest("Registering dynamic event for %s with priority %s", eventClass.getName(), priority);
112-
Bukkit.getPluginManager().registerEvent(eventClass, listener, priority, executor, plugin, ignoreCancelled);
102+
ReflectHelper.tryInvokeMethod(listener, method)
103+
.onFailure(e -> Logging.warning("Failed to invoke event method %s in %s: $s",
104+
method.getName(), listener.getClass().getName(), e.getMessage()))
105+
.filter(EventRunnable.class::isInstance)
106+
.onFailure(e -> Logging.warning("Event method %s in %s did not return an EventRunnable instance!",
107+
method.getName(), listener.getClass().getName()))
108+
.map(EventRunnable.class::cast)
109+
.map(eventRunnable -> new EventRunnableExecutor<>(eventClass, eventRunnable))
110+
.peek(executor -> {
111+
EventPriority priority = getDynamicEventPriority(method);
112+
boolean ignoreCancelled = isIgnoreIfCancelled(method);
113+
Bukkit.getPluginManager().registerEvent(eventClass, listener, priority, executor, plugin, ignoreCancelled);
114+
Logging.finest("Registered dynamic event: %s, priority: %s, ignore if cancelled: %s",
115+
eventClass.getName(), priority, ignoreCancelled);
116+
});
113117
}
114118

115119
private Class<? extends Event> getEventClass(Method method) {
@@ -129,13 +133,12 @@ private Class<? extends Event> getSkipIfEventExist(Method method) {
129133
}
130134

131135
private Class<? extends Event> getEventClassFromString(String className) {
132-
Class<?> annotatedClass = ReflectHelper.getClass(className);
133-
if (annotatedClass == null || !Event.class.isAssignableFrom(annotatedClass)) {
134-
// Usually means the server software used did not implement the event
135-
Logging.fine("Event class does not exist: %s", className);
136-
return null;
137-
}
138-
return annotatedClass.asSubclass(Event.class);
136+
return ReflectHelper.tryGetClass(className)
137+
.onFailure(throwable -> Logging.fine("Failed to find event class: %s", className))
138+
.filter(Event.class::isAssignableFrom)
139+
.onFailure(throwable -> Logging.warning("Class is not an Event: %s", className))
140+
.map(clazz -> (Class<? extends Event>) clazz.asSubclass(Event.class))
141+
.getOrNull();
139142
}
140143

141144
private EventPriority getDynamicEventPriority(Method method) {

src/main/java/org/mvplugins/multiverse/core/utils/ReflectHelper.java

Lines changed: 200 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import java.lang.reflect.Field;
44
import java.lang.reflect.Method;
55

6+
import io.vavr.control.Try;
7+
import org.jetbrains.annotations.ApiStatus;
68
import org.jetbrains.annotations.NotNull;
79
import org.jetbrains.annotations.Nullable;
810

@@ -15,15 +17,14 @@ public final class ReflectHelper {
1517
* Try to get the {@link Class} based on its classpath.
1618
*
1719
* @param classPath The target classpath.
18-
* @return A {@link Class} if found, else null.
20+
* @return A {@link Try} containing the {@link Class} if found, else a failure.
21+
*
22+
* @since 5.7
1923
*/
20-
@Nullable
21-
public static Class<?> getClass(String classPath) {
22-
try {
23-
return Class.forName(classPath);
24-
} catch (ClassNotFoundException e) {
25-
return null;
26-
}
24+
@ApiStatus.AvailableSince("5.7")
25+
@NotNull
26+
public static Try<Class<?>> tryGetClass(@NotNull String classPath) {
27+
return Try.of(() -> Class.forName(classPath));
2728
}
2829

2930
/**
@@ -33,7 +34,168 @@ public static Class<?> getClass(String classPath) {
3334
* @return True if class path is a valid class, else false.
3435
*/
3536
public static boolean hasClass(String classPath) {
36-
return getClass(classPath) != null;
37+
return tryGetClass(classPath).isSuccess();
38+
}
39+
40+
/**
41+
* Try to get a {@link Method} from a given class.
42+
*
43+
* @param clazz The class to search the method on.
44+
* @param methodName Name of the method to get.
45+
* @param parameterTypes Parameters present for that method.
46+
* @param <C> The class type.
47+
* @return A {@link Try} containing the {@link Method} if found, else a failure.
48+
*
49+
* @since 5.7
50+
*/
51+
@ApiStatus.AvailableSince("5.7")
52+
@NotNull
53+
public static <C> Try<Method> tryGetMethod(@NotNull Class<C> clazz, @NotNull String methodName, Class<?>... parameterTypes) {
54+
return Try.of(() -> clazz.getMethod(methodName, parameterTypes))
55+
.orElse(Try.of(() -> {
56+
Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
57+
method.setAccessible(true);
58+
return method;
59+
}));
60+
}
61+
62+
/**
63+
* Check if a {@link Method} exists on a given class.
64+
*
65+
* @param clazz The class to search the method on.
66+
* @param methodName Name of the method to check.
67+
* @param parameterTypes Parameters present for that method.
68+
* @return True if method exists, else false.
69+
*
70+
* @since 5.7
71+
*/
72+
@ApiStatus.AvailableSince("5.7")
73+
public static boolean hasMethod(@NotNull Class<?> clazz, @NotNull String methodName, Class<?>... parameterTypes) {
74+
return tryGetMethod(clazz, methodName, parameterTypes).isSuccess();
75+
}
76+
77+
/**
78+
* Try to invoke a {@link Method} on a class instance.
79+
*
80+
* @param classInstance Instance of the class responsible for the method.
81+
* @param method The method to invoke.
82+
* @param parameters Parameters needed when invoking the method.
83+
* @param <C> The class type.
84+
* @param <R> The return type of the method.
85+
* @return A {@link Try} containing the return value of the method if found, else a failure.
86+
*
87+
* @since 5.7
88+
*/
89+
@ApiStatus.AvailableSince("5.7")
90+
@NotNull
91+
@SuppressWarnings("unchecked")
92+
public static <C, R> Try<R> tryInvokeMethod(@NotNull C classInstance, @NotNull Method method, Object...parameters) {
93+
return Try.of(() -> (R) method.invoke(classInstance, parameters));
94+
}
95+
96+
/**
97+
* Try to invoke a static {@link Method}.
98+
*
99+
* @param method The static method to invoke.
100+
* @param parameters Parameters needed when invoking the method.
101+
* @param <R> The return type of the method.
102+
* @return A {@link Try} containing the return value of the method if found, else a failure.
103+
*
104+
* @since 5.7
105+
*/
106+
@ApiStatus.AvailableSince("5.7")
107+
@NotNull
108+
@SuppressWarnings("unchecked")
109+
public static <R> Try<R> tryInvokeStaticMethod(@NotNull Method method, Object...parameters) {
110+
return Try.of(() -> (R) method.invoke(null, parameters));
111+
}
112+
113+
/**
114+
* Try to get a {@link Field} from a given class.
115+
*
116+
* @param clazz The class to search the field on.
117+
* @param fieldName Name of the field to get.
118+
* @param <C> The class type.
119+
* @return A {@link Try} containing the {@link Field} if found, else a failure.
120+
*
121+
* @since 5.7
122+
*/
123+
@ApiStatus.AvailableSince("5.7")
124+
@NotNull
125+
public static <C> Try<Field> tryGetField(@NotNull Class<C> clazz, @NotNull String fieldName) {
126+
return Try.of(() -> clazz.getField(fieldName))
127+
.orElse(Try.of(() -> {
128+
Field field = clazz.getDeclaredField(fieldName);
129+
field.setAccessible(true);
130+
return field;
131+
}));
132+
}
133+
134+
/**
135+
* Check if a {@link Field} exists on a given class.
136+
*
137+
* @param clazz The class to search the field on.
138+
* @param fieldName Name of the field to check.
139+
* @return True if field exists, else false.
140+
*
141+
* @since 5.7
142+
*/
143+
@ApiStatus.AvailableSince("5.7")
144+
public static boolean hasField(@NotNull Class<?> clazz, @NotNull String fieldName) {
145+
return tryGetField(clazz, fieldName).isSuccess();
146+
}
147+
148+
/**
149+
* Try to get the value of a {@link Field} from an instance of the class responsible.
150+
*
151+
* @param classInstance Instance of the class to get the field value from.
152+
* @param field The field to get the value from.
153+
* @param fieldType Type of the field.
154+
* @param <C> The class type.
155+
* @param <V> The field value type.
156+
* @return A {@link Try} containing the field value if found, else a failure.
157+
*
158+
* @since 5.7
159+
*/
160+
@ApiStatus.AvailableSince("5.7")
161+
@NotNull
162+
public static <C, V> Try<V> tryGetFieldValue(@NotNull C classInstance, @NotNull Field field, @NotNull Class<V> fieldType) {
163+
return Try.of(() -> fieldType.cast(field.get(classInstance)));
164+
}
165+
166+
/**
167+
* Try to get the value of a static {@link Field}.
168+
*
169+
* @param field The static field to get the value from.
170+
* @param fieldType Type of the field.
171+
* @param <V> The field value type.
172+
* @return A {@link Try} containing the field value if found, else a failure.
173+
*
174+
* @since 5.7
175+
*/
176+
@ApiStatus.AvailableSince("5.7")
177+
@NotNull
178+
public static <V> Try<V> tryGetStaticFieldValue(@NotNull Field field, @NotNull Class<V> fieldType) {
179+
return Try.of(() -> fieldType.cast(field.get(null)));
180+
}
181+
182+
/**
183+
* Try to get the {@link Class} based on its classpath.
184+
*
185+
* @param classPath The target classpath.
186+
* @return A {@link Class} if found, else null.
187+
*
188+
* @deprecated Use {@link #tryGetClass(String)} instead, which returns a {@link Try} that can be used to handle
189+
* the failure case more explicitly.
190+
*/
191+
@Deprecated(forRemoval = true, since = "5.7")
192+
@Nullable
193+
public static Class<?> getClass(String classPath) {
194+
try {
195+
return Class.forName(classPath);
196+
} catch (ClassNotFoundException e) {
197+
return null;
198+
}
37199
}
38200

39201
/**
@@ -44,7 +206,11 @@ public static boolean hasClass(String classPath) {
44206
* @param parameterTypes Parameters present for that method.
45207
* @param <C> The class type.
46208
* @return A {@link Method} if found, else null.
209+
*
210+
* @deprecated Use {@link #tryGetMethod(Class, String, Class[])} instead, which returns a {@link Try} that can be
211+
* used to handle the failure case more explicitly.
47212
*/
213+
@Deprecated(forRemoval = true, since = "5.7")
48214
@Nullable
49215
public static <C> Method getMethod(Class<C> clazz, String methodName, Class<?>... parameterTypes) {
50216
try {
@@ -64,7 +230,11 @@ public static <C> Method getMethod(Class<C> clazz, String methodName, Class<?>..
64230
* @param parameterTypes Parameters present for that method.
65231
* @param <C> The class type.
66232
* @return A {@link Method} if found, else null.
233+
*
234+
* @deprecated Use {@link #tryGetMethod(Class, String, Class[])} instead, which returns a {@link Try} that can be
235+
* used to handle the failure case more explicitly.
67236
*/
237+
@Deprecated(forRemoval = true, since = "5.7")
68238
@Nullable
69239
public static <C> Method getMethod(C classInstance, String methodName, Class<?>... parameterTypes) {
70240
return getMethod(classInstance.getClass(), methodName, parameterTypes);
@@ -79,8 +249,13 @@ public static <C> Method getMethod(C classInstance, String methodName, Class<?>.
79249
* @param <C> The class type.
80250
* @param <R> The return type.
81251
* @return Return value of the method call if any, else null.
252+
*
253+
* @deprecated Use {@link #tryInvokeMethod(Object, Method, Object...)} instead, which returns a {@link Try} that can
254+
* be used to handle the failure case more explicitly.
82255
*/
256+
@Deprecated(forRemoval = true, since = "5.7")
83257
@Nullable
258+
@SuppressWarnings("unchecked")
84259
public static <C, R> R invokeMethod(C classInstance, Method method, Object...parameters) {
85260
try {
86261
return (R) method.invoke(classInstance, parameters);
@@ -96,7 +271,11 @@ public static <C, R> R invokeMethod(C classInstance, Method method, Object...par
96271
* @param fieldName Name of the field to get.
97272
* @param <C> The class type.
98273
* @return A {@link Field} if found, else null.
274+
*
275+
* @deprecated Use {@link #tryGetField(Class, String)} instead, which returns a {@link Try} that can be used to
276+
* handle the failure case more explicitly.
99277
*/
278+
@Deprecated(forRemoval = true, since = "5.7")
100279
@Nullable
101280
public static <C> Field getField(Class<C> clazz, String fieldName) {
102281
try {
@@ -115,7 +294,11 @@ public static <C> Field getField(Class<C> clazz, String fieldName) {
115294
* @param fieldName Name of the field to get.
116295
* @param <C> The class type.
117296
* @return A {@link Field} if found, else null.
297+
*
298+
* @deprecated Use {@link #tryGetField(Class, String)} instead, which returns a {@link Try} that can be used to
299+
* handle the failure case more explicitly.
118300
*/
301+
@Deprecated(forRemoval = true, since = "5.7")
119302
@Nullable
120303
public static <C> Field getField(C classInstance, String fieldName) {
121304
return getField(classInstance.getClass(), fieldName);
@@ -130,7 +313,11 @@ public static <C> Field getField(C classInstance, String fieldName) {
130313
* @param <C> The class type.
131314
* @param <V> The field value type.
132315
* @return The field value if any, else null.
316+
*
317+
* @deprecated Use {@link #tryGetFieldValue(Object, Field, Class)} instead, which returns a {@link Try} that can be
318+
* used to handle the failure case more explicitly.
133319
*/
320+
@Deprecated(forRemoval = true, since = "5.7")
134321
@Nullable
135322
public static <C, V> V getFieldValue(C classInstance, @Nullable Field field, @NotNull Class<V> fieldType) {
136323
try {
@@ -153,7 +340,11 @@ public static <C, V> V getFieldValue(C classInstance, @Nullable Field field, @No
153340
* @param <C> The class type.
154341
* @param <V> The field value type.
155342
* @return The field value if any, else null.
343+
*
344+
* @deprecated Use {@link #tryGetField(Class, String)} then map to {@link #tryGetFieldValue(Object, Field, Class)} instead,
345+
* which returns a {@link Try} that can be used to handle the failure case more explicitly.
156346
*/
347+
@Deprecated(forRemoval = true, since = "5.7")
157348
@Nullable
158349
public static <C, V> V getFieldValue(C classInstance, @Nullable String fieldName, @NotNull Class<V> fieldType) {
159350
return getFieldValue(classInstance, getField(classInstance, fieldName), fieldType);

0 commit comments

Comments
 (0)