1 package org.apache.velocity.tools.generic;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.lang.annotation.Annotation;
23 import java.lang.reflect.AnnotatedElement;
24 import java.lang.reflect.Constructor;
25 import java.lang.reflect.Field;
26 import java.lang.reflect.Method;
27 import java.lang.reflect.Modifier;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.List;
32 import java.util.HashSet;
33 import java.util.Set;
34 import org.apache.velocity.runtime.log.Log;
35 import org.apache.velocity.tools.ClassUtils;
36 import org.apache.velocity.tools.config.DefaultKey;
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 @DefaultKey("class")
68 public class ClassTool extends SafeConfig
69 {
70 public static final String INSPECT_KEY = "inspect";
71 public static final String SHOW_DEPRECATED_KEY = "showDeprecated";
72
73 protected Log log;
74 protected Class type;
75 protected List<MethodSub> methods;
76 protected List<ConstructorSub> constructors;
77 protected List<FieldSub> fields;
78
79 private boolean showDeprecated = false;
80
81
82
83
84 public ClassTool()
85 {
86 setType(Object.class);
87 }
88
89
90
91
92
93
94 protected ClassTool(ClassTool tool, Class type)
95 {
96 setType(type);
97 if (tool == null)
98 {
99 throw new IllegalArgumentException("parent tool must not be null");
100 }
101
102
103 this.log = tool.log;
104 this.showDeprecated = tool.showDeprecated;
105 setSafeMode(tool.isSafeMode());
106 setLockConfig(tool.isConfigLocked());
107 }
108
109 protected void configure(ValueParser values)
110 {
111 this.log = (Log)values.getValue("log");
112 this.showDeprecated =
113 values.getBoolean(SHOW_DEPRECATED_KEY, showDeprecated);
114
115 String classname = values.getString(INSPECT_KEY);
116 if (classname != null)
117 {
118 setType(toClass(classname));
119 }
120 }
121
122 private Class toClass(String name)
123 {
124 try
125 {
126 return ClassUtils.getClass(name);
127 }
128 catch (Exception e)
129 {
130 if (this.log != null)
131 {
132 this.log.error("Could not load Class for "+name);
133 }
134 return null;
135 }
136 }
137
138 protected void setType(Class type)
139 {
140 if (type == null)
141 {
142 throw new IllegalArgumentException("target type is null or invalid");
143 }
144 this.type = type;
145 }
146
147 protected static boolean isDeprecated(AnnotatedElement element)
148 {
149 return (element.getAnnotation(Deprecated.class) != null);
150 }
151
152
153
154
155 public boolean getShowDeprecated()
156 {
157 return this.showDeprecated;
158 }
159
160
161
162
163 public Class getType()
164 {
165 return this.type;
166 }
167
168
169
170
171
172
173
174 public ClassTool inspect(String name)
175 {
176 if (name == null)
177 {
178 return null;
179 }
180 return inspect(toClass(name));
181 }
182
183
184
185
186
187
188
189 public ClassTool inspect(Object obj)
190 {
191 if (obj == null)
192 {
193 return null;
194 }
195 return inspect(obj.getClass());
196 }
197
198
199
200
201
202
203
204
205 public ClassTool getSuper()
206 {
207 Class sup = getType().getSuperclass();
208 if (sup == null)
209 {
210 return null;
211 }
212 return inspect(sup);
213 }
214
215
216
217
218
219
220
221
222
223
224 public ClassTool inspect(Class type)
225 {
226 if (type == null)
227 {
228 return null;
229 }
230
231
232 ClassTool tool = new ClassTool(this, type);
233 if (isSafeMode() && !tool.isPublic())
234 {
235 return null;
236 }
237 return tool;
238 }
239
240
241
242
243 public String getPackage()
244 {
245 return getType().getPackage().getName();
246 }
247
248
249
250
251
252 public String getName()
253 {
254 return getType().getSimpleName();
255 }
256
257
258
259
260 public String getFullName()
261 {
262 return getType().getName();
263 }
264
265
266
267
268
269
270
271 public boolean supportsNewInstance()
272 {
273 try
274 {
275 type.newInstance();
276 return true;
277 }
278 catch (Exception e)
279 {
280 return false;
281 }
282 }
283
284
285
286
287 public boolean isDeprecated()
288 {
289 return isDeprecated(getType());
290 }
291
292
293
294
295 public boolean isPublic()
296 {
297 return Modifier.isPublic(getType().getModifiers());
298 }
299
300
301
302
303 public boolean isProtected()
304 {
305 return Modifier.isProtected(getType().getModifiers());
306 }
307
308
309
310
311 public boolean isPrivate()
312 {
313 return Modifier.isPrivate(getType().getModifiers());
314 }
315
316
317
318
319
320 public boolean isStatic()
321 {
322 return Modifier.isStatic(getType().getModifiers());
323 }
324
325
326
327
328 public boolean isFinal()
329 {
330 return Modifier.isFinal(getType().getModifiers());
331 }
332
333
334
335
336 public boolean isInterface()
337 {
338 return Modifier.isInterface(getType().getModifiers());
339 }
340
341
342
343
344
345 public boolean isStrict()
346 {
347 return Modifier.isStrict(getType().getModifiers());
348 }
349
350
351
352
353 public boolean isAbstract()
354 {
355 return Modifier.isAbstract(getType().getModifiers());
356 }
357
358
359
360
361
362
363
364
365 public List<MethodSub> getMethods()
366 {
367 if (methods == null)
368 {
369 Method[] declared = getType().getDeclaredMethods();
370 List<MethodSub> subs = new ArrayList<MethodSub>(declared.length);
371 for (Method method : declared)
372 {
373 MethodSub sub = new MethodSub(method);
374 if ((!isSafeMode() || sub.isPublic()) &&
375 (showDeprecated || !sub.isDeprecated()))
376 {
377 subs.add(sub);
378 }
379 }
380 Collections.sort(subs);
381 methods = Collections.unmodifiableList(subs);
382 }
383 return methods;
384 }
385
386
387
388
389
390
391
392
393 public List<ConstructorSub> getConstructors()
394 {
395 if (constructors == null)
396 {
397 Constructor[] declared = getType().getDeclaredConstructors();
398 List<ConstructorSub> subs = new ArrayList<ConstructorSub>(declared.length);
399 for (Constructor constructor : declared)
400 {
401 ConstructorSub sub = new ConstructorSub(constructor);
402 if ((!isSafeMode() || sub.isPublic()) &&
403 (showDeprecated || !sub.isDeprecated()))
404 {
405 subs.add(sub);
406 }
407 }
408 Collections.sort(subs);
409 constructors = Collections.unmodifiableList(subs);
410 }
411 return constructors;
412 }
413
414
415
416
417
418
419
420
421 public List<FieldSub> getFields()
422 {
423 if (fields == null)
424 {
425 Field[] declared = getType().getDeclaredFields();
426 List<FieldSub> subs = new ArrayList<FieldSub>(declared.length);
427 for (Field field : declared)
428 {
429 FieldSub sub = new FieldSub(field);
430 if ((!isSafeMode() || sub.isPublic()) &&
431 (showDeprecated || !sub.isDeprecated()))
432 {
433 subs.add(sub);
434 }
435 }
436 Collections.sort(subs);
437 fields = Collections.unmodifiableList(subs);
438 }
439 return fields;
440 }
441
442
443
444
445
446
447 public Set<Class> getTypes()
448 {
449 Set<Class> types = new HashSet<Class>();
450 for (MethodSub method : getMethods())
451 {
452 if (!isSafeMode() || method.isPublic())
453 {
454 if (!method.isVoid())
455 {
456 addType(types, method.getReturns());
457 }
458 for (Class type : method.getParameters())
459 {
460 addType(types, type);
461 }
462 }
463 }
464 for (ConstructorSub constructor : getConstructors())
465 {
466 if (!isSafeMode() || constructor.isPublic())
467 {
468 for (Class type : constructor.getParameters())
469 {
470 addType(types, type);
471 }
472 }
473 }
474 for (FieldSub field : getFields())
475 {
476 if (!isSafeMode() || field.isPublic())
477 {
478 addType(types, field.getType());
479 }
480 }
481 return types;
482 }
483
484 private void addType(Set<Class> types, Class type)
485 {
486 if (type.isArray())
487 {
488 type = type.getComponentType();
489 }
490 if (!type.isPrimitive())
491 {
492 types.add(type);
493 }
494 }
495
496
497
498
499 public List<Annotation> getAnnotations()
500 {
501 return Arrays.asList(getType().getAnnotations());
502 }
503
504 public String toString()
505 {
506 return getType().toString();
507 }
508
509
510
511
512
513
514
515 public static class FieldSub extends Sub<FieldSub>
516 {
517 protected Field field;
518
519 public FieldSub(Field field)
520 {
521 this.field = field;
522 }
523
524 protected AnnotatedElement getElement()
525 {
526 return field;
527 }
528
529 public String getName()
530 {
531 return field.getName();
532 }
533
534
535
536
537
538 public String getUniqueName()
539 {
540
541 return field.getName();
542 }
543
544
545
546
547 public String getJavadocRef()
548 {
549 return field.getName();
550 }
551
552 public Class getType()
553 {
554 return field.getType();
555 }
556
557
558
559
560
561
562 public Object getStaticValue()
563 {
564 if (isStatic())
565 {
566 try
567 {
568 return field.get(null);
569 }
570 catch(IllegalAccessException iae)
571 {
572
573 }
574 }
575 return null;
576 }
577
578 protected int getModifiers()
579 {
580 return field.getModifiers();
581 }
582
583 protected String getSubType()
584 {
585 return "field";
586 }
587 }
588
589
590
591
592
593 public static class ConstructorSub extends CallableSub<ConstructorSub>
594 {
595 protected Constructor constructor;
596
597 public ConstructorSub(Constructor constructor)
598 {
599 this.constructor = constructor;
600 }
601
602 protected AnnotatedElement getElement()
603 {
604 return constructor;
605 }
606
607 public String getName()
608 {
609 return constructor.getDeclaringClass().getSimpleName();
610 }
611
612 public Class[] getParameters()
613 {
614 return constructor.getParameterTypes();
615 }
616
617
618
619
620
621 public boolean isVarArgs()
622 {
623 return constructor.isVarArgs();
624 }
625
626 protected int getModifiers()
627 {
628 return constructor.getModifiers();
629 }
630
631 protected String getSubType()
632 {
633 return "constructor";
634 }
635 }
636
637
638
639
640
641 public static class MethodSub extends CallableSub<MethodSub>
642 {
643 protected Method method;
644
645 public MethodSub(Method method)
646 {
647 this.method = method;
648 }
649
650 protected AnnotatedElement getElement()
651 {
652 return method;
653 }
654
655 public String getName()
656 {
657 return method.getName();
658 }
659
660
661
662
663
664
665
666 public String getPropertyName()
667 {
668 String name = getName();
669 switch (getParameterCount())
670 {
671 case 0:
672 if (name.startsWith("get") && name.length() > 3)
673 {
674 return uncapitalize(name.substring(3, name.length()));
675 }
676 else if (name.startsWith("is") && name.length() > 2)
677 {
678 return uncapitalize(name.substring(2, name.length()));
679 }
680 break;
681 case 1:
682 if (name.startsWith("set") && name.length() > 3)
683 {
684 return uncapitalize(name.substring(3, name.length()));
685 }
686 default:
687 }
688 return null;
689 }
690
691 private String uncapitalize(String string)
692 {
693 if (string.length() > 1)
694 {
695 StringBuilder out = new StringBuilder(string.length());
696 out.append(string.substring(0,1).toLowerCase());
697 out.append(string.substring(1, string.length()));
698 return out.toString();
699 }
700 else
701 {
702 return string.toLowerCase();
703 }
704 }
705
706
707
708
709
710 public boolean isVarArgs()
711 {
712 return method.isVarArgs();
713 }
714
715
716
717
718 public boolean isVoid()
719 {
720 return (getReturns() == Void.TYPE);
721 }
722
723 public Class getReturns()
724 {
725 return method.getReturnType();
726 }
727
728 public Class[] getParameters()
729 {
730 return method.getParameterTypes();
731 }
732
733 protected int getModifiers()
734 {
735 return method.getModifiers();
736 }
737
738 protected String getSubType()
739 {
740 return "method";
741 }
742 }
743
744 public abstract static class CallableSub<T extends CallableSub> extends Sub<T>
745 {
746 protected String uniqueName;
747 protected String javadocRef;
748 protected String signature;
749
750 public abstract Class[] getParameters();
751 public abstract boolean isVarArgs();
752
753 public boolean takesParameters()
754 {
755 return (getParameterCount() > 0);
756 }
757
758
759
760
761
762 public int getParameterCount()
763 {
764 return getParameters().length;
765 }
766
767
768
769
770
771
772
773
774 public String getUniqueName()
775 {
776 if (uniqueName == null)
777 {
778 Class[] params = getParameters();
779 if (params.length == 0)
780 {
781 uniqueName = getName();
782 }
783 else
784 {
785 StringBuilder out = new StringBuilder(30);
786 out.append(getName());
787 out.append('_');
788 for (int i=0; i < params.length; i++)
789 {
790 Class param = params[i];
791 if (param.isArray())
792 {
793 out.append(param.getComponentType().getSimpleName());
794
795 if (i == params.length - 1 && isVarArgs())
796 {
797 out.append("VarArgs");
798 }
799 else
800 {
801 out.append("Array");
802 }
803 }
804 else
805 {
806 out.append(param.getSimpleName());
807 }
808 }
809 uniqueName = out.toString();
810 }
811 }
812 return uniqueName;
813 }
814
815 public String getSignature()
816 {
817 if (signature == null)
818 {
819 signature = signature(false);
820 }
821 return signature;
822 }
823
824 public String getJavadocRef()
825 {
826 if (javadocRef == null)
827 {
828 javadocRef = signature(true);
829 }
830 return javadocRef;
831 }
832
833 protected String signature(boolean fullNames)
834 {
835 Class[] params = getParameters();
836 if (params.length == 0)
837 {
838 return getName() + "()";
839 }
840 else
841 {
842 StringBuilder out = new StringBuilder(30);
843 out.append(getName());
844 out.append('(');
845 boolean first = true;
846 for (int i=0; i < params.length; i++)
847 {
848 Class param = params[i];
849 if (first)
850 {
851 first = false;
852 }
853 else
854 {
855 out.append(',');
856 }
857 if (param.isArray())
858 {
859 if (fullNames)
860 {
861 out.append(param.getComponentType().getName());
862 }
863 else
864 {
865 out.append(param.getComponentType().getSimpleName());
866 }
867 if (i == params.length - 1 && isVarArgs())
868 {
869 out.append("...");
870 }
871 else
872 {
873 out.append("[]");
874 }
875 }
876 else
877 {
878 if (fullNames)
879 {
880 out.append(param.getName());
881 }
882 else
883 {
884 out.append(param.getSimpleName());
885 }
886 }
887 }
888 out.append(')');
889 return out.toString();
890 }
891 }
892 }
893
894 public abstract static class Sub<T extends Sub> implements Comparable<T>
895 {
896 protected abstract AnnotatedElement getElement();
897
898 protected abstract int getModifiers();
899
900 protected abstract String getSubType();
901
902 public abstract String getName();
903
904 public abstract String getUniqueName();
905
906 public abstract String getJavadocRef();
907
908
909
910
911 public List<Annotation> getAnnotations()
912 {
913 return Arrays.asList(getElement().getAnnotations());
914 }
915
916 public boolean isDeprecated()
917 {
918 return ClassTool.isDeprecated(getElement());
919 }
920
921 public boolean isPublic()
922 {
923 return Modifier.isPublic(getModifiers());
924 }
925
926 public boolean isProtected()
927 {
928 return Modifier.isProtected(getModifiers());
929 }
930
931 public boolean isPrivate()
932 {
933 return Modifier.isPrivate(getModifiers());
934 }
935
936 public boolean isStatic()
937 {
938 return Modifier.isStatic(getModifiers());
939 }
940
941 public boolean isFinal()
942 {
943 return Modifier.isFinal(getModifiers());
944 }
945
946 public boolean isInterface()
947 {
948 return Modifier.isInterface(getModifiers());
949 }
950
951 public boolean isNative()
952 {
953 return Modifier.isNative(getModifiers());
954 }
955
956 public boolean isStrict()
957 {
958 return Modifier.isStrict(getModifiers());
959 }
960
961 public boolean isSynchronized()
962 {
963 return Modifier.isSynchronized(getModifiers());
964 }
965
966 public boolean isTransient()
967 {
968 return Modifier.isTransient(getModifiers());
969 }
970
971 public boolean isVolatile()
972 {
973 return Modifier.isVolatile(getModifiers());
974 }
975
976 public boolean isAbstract()
977 {
978 return Modifier.isAbstract(getModifiers());
979 }
980
981 public int compareTo(T that)
982 {
983 return this.getUniqueName().compareTo(that.getUniqueName());
984 }
985
986 public int hashCode()
987 {
988 return this.getUniqueName().hashCode();
989 }
990
991 public boolean equals(Object obj)
992 {
993 if (obj instanceof Sub)
994 {
995 Sub that = (Sub)obj;
996 return this.getUniqueName().equals(that.getUniqueName());
997 }
998 return false;
999 }
1000
1001 public String toString()
1002 {
1003 return getSubType() + ' ' + getJavadocRef();
1004 }
1005 }
1006
1007 }