package com.sora.util.akatsuki; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.function.Supplier; import javax.lang.model.element.TypeElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; 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.WildcardType; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import com.google.common.collect.ImmutableSet; public class ProcessorUtils { private final ImmutableSet<TypeMirror> boxedTypes; private final Types types; private final Elements elements; ProcessorUtils(Types types, Elements elements) { this.types = types; this.elements = elements; boxedTypes = ImmutableSet.of(of(Boolean.class), of(Byte.class), of(Character.class), of(Float.class), of(Integer.class), of(Long.class), of(Short.class), of(Double.class)); } public boolean isPrimitive(TypeMirror mirror) { return isBoxedType(mirror) || mirror.getKind().isPrimitive(); } public boolean isBoxedType(TypeMirror mirror) { return mirror instanceof DeclaredType && boxedTypes.stream().anyMatch(t -> isSameType(t, mirror, true)); } public boolean isArray(TypeMirror mirror) { return mirror instanceof ArrayType; } public boolean isObject(TypeMirror mirror) { return mirror instanceof DeclaredType; } public boolean isAssignable(TypeMirror lhs, TypeMirror rhs, boolean ignoreGenericTypes) { if (lhs == null || rhs == null) throw new NullPointerException("mirrors cannot be null!"); if (ignoreGenericTypes) { lhs = toUnboundDeclaredType(lhs); rhs = toUnboundDeclaredType(rhs); } return types.isAssignable(lhs, rhs); } public boolean isSameType(TypeMirror lhs, TypeMirror rhs, boolean ignoreGenericTypes) { if (lhs == null || rhs == null) throw new NullPointerException("mirrors cannot be null!"); if (ignoreGenericTypes) { lhs = toUnboundDeclaredType(lhs); rhs = toUnboundDeclaredType(rhs); } return types.isSameType(lhs, rhs); } public boolean isSameType(TypeMirror target, boolean ignoreGenericTypes, TypeMirror... mirrors) { return Arrays.stream(mirrors).anyMatch(m -> isSameType(target, m, ignoreGenericTypes)); } public DeclaredType getDeclaredType(DeclaredType mirror, TypeMirror... typeArguments) { return types.getDeclaredType((TypeElement) mirror.asElement(), typeArguments); } private TypeMirror toUnboundDeclaredType(TypeMirror mirror) { if (mirror.getKind() != TypeKind.DECLARED) return mirror; final List<? extends TypeMirror> types = getGenericTypes(mirror); if (types.isEmpty()) return mirror; TypeElement lhsElement = (TypeElement) ((DeclaredType) mirror).asElement(); return this.types.getDeclaredType(lhsElement, synthesizeUnboundType(types.size())); } private TypeMirror[] synthesizeUnboundType(int count) { final WildcardType unboundWildcardType = types.getWildcardType(null, null); TypeMirror[] mirrors = new TypeMirror[count]; for (int i = 0; i < mirrors.length; i++) { mirrors[i] = unboundWildcardType; } return mirrors; } public List<? extends TypeMirror> getGenericTypes(final TypeMirror mirror) { if (mirror instanceof DeclaredType) { return ((DeclaredType) mirror).getTypeArguments(); } else { return Collections.emptyList(); } } public TypeMirror of(Class<?> clazz) { // getName() fails because we want '.' instead of '$' for inner classes return of(clazz.getCanonicalName()); } public TypeMirror of(CharSequence qualifiedName) { TypeElement element = elements.getTypeElement(qualifiedName); if (element == null) throw new NullPointerException(qualifiedName + " cannot be found!"); return element.asType(); } public DeclaredType getClassFromAnnotationMethod(Supplier<Class<?>> supplier) { // JDK suggested way of getting type mirrors, do not waste time here, // just move on try { return (DeclaredType) of(supplier.get()); } catch (MirroredTypeException e) { // types WILL be declared return (DeclaredType) e.getTypeMirror(); } } public Optional<DeclaredType> getClassFromAnnotationMethod(Supplier<Class<?>> supplier, Class<?> excludedDefault) { DeclaredType type = getClassFromAnnotationMethod(supplier); return isSameType(type, of(excludedDefault), false) ? Optional.empty() : Optional.of(type); } @SuppressWarnings("unchecked") public List<DeclaredType> getClassArrayFromAnnotationMethod(Supplier<Class<?>[]> supplier) { // JDK suggested way of getting type mirrors, do not waste time here, // just move on try { supplier.get(); } catch (MirroredTypesException e) { // types WILL be declared return (List<DeclaredType>) e.getTypeMirrors(); } return Collections.emptyList(); } }