Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make annotation deprecated only on one target

I have an annotation that can be added on METHOD and TYPE and is used in thousands of places in our project.

@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@Target({METHOD, TYPE})
@Inherited
public @interface RequiredStore{
    Store value();
}

Is it possible to make the annotation deprecated only on methods while keeping it non-deprecated on types? I want other developers to be notified by IDE that it should not be used on methods any more, until we'll refactor all existing usages and finally remove the METHOD part.

If it's not possible, is there any Way to handle such case beside creating new annotation only for types and deprecating the old one?

like image 774
Mariusz Jamro Avatar asked Dec 30 '13 13:12

Mariusz Jamro


2 Answers

You could use an annotation Processor.

For example, the annotation and its processor would be placed in its own .jar file and added as a dependency of the sources that use the annotation.

The custom .jar would have the following structure:

src/main/
    java/com/company/annotations/
        RequiredStore.java
        RequiredStoreProcessor.java
    resources/META-INF/services
        javax.annotation.processing.Processor

RequiredStore.java stays as you have it above.

RequiredStoreProcessor.java could look something like this:

package com.company.annotations;

import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;

@SupportedAnnotationTypes("com.company.annotations.RequiredStore")
public class RequiredStoreProcessor extends AbstractProcessor {
    @Override
    public boolean process(
            Set<? extends TypeElement> annotations,
            RoundEnvironment roundEnv) {
        for (Element element 
                : roundEnv.getElementsAnnotatedWith(RequiredStore.class)) {
            if (element.getKind().equals(ElementKind.METHOD)) {
                processingEnv.getMessager().printMessage(
                    Diagnostic.Kind.WARNING,
                    "Using @RequiredStore on methods has been deprecated\n"
                        + "Class: " + element.getEnclosingElement() + "\n"
                        + "Method: " + element.getSimpleName() + "\n");
            }
        }

        // Other processing...

        return false;
    }

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

The javax.annotation.processing.Processor file allows javac to pickup the Processor via SPI and simply contains

com.company.annotations.RequiredStoreProcessor

Finally, compile this into a .jar and add it to the classpath where the annotations are being used. Any methods that have the @RequiredStore will produce a compiler warning. For example, for this class,

package com.company.business;

import com.company.annotations.RequiredStore;

@RequiredStore
public interface Business {
    @RequiredStore
    public void someMethod();
}

The compiler warning would be this:

warning: Using @RequiredStore on methods has been deprecated
  Class: com.company.business.Business
  Method: someMethod   

As for an indication in the IDE, you might have to write a custom inspection and unfortunately this depends on the IDE used.


Notes:

Decent custom annotations reference: Code Generation using Annotation Processors in the Java language

like image 77
kuporific Avatar answered Oct 03 '22 06:10

kuporific


If you are okay about using native aspectj, another option is to use AspectJ's code enforcement policy this way:

public aspect RequiredStoreAnnotationCheck {

    declare warning: execution(@RequiredStore * *.*(..)) : "Required store annotation not    appropriate for methods..";

}

If the IDE is integrated with AspectJ, this would be flagged as a compile time check.

AspectJ in action book has a good amount of detail on this too.

Here is one of my blog articles for more context: http://www.java-allandsundry.com/2012/03/code-policy-enforcement-using-aspectj.html

like image 29
Biju Kunjummen Avatar answered Oct 03 '22 08:10

Biju Kunjummen