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

io.toolisticon.aptk.tools.InterfaceUtils Maven / Gradle / Ivy

package io.toolisticon.aptk.tools;

import io.toolisticon.aptk.tools.wrapper.ExecutableElementWrapper;
import io.toolisticon.aptk.tools.wrapper.TypeElementWrapper;
import io.toolisticon.aptk.tools.wrapper.TypeParameterElementWrapper;
import io.toolisticon.aptk.tools.wrapper.VariableElementWrapper;

import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.WildcardType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * This class contains helper methods to get all methods to implement by a class implementing the interface.
 * Resolves the issue of type var replacement by concrete types.
 */
public class InterfaceUtils {

    /**
     * Method to get all methods which must be implemented by implementing types of the interface.
     * Replaces type variables according to the type hierarchy.
     *
     * @param typeElementWrapper The TypeElement of the interface to be implemented
     * @param typesToReplace     The types to apply to type variables
     * @return A set that contains all methods to be implemented
     */

    public static Set getMethodsToImplement(TypeElementWrapper typeElementWrapper, TypeMirrorWrapper... typesToReplace) {

        // skip for non interfaces
        if (!typeElementWrapper.isInterface()) {
            return Collections.emptySet();
        }

        Set result = new HashSet<>();

        Map typeVarMappings = mapTypeVars(typeElementWrapper, typesToReplace);

        // Add all methods on this level
        result.addAll(typeElementWrapper.getMethods().stream().map(e -> new InterfaceUtils.TVExecutableElementWrapper(e.unwrap(), typeVarMappings)).collect(Collectors.toList()));

        // Add all methods of parent interfaces
        for (TypeMirrorWrapper parentInterface : typeElementWrapper.getInterfaces()) {
            result.addAll(getMethodsToImplement(parentInterface.getTypeElement().get(), getTypeArgumentsForParentInterface(parentInterface, typeVarMappings)));
        }

        return result;
    }

    public static List getTypeParametersOfInterface(TypeElementWrapper typeElementWrapper, TypeMirrorWrapper interfaceToSearch, TypeMirrorWrapper... typesToReplace) {

        // skip for non interfaces
        if (!typeElementWrapper.isInterface()) {
            return Collections.emptyList();
        }

        List result = new ArrayList<>();

        Map typeVarMappings = mapTypeVars(typeElementWrapper, typesToReplace);


        // Add all methods of parent interfaces
        for (TypeMirrorWrapper parentInterface : typeElementWrapper.getInterfaces()) {

            if (interfaceToSearch.getQualifiedName().equals(parentInterface.getQualifiedName())) {

                for (TypeMirrorWrapper typeArgument : parentInterface.getWrappedTypeArguments()) {
                    if (typeArgument.isTypeVar()) {
                        result.add(typeVarMappings.get(typeArgument.toString()));
                    } else {
                        result.add(typeArgument);
                    }
                }

            } else {
                result.addAll(getTypeParametersOfInterface(parentInterface.getTypeElement().get(), interfaceToSearch, getTypeArgumentsForParentInterface(parentInterface, typeVarMappings)));
            }

        }

        return result;
    }


    /*-
    public static List getTypeParametersOfInterface(TypeElementWrapper typeElementWrapper, Class superclassToGetTypeArgumentsFor) {
        TypeMirrorWrapper typeMirrorWrapper = TypeMirrorWrapper.wrap(superclassToGetTypeArgumentsFor);
        return typeMirrorWrapper.isDeclared() ? getTypeParametersOfInterface(typeElementWrapper, typeMirrorWrapper) : Collections.emptyList();
    }

    public static List getTypeParametersOfInterface(TypeElementWrapper typeElementWrapper, TypeMirrorWrapper superclassToGetTypeArgumentsFor) {
        List result = new ArrayList<>();
        for (TypeMirrorWrapper interfaceTMW : typeElementWrapper.getInterfaces()) {
            if (interfaceTMW.getQualifiedName().equals(superclassToGetTypeArgumentsFor.getQualifiedName())) {
                result.addAll(interfaceTMW.getWrappedTypeArguments());
            } else {
                result.addAll(getTypeParametersOfInterface(interfaceTMW.getTypeElement().get(), superclassToGetTypeArgumentsFor));
            }
        }
        return result;
    }


     */

    static Map mapTypeVars(TypeElementWrapper interfaceTypeElement, TypeMirrorWrapper... interfacesTypeParameterTypes) {

        Map map = new HashMap<>();

        int i = 0;
        for (TypeParameterElementWrapper typeParameterElementWrapper : interfaceTypeElement.getTypeParameters()) {
            map.put(typeParameterElementWrapper.asType().getTypeVar().toString(), i < interfacesTypeParameterTypes.length ? interfacesTypeParameterTypes[i] : typeParameterElementWrapper.asType());
            i++;
        }

        return map;
    }

    static TypeMirrorWrapper[] getTypeArgumentsForParentInterface(TypeMirrorWrapper parentInterface, Map typeVarMappings) {
        List result = new ArrayList<>();
        for (TypeMirrorWrapper typeMirrorWrapper : parentInterface.getWrappedTypeArguments()) {

            if (typeMirrorWrapper.isTypeVar() && typeVarMappings.containsKey(typeMirrorWrapper.getTypeVar().toString())) {
                result.add(typeVarMappings.get(typeMirrorWrapper.getTypeVar().toString()));
            } else {
                result.add(typeMirrorWrapper);
            }

        }

        return result.toArray(new TypeMirrorWrapper[result.size()]);
    }


    static class TVTypeMirrorWrapper extends TypeMirrorWrapper {

        private final Map typeVarMap;

        TVTypeMirrorWrapper(TypeMirror typeMirror, Map typeVarMap) {
            super(typeMirror);
            this.typeVarMap = typeVarMap;
        }

        TypeMirrorWrapper getTypeMirrorWithReplacedTypeVars() {
            if (isTypeVar() && typeVarMap.containsKey(getTypeVar().toString())) {
                return typeVarMap.get(getTypeVar().toString());
            } else {
                return this;
            }
        }


        /**
         * Gets the String containing the type declaration of wrapped TypeMirror.
         * Replaces TypeVars.
         *
         * @return the type declaration String
         */
        public String getTypeDeclaration() {

            TypeMirrorWrapper typeMirror = getTypeMirrorWithReplacedTypeVars();

            if (typeMirror.getKind() == TypeKind.VOID) {
                return "void";
            } else if (typeMirror.isPrimitive()) {
                return typeMirror.toString();
            } else if (typeMirror.isArray()) {
                return getTypeDeclaration(typeMirror.getComponentType()) + "[]";
            } else if (typeMirror.isTypeVar()) {
                return typeMirror.toString();
            } else if (typeMirror.isDeclared()) {

                return typeMirror.getSimpleName() + (
                        typeMirror.hasTypeArguments() ? "<" + typeMirror.getWrappedTypeArguments().stream()
                                .map(e -> new TVTypeMirrorWrapper(e.unwrap(), typeVarMap))
                                .map(e -> e.getTypeDeclaration()).collect(Collectors.joining(", ")) + ">" : ""
                );

            } else if (typeMirror.isWildcardType()) {
                WildcardType wildcardType = typeMirror.getWildcardType();
                if (wildcardType.getSuperBound() != null) {
                    return "? super " + new TVTypeMirrorWrapper(wildcardType.getSuperBound(), typeVarMap).getTypeDeclaration();
                } else if (wildcardType.getExtendsBound() != null) {
                    return "? extends " + new TVTypeMirrorWrapper(wildcardType.getExtendsBound(), typeVarMap).getTypeDeclaration();
                } else {
                    return "?";
                }
            }

            return typeMirror.toString();
        }

        @Override
        public Set getImports() {
            return getTypeMirrorWithReplacedTypeVars().getImports();
        }
    }


    static class TVExecutableElementWrapper extends ExecutableElementWrapper {

        private final Map typeVarMap;

        TVExecutableElementWrapper(ExecutableElement element, Map typeVarMap) {
            super(element);
            this.typeVarMap = typeVarMap;
        }


        /**
         * Gets the method signature String for an ExecutableElement.
         * 

* This is useful for implementing of interfaces in generated classes. *

* This method works perfectly well for ExecutableElements of classes and interfaces in compilation, * but will have some limitations for precompiled classes. *

* Keep in mind that javac doesn't store parameter names in bytecode out of the box. * So passing in an ExecutableElement of a precompiled class will have arg0,arg1,.. as parameter names. * This can be changed if class is compiled with "-parameters" compiler option. *

* Annotations won't be part of the method signature. * * @return the method signature */ public String getMethodSignature() { StringBuilder builder = new StringBuilder(); // Add throws if present if (!this.getTypeParameters().isEmpty()) { builder.append("<"); builder.append( this.getTypeParameters().stream().map( tp -> tp.getSimpleName() + " extends " + tp.getBounds().stream().map(tm -> new TVTypeMirrorWrapper(tm.unwrap(), typeVarMap).getTypeDeclaration()).collect(Collectors.joining(" & ")) ).collect(Collectors.joining(", ")) ); builder.append("> "); } // return type and method name builder.append(this.getReturnType().getTypeDeclaration()).append(" ").append(this.getSimpleName()); // add parameters builder.append("("); if (this.isVarArgs()) { if (this.getParameters().size() > 1) { List nonVarargParameters = this.getParameters().subList(0, this.getParameters().size() - 1); builder.append(nonVarargParameters.stream().map(element -> new TVTypeMirrorWrapper(element.asType().unwrap(), typeVarMap).getTypeDeclaration() + " " + element.getSimpleName()).collect(Collectors.joining(", "))); builder.append(", "); } VariableElementWrapper lastVariableElement = this.getParameters().get(this.getParameters().size() - 1); builder.append( lastVariableElement.asType().getWrappedComponentType().getTypeDeclaration()) .append("... ") .append(lastVariableElement.getSimpleName() ); } else { builder.append(element.getParameters().stream().map(element -> new TVTypeMirrorWrapper(element.asType(), typeVarMap).getTypeDeclaration() + " " + element.getSimpleName()).collect(Collectors.joining(", "))); } builder.append(")"); // Add throws if present if (!this.getThrownTypes().isEmpty()) { builder.append(" throws "); builder.append(this.getThrownTypes().stream().map(TypeMirrorWrapper::getSimpleName).collect(Collectors.joining(", "))); } return builder.toString(); } @Override public Set getImports() { return super.getImports(); } /** * Gets the return type of the method. Type variables will be replaced by concrete types. * * @return The return type */ @Override public TypeMirrorWrapper getReturnType() { return new TVTypeMirrorWrapper(super.getReturnType().unwrap(), typeVarMap); } @Override public List getParameters() { return super.getParameters().stream().map(e -> new TVVariableElementWrapper(e.unwrap(), typeVarMap)).collect(Collectors.toList()); } @Override public int hashCode() { int hashCode = 0; for (VariableElementWrapper parameter : getParameters()) { hashCode += parameter.asType().getTypeDeclaration().hashCode(); } return getSimpleName().hashCode() + hashCode; } @Override public boolean equals(Object obj) { if (!(obj instanceof TVExecutableElementWrapper)) { return false; } ExecutableElementWrapper otherObj = (ExecutableElementWrapper) obj; if (!this.getSimpleName().equals(otherObj.getSimpleName())) { return false; } if (this.getParameters().size() != otherObj.getParameters().size()) { return false; } for (int i = 0; i < this.getParameters().size(); i++) { if (!this.getParameters().get(i).asType().getTypeDeclaration().equals(otherObj.getParameters().get(i).asType().getTypeDeclaration())) { return false; } } return true; } } static class TVVariableElementWrapper extends VariableElementWrapper { private final Map typeVarMap; TVVariableElementWrapper(VariableElement element, Map typeVarMap) { super(element); this.typeVarMap = typeVarMap; } @Override public TypeMirrorWrapper asType() { return new TVTypeMirrorWrapper(super.asType().unwrap(), typeVarMap); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy