001: // Copyright (c) 2003-2007, Jodd Team (jodd.sf.net). All Rights Reserved.
002:
003: package jodd.introspector;
004:
005: import jodd.util.ReflectUtil;
006:
007: import java.lang.reflect.Field;
008: import java.lang.reflect.Method;
009: import java.lang.reflect.Modifier;
010: import java.lang.reflect.Constructor;
011: import java.util.Map;
012: import java.util.List;
013: import java.util.Set;
014: import java.util.Collection;
015:
016: /**
017: * A descriptor class for all methods/fields/contructors of a class.
018: * Static methods/fileds are ignored.
019: * Hash table are pre-built to speed up query.
020: * <p>
021: * Descriptors are 'lazy': various internal caches are created only on request.
022: * <p>
023: * Throughout this class, public members are defined as members
024: * defined with "public" keyword and declared in a public type.
025: * Public members declared by a non-public class is considered non-public
026: * because access to it from outside is prohibited by the java access control
027: * anyway.
028: * <p>
029: * Public members defined in public classes are always prefered even
030: * when we allow private/protected members and types to be visible.
031: * So if a non-public subtype and a public super type both have a field
032: * with the same name, the field in the public super type is always used.
033: */
034: public class ClassDescriptor {
035:
036: private final Class type;
037: private int usage;
038:
039: protected ClassDescriptor(Class type) {
040: this .type = type;
041: usage = 1;
042: isMap = ReflectUtil.isSubclass(type, Map.class);
043: isList = ReflectUtil.isSubclass(type, List.class);
044: isSet = ReflectUtil.isSubclass(type, Set.class);
045: isCollection = ReflectUtil.isSubclass(type, Collection.class);
046: }
047:
048: /**
049: * Get the class object that this descriptor describes.
050: */
051: public Class getType() {
052: return type;
053: }
054:
055: /**
056: * Increases descriptor usage.
057: */
058: protected void increaseUsage() {
059: usage++;
060: }
061:
062: /**
063: * Returns number of class description usages.
064: */
065: public int getUsage() {
066: return usage;
067: }
068:
069: // ---------------------------------------------------------------- special
070:
071: private boolean isMap;
072:
073: /**
074: * Returns <code>true</code> if class is a Map.
075: */
076: public boolean isMap() {
077: return isMap;
078: }
079:
080: private boolean isList;
081:
082: /**
083: * Returns <code>true</code> if class is a List.
084: */
085: public boolean isList() {
086: return isList;
087: }
088:
089: private boolean isSet;
090:
091: public boolean isSet() {
092: return isSet;
093: }
094:
095: private boolean isCollection;
096:
097: public boolean isCollection() {
098: return isCollection;
099: }
100:
101: // ---------------------------------------------------------------- fields
102:
103: protected Fields publicFields;
104: protected Fields allFields;
105:
106: /**
107: * Inspect class fields and create fields cache.
108: * Default implementation uses {@link ReflectUtil#getAccessibleFields(Class)} for retrieving
109: * only accessible fields.
110: */
111: protected void inspectFields() {
112: if (allFields != null) {
113: return;
114: }
115: Fields publicFields = new Fields();
116: Fields allFields = new Fields();
117:
118: Field[] fields = ReflectUtil.getAccessibleFields(type);
119: for (Field field : fields) {
120: String fName = field.getName();
121: if (ReflectUtil.isPublic(field)) {
122: publicFields.addField(fName, field);
123: }
124: ReflectUtil.forceAccess(field);
125: allFields.addField(fName, field);
126: }
127: publicFields.lock();
128: allFields.lock();
129: this .publicFields = publicFields;
130: this .allFields = allFields;
131: }
132:
133: /**
134: * Returns the field identified by name or <code>null</code> if not found.
135: *
136: * @param name field name
137: * @param suppressSecurity whether to look at non-public ones.
138: */
139: public Field getField(String name, boolean suppressSecurity) {
140: inspectFields();
141: if (suppressSecurity == true) {
142: return allFields.getField(name);
143: } else {
144: return publicFields.getField(name);
145: }
146: }
147:
148: /**
149: * Returns the public field identified by name or <code>null</code> if not found.
150: */
151: public Field getField(String name) {
152: inspectFields();
153: return publicFields.getField(name);
154: }
155:
156: /**
157: * Returns the total number of fields.
158: */
159: public int getFieldCount(boolean suppressSecurity) {
160: inspectFields();
161: if (suppressSecurity == true) {
162: return allFields.getCount();
163: } else {
164: return publicFields.getCount();
165: }
166: }
167:
168: /**
169: * Returns the total number of public fields.
170: */
171: public int getFieldCount() {
172: inspectFields();
173: return publicFields.getCount();
174: }
175:
176: /**
177: * Returns an array of all fields.
178: */
179: public Field[] getAllFields(boolean suppressSecurity) {
180: inspectFields();
181: if (suppressSecurity == true) {
182: return allFields.getAllFields();
183: } else {
184: return publicFields.getAllFields();
185: }
186: }
187:
188: /**
189: * Returns an array of all public fields.
190: */
191: public Field[] getAllFields() {
192: inspectFields();
193: return publicFields.getAllFields();
194: }
195:
196: // ---------------------------------------------------------------- methods
197:
198: protected Methods publicMethods;
199: protected Methods allMethods;
200:
201: /**
202: * Inspect methods and create methods cache.
203: * Default implementation uses {@link ReflectUtil#getAccessibleMethods(Class)} for retrieving
204: * only accessible methods.
205: */
206: protected void inspectMethods() {
207: if (allMethods != null) {
208: return;
209: }
210: Methods publicMethods = new Methods();
211: Methods allMethods = new Methods();
212:
213: Method[] methods = ReflectUtil.getAccessibleMethods(type);
214: for (Method method : methods) {
215: String methodName = method.getName();
216: if (ReflectUtil.isPublic(method)) {
217: publicMethods.addMethod(methodName, method);
218: }
219: ReflectUtil.forceAccess(method);
220: allMethods.addMethod(methodName, method);
221: }
222: allMethods.lock();
223: publicMethods.lock();
224: this .allMethods = allMethods;
225: this .publicMethods = publicMethods;
226: }
227:
228: /**
229: * Returns the method identified by name or <code>null</code> if not found.
230: *
231: * @param name method name
232: * @param suppressSecurity whether to look at non-public ones.
233: */
234: public Method getMethod(String name, boolean suppressSecurity) {
235: inspectMethods();
236: if (suppressSecurity == true) {
237: return allMethods.getMethod(name);
238: } else {
239: return publicMethods.getMethod(name);
240: }
241: }
242:
243: /**
244: * Returns the public method identified by name or <code>null</code> if not found.
245: */
246: public Method getMethod(String name) {
247: inspectMethods();
248: return publicMethods.getMethod(name);
249: }
250:
251: /**
252: * Returns the method identified by name and parameters.
253: */
254: public Method getMethod(String name, Class[] params,
255: boolean suppressSecurity) {
256: inspectMethods();
257: if (suppressSecurity == true) {
258: return allMethods.getMethod(name, params);
259: } else {
260: return publicMethods.getMethod(name, params);
261: }
262: }
263:
264: /**
265: * Returns the public method identified by name and parameters.
266: */
267: public Method getMethod(String name, Class[] params) {
268: inspectMethods();
269: return publicMethods.getMethod(name, params);
270: }
271:
272: /**
273: * Returns an array of all methods with the same name.
274: */
275: public Method[] getAllMethods(String name, boolean supressSecurity) {
276: inspectMethods();
277: if (supressSecurity == true) {
278: return allMethods.getAllMethods(name);
279: } else {
280: return publicMethods.getAllMethods(name);
281: }
282: }
283:
284: /**
285: * Returns an array of all public methods with the same name.
286: */
287: public Method[] getAllMethods(String name) {
288: inspectMethods();
289: return publicMethods.getAllMethods(name);
290: }
291:
292: /**
293: * Returns an array of all methods.
294: */
295: public Method[] getAllMethods(boolean supressSecurity) {
296: inspectMethods();
297: if (supressSecurity == true) {
298: return allMethods.getAllMethods();
299: } else {
300: return publicMethods.getAllMethods();
301: }
302: }
303:
304: /**
305: * Returns an array of all public methods.
306: */
307: public Method[] getAllMethods() {
308: inspectMethods();
309: return publicMethods.getAllMethods();
310: }
311:
312: // ---------------------------------------------------------------- beans
313:
314: protected Properties publicProperties;
315: protected Properties allProperties;
316:
317: /**
318: * Inspect methods and create properties cache.
319: */
320: protected void inspectProperties() {
321: if (publicProperties != null) {
322: return;
323: }
324: Properties publicProperties = new Properties();
325: Properties allProperties = new Properties();
326:
327: Method[] methods = ReflectUtil.getAccessibleMethods(type);
328: for (Method method : methods) {
329: if (Modifier.isStatic(method.getModifiers())) {
330: continue; // ignore static
331: }
332: String methodName = method.getName();
333: Class[] paramTypes = method.getParameterTypes();
334: Class returnType = method.getReturnType();
335: boolean add = false;
336:
337: if (methodName.startsWith("get")
338: && methodName.equals("getClass") == false) { // getter method must starts with 'get' and it is not getClass()
339: if ((returnType != null) && (paramTypes.length == 0)) { // getter must have a return type and no arguments
340: methodName = '-'
341: + methodName.substring(3, 4).toLowerCase()
342: + methodName.substring(4);
343: add = true;
344: }
345: } else if (methodName.startsWith("is")) { // ister must starts with 'is'
346: if ((returnType != null) && (paramTypes.length == 0)) { // ister must have return type and no arguments
347: methodName = '-'
348: + methodName.substring(2, 3).toLowerCase()
349: + methodName.substring(3);
350: add = true;
351: }
352: } else if (methodName.startsWith("set")) { // setter must start with a 'set'
353: if (paramTypes.length == 1) { // setter must have just one argument
354: methodName = '+'
355: + methodName.substring(3, 4).toLowerCase()
356: + methodName.substring(4);
357: add = true;
358: }
359: }
360:
361: if (add == true) {
362: if (ReflectUtil.isPublic(method)) {
363: publicProperties.addMethod(methodName, method);
364: }
365: ReflectUtil.forceAccess(method);
366: allProperties.addMethod(methodName, method);
367: }
368: }
369: allProperties.lock();
370: publicProperties.lock();
371: this .allProperties = allProperties;
372: this .publicProperties = publicProperties;
373: }
374:
375: /**
376: * Returns bean setter identified by name.
377: */
378: public Method getBeanSetter(String name, boolean suppressSecurity) {
379: inspectProperties();
380: if (suppressSecurity == true) {
381: return allProperties.setters.getMethod(name);
382: } else {
383: return publicProperties.setters.getMethod(name);
384: }
385: }
386:
387: /**
388: * Returns public bean setter identified by name.
389: */
390: public Method getBeanSetter(String name) {
391: inspectProperties();
392: return publicProperties.setters.getMethod(name);
393: }
394:
395: /**
396: * Returns an array of all bean setters.
397: */
398: public Method[] getAllBeanSetters(boolean suppressSecurity) {
399: inspectProperties();
400: if (suppressSecurity == true) {
401: return allProperties.setters.getAllMethods();
402: } else {
403: return publicProperties.setters.getAllMethods();
404: }
405: }
406:
407: /**
408: * Returns an array of all public bean setters.
409: */
410: public Method[] getAllBeanSetters() {
411: inspectProperties();
412: return publicProperties.setters.getAllMethods();
413: }
414:
415: /**
416: * Returns an array of all bean setters names.
417: */
418: public String[] getAllBeanSetterNames(boolean suppressSecurity) {
419: inspectProperties();
420: if (suppressSecurity == true) {
421: return allProperties.setterNames;
422: } else {
423: return publicProperties.setterNames;
424: }
425: }
426:
427: /**
428: * Returns an array of all public bean setters names.
429: */
430: public String[] getAllBeanSetterNames() {
431: inspectProperties();
432: return publicProperties.setterNames;
433: }
434:
435: /**
436: * Returns bean getter identified by name.
437: */
438: public Method getBeanGetter(String name, boolean suppressSecurity) {
439: inspectProperties();
440: if (suppressSecurity == true) {
441: return allProperties.getters.getMethod(name);
442: } else {
443: return publicProperties.getters.getMethod(name);
444: }
445: }
446:
447: /**
448: * Returns public bean getter identified by name.
449: */
450: public Method getBeanGetter(String name) {
451: inspectProperties();
452: return publicProperties.getters.getMethod(name);
453: }
454:
455: /**
456: * Returns all bean getters.
457: */
458: public Method[] getAllBeanGetters(boolean suppressSecurity) {
459: inspectProperties();
460: if (suppressSecurity == true) {
461: return allProperties.getters.getAllMethods();
462: } else {
463: return publicProperties.getters.getAllMethods();
464: }
465: }
466:
467: /**
468: * Returns all public bean getters.
469: */
470: public Method[] getAllBeanGetters() {
471: inspectProperties();
472: return publicProperties.getters.getAllMethods();
473: }
474:
475: /**
476: * Returns all bean getters names.
477: */
478: public String[] getAllBeanGetterNames(boolean suppressSecurity) {
479: inspectProperties();
480: if (suppressSecurity == true) {
481: return allProperties.getterNames;
482: } else {
483: return publicProperties.getterNames;
484: }
485: }
486:
487: /**
488: * Returns all public bean getters names.
489: */
490: public String[] getAllBeanGetterNames() {
491: inspectProperties();
492: return publicProperties.getterNames;
493: }
494:
495: // ---------------------------------------------------------------- ctors
496:
497: protected Ctors publicCtors;
498: protected Ctors allCtors;
499:
500: /**
501: * Inspect class ctors and create ctors cache.
502: * Default implementation uses {@link ReflectUtil#getAccessibleFields(Class)} for retrieving
503: * only accessible fields.
504: */
505: protected void inspectCtors() {
506: if (allCtors != null) {
507: return;
508: }
509: Ctors publicCtors = new Ctors();
510: Ctors allCtors = new Ctors();
511:
512: publicCtors.addCtors(type.getConstructors());
513: allCtors.addCtors(type.getDeclaredConstructors());
514: Constructor[] ctors = allCtors.getAllCtors();
515: for (Constructor ctor : ctors) {
516: if (ReflectUtil.isPublic(ctor) == false) {
517: ReflectUtil.forceAccess(ctor);
518: }
519: }
520: publicCtors.lock();
521: allCtors.lock();
522: this .publicCtors = publicCtors;
523: this .allCtors = allCtors;
524: }
525:
526: /**
527: * Returns the default ctor or <code>null</code> if not found.
528: */
529: public Constructor getDefaultCtor(boolean suppressSecurity) {
530: inspectCtors();
531: if (suppressSecurity == true) {
532: return allCtors.getDefaultCtor();
533: } else {
534: return publicCtors.getDefaultCtor();
535: }
536: }
537:
538: /**
539: * Returns the constructor identified by arguments or <code>null</code> if not found.
540: *
541: * @param args ctor arguments
542: * @param suppressSecurity whether to look at non-public ones.
543: */
544: public Constructor getCtor(Class[] args, boolean suppressSecurity) {
545: inspectCtors();
546: if (suppressSecurity == true) {
547: return allCtors.getCtor(args);
548: } else {
549: return publicCtors.getCtor(args);
550: }
551: }
552:
553: /**
554: * Returns the public ctor identified by arguments or <code>null</code> if not found.
555: */
556: public Constructor getCtor(Class[] args) {
557: inspectCtors();
558: return publicCtors.getCtor(args);
559: }
560:
561: /**
562: * Returns the public default ctor or <code>null</code> if not found.
563: */
564: public Constructor getDefaultCtor() {
565: inspectCtors();
566: return publicCtors.getDefaultCtor();
567: }
568:
569: /**
570: * Returns the total number of constructors.
571: */
572: public int getCtorCount(boolean suppressSecurity) {
573: inspectCtors();
574: if (suppressSecurity == true) {
575: return allCtors.getCount();
576: } else {
577: return publicCtors.getCount();
578: }
579: }
580:
581: /**
582: * Returns the total number of public constructors.
583: */
584: public int getCtorCount() {
585: inspectCtors();
586: return publicCtors.getCount();
587: }
588:
589: /**
590: * Returns an array of all ctors.
591: */
592:
593: public Constructor[] getAllCtors(boolean suppressSecurity) {
594: inspectCtors();
595: if (suppressSecurity == true) {
596: return allCtors.getAllCtors();
597: } else {
598: return publicCtors.getAllCtors();
599: }
600: }
601:
602: /**
603: * Returns an array of all public ctors.
604: */
605: public Constructor[] getAllCtors() {
606: inspectCtors();
607: return publicCtors.getAllCtors();
608: }
609:
610: }
|