/* * Copyright 2014 Google Inc. All rights reserved. * * 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.inferred.freebuilder.processor.util; import static javax.lang.model.util.ElementFilter.methodsIn; import com.google.common.base.Optional; import java.lang.annotation.Annotation; import java.util.Map.Entry; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; import javax.lang.model.util.SimpleElementVisitor6; import javax.lang.model.util.SimpleTypeVisitor6; import javax.lang.model.util.Types; /** * Utility methods for the javax.lang.model package. */ public class ModelUtils { /** * Returns an {@link AnnotationMirror} for the annotation of type {@code annotationClass} on * {@code element}, or {@link Optional#absent()} if no such annotation exists. */ public static Optional<AnnotationMirror> findAnnotationMirror( Element element, Class<? extends Annotation> annotationClass) { return findAnnotationMirror(element, Shading.unshadedName(annotationClass.getName())); } /** * Returns an {@link AnnotationMirror} for the annotation of type {@code annotationClass} on * {@code element}, or {@link Optional#absent()} if no such annotation exists. */ public static Optional<AnnotationMirror> findAnnotationMirror( Element element, QualifiedName annotationClass) { return findAnnotationMirror(element, Shading.unshadedName(annotationClass.toString())); } /** * Returns an {@link AnnotationMirror} for the annotation of type {@code annotationClassName} on * {@code element}, or {@link Optional#absent()} if no such annotation exists. */ public static Optional<AnnotationMirror> findAnnotationMirror( Element element, String annotationClassName) { for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) { TypeElement annotationTypeElement = (TypeElement) (annotationMirror.getAnnotationType().asElement()); if (annotationTypeElement.getQualifiedName().contentEquals(annotationClassName)) { return Optional.of(annotationMirror); } } return Optional.absent(); } public static Optional<AnnotationValue> findProperty( AnnotationMirror annotation, String propertyName) { for (Entry<? extends ExecutableElement, ? extends AnnotationValue> element : annotation.getElementValues().entrySet()) { if (element.getKey().getSimpleName().contentEquals(propertyName)) { return Optional.<AnnotationValue>of(element.getValue()); } } return Optional.absent(); } /** Returns {@code element} as a {@link TypeElement}, if it is one. */ public static Optional<TypeElement> maybeType(Element element) { return TYPE_ELEMENT_VISITOR.visit(element); } /** Returns {@code type} as a {@link DeclaredType}, if it is one. */ public static Optional<DeclaredType> maybeDeclared(TypeMirror type) { return DECLARED_TYPE_VISITOR.visit(type); } public static Optional<TypeVariable> maybeVariable(TypeMirror type) { return TYPE_VARIABLE_VISITOR.visit(type); } /** Returns the {@link TypeElement} corresponding to {@code type}, if there is one. */ public static Optional<TypeElement> maybeAsTypeElement(TypeMirror type) { Optional<DeclaredType> declaredType = maybeDeclared(type); if (declaredType.isPresent()) { return maybeType(declaredType.get().asElement()); } else { return Optional.absent(); } } /** Returns the {@link TypeElement} corresponding to {@code type}. */ public static TypeElement asElement(DeclaredType type) { return maybeType(type.asElement()).get(); } /** Applies unboxing conversion to {@code mirror}, if it can be unboxed. */ public static Optional<TypeMirror> maybeUnbox(TypeMirror mirror, Types types) { try { return Optional.<TypeMirror>of(types.unboxedType(mirror)); } catch (IllegalArgumentException e) { return Optional.absent(); } } /** Returns whether {@code type} overrides method {@code methodName(params)}. */ public static boolean overrides( TypeElement type, Types types, String methodName, TypeMirror... params) { for (ExecutableElement method : methodsIn(type.getEnclosedElements())) { if (signatureMatches(method, types, methodName, params)) { return true; } } return false; } private static boolean signatureMatches( ExecutableElement method, Types types, String name, TypeMirror... params) { if (!method.getSimpleName().contentEquals(name)) { return false; } if (method.getParameters().size() != params.length) { return false; } for (int i = 0; i < params.length; ++i) { if (!types.isSameType(params[i], method.getParameters().get(i).asType())) { return false; } } return true; } private static final SimpleElementVisitor6<Optional<TypeElement>, ?> TYPE_ELEMENT_VISITOR = new SimpleElementVisitor6<Optional<TypeElement>, Void>() { @Override public Optional<TypeElement> visitType(TypeElement e, Void p) { return Optional.of(e); } @Override protected Optional<TypeElement> defaultAction(Element e, Void p) { return Optional.absent(); } }; private static final SimpleTypeVisitor6<Optional<DeclaredType>, ?> DECLARED_TYPE_VISITOR = new SimpleTypeVisitor6<Optional<DeclaredType>, Void>() { @Override public Optional<DeclaredType> visitDeclared(DeclaredType t, Void p) { return Optional.of(t); } @Override protected Optional<DeclaredType> defaultAction(TypeMirror e, Void p) { return Optional.absent(); } }; private static final SimpleTypeVisitor6<Optional<TypeVariable>, ?> TYPE_VARIABLE_VISITOR = new SimpleTypeVisitor6<Optional<TypeVariable>, Void>() { @Override public Optional<TypeVariable> visitTypeVariable(TypeVariable t, Void p) { return Optional.of(t); } @Override protected Optional<TypeVariable> defaultAction(TypeMirror e, Void p) { return Optional.absent(); } }; /** * Determines the return type of {@code method}, if called on an instance of type {@code type}. * * <p>For instance, in this example, myY.getProperty() returns List<T>, not T:<pre><code> * interface X<T> { * T getProperty(); * } * @FreeBuilder interface Y<T> extends X<List<T>> { }</code></pre> * * <p>(Unfortunately, a bug in Eclipse prevents us handling these cases correctly at the moment. * javac works fine.) */ public static TypeMirror getReturnType(TypeElement type, ExecutableElement method, Types types) { try { ExecutableType executableType = (ExecutableType) types.asMemberOf((DeclaredType) type.asType(), method); return executableType.getReturnType(); } catch (IllegalArgumentException e) { // Eclipse incorrectly throws an IllegalArgumentException here: // "element is not valid for the containing declared type" // As a workaround for the common case, fall back to the declared return type. return method.getReturnType(); } } }