org.apache.commons.digester.annotations.DigesterLoader.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.commons.digester.annotations.DigesterLoader.java

Source

/* $Id: DigesterLoader.java 992084 2010-09-02 19:52:17Z simonetripodi $
 *
 * 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.
 */
package org.apache.commons.digester.annotations;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import org.apache.commons.digester.Digester;
import org.apache.commons.digester.Rule;
import org.apache.commons.digester.RuleSet;
import org.apache.commons.digester.annotations.handlers.DefaultLoaderHandler;
import org.apache.commons.digester.annotations.internal.RuleSetCache;
import org.apache.commons.digester.annotations.reflect.MethodArgument;
import org.apache.commons.digester.annotations.spi.AnnotationRuleProviderFactory;
import org.apache.commons.digester.annotations.spi.DigesterLoaderHandlerFactory;
import org.apache.commons.digester.annotations.utils.AnnotationUtils;

/**
 * This class manages the creation of Digester instances analyzing target classes
 * annotated with digester annotations.
 *
 * @since 2.1
 */
public final class DigesterLoader {

    /**
     * In-memory LRU cache that stores already analyzed classes and relative
     * {@link RuleSet}.
     */
    private final RuleSetCache cachedRuleSet = new RuleSetCache();

    private final AnnotationRuleProviderFactory annotationRuleProviderFactory;

    private final DigesterLoaderHandlerFactory digesterLoaderHandlerFactory;

    /**
     * Creates a new {@link DigesterLoader} instance.
     *
     * @param annotationRuleProviderFactory
     * @param digesterLoaderHandlerFactory
     */
    protected DigesterLoader(AnnotationRuleProviderFactory annotationRuleProviderFactory,
            DigesterLoaderHandlerFactory digesterLoaderHandlerFactory) {
        this.annotationRuleProviderFactory = annotationRuleProviderFactory;
        this.digesterLoaderHandlerFactory = digesterLoaderHandlerFactory;
    }

    protected AnnotationRuleProviderFactory getAnnotationRuleProviderFactory() {
        return annotationRuleProviderFactory;
    }

    protected DigesterLoaderHandlerFactory getDigesterLoaderHandlerFactory() {
        return digesterLoaderHandlerFactory;
    }

    /**
     * Creates a new digester which rules are defined by analyzing the digester
     * annotations in the target class.
     *
     * @param target the class has to be analyzed.
     * @return a new Digester instance.
     */
    public Digester createDigester(final Class<?> target) {
        Digester digester = new Digester();
        addRules(target, digester);
        return digester;
    }

    /**
     * Add rules to an already created Digester instance, analyzing the digester
     * annotations in the target class.
     *
     * @param target the class has to be analyzed.
     * @param digester the Digester instance reference.
     */
    public void addRules(final Class<?> target, final Digester digester) {
        RuleSet ruleSet = getRuleSet(target);
        ruleSet.addRuleInstances(digester);
    }

    /**
     * Builds a new {@link RuleSet} analyzing the digester annotations in the
     * target class.
     *
     * It avoids iterate the annotations analysis for already analyzed classes,
     * using an in-memory LRU cache.
     *
     * @param target the class has to be analyzed.
     * @return a new {@link RuleSet}.
     */
    public RuleSet getRuleSet(final Class<?> target) {
        if (this.cachedRuleSet.containsKey(target)) {
            return this.cachedRuleSet.get(target);
        }

        FromAnnotationsRuleSet ruleSet = new FromAnnotationsRuleSet(this);
        addRulesTo(target, ruleSet);
        this.cachedRuleSet.put(target, ruleSet);

        return ruleSet;
    }

    /**
     * Analyzes the target class and adds the {@link AnnotationRuleProvider}s to
     * the existing {@link FromAnnotationsRuleSet}.
     *
     * @param target the class has to be analyzed.
     * @param ruleSet the RuleSet where adding the providers.
     */
    public void addRulesTo(final Class<?> target, FromAnnotationsRuleSet ruleSet) {
        if (target == Object.class || target.isInterface() || ruleSet.mapsClass(target)) {
            return;
        }

        if (this.cachedRuleSet.containsKey(target)) {
            ruleSet.addRulesProviderFrom(this.cachedRuleSet.get(target));
            ruleSet.addMappedClass(target);
            return;
        }

        // current analyzed class
        handle(target, ruleSet);

        // class fields
        for (Field field : target.getDeclaredFields()) {
            handle(field, ruleSet);
        }

        // class methods
        for (Method method : target.getDeclaredMethods()) {
            handle(method, ruleSet);

            // method args
            Annotation[][] parameterAnnotations = method.getParameterAnnotations();
            Class<?>[] parameterTypes = method.getParameterTypes();
            for (int i = 0; i < parameterTypes.length; i++) {
                handle(new MethodArgument(i, parameterTypes[i], parameterAnnotations[i]), ruleSet);
            }
        }

        ruleSet.addMappedClass(target);
        addRulesTo(target.getSuperclass(), ruleSet);
    }

    /**
     * Executes an analysis for each annotation present in the element.
     *
     * @param element the current element under analysis.
     * @param ruleSet the ruleSet where add providers.
     */
    private void handle(AnnotatedElement element, FromAnnotationsRuleSet ruleSet) {
        for (Annotation annotation : element.getAnnotations()) {
            handle(annotation, element, ruleSet);
        }
    }

    /**
     * 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,
            FromAnnotationsRuleSet ruleSet) {
        Class<?> annotationType = annotation.annotationType();

        // check if it is one of the @*.List annotation
        if (annotationType.isAnnotationPresent(DigesterRuleList.class)) {
            Annotation[] annotations = AnnotationUtils.getAnnotationsArrayValue(annotation);
            if (annotations != null && annotations.length > 0) {
                // if it is an annotations array, process them
                for (Annotation ptr : annotations) {
                    handle(ptr, element, ruleSet);
                }
            }
        } else if (annotationType.isAnnotationPresent(DigesterRule.class)) {
            DigesterRule digesterRule = annotationType.getAnnotation(DigesterRule.class);

            if (DefaultLoaderHandler.class == digesterRule.handledBy()) {
                Class<? extends AnnotationRuleProvider<A, E, R>> providerType = (Class<? extends AnnotationRuleProvider<A, E, R>>) digesterRule
                        .providedBy();
                ruleSet.addRuleProvider(AnnotationUtils.getAnnotationPattern(annotation), providerType, annotation,
                        element);
            } else {
                Class<? extends DigesterLoaderHandler<Annotation, AnnotatedElement>> handlerType = (Class<? extends DigesterLoaderHandler<Annotation, AnnotatedElement>>) digesterRule
                        .handledBy();
                DigesterLoaderHandler<Annotation, AnnotatedElement> handler = this.digesterLoaderHandlerFactory
                        .newInstance(handlerType);

                // run!
                handler.handle(annotation, element, ruleSet);
            }
        }
    }

}