1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package no.sesat.commons.visitor;
24
25 import java.lang.ref.Reference;
26 import java.lang.reflect.InvocationTargetException;
27 import java.lang.reflect.Method;
28 import java.util.Map;
29 import java.util.concurrent.ConcurrentHashMap;
30
31 import no.sesat.commons.ref.ReferenceMap;
32 import org.apache.log4j.Logger;
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 public abstract class AbstractReflectionVisitor implements Visitor {
48
49
50 public static final String VISIT_METHOD_IMPL = "visitImpl";
51
52 private static final Logger LOG = Logger.getLogger(AbstractReflectionVisitor.class);
53
54 private static final String ERR_CLAUSE_SUBTYPE_NOT_FOUND = "Current visitor implementation does not handle visiting "
55 + "non clause subtypes. Tried to visit object ";
56 private static final String ERR_FAILED_TO_VISIT = "Failed to visit object ";
57 private static final String ERR_FAILED_TO_FIND_VISIT_IMPL_OBJECT = "Failed to find method that exists in this class!!"
58 + "Was trying to visit object ";
59 private static final String DEBUG_LOOKING_AT = "Looking for method "
60 + VISIT_METHOD_IMPL + "(";
61 private static final String TRACE_KEEP_LOOKING = "keep looking";
62 private static final String RB = ")";
63
64 private static final ReferenceMap<Class<? extends Visitor>,Map<Class<? extends Visitable>,Method>> CACHE =
65 new ReferenceMap<Class<? extends Visitor>,Map<Class<? extends Visitable>,Method>>(
66 ReferenceMap.Type.SOFT,
67 new ConcurrentHashMap<Class<? extends Visitor>,Reference<Map<Class<? extends Visitable>,Method>>>());
68
69
70
71
72 public AbstractReflectionVisitor() {
73 }
74
75
76
77
78
79
80
81
82
83
84 public void visit(final Visitable clause) {
85
86 Map<Class<? extends Visitable>,Method> map = CACHE.get(getClass());
87
88 if (null == map) {
89
90 map = new ConcurrentHashMap<Class<? extends Visitable>,Method>();
91
92 CACHE.put(getClass(), map);
93 }
94
95 Method method = map.get(clause.getClass());
96 if (null == method) {
97 method = getMethod(clause.getClass());
98 method.setAccessible(true);
99 map.put(clause.getClass(), method);
100 }
101 assert method.equals(getMethod(clause.getClass()));
102 try {
103 method.invoke(this, new Object[] {clause});
104
105 } catch (IllegalArgumentException ex) {
106 LOG.error(ERR_FAILED_TO_VISIT + clause, ex);
107
108 } catch (InvocationTargetException ex) {
109 LOG.error(ERR_FAILED_TO_VISIT + clause, ex);
110
111
112
113
114 for (Throwable t = ex; t != null; t = t.getCause()) {
115 LOG.error(t.getMessage(), t);
116 }
117
118 } catch (IllegalAccessException ex) {
119 LOG.error(ERR_FAILED_TO_VISIT + clause, ex);
120 }
121 }
122
123
124
125
126
127
128 protected void visitImpl(final Object clause) {
129 throw new IllegalArgumentException(ERR_CLAUSE_SUBTYPE_NOT_FOUND + clause.getClass().getName());
130 }
131
132 private Method getMethod(final Class clauseClass) {
133 Method method = null;
134
135 LOG.trace("getMethod(" + clauseClass.getName() + ")");
136
137
138 Class currClauseClass = clauseClass;
139 while (method == null && currClauseClass != Object.class) {
140 LOG.trace(DEBUG_LOOKING_AT + currClauseClass.getName() + RB);
141
142 method = getDeclaredMethod(currClauseClass);
143
144 if (method == null) {
145 currClauseClass = currClauseClass.getSuperclass();
146 }
147 }
148
149
150
151
152 currClauseClass = clauseClass;
153 while (method == null && currClauseClass != Object.class) {
154
155 method = getMethodFromInterface(currClauseClass);
156 currClauseClass = currClauseClass.getSuperclass();
157 }
158
159
160 if (method == null) {
161
162 method = getDeclaredMethod(Object.class);
163
164 if (method == null) {
165 LOG.fatal(ERR_FAILED_TO_FIND_VISIT_IMPL_OBJECT + clauseClass.getName());
166 }
167
168 }
169 LOG.trace("end getMethod(" + clauseClass.getName() + ")");
170 return method;
171 }
172
173
174
175
176
177 private Method getMethodFromInterface(final Class clauseClass) {
178
179 Method method = null;
180
181 LOG.trace("getMethodFromInterface(" + clauseClass.getName() + ")");
182
183 final Class[] interfaces = clauseClass.getInterfaces();
184 for (int i = 0; i < interfaces.length && method == null; i++) {
185
186 LOG.trace(DEBUG_LOOKING_AT + interfaces[i].getName() + RB);
187
188
189 method = getDeclaredMethod(interfaces[i]);
190
191
192 if (method == null) {
193
194 method = getMethodFromInterface(interfaces[i]);
195 } else {
196
197
198 LOG.trace("Found method accepting <" + interfaces[i].getSimpleName()
199 + "> in " + method.getDeclaringClass().getSimpleName());
200 }
201 }
202
203 LOG.trace("end getMethodFromInterface(" + clauseClass.getName() + ")");
204 return method;
205 }
206
207
208
209
210
211
212 private Method getDeclaredMethod(final Class clauseClass) {
213
214 for (Class cls = getClass();; cls = cls.getSuperclass()) {
215 if (cls != null) {
216 try {
217 return cls.getDeclaredMethod(VISIT_METHOD_IMPL, new Class[] {clauseClass});
218
219 } catch (NoSuchMethodException e) {
220 LOG.trace(TRACE_KEEP_LOOKING);
221 }
222 } else {
223 return null;
224 }
225 }
226 }
227
228 }