atg.tools.dynunit.internal.reflect.ClassReflection.java Source code

Java tutorial

Introduction

Here is the source code for atg.tools.dynunit.internal.reflect.ClassReflection.java

Source

/*
 * Copyright 2013 Matt Sicker and Contributors
 *
 * 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 atg.tools.dynunit.internal.reflect;

import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Container for scanning annotated elements in a class. Because reflection is an expensive operation in Java (sigh),
 * the caller must call initFields(), initMethods(), or init() to initialize the fields, methods, or both, respectively.
 * <p/>
 * This class is not a part of the public API and is subject to change. It is used for finding injection points. It is
 * also used for caching annotations and such for faster lookup afterward.
 *
 * @author msicker
 * @version 1.0.0
 */
public class ClassReflection<T> {
    // TODO: cache annotation types

    private static final Logger logger = LogManager.getLogger();

    private final T object;
    private final Class<T> klass;
    private Field[] fields = null;
    private Method[] methods = null;

    @SuppressWarnings("unchecked") // seriously Java, get your generic shit together
    public ClassReflection(@NotNull final T object) {
        this.object = object;
        klass = (Class<T>) object.getClass();
    }

    public void init() {
        logger.entry();
        initFields();
        initMethods();
        logger.exit();
    }

    public void initFields() {
        logger.entry();
        fields = klass.getFields();
        logger.exit();
    }

    public void initMethods() {
        logger.entry();
        methods = klass.getMethods();
        logger.exit();
    }

    @NotNull
    public T getObject() {
        logger.entry();
        return logger.exit(object);
    }

    @NotNull
    public Class<T> getObjectClass() {
        logger.entry();
        return logger.exit(klass);
    }

    @Nullable
    public <A extends Annotation> Pair<? extends AnnotatedElement, A> getAnnotatedElement(
            @NotNull final Class<A> annotationClass) {
        logger.entry(annotationClass);
        A annotation = getClassAnnotation(annotationClass);
        if (annotation != null) {
            return logger.exit(Pair.of(klass, annotation));
        }
        for (final Field field : fields) {
            annotation = getFieldAnnotation(field, annotationClass);
            if (annotation != null) {
                return logger.exit(Pair.of(field, annotation));
            }
        }
        for (final Method method : methods) {
            annotation = getMethodAnnotation(method, annotationClass);
            if (annotation != null) {
                return logger.exit(Pair.of(method, annotation));
            }
        }
        return logger.exit(null);
    }

    public Annotation[] getClassAnnotations() {
        logger.entry();
        final Annotation[] annotations = klass.getAnnotations();
        return logger.exit(annotations);
    }

    @Nullable
    public <A extends Annotation> A getClassAnnotation(@NotNull final Class<A> annotationClass) {
        return getElementAnnotation(klass, annotationClass);
    }

    public Field[] getFields() {
        logger.entry();
        return logger.exit(fields);
    }

    @NotNull
    public Map<Field, Annotation[]> getFieldAnnotations() {
        return getElementalAnnotations(fields);
    }

    @Nullable
    public <A extends Annotation> A getFieldAnnotation(@NotNull final Field field,
            @NotNull final Class<A> annotationClass) {
        return getElementAnnotation(field, annotationClass);
    }

    public Method[] getMethods() {
        logger.entry();
        return logger.exit(methods);
    }

    @NotNull
    public Map<Method, Annotation[]> getMethodAnnotations() {
        return getElementalAnnotations(methods);
    }

    @Nullable
    public <A extends Annotation> A getMethodAnnotation(@NotNull final Method method,
            @NotNull final Class<A> annotationClass) {
        return getElementAnnotation(method, annotationClass);
    }

    @Nullable
    private static <E extends AnnotatedElement, A extends Annotation> A getElementAnnotation(@NotNull E element,
            @NotNull Class<A> annotationClass) {
        logger.entry(element, annotationClass);
        final A annotation = element.getAnnotation(annotationClass);
        return logger.exit(annotation);
    }

    @NotNull
    private static <E extends AnnotatedElement> Map<E, Annotation[]> getElementalAnnotations(
            @NotNull E[] elements) {
        logger.entry((Object[]) elements);
        final Map<E, Annotation[]> elementAnnotations = new ConcurrentHashMap<E, Annotation[]>();
        for (final E element : elements) {
            elementAnnotations.put(element, element.getAnnotations());
        }
        return logger.exit(elementAnnotations);
    }
}