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

com.malinskiy.sheldon.codegen.model.Preference Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
package com.malinskiy.sheldon.codegen.model;

import com.google.common.base.CaseFormat;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;

import com.malinskiy.sheldon.GatewayBuilder;
import com.malinskiy.sheldon.IGateway;
import com.malinskiy.sheldon.annotation.Delete;
import com.malinskiy.sheldon.annotation.Get;
import com.malinskiy.sheldon.annotation.Preferences;
import com.malinskiy.sheldon.annotation.Set;
import com.malinskiy.sheldon.codegen.ProcessingException;
import com.malinskiy.sheldon.codegen.Utils;
import com.malinskiy.sheldon.codegen.validator.ConsistencyValidator;
import com.malinskiy.sheldon.codegen.validator.ContainsValidator;
import com.malinskiy.sheldon.codegen.validator.DeleteValidator;
import com.malinskiy.sheldon.codegen.validator.GetValidator;
import com.malinskiy.sheldon.codegen.validator.SetValidator;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;

import java.io.IOException;
import java.util.Map;

import javax.annotation.Nonnull;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.Elements;

public class Preference {

    private static final String SUFFIX = "_Preferences";
    private static final String PROVIDER_FIELD_NAME = "provider";

    @Nonnull private final String namespace;
    @Nonnull private final TypeElement annotatedClassElement;
    @Nonnull private final ClassName repositoryClassName;
    @Nonnull private final Messager messager;

    @Nonnull private final Map gettersMap = Maps.newHashMap();
    @Nonnull private final Map settersMap = Maps.newHashMap();
    @Nonnull private final Map defaultsMap = Maps.newHashMap();
    @Nonnull private Optional contains = Optional.absent();
    @Nonnull private Optional deleter = Optional.absent();

    public Preference(@Nonnull TypeElement interfaceElement,
                      @Nonnull ClassName repositoryClassName,
                      @Nonnull Messager messager) throws ProcessingException {

        this.annotatedClassElement = Preconditions.checkNotNull(interfaceElement);
        this.repositoryClassName = Preconditions.checkNotNull(repositoryClassName);
        this.messager = Preconditions.checkNotNull(messager);

        Preferences annotation = interfaceElement.getAnnotation(Preferences.class);
        namespace = annotation.name();

        if (Strings.isNullOrEmpty(namespace)) {
            throw new ProcessingException(interfaceElement,
                    "name() in @%s for interface %s is null or empty! that's not allowed",
                    Preferences.class.getSimpleName(),
                    interfaceElement.getQualifiedName().toString());
        }

        //1. Populate defaults first
        for (Element element : annotatedClassElement.getEnclosedElements()) {
            if (element.getKind().equals(ElementKind.FIELD)) {
                VariableElement variable = (VariableElement) element;
                addDefault(variable);
            }
        }

        //2. Populate getters and setters
        for (Element element : annotatedClassElement.getEnclosedElements()) {
            if (element.getKind().equals(ElementKind.METHOD)) {
                ExecutableElement method = (ExecutableElement) element;
                if (method.getAnnotation(Get.class) != null) {
                    GetValidator.checkValidGetter(method, defaultsMap);
                    addGetter(method);
                } else if (method.getAnnotation(Set.class) != null) {
                    SetValidator.checkValidSetter(method, defaultsMap);
                    addSetter(method);
                } else if (method.getAnnotation(Delete.class) != null) {
                    DeleteValidator.checkValidDelete(method);
                    deleter = Optional.of(new Deleter(method, PROVIDER_FIELD_NAME));
                } else if (method.getAnnotation(com.malinskiy.sheldon.annotation.Contains.class) != null) {
                    ContainsValidator.checkValidContainsMethod(method);
                    contains = Optional.of(new com.malinskiy.sheldon.codegen.model.Contains(method, PROVIDER_FIELD_NAME));
                } else {
                    throw new ProcessingException(
                            method,
                            "Unrecognized method"
                    );
                }
            }
        }


        ConsistencyValidator.checkConsistency(defaultsMap.keySet(), gettersMap.keySet(), settersMap.keySet());
    }

    private void addDefault(VariableElement variable) throws ProcessingException {
        String name = Utils.getName(variable);

        defaultsMap.put(name, new com.malinskiy.sheldon.codegen.model.DefaultValue(variable, name));
    }

    private void addSetter(ExecutableElement method) throws ProcessingException {
        String name = Utils.getName(method);

        settersMap.put(name, new Setter(namespace, method, PROVIDER_FIELD_NAME, repositoryClassName));
    }

    private void addGetter(ExecutableElement method) throws ProcessingException {
        String name = Utils.getName(method);
        gettersMap.put(name, new com.malinskiy.sheldon.codegen.model.Getter(namespace, method, PROVIDER_FIELD_NAME, defaultsMap.get(name), repositoryClassName));
    }

    public String getNamespace() {
        return namespace;
    }

    /**
     * The original element that was annotated with @Preferences
     */
    public TypeElement getTypeElement() {
        return annotatedClassElement;
    }

    public void generateCode(Elements elementUtils, Filer filer) throws IOException, ProcessingException {
        String className = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, namespace.toLowerCase());
        String preferencesClassName = className + SUFFIX;

        PackageElement pkg = elementUtils.getPackageOf(annotatedClassElement);
        String packageName = pkg.isUnnamed() ? null : pkg.getQualifiedName().toString();

        FieldSpec.Builder providerFieldSpec =
                FieldSpec.builder(TypeName.get(IGateway.class), PROVIDER_FIELD_NAME, Modifier.PRIVATE, Modifier.FINAL);

        MethodSpec.Builder builderConstructor =
                MethodSpec.constructorBuilder()
                          .addModifiers(Modifier.PUBLIC)
                          .addParameter(GatewayBuilder.class, "builder")
                          .addStatement("this.$N = builder.namespace($S).build()", PROVIDER_FIELD_NAME, namespace);

        TypeSpec.Builder builder = TypeSpec.classBuilder(preferencesClassName)
                                           .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                                           .addSuperinterface(TypeName.get(annotatedClassElement.asType()))
                                           .addField(providerFieldSpec.build())
                                           .addMethod(builderConstructor.build());


        for (Setter setter : settersMap.values()) {
            setter.generateCode(builder);
        }

        for (com.malinskiy.sheldon.codegen.model.Getter getter : gettersMap.values()) {
            getter.generateCode(builder);
        }

        if (contains.isPresent()) {
            contains.get().generateCode(builder);
        }

        if (deleter.isPresent()) {
            deleter.get().generateCode(builder);
        }

        JavaFile javaFile = JavaFile.builder(packageName, builder.build())
                                    .build();

        javaFile.writeTo(filer);
    }

    public void generateUpdateMethod(Elements elementUtils, Filer filer) throws IOException {

    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy