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

net.beanstandard.compiler.MethodScanner Maven / Gradle / Ivy

package net.beanstandard.compiler;

import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;

import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import static java.util.stream.Collectors.groupingBy;
import static net.beanstandard.compiler.Arity0.parameterlessMethods;
import static net.beanstandard.compiler.Arity1.singleParameterMethodsReturningVoid;
import static net.beanstandard.compiler.BeanStandardProcessor.rawType;

final class MethodScanner {

  static final Pattern GETTER_PATTERN =
      Pattern.compile("^get[A-Z].*$");
  static final Pattern IS_PATTERN =
      Pattern.compile("^is[A-Z].*$");
  static final Pattern SETTER_PATTERN =
      Pattern.compile("^set[A-Z].*$");

  private final TypeElement sourceClassElement;

  private MethodScanner(TypeElement sourceClassElement) {
    this.sourceClassElement = sourceClassElement;
  }

  static MethodScanner create(TypeElement sourceClassElement) {
    checkSourceElement(sourceClassElement);
    return new MethodScanner(sourceClassElement);
  }

  List scan() {
    Map setters = setters();
    List result = new ArrayList<>();
    parameterlessMethods(sourceClassElement).stream()
        .filter(m -> m.getParameters().isEmpty())
        .filter(m -> m.getReturnType().getKind() != TypeKind.VOID)
        .filter(m -> GETTER_PATTERN.matcher(m.getSimpleName().toString()).matches() ||
            TypeKind.BOOLEAN == m.getReturnType().getKind() &&
                IS_PATTERN.matcher(m.getSimpleName().toString()).matches())
        .collect(groupingBy(m -> m.getSimpleName().toString()))
        .forEach((name, executableElements) -> {
          ExecutableElement getter = executableElements.get(0);
          ExecutableElement matchingSetter = matchingSetter(getter, setters);
          if (matchingSetter != null) {
            result.add(AccessorPair.create(getter, matchingSetter));
          } else {
            TypeName type = TypeName.get(getter.getReturnType());
            if (type instanceof ParameterizedTypeName &&
                rawType(type).equals(TypeName.get(List.class))) {
              result.add(AccessorPair.create(getter));
            }
          }
        });
    return result;
  }

  private static ExecutableElement matchingSetter(
      ExecutableElement getter,
      Map setters) {
    if (!getter.getParameters().isEmpty() ||
        getter.getReturnType().getKind() == TypeKind.VOID) {
      throw new AssertionError();
    }
    String truncatedGetterName = truncatedGetterName(getter);
    return setters.get(SetterSignature.ofSetter("set" +
        truncatedGetterName, getter.getReturnType()));
  }

  private static String truncatedGetterName(ExecutableElement getter) {
    String getterName = getter.getSimpleName().toString();
    String truncatedGetterName;
    if (GETTER_PATTERN.matcher(getterName).matches()) {
      truncatedGetterName = getterName.substring(3);
    } else if (getter.getReturnType().getKind() == TypeKind.BOOLEAN &&
        IS_PATTERN.matcher(getterName).matches()) {
      truncatedGetterName = getterName.substring(2);
    } else {
      throw new AssertionError();
    }
    return truncatedGetterName;
  }

  private Map setters() {
    Map result = new HashMap<>();
    singleParameterMethodsReturningVoid(sourceClassElement).stream()
        .filter(m -> m.getParameters().size() == 1)
        .filter(m -> m.getReturnType().getKind() == TypeKind.VOID)
        .filter(m -> SETTER_PATTERN.matcher(m.getSimpleName().toString()).matches())
        .collect(groupingBy(m -> m.getSimpleName().toString()))
        .forEach((name, executableElements) -> executableElements.forEach(executableElement -> {
          result.put(SetterSignature.ofSetter(executableElement), executableElement);
        }));
    return result;
  }

  private static void checkSourceElement(TypeElement sourceClassElement) {
    if (!sourceClassElement.getTypeParameters().isEmpty()) {
      throw new ValidationException("Type parameters not allowed here", sourceClassElement);
    }
    if (sourceClassElement.getModifiers().contains(Modifier.PRIVATE)) {
      throw new ValidationException("The class may not be private", sourceClassElement);
    }
    if (sourceClassElement.getModifiers().contains(Modifier.ABSTRACT)) {
      throw new ValidationException("The class may not be abstract", sourceClassElement);
    }
    if (sourceClassElement.getEnclosingElement() != null &&
        sourceClassElement.getEnclosingElement().getKind() == ElementKind.CLASS &&
        !sourceClassElement.getModifiers().contains(Modifier.STATIC)) {
      throw new ValidationException("The inner class must be static " +
          sourceClassElement.getEnclosingElement(), sourceClassElement);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy