I am a daily user of IntelliJ IDEA to edit my Java code and Apache Wicket to write my web application. I decided recently to use IntelliJ's annotations to make my code a little more robust by using @Nullable, @NotNull, @NonNls and @PropertyKey.
In this blog post I explain how to manually add annotation to an external library.

IntelliJ annotations

Let's explain quickly how you can use these annotations and the problem I faced with the latter.

  • @Nullable : tells the editor that the field, parameter, return type may be null. This way, IDEA will highlight you if you forget to check nullity.
  • @NotNull : tells the editor that the field, parameter, return type will never be null. This way, IDEA will highlight you if you do unnecessary null checking. It even can throw RuntimeException if a method is invoked with a null parameter although it shouldn't. The exception tells exactly what parameter of what method, it can help a lot debbugging a NullPointerException coming out from nowhere !
  • @NonNls : tells the editor that the string you entered is not involved in internationalization of any kind so it will not bother you with duplicate string literals.
  • @PropertyKey(resourceBundle) : tells the editor that the following string have to be present in a specific resource bundle. This way, IDEA is able to notice you missing translations in your application.

How to use it...

The first thing to do to use it is simply to annotate your source code like this:

@NotNull
public String doSomething(@NotNull Object myObject,
                   @PropertyKey(resourceBundle="fr.gatay.cedric.bloggure.language") 
                   String propertyKey, 
                   @Nullable String argument){
	 @NonNls String s =  "Return Value";
     return s;
}

This method is completely useless, but it can ease explaining how annotations work. By looking at the prototype we can now see that the method never returns null value, doesn't accept a first parameter with a null value, takes its second parameter from the "fr.gatay.cedric.bloggure.language" resource bundle and accepts a potentially null third parameter.
Last but not least, if we look at the implementation, we see that we tell IDEA not to verify the "Return Value" string against i18n rules.

How to use it, in libraries...

If in your project, you want to use this feature in included libraries you might be glad the guys at Jetbrains included a feature called "External annotations". It allows you to specify a location where to save these annotations.
Here is a quick example on how to add @NotNull on org.apache.wicket.extensions.Initializer :
First, invoke intention action, and select the annotation you want to put
Then, configure external annotation root, I tend to use a folder named annotation at the root of my workspace.
That's all, if you look at the folder you specified, you will have the following structure:
In the folder corresponding to the package of the class you wanted to annotate you will find an annotations.xml file like the following:

<root>
    <item name='org.apache.wicket.extensions.Initializer void init(org.apache.wicket.Application) 0'>
        <annotation name='org.jetbrains.annotations.NotNull'>
        </annotation>
    </item>
</root>

The problem is that it is not easily possible to add @PropertyKey or @NotNls annotation in external libraries.

How to manually add an annotation

In my case, I wanted to add @PropertyKey on Apache Wicket feedback messages methods. My Wicket Application have a converter for string that handles internationalization, so I want to add @PropertyKey(resourceBundle="fr.gatay.cedric.bloggure.language") to the String parameter of org.apache.wicket.Component info and error methods.
By digging into the generated annotations.xml file for @NotNull annotation, I found out how to manually add my annotation. In my case, I end up with an annotations.xml file like this in the folder ${annotationsRoot}/org/apache/wicket/ :

<root>
 <item name="org.apache.wicket.Component void error(java.io.Serializable) 0">
	 <annotation name="org.jetbrains.annotations.PropertyKey">
	 	<val name="resourceBundle" val="fr.gatay.cedric.bloggure.language"/>
	 </annotation>
 </item>
 <item name="org.apache.wicket.Component void info(java.lang.String) 0">
	 <annotation name="org.jetbrains.annotations.PropertyKey">
 		<val name="resourceBundle" val="fr.gatay.cedric.bloggure.language"/>
	 </annotation>
 </item>
</root>

If you look closely at this sample, you will quickly understand how things work. First, the item element specify the method on which you want to put the annotation, please notice the number at the end specifying the parameter on which the annotation will be applied (starting at 0).
Inside the item element, you find the annotation element specifying the fully qualified class name of the annotation to apply. In the case of an annotation with parameters (like @PropertyKey), you can set these parameters using the val element, name xml attribute refers to the attribute's name and val xml attribute to the attribute's value.
You might need to restart IntelliJ for the new values to be read.

comments powered by Disqus