org.codeseed.common.config.AnnotationPropertySource.java Source code

Java tutorial

Introduction

Here is the source code for org.codeseed.common.config.AnnotationPropertySource.java

Source

/*
 * Copyright 2013 Jeremy Gustie
 *
 * 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 org.codeseed.common.config;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.reflect.TypeToken;

/**
 * A property source whose values are triggered by the presence of an
 * annotation. If an annotation of the captured type variable {@code A} is not
 * present on the configuration method, this property source will always return
 * {@code null}; otherwise the property value is implementation specific.
 *
 * @author Jeremy Gustie
 * @param <A>
 *            the type of annotation to trigger this property source
 */
public abstract class AnnotationPropertySource<A extends Annotation> implements PropertySource {

    /**
     * @see AnnotationPropertySource#compose
     */
    private static final class AnnotationsPropertySource implements PropertySource {
        /**
         * A map from annotation type to the property source handling it.
         */
        private final Map<Class<? extends Annotation>, ? extends AnnotationPropertySource<?>> sources;

        private AnnotationsPropertySource(Iterable<? extends AnnotationPropertySource<?>> sources) {
            this.sources = Maps.uniqueIndex(sources,
                    new Function<AnnotationPropertySource<?>, Class<? extends Annotation>>() {
                        @Override
                        public Class<? extends Annotation> apply(AnnotationPropertySource<?> input) {
                            return input.annotationClass;
                        }
                    });
        }

        @Override
        public String getProperty(Method method) {
            for (Annotation annotation : method.getAnnotations()) {
                PropertySource source = sources.get(annotation.annotationType());
                if (source != null) {
                    String value = source.getProperty(method);
                    if (value != null) {
                        return value;
                    }
                }
            }
            return null;
        }
    }

    /**
     * Creates a property source backed by some number of annotation property
     * sources. The resulting property source will consider annotations (in
     * order) on configuration interface methods when trying to resolve property
     * values. If the configuration method contains no annotations, this
     * property source will always return {@code null}.
     *
     * @param sources
     *            the annotation property sources to aggregate
     * @return a single property source which delegates to the supplied sources
     *         in annotation declaration order
     */
    public static PropertySource create(Iterable<? extends AnnotationPropertySource<?>> sources) {
        if (Iterables.isEmpty(sources)) {
            return PropertySources.empty();
        }
        return new AnnotationsPropertySource(sources);
    }

    /**
     * Creates a property source backed by some number of annotation property
     * sources.
     *
     * @param sources
     *            the annotation property sources to aggregate
     * @return a single property source which delegates to the supplied sources
     *         in annotation declaration order
     * @see #create(Iterable)
     */
    public static PropertySource create(AnnotationPropertySource<?>... sources) {
        return create(Arrays.asList(sources));
    }

    /**
     * Captured type of the annotation that triggers this property source.
     */
    // TODO Why can't this be safe?
    @SuppressWarnings({ "unchecked", "serial" })
    private final Class<A> annotationClass = (Class<A>) new TypeToken<A>(getClass()) {
    }.getRawType();

    /**
     * No-argument constructor for subclasses.
     */
    protected AnnotationPropertySource() {
    }

    @Override
    public final String getProperty(Method method) {
        if (method.isAnnotationPresent(annotationClass)) {
            return getProperty(method, method.getAnnotation(annotationClass));
        } else {
            return null;
        }
    }

    /**
     * Returns a property value or {@code null} if no such property is defined.
     * This is a specialized contract for a property which is triggered by an
     * annotation.
     *
     * @param method
     *            the configuration method to look up
     * @param annotation
     *            the annotation which triggered this property lookup
     * @return property value, {@code null} if undefined
     */
    protected abstract String getProperty(Method method, A annotation);

}