1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springmodules.validation.valang.javascript;
18
19 import java.lang.reflect.Method;
20 import java.lang.reflect.Modifier;
21 import java.util.LinkedList;
22
23 import org.apache.commons.logging.Log;
24 import org.apache.commons.logging.LogFactory;
25
26 import org.springframework.util.Assert;
27 import org.springframework.util.CachingMapDecorator;
28 import org.springframework.util.ReflectionUtils;
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public class ReflectiveVisitorHelper {
59
60 private static final String VISIT_METHOD = "visit";
61
62 private static final String VISIT_NULL = "visitNull";
63
64 private static final Log logger = LogFactory.getLog(ReflectiveVisitorHelper.class);
65
66
67 private final CachingMapDecorator visitorClassVisitMethods = new CachingMapDecorator() {
68 public Object create(Object key) {
69 return new ClassVisitMethods((Class) key);
70 }
71 };
72
73
74
75
76
77
78
79
80
81 public Object invokeVisit(Object visitor, Object argument) {
82 Assert.notNull(visitor, "The visitor to visit is required");
83
84 Method method = getMethod(visitor.getClass(), argument);
85 if (method == null) {
86 if (logger.isWarnEnabled()) {
87 logger.warn("No method found by reflection for visitor class [" + visitor.getClass().getName()
88 + "] and argument of type [" + (argument != null ? argument.getClass().getName() : "") + "]");
89 }
90 return null;
91 }
92 try {
93 Object[] args = null;
94 if (argument != null) {
95 args = new Object[] {argument};
96 }
97 if (!Modifier.isPublic(method.getModifiers())) {
98 method.setAccessible(true);
99 }
100 return method.invoke(visitor, args);
101 }
102 catch (Exception ex) {
103 ReflectionUtils.handleReflectionException(ex);
104 throw new IllegalStateException("Should never get here");
105 }
106 }
107
108
109
110
111
112 private Method getMethod(Class visitorClass, Object argument) {
113 ClassVisitMethods visitMethods = (ClassVisitMethods) this.visitorClassVisitMethods.get(visitorClass);
114 return visitMethods.getVisitMethod(argument != null ? argument.getClass() : null);
115 }
116
117
118
119
120
121 private static class ClassVisitMethods {
122
123 private final Class visitorClass;
124
125 private final CachingMapDecorator visitMethodCache = new CachingMapDecorator() {
126 public Object create(Object argumentClazz) {
127 if (argumentClazz == null) {
128 return findNullVisitorMethod();
129 }
130 Method method = findVisitMethod((Class) argumentClazz);
131 if (method == null) {
132 method = findDefaultVisitMethod();
133 }
134 return method;
135 }
136 };
137
138 public ClassVisitMethods(Class visitorClass) {
139 this.visitorClass = visitorClass;
140 }
141
142 private Method findNullVisitorMethod() {
143 for (Class clazz = this.visitorClass; clazz != null; clazz = clazz.getSuperclass()) {
144 try {
145 return clazz.getDeclaredMethod(VISIT_NULL, (Class[]) null);
146 }
147 catch (NoSuchMethodException ex) {
148 }
149 }
150 return findDefaultVisitMethod();
151 }
152
153 private Method findDefaultVisitMethod() {
154 final Class[] args = {Object.class};
155 for (Class clazz = this.visitorClass; clazz != null; clazz = clazz.getSuperclass()) {
156 try {
157 return clazz.getDeclaredMethod(VISIT_METHOD, args);
158 }
159 catch (NoSuchMethodException ex) {
160 }
161 }
162 if (logger.isWarnEnabled()) {
163 logger.warn("No default '" + VISIT_METHOD + "' method found. Returning <null>.");
164 }
165 return null;
166 }
167
168
169
170
171 private Method getVisitMethod(Class argumentClass) {
172 return (Method) this.visitMethodCache.get(argumentClass);
173 }
174
175
176
177
178 private Method findVisitMethod(Class rootArgumentType) {
179 if (rootArgumentType == Object.class) {
180 return null;
181 }
182 LinkedList classQueue = new LinkedList();
183 classQueue.addFirst(rootArgumentType);
184
185 while (!classQueue.isEmpty()) {
186 Class argumentType = (Class) classQueue.removeLast();
187
188
189 try {
190 if (logger.isTraceEnabled()) {
191 logger.trace("Looking for method " + VISIT_METHOD + "(" + argumentType + ")");
192 }
193 return findVisitMethod(this.visitorClass, argumentType);
194 }
195 catch (NoSuchMethodException e) {
196
197 if (!argumentType.isInterface() && (argumentType.getSuperclass() != Object.class)) {
198 classQueue.addFirst(argumentType.getSuperclass());
199 }
200
201 Class[] interfaces = argumentType.getInterfaces();
202 for (int i = 0; i < interfaces.length; i++) {
203 classQueue.addFirst(interfaces[i]);
204 }
205 }
206 }
207
208 return findDefaultVisitMethod();
209 }
210
211 private Method findVisitMethod(Class visitorClass, Class argumentType) throws NoSuchMethodException {
212 try {
213 return visitorClass.getDeclaredMethod(VISIT_METHOD, new Class[] {argumentType});
214 }
215 catch (NoSuchMethodException ex) {
216
217 if (visitorClass.getSuperclass() != Object.class) {
218 return findVisitMethod(visitorClass.getSuperclass(), argumentType);
219 }
220 else {
221 throw ex;
222 }
223 }
224 }
225 }
226
227 }