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