View Javadoc

1   /*
2    * Copyright (c) 2003-2008 by Cosylab d. d.
3    *
4    * This file is part of Java-Common.
5    *
6    * Java-Common is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU General Public License as published by
8    * the Free Software Foundation, either version 3 of the License, or
9    * (at your option) any later version.
10   *
11   * Java-Common is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU General Public License for more details.
15   *
16   * You should have received a copy of the GNU General Public License
17   * along with Java-Common.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  
20  package com.cosylab.introspection;
21  
22  import java.beans.IntrospectionException;
23  import java.beans.Introspector;
24  import java.beans.MethodDescriptor;
25  import java.beans.ParameterDescriptor;
26  import java.lang.reflect.Constructor;
27  import java.lang.reflect.Field;
28  import java.lang.reflect.Member;
29  import java.lang.reflect.Method;
30  import java.lang.reflect.Modifier;
31  import java.util.ArrayList;
32  import java.util.Arrays;
33  import java.util.Comparator;
34  import java.util.HashSet;
35  
36  /**
37   * This class provides helper methods to easily manipulate an analyze
38   * classes and their members.
39   * 
40   * @author Miha Kadunc
41   */
42  public class ClassIntrospector {
43  
44    public static Method getMethod(Class targetClass, String name,Class[] paramTypes) {
45    	Method ret=null;
46  	try {
47  		 ret=targetClass.getMethod(name,paramTypes);
48  	}
49  	catch (NoSuchMethodException e) {
50  	}
51    	return ret;
52    }
53    
54    public static Class forName(String className) throws ClassNotFoundException {
55    	if (className.indexOf('.')!=-1 || className.indexOf('[')!=-1) return Class.forName(className);
56    	if (className.equals("short")) {
57    		return Short.TYPE;
58    	} else if (className.equals("byte")) {
59    		return Byte.TYPE;
60  		} else if (className.equals("char")) {
61  			return Character.TYPE;
62  		} else if (className.equals("boolean")) {
63  			return Boolean.TYPE;
64  		} else if (className.equals("int")) {
65  			return Integer.TYPE;
66  		} else if (className.equals("float")) {
67  			return Float.TYPE;
68  		} else if (className.equals("void")) {
69  			return Void.TYPE;
70  		} else if (className.equals("double")) {
71  			return Double.TYPE;
72  		} else if (className.equals("long")) {
73  			return Long.TYPE;
74    	}
75    	throw new IllegalArgumentException("Cannot parse string "+className);
76    }
77    
78    /**
79     * Returns declared members of the specified class. By setting the fields,
80     * constructors or methods flags users can choose whether the returned members should
81     * contain these items.
82     * 
83     * 
84     * @param theClass Class
85     * @param fields boolean
86     * @param constructors boolean
87     * @param methods boolean
88     * @return Member[]
89     */
90    public static Member[] getDeclaredMembers(
91      Class theClass,
92      boolean fields,
93      boolean constructors,
94      boolean methods) {
95      if (theClass == null)
96        throw new NullPointerException("theClass");
97      ArrayList list = new ArrayList();
98  
99      if (methods) {
100       list.addAll(Arrays.asList(theClass.getDeclaredMethods()));
101     }
102 
103     if (fields) {
104       list.addAll(Arrays.asList(theClass.getDeclaredFields()));
105     }
106 
107     if (constructors) {
108       list.addAll(Arrays.asList(theClass.getDeclaredConstructors()));
109     }
110     Member[] retVal = new Member[list.size()];
111     list.toArray(retVal);
112     return retVal;
113   }
114   /**
115    * Identifies the Java Enum pattern.
116    * 
117    * @param theClass - the class that is to be inspected
118    * @return boolean
119    */
120   public static boolean isEnum(Class theClass) {
121     boolean ret = false;
122     if (theClass.getConstructors().length == 0) {
123       Field[] fields = theClass.getFields();
124       if (fields.length>0) ret=true;
125       for (int i = 0; i < fields.length; i++) {
126         if (Modifier.isPublic(fields[i].getModifiers()))
127           if (!(fields[i].getType().equals(theClass)))
128             ret = false;
129       }
130     }
131     return ret;
132   }
133   
134 	/**
135 	 * @param primitiveType
136 	 * @return the wrapper of the passed primitive type.
137 	 */
138 	public static Class getPrimitiveWrapper(Class primitiveType){
139 		if (primitiveType==Boolean.TYPE){
140 			return Boolean.class;
141 		} else if (primitiveType==Byte.TYPE){
142 			return Byte.class;
143 		} else if (primitiveType==Character.TYPE){
144 			return Character.class;
145 		} else if (primitiveType==Short.TYPE){
146 			return Short.class;
147 		} else if (primitiveType==Integer.TYPE){
148 			return Integer.class;
149 		} else if (primitiveType==Long.TYPE){
150 			return Long.class;
151 		} else if (primitiveType==Float.TYPE){
152 			return Float.class;
153 		} else if (primitiveType==Double.TYPE){
154 			return Double.class;			
155 		} else if (primitiveType==Void.TYPE){
156 			return Void.class;	
157 		}		
158 		else throw new IllegalArgumentException("Type should be primitive. "+primitiveType+" is not");
159 	}
160 	
161 	public static boolean isPrimitiveWrapper(Class wrapperType) {
162 		if ((wrapperType==Boolean.class) ||
163 				(wrapperType==Byte.class) ||
164 				(wrapperType==Character.class) ||
165 				(wrapperType==Short.class) ||
166 				(wrapperType==Integer.class) ||
167 				(wrapperType==Long.class) ||
168 				(wrapperType==Float.class) ||
169 				(wrapperType==Double.class))
170 			return true;			
171 		else return false;
172 	}
173   /**
174    * Determines whether the <code>objectType</code> class can be used in
175    * introspection operations to set a value which is declared
176    * to be of the <code>targetType</code> class.
177    * <p>
178    * This method returns <code>targetType.isAssignableFrom(objectType)</code>
179    * for object types, and also returns true when <code>targetType</code>
180    * is primitive and <code>objectType</code> is its object wrapper.
181    */
182   public static boolean isCompatible(Class objectType, Class targetType){
183   	if (targetType.isAssignableFrom(objectType)) return true;
184   	else if (targetType.isPrimitive()) {
185   		return (objectType.equals(getPrimitiveWrapper(targetType)));
186   	}
187   	else return false;
188   }
189 
190   /**
191    * Returns array of fields are decleared by Java Enum pattern.
192    * 
193    * @param theClass - the class that is to be inspected and interpreted as enum
194    * @return array of enum <code>java.lang.reflect.Field</code> objects
195    */
196   public static Field[] getEnumFields(Class theClass) {
197     if (theClass.getConstructors().length == 0) {
198       ArrayList newFields=new ArrayList();
199       boolean ret = true;
200       Field[] fields = theClass.getFields();
201       for (int i = 0; i < fields.length; i++) {
202         if (Modifier.isPublic(fields[i].getModifiers()) && Modifier.isStatic(fields[i].getModifiers())&& Modifier.isFinal(fields[i].getModifiers())){
203           if (!(fields[i].getType().equals(theClass))){
204             ret = false;
205           }
206           else {
207             newFields.add(fields[i]);
208           }
209         }
210       }
211       if (ret)
212         return (Field[])newFields.toArray(new Field[newFields.size()]);
213     }
214     return null;
215   }
216 
217   /**
218    * If class can be interpreted as Java enum pattern, then returns array this class instances as they would be enums. Method tries to order them by <code>value()</code> field. 
219    * 
220    * @param theClass - the class that is to be inspected and interpreted as enum
221    * @return array of enum instances objects
222    */
223   public static Object[] getEnums(Class theClass) throws IllegalAccessException{
224   	Field[] f= getEnumFields(theClass);
225   	if (f==null) return null;
226   	
227   	Object[] o= new Object[f.length];
228 
229 		for (int i = 0; i < o.length; i++) {
230 			o[i]= f[i].get(null);
231 		}
232   	
233 		Method m=null;
234 		
235   	try {
236   		m= getMethod(theClass,"value",new Class[]{});
237   		if (!m.getReturnType().equals(int.class)) {
238   			m=null;
239   		}
240   	} catch (Exception e) {
241   		//
242   	}
243   	
244   	if (m!=null) {
245   		final Method mm=m;
246   		
247   		Arrays.sort(o,new Comparator() {
248 			public boolean equals(Object obj) {
249 				return false;
250 			}
251 
252 			public int compare(Object o1, Object o2) {
253 				try {
254 				return ((Number)mm.invoke(o1,new Object[0])).intValue()-((Number)mm.invoke(o2,new Object[0])).intValue();
255 				} catch (Exception e) {
256 					return 0;
257 				}
258 			}
259 		});
260   	}
261   	
262   	return o;
263   	
264   }
265   	
266   	/**
267    * Selects members that have the specified modifiers from an array
268    * of <code>Member</code> instances.
269    * 
270    * @param members Member[]
271    * @param modifiers int - as specified by the java.lang.reflect.Modifier
272    * @return Member[]
273    */
274   public static Member[] getForModifiers(Member[] members, int modifiers) {
275     ArrayList list = new ArrayList();
276     System.out.println(Modifier.toString(modifiers) + "<---" + modifiers);
277     for (int i = 0; i < members.length; i++) {
278       if (areInModifiers(members[i].getModifiers(), modifiers))
279         list.add(members[i]);
280     }
281     Member[] retVal = new Member[list.size()];
282     list.toArray(retVal);
283     return retVal;
284   }
285   /**
286    * Returns all members (methods and constructors) that take the same parameters.
287    * 
288    * @param members Member[]
289    * @param parameterTypes Class[]
290    * @return Member[]
291    */
292   public static Member[] getForParameterTypes(Member[] members, Class[] parameterTypes) {
293     ArrayList list = new ArrayList();
294     for (int i = 0; i < members.length; i++) {
295       if ((members[i] instanceof Method)
296         && (((Method) members[i]).getParameterTypes().equals(parameterTypes)))
297         list.add(members[i]);
298       else if (
299         (members[i] instanceof Constructor)
300           && (((Constructor) members[i]).getParameterTypes().equals(parameterTypes)))
301         list.add(members[i]);
302     }
303     Member[] retVal = new Member[list.size()];
304     list.toArray(retVal);
305     return retVal;
306   }
307   /**
308    * Returns all members (methods and fields) that have the same declared
309    * return type (or field type).
310    * 
311    * @param members Member[]
312    * @param returnType Class
313    * @return Member[]
314    */
315   public static Member[] getForReturnType(Member[] members, Class returnType) {
316     ArrayList list = new ArrayList();
317     for (int i = 0; i < members.length; i++) {
318       if ((members[i] instanceof Method)
319         && (((Method) members[i]).getReturnType().equals(returnType)))
320         list.add(members[i]);
321       else if (
322         (members[i] instanceof Field) && (((Field) members[i]).getType().equals(returnType)))
323         list.add(members[i]);
324     }
325     Member[] retVal = new Member[list.size()];
326     list.toArray(retVal);
327     return retVal;
328   }
329   /**
330    * Returns all members of the specified class. By setting the fields,
331    * constructors or methods flags users can choose whether the returned members should
332    * contain these items.
333    * 
334    * @param theClass Class
335    * @param fields boolean
336    * @param constructors boolean
337    * @param methods boolean
338    * @return Member[]
339    */
340   public static Member[] getMembers(
341     Class theClass,
342     boolean fields,
343     boolean constructors,
344     boolean methods) {
345     if (theClass == null)
346       throw new NullPointerException("theClass");
347     HashSet set = new HashSet();
348     set.addAll(Arrays.asList(getDeclaredMembers(theClass, constructors, fields, methods)));
349     if (methods) {
350       set.addAll(Arrays.asList(theClass.getMethods()));
351     }
352 
353     if (fields) {
354       set.addAll(Arrays.asList(theClass.getFields()));
355     }
356 
357     if (constructors) {
358       set.addAll(Arrays.asList(theClass.getConstructors()));
359     }
360     Member[] retVal = new Member[set.size()];
361     set.toArray(retVal);
362     return retVal;
363   }
364   /**
365    * Returns all private members from an array of <code>Member</code> instances.
366    * 
367    * @param members Member[]
368    * @return Member[]
369    */
370   public static Member[] getPrivate(Member[] members) {
371     ArrayList list = new ArrayList();
372     for (int i = 0; i < members.length; i++) {
373       if (Modifier.isPrivate(members[i].getModifiers()))
374         list.add(members[i]);
375     }
376     Member[] retVal = new Member[list.size()];
377     list.toArray(retVal);
378     return retVal;
379   }
380   /**
381    * Returns all protected members from an array of <code>Member</code> instances.
382    * 
383    * @param members Member[]
384    * @return Member[]
385    */
386   public static Member[] getProtected(Member[] members) {
387     ArrayList list = new ArrayList();
388     for (int i = 0; i < members.length; i++) {
389       if (Modifier.isProtected(members[i].getModifiers()))
390         list.add(members[i]);
391     }
392     Member[] retVal = new Member[list.size()];
393     list.toArray(retVal);
394     return retVal;
395   }
396   /**
397    * Returns all public members from an array of <code>Member</code> instances.
398    * 
399    * @param members Member[]
400    * @return Member[]
401    */
402   public static Member[] getPublic(Member[] members) {
403     ArrayList list = new ArrayList();
404     for (int i = 0; i < members.length; i++) {
405       if (Modifier.isPublic(members[i].getModifiers()))
406         list.add(members[i]);
407     }
408     Member[] retVal = new Member[list.size()];
409     list.toArray(retVal);
410     return retVal;
411   }
412   /**
413    * Returns all public members of the specified class. By setting the fields,
414    * constructors or methods flags users can choose whether the returned members should
415    * contain these items.
416    * 
417    * @param theClass Class
418    * @param fields boolean
419    * @param constructors boolean
420    * @param methods boolean
421    * @return Member[]
422    */
423   public static Member[] getPublicMembers(
424     Class theClass,
425     boolean fields,
426     boolean constructors,
427     boolean methods) {
428     if (theClass == null)
429       throw new NullPointerException("theClass");
430     ArrayList list = new ArrayList();
431 
432     if (methods) {
433       list.addAll(Arrays.asList(theClass.getMethods()));
434     }
435 
436     if (fields) {
437       list.addAll(Arrays.asList(theClass.getFields()));
438     }
439 
440     if (constructors) {
441       list.addAll(Arrays.asList(theClass.getConstructors()));
442     }
443     Member[] retVal = new Member[list.size()];
444     list.toArray(retVal);
445     return retVal;
446   }
447   /**
448    * Returns names of parameters of a given method.
449    * 
450    * @param m Member
451    * @return String[]
452    */
453   public static String[] getParameterNames(Member m) {
454     if (m instanceof Method) {
455       String[] ret;
456 
457       try {
458         MethodDescriptor md =
459           (MethodDescriptor) BeanIntrospector.getDescriptor(
460             Introspector.getBeanInfo(m.getDeclaringClass()).getMethodDescriptors(),
461             m);
462         if (md != null) {
463           ParameterDescriptor[] pds = md.getParameterDescriptors();
464           if ((pds != null) && (pds.length == ((Method) m).getParameterTypes().length)) {
465             ret = DataFormatter.toStringArray(pds, false, false);
466             return ret;
467           }
468         }
469       } catch (IntrospectionException e) {
470 
471       }
472       int len = ((Method) m).getParameterTypes().length;
473       ret = new String[len];
474       for (int i = 0; i < ret.length; i++) {
475         ret[i] = "arg" + i;
476       }
477       return ret;
478     }
479     if (m instanceof Constructor) {
480       int len = ((Constructor) m).getParameterTypes().length;
481       String[] ret = new String[len];
482       for (int i = 0; i < ret.length; i++) {
483         ret[i] = "arg" + i;
484       }
485       return ret;
486     }
487     return null;
488   }
489 
490   /**
491    * Returns <code>true</code> if the <code>targetModifiers</code> contains all
492    * of the specified <code>modifiers<code>.
493    * 
494    * @param targetModifiers int
495    * @param modifiers int
496    * @return boolean
497    */
498   public static boolean areInModifiers(int targetModifiers, int modifiers) {
499     return true;
500   }
501 
502   /**
503    * Returns all members of the specified class that are accepted by the <code>filter</code>
504    * object passed as a parameter.
505    * 
506    * @param theClass Class
507    * @param filter MemberFilter
508    * @return Member[]
509    */
510   public static Member[] getMembers(Class theClass, MemberFilter filter) {
511     if (theClass == null)
512       throw new NullPointerException("theClass");
513     HashSet set = new HashSet();
514     if (filter == null)
515       return getMembers(theClass, true, true, true);
516     if (filter.areFieldsAllowed())
517       internalGetFields(set, theClass, filter);
518     if (filter.areConstructorsAllowed())
519       internalGetConstructors(set, theClass, filter);
520     if (filter.areMethodsAllowed())
521       internalGetMethods(set, theClass, filter);
522     Member[] retVal = new Member[set.size()];
523     set.toArray(retVal);
524     return retVal;
525   }
526 
527   /**
528    * Creation date: (12.1.2002 1:18:43)
529    */
530   private static void internalGetConstructors(HashSet set, Class theClass, MemberFilter filter) {
531     if (filter == null) {
532       set.addAll(Arrays.asList(theClass.getConstructors()));
533       return;
534     } else {
535       Member[] members = null;
536 
537       if (!filter.areDeclaredOnly()) {
538         members = theClass.getConstructors();
539         for (int i = 0; i < members.length; i++) {
540           if (filter.isAllowed(members[i]))
541             set.add(members[i]);
542         }
543         if (filter.arePublicOnly())
544           return;
545       }
546 
547       members = theClass.getDeclaredConstructors();
548       for (int i = 0; i < members.length; i++) {
549         if (filter.isAllowed(members[i]))
550           set.add(members[i]);
551       }
552     }
553   }
554 
555   /**
556    * Creation date: (12.1.2002 1:18:43)
557    */
558   private static void internalGetFields(HashSet set, Class theClass, MemberFilter filter) {
559     if (filter == null) {
560       set.addAll(Arrays.asList(theClass.getFields()));
561       return;
562     } else {
563       Member[] members = null;
564 
565       if (!filter.areDeclaredOnly()) {
566         members = theClass.getFields();
567         for (int i = 0; i < members.length; i++) {
568           if (filter.isAllowed(members[i]))
569             set.add(members[i]);
570         }
571         if (filter.arePublicOnly())
572           return;
573       }
574 
575       members = theClass.getDeclaredFields();
576       for (int i = 0; i < members.length; i++) {
577         if (filter.isAllowed(members[i]))
578           set.add(members[i]);
579       }
580     }
581   }
582 
583   /**
584    * Creation date: (12.1.2002 1:18:43)
585    */
586   private static void internalGetMethods(HashSet set, Class theClass, MemberFilter filter) {
587     if (filter == null) {
588       set.addAll(Arrays.asList(theClass.getMethods()));
589       return;
590     } else {
591       Member[] members = null;
592 
593       if (!filter.areDeclaredOnly()) {
594         members = theClass.getMethods();
595         for (int i = 0; i < members.length; i++) {
596           if (filter.isAllowed(members[i]))
597             set.add(members[i]);
598         }
599         if (filter.arePublicOnly())
600           return;
601       }
602 
603       members = theClass.getDeclaredMethods();
604       for (int i = 0; i < members.length; i++) {
605         if (filter.isAllowed(members[i]))
606           set.add(members[i]);
607       }
608     }
609   }
610 
611 }