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 * Helper implementation for a reflective visitor.
32 * Mainly for internal use within the framework.
33 *
34 * <p>To use, call <code>invokeVisit</code>, passing a Visitor object
35 * and the data argument to accept (double-dispatch). For example:
36 *
37 * <pre>
38 * public String styleValue(Object value) {
39 * reflectiveVistorSupport.invokeVisit(this, value)
40 * }
41 *
42 * // visit call back will be invoked via reflection
43 * String visit(<valueType> arg) {
44 * // process argument of type <valueType>
45 * }
46 * </pre>
47 *
48 * See the {@link org.springframework.core.style.DefaultValueStyler} class
49 * for a concrete usage of this visitor helper.
50 *
51 * <p><strong>Note</strong>: Copied from Spring 2.5 to avoid redoing work using this.
52 *
53 * @author Keith Donald
54 * @author Juergen Hoeller
55 * @since 1.2.2
56 * @deprecated as of Spring 2.5, to be removed in Spring 3.0
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 * Use reflection to call the appropriate <code>visit</code> method
76 * on the provided visitor, passing in the specified argument.
77 * @param visitor the visitor encapsulating the logic to process the argument
78 * @param argument the argument to dispatch
79 * @throws IllegalArgumentException if the visitor parameter is null
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 * Determines the most appropriate visit method for the
110 * given visitor class and argument.
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 * Internal class caching visitor methods by argument class.
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 * Gets a cached visitor method for the specified argument type.
170 */
171 private Method getVisitMethod(Class argumentClass) {
172 return (Method) this.visitMethodCache.get(argumentClass);
173 }
174
175 /**
176 * Traverses class hierarchy looking for applicable visit() method.
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 }