Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get methods in source order

I have a custom annotation that I want to use at runtime to display object properties. I would like them to appear in source code order but reflection does not guarantee any particular order for Class.getMethods().

Is there a way, either via reflection or via annotation processing, to get the methods in source order (at least per class if multiple levels of inheritance are involved)?

As an example, say I had an interface Property

package test;

public @interface Property {
    public String name();
}

and a class using that annotation

package test;

public class MyObject {
    @Property(name = "First")
    public void getFirst() {}

    @Property(name = "Another")
    public void getAnother() {}
}

I'd like to reliably get the property "First" before the property "Another".

I know I can add an ordering property to my annotation and sort on that but I have a lot of classes that would need to be updated if that is required so I'm looking for a generic method to achieve this without modifying individual annotations.

like image 942
Rangi Keen Avatar asked Nov 27 '13 17:11

Rangi Keen


2 Answers

It appears this is not possible via reflection unless you are willing and able to change the original source to impose an order via an additional annotation or attribute of an existing annotation.

It is possible, however, during annotation processing. The documentation for TypeElement.getEnclosedElements()] indicates that

the list of enclosed elements will be returned in the natural order for the originating source of information about the type. For example, if the information about the type is originating from a source file, the elements will be returned in source code order [emphasis added].

To make this available at runtime, you need to process the methods with the annotations and store somewhere that will be accessible at runtime (e.g a generated resource file).

like image 110
Rangi Keen Avatar answered Oct 05 '22 21:10

Rangi Keen


If you collect the annotations into aList<Property> you can order the List like any other collection using Collections.sort(collection, comparator). The main issue is there is no natural order to how the annotations should be ordered, so you will need to define this order. I have defined the order via a List which is used in the comparator.

public class MyObject {

    @Property(name = "First")
    public void getFirst() {
    }

    @Property(name = "Another")
    public void getAnother() {
    }

    @Property(name = "Last")
    public void getLast() {
    }

    public static void main(String[] args) {
        Method[] methods = MyObject.class.getDeclaredMethods();
        List<Property> properties = new ArrayList<Property>();

        for(Method method: methods){
            if(method.isAnnotationPresent(Property.class)){
                properties.add(method.getAnnotation(Property.class));
            }
        }

        for(Property property:properties){
            System.out.println(property.name());
        }

        Collections.sort(properties, new Comparator<Property>(){

            List<String> order = Arrays.asList("Last", "First", "Another");

            @Override
            public int compare(Property arg0, Property arg1) {
              //Compare only considers the sign of result.  
              return (order.indexOf(arg0.name()) - order.indexOf(arg1.name()));
            }

        });

        for(Property property:properties){
            System.out.println(property.name());
        }

    }
}
like image 23
Kevin Bowersox Avatar answered Oct 05 '22 21:10

Kevin Bowersox