View Javadoc
1   package pl.matsuo.core.util;
2   
3   import com.google.common.base.Joiner;
4   import org.springframework.core.ResolvableType;
5   import pl.matsuo.core.util.function.Failure;
6   import pl.matsuo.core.util.function.Success;
7   import pl.matsuo.core.util.function.Try;
8   
9   import java.lang.reflect.AccessibleObject;
10  import java.lang.reflect.AnnotatedElement;
11  import java.lang.reflect.Field;
12  import java.lang.reflect.Method;
13  import java.util.ArrayList;
14  import java.util.List;
15  
16  import static com.google.common.collect.Lists.*;
17  import static java.util.Arrays.asList;
18  import static org.springframework.util.StringUtils.*;
19  
20  
21  @SuppressWarnings("unchecked")
22  public class ReflectUtil {
23  
24  
25    public static <X> Class<X> resolveType(Class<?> clazz, Class<?> superClazz, int index) {
26      return (Class<X>) ResolvableType.forClass(clazz).as(superClazz).resolveGeneric(index);
27    }
28  
29  
30    interface EFunction<T, R> {
31      R apply(T t) throws Exception;
32    }
33  
34  
35    interface EBiFunction<T, U, R> {
36      R apply(T t, U u) throws Exception;
37    }
38  
39  
40    private static <E extends AccessibleObject> Try<Object> accessMember(Object object, String fieldName,
41                                           EFunction<String, E> memberGetter, EBiFunction<E, Object, Object> valueGetter) {
42      try {
43        E member = memberGetter.apply(fieldName);
44        try {
45          member.setAccessible(true);
46          return new Success<>(valueGetter.apply(member, object));
47        } finally {
48          member.setAccessible(false);
49        }
50      } catch (Exception e) {}
51  
52      return new Failure<>();
53    }
54  
55  
56    protected static <E> Try<Object> getValue(Object object, String fieldName, Class clazz) {
57      if (!clazz.equals(Object.class)) {
58        return ReflectUtil.<Field>accessMember(object, fieldName, clazz::getDeclaredField, Field::get).ifFailure(
59                () -> ReflectUtil.<Method>accessMember(object, "get" + capitalize(fieldName), clazz::getDeclaredMethod, Method::invoke)
60            ).ifFailure(() -> getValue(object, fieldName, clazz.getSuperclass()));
61      }
62  
63      throw new RuntimeException("No such property/getter in class ");
64    }
65  
66  
67    public static <E> E getValue(Object object, String fieldName) {
68      return (E) getValue(object, fieldName, object.getClass()).get();
69    }
70  
71  
72    /**
73     * Pobiera typ pojedynczego pola.
74     */
75    protected static <E> Class<E> getExactPropertyType(List<Class> classes, String exactFieldName) {
76      for (Class clazz : classes) {
77        Class<E> propertyType = getExactPropertyType(clazz, exactFieldName);
78        if (propertyType != null) {
79          return propertyType;
80        }
81      }
82  
83      return null;
84    }
85  
86  
87    /**
88     * Pobiera typ pojedynczego pola.
89     */
90    protected static <E> Class<E> getExactPropertyType(final Class<?> clazz, String exactFieldName) {
91      if (clazz == null || clazz.equals(Object.class)) {
92        return null;
93      }
94  
95      // próba pobrania typu z pola
96      try {
97        return (Class<E>) clazz.getDeclaredField(exactFieldName).getType();
98      } catch (Exception e) {}
99  
100     // próba pobrania typu z gettera
101     try {
102       return (Class<E>) clazz.getDeclaredMethod("get" + capitalize(exactFieldName)).getReturnType();
103     } catch (Exception e) {}
104 
105     // tworzymy listę nadklas i nadinterfejsów
106     List<Class> classes = new ArrayList<>();
107     if (clazz.getSuperclass() != null) {
108       classes.add(clazz.getSuperclass());
109     }
110     classes.addAll(asList(clazz.getInterfaces()));
111 
112     // wywołujemy
113     return getExactPropertyType(classes, exactFieldName);
114   }
115 
116 
117   /**
118    * Pobiera typ pola, nawet jeśli jest to zagnieżdzona definicja (typu "entity.person.id").
119    */
120   public static <E> Class<E> getPropertyType(final Class<?> clazz, String fieldName) {
121     Class<?> exactType = clazz;
122     for (String fieldPart : fieldName.split("[.]")) {
123       exactType = getExactPropertyType(exactType, fieldPart);
124 
125       if (exactType == null) {
126         throw new RuntimeException("No such property/getter in class " + clazz.getSimpleName() + " for " + fieldName);
127       }
128     }
129 
130     if (exactType == null) {
131       throw new RuntimeException("No such property/getter in class" + clazz.getSimpleName() + " for " + fieldName);
132     } else {
133       return (Class<E>) exactType;
134     }
135   }
136 
137 
138   /**
139    * Pobiera typ pojedynczego pola.
140    */
141   protected static AnnotatedElement getExactAnnotatedElement(List<Class> classes, String exactFieldName) {
142     for (Class clazz : classes) {
143       AnnotatedElement annotatedElement = getExactAnnoatedElement(clazz, exactFieldName);
144       if (annotatedElement != null) {
145         return annotatedElement;
146       }
147     }
148 
149     return null;
150   }
151 
152 
153   /**
154    * Pobiera typ pojedynczego pola.
155    */
156   protected static AnnotatedElement getExactAnnoatedElement(final Class<?> clazz, String exactFieldName) {
157     if (clazz == null || clazz.equals(Object.class)) {
158       return null;
159     }
160 
161     // próba pobrania typu z pola
162     try {
163       return clazz.getDeclaredField(exactFieldName);
164     } catch (Exception e) {}
165 
166     try {
167       return clazz.getDeclaredMethod("get" + capitalize(exactFieldName));
168     } catch (Exception e) {}
169 
170     // tworzymy listę nadklas i nadinterfejsów
171     List<Class> classes = new ArrayList<>();
172     if (clazz.getSuperclass() != null) {
173       classes.add(clazz.getSuperclass());
174     }
175     classes.addAll(asList(clazz.getInterfaces()));
176 
177     // wywołujemy
178     return getExactAnnotatedElement(classes, exactFieldName);
179   }
180 
181 
182   public static AnnotatedElement getAnnoatedElement(Class<?> clazz, String fieldName) {
183     String[] splitted = fieldName.split("[.]");
184     List<String> prefix = newArrayList(splitted);
185     String lastElement = prefix.remove(prefix.size() - 1);
186 
187 
188     if (!prefix.isEmpty()) {
189       clazz = getPropertyType(clazz, Joiner.on(".").join(prefix));
190     }
191 
192     return getExactAnnoatedElement(clazz, lastElement);
193   }
194 
195 
196   public static <E> E invoke(Object target, String methodName, Object ... args) {
197     for (Method method : target.getClass().getMethods()) {
198       if (method.getName().equals(methodName)) {
199         try {
200           return (E) method.invoke(target, args);
201         } catch (Exception e) {
202           throw new RuntimeException("Exception invoking method " + methodName, e);
203         }
204       }
205     }
206 
207     throw new RuntimeException("Method " + methodName + " not found");
208   }
209 
210 
211   public static String fieldName(String methodName) {
212     return uncapitalize(methodName.substring(3));
213   }
214 }
215