View Javadoc

1   package org.apache.commons.digester3.annotations;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import static org.apache.commons.digester3.annotations.utils.AnnotationUtils.getAnnotationsArrayValue;
23  
24  import java.lang.annotation.Annotation;
25  import java.lang.reflect.AnnotatedElement;
26  import java.lang.reflect.Constructor;
27  import java.lang.reflect.Field;
28  import java.lang.reflect.Method;
29  import java.security.AccessController;
30  import java.security.PrivilegedAction;
31  
32  import org.apache.commons.digester3.Rule;
33  import org.apache.commons.digester3.annotations.reflect.MethodArgument;
34  import org.apache.commons.digester3.binder.AbstractRulesModule;
35  
36  /**
37   * {@link org.apache.commons.digester3.binder.RulesModule} implementation that allows loading rules from
38   * annotated classes.
39   *
40   * @since 3.0
41   */
42  public abstract class FromAnnotationsRuleModule
43      extends AbstractRulesModule
44  {
45  
46      private static final String JAVA_PACKAGE = "java";
47  
48      private static final AnnotationHandlerFactory DEFAULT_HANDLER_FACTORY = new DefaultAnnotationHandlerFactory();
49  
50      private AnnotationHandlerFactory annotationHandlerFactory = DEFAULT_HANDLER_FACTORY;
51  
52      private WithMemoryRulesBinder rulesBinder;
53  
54      /**
55       * {@inheritDoc}
56       */
57      @Override
58      protected final void configure()
59      {
60          if ( rulesBinder == null )
61          {
62              rulesBinder = new WithMemoryRulesBinder( rulesBinder() );
63          }
64  
65          try
66          {
67              configureRules();
68          }
69          finally
70          {
71              rulesBinder = null;
72          }
73      }
74  
75      /**
76       * Configures a {@link org.apache.commons.digester3.binder.RulesBinder} via the exposed methods.
77       */
78      protected abstract void configureRules();
79  
80      /**
81       * Allows users plug a different {@link AnnotationHandlerFactory} to create {@link AnnotationHandler} instances.
82       *
83       * @param annotationHandlerFactory A custom {@link AnnotationHandlerFactory} to create
84       *        {@link AnnotationHandler} instances
85       */
86      protected final void useAnnotationHandlerFactory( AnnotationHandlerFactory annotationHandlerFactory )
87      {
88          if ( annotationHandlerFactory == null )
89          {
90              throw new IllegalArgumentException( "Argument 'annotationHandlerFactory' must be not null" );
91          }
92  
93          this.annotationHandlerFactory = annotationHandlerFactory;
94      }
95  
96      /**
97       * Allows users to switch back to the default {@link AnnotationHandlerFactory} implementation.
98       */
99      protected final void useDefaultAnnotationHandlerFactory()
100     {
101         useAnnotationHandlerFactory( DEFAULT_HANDLER_FACTORY );
102     }
103 
104     /**
105      * Scan the input Class, looking for Digester rules expressed via annotations, and binds them.
106      *
107      * @param type the type has to be analyzed
108      * @see DigesterRule
109      */
110     protected final void bindRulesFrom( final Class<?> type )
111     {
112         if ( type == null || type.getPackage().getName().startsWith( JAVA_PACKAGE )
113             || rulesBinder.isAlreadyBound( type ) )
114         {
115             return;
116         }
117 
118         // TYPE
119         visitElements( type );
120 
121         if ( !type.isInterface() )
122         {
123             // CONSTRUCTOR
124             visitElements( new PrivilegedAction<Constructor<?>[]>()
125             {
126                 public Constructor<?>[] run()
127                 {
128                     return type.getDeclaredConstructors();
129                 }
130             } );
131 
132             // FIELD
133             visitElements( new PrivilegedAction<Field[]>()
134             {
135                 public Field[] run()
136                 {
137                     return type.getDeclaredFields();
138                 }
139             } );
140         }
141 
142         // METHOD
143         visitElements( new PrivilegedAction<Method[]>()
144         {
145             public Method[] run()
146             {
147                 return type.getDeclaredMethods();
148             }
149         } );
150 
151         rulesBinder.markAsBound( type );
152         bindRulesFrom( type.getSuperclass() );
153     }
154 
155     /**
156      *
157      *
158      * @param <AE>
159      * @param action
160      */
161     private <AE extends AnnotatedElement> void visitElements( PrivilegedAction<AE[]> action )
162     {
163         AE[] annotatedElements = null;
164         if ( System.getSecurityManager() != null )
165         {
166             annotatedElements = AccessController.doPrivileged( action );
167         }
168         else
169         {
170             annotatedElements = action.run();
171         }
172         visitElements( annotatedElements );
173     }
174 
175     /**
176      *
177      *
178      * @param annotatedElements
179      */
180     private void visitElements( AnnotatedElement... annotatedElements )
181     {
182         for ( AnnotatedElement element : annotatedElements )
183         {
184             for ( Annotation annotation : element.getAnnotations() )
185             {
186                 handle( annotation, element );
187             }
188 
189             if ( element instanceof Constructor || element instanceof Method )
190             {
191                 Annotation[][] parameterAnnotations;
192                 Class<?>[] parameterTypes;
193 
194                 if ( element instanceof Constructor )
195                 {
196                     // constructor args
197                     Constructor<?> construcotr = (Constructor<?>) element;
198                     parameterAnnotations = construcotr.getParameterAnnotations();
199                     parameterTypes = construcotr.getParameterTypes();
200                 }
201                 else
202                 {
203                     // method args
204                     Method method = (Method) element;
205                     parameterAnnotations = method.getParameterAnnotations();
206                     parameterTypes = method.getParameterTypes();
207                 }
208 
209                 for ( int i = 0; i < parameterTypes.length; i++ )
210                 {
211                     visitElements( new MethodArgument( i, parameterTypes[i], parameterAnnotations[i] ) );
212                 }
213             }
214         }
215     }
216 
217     /**
218      * Handles the current visited element and related annotation, invoking the
219      * right handler putting the rule provider in the rule set.
220      *
221      * @param annotation the current visited annotation.
222      * @param element the current visited element.
223      */
224     @SuppressWarnings( "unchecked" )
225     private <A extends Annotation, E extends AnnotatedElement, R extends Rule> void handle( A annotation, E element )
226     {
227         Class<?> annotationType = annotation.annotationType();
228 
229         // check if it is one of the @*.List annotation
230         if ( annotationType.isAnnotationPresent( DigesterRuleList.class ) )
231         {
232             Annotation[] annotations = getAnnotationsArrayValue( annotation );
233             if ( annotations != null && annotations.length > 0 )
234             {
235                 // if it is an annotations array, process them
236                 for ( Annotation ptr : annotations )
237                 {
238                     handle( ptr, element );
239                 }
240             }
241         }
242         else if ( annotationType.isAnnotationPresent( DigesterRule.class ) )
243         {
244             DigesterRule digesterRule = annotationType.getAnnotation( DigesterRule.class );
245 
246             // the default behavior if the handler is not specified
247             Class<? extends AnnotationHandler<Annotation, AnnotatedElement>> handlerType =
248                 (Class<? extends AnnotationHandler<Annotation, AnnotatedElement>>) digesterRule.handledBy();
249             try
250             {
251                 AnnotationHandler<Annotation, AnnotatedElement> handler =
252                     annotationHandlerFactory.newInstance( handlerType );
253 
254                 // run!
255                 handler.handle( annotation, element, this.rulesBinder );
256             }
257             catch ( Exception e )
258             {
259                 rulesBinder.addError( e );
260             }
261         }
262     }
263 
264 }