001    package com.softwarementors.extjs.djn.scanner;
002    
003    import java.lang.reflect.Method;
004    import java.util.ArrayList;
005    import java.util.Collections;
006    import java.util.HashMap;
007    import java.util.List;
008    import java.util.Map;
009    
010    import org.apache.log4j.Logger;
011    
012    import com.softwarementors.extjs.djn.ClassUtils;
013    import com.softwarementors.extjs.djn.api.RegisteredAction;
014    import com.softwarementors.extjs.djn.api.RegisteredApi;
015    import com.softwarementors.extjs.djn.api.RegisteredMethod;
016    import com.softwarementors.extjs.djn.api.RegisteredPollMethod;
017    import com.softwarementors.extjs.djn.api.Registry;
018    import com.softwarementors.extjs.djn.config.ApiConfiguration;
019    import com.softwarementors.extjs.djn.config.ApiConfigurationException;
020    import com.softwarementors.extjs.djn.config.annotations.DirectAction;
021    import com.softwarementors.extjs.djn.config.annotations.DirectFormPostMethod;
022    import com.softwarementors.extjs.djn.config.annotations.DirectMethod;
023    import com.softwarementors.extjs.djn.config.annotations.DirectPollMethod;
024    
025    public class Scanner {
026    
027      private static final Logger logger = Logger.getLogger( Scanner.class);
028      
029      private Registry registry;
030      
031      public Scanner(Registry registry) {
032        assert registry != null;
033        
034        this.registry = registry;
035      }
036      
037      public void scanAndRegisterApiConfigurations(List<ApiConfiguration> apiConfigurations) {
038        assert apiConfigurations != null;
039        
040        for( ApiConfiguration api: apiConfigurations ) {
041          scanAndRegisterApiConfiguration(api);
042        }
043      }
044    
045      /* package */ void scanAndRegisterApiConfiguration(ApiConfiguration api) {
046        assert api != null;
047        
048        if( this.registry.hasApi( api.getName() )) {
049          ApiConfigurationException ex = ApiConfigurationException.forApiAlreadyRegistered( api.getName());
050          logger.fatal( ex.getMessage(), ex );
051          throw ex;
052        }
053        
054        RegisteredApi registeredApi = this.registry.addApi( api.getName(), api.getFullApiFileName(), api.getApiNamespace(), api.getActionsNamespace());
055        
056        List<Class<?>> actionClasses = api.getClasses();    
057        for( Class<?> cls : actionClasses ) {
058          assert cls != null;
059          scanAndRegisterActionClass(registeredApi, cls );
060        }
061      }
062    
063      public void scanAndRegisterActionClass( RegisteredApi api, Class<?> actionClass ) {
064        assert api != null;
065        assert actionClass != null;
066        
067        Map<Class<?>, RegisteredAction> actionsByClass = new HashMap<Class<?>, RegisteredAction>();
068    
069        if( actionsByClass.containsKey( actionClass ) ) {
070          ApiConfigurationException ex = ApiConfigurationException.forClassAlreadyRegisteredAsAction(actionClass);
071          logger.fatal( ex.getMessage(), ex );
072          throw ex;
073        }
074        
075        if( logger.isDebugEnabled() ) {
076          logger.debug( "Scanning Java class: " + actionClass.getName() );
077        }
078        
079        RegisteredAction action = createActionFromJavaClass(api, actionClass);
080        
081        scanAndRegisterActionClass(action);
082        actionsByClass.put( actionClass, action);
083      }
084      
085      private RegisteredAction createActionFromJavaClass(RegisteredApi api, Class<?> actionClass) {
086        assert api != null;
087        assert actionClass != null;
088        
089        DirectAction actionAnnotation = actionClass.getAnnotation(DirectAction.class);
090        String actionName = "";
091        if( actionAnnotation != null ) {
092          actionName = actionAnnotation.action();      
093        }
094        
095        if( actionName.equals("")) {
096          actionName = ClassUtils.getSimpleName(actionClass);
097        }
098        
099        if( this.registry.hasAction( actionName ) ) {
100          RegisteredAction existingAction = this.registry.getAction( actionName );
101          ApiConfigurationException ex = ApiConfigurationException.forActionAlreadyRegistered(actionName, actionClass, existingAction.getActionClass());
102          logger.fatal( ex.getMessage(), ex );
103          throw ex;
104        }
105        RegisteredAction action = api.addAction( actionClass, actionName );
106        
107        return action;
108      }
109      
110      private static final String POLL_METHOD_NAME_PREFIX = "djnpoll_";
111      private static final String FORM_POST_METHOD_NAME_PREFIX = "djnform_";
112      private static final String STANDARD_METHOD_NAME_PREFIX = "djn_";
113    
114      private void scanAndRegisterActionClass(RegisteredAction action) {
115        assert action != null;
116            
117        RegisteredApi api = action.getApi();
118        
119        // *All* methods are candidates, including those in base classes, 
120        // even if the base class does not have a DirectAction annotation!
121        List<Method> allMethods = new ArrayList<Method>();
122        Class<?> cls = action.getActionClass();
123        while( cls != null ) {
124          Method[] methods = cls.getDeclaredMethods(); // Get private, protected and other methods!
125          Collections.addAll( allMethods, methods );
126          cls = cls.getSuperclass();
127        }
128        
129        for( Method method : allMethods ) {
130          // Check if the kind of direct method -if any
131          DirectMethod methodAnnotation = method.getAnnotation(DirectMethod.class);
132          boolean isStandardMethod = methodAnnotation != null;
133          if( !isStandardMethod ) {
134            isStandardMethod = method.getName().startsWith(STANDARD_METHOD_NAME_PREFIX);
135          }
136          
137          DirectFormPostMethod postMethodAnnotation = method.getAnnotation(DirectFormPostMethod.class);
138          boolean isFormPostMethod = postMethodAnnotation != null;
139          if( !isFormPostMethod ) {
140            isFormPostMethod = method.getName().startsWith(FORM_POST_METHOD_NAME_PREFIX);  
141          }
142          
143          DirectPollMethod pollMethodAnnotation = method.getAnnotation(DirectPollMethod.class);
144          boolean isPollMethod = pollMethodAnnotation != null;
145          if( !isPollMethod ) {
146            isPollMethod = method.getName().startsWith( POLL_METHOD_NAME_PREFIX );
147          }
148          
149          // Check that a method is just of only one kind of method
150          if( isStandardMethod && isFormPostMethod ) {
151            ApiConfigurationException ex = ApiConfigurationException.forMethodCantBeStandardAndFormPostMethodAtTheSameTime(action, method);
152            logger.fatal( ex.getMessage(), ex );
153            throw ex;
154          }
155          if( (methodAnnotation != null || postMethodAnnotation != null) && isPollMethod) {
156            ApiConfigurationException ex = ApiConfigurationException.forPollMethodCantBeStandardOrFormPostMethodAtTheSameTime(action, method);
157            logger.fatal( ex.getMessage(), ex );
158            throw ex;
159          }
160    
161          // Process standard and form post methods together, as they are very similar
162          if( isStandardMethod || isFormPostMethod) {
163            
164            String methodName = "";
165            if( isStandardMethod ) {
166              methodName = getStandardMethodName(method, methodAnnotation);
167            }
168            else {
169              methodName = getFormPostMethodName( method, postMethodAnnotation);
170            }        
171            if( action.hasMethod(methodName)  ) {
172              ApiConfigurationException ex = ApiConfigurationException.forMethodAlreadyRegisteredInAction(methodName, action.getName());
173              logger.fatal( ex.getMessage(), ex );
174              throw ex;
175            }
176            
177            if( isFormPostMethod && !RegisteredMethod.isValidFormHandlingMethod(method)) {
178              ApiConfigurationException ex = ApiConfigurationException.forMethodHasWrongParametersForAFormHandler( action.getName(), methodName );
179              logger.fatal( ex.getMessage(), ex );
180              throw ex;
181            }
182    
183    
184            action.addMethod( methodName, method, isFormPostMethod );
185          }
186                
187          // Process "poll" method
188          if( isPollMethod ) {
189            createPollMethod(api, method, pollMethodAnnotation);
190          }
191        }
192      }
193    
194      private String getFormPostMethodName(Method method, DirectFormPostMethod postMethodAnnotation) {
195        String methodName = "";
196        if( postMethodAnnotation != null ) {
197          methodName = postMethodAnnotation.method();
198        }
199        if( methodName.equals("" )) {
200          methodName = method.getName(); 
201        }
202        if( methodName.startsWith(FORM_POST_METHOD_NAME_PREFIX)) {
203          methodName = method.getName().substring(FORM_POST_METHOD_NAME_PREFIX.length());
204        }
205        return methodName;
206      }
207    
208      private String getStandardMethodName(Method method, DirectMethod methodAnnotation) {
209        String methodName = "";
210        if( methodAnnotation != null ) {
211          methodName = methodAnnotation.method();
212        }
213        if( methodName.equals("" )) {
214          methodName = method.getName(); 
215        }
216        if( methodName.startsWith(STANDARD_METHOD_NAME_PREFIX)) {
217          methodName = method.getName().substring(STANDARD_METHOD_NAME_PREFIX.length());
218        }
219        return methodName;
220      }
221    
222      private RegisteredPollMethod createPollMethod(RegisteredApi api, Method method, DirectPollMethod pollMethodAnnotation) {
223        assert api != null;
224        assert method != null;
225        
226        String eventName = getEventName(method, pollMethodAnnotation);
227            
228        if( this.registry.hasPollMethod(eventName)) {
229          ApiConfigurationException ex = ApiConfigurationException.forPollEventAlreadyRegistered( eventName );
230          logger.fatal( ex.getMessage(), ex );
231          throw ex;
232        }
233        
234        if( !RegisteredPollMethod.isValidPollMethod(method)) {
235          ApiConfigurationException ex = ApiConfigurationException.forMethodHasWrongParametersForAPollHandler( method );
236          logger.fatal( ex.getMessage(), ex );
237          throw ex;
238        }
239        
240        RegisteredPollMethod poll = api.addPollMethod( eventName, method);
241        return poll;    
242      }
243    
244      private String getEventName(Method method, DirectPollMethod pollMethodAnnotation) {
245        assert method != null;
246        
247        String eventName = "";
248        if( pollMethodAnnotation != null ) {
249          eventName = pollMethodAnnotation.event();
250        }
251        if( eventName.equals("")) {
252          eventName = method.getName();
253        }
254        if( eventName.startsWith(POLL_METHOD_NAME_PREFIX)) {
255          eventName = method.getName().substring(POLL_METHOD_NAME_PREFIX.length());
256        }
257        return eventName;
258      }
259    
260    }