View Javadoc
1   package org.codehaus.plexus.util;
2   
3   /*
4    * Copyright The Codehaus Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import java.lang.reflect.AccessibleObject;
20  import java.lang.reflect.Field;
21  import java.lang.reflect.Method;
22  import java.lang.reflect.Modifier;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Map;
28  
29  /**
30   * Operations on a class' fields and their setters.
31   *
32   * @author <a href="mailto:michal@codehaus.org">Michal Maczka</a>
33   * @author <a href="mailto:jesse@codehaus.org">Jesse McConnell</a>
34   * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
35   */
36  public final class ReflectionUtils {
37      // ----------------------------------------------------------------------
38      // Field utils
39      // ----------------------------------------------------------------------
40  
41      public static Field getFieldByNameIncludingSuperclasses(String fieldName, Class<?> clazz) {
42          Field retValue = null;
43  
44          try {
45              retValue = clazz.getDeclaredField(fieldName);
46          } catch (NoSuchFieldException e) {
47              Class<?> superclass = clazz.getSuperclass();
48  
49              if (superclass != null) {
50                  retValue = getFieldByNameIncludingSuperclasses(fieldName, superclass);
51              }
52          }
53  
54          return retValue;
55      }
56  
57      public static List<Field> getFieldsIncludingSuperclasses(Class<?> clazz) {
58          List<Field> fields = new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()));
59  
60          Class<?> superclass = clazz.getSuperclass();
61  
62          if (superclass != null) {
63              fields.addAll(getFieldsIncludingSuperclasses(superclass));
64          }
65  
66          return fields;
67      }
68  
69      // ----------------------------------------------------------------------
70      // Setter utils
71      // ----------------------------------------------------------------------
72  
73      /**
74       * Finds a setter in the given class for the given field. It searches interfaces and superclasses too.
75       *
76       * @param fieldName the name of the field (i.e. 'fooBar'); it will search for a method named 'setFooBar'.
77       * @param clazz The class to find the method in.
78       * @return null or the method found.
79       */
80      public static Method getSetter(String fieldName, Class<?> clazz) {
81          Method[] methods = clazz.getMethods();
82  
83          fieldName = "set" + StringUtils.capitalizeFirstLetter(fieldName);
84  
85          for (Method method : methods) {
86              if (method.getName().equals(fieldName) && isSetter(method)) {
87                  return method;
88              }
89          }
90  
91          return null;
92      }
93  
94      /**
95       * @return all setters in the given class and super classes.
96       * @param clazz the Class
97       */
98      public static List<Method> getSetters(Class<?> clazz) {
99          Method[] methods = clazz.getMethods();
100 
101         List<Method> list = new ArrayList<>();
102 
103         for (Method method : methods) {
104             if (isSetter(method)) {
105                 list.add(method);
106             }
107         }
108 
109         return list;
110     }
111 
112     /**
113      * @param method the method
114      * @return the class of the argument to the setter. Will throw an RuntimeException if the method isn't a setter.
115      */
116     public static Class<?> getSetterType(Method method) {
117         if (!isSetter(method)) {
118             throw new RuntimeException("The method "
119                     + method.getDeclaringClass().getName() + "." + method.getName() + " is not a setter.");
120         }
121 
122         return method.getParameterTypes()[0];
123     }
124 
125     // ----------------------------------------------------------------------
126     // Value accesstors
127     // ----------------------------------------------------------------------
128 
129     /**
130      * attempts to set the value to the variable in the object passed in
131      *
132      * @param object see name
133      * @param variable see name
134      * @param value see name
135      * @throws IllegalAccessException if error
136      */
137     public static void setVariableValueInObject(Object object, String variable, Object value)
138             throws IllegalAccessException {
139         Field field = getFieldByNameIncludingSuperclasses(variable, object.getClass());
140 
141         field.setAccessible(true);
142 
143         field.set(object, value);
144     }
145 
146     /**
147      * Generates a map of the fields and values on a given object, also pulls from superclasses
148      *
149      * @param variable field name
150      * @param object the object to generate the list of fields from
151      * @return map containing the fields and their values
152      * @throws IllegalAccessException cannot access
153      */
154     public static Object getValueIncludingSuperclasses(String variable, Object object) throws IllegalAccessException {
155 
156         Field field = getFieldByNameIncludingSuperclasses(variable, object.getClass());
157 
158         field.setAccessible(true);
159 
160         return field.get(object);
161     }
162 
163     /**
164      * Generates a map of the fields and values on a given object, also pulls from superclasses
165      *
166      * @param object the object to generate the list of fields from
167      * @return map containing the fields and their values
168      * @throws IllegalAccessException cannot access
169      */
170     public static Map<String, Object> getVariablesAndValuesIncludingSuperclasses(Object object)
171             throws IllegalAccessException {
172         Map<String, Object> map = new HashMap<>();
173 
174         gatherVariablesAndValuesIncludingSuperclasses(object, map);
175 
176         return map;
177     }
178 
179     // ----------------------------------------------------------------------
180     // Private
181     // ----------------------------------------------------------------------
182 
183     public static boolean isSetter(Method method) {
184         return method.getReturnType().equals(Void.TYPE)
185                 && // FIXME: needed /required?
186                 !Modifier.isStatic(method.getModifiers())
187                 && method.getParameterTypes().length == 1;
188     }
189 
190     /**
191      * populates a map of the fields and values on a given object, also pulls from superclasses
192      *
193      * @param object the object to generate the list of fields from
194      * @param map to populate
195      */
196     private static void gatherVariablesAndValuesIncludingSuperclasses(Object object, Map<String, Object> map)
197             throws IllegalAccessException {
198 
199         Class<?> clazz = object.getClass();
200 
201         if (Float.parseFloat(System.getProperty("java.specification.version")) >= 11
202                 && Class.class.getCanonicalName().equals(clazz.getCanonicalName())) {
203             // Updating Class fields accessibility is forbidden on Java 16 (and throws warning from version 11)
204             // No concrete use case to modify accessibility at this level
205             return;
206         }
207 
208         Field[] fields = clazz.getDeclaredFields();
209 
210         AccessibleObject.setAccessible(fields, true);
211 
212         for (Field field : fields) {
213             map.put(field.getName(), field.get(object));
214         }
215 
216         Class<?> superclass = clazz.getSuperclass();
217 
218         if (!Object.class.equals(superclass)) {
219             gatherVariablesAndValuesIncludingSuperclasses(superclass, map);
220         }
221     }
222 }