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

io.micronaut.annotation.processing.GenericUtils Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2017-2020 original 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
 *
 * https://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 io.micronaut.annotation.processing;

import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.inject.processing.JavaModelUtils;

import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
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 javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Utility methods for dealing with generic type signatures.
 *
 * @author Graeme Rocher
 * @deprecated No longer needed
 */
@Deprecated(forRemoval = true, since = "4.3.0")
@Internal
public class GenericUtils {

    private final Elements elementUtils;
    private final Types typeUtils;
    private final ModelUtils modelUtils;

    /**
     * @param elementUtils The {@link Elements}
     * @param typeUtils The {@link Types}
     * @param modelUtils The {@link ModelUtils}
     */
    public GenericUtils(Elements elementUtils, Types typeUtils, ModelUtils modelUtils) {
        this.elementUtils = elementUtils;
        this.typeUtils = typeUtils;
        this.modelUtils = modelUtils;
    }

    /**
     * Builds type argument information for the given type.
     *
     * @param dt The declared type
     * @return The type argument information
     */
    Map> buildGenericTypeArgumentInfo(DeclaredType dt) {
        Element element = dt.asElement();
        return buildGenericTypeArgumentInfo(element, dt, Collections.emptyMap());
    }

    private Map> buildGenericTypeArgumentInfo(@NonNull Element element, @Nullable DeclaredType dt, Map boundTypes) {

        var beanTypeArguments = new LinkedHashMap>();
        if (dt != null) {

            List typeArguments = dt.getTypeArguments();
            if (CollectionUtils.isNotEmpty(typeArguments)) {
                var typeElement = (TypeElement) element;

                Map directTypeArguments = resolveBoundTypes(dt);
                if (CollectionUtils.isNotEmpty(directTypeArguments)) {
                    beanTypeArguments.put(typeElement.getQualifiedName().toString(), directTypeArguments);
                }
            }
        }

        if (element instanceof TypeElement typeElement) {
            if (CollectionUtils.isNotEmpty(boundTypes)) {
                beanTypeArguments.put(JavaModelUtils.getClassName(typeElement), boundTypes);
            }
            populateTypeArguments(typeElement, beanTypeArguments);
        }
        return beanTypeArguments;
    }

    /**
     * Finds the generic types for the given interface for the given class element.
     *
     * @param element The class element
     * @param interfaceName The interface
     * @return The generic types or an empty list
     */
    public List interfaceGenericTypesFor(TypeElement element, String interfaceName) {
        for (TypeMirror tm : element.getInterfaces()) {
            var declaredType = (DeclaredType) tm;
            Element declaredElement = declaredType.asElement();
            if (declaredElement instanceof TypeElement te) {
                if (interfaceName.equals(te.getQualifiedName().toString())) {
                    return declaredType.getTypeArguments();
                }
            }
        }
        return Collections.emptyList();
    }

    /**
     * Resolve the generic type arguments for the given type mirror and bound type arguments.
     *
     * @param type The declaring type
     * @param typeElement The type element
     * @param boundTypes The bound types
     * @return A map of generic type arguments
     */
    private Map resolveGenericTypes(DeclaredType type, TypeElement typeElement, Map boundTypes) {
        List typeArguments = type.getTypeArguments();
        var resolvedParameters = new LinkedHashMap();
        List typeParameters = typeElement.getTypeParameters();
        if (typeArguments.size() == typeParameters.size()) {
            Iterator i = typeArguments.iterator();
            for (TypeParameterElement typeParameter : typeParameters) {
                String parameterName = typeParameter.toString();
                TypeMirror mirror = i.next();

                TypeKind kind = mirror.getKind();
                switch (kind) {
                    case DECLARED:
                        resolvedParameters.put(parameterName, mirror);
                        break;
                    case TYPEVAR:
                        var tv = (TypeVariable) mirror;
                        String variableName = tv.asElement().getSimpleName().toString();
                        if (boundTypes.containsKey(variableName)) {
                            resolvedParameters.put(parameterName, boundTypes.get(variableName));
                        } else {
                            TypeMirror upperBound = tv.getUpperBound();
                            TypeMirror lowerBound = tv.getLowerBound();
                            if (upperBound.getKind() != TypeKind.NULL) {
                                resolvedParameters.put(parameterName, resolveTypeReference(upperBound, boundTypes));
                            } else if (lowerBound.getKind() != TypeKind.NULL) {
                                resolvedParameters.put(parameterName, resolveTypeReference(lowerBound, boundTypes));
                            }
                        }
                        continue;
                    case ARRAY:
                    case BOOLEAN:
                    case BYTE:
                    case CHAR:
                    case DOUBLE:
                    case FLOAT:
                    case INT:
                    case LONG:
                    case SHORT:
                        resolveGenericTypeParameter(resolvedParameters, parameterName, mirror, boundTypes);
                        continue;
                    case WILDCARD:
                        var wcType = (WildcardType) mirror;
                        TypeMirror extendsBound = wcType.getExtendsBound();
                        TypeMirror superBound = wcType.getSuperBound();
                        if (extendsBound != null) {
                            resolveGenericTypeParameter(resolvedParameters, parameterName, extendsBound, boundTypes);
                        } else if (superBound != null) {
                            if (superBound instanceof TypeVariable superTypeVar) {
                                final TypeMirror upperBound = superTypeVar.getUpperBound();
                                if (upperBound != null && !type.equals(upperBound)) {
                                    resolveGenericTypeParameter(resolvedParameters, parameterName, superBound, boundTypes);
                                }
                            } else {
                                resolveGenericTypeParameter(resolvedParameters, parameterName, superBound, boundTypes);
                            }
                        } else {
                            resolvedParameters.put(parameterName, elementUtils.getTypeElement(Object.class.getName()).asType());
                        }
                        break;
                    default:
                        // no-op
                }
            }
        }
        return resolvedParameters;
    }

    /**
     * Resolve a type reference to use for the given type mirror taking into account generic type variables.
     *
     * @param mirror The mirror
     * @param boundTypes The already bound types for any type variable
     * @return A type reference
     */
    protected TypeMirror resolveTypeReference(TypeMirror mirror, Map boundTypes) {
        TypeKind kind = mirror.getKind();
        switch (kind) {
            case TYPEVAR:
                var tv = (TypeVariable) mirror;
                String name = tv.asElement().getSimpleName().toString();
                if (boundTypes.containsKey(name)) {
                    return boundTypes.get(name);
                } else {
                    return resolveTypeReference(tv.getUpperBound(), boundTypes);
                }
            case WILDCARD:
                var wcType = (WildcardType) mirror;
                TypeMirror extendsBound = wcType.getExtendsBound();
                TypeMirror superBound = wcType.getSuperBound();
                if (extendsBound == null && superBound == null) {
                    return elementUtils.getTypeElement(Object.class.getName()).asType();
                } else if (extendsBound != null) {
                    return resolveTypeReference(typeUtils.erasure(extendsBound), boundTypes);
                } else {
                    return resolveTypeReference(superBound, boundTypes);
                }
            case ARRAY:
                var arrayType = (ArrayType) mirror;
                TypeMirror reference = resolveTypeReference(arrayType.getComponentType(), boundTypes);
                return typeUtils.getArrayType(reference);
            default:
                return modelUtils.resolveTypeReference(mirror);
        }
    }

    /**
     * Resolve bound types for the given declared type.
     *
     * @param type The declaring type
     * @return The type bounds
     */
    protected Map resolveBoundTypes(DeclaredType type) {
        TypeElement element = (TypeElement) type.asElement();

        List typeParameters = element.getTypeParameters();
        List typeArguments = type.getTypeArguments();
        if (typeArguments.size() == typeParameters.size()) {
            var boundTypes = CollectionUtils.newLinkedHashMap(typeParameters.size());
            Iterator i = typeArguments.iterator();
            for (TypeParameterElement typeParameter : typeParameters) {
                boundTypes.put(typeParameter.toString(), resolveTypeReference(i.next(), boundTypes));
            }
            return boundTypes;
        }

        return Collections.emptyMap();
    }

    private void resolveGenericTypeParameter(Map resolvedParameters, String parameterName, TypeMirror mirror, Map boundTypes) {
        if (mirror instanceof DeclaredType) {
            resolvedParameters.put(
                parameterName,
                mirror
            );
        } else if (mirror instanceof TypeVariable tv) {
            String variableName = tv.asElement().getSimpleName().toString();
            if (boundTypes.containsKey(variableName)) {
                resolvedParameters.put(
                    parameterName,
                    boundTypes.get(variableName)
                );
            } else {
                TypeMirror upperBound = tv.getUpperBound();
                if (upperBound instanceof DeclaredType) {
                    resolveGenericTypeParameter(
                        resolvedParameters,
                        parameterName,
                        upperBound,
                        boundTypes
                    );
                }
            }
        }
    }

    private void populateTypeArguments(TypeElement typeElement, Map> typeArguments) {
        TypeElement current = typeElement;
        while (current != null) {

            populateTypeArgumentsForInterfaces(typeArguments, current);
            TypeMirror superclass = current.getSuperclass();

            if (superclass.getKind() == TypeKind.NONE) {
                current = null;
            } else {
                if (superclass instanceof DeclaredType dt) {
                    List superArguments = dt.getTypeArguments();


                    Element te = dt.asElement();
                    if (te instanceof TypeElement element) {
                        TypeElement child = current;
                        current = element;
                        if (CollectionUtils.isNotEmpty(superArguments)) {
                            Map boundTypes = typeArguments.get(JavaModelUtils.getClassName(child));
                            if (boundTypes != null) {
                                Map types = resolveGenericTypes(dt, current, boundTypes);

                                String name = JavaModelUtils.getClassName(current);
                                typeArguments.put(name, types);
                            } else {
                                List typeParameters = current.getTypeParameters();
                                var types = CollectionUtils.newLinkedHashMap(typeParameters.size());
                                if (typeParameters.size() == superArguments.size()) {
                                    Iterator i = superArguments.iterator();
                                    for (TypeParameterElement typeParameter : typeParameters) {
                                        String n = typeParameter.getSimpleName().toString();
                                        types.put(n, i.next());
                                    }
                                }
                                typeArguments.put(JavaModelUtils.getClassName(current), types);
                            }
                        }

                    } else {
                        break;
                    }
                } else {
                    break;
                }
            }
        }
    }

    private void populateTypeArgumentsForInterfaces(Map> typeArguments, TypeElement child) {
        for (TypeMirror anInterface : child.getInterfaces()) {
            if (anInterface instanceof DeclaredType declaredType) {
                Element element = declaredType.asElement();
                if (element instanceof TypeElement te) {
                    String name = JavaModelUtils.getClassName(te);
                    if (!typeArguments.containsKey(name)) {
                        Map boundTypes = typeArguments.get(JavaModelUtils.getClassName(child));
                        if (boundTypes == null) {
                            boundTypes = Collections.emptyMap();
                        }
                        Map types = resolveGenericTypes(declaredType, te, boundTypes);
                        if (!types.isEmpty()) {
                            typeArguments.put(name, types);
                        }
                    }
                    populateTypeArgumentsForInterfaces(typeArguments, te);
                }
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy