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

com.github.nalukit.malio.processor.MalioProcessor Maven / Gradle / Ivy

There is a newer version: 1.0.4
Show newest version
/*
 * Copyright © 2023 Frank Hossfeld, Philipp Kohl
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.nalukit.malio.processor;

import com.github.nalukit.malio.processor.constraint.AbstractProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.ArrayItemMaxLengthProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.ArrayItemMinLengthProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.ArrayItemNotBlankProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.ArrayItemNotNullProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.ArraySizeProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.BlacklistProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.CollectionItemMaxLengthProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.CollectionItemMinLengthProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.CollectionItemNotBlankProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.CollectionItemNotNullProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.DecimalMaxProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.DecimalMinProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.EmailProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.MaxLengthProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.MaxProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.MinLengthProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.MinProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.NotBlankProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.NotEmptyProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.NotNullProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.NotZeroProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.RegexpProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.SizeProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.UuidProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.WhitelistProcessorConstraint;
import com.github.nalukit.malio.processor.constraint.generator.ValidatorGenerator;
import com.github.nalukit.malio.processor.constraint.scanner.ValidatorScanner;
import com.github.nalukit.malio.processor.exception.UnsupportedTypeException;
import com.github.nalukit.malio.processor.model.ValidatorModel;
import com.github.nalukit.malio.processor.util.ProcessorUtils;
import com.github.nalukit.malio.shared.Malio;
import com.github.nalukit.malio.shared.annotation.MalioValidator;
import com.github.nalukit.malio.shared.annotation.field.ArrayItemMaxLength;
import com.github.nalukit.malio.shared.annotation.field.ArrayItemMinLength;
import com.github.nalukit.malio.shared.annotation.field.ArrayItemNotBlank;
import com.github.nalukit.malio.shared.annotation.field.ArrayItemNotNull;
import com.github.nalukit.malio.shared.annotation.field.ArraySize;
import com.github.nalukit.malio.shared.annotation.field.Blacklist;
import com.github.nalukit.malio.shared.annotation.field.CollectionItemMaxLength;
import com.github.nalukit.malio.shared.annotation.field.CollectionItemMinLength;
import com.github.nalukit.malio.shared.annotation.field.CollectionItemNotBlank;
import com.github.nalukit.malio.shared.annotation.field.CollectionItemNotNull;
import com.github.nalukit.malio.shared.annotation.field.DecimalMax;
import com.github.nalukit.malio.shared.annotation.field.DecimalMin;
import com.github.nalukit.malio.shared.annotation.field.Email;
import com.github.nalukit.malio.shared.annotation.field.Max;
import com.github.nalukit.malio.shared.annotation.field.MaxLength;
import com.github.nalukit.malio.shared.annotation.field.Min;
import com.github.nalukit.malio.shared.annotation.field.MinLength;
import com.github.nalukit.malio.shared.annotation.field.NotBlank;
import com.github.nalukit.malio.shared.annotation.field.NotEmpty;
import com.github.nalukit.malio.shared.annotation.field.NotNull;
import com.github.nalukit.malio.shared.annotation.field.NotZero;
import com.github.nalukit.malio.shared.annotation.field.Regexp;
import com.github.nalukit.malio.shared.annotation.field.Size;
import com.github.nalukit.malio.shared.annotation.field.Uuid;
import com.github.nalukit.malio.shared.annotation.field.Whitelist;
import com.google.auto.service.AutoService;
import com.google.common.base.Stopwatch;
import com.squareup.javapoet.CodeBlock;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toSet;

@AutoService(Processor.class)
public class MalioProcessor
    extends AbstractProcessor {

  Map> constraints;
  private ProcessorUtils processorUtils;
  private Stopwatch      stopwatch;
  private boolean        generateCheckMethod;
  private boolean        generateValidateMethod;

  public MalioProcessor() {
    super();
    this.constraints = new HashMap<>();
  }

  @Override
  public Set getSupportedAnnotationTypes() {
    return Stream.of(MalioValidator.class.getCanonicalName())
                 .collect(toSet());
  }

  @Override
  public SourceVersion getSupportedSourceVersion() {
    return SourceVersion.latestSupported();
  }

  @Override
  public synchronized void init(ProcessingEnvironment processingEnv) {
    super.init(processingEnv);
    this.stopwatch = Stopwatch.createStarted();
    this.setUp();
    this.processorUtils.createNoteMessage("Nalu-Plugin-GWT-Processor started ...");
    String implementationVersion = Malio.getVersion();
    this.processorUtils.createNoteMessage("Nalu-Plugin-GWT-Processor version >>" + implementationVersion + "<<");
  }

  private void setUp() {
    this.processorUtils = ProcessorUtils.builder()
                                        .processingEnvironment(this.processingEnv)
                                        .build();

    this.constraints.put(ArrayItemMaxLength.class.getCanonicalName(),
                         new ArrayItemMaxLengthProcessorConstraint().setUp(this.processingEnv,
                                                                           this.processorUtils));
    this.constraints.put(ArrayItemMinLength.class.getCanonicalName(),
                         new ArrayItemMinLengthProcessorConstraint().setUp(this.processingEnv,
                                                                           this.processorUtils));
    this.constraints.put(ArrayItemNotBlank.class.getCanonicalName(),
                         new ArrayItemNotBlankProcessorConstraint().setUp(this.processingEnv,
                                                                          this.processorUtils));
    this.constraints.put(ArrayItemNotNull.class.getCanonicalName(),
                         new ArrayItemNotNullProcessorConstraint().setUp(this.processingEnv,
                                                                         this.processorUtils));
    this.constraints.put(Blacklist.class.getCanonicalName(),
                         new BlacklistProcessorConstraint().setUp(this.processingEnv,
                                                                  this.processorUtils));
    this.constraints.put(CollectionItemMaxLength.class.getCanonicalName(),
                         new CollectionItemMaxLengthProcessorConstraint().setUp(this.processingEnv,
                                                                                this.processorUtils));
    this.constraints.put(CollectionItemMinLength.class.getCanonicalName(),
                         new CollectionItemMinLengthProcessorConstraint().setUp(this.processingEnv,
                                                                                this.processorUtils));
    this.constraints.put(CollectionItemNotBlank.class.getCanonicalName(),
                         new CollectionItemNotBlankProcessorConstraint().setUp(this.processingEnv,
                                                                               this.processorUtils));
    this.constraints.put(CollectionItemNotNull.class.getCanonicalName(),
                         new CollectionItemNotNullProcessorConstraint().setUp(this.processingEnv,
                                                                              this.processorUtils));
    this.constraints.put(DecimalMax.class.getCanonicalName(),
                         new DecimalMaxProcessorConstraint().setUp(this.processingEnv,
                                                                   this.processorUtils));
    this.constraints.put(Email.class.getCanonicalName(),
                         new EmailProcessorConstraint().setUp(this.processingEnv,
                                                              this.processorUtils));
    this.constraints.put(MaxLength.class.getCanonicalName(),
                         new MaxLengthProcessorConstraint().setUp(this.processingEnv,
                                                                  this.processorUtils));
    this.constraints.put(Max.class.getCanonicalName(),
                         new MaxProcessorConstraint().setUp(this.processingEnv,
                                                            this.processorUtils));
    this.constraints.put(DecimalMin.class.getCanonicalName(),
                         new DecimalMinProcessorConstraint().setUp(this.processingEnv,
                                                                   this.processorUtils));
    this.constraints.put(MinLength.class.getCanonicalName(),
                         new MinLengthProcessorConstraint().setUp(this.processingEnv,
                                                                  this.processorUtils));
    this.constraints.put(Min.class.getCanonicalName(),
                         new MinProcessorConstraint().setUp(this.processingEnv,
                                                            this.processorUtils));
    this.constraints.put(NotBlank.class.getCanonicalName(),
                         new NotBlankProcessorConstraint().setUp(this.processingEnv,
                                                                 this.processorUtils));
    this.constraints.put(NotEmpty.class.getCanonicalName(),
                         new NotEmptyProcessorConstraint().setUp(this.processingEnv,
                                                                 this.processorUtils));
    this.constraints.put(NotNull.class.getCanonicalName(),
                         new NotNullProcessorConstraint().setUp(this.processingEnv,
                                                                this.processorUtils));
    this.constraints.put(Regexp.class.getCanonicalName(),
                         new RegexpProcessorConstraint().setUp(this.processingEnv,
                                                               this.processorUtils));
    this.constraints.put(Size.class.getCanonicalName(),
                         new SizeProcessorConstraint().setUp(this.processingEnv,
                                                             this.processorUtils));
    this.constraints.put(ArraySize.class.getCanonicalName(),
                         new ArraySizeProcessorConstraint().setUp(this.processingEnv,
                                                                  this.processorUtils));
    this.constraints.put(Uuid.class.getCanonicalName(),
                         new UuidProcessorConstraint().setUp(this.processingEnv,
                                                             this.processorUtils));
    this.constraints.put(Whitelist.class.getCanonicalName(),
                         new WhitelistProcessorConstraint().setUp(this.processingEnv,
                                                                  this.processorUtils));
    this.constraints.put(NotZero.class.getCanonicalName(),
                         new NotZeroProcessorConstraint().setUp(this.processingEnv,
                                                                this.processorUtils));
  }

  @Override
  public boolean process(Set annotations,
                         RoundEnvironment roundEnv) {
    try {
      if (roundEnv.processingOver()) {
        this.processorUtils.createNoteMessage("Malio-Processor finished ... processing takes: " +
                                              this.stopwatch.stop()
                                                            .toString());
      } else {
        processSingleRun(annotations,
                         roundEnv);
      }
    } catch (ProcessorException e) {
      this.processorUtils.createErrorMessage(e.getMessage());
      this.processorUtils.createErrorMessage(e);
      return true;
    }
    return true;
  }

  private void processSingleRun(Set annotations,
                                RoundEnvironment roundEnv)
      throws ProcessorException {
    if (annotations.size() > 0) {
      for (TypeElement annotation : annotations) {
        if (MalioValidator.class.getCanonicalName()
                                .equals(annotation.toString())) {
          processMalioValidator(roundEnv);
        }
      }
    }
  }

  private void processMalioValidator(RoundEnvironment roundEnv)
      throws ProcessorException {
    for (Element validatorElement : roundEnv.getElementsAnnotatedWith(MalioValidator.class)) {
      TypeElement typeElement = (TypeElement) validatorElement;
      this.generateCheckMethod    = typeElement.getAnnotation(MalioValidator.class)
                                               .generateCheckMethod();
      this.generateValidateMethod = typeElement.getAnnotation(MalioValidator.class)
                                               .generateValidateMethod();

      processClass(validatorElement);
    }
  }

  private void processClass(Element clazz)
      throws ProcessorException {
    MalioValidatorGenerator malioValidatorGenerator = new MalioValidatorGenerator(this.processingEnv,
                                                                                  processorUtils,
                                                                                  clazz,
                                                                                  this.generateCheckMethod,
                                                                                  this.generateValidateMethod);
    this.processVariable(clazz.asType(),
                         malioValidatorGenerator);
    this.createSubAndSuperValidators(clazz,
                                     malioValidatorGenerator);
    malioValidatorGenerator.writeFile(this.generateCheckMethod,
                                      this.generateValidateMethod);
  }

  private void processVariable(TypeMirror clazz,
                               MalioValidatorGenerator malioValidatorGenerator)
      throws ProcessorException {
    TypeElement element = (TypeElement) this.processingEnv.getTypeUtils()
                                                          .asElement(clazz);
    List listOfDirectVariables = this.processingEnv.getElementUtils()
                                                                    .getAllMembers(element)
                                                                    .stream()
                                                                    .filter(e -> e.getKind() == ElementKind.FIELD)
                                                                    .filter(e -> e.getEnclosingElement()
                                                                                  .toString()
                                                                                  .equals(element.toString()))
                                                                    .map(e -> (VariableElement) e)
                                                                    .collect(Collectors.toList());
    for (VariableElement field : listOfDirectVariables) {
      this.createConstraints(field,
                             malioValidatorGenerator);
    }
  }

  private void createConstraints(VariableElement field,
                                 MalioValidatorGenerator malioValidatorGenerator)
      throws ProcessorException {
    for (AnnotationMirror annotation : field.getAnnotationMirrors()) {
      AbstractProcessorConstraint constraint = this.constraints.get(annotation.getAnnotationType()
                                                                                 .toString());
      // c heck whether he annotation is related to Malio or not ...
      if (Objects.isNull(constraint)) {
        continue;
      }
      if (constraint.isTargetingNative()) {
        constraint.checkDataType(field);
        this.createCodeForNativeConstraint(field,
                                           malioValidatorGenerator,
                                           constraint);
      }
      if (constraint.isTargetingArrayItem()) {
        if (!field.asType()
                  .getKind()
                  .equals(TypeKind.ARRAY)) {
          throw new UnsupportedTypeException("Constraint >>" + annotation + "<< not supported for >>" + field.asType() + "<<");
        }
        constraint.checkDataTypeArrayItem(field);
        this.createCodeForArrayConstraint(field,
                                          malioValidatorGenerator,
                                          constraint);
      }
      if (constraint.isTargetingListItem()) {
        ValidatorModel.ComponentType componentType = this.getComponentTypeFromVariableElement(field);
        if (ValidatorModel.ComponentType.COLLECTION != componentType) {
          throw new UnsupportedTypeException("Constraint >>" + annotation + "<< not supported for >>" + field.asType() + "<<");
        }
        constraint.checkDataTypeCollectionItem(field);
        this.createCodeForCollectionConstraint(field,
                                               malioValidatorGenerator,
                                               constraint);
      }
    }
  }

  private void createCodeForNativeConstraint(VariableElement field,
                                             MalioValidatorGenerator malioValidatorGenerator,
                                             AbstractProcessorConstraint constraint)
      throws ProcessorException {
    if (this.generateCheckMethod) {
      CodeBlock checkBlock = constraint.generateCheckNative(field,
                                                            field);
      malioValidatorGenerator.appendCheckStatement(checkBlock);
    }
    if (this.generateValidateMethod) {
      CodeBlock validBlock = constraint.generateValidNative(field,
                                                            field);
      malioValidatorGenerator.appendValidStatement(validBlock);
    }
  }

  private void createCodeForArrayConstraint(VariableElement field,
                                            MalioValidatorGenerator malioValidatorGenerator,
                                            AbstractProcessorConstraint constraint)
      throws ProcessorException {
    malioValidatorGenerator.appendBeginControlFlowArray(field,
                                                        this.generateCheckMethod,
                                                        this.generateValidateMethod);
    if (this.generateCheckMethod) {
      CodeBlock checkBlock = constraint.generateCheckArray(field,
                                                           field);
      malioValidatorGenerator.appendCheckStatement(checkBlock);
    }
    if (this.generateValidateMethod) {
      CodeBlock validBlock = constraint.generateValidArray(field,
                                                           field);
      malioValidatorGenerator.appendValidStatement(validBlock);
    }
    malioValidatorGenerator.appendEndControlFlow(this.generateCheckMethod,
                                                 this.generateValidateMethod);
  }

  private void createCodeForCollectionConstraint(VariableElement field,
                                                 MalioValidatorGenerator malioValidatorGenerator,
                                                 AbstractProcessorConstraint constraint)
      throws ProcessorException {
    malioValidatorGenerator.appendBeginControlFlowCollection(field,
                                                             this.generateCheckMethod,
                                                             this.generateValidateMethod);
    if (this.generateCheckMethod) {
      CodeBlock checkBlock = constraint.generateCheckCollection(field,
                                                                field);
      malioValidatorGenerator.appendCheckStatement(checkBlock);
    }
    if (this.generateValidateMethod) {
      CodeBlock validBlock = constraint.generateValidCollection(field,
                                                                field);
      malioValidatorGenerator.appendValidStatement(validBlock);
    }
    malioValidatorGenerator.appendEndControlFlow(this.generateCheckMethod,
                                                 this.generateValidateMethod);
  }

  private void createSubAndSuperValidators(Element validatorElement,
                                           MalioValidatorGenerator malioValidatorGenerator)
      throws ProcessorException {
    List subValidatorList = ValidatorScanner.builder()
                                                            .elements(this.processingEnv.getElementUtils())
                                                            .types(this.processingEnv.getTypeUtils())
                                                            .validatorElement(validatorElement)
                                                            .processorUtils(this.processorUtils)
                                                            .build()
                                                            .createSubValidatorList();

    List superValidatorList = ValidatorScanner.builder()
                                                              .elements(this.processingEnv.getElementUtils())
                                                              .types(this.processingEnv.getTypeUtils())
                                                              .validatorElement(validatorElement)
                                                              .processorUtils(this.processorUtils)
                                                              .build()
                                                              .createSuperValidatorList();

    ValidatorGenerator generator = ValidatorGenerator.builder()
                                                     .elements(this.processingEnv.getElementUtils())
                                                     .filer(this.processingEnv.getFiler())
                                                     .types(this.processingEnv.getTypeUtils())
                                                     .superValidatorList(superValidatorList)
                                                     .subValidatorList(subValidatorList)
                                                     .malioValidatorGenerator(malioValidatorGenerator)
                                                     .processorUtils(this.processorUtils)
                                                     .build();
    if (this.generateCheckMethod) {
      generator.appendSuperAndSubValidatorsCheck();
    }
    if (this.generateValidateMethod) {
      generator.appendSuperAndSubValidatorsValid();
    }
  }

  private ValidatorModel.ComponentType getComponentTypeFromVariableElement(VariableElement variableElement) {
    if (variableElement.asType()
                       .getKind()
                       .equals(TypeKind.ARRAY)) {
      return ValidatorModel.ComponentType.ARRAY;
    }

    String elementOfVariableTypeString = variableElement.asType()
                                                        .toString();
    // check for generic ... if there is none, we can't do anything ...
    if (elementOfVariableTypeString.contains("<")) {
      // get flatten super type list and look for Collection class
      Set superClasses = this.processorUtils.getFlattenedSupertypeHierarchy(this.processingEnv.getTypeUtils(),
                                                                                        variableElement.asType());
      for (TypeMirror tm : superClasses) {
        if (tm.toString()
              .startsWith(Collection.class.getCanonicalName())) {
          return ValidatorModel.ComponentType.COLLECTION;
        }
      }
    }

    return ValidatorModel.ComponentType.NATIVE;
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy