All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.slimgears.apt.util.ElementUtils Maven / Gradle / Ivy

There is a newer version: 0.7.58
Show newest version
package com.slimgears.apt.util;

import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.google.common.base.Preconditions;
import com.slimgears.apt.data.Environment;
import com.slimgears.apt.data.TypeInfo;
import com.slimgears.util.stream.Optionals;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.IntersectionType;
import javax.lang.model.type.MirroredTypeException;
import javax.lang.model.type.MirroredTypesException;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.type.WildcardType;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.slimgears.util.stream.Streams.ofType;
import static com.slimgears.util.stream.Streams.self;

@SuppressWarnings({"WeakerAccess", "UnstableApiUsage"})
public class ElementUtils {
    public static boolean isKnownType(TypeElement typeElement) {
        return Environment.instance().isIgnoredType(TypeInfo.of(typeElement));
    }

    public static boolean isUnknownType(TypeElement typeElement) {
        return !isKnownType(typeElement);
    }

    public static boolean isPublic(Element element) {
        return modifiersContainAll(element, Modifier.PUBLIC);
    }

    public static boolean isNotStatic(Element element) {
        return modifiersContainNone(element, Modifier.STATIC);
    }

    public static boolean isAbstract(Element element) {
        return modifiersContainNone(element, Modifier.DEFAULT) && modifiersContainAll(element, Modifier.ABSTRACT);
    }

    public static boolean isInterface(Element element) {
        return isOfKind(element, ElementKind.INTERFACE);
    }

    public static boolean isOfKind(Element element, ElementKind kind) {
        return element.getKind() == kind;
    }

    public static boolean modifiersContainAll(Element element, Modifier... modifiers) {
        Set elementModifiers = element.getModifiers();
        return elementModifiers.containsAll(Arrays.asList(modifiers));
    }

    public static boolean modifiersContainNone(Element element, Modifier... modifiers) {
        Set elementModifiers = element.getModifiers();
        return Stream.of(modifiers).noneMatch(elementModifiers::contains);
    }

    public static String fullName(Element element) {
        return Optional
            .ofNullable(element.getEnclosingElement())
            .map(e -> fullName(e) + "." + element.getSimpleName().toString())
            .orElseGet(element::toString);
    }

    public static Predicate ofKind(ElementKind kind) {
        return el -> el.getKind() == kind;
    }

    public static boolean isEnum(TypeElement typeElement) {
        return ofKind(ElementKind.ENUM).test(typeElement);
    }

    public static boolean isEnumConstant(Element element) {
        return ofKind(ElementKind.ENUM_CONSTANT).test(element);
    }

    public static boolean hasAnnotation(Element elemenet, Class annotationCls) {
        return MoreElements.isAnnotationPresent(elemenet, annotationCls);
    }

    public static boolean hasErrors(TypeMirror typeMirror) {
        return typeMirror.getKind() == TypeKind.ERROR || (typeMirror.getKind() == TypeKind.DECLARED && MoreTypes
                .asDeclared(typeMirror)
                .getTypeArguments()
                .stream()
                .anyMatch(ElementUtils::hasErrors));
    }

    public static Stream findErrors(TypeMirror typeMirror) {
        Stream thisError = typeMirror.getKind() == TypeKind.ERROR
                ? Stream.of(typeMirror)
                : Stream.empty();

        return typeMirror.getKind() == TypeKind.DECLARED
                ? Stream.concat(
                        thisError,
                        MoreTypes
                                .asDeclared(typeMirror)
                                .getTypeArguments()
                                .stream()
                                .flatMap(ElementUtils::findErrors))
                : thisError;
    }

    public static boolean hasInterface(TypeMirror type, Class... interfaceTypes) {
        if (type.getKind() != TypeKind.DECLARED) {
            return false;
        }

        if (hasExtendsBound(type, interfaceTypes)) {
            return true;
        }

        Set actualInterfaces = Optional.of(type)
                .flatMap(Optionals.ofType(DeclaredType.class))
                .map(ElementUtils::getHierarchy)
                .orElseGet(Stream::empty)
                .collect(Collectors.toSet());

        return Arrays
                .stream(interfaceTypes)
                .allMatch(it -> actualInterfaces.stream().anyMatch(t -> MoreTypes.isTypeOf(it, t)));
    }

    private static boolean hasExtendsBound(TypeMirror type, Class... interfaceTypes) {
        if (type.getKind() == TypeKind.WILDCARD) {
            return boundHasInterface(((WildcardType)type).getExtendsBound(), interfaceTypes);
        } else if (type.getKind() == TypeKind.TYPEVAR) {
            return boundHasInterface(((TypeVariable)type).getUpperBound(), interfaceTypes);
        }
        return false;
    }

    private static boolean boundHasInterface(TypeMirror bound, Class... interfaceTypes) {
        if (bound.getKind() == TypeKind.DECLARED) {
            return hasInterface(bound, interfaceTypes);
        }
        if (bound.getKind() == TypeKind.WILDCARD || bound.getKind() == TypeKind.TYPEVAR) {
            return hasExtendsBound(bound, interfaceTypes);
        }
        if (bound.getKind() == TypeKind.INTERSECTION) {
            return ((IntersectionType)bound).getBounds()
                    .stream()
                    .flatMap(ofType(DeclaredType.class))
                    .flatMap(ElementUtils::getHierarchy)
                    .collect(Collectors.toSet())
                    .containsAll(Arrays.asList(interfaceTypes));
        }
        return false;
    }

    public static  Stream getMethodAnnotation(ExecutableElement element, Class cls) {
        return getOverrides(element).map(ee -> ee.getAnnotation(cls)).filter(Objects::nonNull);
    }

    public static Stream getMethodAnnotations(ExecutableElement element) {
        return getOverrides(element).flatMap(ee -> ee.getAnnotationMirrors().stream());
    }

    public static Stream getOverrides(ExecutableElement element) {
        TypeElement overridenType = MoreElements.asType(element.getEnclosingElement());
        return Stream.concat(Stream.of(element), getOverrides(overridenType, element));
    }

    private static Stream getOverrides(TypeElement overridenType, ExecutableElement element) {
        return Stream
                .concat(
                        Stream.of(overridenType.getSuperclass()).flatMap(ElementUtils::toTypeElement),
                        overridenType.getInterfaces().stream().flatMap(ElementUtils::toTypeElement))
                .flatMap(superType -> Stream
                        .concat(Stream.of(superType)
                                .map(TypeElement::getEnclosedElements)
                                .flatMap(Collection::stream)
                                .flatMap(ofType(ExecutableElement.class))
                                .filter(ee -> overrides(element, ee)),
                                getOverrides(superType, element)));
    }

    public static boolean overrides(ExecutableElement overrider, ExecutableElement overriden) {
        return Environment.instance().elements().overrides(overrider, overriden, MoreElements.asType(overrider.getEnclosingElement()));
    }

    public static Stream getReferencedTypes(TypeElement typeElement) {
        Stream enclosedElements = typeElement
                .getEnclosedElements()
                .stream()
                .filter(ElementUtils::isPublic)
                .filter(ElementUtils::isNotStatic)
                .flatMap(element -> Stream.concat(
                        Stream.of(element)
                                .flatMap(ofType(ExecutableElement.class))
                                .flatMap(ElementUtils::getReferencedTypes),
                        Stream.of(element)
                                .flatMap(ofType(VariableElement.class))
                                .flatMap(v -> getReferencedTypeParams(v.asType()))
                                .flatMap(ElementUtils::toTypeElement)))
                .filter(ElementUtils::isUnknownType)
                .distinct();
        return Stream.concat(getHierarchy(typeElement), enclosedElements);
    }

    public static Stream getReferencedTypes(ExecutableElement executableElement) {
        return Stream.concat(
                Stream.of(executableElement.getReturnType()),
                executableElement.getParameters().stream()
                        .map(VariableElement::asType))
                        .flatMap(ElementUtils::getReferencedTypeParams)
                .flatMap(ElementUtils::toTypeElement)
                .distinct();
    }

    public static Stream getReferencedTypeParams(TypeMirror type) {
        return Stream.of(
                Stream.of(type)
                        .flatMap(ofType(DeclaredType.class)),
                Stream.of(type)
                        .flatMap(ofType(WildcardType.class))
                        .flatMap(wt -> Stream.of(wt.getExtendsBound(), wt.getSuperBound()))
                        .flatMap(ofType(DeclaredType.class))
                        .flatMap(t -> t.getTypeArguments().stream())
                        .flatMap(ElementUtils::getReferencedTypeParams),
                Stream.of(type)
                        .flatMap(ofType(DeclaredType.class))
                        .flatMap(t -> t.getTypeArguments().stream())
                        .flatMap(ElementUtils::getReferencedTypeParams),
                Stream.of(type)
                        .flatMap(ofType(ArrayType.class))
                        .map(ArrayType::getComponentType))
                .flatMap(self())
                .filter(DeclaredType.class::isInstance)
                .map(typeMirror -> (TypeMirror)typeMirror)
                .distinct();
    }

    public static Stream getHierarchy(DeclaredType declaredType) {
        return Stream.of(
                Stream.of(declaredType),
                getSuperClass(declaredType).flatMap(ElementUtils::getHierarchy),
                getInterfaces(declaredType).flatMap(ElementUtils::getHierarchy))
                .flatMap(self())
                .distinct();
    }

    public static Stream getMethods(DeclaredType declaredType) {
        return toTypeElement(declaredType)
                .map(TypeElement::getEnclosedElements)
                .flatMap(Collection::stream)
                .flatMap(ofType(ExecutableElement.class))
                .filter(ElementUtils::isPublic)
                .filter(ElementUtils::isNotStatic);
    }

    public static Stream getHierarchy(TypeElement typeElement) {
        return Stream.of(
                Stream.of(typeElement),
                Stream.of(typeElement)
                        .map(TypeElement::getSuperclass)
                        .flatMap(ElementUtils::toTypeElement)
                        .flatMap(ElementUtils::getHierarchy),
                Stream.of(typeElement)
                        .flatMap(t -> t.getInterfaces().stream())
                        .flatMap(ElementUtils::toTypeElement)
                        .flatMap(ElementUtils::getHierarchy))
                .flatMap(self())
                .distinct();
    }

    public static Stream toDeclaredTypeStream(TypeElement typeElement) {
        return Stream.of(typeElement)
                .map(TypeElement::asType)
                .flatMap(ofType(DeclaredType.class));
    }

    public static DeclaredType toDeclaredType(TypeElement typeElement) {
        return toDeclaredTypeStream(typeElement)
                .findFirst()
                .orElseThrow(() -> new RuntimeException("Cannot convert " + typeElement.getQualifiedName() + " to DeclaredType"));
    }

    public static Stream toTypeElement(TypeMirror type) {
        return Stream.of(type)
                .flatMap(ofType(DeclaredType.class))
                .map(DeclaredType::asElement)
                .flatMap(ofType(TypeElement.class));
    }

    public static  TypeInfo[] typesFromAnnotation(A annotation, Function classRetriever) {
        return Stream
                .of(typeMirrorsFromAnnotation(annotation, classRetriever))
                .map(TypeInfo::of)
                .toArray(TypeInfo[]::new);
    }

    public static  TypeMirror[] typeMirrorsFromAnnotation(A annotation, Function classRetriever) {
        try {
            return Stream
                    .of(classRetriever.apply(annotation))
                    .map(TypeMirror.class::cast)
                    .toArray(TypeMirror[]::new);
        } catch (MirroredTypesException e) {
            return e.getTypeMirrors().toArray(new TypeMirror[0]);
        }
    }

    public static  TypeInfo typeFromAnnotation(A annotation, Function classRetriever) {
        return TypeInfo.of(Preconditions.checkNotNull(typeMirrorFromAnnotation(annotation, classRetriever)));
    }

    public static TypeMirror findInterface(TypeMirror typeMirror, Class interfaceType) {
        Optional type = Optional.ofNullable(typeMirror);
        return Stream.of(
                type.flatMap(Optionals.ofType(IntersectionType.class))
                        .map(it -> it.getBounds().stream())
                        .orElseGet(Stream::empty),
                type
                        .flatMap(Optionals.ofType(TypeVariable.class))
                        .map(TypeVariable::getUpperBound)
                        .map(t -> findInterface(t, interfaceType))
                        .map(Stream::of)
                        .orElseGet(Stream::empty),
                type
                        .flatMap(Optionals.ofType(WildcardType.class))
                        .map(WildcardType::getExtendsBound)
                        .map(t -> findInterface(t, interfaceType))
                        .map(Stream::of)
                        .orElseGet(Stream::empty),
                type
                        .flatMap(Optionals.ofType(DeclaredType.class))
                        .map(ElementUtils::getHierarchy)
                        .orElseGet(Stream::empty))
                .flatMap(Function.identity())
                .map(t -> (TypeMirror)t)
                .filter(t -> MoreTypes.isTypeOf(interfaceType, t))
                .findFirst()
                .orElse(null);
    }

    public static  TypeMirror typeMirrorFromAnnotation(A annotation, Function classRetriever) {
        try {
            classRetriever.apply(annotation);
            return null;
        } catch (MirroredTypeException e) {
            return e.getTypeMirror();
        }
    }

    private static Stream getSuperClass(DeclaredType type) {
        return toTypeElement(type)
                .map(TypeElement::getSuperclass)
                .flatMap(ofType(DeclaredType.class));
    }

    private static Stream getInterfaces(DeclaredType type) {
        return toTypeElement(type)
                .flatMap(e -> e.getInterfaces().stream())
                .flatMap(ofType(DeclaredType.class));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy