Something I've been working on at Widen has been the ability to add OGNL expressions into a Tapestry Component for Lists. I set up this whole elaborate CRUD application to add in all this data, but when it got to the Users actually entering in an OGNL expression for a specified Class.. they were somewhat baffled. When looking at a screen with a Class name and a blank text field, how are you supposed to know which OGNL expressions you can use?
I can't expect the User to have the entire application's API memorized in their head. So, it was no surprise that the first suggestion on my CRUD page was a list of the Class's methods. Of course, not all the methods.. just the ones that play nice with OGNL.
Getting at these methods is dirt simple when you have the Class object of what you're working with.
Class object = MyClass.class;
Method[] publicMethods = object.getMethods();
huzzah..
getMethods() will only return the methods visible to anyone. Since private and protected methods are not accessible to just anyone, you'll only get the public methods. Of course, this will return every single public method from every super class.. so you'll get a bunch of methods from Object and other classes that have information you don't really care about. Use this instead:
Method[] publicMethods = object.getDeclaredMethods();
getDeclaredMethods() will only return methods declared within the Class itself (and are only visible to anyone). But, we probably want some methods from super classes that are interesting. Thus begins the filtering out process.
______
Anyways, now that I've got my Method, I want a couple things from it: a) the Name of the Method, b) the Return Type, and c) the Declaring class. Mostly, I want to mimic the basic method list that pops up when developing in an IDE. Well, it just so happens the Method has all this information built in for me.
String methodName = method.getName();
Class returnType = method.getReturnType();
Class declaringClass = method.getDeclaringClass();
It's all pretty self explanatory until I got a new request yesterday about all this. Some of these methods return Lists, Sets, Collections and like and when it displays this information, OGNL simply uses toString() which doesn't look pretty at all. It usually prints out something like this:
[MyClass info info, MyClass info info, MyClass info info]
The request was to add some way of stylizing the object within a Collection, which wasn't too tough. I added another OGNL expression value to the CRUD page and intercepted the value going into the List Component.. but now I'm back at the beginning. Another blank text field will no idea what to put in it, and this time I don't even have the Class. Luckily, reflection is here to save the day.
Let's say this is my class:
public class MyClass
{
...
private List otherClasses = new ArrayList();
...
public List getOtherClasses()
{
return otherClasses;
}
}
and I've got my Method object of getOtherClasses. You might be tempted to do this:
method.getTypeParameters()
which returns an Array of TypeVariables. These are the declaring Generic types, rather than the declared types, so you'd get the letter E, rather than OtherClass, since the declaration of List looks like:
public class List<E>
This baffled me for a while, but it makes sense after thinking about it. This is the return type and its properties. On its own, it is completely independent from your Method. After much searching around and asking for help, Method has another helpful function:
Type returnType = method.getGenericReturnType()
I was always looking for the generic type on the return type's Class rather than the Method. As far as I can tell, this will return two different Object which extend Type: either a Class equal to the Return Type, or a ParameterizedType. The ParameterizedType holds the owner and the generic parameters.
if(method.getGenericReturnType() instanceof ParameterizedTypeImpl)
{
for(Type type : ((ParameterizedTypeImpl) method.getGenericReturnType()).getActualTypeArguments())
{
genericsOfReturnType.add((Class) type);
}
}
Now my little dynamic API window on my CRUD page is complete. I wonder if this is how javadoc compiles all of its html pages.