7 Ağustos 2019 Çarşamba

AbstractProcessor Sınıfı

Giriş
Şu satırı dahil ederiz. Bu sınıf Java 9 ile geliyor. java.compiler module içindedir.
import javax.annotation.processing.AbstractProcessor;
Annotation tanımı ve onu işleyen AbstractProcessor bir jar dosyasına dahil edilir.

Lombok İle Farkı
Açıklaması şöyle.
Java Annotation Processing (APT) plugins are intended for generating code based on other classes. These classes end up in a generated sources folder which is then later compiled as well. These APT plugins are discovered from the classpath / build tool configuration and ran by the IntelliJ compiler as well. Keep in mind: APT is meant to be for generated source code generation, and not at all for replacing existing classes. The only reason why Lombok is still able to do so is because they hack their way very deep into the compiler and are by that means able to manipulate the AST of classes under compilation.
Açıklaması şöyle.
Instead of actually creating a file and writing to it, you can modify the Abstract Syntax Tree (AST), like Lombok does. This isn't recommended and different compilers implement the AST in different ways, ...

Annotation Tanımlama
Açıklaması şöyle.
The first main distinction between kinds of annotation is whether they're used at compile time and then discarded (like @Override) or placed in the compiled class file and available at runtime (like Spring's @Component). This is determined by the @Retention policy of the annotation. If you're writing your own annotation, you'd need to decide whether the annotation is helpful at runtime (for autoconfiguration, perhaps) or only at compile time (for checking or code generation).

When compiling code with annotations, the compiler sees the annotation just like it sees other modifiers on source elements, like access modifiers (public/private) or final. When it encounters an annotation, it runs an annotation processor, which is like a plug-in class that says it's interested a specific annotation. The annotation processor generally uses the Reflection API to inspect the elements being compiled and may simply run checks on them, modify them, or generate new code to be compiled. @Override is an example of the first; it uses the Reflection API to make sure it can find a match for the method signature in one of the superclasses and uses the Messager to cause a compile error if it can't.
Kodlamak istediğimiz Annotation sınıfını kodlarız. Şöyle yaparız.
package gearon.customAnnotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target(value=ElementType.METHOD)
public @interface Overload {

}
AbstractProcessor Tanımlama
Örnek
Şöyle yaparız.
package gearon.customAnnotation;

import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;

@SupportedAnnotationTypes("gearon.customAnnotation.Overload")
public class OverloadProcessor extends AbstractProcessor{
  ...
}
process metodu
RoundEnvironment nesnesinin getElementsAnnotatedWith() metodu çağrılarak bir Element set'i elde edilir. Bu setin içindeki elemanlar VariableElement olabilir. Yani üye alan değişkeni.

Örnek
Şöyle yaparız.
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
  for (TypeElement annotation : annoations) {
    Set<? extends Element> annotatedElements = env.getElementsAnnotatedWith(annotation);
    ...  
  }
  return true;
}
Örnek
Şöyle yaparız. @Case anotasyonu ile işaretli yerin bir String değişkeni olmasını kontrol eder. Değilse derleme hatası verir.
@SupportedAnnotationTypes({"*"})
class ImaginaryProcessor extends AnnotationProcessor {
  @Override
  public boolean process(Set<? extends TypeElement> set, RoundEnvironment round) {
    var elements = round.getElementsAnnotatedWith(Case.class);
    for (var element : elements) {
      if (element instanceof VariableElement)) {
        var variable = (VariableElement) element;
        if (!types.isSameType(variable.asType(), types.type(String.class))) {
          logger.error(element, "Element is not a string");
        }
      } else {
        logger.error(element, "Element is not a variable");
      }
    }
    return false;
  }
}
Aynısı şöyle de yapılabilir
import com.karuslabs.utilitary.Logger;
import com.karuslabs.utilitary.type.TypeMirrors;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.Elements;

class StringFieldLint extends AbstractProcessor {
  Elements elements;
  TypeMirrors types;
 
  @Override
  public void init(ProcessingEnvironment environment) {
    super.init(environment);
    elements = environment.getElementUtils(); // (1)
    types = new TypeMirrors(elements, environment.getTypeUtils()); // (2)
  }
  @Override
  public boolean process(Set<? extends TypeElement> set, RoundEnvironment round) {
    var elements = round.getElementsAnnotatedWith(Case.class); // (4)
    for (var element : elements) {
      if (!(element instanceof VariableElement)) {
        logger.error(element, "Element is not a variable"); // (5)
        continue;
      }
            
      var variable = (VariableElement) element;
      if (!types.isSameType(variable.asType(), types.type(String.class))) {
        continue;
      }
    }
    return false;
  }
}
Örnek
Şöyle yaparız.
@Override
public boolean process(Set<? extends TypeElement> annotations,
 RoundEnvironment roundEnv) {

  HashMap<String, Integer> map = new HashMap<String, Integer>();

  for(Element element : roundEnv.getElementsAnnotatedWith(Overload.class)){
    String signature = element.getSimpleName().toString();
    int count = map.containsKey(signature) ? map.get(signature) : 0;
    map.put(signature, ++count);
  }

  for(Entry<String, Integer> entry: map.entrySet()){
    if(entry.getValue() == 1){
      processingEnv.getMessager().printMessage(Kind.ERROR,
       "The method which signature is " + entry.getKey() +  
       " has not been overloaded");
    }
  }
  return true;
}


Hiç yorum yok:

Yorum Gönder