Java tutorial
/* * Copyright (C) 2015 The Dagger 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 dagger.internal.codegen; import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations; import static com.google.auto.common.AnnotationMirrors.getAnnotationValuesWithDefaults; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.Iterables.getOnlyElement; import static javax.lang.model.util.ElementFilter.methodsIn; import com.google.auto.common.MoreTypes; import com.google.common.collect.ImmutableSet; import com.squareup.javapoet.CodeBlock; import dagger.MapKey; import java.util.NoSuchElementException; import java.util.Optional; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.SimpleTypeVisitor6; import javax.lang.model.util.Types; /** * Methods for extracting {@link MapKey} annotations and key code blocks from binding elements. */ final class MapKeys { /** * If {@code bindingElement} is annotated with a {@link MapKey} annotation, returns it. * * @throws IllegalArgumentException if the element is annotated with more than one {@code MapKey} * annotation */ static Optional<AnnotationMirror> getMapKey(Element bindingElement) { ImmutableSet<? extends AnnotationMirror> mapKeys = getMapKeys(bindingElement); return mapKeys.isEmpty() ? Optional.empty() : Optional.<AnnotationMirror>of(getOnlyElement(mapKeys)); } /** * Returns all of the {@link MapKey} annotations that annotate {@code bindingElement}. */ static ImmutableSet<? extends AnnotationMirror> getMapKeys(Element bindingElement) { return getAnnotatedAnnotations(bindingElement, MapKey.class); } /** * Returns the annotation value if {@code mapKey}'s type is annotated with * {@link MapKey @MapKey(unwrapValue = true)}. * * @throws IllegalArgumentException if {@code mapKey}'s type is not annotated with * {@link MapKey @MapKey} at all. */ static Optional<? extends AnnotationValue> unwrapValue(AnnotationMirror mapKey) { MapKey mapKeyAnnotation = mapKey.getAnnotationType().asElement().getAnnotation(MapKey.class); checkArgument(mapKeyAnnotation != null, "%s is not annotated with @MapKey", mapKey.getAnnotationType()); return mapKeyAnnotation.unwrapValue() ? Optional.of(getOnlyElement(getAnnotationValuesWithDefaults(mapKey).values())) : Optional.empty(); } /** * Returns the map key type for an unwrapped {@link MapKey} annotation type. If the single member * type is primitive, returns the boxed type. * * @throws IllegalArgumentException if {@code mapKeyAnnotationType} is not an annotation type or * has more than one member, or if its single member is an array * @throws NoSuchElementException if the annotation has no members */ public static DeclaredType getUnwrappedMapKeyType(final DeclaredType mapKeyAnnotationType, final Types types) { checkArgument(MoreTypes.asTypeElement(mapKeyAnnotationType).getKind() == ElementKind.ANNOTATION_TYPE, "%s is not an annotation type", mapKeyAnnotationType); final ExecutableElement onlyElement = getOnlyElement( methodsIn(mapKeyAnnotationType.asElement().getEnclosedElements())); SimpleTypeVisitor6<DeclaredType, Void> keyTypeElementVisitor = new SimpleTypeVisitor6<DeclaredType, Void>() { @Override public DeclaredType visitArray(ArrayType t, Void p) { throw new IllegalArgumentException( mapKeyAnnotationType + "." + onlyElement.getSimpleName() + " cannot be an array"); } @Override public DeclaredType visitPrimitive(PrimitiveType t, Void p) { return MoreTypes.asDeclared(types.boxedClass(t).asType()); } @Override public DeclaredType visitDeclared(DeclaredType t, Void p) { return t; } }; return keyTypeElementVisitor.visit(onlyElement.getReturnType()); } /** * Returns a code block for the map key specified by the {@link MapKey} annotation on * {@code bindingElement}. * * @throws IllegalArgumentException if the element is annotated with more than one {@code MapKey} * annotation * @throws IllegalStateException if {@code bindingElement} is not annotated with a {@code MapKey} * annotation */ static CodeBlock getMapKeyExpression(AnnotationMirror mapKey) { Optional<? extends AnnotationValue> unwrappedValue = unwrapValue(mapKey); AnnotationExpression annotationExpression = new AnnotationExpression(mapKey); if (unwrappedValue.isPresent()) { TypeMirror unwrappedValueType = getOnlyElement(getAnnotationValuesWithDefaults(mapKey).keySet()) .getReturnType(); return annotationExpression.getValueExpression(unwrappedValueType, unwrappedValue.get()); } else { return annotationExpression.getAnnotationInstanceExpression(); } } private MapKeys() { } }