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

io.avaje.validation.generator.ElementAnnotationContainer Maven / Gradle / Ivy

package io.avaje.validation.generator;

import static io.avaje.validation.generator.APContext.typeElement;
import static io.avaje.validation.generator.PrimitiveUtil.isPrimitiveValidationAnnotations;
import static java.util.stream.Collectors.toMap;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;

record ElementAnnotationContainer(
    UType genericType,
    boolean hasValid,
    Map annotations,
    Map typeUse1,
    Map typeUse2,
    Map crossParam) {

  static ElementAnnotationContainer create(Element element) {
    final var hasValid = ValidPrism.isPresent(element);
    Map typeUse1;
    Map typeUse2;
    final Map crossParam = new HashMap<>();
    UType uType;
    if (element instanceof final ExecutableElement executableElement) {
      uType = UType.parse(executableElement.getReturnType());
    } else {
      uType = UType.parse(element.asType());
    }

    typeUse1 =
      Optional.ofNullable(uType.param0()).map(UType::annotations).stream()
        .flatMap(List::stream)
        .filter(ElementAnnotationContainer::hasMetaConstraintAnnotation)
        .collect(
          toMap(
            a -> UType.parse(a.getAnnotationType()),
            a -> AnnotationUtil.annotationAttributeMap(a, element)));

    typeUse2 =
      Optional.ofNullable(uType.param1()).map(UType::annotations).stream()
        .flatMap(List::stream)
        .filter(ElementAnnotationContainer::hasMetaConstraintAnnotation)
        .collect(
          toMap(
            a -> UType.parse(a.getAnnotationType()),
            a -> AnnotationUtil.annotationAttributeMap(a, element)));

    final var annotations =
      element.getAnnotationMirrors().stream()
        .filter(m -> !ValidPrism.isInstance(m))
        .filter(ElementAnnotationContainer::hasMetaConstraintAnnotation)
        .map(a -> {
          if (CrossParamConstraintPrism.isPresent(a.getAnnotationType().asElement())) {
            crossParam.put(
              UType.parse(a.getAnnotationType()),
              AnnotationUtil.annotationAttributeMap(a, element));
            return null;
          }
          return a;
        })
        .filter(Objects::nonNull)
        .collect(
          toMap(
            a -> UType.parse(a.getAnnotationType()),
            a -> AnnotationUtil.annotationAttributeMap(a, element)));

    if (Util.isNonNullable(element)) {
      var nonNull = UType.parse(APContext.typeElement(NonNullPrism.PRISM_TYPE).asType());
      annotations.put(nonNull, "Map.of(\"message\",\"{avaje.NotNull.message}\")");
    }

    return new ElementAnnotationContainer(uType, hasValid, annotations, typeUse1, typeUse2, crossParam);
  }

  static boolean hasMetaConstraintAnnotation(AnnotationMirror m) {
    return hasMetaConstraintAnnotation(m.getAnnotationType().asElement())
      || ValidPrism.isInstance(m);
  }

  static boolean hasMetaConstraintAnnotation(Element element) {
    return ConstraintPrism.isPresent(element);
  }

  // it seems we cannot directly retrieve mirrors from var elements, so var Elements needs special handling

  static ElementAnnotationContainer create(VariableElement varElement) {
    var uType = UType.parse(varElement.asType());
    final var annotations =
      uType.annotations().stream()
        .filter(m -> !ValidPrism.isInstance(m))
        .filter(ElementAnnotationContainer::hasMetaConstraintAnnotation)
        .collect(
          toMap(
            a -> UType.parse(a.getAnnotationType()),
            a -> AnnotationUtil.annotationAttributeMap(a, varElement)));

    var typeUse1 =
      Optional.ofNullable(uType.param0()).map(UType::annotations).stream()
        .flatMap(List::stream)
        .filter(ElementAnnotationContainer::hasMetaConstraintAnnotation)
        .collect(
          toMap(
            a -> UType.parse(a.getAnnotationType()),
            a -> AnnotationUtil.annotationAttributeMap(a, varElement)));

    var typeUse2 =
      Optional.ofNullable(uType.param1()).map(UType::annotations).stream()
        .flatMap(List::stream)
        .filter(ElementAnnotationContainer::hasMetaConstraintAnnotation)
        .collect(
          toMap(
            a -> UType.parse(a.getAnnotationType()),
            a -> AnnotationUtil.annotationAttributeMap(a, varElement)));

    final boolean hasValid = uType.annotations().stream().anyMatch(ValidPrism::isInstance);

    if (Util.isNonNullable(varElement)) {
      var nonNull = UType.parse(APContext.typeElement(NonNullPrism.PRISM_TYPE).asType());
      annotations.put(nonNull, "Map.of(\"message\",\"{avaje.NotNull.message}\")");
    }

    return new ElementAnnotationContainer(uType, hasValid, annotations, typeUse1, typeUse2, Map.of());
  }

  public void addImports(Set importTypes) {
    importTypes.addAll(genericType.importTypes());
    annotations.keySet().forEach(t -> importTypes.addAll(t.importTypes()));
    typeUse1.keySet().forEach(t -> importTypes.addAll(t.importTypes()));
    typeUse2.keySet().forEach(t -> importTypes.addAll(t.importTypes()));
  }

  boolean isEmpty() {
    return annotations.isEmpty() && typeUse1.isEmpty() && typeUse2.isEmpty();
  }

  boolean supportsPrimitiveValidation() {
    for (final var validationAnnotation : annotations.keySet()) {
      ConstraintPrism.getOptionalOn(typeElement(validationAnnotation.full()))
        .ifPresent(p -> {
          if (p.unboxPrimitives()) {
            validationAnnotation
              .shortType()
              .transform(PrimitiveUtil::addPrimitiveValidationAnnotation);
          }
        });

      if (!isPrimitiveValidationAnnotations(validationAnnotation.shortType())) {
        return false;
      }
    }
    return true;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy