Using reflection to customize private field of superclass - java

I am running into a problem trying to modify the behavior of my superclass' private fields from the sub-class (The superclass wasn't designed to be extended and I can't change that).
Basically, what I have is :
public class OrthoCanvas {
private OrthoView xy, xz, zy;
public class OrthoView { ... }
}
And I want to do something like that :
public class CustomOrthoCanvas extends OrthoCanvas {
public CustomOrthoCanvas {
// Sets superclass xy, xz, zy to instances of CustomOrthoView
// This seem to work fine (I'm using reflection to change the fields)
}
public class CustomOrthoView extends OrthoView { ... }
}
As I said, the reflection seems to work (I'm building CustomOrthoView). But, for the moment, I didn't override any method, and my constructor is just super(whatever), and a Sysout to check what I'm doing. Yet, the original behavior of OrthoView just disappeared, and nothing is working.
Did I make a mistake in my code or is this something more related to my specific case ?
Thanks a lot
Edit : I've just thought that it would be easier if I showed you how I used reflection, so there it is :
Field fieldXY = null;
Field fieldXZ = null;
Field fieldZY = null;
try {
System.out.println(this.getClass().getSuperclass().getName());
fieldXY = Class.forName(this.getClass().getSuperclass().getName()).getDeclaredField("xy");
fieldXZ = Class.forName(this.getClass().getSuperclass().getName()).getDeclaredField("xz");
fieldZY = Class.forName(this.getClass().getSuperclass().getName()).getDeclaredField("zy");
} catch (NoSuchFieldException e) {
System.out.println("-- No such field --");
System.out.println(e.getMessage());
} catch (SecurityException e) {
System.out.println("-- Security failure --");
System.out.println(e.getMessage());
} catch (ClassNotFoundException e) {
System.out.println("-- Class not found --");
System.out.println(e.getMessage());
}
fieldXY.setAccessible(true);
fieldXZ.setAccessible(true);
fieldZY.setAccessible(true);
try {
fieldXY.set(this, new CustomOrthoView(this, DimensionId.Z));
fieldXZ.set(this, new CustomOrthoView(this, DimensionId.Y));
fieldZY.set(this, new CustomOrthoView(this, DimensionId.X));
} catch (IllegalArgumentException e) {
System.out.println("-- Illegal argument --");
System.out.println(e.getMessage());
} catch (IllegalAccessException e) {
System.out.println("-- Illegal access --");
System.out.println(e.getMessage());
}
Edit2 : This is a simplified behavior of the superclass :
public class Orthoviewer {
// This class creates a canvas to display an image
public class OrthoCanvas {
// This class represents how an image is displayed
// It implements listeners (to navigate through the image for
// example), and ways to refresh the image
public class OrthoView extends JPanel {
// This class displays one part of the image (one plane)
// To represent a 3D point by the intersection of the three corresponding planes
// It has an attribute which indicates its dimension
// (X is for ZY plane, Y for XZ plane etc)
// It overrides the method paintComponent to draw itself
public class ImageCache implements Runnable {
// This handles the image to display on the corresponding plane
}
}
}
}

Related

How to reuse logic for class instantiation and get around IllegalAccessException?

Goal: create a util class that will contain some reflection code, in particular code that creates a new class instance. That code is not very simple, so I want to keep it in a util function so that I can reuse it multiple times.
Approach/Idea: create ClassUtil that will have a function that returns a lambda that creates a new class instance. I will execute that lambda in MyClassFactory, a class that I know can create a new instance of MyClassOne because it will be in the same package and the default constructor is package-private access modifier, so ClassUtil cannot make an instance of that class, but since I am executing the lambda in a class that can all should be good.
Error:
java.lang.IllegalAccessException: class com.util.ClassUtil cannot access a member of class com.somepackage.MyClassOne with modifiers ""
Question: How to make Java runtime think that it is MyClassFactory the one that is trying the instantiation?
UML:
Code:
package com.somepackage
import com.util.ClassUtil;
public class MyClassFactory {
public MyClass createMyClass() {
String implClassFQN = <get impl class FQN from somewhere>;
return (MyClass ) ClassUtil.createClassInstance().apply(implClassFQN);
}
}
package com.util;
import java.lang.reflect.InvocationTargetException;
import java.util.function.Function;
public class ClassUtil {
/**
* #return a new instance of the class given the class fully qualified name
*/
public static Function<String, Object> createClassInstance() {
return (classFQN) -> {
try {
return Class.forName(classFQN).getDeclaredConstructor().newInstance();
} catch (ClassNotFoundException e) {
throw new RuntimeException("Impl class specified in properties file not found", e);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Default constructor not found", e);
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
throw new RuntimeException(e);
}
};
}
}
Workaround/New Approach: would anyone know how I could have taken a different approach to achieve the same goal?
Note: If I put the code from ClassUtil into MyClassFactory it works fine, but I want it reusable.
To answer your question literally, there is a class capable of encapsulating an access context that can be passed to another method to perform actions with it without the need for access override, assuming that the caller has enough trust in the invoked method.
Unlike access override, this will work even in restricted environments, e.g. with an installed security manager or when the access would cross module boundaries. Also, the class lookup is performed as-if happening in the caller’s code which can be relevant when the two packages belong to different class loaders.
package com.somepackage;
import com.util.ClassUtil;
import java.lang.invoke.MethodHandles;
public class MyClassFactory {
public static void main(String[] args) {
MyClass obj = new MyClassFactory().createMyClass();
System.out.println("created "+obj);
}
public MyClass createMyClass() {
String implClassFQN = MyClassFactory.class.getName()+"$MyClass";
return (MyClass)ClassUtil.createClassInstance(MethodHandles.lookup())
.apply(implClassFQN);
}
private static class MyClass { // normally inaccessible by com.util.ClassUtil
}
}
package com.util;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.function.Function;
public class ClassUtil {
/**
* #return a new instance of the class given the class fully qualified name
*/
public static Function<String, Object>
createClassInstance(MethodHandles.Lookup context) {
return name -> {
try {
return context.findConstructor(context.findClass(name),
MethodType.methodType(void.class)).invoke();
} catch(ClassNotFoundException e) {
throw new RuntimeException(
"Impl class specified in properties file not found", e);
} catch(NoSuchMethodException e) {
throw new RuntimeException("Default constructor not found", e);
} catch(RuntimeException | Error e) {
throw e;
} catch(Throwable e) {
throw new RuntimeException(e);
}
};
}
}
created com.somepackage.MyClassFactory$MyClass#4783da3f
Try setting the constructor accessible via:
AccessibleObject#setAccessible(true)
public static Function<String, Object> createClassInstance() {
return (classFQN) -> {
try {
Constructor<?> constructor = Class.forName(classFQN).getDeclaredConstructor();
constructor.setAccessible(true);
return constructor.newInstance();
} catch (ClassNotFoundException e) {
throw new RuntimeException("Impl class specified in properties file not found", e);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Default constructor not found", e);
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
throw new RuntimeException(e);
}
};
}

Typecasting with a class that is protected

I am trying to override some class of vertx web project, since I have to change some of the features. So the tricky part comes here.
#Override
public void reroute(HttpMethod method, String path) {
int split = path.indexOf('?');
if (split == -1) {
split = path.indexOf('#');
}
if (split != -1) {
log.warn("Non path segment is not considered: " + path.substring(split));
// reroute is path based so we trim out the non url path parts
path = path.substring(0, split);
}
/*((HttpServerRequestWrapper) request).setMethod(method);
((HttpServerRequestWrapper) request).setPath(path);*/
((HttpServerRequestWrapper) request).setMethod(method);
((HttpServerRequestWrapper) request).setPath(path);
request.params().clear();
// we need to reset the normalized path
normalisedPath = null;
// we also need to reset any previous status
statusCode = -1;
// we need to reset any response headers
response().headers().clear();
// special header case cookies are parsed and cached
if (cookies != null) {
cookies.clear();
}
// reset the end handlers
if (headersEndHandlers != null) {
headersEndHandlers.clear();
}
if (bodyEndHandlers != null) {
bodyEndHandlers.clear();
}
failure = null;
restart();
}
This code throws me a compilation error saying:
'HttpServerRequestWrapper cannot be accessed from outside package'
I know for a fact that we can use reflection to create objects of a class that cannot be accessed. Can reflection be used in this case? How can I fix such an issue.
Any help will be much appreciated.
In java 8 and/or without modules it is possible to just place class like that in same package as original one to get access to all package-default classes.
Otherwise you need to use reflections like in other response, but I would add that it is good idea to cache that Class and Method instance, as using Class.forName and clazz.getDeclaredMethod each time will slowdown code.
What about getting the Class object and then calling the methods on your specific (uncasted) object?
I assume request is a class attribute of type HttpServerRequestWrapper. Then, this is what I suggest:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
...
private final Method setMethod;
private final Method setPath;
public MyConstructor() {
Method tmp1 = null, tmp2 = null;
try {
final Class<?> clazz = Class.forName("io.vertx.ext.web.impl.HttpServerRequestWrapper");
tmp1 = clazz.getMethod("setMethod", HttpMethod.class);
tmp1.setAccessible(true);
tmp2 = clazz.getMethod("setPath", String.class);
tmp2.setAccessible(true);
} catch (ClassNotFoundException e) {
// do something
} catch (NoSuchMethodException e) {
// do something
} catch (SecurityException e) {
// do something
}
this.setMethod = tmp1;
this.setPath = tmp2;
}
...
#Override
public void reroute(HttpMethod method, String path) {
...
try {
this.setMethod.invoke(request, method);
this.setPath.invoke(request, path);
} catch (IllegalAccessException e) {
// do something
} catch (IllegalArgumentException e) {
// do something
} catch (InvocationTargetException e) {
// do something
}
...
}
EDIT: I updated this answer based on #GotoFinal's suggestion.
It looks like HttpServerRequestWrapper implements HttpServerRequest. So, you can change "HttpServerRequestWrapper" to "HttpServerRequest" in your code. But remember that by doing so, you'll only be able to call methods specified in the interface.
You can see those methods in https://vertx.io/docs/apidocs/io/vertx/rxjava/core/http/HttpServerRequest.html.

Flickering Annotations in WorldWind Java

I'm trying to implement my own clutter filter in NASA Worldwind for Java and its causing a weird problem -- the clutter filter isn't doing much yet, but I will use it to move things around when I get passed the "flickering" issue. Whenever the mouse is moved the GlobeAnnotation renderables are flickering. When I have the clutter filter set to null, the flickering does not seem to occur.
Here is a GIF that shows what I mean: https://media.giphy.com/media/xT9IgFiZwYZ3VJHQU8/giphy.gif
I've cloned the NASA worldwind code from here: https://github.com/NASAWorldWind/WorldWindJava. I've made a couple of changes to make things work for my eventual filter. One note is that I want the GlobeAnnotations to appear as Always On Top of everything else.
How can I make the GlobeAnnotations not fight with each other and flicker, but still appear on top of everything else -- while having the Clutter Filter turned on?
Note that the following code is just an example I put together to show the issue that I'm seeing in my "real" application. I want the GlobeAnnotations to always be on top of everything else -- but not flickering and fighting with each other.
Here is my test driver:
package gov.nasa.worldwindx.examples;
import java.awt.Color;
import gov.nasa.worldwind.geom.LatLon;
import gov.nasa.worldwind.geom.Position;
import gov.nasa.worldwind.layers.AnnotationLayer;
import gov.nasa.worldwind.layers.RenderableLayer;
import gov.nasa.worldwind.render.GlobeAnnotation;
import gov.nasa.worldwind.render.Material;
import gov.nasa.worldwind.render.airspaces.CappedCylinder;
public class FlashyAnnotations extends ApplicationTemplate {
#SuppressWarnings("unchecked")
private static class AppFrame extends ApplicationTemplate.AppFrame {
private AnnotationLayer layer;
public AppFrame() {
this.getWwd().getSceneController().setClutterFilter(new SimpleClutterFilter());
CappedCylinder cappedCyl = new CappedCylinder(LatLon.fromDegrees(27, -100), 3000000);
cappedCyl.getAttributes().setDrawInterior(true);
cappedCyl.getAttributes().setInteriorMaterial(Material.GREEN);
cappedCyl.getAttributes().setInteriorOpacity(.75f);
cappedCyl.setAltitudes(10, 100000);
RenderableLayer renderLayer = new RenderableLayer();
renderLayer.addRenderable(cappedCyl);
insertBeforeCompass(this.getWwd(), renderLayer);
// Create example annotations
this.setupAnnotations();
}
private void setupAnnotations() {
// Create an AnnotationLayer with lots of annotations
this.layer = new AnnotationLayer();
GlobeAnnotation ga = new GlobeAnnotation("Annotation", Position.fromDegrees(20, -100.9, 1000));
ga.getAttributes().setTextColor(Color.white);
ga.getAttributes().setBackgroundColor(Color.BLACK);
ga.getAttributes().setOpacity(.75f);
ga.setAlwaysOnTop(true);
layer.addAnnotation(ga);
ga = new GlobeAnnotation("Annotation", Position.fromDegrees(25, -100.9, 1000));
ga.getAttributes().setTextColor(Color.white);
ga.getAttributes().setBackgroundColor(Color.BLACK);
ga.getAttributes().setOpacity(.75f);
ga.setAlwaysOnTop(true);
layer.addAnnotation(ga);
// Add layer to the layer list and update the layer panel
insertBeforeCompass(this.getWwd(), layer);
}
}
public static void main(String[] args) {
ApplicationTemplate.start("WorldWind Annotations", AppFrame.class);
}
}
Here is my (essentially no-op) Clutter Filter:
package gov.nasa.worldwindx.examples;
import java.util.List;
import gov.nasa.worldwind.render.Declutterable;
import gov.nasa.worldwind.render.DrawContext;
import gov.nasa.worldwind.util.ClutterFilter;
public class SimpleClutterFilter implements ClutterFilter{
#Override
public void apply(DrawContext dc, List<Declutterable> shapes) {
for(Declutterable shape: shapes) {
dc.addOrderedRenderable(shape);
}
}
}
And I also had to update the gov.nasa.worldwind.render.BasicAnnotationRenderer to have the OrderedAnnotations it creates implement Declutterable. (The only change to this inner class was adding isEnableDecluttering and getBounds):
public class OrderedAnnotation implements OrderedRenderable, Declutterable
{
protected Annotation annotation;
protected double eyeDistance;
protected Layer layer;
public OrderedAnnotation(Annotation annotation, double eyeDistance)
{
this.annotation = annotation;
this.eyeDistance = eyeDistance;
}
public OrderedAnnotation(Annotation annotation, Layer layer, double eyeDistance)
{
this.annotation = annotation;
this.eyeDistance = eyeDistance;
this.layer = layer;
}
public double getDistanceFromEye()
{
return this.eyeDistance;
}
public void render(DrawContext dc)
{
OGLStackHandler stackHandler = new OGLStackHandler();
BasicAnnotationRenderer.this.beginDrawAnnotations(dc, stackHandler);
try
{
this.doRender(dc, this);
// Draw as many as we can in a batch to save ogl state switching.
while (dc.peekOrderedRenderables() instanceof OrderedAnnotation)
{
OrderedAnnotation oa = (OrderedAnnotation) dc.pollOrderedRenderables();
this.doRender(dc, oa);
}
}
catch (WWRuntimeException e)
{
Logging.logger().log(Level.SEVERE, "generic.ExceptionWhileRenderingAnnotation", e);
}
catch (Exception e)
{
Logging.logger().log(Level.SEVERE, "generic.ExceptionWhileRenderingAnnotation", e);
}
finally
{
BasicAnnotationRenderer.this.endDrawAnnotations(dc, stackHandler);
}
}
public void pick(DrawContext dc, java.awt.Point pickPoint)
{
OGLStackHandler stackHandler = new OGLStackHandler();
BasicAnnotationRenderer.this.pickSupport.clearPickList();
BasicAnnotationRenderer.this.beginDrawAnnotations(dc, stackHandler);
try
{
this.annotation.setPickSupport(BasicAnnotationRenderer.this.pickSupport);
this.doRender(dc, this);
// Draw as many as we can in a batch to save ogl state switching.
while (dc.peekOrderedRenderables() instanceof OrderedAnnotation)
{
OrderedAnnotation oa = (OrderedAnnotation) dc.pollOrderedRenderables();
oa.annotation.setPickSupport(BasicAnnotationRenderer.this.pickSupport);
this.doRender(dc, oa);
}
}
catch (WWRuntimeException e)
{
Logging.logger().log(Level.SEVERE, "generic.ExceptionWhilePickingAnnotation", e);
}
catch (Exception e)
{
Logging.logger().log(Level.SEVERE, "generic.ExceptionWhilePickingAnnotation", e);
}
finally
{
BasicAnnotationRenderer.this.endDrawAnnotations(dc, stackHandler);
BasicAnnotationRenderer.this.pickSupport.resolvePick(dc, pickPoint, this.layer);
BasicAnnotationRenderer.this.pickSupport.clearPickList(); // to ensure entries can be garbage collected
}
}
protected void doRender(DrawContext dc, OrderedAnnotation oa)
{
// Swap the draw context's current layer with that of the ordered annotation
Layer previousCurrentLayer = dc.getCurrentLayer();
try
{
dc.setCurrentLayer(oa.layer);
oa.annotation.renderNow(dc);
}
finally
{
dc.setCurrentLayer(previousCurrentLayer); // restore the original layer
}
}
#Override
public boolean isEnableDecluttering() {
return (annotation instanceof GlobeAnnotation);
}
#Override
public Rectangle2D getBounds(DrawContext dc) {
if(annotation instanceof GlobeAnnotation) {
return ((GlobeAnnotation) annotation).computeBounds(dc);
}
return null;
}
}
First of all;
Draw order of PointPlacemarks
https://forum.worldwindcentral.com/forum/world-wind-java-forums/development-help/13263-layer-priority-order
In setupAnnotations method, you set alwaysOnTop as true for both GlobeAnnotation objects. This might be the reason.
private void setupAnnotations() {
// Create an AnnotationLayer with lots of annotations
this.layer = new AnnotationLayer();
GlobeAnnotation ga = new GlobeAnnotation("Annotation", Position.fromDegrees(20, -100.9, 1000));
ga.getAttributes().setTextColor(Color.white);
ga.getAttributes().setBackgroundColor(Color.BLACK);
ga.getAttributes().setOpacity(.75f);
**ga.setAlwaysOnTop(true);**
layer.addAnnotation(ga);
ga = new GlobeAnnotation("Annotation", Position.fromDegrees(25, -100.9, 1000));
ga.getAttributes().setTextColor(Color.white);
ga.getAttributes().setBackgroundColor(Color.BLACK);
ga.getAttributes().setOpacity(.75f);
**ga.setAlwaysOnTop(true);**
layer.addAnnotation(ga);
// Add layer to the layer list and update the layer panel
insertBeforeCompass(this.getWwd(), layer);
}
Instead of that, putting annotations that you want to be always on top into separate layer and remaining ones into another layer might be solution by using the links above.

Android Informing application through different process non-activity class

I'm currently developing an app which requires some adb commands
First i have a seperate module in application Module1, and there is com.mordred.xyz.Main class in there
public class Main {
public static void main(String[] args) {
System.out.println("mordred main class is instantiated");
// rest of code
}
}
I'm instantiating that Main class (com.mordred.xyz.Main) via adb (adb shell dalvikvm -cp com.mordred.MyApplication com.mordred.xyz.Main)
and i have an MainActivity in my application (com.mordred.MyApplication.MainActivity)
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// MainActivity rest of code
}
}
so the question is How can i inform the application.MainActivity that the "Main class is instantiated through adb" even if application is closed?
I tried to Context.sendIntent but it requires Context object, and also i cannot pass Context object from MainActivity nor other object (because i want Main class can be instantiated even if application is not running)
i dont want to use hidden apis (IActivityManager has broadcastIntent method but that class is hidden system api)
What can i do for it?
UPDATE:
eureka, probably i found a way,
i will set a hardcoded path in Main class and create a dummy file in there, and then will give a readable and writable permissions to everyone via File.setReadable() and then when Main class is instantiated, it will write something like "Main: 1" into that file, and then i will add a check into application's MainActivity class that will check that file if its "Main: 1" or not, ("Main: 1" means that the Main class is instantiated otherwise "Main: 0" means vice versa)
Ok i found a cleaner and nicer way to do it, yeah its reflection hack :P
here is the function that i made to send Intents globally which can be called from everywhere
i tested it and it works good, and also note that: it is for >Marshmallow, (because of main broadcastIntent() method's parameter types)
public void sendGlobalIntent(Intent intent,String[] requiredPermissions) {
try {
Class<?> cActivityManagerNative = Class
.forName("android.app.ActivityManagerNative");
Method mGetDefault = cActivityManagerNative.getMethod("getDefault");
mGetDefault.setAccessible(true);
Object oActivityManagerNative = mGetDefault.invoke(null);
Method[] methods = cActivityManagerNative.getMethods();
Method m = null;
for (Method method : methods) {
if (m.getName().equals("broadcastIntent")) {
m = method;
}
}
if (m == null) {
return;
}
m.setAccessible(true);
m.invoke(oActivityManagerNative, null, intent, null, null, 0, null,
null, requiredPermissions, -1, null, true, false, android.os.Process.myUid() / 100000);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}

Event handling with Java Reflection

Ok so, this is quite confusing to explain. I will try my best.
Inspired by the Bukkit Event System where you can make voids an event handler by just using #EventHandler.
Example:
#EventHandler
public void aRandomName(PlayerMoveEvent ev) {
}
As you can see, the name of the method doesn't matter. Which event is passed on is determined by the event argument type.
All events extend the Event class.
I have made up some code which I think would work, except for one thing.
public List<Object> eventContainers;
public void fireEvent(Event e) {
Method[] methods;
for (Object o : eventContainers) {
Object[] classes = o.getClass().getClasses();
for (Object clss : classes) {
methods = clss.getClass().getMethods();
for (Method m : methods) {
if (m.getAnnotation(EventHandler.class) != null) {
try {
Class[] requiredTypes = m.getParameterTypes();
for(Class cl : requiredTypes) {
if(e.equals(cl)) {
m.invoke(clss, e);
}
}
} catch (IllegalAccessException ex) {
} catch (IllegalArgumentException ex) {
} catch (InvocationTargetException ex) {
}
}
}
}
}
}
What my code does:
Loops through all the classes in eventContainers, looks for methods that have the #EventHandler annotation and sends the specified event to that method. However, I want to see what kind of event the given event in fireEvent(Event e) is, and then look at the methods who require an event parameter of that kind. How would I do that? I figure that
Class[] requiredTypes = m.getParameterTypes();
for(Class cl : requiredTypes) {
if(e.equals(cl)) {
m.invoke(clss, e);
}
}
will not work.
Ultimately I want to be able to pass on events to plugins. Like this:
EventManager.fireEvent(new PlayerMoveEvent(player));
Which will be sent to all plugins and the plugins that have
#EventHandler
public void aVoid(PlayerMoveEvent e) {
//stuff
}
If you have any questions, I will try to explain it better. Thanks in advance for your help!
Your code uses e.equals(cl), which is comparing an instance of Event with an instance of Class (the class of an instance of Event) - this will never return true. What you want to do instead is:
if(e.getClass().equals(cl)) {
m.invoke(clss, e);
}
Alternatively, if you want methods annotated with #EventHandler to handle all subclasses of the class that their method signature defines (i.e. a method like handle(Event e) would be called with PlayerMoveEvents as well as all other events), then you want:
if(cl.isAssignableFrom(e.getClass())) {
m.invoke(clss, e);
}
See the Class Javadoc here for more information.
Note that I think there are a few other problems in you code. For example, Method.invoke should be called with an instance of the class that contains a method that is annotated with #EventHandler. It is a little unclear from your code, but I believe this should therefore be:
m.invoke(o, e);
Also, by calling o.getClass().getClasses(), you are iterating over the classes defined in the class of o - you probably want to iterate over the methods of the class of o directly, i.e.:
for (Method m : o.getClass().getMethods()) {
if (m.getAnnotation(EventHandler.class) != null) {
Class[] requiredTypes = m.getParameterTypes();
if (requiredTypes.length == 1 && requiredTypes[0].isAssignableFrom(e.getClass()) {
m.invoke(o, e);
}
}
}
You can get the parameter types from a Method using method.getGenericParameterTypes(), so:
m.invoke(clss, m.getGenericParameterTypes()[0].class.cast(e));
Not sure if that's what you want.
Assuming the EventHandler annotated method only has one parameter
Method[] methods = YourClass.class.getDeclaredMethods();
Object yourInstance = null; // get it
Event e = null; // get it
for (Method method : methods) {
EventHandler handler = method.getAnnotation(EventHandler.class);
if (handler != null) {
Class<?>[] parameterTypes = method.getParameterTypes();
// you're going to need different logic if you have more than one parameter
if (parameterTypes.length == 1 && parameterTypes[0].isAssignableFrom(e.getClass())) {
method.invoke(yourInstance, e);
}
}
}
I've not included any exception handling.
Get all the methods of event handler candidate classes and iterate over them. If a method has the #EventHandler annotation, get its parameter type list. If it only has one parameter and that type is assignable from your event type e.getClass(), then invoke it passing in your event.
I have now modified the code to a working event system!!!!!! :D thanks so much andersschuller!
public void fireEvent(Event e) {
Method[] methods;
for (Object o : eventContainers) {
methods = o.getClass().getMethods();
for (Method m : methods) {
if (m.getAnnotation(EventHandler.class) != null) {
try {
if (m.getParameterTypes()[0].isAssignableFrom(e.getClass())) {
m.invoke(o, e);
}
} catch (IllegalAccessException ex) {
} catch (IllegalArgumentException ex) {
} catch (InvocationTargetException ex) {
}
}
}
}
}
I kept all answers in mind, thanks all!

Categories

Resources