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

net.zerobuilder.compiler.analyse.DtoGoalElement Maven / Gradle / Ivy

The newest version!
package net.zerobuilder.compiler.analyse;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeVariableName;
import net.zerobuilder.Builder;
import net.zerobuilder.Style;
import net.zerobuilder.Updater;
import net.zerobuilder.compiler.generate.Access;
import net.zerobuilder.compiler.generate.DtoContext;
import net.zerobuilder.compiler.generate.DtoGoalDetails.AbstractRegularDetails;
import net.zerobuilder.compiler.generate.DtoGoalDetails.BeanGoalDetails;
import net.zerobuilder.compiler.generate.DtoGoalDetails.ConstructorGoalDetails;
import net.zerobuilder.compiler.generate.DtoGoalDetails.InstanceMethodGoalDetails;
import net.zerobuilder.compiler.generate.DtoGoalDetails.StaticMethodGoalDetails;

import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.toList;
import static javax.lang.model.element.ElementKind.CONSTRUCTOR;
import static javax.lang.model.element.Modifier.STATIC;
import static net.zerobuilder.compiler.analyse.DtoGoalElement.ModuleChoice.BUILDER;
import static net.zerobuilder.compiler.analyse.DtoGoalElement.ModuleChoice.UPDATER;
import static net.zerobuilder.compiler.common.LessTypes.asTypeElement;
import static net.zerobuilder.compiler.common.LessTypes.isDeclaredType;
import static net.zerobuilder.compiler.generate.ZeroUtil.downcase;
import static net.zerobuilder.compiler.generate.ZeroUtil.simpleName;
import static net.zerobuilder.compiler.generate.ZeroUtil.transform;

final class DtoGoalElement {

  interface GoalElementCases {
    R regularGoal(AbstractRegularGoalElement goal);
    R beanGoal(BeanGoalElement goal);
  }

  private static  Function asFunction(GoalElementCases cases) {
    return goal -> goal.accept(cases);
  }

  interface AbstractGoalElement {
     R accept(GoalElementCases goalElementCases);
  }

  interface RegularGoalElementCases {
    R general(RegularGoalElement regular);
    R projectable(RegularProjectableGoalElement projectable);
  }

  interface AbstractRegularGoalElement extends AbstractGoalElement {
     R accept(RegularGoalElementCases cases);
  }

  private static  Function asFunction(RegularGoalElementCases cases) {
    return element -> element.accept(cases);
  }

  static  Function regularGoalElementCases(
      Function generalFunction,
      Function projectableFunction) {
    return asFunction(new RegularGoalElementCases() {
      @Override
      public R general(RegularGoalElement regular) {
        return generalFunction.apply(regular);
      }
      @Override
      public R projectable(RegularProjectableGoalElement projectable) {
        return projectableFunction.apply(projectable);
      }
    });
  }

  static final Function executableElement =
      regularGoalElementCases(
          general -> general.executableElement,
          projectable -> projectable.executableElement);

  static  Function goalElementCases(
      Function regularGoalFunction,
      Function beanGoalFunction) {
    return asFunction(new GoalElementCases() {
      @Override
      public R regularGoal(AbstractRegularGoalElement executableGoal) {
        return regularGoalFunction.apply(executableGoal);
      }

      @Override
      public R beanGoal(BeanGoalElement beanGoal) {
        return beanGoalFunction.apply(beanGoal);
      }
    });
  }

  private static final Function regularGoalName =
      regularGoalElementCases(
          general -> general.details.name,
          projected -> projected.details.name);

  static final Function goalName =
      goalElementCases(
          regularGoalName,
          bean -> bean.details.name);

  static final Function element =
      goalElementCases(
          regularGoalElementCases(
              regular -> regular.executableElement,
              projected -> projected.executableElement),
          bean -> bean.beanType);

  static final class RegularGoalElement implements AbstractRegularGoalElement {
    final AbstractRegularDetails details;
    final ExecutableElement executableElement;
    final GoalModifiers goalAnnotation;
    final DtoContext.GoalContext context;
    final Style style;

    private RegularGoalElement(ExecutableElement element, AbstractRegularDetails details,
                               DtoContext.GoalContext context, Style style) {
      this.goalAnnotation = GoalModifiers.create(element);
      this.details = details;
      this.executableElement = element;
      this.context = context;
      this.style = style;
    }

    @Override
    public  R accept(GoalElementCases goalElementCases) {
      return goalElementCases.regularGoal(this);
    }

    @Override
    public  R accept(RegularGoalElementCases cases) {
      return cases.general(this);
    }
  }

  static final class RegularProjectableGoalElement implements AbstractRegularGoalElement {
    final AbstractRegularDetails details;
    final ExecutableElement executableElement;
    final GoalModifiers goalAnnotation;
    final DtoContext.GoalContext context;

    private RegularProjectableGoalElement(ExecutableElement element, AbstractRegularDetails details, DtoContext.GoalContext context) {
      this.goalAnnotation = GoalModifiers.create(element);
      this.details = details;
      this.executableElement = element;
      this.context = context;
    }

    @Override
    public  R accept(GoalElementCases goalElementCases) {
      return goalElementCases.regularGoal(this);
    }

    @Override
    public  R accept(RegularGoalElementCases cases) {
      return cases.projectable(this);
    }
  }

  private static List parameterNames(ExecutableElement element) {
    return transform(element.getParameters(),
        parameter -> parameter.getSimpleName().toString());
  }

  static final class BeanGoalElement implements AbstractGoalElement {
    final BeanGoalDetails details;
    final TypeElement beanType;
    final ModuleChoice moduleChoice;

    private BeanGoalElement(ClassName goalType, String name, TypeElement beanType,
                            ModuleChoice moduleChoice, DtoContext.GoalContext context) {
      this.moduleChoice = moduleChoice;
      this.details = new BeanGoalDetails(goalType, name, Access.PUBLIC, context);
      this.beanType = beanType;
    }

    static List create(TypeElement beanType, DtoContext.GoalContext context) {
      ClassName goalType = ClassName.get(beanType);
      String name = downcase(simpleName(goalType));
      List goalOptions = Arrays.asList(BUILDER, UPDATER);
      return transform(goalOptions,
          goalOption -> new BeanGoalElement(goalType, name, beanType, goalOption, context));
    }

    @Override
    public  R accept(GoalElementCases goalElementCases) {
      return goalElementCases.beanGoal(this);
    }
  }

  enum ModuleChoice {
    UPDATER, BUILDER
  }

  private static List goalOptions(ExecutableElement element) {
    ArrayList options = new ArrayList<>(2);
    if (element.getAnnotation(Builder.class) != null) {
      options.add(BUILDER);
    }
    if (element.getAnnotation(Updater.class) != null) {
      options.add(UPDATER);
    }
    return options;
  }

  static TypeName goalType(ExecutableElement goal) {
    switch (goal.getKind()) {
      case CONSTRUCTOR:
        return ClassName.get(goal.getEnclosingElement().asType());
      default:
        return TypeName.get(goal.getReturnType());
    }
  }

  static Function> createRegular(DtoContext.GoalContext context) {
    return element -> {
      TypeName goalType = goalType(element);
      GoalModifiers modifiers = GoalModifiers.create(element);
      List goalOptions = goalOptions(element);
      String methodName = element.getSimpleName().toString();
      return transform(goalOptions,
          goalOption ->
              goalOption == BUILDER ?
                  createBuilderGoal(element, goalType, modifiers, methodName,
                      parameterNames(element), context) :
                  createUpdaterGoal(element, goalType, modifiers, methodName,
                      parameterNames(element), context));
    };
  }

  private static AbstractRegularGoalElement createUpdaterGoal(ExecutableElement element, TypeName goalType,
                                                              GoalModifiers goalModifiers,
                                                              String methodName,
                                                              List parameterNames,
                                                              DtoContext.GoalContext context) {
    if (element.getKind() == CONSTRUCTOR) {
      return new RegularProjectableGoalElement(element, ConstructorGoalDetails.create(
          ClassName.get(asTypeElement(element.getEnclosingElement().asType())),
          goalModifiers.goalName, parameterNames, goalModifiers.access, instanceTypevars(element),
          goalModifiers.lifecycle), context);
    }
    AbstractRegularDetails details =
        element.getModifiers().contains(STATIC) ?
            StaticMethodGoalDetails.create(goalType, goalModifiers.goalName, parameterNames, methodName,
                goalModifiers.access, methodTypevars(element), goalModifiers.lifecycle) :
            InstanceMethodGoalDetails.create(goalType, goalModifiers.goalName, parameterNames, methodName,
                goalModifiers.access,
                methodTypevars(element),
                instanceTypevars(element),
                returnTypeInstanceTypevars(element),
                goalModifiers.lifecycle);
    return new RegularProjectableGoalElement(element, details, context);
  }

  private static List instanceTypevars(ExecutableElement element) {
    TypeElement type = asTypeElement(element.getEnclosingElement().asType());
    return transform(type.getTypeParameters(), TypeVariableName::get);
  }

  private static List returnTypeInstanceTypevars(ExecutableElement element) {
    if (!isDeclaredType(element.getReturnType())) {
      return emptyList();
    }
    TypeElement type = asTypeElement(element.getReturnType());
    return transform(type.getTypeParameters(), TypeVariableName::get);
  }

  private static List methodTypevars(ExecutableElement element) {
    return element.getTypeParameters().stream()
        .map(TypeVariableName::get)
        .collect(toList());
  }

  private static AbstractRegularGoalElement createBuilderGoal(ExecutableElement element, TypeName goalType,
                                                              GoalModifiers goalModifiers,
                                                              String methodName,
                                                              List parameterNames,
                                                              DtoContext.GoalContext context) {
    Builder builderAnnotation = element.getAnnotation(Builder.class);
    if (element.getKind() == CONSTRUCTOR) {
      ConstructorGoalDetails details = ConstructorGoalDetails.create(
          ClassName.get(asTypeElement(element.getEnclosingElement().asType())),
          goalModifiers.goalName, parameterNames, goalModifiers.access, instanceTypevars(element),
          goalModifiers.lifecycle);
      return new RegularGoalElement(element, details, context, builderAnnotation.style());
    }
    AbstractRegularDetails details =
        element.getModifiers().contains(STATIC) ?
            StaticMethodGoalDetails.create(goalType, goalModifiers.goalName, parameterNames, methodName,
                goalModifiers.access,
                methodTypevars(element), goalModifiers.lifecycle) :
            InstanceMethodGoalDetails.create(goalType, goalModifiers.goalName, parameterNames, methodName,
                goalModifiers.access,
                methodTypevars(element),
                instanceTypevars(element),
                returnTypeInstanceTypevars(element),
                goalModifiers.lifecycle);
    return new RegularGoalElement(element, details, context, builderAnnotation.style());
  }

  private DtoGoalElement() {
    throw new UnsupportedOperationException("no instances");
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy