com.qrmedia.pattern.compositeannotation.CompositeAnnotatedElements.java Source code

Java tutorial

Introduction

Here is the source code for com.qrmedia.pattern.compositeannotation.CompositeAnnotatedElements.java

Source

/*
 * @(#)CompositeAnnotatedElements.java     28 May 2009
 * 
 * Copyright  2009 Andrew Phillips.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package com.qrmedia.pattern.compositeannotation;

import static com.qrmedia.commons.validation.ValidationUtils.checkNotNull;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.apache.commons.collections.CollectionUtils;

import com.qrmedia.pattern.compositeannotation.api.AnnotatedElements;
import com.qrmedia.pattern.compositeannotation.metadata.CompositeAnnotationDescriptor;
import com.qrmedia.pattern.compositeannotation.metadata.CompositeAnnotationTypeRegistry;

/**
 * An implementation of {@link AnnotatedElements} based solely on composite
 * annotations.
 * 
 * @author aphillips
 * @since 28 May 2009
 *
 */
public class CompositeAnnotatedElements implements AnnotatedElements {
    private final Map<Class<? extends Annotation>, CompositeAnnotationDescriptor<? extends Annotation>> compositeAnnotationTypeDescriptors = new HashMap<Class<? extends Annotation>, CompositeAnnotationDescriptor<? extends Annotation>>();

    // a map of "standard" annotation -> composites providing that annotation
    private final Map<Class<? extends Annotation>, Set<Class<? extends Annotation>>> leafAnnotationTypeProvidingCompositeTypes = new HashMap<Class<? extends Annotation>, Set<Class<? extends Annotation>>>();

    /**
     * Creates a {@code CompositeAnnotatedElements} instance based on the given
     * composite annotation registry.
     * 
     * @param compositeAnnotationTypeRegistry the registry of supported composite annotation types
     * @throws IllegalArgumentException if {@code compositeAnnotationTypeRegistry} is {@code null} or
     *                                  one of the given annotation types is not a valid composite
     */
    @SuppressWarnings("unchecked")
    public CompositeAnnotatedElements(CompositeAnnotationTypeRegistry compositeAnnotationTypeRegistry) {
        checkNotNull("'compositeAnnotationTypeRegistry' may not be null", compositeAnnotationTypeRegistry);

        // create descriptors for each composite - will blow up if one is not valid
        for (Class<? extends Annotation> compositeAnnotationType : compositeAnnotationTypeRegistry
                .getCompositeAnnotationTypes()) {
            // TODO: Urgh: see if we can avoid the cast here
            compositeAnnotationTypeDescriptors.put(compositeAnnotationType,
                    new CompositeAnnotationDescriptor<Annotation>((Class<Annotation>) compositeAnnotationType));
        }

        for (Entry<Class<? extends Annotation>, CompositeAnnotationDescriptor<? extends Annotation>> compositeAnnotationDescriptor : compositeAnnotationTypeDescriptors
                .entrySet()) {
            addProvidingCompositeType(compositeAnnotationDescriptor.getValue().getLeafAnnotationTypes(),
                    compositeAnnotationDescriptor.getKey());
        }

    }

    // add to the "standard" -> providing composite map based on the descriptor information
    private void addProvidingCompositeType(Set<Class<? extends Annotation>> leafAnnotationTypes,
            Class<? extends Annotation> compositeAnnotationType) {

        for (Class<? extends Annotation> leafAnnotationType : leafAnnotationTypes) {
            // before the first entry is added the value collection must be created
            if (!leafAnnotationTypeProvidingCompositeTypes.containsKey(leafAnnotationType)) {
                leafAnnotationTypeProvidingCompositeTypes.put(leafAnnotationType,
                        new HashSet<Class<? extends Annotation>>());
            }

            leafAnnotationTypeProvidingCompositeTypes.get(leafAnnotationType).add(compositeAnnotationType);
        }

    }

    /* (non-Javadoc)
     * @see com.qrmedia.pattern.compositeannotation.api.AnnotatedElements#getAnnotation(java.lang.reflect.AnnotatedElement, java.lang.Class)
     */
    @SuppressWarnings("unchecked")
    public <T extends Annotation> T getAnnotation(AnnotatedElement annotatedElement, Class<T> annotationClass) {
        checkNotNull("The element and class must be non-null", annotatedElement, annotationClass);

        // if *no* composite provides the type it cannot be present
        if (!leafAnnotationTypeProvidingCompositeTypes.containsKey(annotationClass)) {
            return null;
        }

        // throws an IllegalArgumentException if there is more than one providing composite
        Collection<Class<? extends Annotation>> presentProvidingCompositeTypes = getPresentProvidingCompositeTypes(
                annotatedElement, annotationClass);

        if (presentProvidingCompositeTypes.isEmpty()) {
            return null;
        }

        Class<? extends Annotation> presentProvidingCompositeType = presentProvidingCompositeTypes.iterator()
                .next();

        return ((CompositeAnnotationDescriptor<Annotation>) compositeAnnotationTypeDescriptors
                .get(presentProvidingCompositeType)).getLeafAnnotation(
                        annotatedElement.getAnnotation(presentProvidingCompositeType), annotationClass);
    }

    @SuppressWarnings("unchecked")
    private Collection<Class<? extends Annotation>> getPresentProvidingCompositeTypes(
            AnnotatedElement annotatedElement, Class<? extends Annotation> annotationClass) {
        // check how many of the composite elements that deliver the required elements are present
        Collection<Class<? extends Annotation>> presentProvidingCompositeTypes = (Collection<Class<? extends Annotation>>) CollectionUtils
                .intersection(getCompositeAnnotationTypes(annotatedElement, true),
                        leafAnnotationTypeProvidingCompositeTypes.get(annotationClass));

        // at most one providing composite may be present
        if (presentProvidingCompositeTypes.size() > 1) {
            throw new IllegalArgumentException(
                    annotatedElement + " is annotated with multiple composite annotations ("
                            + presentProvidingCompositeTypes + ") that declare leaves of type " + annotationClass);
        }

        return presentProvidingCompositeTypes;
    }

    // uses getMethods() or getDeclaredMethods(), depending on the value of includeInherited
    private Set<Class<? extends Annotation>> getCompositeAnnotationTypes(AnnotatedElement annotatedElement,
            boolean includeInherited) {
        Annotation[] annotations = (includeInherited ? annotatedElement.getAnnotations()
                : annotatedElement.getDeclaredAnnotations());
        Set<Class<? extends Annotation>> compositeAnnotations = new HashSet<Class<? extends Annotation>>();

        // collect all the annotations that are annotated with @CompositeAnnotation
        for (Annotation annotation : annotations) {
            Class<? extends Annotation> annotationType = annotation.annotationType();

            if (compositeAnnotationTypeDescriptors.containsKey(annotationType)) {
                compositeAnnotations.add(annotationType);
            }

        }

        return compositeAnnotations;
    }

    /* (non-Javadoc)
     * @see com.qrmedia.pattern.compositeannotation.api.AnnotatedElements#getAnnotations(java.lang.reflect.AnnotatedElement)
     */
    public Annotation[] getAnnotations(AnnotatedElement annotatedElement) {
        return getAnnotations(annotatedElement, true);
    }

    @SuppressWarnings("unchecked")
    private Annotation[] getAnnotations(AnnotatedElement annotatedElement, boolean includeInherited) {
        checkNotNull("The element must be non-null", annotatedElement);

        Set<Annotation> leafAnnotations = new HashSet<Annotation>();
        Set<Class<? extends Annotation>> presentLeafAnnotationTypes = new HashSet<Class<? extends Annotation>>();

        for (Class<? extends Annotation> compositeAnnotationType : getCompositeAnnotationTypes(annotatedElement,
                includeInherited)) {
            CompositeAnnotationDescriptor<Annotation> compositeAnnotationDescriptor = (CompositeAnnotationDescriptor<Annotation>) compositeAnnotationTypeDescriptors
                    .get(compositeAnnotationType);
            Set<Class<? extends Annotation>> newLeafAnnotationTypes = compositeAnnotationDescriptor
                    .getLeafAnnotationTypes();

            // multiple composites may not return leaf annotations of the same type
            if (CollectionUtils.containsAny(presentLeafAnnotationTypes, newLeafAnnotationTypes)) {
                throw new IllegalArgumentException(annotatedElement
                        + " is annotated with multiple composite annotations that declare leaves of the same type");
            }

            presentLeafAnnotationTypes.addAll(newLeafAnnotationTypes);
            leafAnnotations.addAll(compositeAnnotationDescriptor
                    .getLeafAnnotations(annotatedElement.getAnnotation(compositeAnnotationType)));
        }

        return leafAnnotations.toArray(new Annotation[0]);
    }

    /* (non-Javadoc)
     * @see com.qrmedia.pattern.compositeannotation.api.AnnotatedElements#getDeclaredAnnotations(java.lang.reflect.AnnotatedElement)
     */
    public Annotation[] getDeclaredAnnotations(AnnotatedElement annotatedElement) {
        return getAnnotations(annotatedElement, false);
    }

    /* (non-Javadoc)
     * @see com.qrmedia.pattern.compositeannotation.api.AnnotatedElements#isAnnotationPresent(java.lang.reflect.AnnotatedElement, java.lang.Class)
     */
    public boolean isAnnotationPresent(AnnotatedElement annotatedElement,
            Class<? extends Annotation> annotationClass) {
        checkNotNull("The element and class must be non-null", annotatedElement, annotationClass);

        // if *no* composite provides the type it cannot be present
        if (!leafAnnotationTypeProvidingCompositeTypes.containsKey(annotationClass)) {
            return false;
        }

        // will throw an IllegalArgumentException if there is more than providing composite
        return (getPresentProvidingCompositeTypes(annotatedElement, annotationClass).size() == 1);
    }

}