Package com.blazebit.apt.validation

Source Code of com.blazebit.apt.validation.ConstraintBasedAnnotationProcessor

/*
* Copyright 2011 Blazebit
*/
package com.blazebit.apt.validation;

import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.ElementFilter;
import javax.tools.Diagnostic;

import com.blazebit.apt.validation.constraint.Constraint;
import com.blazebit.apt.validation.constraint.ConstraintValidator;
import com.blazebit.apt.validation.constraint.ValueConstraint;
import com.blazebit.apt.validation.constraint.ValueConstraintValidator;

/**
* Constraint Validator classes must be available in compiled form!
*
* @author Christian Beikov
* @since 0.1.2
*/
@SupportedAnnotationTypes("com.blazebit.apt.validation.constraint.*")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class ConstraintBasedAnnotationProcessor extends AbstractProcessor {

  private Map<Class<? extends ConstraintValidator>, ConstraintValidator> constraintValidatorMap = new HashMap<Class<? extends ConstraintValidator>, ConstraintValidator>();
  private Map<Class<? extends ValueConstraintValidator>, ValueConstraintValidator> valueConstraintValidatorMap = new HashMap<Class<? extends ValueConstraintValidator>, ValueConstraintValidator>();

  @Override
  @SuppressWarnings("unchecked")
  public boolean process(Set<? extends TypeElement> annotations,
      RoundEnvironment roundEnv) {
    for (TypeElement annotationType : annotations) {
      if (shouldSkip(annotationType)) {
        continue;
      }

      // validation processing for annotations with annotation constraint
      for (AnnotationMirror annotationMirror : annotationType
          .getAnnotationMirrors()) {
        TypeElement memberAnnotationTypeElement = (TypeElement) annotationMirror
            .getAnnotationType().asElement();
        Class<? extends ConstraintValidator> constraintValidatorClass = null;

        if (shouldSkip(memberAnnotationTypeElement)) {
          continue;
        }

        constraintValidatorClass = (Class<? extends ConstraintValidator>) getConstraintValidatorClass(
            memberAnnotationTypeElement, Constraint.class);
        ConstraintValidator validator = getConstraintValidator(
            constraintValidatorClass, constraintValidatorMap);

        if (validator != null) {
          for (Element e : roundEnv
              .getElementsAnnotatedWith(annotationType)) {
            validator.validate(processingEnv, roundEnv,
                annotationType, annotationMirror, e);
          }
        }
      }

      // validation processing for annotation member values
      for (ExecutableElement annotationMember : ElementFilter
          .methodsIn(annotationType.getEnclosedElements())) {
        // annotationMember is a method of an annotation
        for (AnnotationMirror annotationMirror : annotationMember
            .getAnnotationMirrors()) {
          // annotationMirror is an annotation on a method of an
          // annotation
          TypeElement memberAnnotationTypeElement = (TypeElement) annotationMirror
              .getAnnotationType().asElement();
          Class<? extends ValueConstraintValidator> constraintValidatorClass = null;

          if (shouldSkip(memberAnnotationTypeElement)) {
            continue;
          }

          constraintValidatorClass = (Class<? extends ValueConstraintValidator>) getConstraintValidatorClass(
              memberAnnotationTypeElement, ValueConstraint.class);
          ValueConstraintValidator validator = getConstraintValidator(
              constraintValidatorClass,
              valueConstraintValidatorMap);

          if (validator != null) {
            for (Element e : roundEnv
                .getElementsAnnotatedWith(annotationType)) {
              AnnotationMirror elementAnnotationMirror = AnnotationProcessingUtils
                  .findAnnotationMirror(processingEnv, e,
                      annotationType);
              Object actualValue = AnnotationProcessingUtils
                  .getAnnotationElementValue(
                      processingEnv,
                      elementAnnotationMirror,
                      annotationMember.getSimpleName()
                          .toString()).getValue();
              validator.validate(processingEnv, roundEnv,
                  annotationType, annotationMirror,
                  annotationMember, e, actualValue);
            }
          }
        }
      }
    }

    return true;
  }

  private Class<?> getConstraintValidatorClass(
      TypeElement memberAnnotationTypeElement,
      Class<? extends Annotation> constraintAnnotationClass) {
    AnnotationMirror constraintAnnotation = AnnotationProcessingUtils
        .findAnnotationMirror(processingEnv,
            memberAnnotationTypeElement, constraintAnnotationClass);
    Class<?> constraintValidatorClass = null;

    if (constraintAnnotation != null) {
      String constraintValidatorClassName = AnnotationProcessingUtils
          .getAnnotationElementValue(processingEnv,
              constraintAnnotation, "value").getValue()
          .toString();

      try {
        constraintValidatorClass = Class
            .forName(constraintValidatorClassName);
      } catch (Exception ex) {
        processingEnv.getMessager().printMessage(
            Diagnostic.Kind.ERROR,
            "Cannot get class object for ConstraintValidator class: "
                + constraintValidatorClassName);
      }
    }

    return constraintValidatorClass;
  }

  private <T extends ConstraintValidator> T getConstraintValidator(
      Class<? extends T> constraintValidatorClass,
      Map<Class<? extends T>, T> cacheMap) {
    if (constraintValidatorClass == null) {
      return null;
    }

    // annotationMirror is an constraint annotation
    T validator = cacheMap.get(constraintValidatorClass);

    if (validator == null) {
      try {
        validator = constraintValidatorClass.newInstance();
        cacheMap.put(constraintValidatorClass, validator);
      } catch (Exception ex) {
        processingEnv.getMessager().printMessage(
            Diagnostic.Kind.ERROR,
            constraintValidatorClass
                + " needs an empty constructor!");
      }
    }

    return validator;
  }

  private boolean shouldSkip(TypeElement memberAnnotationTypeElement) {
    String qualifierPrefix = memberAnnotationTypeElement.getQualifiedName()
        .toString();

    // Don't process system annotations
    return qualifierPrefix.startsWith("java.")
        || qualifierPrefix.startsWith("javax.");
  }
}
TOP

Related Classes of com.blazebit.apt.validation.ConstraintBasedAnnotationProcessor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.