Java tutorial
/* * 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); }