Validate attribute of type array in an annotation

10

When using the Ordem annotation you would like to make your valores attribute mandatory. By binding I want to say that the value of your attribute should not accept an empty array, or an array with an empty String.

Is it possible to perform this type of restriction on the attribute of an annotation, or is this type of restriction only possible through reflection?

Order notation:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Ordem {
    String[] valores();
}

Unsupported values:

@Ordem(valores = {}) //array vazio
public class Foo1 {
    private String atributo;
}

@Ordem(valores = {""}) //array com elemento vazio
public class Foo2 {
    private String atributo;
}

Values accepted:

@Ordem(valores = {"atributo"})
public class Foo3 {
    private String atributo;
}

@Ordem(valores = {"atributo1", "atributo2", "atributo3"})
public class Foo4 {
    private String atributo1;
    private String atributo2;
    private String atributo3;
}
    
asked by anonymous 07.06.2015 / 02:57

1 answer

5

At first when you asked, I immediately remembered my own experiences creating annotations, and how frustrated I was when trying to create a note that had a validation against its possible values, so my first response, which is in below, contemplates only the alternatives that I myself adopted at the time, which would be to use a different version of the compiler and / or validate the values of annotations in Runtime with unchecked Exceptions. Investigating further, I discovered a feature of the Java SE platform available from version 1.6

Annotation Processor - Java 1.6 or >

Available from version 1.6, an API that renders annotations that you define to be validated at compile time through a Processor interface and an Abstract class AbstractProcessor

How does it work?

Firstly, you do not need any third-party libraries, plugins or artifacts, just write your own processor and use it when compiling / processing your classes that have annotations. In it you define which annotations you want to process, and whatever happens, you can even stop the compilation if it does not comply with your contracts, issue warnings, or generate codes. It is possible to have more than one Processor. Using a design cycle control tool such as Maven you will be able to apply your processors.

So let's go to your case

You want to validate the values attribute, can not have empty or null strings, can not have an empty array, with length == 0

The class below, it does the job! It is in the default package (without package defined) to facilitate understanding and testing

import java.util.Set;

import javax.annotation.processing.Messager;
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.TypeElement;
import javax.tools.Diagnostic;

@SupportedAnnotationTypes(value={"Ordem"})
public class MandatoryValuesAnnotationProcessor extends javax.annotation.processing.AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations,
            RoundEnvironment roundEnv) {
        System.out.println("/n/n Processing... /n Processing /n");

        Messager messager = processingEnv.getMessager();

        for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(Ordem.class)){
            TypeElement typeElement = (TypeElement)annotatedElement;
            Ordem ordemAnnotation = typeElement.getAnnotation(Ordem.class);
            String valores[] = ordemAnnotation.valores();

            if(valores == null || valores.lenght == 0){
                messager.printMessage(Diagnostic.Kind.ERROR, "Annotation Ordem nao pode ter valores vazios, nao pode ser um array vazio");
                return true;    
            }

            for (String valor : valores) {
                if(valor == null || valor.isEmpty()){
                    messager.printMessage(Diagnostic.Kind.ERROR, "Annotation Ordem nao pode ter strings vazias ou nulas");
                    return true;
                }
            }
        }

        return true;//because we don't want other processors to process this annotations
    }

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

}

Above we are defining the Processor for your classes with your annotation, below, we use it, already compiled to process your classes.

Compile your annotation Order first, if you sequence compile the processor, and then use it to compile the other classes, as you can see, this is not the best way to manage your compilation clicks so I recommend that you use Maven to build your project, or another solution. You can also create a jar with your processor, here is where I learned:

link

For teaching purposes, use this command line when compiling your classes (you can use Maven as well)

javac -cp . -Xlint:processing -processor MandatoryValuesAnnotationProcessor -proc:only Foo2.java

Read this reference for a more complete understanding

Reference

-

Note: From the original response, it is still valid to use a prepared compiler, which is not the best because it gives you less flexibility and you can also validate annotation attribute values in Runtime, which is common on the SE platform and EE in some APIs, such as JAX-WS , for example

Original Answer

No, it is not possible Geison, at least not working only with the Oracle HotSpot compiler, there are alternative ways, such as creating your own compiler, or customizing one of OpenJDK .

I have tried to do this that you want to do, in addition to other things.

What you can do as an alternative is to write a unit test with JUnit that uses Reflection , checks the values defined for this annotation, not allowing arrays empty or Empty strings.

What I always did was when I need to read the annotation values, if they are mandatory, I throw an exception - IllegalArgumentException Where I notice the programmer that the set value can not be null or empty.

    
07.06.2015 / 03:07