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.Field;
20  import java.lang.reflect.Method;
21  import java.lang.reflect.Modifier;
22  import java.lang.reflect.AccessibleObject;
23  import java.util.Map;
24  import java.util.HashMap;
25  import java.util.List;
26  import java.util.ArrayList;
27  import java.util.Arrays;
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      // ----------------------------------------------------------------------
39      // Field utils
40      // ----------------------------------------------------------------------
41  
42      public static Field getFieldByNameIncludingSuperclasses( String fieldName, Class<?> clazz )
43      {
44          Field retValue = null;
45  
46          try
47          {
48              retValue = clazz.getDeclaredField( fieldName );
49          }
50          catch ( NoSuchFieldException e )
51          {
52              Class<?> superclass = clazz.getSuperclass();
53  
54              if ( superclass != null )
55              {
56                  retValue = getFieldByNameIncludingSuperclasses( fieldName, superclass );
57              }
58          }
59  
60          return retValue;
61      }
62  
63      public static List<Field> getFieldsIncludingSuperclasses( Class<?> clazz )
64      {
65          List<Field> fields = new ArrayList<>( Arrays.asList( clazz.getDeclaredFields() ) );
66  
67          Class<?> superclass = clazz.getSuperclass();
68  
69          if ( superclass != null )
70          {
71              fields.addAll( getFieldsIncludingSuperclasses( superclass ) );
72          }
73  
74          return fields;
75      }
76  
77      // ----------------------------------------------------------------------
78      // Setter utils
79      // ----------------------------------------------------------------------
80  
81      /**
82       * Finds a setter in the given class for the given field. It searches interfaces and superclasses too.
83       *
84       * @param fieldName the name of the field (i.e. 'fooBar'); it will search for a method named 'setFooBar'.
85       * @param clazz The class to find the method in.
86       * @return null or the method found.
87       */
88      public static Method getSetter( String fieldName, Class<?> clazz )
89      {
90          Method[] methods = clazz.getMethods();
91  
92          fieldName = "set" + StringUtils.capitalizeFirstLetter( fieldName );
93  
94          for ( Method method : methods )
95          {
96              if ( method.getName().equals( fieldName ) && isSetter( method ) )
97              {
98                  return method;
99              }
100         }
101 
102         return null;
103     }
104 
105     /**
106      * @return all setters in the given class and super classes.
107      * @param clazz the Class
108      */
109     public static List<Method> getSetters( Class<?> clazz )
110     {
111         Method[] methods = clazz.getMethods();
112 
113         List<Method> list = new ArrayList<>();
114 
115         for ( Method method : methods )
116         {
117             if ( isSetter( method ) )
118             {
119                 list.add( method );
120             }
121         }
122 
123         return list;
124     }
125 
126     /**
127      * @param method the method
128      * @return the class of the argument to the setter. Will throw an RuntimeException if the method isn't a setter.
129      */
130     public static Class<?> getSetterType( Method method )
131     {
132         if ( !isSetter( method ) )
133         {
134             throw new RuntimeException( "The method " + method.getDeclaringClass().getName() + "." + method.getName()
135                 + " is not a setter." );
136         }
137 
138         return method.getParameterTypes()[0];
139     }
140 
141     // ----------------------------------------------------------------------
142     // Value accesstors
143     // ----------------------------------------------------------------------
144 
145     /**
146      * attempts to set the value to the variable in the object passed in
147      *
148      * @param object see name
149      * @param variable see name
150      * @param value see name
151      * @throws IllegalAccessException if error
152      */
153     public static void setVariableValueInObject( Object object, String variable, Object value )
154         throws IllegalAccessException
155     {
156         Field field = getFieldByNameIncludingSuperclasses( variable, object.getClass() );
157 
158         field.setAccessible( true );
159 
160         field.set( object, value );
161     }
162 
163     /**
164      * Generates a map of the fields and values on a given object, also pulls from superclasses
165      * 
166      * @param variable field name
167      * @param object the object to generate the list of fields from
168      * @return map containing the fields and their values
169      * @throws IllegalAccessException cannot access
170      */
171     public static Object getValueIncludingSuperclasses( String variable, Object object )
172         throws IllegalAccessException
173     {
174 
175         Field field = getFieldByNameIncludingSuperclasses( variable, object.getClass() );
176 
177         field.setAccessible( true );
178 
179         return field.get( object );
180     }
181 
182     /**
183      * Generates a map of the fields and values on a given object, also pulls from superclasses
184      *
185      * @param object the object to generate the list of fields from
186      * @return map containing the fields and their values
187      * @throws IllegalAccessException cannot access
188      */
189     public static Map<String, Object> getVariablesAndValuesIncludingSuperclasses( Object object )
190         throws IllegalAccessException
191     {
192         Map<String, Object> map = new HashMap<>();
193 
194         gatherVariablesAndValuesIncludingSuperclasses( object, map );
195 
196         return map;
197     }
198 
199     // ----------------------------------------------------------------------
200     // Private
201     // ----------------------------------------------------------------------
202 
203     public static boolean isSetter( Method method )
204     {
205         return method.getReturnType().equals( Void.TYPE ) && // FIXME: needed /required?
206             !Modifier.isStatic( method.getModifiers() ) && method.getParameterTypes().length == 1;
207     }
208 
209     /**
210      * populates a map of the fields and values on a given object, also pulls from superclasses
211      *
212      * @param object the object to generate the list of fields from
213      * @param map to populate
214      */
215     private static void gatherVariablesAndValuesIncludingSuperclasses( Object object, Map<String, Object> map )
216         throws IllegalAccessException
217     {
218 
219         Class<?> clazz = object.getClass();
220 
221         if ( Float.parseFloat( System.getProperty( "java.specification.version" ) ) >= 11
222             && Class.class.getCanonicalName().equals( clazz.getCanonicalName() ) )
223         {
224             // Updating Class fields accessibility is forbidden on Java 16 (and throws warning from version 11)
225             // No concrete use case to modify accessibility at this level
226             return;
227         }
228 
229         Field[] fields = clazz.getDeclaredFields();
230 
231         AccessibleObject.setAccessible( fields, true );
232 
233         for ( Field field : fields )
234         {
235             map.put( field.getName(), field.get( object ) );
236 
237         }
238 
239         Class<?> superclass = clazz.getSuperclass();
240 
241         if ( !Object.class.equals( superclass ) )
242         {
243             gatherVariablesAndValuesIncludingSuperclasses( superclass, map );
244         }
245     }
246 }