1

I have a custom Java annotation which generates a file when test sources are compiled. But when I import this annotation into a Scala class and I use it in the same way that in Java, Scala is not processing it.

Java Annotation

@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Trace {
    public String key() default "";
    public byte step() default 0;
    public String url() default "";
}

@SupportedAnnotationTypes("com.custom.annotations.Trace")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
@AutoService(Processor.class)
public class TraceProcessor extends AbstractProcessor {

    public static final String DEFAULT_FILE_PATH = "target/";
    public static final String DEFAULT_FILE_NAME = "trazability.txt";

    private Messager messager;

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Set<? extends Element> annotatedElements = getTraceAnnotatedElements(roundEnv);
        if(annotatedElements.size() == 0) {
            return true; // If there is not annotation, fast exist.
        }

        System.out.println("Processing @Trace annotations...");
        try {
            generateTraceFile(annotatedElements);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return true;
    }

    @Override
    public void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);

        // get messager for printing errors
        messager = processingEnvironment.getMessager();
    }

    private Set<? extends Element> getTraceAnnotatedElements(RoundEnvironment roundEnv) {
        return roundEnv.getElementsAnnotatedWith(Trace.class);
    }

    private void generateTraceFile(Set<? extends Element> annotatedElements) throws FileNotFoundException {
        PrintWriter out = new PrintWriter(DEFAULT_FILE_PATH + DEFAULT_FILE_NAME);

        System.out.println("Annotated elements with @Trace: " + annotatedElements.size());
        for (Element element : annotatedElements) {
            if (element.getKind() == ElementKind.CLASS || element.getKind() == ElementKind.METHOD) {
                String key = element.getAnnotation(Trace.class).key();
                int step = element.getAnnotation(Trace.class).step();

                if(element.getKind() == ElementKind.METHOD) {
                    String className = ((TypeElement) element.getEnclosingElement()).getQualifiedName().toString();
                    String methodName = element.getSimpleName().toString();
                    System.out.println("Add " + element.getKind().name() + " with key: " + key + " step: " + step + " class: " + className + " method: " + methodName);
                    out.print(key + ";" + step + ";" + className + "." + methodName);
                } else {
                    String packageName = ((Symbol) element).getQualifiedName().toString();
                    String className = packageName + "." + element.getSimpleName().toString();
                    System.out.println("Add " + element.getKind().name() + " with key: " + key + " step: " + step + " class: " + className);
                    out.print(key + ";" + step + ";" +  className);
                }
                out.println();
            }
        }
        out.println();
        out.close();
        System.out.println("File trazability generated into target folder");
    }

    private void printError(Element element, String message) {
        messager.printMessage(Diagnostic.Kind.ERROR, message, element);
    }

}

Scala

import com.custom.annotations.Trace

@Trace(key = "ECI-12")
class Simulation1 extends Simulation {}

When I use the java annotation into a Java source then the Trace annotation is processed by TraceProcessor and a file is generated. But when I do the same with Scala it doesn't work.

Fran b
  • 3,016
  • 6
  • 38
  • 65
  • https://stackoverflow.com/questions/58443039/is-there-a-way-to-create-custom-annotations-in-scala-and-write-a-custom-annotati/ – Dmytro Mitin Apr 07 '20 at 17:05

2 Answers2

2

Usage of java annotations in scala code can lead to unpredictable results, most of them just simply won't work. Please don't use them in your Scala code, because by doing so you draw hate from both Java and Scala guys. Fusing your favorite libraries from java world with scala not yields you the best you can get from scala but raises a bunch of problems that are rarely considered on different resources. Scala has its own ecosystem, and it is better to avoid using java libraries as long you can solve problem with scala library even if you super proficient with java.

Java annotations commonly used to shave off boilerplate that is enforced by java verbose syntax and poor type system. Scala has own metaprogramming framework (blackbox macro, whitebox macro, macro annotations and implicit macro) and many other abstraction stuff such as potent type system with HKT and omnipotent implicit search mechanism. So, you definitely don't need macro annotations for most of the cases.

Moreover for your problem could exist a scala solution. Someone can find it for you if you decide to describe the problem rather than bring a wall of code instead.

Iva Kam
  • 932
  • 4
  • 13
  • The goal is annotate several test classes with the @Trace annotation and to generate a file that contains a list of the qualified class names with this annotation. – Fran b Apr 07 '20 at 18:55
  • @Franb, you could create empty annotation and use https://scalameta.org/ to grep all classes marked with it. – Iva Kam Apr 08 '20 at 08:28
0

The Java annotation will only work with Java compilers since the processor needs to work with the Java AST. You must write your own macro annotation for Scala, which uses a compiler incompatible with Java's annotation processors. See here: https://docs.scala-lang.org/overviews/macros/annotations.html

user
  • 7,435
  • 3
  • 14
  • 44
  • I'm using scala 2.12.10 and I read macros is an experimental feature: https://docs.scala-lang.org/overviews/macros/overview.html . I would like something as `class Trace(val key: String) extends ClassfileAnnotation` but how should I linked with the "processor" or scala equivalent. I mean, how can I codify an annotation processor in order to run it in compilation time? – Fran b Apr 07 '20 at 18:52
  • I'm sorry, I can't help you with that. I don't know too much about the specific details. You should probably read more of the documentation, though. However, that link you gave was for def macros, which probably won't help here. You want macro annotations, which can be applied to classes. – user Apr 07 '20 at 19:12
  • 1
    To make an annotation, you extend StaticAnnotation. Then you put the a reference to the macro's implementation in the annotation (unlike Java, I think, where processors are separate from annotation). Then the actual macro implementation takes a context and a few AST nodes, and can generate things from that. Look at this article for an example: https://medium.com/kifi-engineering/scala-macro-annotations-a-real-world-example-14c074416d3 – user Apr 07 '20 at 19:14