001 package org.apache.commons.digester3.annotations;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import static org.apache.commons.digester3.annotations.utils.AnnotationUtils.getAnnotationsArrayValue;
023
024 import java.lang.annotation.Annotation;
025 import java.lang.reflect.AnnotatedElement;
026 import java.lang.reflect.Constructor;
027 import java.lang.reflect.Field;
028 import java.lang.reflect.Method;
029 import java.security.AccessController;
030 import java.security.PrivilegedAction;
031
032 import org.apache.commons.digester3.Rule;
033 import org.apache.commons.digester3.annotations.reflect.MethodArgument;
034 import org.apache.commons.digester3.binder.AbstractRulesModule;
035
036 /**
037 * {@link org.apache.commons.digester3.binder.RulesModule} implementation that allows loading rules from
038 * annotated classes.
039 *
040 * @since 3.0
041 */
042 public abstract class FromAnnotationsRuleModule
043 extends AbstractRulesModule
044 {
045
046 private static final String JAVA_PACKAGE = "java";
047
048 private static final AnnotationHandlerFactory DEFAULT_HANDLER_FACTORY = new DefaultAnnotationHandlerFactory();
049
050 private AnnotationHandlerFactory annotationHandlerFactory = DEFAULT_HANDLER_FACTORY;
051
052 private WithMemoryRulesBinder rulesBinder;
053
054 /**
055 * {@inheritDoc}
056 */
057 @Override
058 protected final void configure()
059 {
060 if ( rulesBinder == null )
061 {
062 rulesBinder = new WithMemoryRulesBinder( rulesBinder() );
063 }
064
065 try
066 {
067 configureRules();
068 }
069 finally
070 {
071 rulesBinder = null;
072 }
073 }
074
075 /**
076 * Configures a {@link org.apache.commons.digester3.binder.RulesBinder} via the exposed methods.
077 */
078 protected abstract void configureRules();
079
080 /**
081 * Allows users plug a different {@link AnnotationHandlerFactory} to create {@link AnnotationHandler} instances.
082 *
083 * @param annotationHandlerFactory A custom {@link AnnotationHandlerFactory} to create
084 * {@link AnnotationHandler} instances
085 */
086 protected final void useAnnotationHandlerFactory( AnnotationHandlerFactory annotationHandlerFactory )
087 {
088 if ( annotationHandlerFactory == null )
089 {
090 throw new IllegalArgumentException( "Argument 'annotationHandlerFactory' must be not null" );
091 }
092
093 this.annotationHandlerFactory = annotationHandlerFactory;
094 }
095
096 /**
097 * Allows users to switch back to the default {@link AnnotationHandlerFactory} implementation.
098 */
099 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 }