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

in.workarounds.bundler.compiler.model.ReqBundlerModel Maven / Gradle / Ivy

The newest version!
package in.workarounds.bundler.compiler.model;

import com.squareup.javapoet.ClassName;

import java.util.ArrayList;
import java.util.HashMap;
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.TypeElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;

import in.workarounds.bundler.annotations.Arg;
import in.workarounds.bundler.annotations.RequireBundler;
import in.workarounds.bundler.annotations.State;
import in.workarounds.bundler.compiler.Provider;
import in.workarounds.bundler.compiler.util.names.ClassProvider;

/**
 * Created by madki on 16/10/15.
 */
public class ReqBundlerModel {
    private static final String ACTIVITY = "android.app.Activity";
    private static final String FRAGMENT = "android.app.Fragment";
    private static final String FRAGMENT_V4 = "android.support.v4.app.Fragment";
    private static final String SERVICE = "android.app.Service";

    private VARIETY variety;
    private ClassName className;
    private Element element;
    private String bundlerMethodName;
    private boolean requireAll;

    private List states;
    private List args;

    public ReqBundlerModel(Element element, Provider provider) {
        init(element, provider);

        args = retrieveArgs(element, provider);
        states = retrieveStates(element, provider);

        validateKeys(element, provider);
    }

    public ReqBundlerModel(Element element, ReqBundlerModel superClass, Provider provider) {
        init(element, provider);

        RequireBundler annotation = element.getAnnotation(RequireBundler.class);
        if (annotation.inheritState()) {
            states = getInheritedFields(retrieveStates(element, provider), superClass.getStates());
        } else {
            states = retrieveStates(element, provider);
        }

        if (annotation.inheritArgs()) {
            args = getInheritedFields(retrieveArgs(element, provider), superClass.getArgs());
        } else {
            args = retrieveArgs(element, provider);
        }
    }

    private void init(Element element, Provider provider) {
        if (element.getKind() != ElementKind.CLASS) {
            provider.error(element, "@%s annotation used on a non-class element %s",
                    RequireBundler.class.getSimpleName(),
                    element.getSimpleName());
            provider.reportError();
            return;
        }

        this.element = element;
        RequireBundler annotation = element.getAnnotation(RequireBundler.class);
        this.bundlerMethodName = annotation.bundlerMethod();
        this.requireAll = annotation.requireAll();

        variety = getVariety((TypeElement) element, provider.typeUtils());
        String qualifiedName = ((TypeElement) element).getQualifiedName().toString();
        className = ClassName.bestGuess(qualifiedName);

    }

    private void validateKeys(Element element, Provider provider) {
        Map> argKeys = new HashMap<>();
        Map> stateKeys = new HashMap<>();

        mapKeys(args, argKeys);
        mapKeys(states, stateKeys);

        checkForErrors(args, argKeys, element, provider);
        checkForErrors(states, stateKeys, element, provider);
    }

    private void checkForErrors(List fields,
                                Map> map,
                                Element element, Provider provider) {
        if(map.size() == fields.size()) return;

        for(Map.Entry> entry: map.entrySet()) {
            if(entry.getValue().size() > 1) {
                reportDuplicateKeys(entry.getKey(), entry.getValue(), element, provider);
            }
        }
    }

    private void reportDuplicateKeys(String key, List duplicateFields, Element element, Provider provider) {
        StringBuilder error = new StringBuilder("Multiple fields {");
        AnnotatedField field = null;
        for (int i = 0; i < duplicateFields.size(); i++) {
            field = duplicateFields.get(i);
            error.append(field.getLabel());
            if(i != duplicateFields.size() - 1) error.append(", ");
        }
        error.append("} in ")
                .append(className.simpleName())
                .append(" annotated with @")
                .append(field != null ? field.getAnnotation().getSimpleName() : "?")
                .append(" have the same key ")
                .append("\"")
                .append(key)
                .append("\"")
                .append(". Please make them unique.");
        provider.error(element, error.toString());
    }

    private void mapKeys(List fields, Map> map) {
        String key;
        for (AnnotatedField field : fields) {
            key = field.getKeyValue();
            if (map.containsKey(key)) {
                map.get(key).add(field);
            } else {
                List temp = new ArrayList<>();
                temp.add(field);
                map.put(key, temp);
            }
        }

    }

    private  List getInheritedFields(List currentFields, List superFields) {
        List tempFields = new ArrayList<>();
        tempFields.addAll(superFields);

        for (AnnotatedField field : currentFields) {
            removeIfLabelPresent(field.getLabel(), tempFields);
        }

        tempFields.addAll(currentFields);

        return tempFields;
    }

    private void removeIfLabelPresent(String label, List fields) {
        AnnotatedField foundField = null;
        for (AnnotatedField field : fields) {
            if (field.getLabel().equals(label)) {
                foundField = field;
            }
        }
        if (foundField != null) {
            fields.remove(foundField);
        }
    }

    private List retrieveStates(Element element, Provider provider) {
        List tempStates = new ArrayList<>();
        for (Element enclosedElement : element.getEnclosedElements()) {
            State instanceState = enclosedElement.getAnnotation(State.class);

            if (instanceState != null) {
                ClassName serializer = AnnotatedField.serializer(instanceState);
                if (serializer == null)
                    reportInvalidSerializer(enclosedElement, State.class, provider);
                StateModel state = new StateModel(enclosedElement, provider, serializer, instanceState.key());
                tempStates.add(state);
            }
        }
        return tempStates;
    }

    private List retrieveArgs(Element element, Provider provider) {
        List tempArgs = new ArrayList<>();
        for (Element enclosedElement : element.getEnclosedElements()) {
            Arg arg = enclosedElement.getAnnotation(Arg.class);

            if (arg != null) {
                ClassName serializer = AnnotatedField.serializer(arg);
                if (serializer == null)
                    reportInvalidSerializer(enclosedElement, Arg.class, provider);
                ArgModel argModel = new ArgModel(enclosedElement, provider, requireAll(), serializer, arg.key());
                tempArgs.add(argModel);
            }
        }
        return tempArgs;
    }

    private void reportInvalidSerializer(Element element, Class annotation, Provider provider) {
        provider.error(element,
                "The serializer provided with @%s annotation does not implement %s. Please provide a valid serializer",
                annotation.getSimpleName(), ClassProvider.serializer.simpleName());
    }

    private VARIETY getVariety(TypeElement element, Types typeUtils) {
        // Check subclassing
        TypeElement currentClass = element;
        while (true) {
            TypeMirror superClassType = currentClass.getSuperclass();

            if (superClassType.getKind() == TypeKind.NONE) {
                // Basis class (java.lang.Object) reached, so exit
                return VARIETY.OTHER;
            }

            if (getVariety(superClassType.toString()) != VARIETY.OTHER) {
                // Required super class found
                return getVariety(superClassType.toString());
            }

            // Moving up in inheritance tree
            currentClass = (TypeElement) typeUtils.asElement(superClassType);
        }
    }

    private VARIETY getVariety(String className) {
        switch (className) {
            case ACTIVITY:
                return VARIETY.ACTIVITY;
            case FRAGMENT:
                return VARIETY.FRAGMENT;
            case FRAGMENT_V4:
                return VARIETY.FRAGMENT_V4;
            case SERVICE:
                return VARIETY.SERVICE;
            default:
                return VARIETY.OTHER;
        }
    }

    public List getRequiredArgs() {
        List requiredArgs = new ArrayList<>();

        for (ArgModel arg : getArgs()) {
            if (arg.isRequired()) {
                requiredArgs.add(arg);
            }
        }
        return requiredArgs;
    }

    public boolean requireAll() {
        return this.requireAll;
    }

    public String getBundlerMethodName() {
        return this.bundlerMethodName;
    }

    public Element getElement() {
        return element;
    }

    public VARIETY getVariety() {
        return variety;
    }

    public String getSimpleName() {
        return className.simpleName();
    }

    public String getPackageName() {
        return className.packageName();
    }

    public ClassName getClassName() {
        return className;
    }

    public enum VARIETY {
        ACTIVITY,
        FRAGMENT,
        FRAGMENT_V4,
        SERVICE,
        OTHER
    }

    public List getArgs() {
        return args;
    }

    public List getStates() {
        return states;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy