1

I created a custom annotation called CrudSearchable and have defined some attributes there. However, the attributes I am assigning are already visible from the bean. Is there a way I can grab these values without having to redefine them manually?

// Bean
public class MockUser {

    @CrudSearchable(attribute = "name", 
                    parentClass = MockUser.class)
    private String name;

    @CrudSearchable(attribute = "group", 
                    parentClass = MockUser.class, 
                    mappedClass = MockGroup.class)
    private MockGroup group;

    // Get/Set, Equals/Hashcode, etc... 
}

// Annotation Class
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CrudSearchable {

    String attribute();

    boolean searchable() default true;

    Class<?> mappedClass() default CrudSearchable.class;

    Class<?> parentClass() default Object.class;
}

Where attribute is the attribute name, parentClass is the class literal using the annotation, and mapped class is the nested class object if applicatable.

wheeleruniverse
  • 1,511
  • 2
  • 20
  • 37

2 Answers2

0
MockUser obj = new MockUser();
Class<?> c = obj.getClass();
Field[] fields = c.getDeclaredFields();       
CrudSearchable annotation = fields[0].getAnnotation(CrudSearchable.class);
System.out.println("attribute: "   + annotation.attribute() + 
                   "searchable: "  + annotation.searchable());

Hope this helps

Yevhen Danchenko
  • 1,039
  • 2
  • 9
  • 17
  • Thanks for your post, but I am already able to access the elements once I hardcode the values, but if you see in the bean I am restating the information visible from the class. I want to grab this from the annotation. e. g @CrudSearchable(attribute="name", parentClass=MockUser.class) String name; should be replaced with @CrudSearchable String name; and I infer attribute name and class from inside the annotation code. Is this possible? – wheeleruniverse Apr 15 '17 at 02:29
0

I found the answer on another Question. This is what I was looking for.

// Put in Reflector.java

/**
 * Changes the annotation value for the given key of the given annotation to newValue and returns
 * the previous value.
 */
@SuppressWarnings("unchecked")
public static Object changeAnnotationValue(Annotation annotation, String key, Object newValue){
    Object handler = Proxy.getInvocationHandler(annotation);
    Field f;
    try {
        f = handler.getClass().getDeclaredField("memberValues");
    } catch (NoSuchFieldException | SecurityException e) {
        throw new IllegalStateException(e);
    }
    f.setAccessible(true);
    Map<String, Object> memberValues;
    try {
        memberValues = (Map<String, Object>) f.get(handler);
    } catch (IllegalArgumentException | IllegalAccessException e) {
        throw new IllegalStateException(e);
    }
    Object oldValue = memberValues.get(key);
    if (oldValue == null || oldValue.getClass() != newValue.getClass()) {
        throw new IllegalArgumentException();
    }
    memberValues.put(key,newValue);
    return oldValue;
}

Once I have this I can call the method below in the Constructor to alter the annotations I use.

// Put in Service Class

public void modifySearchable(Class<?> clazz) {
    for(Field f : clazz.getDeclaredFields()){
        CrudSearchable[] searchableArray = f.getDeclaredAnnotationsByType(CrudSearchable.class);

        for(CrudSearchable searchable : searchableArray){
            if(searchable == null){
                continue;
            }

            Reflector.alterAnnotation(searchable, "attribute", f.getName());
            Reflector.alterAnnotation(searchable, "parentClass", clazz);

            if(!(searchable.mappedAttribute().equals(""))){
                String mappedGetter = "get" + 
                        searchable.mappedAttribute().substring(0, 1).toUpperCase() + 
                        searchable.mappedAttribute().substring(1);

                Reflector.alterAnnotation(searchable, "mappedClass", f.getType());
                Reflector.alterAnnotation(searchable, "mappedGetter", mappedGetter);
            }
        }
    }
}

// Changed Bean

@Id
@GeneratedValue
@Column(name = "MOCK_USER_ID")
private Long id;

@Column(name = "MOCK_USER_NAME")
@CrudSearchable
private String name;

@ManyToOne
@JoinColumn(name = "MOCK_GROUP", nullable = false)
@CrudSearchable(mappedAttribute = "name")
private MockGroup group;

public MockUser(){
    super();
    new Searchable<>().modifySearchable(this.getClass());
}

Seems like a lot to change the values instead of having the user define them, but I believe that it will make the code more user friendly.

Hope this helps someone. I found the answer on this post: Modify a class definition's annotation string parameter at runtime. Check it out!

Community
  • 1
  • 1
wheeleruniverse
  • 1,511
  • 2
  • 20
  • 37