View Javadoc
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 }