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

moxy.compiler.PresenterInjectorRules Maven / Gradle / Ivy

There is a newer version: 2.2.2
Show newest version
package moxy.compiler;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
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.tools.Diagnostic;

import moxy.MvpPresenter;
import moxy.MvpView;
import moxy.presenter.InjectPresenter;

import static moxy.compiler.Util.fillGenerics;

public class PresenterInjectorRules extends AnnotationRule {

    public PresenterInjectorRules(ElementKind validKind, Modifier... validModifiers) {
        super(validKind, validModifiers);
    }

    @SuppressWarnings("StringConcatenationInsideStringBufferAppend")
    @Override
    public void checkAnnotation(Element annotatedField) {
        checkEnvironment(annotatedField);

        if (annotatedField.getKind() != mValidKind) {
            mErrorBuilder
                    .append("Field " + annotatedField + " of " + annotatedField.getEnclosingElement().getSimpleName()
                            + " should be " + mValidKind.name() + ", or not mark it as @" + InjectPresenter.class
                            .getSimpleName()).append("\n");
        }

        for (Modifier modifier : annotatedField.getModifiers()) {
            if (!mValidModifiers.contains(modifier)) {
                mErrorBuilder.append("Field " + annotatedField + " of " + annotatedField.getEnclosingElement()
                        .getSimpleName() + " can't be a " + modifier).append(". Use ").append(validModifiersToString())
                        .append("\n");
            }
        }

        Element enclosingElement = annotatedField.getEnclosingElement();
        while (enclosingElement.getKind() == ElementKind.CLASS) {
            if (!enclosingElement.getModifiers().contains(Modifier.PUBLIC)) {
                mErrorBuilder.append(enclosingElement.getSimpleName() + " should be PUBLIC ");
                break;
            }

            enclosingElement = enclosingElement.getEnclosingElement();
        }
    }

    private void checkEnvironment(final Element annotatedField) {
        if (!(annotatedField.asType() instanceof DeclaredType)) {
            return;
        }

        TypeElement typeElement = (TypeElement) ((DeclaredType) annotatedField.asType()).asElement();
        String viewClassFromGeneric = getViewClassFromGeneric(typeElement, (DeclaredType) annotatedField.asType());

        Collection viewsType = getViewsType(
                (TypeElement) ((DeclaredType) annotatedField.getEnclosingElement().asType()).asElement());

        boolean result = false;

        for (TypeMirror typeMirror : viewsType) {
            if (Util.getFullClassName(typeMirror).equals(viewClassFromGeneric) || Util
                    .fillGenerics(Collections.emptyMap(), typeMirror).equals(viewClassFromGeneric)) {
                result = true;
                break;
            }
        }
        if (!result) {
            MvpCompiler.getMessager().printMessage(Diagnostic.Kind.ERROR,
                    "You can not use @InjectPresenter in classes that are not View, which is typified target Presenter",
                    annotatedField);
        }
    }

    private String getViewClassFromGeneric(TypeElement typeElement, DeclaredType declaredType) {
        TypeMirror superclass = declaredType;
        Map mTypedMap = Collections.emptyMap();

        if (!typeElement.getTypeParameters().isEmpty()) {
            mTypedMap = getChildInstanceOfClassFromGeneric(typeElement, MvpView.class);
        }

        Map parentTypes = Collections.emptyMap();
        List totalTypeArguments = new ArrayList<>(((DeclaredType) superclass).getTypeArguments());
        while (superclass.getKind() != TypeKind.NONE) {
            TypeElement superclassElement = (TypeElement) ((DeclaredType) superclass).asElement();
            List typeArguments = ((DeclaredType) superclass).getTypeArguments();
            totalTypeArguments.retainAll(typeArguments);
            final List typeParameters = superclassElement.getTypeParameters();

            Map types = new HashMap<>();
            for (int i = 0; i < typeArguments.size(); i++) {
                types.put(typeParameters.get(i).toString(), fillGenerics(parentTypes, typeArguments.get(i)));
            }

            if (superclassElement.toString().equals(MvpPresenter.class.getCanonicalName())) {
                if (!typeArguments.isEmpty()) {
                    TypeMirror typeMirror = typeArguments.get(0);
                    if (typeMirror instanceof TypeVariable) {
                        Element key = ((TypeVariable) typeMirror).asElement();

                        for (Map.Entry entry : mTypedMap.entrySet()) {
                            if (entry.getKey().toString().equals(key.toString())) {
                                return Util.getFullClassName(entry.getValue());
                            }
                        }
                    }
                }

                if (typeArguments.isEmpty() && typeParameters.isEmpty()) {
                    return ((DeclaredType) superclass).asElement().getSimpleName().toString();
                }
                // MvpPresenter is typed only on View class
                return fillGenerics(parentTypes, typeArguments);
            }

            parentTypes = types;

            superclass = superclassElement.getSuperclass();
        }

        return "";
    }

    private Map getChildInstanceOfClassFromGeneric(final TypeElement typeElement,
            final Class aClass) {
        Map result = new HashMap<>();
        for (TypeParameterElement element : typeElement.getTypeParameters()) {
            List bounds = element.getBounds();
            for (TypeMirror bound : bounds) {
                if (bound instanceof DeclaredType && ((DeclaredType) bound).asElement() instanceof TypeElement) {
                    Collection viewsType = getViewsType((TypeElement) ((DeclaredType) bound).asElement());
                    boolean isViewType = false;
                    for (TypeMirror viewType : viewsType) {
                        if (((DeclaredType) viewType).asElement().toString().equals(aClass.getCanonicalName())) {
                            isViewType = true;
                        }
                    }

                    if (isViewType) {
                        result.put(element, bound);
                        break;
                    }
                }
            }
        }

        return result;
    }

    private Collection getViewsType(TypeElement typeElement) {
        TypeMirror superclass = typeElement.asType();

        List result = new ArrayList<>();

        while (superclass.getKind() != TypeKind.NONE) {
            TypeElement superclassElement = (TypeElement) ((DeclaredType) superclass).asElement();
            Collection interfaces = new HashSet<>(superclassElement.getInterfaces());
            for (TypeMirror typeMirror : interfaces) {
                if (typeMirror instanceof DeclaredType) {
                    result.addAll(getViewsType((TypeElement) ((DeclaredType) typeMirror).asElement()));
                }
            }
            result.addAll(interfaces);
            result.add(superclass);

            superclass = superclassElement.getSuperclass();
        }

        return result;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy