-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Nahara's Toolkit Reflective: Reflection for mad lads!
- Loading branch information
Showing
13 changed files
with
496 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
plugins { | ||
} | ||
|
||
eclipse { | ||
project.name = "Nahara Toolkit - Common - Reflective" | ||
} | ||
|
||
repositories { | ||
mavenCentral() | ||
} | ||
|
||
dependencies { | ||
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0' | ||
} | ||
|
||
tasks.named('test') { | ||
useJUnitPlatform() | ||
} |
21 changes: 21 additions & 0 deletions
21
common/reflective/src/main/java/nahara/common/reflective/Handle.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package nahara.common.reflective; | ||
|
||
import java.util.function.Consumer; | ||
import java.util.function.UnaryOperator; | ||
|
||
/** | ||
* <p>Handles allows you to get/set objects using either field accessing or getter and setter.</p> | ||
* @param <T> The type. | ||
*/ | ||
public interface Handle<T> { | ||
public T get(); | ||
public void set(T obj); | ||
|
||
default void apply(Consumer<Handle<T>> applier) { | ||
applier.accept(this); | ||
} | ||
|
||
default void map(UnaryOperator<T> mapper) { | ||
set(mapper.apply(get())); | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
common/reflective/src/main/java/nahara/common/reflective/HandleFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package nahara.common.reflective; | ||
|
||
/** | ||
* <p>The factory for getting the handle from given object.</p> | ||
* @param <S> The object type. | ||
* @param <T> The type for handle. | ||
*/ | ||
public interface HandleFactory<S, T> { | ||
public Handle<T> of(S obj); | ||
|
||
default Handle<T> ofStatic() { | ||
return of(null); | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
common/reflective/src/main/java/nahara/common/reflective/Method.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package nahara.common.reflective; | ||
|
||
public interface Method<T> { | ||
public T invoke(Object... args); | ||
} |
9 changes: 9 additions & 0 deletions
9
common/reflective/src/main/java/nahara/common/reflective/MethodFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package nahara.common.reflective; | ||
|
||
public interface MethodFactory<S, T> { | ||
public Method<T> of(S obj); | ||
|
||
default Method<T> ofStatic() { | ||
return of(null); | ||
} | ||
} |
105 changes: 105 additions & 0 deletions
105
common/reflective/src/main/java/nahara/common/reflective/Reflective.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
package nahara.common.reflective; | ||
|
||
import java.lang.reflect.AccessibleObject; | ||
|
||
import nahara.common.reflective.impl.ReflectiveImpl; | ||
|
||
/** | ||
* <p><i>"Once upon a time, there was a small pond in a middle of the forest. The pond water is said to be | ||
* precious and shiny, which means it is highly <b>reflective</b>. Nahara saw the pond, got extremely | ||
* curious, then decided to get closer to it.</i></p> | ||
* <p><i>Nahara stands still, staring at her reflection on the watery surface. "What is this all about?", | ||
* "Why is the {@code PI} constant of universe becomes 4.0?", "Am I living in a simulation?", "Am I living | ||
* in an illusion?", "Is this a dream?". Nahara begins questioning her existence as she is about to perform | ||
* an act that no ones could have guessed: It's Java reflectin' time!"</i></p> | ||
* | ||
* <p>This is Nahara's Toolkit for Reflections, a simple tool for your toolbox so that you can perform | ||
* Java Reflection operations without dealing with nasty exceptions (I'm looking at you, | ||
* {@link ReflectiveOperationException}). While the main point of this is to avoid catching exceptions, | ||
* {@link Reflective} contains methods that helps you shorten the time it takes to use reflection.</p> | ||
* <p>First, some methods allows you to feed in multiple possible names for a field or method. This is | ||
* to ensure no magic will happens in the production environment, especially in Fabric modding, where dev | ||
* environment may have remapped names, but in prouction, it becomes "method_abcdef".</p> | ||
* <p>Second, some methods allows you to pick the only method with matching signature. There can be always | ||
* more than 1 constructor or method with same name, but method signatures can't be the same.</p> | ||
* <p>Third, all {@link Handle}s and {@link Method}s will calls {@link AccessibleObject#setAccessible(boolean)} | ||
* everytime you access the fields or methods. You don't have to set its accessibility everytime you need | ||
* to, well, access them.</p> | ||
* | ||
* <p>Now that I've done talkin' about {@link Reflective}, let's talk about "Where did the person named 'Nahara' | ||
* comes from?". It might seems weird that I'm talking to you using Javadocs, but I kinda want to make this | ||
* as a little "easter egg".</p> | ||
* <p>To say "it comes from my dream" wouldn't be entirely correct, but the whole idea is: every person should have | ||
* someone to help with their job. But because you can't see Nahara in person (she isn't real), she decided to mail | ||
* her toolbox to you, with the hope of improving your productivity. That's why "Nahara's Toolkit" was created: to | ||
* help me write Java code faster and more enjoyable. And what is the result of enjoying? Being productive!</p> | ||
* <p>I don't expect this little dumb repository gets some attentions, but if you are using Nahara's toolkit in your | ||
* project, consider making an "issue" in the Issues tab of my GitHub special repository for profile card. Nahara | ||
* would likes to know her toolkit is actually useful!</p> | ||
* | ||
* @see #constructor(Class...) | ||
* @see #field(Class, String...) | ||
* @see #method(Class, Class[], String...) | ||
* @see #getterSetter(Class, String[], String[]) | ||
*/ | ||
public interface Reflective<S> { | ||
/** | ||
* <p>Combine getter and setter into a single handle.</p> | ||
* @param type The output type. | ||
* @param getter All possible names for getter. Nahara will choose the method that is appeared first | ||
* in this array that also present in the class. | ||
* @param setter All possible names for setter. Nahara will choose the method that is appeared first | ||
* in this array that also present in the class. | ||
* @return The handle. | ||
*/ | ||
public <T> HandleFactory<S, T> getterSetter(Class<T> type, String[] getter, String[] setter); | ||
|
||
/** | ||
* <p>Get a field as a handle.</p> | ||
* @param type The output type. | ||
* @param names All possible names for the field. | ||
* @return The handle. | ||
*/ | ||
public <T> HandleFactory<S, T> field(Class<T> type, String... names); | ||
|
||
/** | ||
* <p>Find a method with specified name(s).</p> | ||
* @param returnType The return type of method. | ||
* @param arguments Arguments to check, or {@code null} to ignore checking arguments. | ||
* @param names All possible names for the method. | ||
* @return The method. | ||
*/ | ||
public <T> MethodFactory<S, T> method(Class<T> returnType, Class<?>[] arguments, String... names); | ||
|
||
/** | ||
* <p>Find a constructor with given types for all arguments.</p> | ||
* @param arguments Arguments to check. | ||
* @return The constructor described as a method. | ||
*/ | ||
public Method<S> constructor(Class<?>... arguments); | ||
|
||
/** | ||
* <p>Obtain a {@link Reflective} instance of a class.</p> | ||
* @param clazz The class. | ||
* @return The {@link Reflective} instance. | ||
*/ | ||
public static <S> Reflective<S> of(Class<S> clazz) { | ||
return new ReflectiveImpl<>(clazz); | ||
} | ||
|
||
public static Reflective<?> of(String className) { | ||
try { | ||
return of(Class.forName(className)); | ||
} catch (ClassNotFoundException e) { | ||
throw new RuntimeException(); | ||
} | ||
} | ||
|
||
public static String[] names(String... names) { | ||
return names; | ||
} | ||
|
||
public static Class<?>[] args(Class<?>... args) { | ||
return args; | ||
} | ||
} |
53 changes: 53 additions & 0 deletions
53
common/reflective/src/main/java/nahara/common/reflective/impl/FieldHandleFactoryImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package nahara.common.reflective.impl; | ||
|
||
import java.lang.reflect.Field; | ||
|
||
import nahara.common.reflective.Handle; | ||
import nahara.common.reflective.HandleFactory; | ||
|
||
public class FieldHandleFactoryImpl<S, T> implements HandleFactory<S, T> { | ||
private Field field; | ||
|
||
public FieldHandleFactoryImpl(Field field) { | ||
this.field = field; | ||
} | ||
|
||
@Override | ||
public Handle<T> of(S obj) { | ||
return new FieldHandleImpl<>(this, obj); | ||
} | ||
|
||
public static class FieldHandleImpl<S, T> implements Handle<T> { | ||
private FieldHandleFactoryImpl<S, T> factory; | ||
private S obj; | ||
|
||
public FieldHandleImpl(FieldHandleFactoryImpl<S, T> factory, S obj) { | ||
this.factory = factory; | ||
this.obj = obj; | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
@Override | ||
public T get() { | ||
try { | ||
factory.field.setAccessible(true); | ||
T t = (T) factory.field.get(obj); | ||
factory.field.setAccessible(false); | ||
return t; | ||
} catch (IllegalArgumentException | IllegalAccessException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
@Override | ||
public void set(T obj) { | ||
try { | ||
factory.field.setAccessible(true); | ||
factory.field.set(this.obj, obj); | ||
factory.field.setAccessible(false); | ||
} catch (IllegalArgumentException | IllegalAccessException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} | ||
} |
52 changes: 52 additions & 0 deletions
52
common/reflective/src/main/java/nahara/common/reflective/impl/MethodFactoryImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package nahara.common.reflective.impl; | ||
|
||
import java.lang.reflect.Constructor; | ||
import java.lang.reflect.InvocationTargetException; | ||
|
||
import nahara.common.reflective.Method; | ||
import nahara.common.reflective.MethodFactory; | ||
|
||
public class MethodFactoryImpl<S, T> implements MethodFactory<S, T> { | ||
private java.lang.reflect.Method method; | ||
private Constructor<T> constructor; | ||
|
||
public MethodFactoryImpl(java.lang.reflect.Method method, Constructor<T> constructor) { | ||
this.method = method; | ||
this.constructor = constructor; | ||
} | ||
|
||
@Override | ||
public Method<T> of(S obj) { | ||
return new MethodImpl<>(this, obj); | ||
} | ||
|
||
public static class MethodImpl<S, T> implements Method<T> { | ||
private MethodFactoryImpl<S, T> factory; | ||
private S obj; | ||
|
||
public MethodImpl(MethodFactoryImpl<S, T> factory, S obj) { | ||
this.factory = factory; | ||
this.obj = obj; | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
@Override | ||
public T invoke(Object... args) { | ||
try { | ||
if (factory.constructor != null) { | ||
factory.constructor.setAccessible(true); | ||
T t = (T) factory.constructor.newInstance(args); | ||
factory.constructor.setAccessible(false); | ||
return t; | ||
} else { | ||
factory.method.setAccessible(true); | ||
T t = (T) factory.method.invoke(obj, args); | ||
factory.method.setAccessible(false); | ||
return t; | ||
} | ||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | InstantiationException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} | ||
} |
58 changes: 58 additions & 0 deletions
58
common/reflective/src/main/java/nahara/common/reflective/impl/MethodHandleFactoryImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package nahara.common.reflective.impl; | ||
|
||
import java.lang.reflect.InvocationTargetException; | ||
import java.lang.reflect.Method; | ||
|
||
import nahara.common.reflective.Handle; | ||
import nahara.common.reflective.HandleFactory; | ||
|
||
public class MethodHandleFactoryImpl<S, T> implements HandleFactory<S, T> { | ||
private Method getter; | ||
private Method setter; | ||
|
||
public MethodHandleFactoryImpl(Method getter, Method setter) { | ||
this.getter = getter; | ||
this.setter = setter; | ||
} | ||
|
||
@Override | ||
public Handle<T> of(S obj) { | ||
return new MethodHandleImpl<>(this, obj); | ||
} | ||
|
||
public static class MethodHandleImpl<S, T> implements Handle<T> { | ||
private MethodHandleFactoryImpl<S, T> factory; | ||
private S obj; | ||
|
||
public MethodHandleImpl(MethodHandleFactoryImpl<S, T> factory, S obj) { | ||
this.factory = factory; | ||
this.obj = obj; | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
@Override | ||
public T get() { | ||
if (factory.getter == null) throw new RuntimeException("Handle does not link to a getter"); | ||
try { | ||
factory.getter.setAccessible(true); | ||
T t = (T) factory.getter.invoke(obj); | ||
factory.getter.setAccessible(false); | ||
return t; | ||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
@Override | ||
public void set(T obj) { | ||
if (factory.getter == null) throw new RuntimeException("Handle does not link to a setter"); | ||
try { | ||
factory.getter.setAccessible(true); | ||
factory.setter.invoke(this.obj, obj); | ||
factory.getter.setAccessible(false); | ||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.