All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.neba.core.util.Annotations Maven / Gradle / Ivy

/*
  Copyright 2013 the original author or authors.

  Licensed 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 io.neba.core.util;

import javax.annotation.Nonnull;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import static io.neba.core.util.ReadOnlyIterator.readOnly;
import static java.util.Arrays.asList;
import static java.util.Collections.addAll;

/**
 * Supports meta-annotations by looking up annotations in the transitive
 * hull (annotations and their annotations, called meta-annotations) of a given
 * {@link java.lang.reflect.AnnotatedElement}.
 *
 * @author Olaf Otto
 */
public class Annotations implements Iterable {
    private final AnnotatedElement annotatedElement;
    private Map, Annotation> annotations = null;

    /**
     * @param annotatedElement must not be null
     * @return never null.
     */
    public static Annotations annotations(AnnotatedElement annotatedElement) {
        if (annotatedElement == null) {
            throw new IllegalArgumentException("Method argument annotatedElement must not be null.");
        }
        return new Annotations(annotatedElement);
    }

    /**
     * @param annotatedElement must not be null.
     */
    Annotations(AnnotatedElement annotatedElement) {
        if (annotatedElement == null) {
            throw new IllegalArgumentException("Constructor parameter annotatedElement must not be null.");
        }
        this.annotatedElement = annotatedElement;
    }

    /**
     * @param type must not be null.
     * @return whether the given element or any of its meta-annotations is annotated with the given annotation type.
     */
    public boolean contains(Class type) {
        if (type == null) {
            throw new IllegalArgumentException("Method argument type must not be null.");
        }
        return getAnnotationMap().get(type) != null;
    }

    /**
     * @param name must not be null.
     * @return whether the given type name matches one of the present annotations
     */
    public boolean containsName(String name) {
        if (name == null) {
            throw new IllegalArgumentException("Method argument name must not be null.");
        }
        for (Class annotationType : getAnnotationMap().keySet()) {
            if (annotationType.getName().equals(name)) {
                return true;
            }
        }
        return false;
    }

    /**
     * @param type must not be null.
     * @return the annotation if present on the given element or any meta-annotation thereof, or null.
     */
    @SuppressWarnings("unchecked")
    public  T get(Class type) {
        if (type == null) {
            throw new IllegalArgumentException("Method argument type must not be null.");
        }

        return (T) getAnnotationMap().get(type);
    }

    /**
     * @return never null.
     */
    public Stream stream() {
        return StreamSupport.stream(this.spliterator(), false);
    }

    /**
     * @return all annotations and meta-annotations present on the element. Never null but rather an empty map.
     */
    private Map, Annotation> getAnnotationMap() {
        if (this.annotations == null) {
            // We do not care about calculating the same thing twice in case of concurrent access.
            HashMap, Annotation> annotations = new HashMap<>();
            Queue queue = new LinkedList<>(asList(this.annotatedElement.getAnnotations()));
            while (!queue.isEmpty()) {
                Annotation annotation = queue.remove();
                // Prevent lookup loops (@A annotated with @B annotated with @A ...)
                if (!annotations.containsKey(annotation.annotationType())) {
                    annotations.put(annotation.annotationType(), annotation);
                    addAll(queue, annotation.annotationType().getAnnotations());
                }
            }
            this.annotations = annotations;
        }

        return this.annotations;
    }

    @Override
    @Nonnull
    public Iterator iterator() {
        return readOnly(getAnnotationMap().values().iterator());
    }

    public Map, Annotation> getAnnotations() {
        return new HashMap<>(getAnnotationMap());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy