Package org.apache.commons.digester3.annotations

Source Code of org.apache.commons.digester3.annotations.FromAnnotationsRuleModule

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;

/**
* {@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}
     */
    @Override
    protected final void configure()
    {
        if ( rulesBinder == null )
        {
            rulesBinder = new WithMemoryRulesBinder( rulesBinder() );
        }

        try
        {
            configureRules();
        }
        finally
        {
            rulesBinder = null;
        }
    }

    /**
     * Configures a {@link org.apache.commons.digester3.binder.RulesBinder} via the exposed methods.
     */
    protected abstract void configureRules();

    /**
     * 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 {@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.
     *
     * @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() );
    }

    /**
     *
     *
     * @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 );
    }

    /**
     *
     *
     * @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.
     *
     * @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 );
            }
        }
    }

}
TOP

Related Classes of org.apache.commons.digester3.annotations.FromAnnotationsRuleModule

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.