1 //----------------------------------------------------------------------
2 //
3 // PerfectJPattern: "Design patterns are good but components are better!"
4 // AbstractVisitor.java Copyright (c) 2009 Giovanni Azua Garcia
5 // bravegag@hotmail.com
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 3
10 // of the License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, see <http://www.gnu.org/licenses/>.
19 //
20 //----------------------------------------------------------------------
21 package org.perfectjpattern.core.behavioral.visitor;
22
23 import java.lang.reflect.*;
24 import java.util.*;
25
26 import org.apache.commons.lang.*;
27 import org.perfectjpattern.core.api.behavioral.visitor.*;
28 import org.perfectjpattern.core.extras.delegate.*;
29
30
31 /**
32 * Reusable abstract base implementation of <code>IVisitor</code> interface.
33 * <br/>
34 *
35 * @see IVisitor
36 *
37 * @param <E> Element type that this <code>IVisitor</code> can visit.
38 *
39 * @author <a href="mailto:bravegag@hotmail.com">Giovanni Azua</a>
40 * @version $Revision: 1.0 $ $Date: Jul 1, 2007 7:02:55 AM $
41 */
42 public abstract
43 class AbstractVisitor<E>
44 implements IVisitor<E>
45 {
46 //------------------------------------------------------------------------
47 // public
48 //------------------------------------------------------------------------
49 /**
50 * {@inheritDoc}
51 */
52 @SuppressWarnings("unchecked")
53 public final void
54 visit(E anElement)
55 {
56 Validate.notNull(anElement, "'anElement' must not be null");
57
58 String myElementClassName = anElement.getClass().getName();
59
60 // do we have it already?
61 if (!theLookup.containsKey(myElementClassName))
62 {
63 // lookup the right implementation
64 Class myElementType = anElement.getClass();
65 Class myVisitorType = this.getClass();
66
67 Method myMethod = findVisitorMethod(myElementType,
68 myVisitorType, theDelegator);
69
70 // any matching methods found?
71 if (myMethod != null)
72 {
73 theLookup.put(myElementClassName, theDelegator.build(
74 this, myMethod.getName()));
75 }
76 }
77
78 // if a matching visit method was found then visit
79 if (theLookup.containsKey(myElementClassName))
80 {
81 // do the visiting
82 theLookup.get(myElementClassName).visit(anElement);
83 }
84 }
85
86 //------------------------------------------------------------------------
87 /**
88 * Reusable implementation of the visit method that may be reused by
89 * implementors of <code>IVisitor</code> that for any reason can not
90 * subclass <code>AbstractVisitor</code>.
91 *
92 * @param <E> Type of Element to visit.
93 *
94 * @param aVisitor Concrete IVisitor instance.
95 * @param anElements Concrete IElement instances to visit.
96 * @throws IllegalArgumentException 'aVisitor' must not be null.
97 * @throws IllegalArgumentException 'anElements' must not be null.
98 * @throws IllegalArgumentException 'anElements' must not be empty.
99 */
100 @SuppressWarnings("unchecked")
101 public static <E> void
102 reusableVisit(IVisitor<E> aVisitor, E... anElements)
103 throws IllegalArgumentException
104 {
105 Validate.notNull(aVisitor, "'aVisitor' must not be null");
106 Validate.notNull(anElements, "'anElements' must not be null");
107 Validate.notEmpty(anElements, "'anElements' must not be empty.");
108
109 for (E myElement : anElements)
110 {
111 // lookup the right implementation
112 Class myElementType = myElement.getClass();
113 Class myVisitorType = aVisitor.getClass();
114
115 VisitorDelegator myDelegator = new VisitorDelegator(IVisitor.class);
116
117 Method myMethod = findVisitorMethod(myElementType, myVisitorType,
118 myDelegator);
119
120 // any matching methods found?
121 if (myMethod != null)
122 {
123 IVisitor<E> myRealVisitor =
124 (IVisitor) myDelegator.build(aVisitor, myMethod.getName());
125
126 // do the visiting
127 myRealVisitor.visit(myElement);
128 }
129 }
130 }
131
132 //------------------------------------------------------------------------
133 // inner classes
134 //------------------------------------------------------------------------
135 /**
136 * Subclass implementation of Delegator redefining specific methods.
137 */
138 @SuppressWarnings("unchecked")
139 private static
140 class VisitorDelegator<E>
141 extends Delegator<IVisitor<E>>
142 {
143 //--------------------------------------------------------------------
144 /**
145 * Constructs a <code>VisitorDelegator</code> instance from a Visitor
146 * that visits Elements of Type E.
147 *
148 * @param aClass Class type of Visitor.
149 */
150 public
151 VisitorDelegator(Class<IVisitor<E>> aClass)
152 {
153 super(aClass);
154 }
155
156 //--------------------------------------------------------------------
157 /**
158 * {@inheritDoc}
159 */
160 @Override
161 protected boolean
162 isSuitableMethod(Method aTestMethod, Class aReturnClass,
163 Class... anArguments)
164 {
165 Class[] myMethodArguments = aTestMethod.getParameterTypes();
166 for (int i = 0; i < myMethodArguments.length; i++)
167 {
168 Class myArgument = myMethodArguments[i];
169
170 if (!anArguments[i].isAssignableFrom(myArgument))
171 {
172 return false;
173 }
174
175 }
176
177 isValidReturn(aTestMethod, aReturnClass);
178
179 return true;
180 }
181
182 //--------------------------------------------------------------------
183 /**
184 * {@inheritDoc}
185 */
186 @Override
187 protected boolean
188 isValidReturn(Method aTestMethod, Class aReturnClass)
189 {
190 return (aReturnClass == null
191 || aTestMethod.getReturnType() == aReturnClass
192 || aTestMethod.getReturnType().equals(aReturnClass)
193 || aTestMethod.getReturnType().isAssignableFrom(aReturnClass));
194 }
195
196 //--------------------------------------------------------------------
197 @Override
198 public Class
199 getReturn()
200 {
201 return super.getReturn();
202 }
203 }
204
205 //------------------------------------------------------------------------
206 // private
207 //------------------------------------------------------------------------
208 /**
209 * Returns Visitor method matching the specified Element argument type,
210 * null if the requested Method is not found.
211 *
212 * @param anElementType Class that corresponds to the concrete Element type.
213 * @param aVisitorType Class that corresponds to the concrete Visitor type.
214 * @param aDelegator Delegator implementation.
215 * @return Visitor method matching the specified Element argument type,
216 * null if the requested Method is not found.
217 */
218 @SuppressWarnings("unchecked")
219 private static Method
220 findVisitorMethod(Class anElementType, Class aVisitorType,
221 VisitorDelegator aDelegator)
222 {
223 Class myReturnClass = aDelegator.getReturn();
224 Method myResult = null;
225 for (Method myMethod : aVisitorType.getMethods())
226 {
227 if (aDelegator.isValidReturn(myMethod, myReturnClass)
228 && Modifier.isPublic(myMethod.getModifiers())
229 && myMethod.getParameterTypes().length == 1
230 && myMethod.getParameterTypes()[0].equals(anElementType))
231 {
232 myResult = myMethod;
233 break;
234 }
235 }
236
237 return myResult;
238 }
239
240 //------------------------------------------------------------------------
241 // members
242 //------------------------------------------------------------------------
243 /**
244 * <code>Map</code> of <code>IVisitor</code> instances keyed by
245 * <code>IElement</code>-based Class names.
246 */
247 private final Map<String, IVisitor<E>> theLookup = new HashMap<String,
248 IVisitor<E>>();
249
250 /**
251 * Special subtype of <code>Delegator<code> that delegates only to
252 * subtypes of the <code>IVisitor</code> interface.
253 */
254 @SuppressWarnings("unchecked")
255 private final VisitorDelegator<E> theDelegator = new VisitorDelegator(
256 IVisitor.class);
257 }