package org.apache.commons.digester3.annotations;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import static org.apache.commons.digester3.annotations.utils.AnnotationUtils.getAnnotationsArrayValue;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;

import org.apache.commons.digester3.Rule;
import org.apache.commons.digester3.annotations.reflect.MethodArgument;
import org.apache.commons.digester3.binder.AbstractRulesModule;

RulesModule implementation that allows loading rules from annotated classes.
Since:3.0
/** * {@link org.apache.commons.digester3.binder.RulesModule} implementation that allows loading rules from * annotated classes. * * @since 3.0 */
public abstract class FromAnnotationsRuleModule extends AbstractRulesModule { private static final String JAVA_PACKAGE = "java"; private static final AnnotationHandlerFactory DEFAULT_HANDLER_FACTORY = new DefaultAnnotationHandlerFactory(); private AnnotationHandlerFactory annotationHandlerFactory = DEFAULT_HANDLER_FACTORY; private WithMemoryRulesBinder rulesBinder;
{@inheritDoc}
/** * {@inheritDoc} */
@Override protected final void configure() { if ( rulesBinder == null ) { rulesBinder = new WithMemoryRulesBinder( rulesBinder() ); } try { configureRules(); } finally { rulesBinder = null; } }
Configures a RulesBinder via the exposed methods.
/** * Configures a {@link org.apache.commons.digester3.binder.RulesBinder} via the exposed methods. */
protected abstract void configureRules();
Allows users plug a different AnnotationHandlerFactory to create AnnotationHandler instances.
Params:
/** * Allows users plug a different {@link AnnotationHandlerFactory} to create {@link AnnotationHandler} instances. * * @param annotationHandlerFactory A custom {@link AnnotationHandlerFactory} to create * {@link AnnotationHandler} instances */
protected final void useAnnotationHandlerFactory( AnnotationHandlerFactory annotationHandlerFactory ) { if ( annotationHandlerFactory == null ) { throw new IllegalArgumentException( "Argument 'annotationHandlerFactory' must be not null" ); } this.annotationHandlerFactory = annotationHandlerFactory; }
Allows users to switch back to the default AnnotationHandlerFactory implementation.
/** * Allows users to switch back to the default {@link AnnotationHandlerFactory} implementation. */
protected final void useDefaultAnnotationHandlerFactory() { useAnnotationHandlerFactory( DEFAULT_HANDLER_FACTORY ); }
Scan the input Class, looking for Digester rules expressed via annotations, and binds them.
Params:
  • type – the type has to be analyzed
See Also:
/** * Scan the input Class, looking for Digester rules expressed via annotations, and binds them. * * @param type the type has to be analyzed * @see DigesterRule */
protected final void bindRulesFrom( final Class<?> type ) { if ( type == null || type.getPackage().getName().startsWith( JAVA_PACKAGE ) || rulesBinder.isAlreadyBound( type ) ) { return; } // TYPE visitElements( type ); if ( !type.isInterface() ) { // CONSTRUCTOR visitElements( new PrivilegedAction<Constructor<?>[]>() { public Constructor<?>[] run() { return type.getDeclaredConstructors(); } } ); // FIELD visitElements( new PrivilegedAction<Field[]>() { public Field[] run() { return type.getDeclaredFields(); } } ); } // METHOD visitElements( new PrivilegedAction<Method[]>() { public Method[] run() { return type.getDeclaredMethods(); } } ); rulesBinder.markAsBound( type ); bindRulesFrom( type.getSuperclass() ); }
Params:
  • action –
Type parameters:
  • <AE> –
/** * * * @param <AE> * @param action */
private <AE extends AnnotatedElement> void visitElements( PrivilegedAction<AE[]> action ) { AE[] annotatedElements = null; if ( System.getSecurityManager() != null ) { annotatedElements = AccessController.doPrivileged( action ); } else { annotatedElements = action.run(); } visitElements( annotatedElements ); }
Params:
  • annotatedElements –
/** * * * @param annotatedElements */
private void visitElements( AnnotatedElement... annotatedElements ) { for ( AnnotatedElement element : annotatedElements ) { for ( Annotation annotation : element.getAnnotations() ) { handle( annotation, element ); } if ( element instanceof Constructor || element instanceof Method ) { Annotation[][] parameterAnnotations; Class<?>[] parameterTypes; if ( element instanceof Constructor ) { // constructor args Constructor<?> construcotr = (Constructor<?>) element; parameterAnnotations = construcotr.getParameterAnnotations(); parameterTypes = construcotr.getParameterTypes(); } else { // method args Method method = (Method) element; parameterAnnotations = method.getParameterAnnotations(); parameterTypes = method.getParameterTypes(); } for ( int i = 0; i < parameterTypes.length; i++ ) { visitElements( new MethodArgument( i, parameterTypes[i], parameterAnnotations[i] ) ); } } } }
Handles the current visited element and related annotation, invoking the right handler putting the rule provider in the rule set.
Params:
  • annotation – the current visited annotation.
  • element – the current visited element.
/** * Handles the current visited element and related annotation, invoking the * right handler putting the rule provider in the rule set. * * @param annotation the current visited annotation. * @param element the current visited element. */
@SuppressWarnings( "unchecked" ) private <A extends Annotation, E extends AnnotatedElement, R extends Rule> void handle( A annotation, E element ) { Class<?> annotationType = annotation.annotationType(); // check if it is one of the @*.List annotation if ( annotationType.isAnnotationPresent( DigesterRuleList.class ) ) { Annotation[] annotations = getAnnotationsArrayValue( annotation ); if ( annotations != null && annotations.length > 0 ) { // if it is an annotations array, process them for ( Annotation ptr : annotations ) { handle( ptr, element ); } } } else if ( annotationType.isAnnotationPresent( DigesterRule.class ) ) { DigesterRule digesterRule = annotationType.getAnnotation( DigesterRule.class ); // the default behavior if the handler is not specified Class<? extends AnnotationHandler<Annotation, AnnotatedElement>> handlerType = (Class<? extends AnnotationHandler<Annotation, AnnotatedElement>>) digesterRule.handledBy(); try { AnnotationHandler<Annotation, AnnotatedElement> handler = annotationHandlerFactory.newInstance( handlerType ); // run! handler.handle( annotation, element, this.rulesBinder ); } catch ( Exception e ) { rulesBinder.addError( e ); } } } }