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ø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 }