001 /*
002 * $Id: ReflectionHelper.java,v 1.12 2012/02/09 17:56:39 oboehm Exp $
003 *
004 * Copyright (c) 2009 by Oliver Boehm
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express orimplied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 *
018 * (c)reated 09.03.2009 by oliver (ob@aosd.de)
019 */
020 package patterntesting.runtime.util;
021
022 import java.lang.reflect.*;
023 import java.util.*;
024
025 import org.slf4j.*;
026
027 /**
028 * This class is a helper class to access some fields via reflection. Normally
029 * you should avoid reflection. Handle with care if you use it.
030 *
031 * @author <a href="boehm@javatux.de">oliver</a>
032 * @since 09.03.2009
033 * @version $Revision: 1.12 $
034 */
035 public class ReflectionHelper {
036
037 private static final Logger log = LoggerFactory.getLogger(ReflectionHelper.class);
038
039 /** Utility class - no need to instantiate it */
040 private ReflectionHelper() {}
041
042 /**
043 * Tries to get the wanted field. Before it is returned the acc
044 *
045 * @param cl the cl
046 * @param name the name
047 *
048 * @return the wanted field
049 *
050 * @throws NoSuchFieldException the no such field exception
051 */
052 public static Field getField(final Class<?> cl, final String name)
053 throws NoSuchFieldException {
054 try {
055 Field field = cl.getDeclaredField(name);
056 field.setAccessible(true);
057 return field;
058 } catch (NoSuchFieldException e) {
059 Class<?> superclass = cl.getSuperclass();
060 if (superclass == null) {
061 throw e;
062 } else {
063 if (log.isTraceEnabled()) {
064 log.trace("using " + superclass + " to get " + cl.getName()
065 + "." + name + "...");
066 }
067 return ReflectionHelper.getField(superclass, name);
068 }
069 }
070 }
071
072 /**
073 * If it can it returns the value of the given field. If not it throws an
074 * exception.
075 *
076 * @param obj the obj
077 * @param name the name
078 * @return the field value
079 * @throws NoSuchFieldException the no such field exception
080 * @throws IllegalArgumentException the illegal argument exception
081 * @throws IllegalAccessException the illegal access exception
082 */
083 public static Object getFieldValue(final Object obj, final String name)
084 throws NoSuchFieldException, IllegalArgumentException,
085 IllegalAccessException {
086 Field field = ReflectionHelper.getField(obj.getClass(), name);
087 return field.get(obj);
088 }
089
090 /**
091 * To get all uninitialized field you can call this method.
092 *
093 * @param obj the object where to get the fields from
094 *
095 * @return a collecion of unitialized fields (can be empty)
096 */
097 public static Collection<Field> getUninitializedNonStaticFields(final Object obj) {
098 Collection<Field> unitializedFields = new ArrayList<Field>();
099 Field[] fields = obj.getClass().getDeclaredFields();
100 for (int i = 0; i < fields.length; i++) {
101 try {
102 fields[i].setAccessible(true);
103 if ((fields[i].get(obj) == null) && !isStatic(fields[i])) {
104 unitializedFields.add(fields[i]);
105 }
106 } catch (IllegalArgumentException e) {
107 log.info(e + " => " + fields[i] + " ignored");
108 } catch (IllegalAccessException e) {
109 log.debug("can't access " + fields[i] + " => ignored");
110 }
111 }
112 return unitializedFields;
113 }
114
115 /**
116 * Checks if the given field is static.
117 *
118 * @param field the field
119 * @return true, if is static
120 */
121 public static boolean isStatic(final Field field) {
122 int m = field.getModifiers();
123 return Modifier.isStatic(m);
124 }
125
126 /**
127 * To short string.
128 *
129 * @param field the field
130 *
131 * @return the string
132 */
133 public static String toShortString(final Field field) {
134 return field.getType().getSimpleName() + " " + field.getName();
135 }
136
137 /**
138 * To short string.
139 *
140 * @param fields the fields
141 *
142 * @return the string
143 */
144 public static String toShortString(final Collection<Field> fields) {
145 StringBuffer sbuf = new StringBuffer();
146 for (Iterator<Field> iterator = fields.iterator(); iterator.hasNext();) {
147 Field field = iterator.next();
148 sbuf.append(", ");
149 sbuf.append(toShortString(field));
150 }
151 return sbuf.substring(2);
152 }
153
154 /**
155 * Gets the method.
156 *
157 * @param cl the cl
158 * @param name the name
159 * @param args the args
160 * @return the method
161 * @throws NoSuchMethodException the no such method exception
162 */
163 public static Method getMethod(final Class<?> cl, final String name, final Object... args)
164 throws NoSuchMethodException {
165 Class<?> parameterTypes[] = new Class<?>[args.length];
166 for (int i = 0; i < args.length; i++) {
167 parameterTypes[i] = args[i].getClass();
168 }
169 return getMethod(cl, name, parameterTypes);
170 }
171
172 /**
173 * Gets the method.
174 *
175 * @param cl e.g. class java.lang.ClassLoader
176 * @param name e.g. "getPackages"
177 * @param parameterTypes the parameter types
178 * @return a Method object
179 * @throws NoSuchMethodException the no such method exception
180 */
181 public static Method getMethod(final Class<?> cl, final String name,
182 final Class<?>... parameterTypes) throws NoSuchMethodException {
183 try {
184 return cl.getDeclaredMethod(name, parameterTypes);
185 } catch (NoSuchMethodException origException) {
186 try {
187 try {
188 return findMethod(cl, name, parameterTypes);
189 } catch (NoSuchMethodException nsme) {
190 Class<?> superclass = cl.getSuperclass();
191 if (superclass == null) {
192 throw origException;
193 } else {
194 return getMethod(superclass, name, parameterTypes);
195 }
196 }
197 } catch (NoSuchMethodException derived) {
198 throw origException;
199 }
200 }
201 }
202
203 private static Method findMethod(final Class<?> cl, final String name,
204 final Class<?>... parameterTypes) throws NoSuchMethodException {
205 Method[] methods = cl.getDeclaredMethods();
206 for (int i = 0; i < methods.length; i++) {
207 if (name.equals(methods[i].getName()) && matchesParameters(methods[i], parameterTypes)) {
208 return methods[i];
209 }
210 }
211 throw new NoSuchMethodException(cl.getName() + "." + name + "("
212 + Converter.toShortString(parameterTypes) + ")");
213 }
214
215 private static boolean matchesParameters(final Method method, final Class<?>[] matchingTypes) {
216 Class<?>[] parameterTypes = method.getParameterTypes();
217 if (parameterTypes.length != matchingTypes.length) {
218 return false;
219 }
220 for (int i = 0; i < parameterTypes.length; i++) {
221 if (!matches(parameterTypes[i], matchingTypes[i])) {
222 return false;
223 }
224 }
225 return true;
226 }
227
228 private static boolean matches(final Class<?> parameterType, final Class<?> matchingType) {
229 if (parameterType.isAssignableFrom(matchingType)) {
230 return true;
231 }
232 if (parameterType.isPrimitive()) {
233 if (parameterType.equals(boolean.class)) {
234 return matchingType.equals(Boolean.class);
235 } else if (parameterType.equals(byte.class)) {
236 return matchingType.equals(Byte.class);
237 } else if (parameterType.equals(char.class)) {
238 return matchingType.equals(Character.class);
239 } else if (parameterType.equals(short.class)) {
240 return matchingType.equals(Short.class);
241 } else if (parameterType.equals(int.class)) {
242 return matchingType.equals(Integer.class);
243 } else if (parameterType.equals(long.class)) {
244 return matchingType.equals(Long.class);
245 } else if (parameterType.equals(float.class)) {
246 return matchingType.equals(Float.class);
247 } else if (parameterType.equals(double.class)) {
248 return matchingType.equals(Double.class);
249 } else {
250 log.warn("unknown primitive type \"" + parameterType + "\" not yet supported - sorry!");
251 return false;
252 }
253 }
254 return false;
255 }
256
257 /**
258 * Invoke method. This could be also a protected or private method.
259 *
260 * @param target the target
261 * @param name the method name
262 * @param args the args for the method
263 * @return the result of the method.
264 */
265 public static Object invokeMethod(final Object target, final String name, final Object... args) {
266 try {
267 Method method = getMethod(target.getClass(), name, args);
268 method.setAccessible(true);
269 return method.invoke(target, args);
270 } catch (NoSuchMethodException e) {
271 throw new IllegalArgumentException(
272 "no method \"" + name + "(" + toParamString(args) + ")\" in " + target.getClass(), e);
273 } catch (IllegalAccessException iae) {
274 throw new RuntimeException("can't access method \"" + name + "\" in "
275 + target.getClass(), iae);
276 } catch (InvocationTargetException ite) {
277 throw new RuntimeException("exception in method \"" + name + "\" of "
278 + target.getClass(), ite.getTargetException());
279 }
280 }
281
282 private static String toParamString(final Object[] args) {
283 Class<?> paramTypes[] = new Class<?>[args.length];
284 for (int i = 0; i < args.length; i++) {
285 paramTypes[i] = args[i].getClass();
286 }
287 return Converter.toShortString(paramTypes);
288 }
289
290 }