Reduce use of javax.annotation annotations 32/76732/21
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 8 Oct 2018 09:12:50 +0000 (11:12 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 31 Oct 2018 10:04:43 +0000 (11:04 +0100)
The annotations in use have runtime retention, which triggers
generation of import-package for javax.annotation, which is no longer
satisfied with newer JDKs.

Use JDT @Nullable annotation instead. Since @Nullable is TYPE_USE
annotation, this also requires a bit of juggling in JavaFileTemplate
to make it work correctly with types which end up being referenced
via their FQCN.

This still retains javax.annotation.CheckReturnValue, but marks it
for replacement once we decide where to go annotation-wise.

JIRA: MDSAL-372
Change-Id: I792094d3c07162a5f8e6ca82ceea3a75b48c7b6e
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/AbstractTypeGenerator.java
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/AbstractJavaGeneratedType.java
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/InterfaceTemplate.xtend
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/JavaFileTemplate.java
binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/test/CompilationTest.java

index 47b4adaa08d38d05f3f41748e7dcfaef23b99ef0..f2a0cbe71dceff917bde5ef5da67d831f28bea2c 100644 (file)
@@ -503,6 +503,7 @@ abstract class AbstractTypeGenerator {
                 final MethodSignatureBuilder method = interfaceBuilder.addMethod(rpcMethodName);
 
                 // Do not refer to annotation class, as it may not be available at runtime
+                // FIXME: migrate this to some other annotation type
                 method.addAnnotation("javax.annotation", "CheckReturnValue");
                 addComment(method, rpc);
                 method.addParameter(
@@ -1681,10 +1682,6 @@ abstract class AbstractTypeGenerator {
         getMethod.setReturnType(returnType);
 
         annotateDeprecatedIfNecessary(node.getStatus(), getMethod);
-        if (!returnType.getPackageName().isEmpty()) {
-            // The return type has a package, so it's not a primitive type
-            getMethod.addAnnotation("javax.annotation", "Nullable");
-        }
         addComment(getMethod, node);
 
         return getMethod;
index 0d368f0252035d983d6e3af79634e373e3cf45d6..75461fa843cf7e8a0933fa1864e40f90b1bde42e 100644 (file)
@@ -61,7 +61,7 @@ abstract class AbstractJavaGeneratedType {
         conflictingNames = ImmutableSet.copyOf(cb);
     }
 
-    private void collectAccessibleTypes(Set<String> set, GeneratedType type) {
+    private void collectAccessibleTypes(final Set<String> set, final GeneratedType type) {
         for (Type impl : type.getImplements()) {
             if (impl instanceof GeneratedType) {
                 final GeneratedType genType = (GeneratedType) impl;
@@ -81,13 +81,13 @@ abstract class AbstractJavaGeneratedType {
         return name.simpleName();
     }
 
-    final String getReferenceString(final Type type) {
+    final String getReferenceString(final Type type, final String... annotations) {
+        final String ref = getReferenceString(type.getIdentifier());
         if (!(type instanceof ParameterizedType)) {
-            return getReferenceString(type.getIdentifier());
+            return annotations.length == 0 ? ref : annotate(ref, annotations).toString();
         }
 
-        final StringBuilder sb = new StringBuilder();
-        sb.append(getReferenceString(type.getIdentifier())).append('<');
+        final StringBuilder sb = annotate(ref, annotations).append('<');
         final Type[] types = ((ParameterizedType) type).getActualTypeArguments();
         if (types.length == 0) {
             return sb.append("?>").toString();
@@ -161,4 +161,21 @@ abstract class AbstractJavaGeneratedType {
         // Try to anchor the top-level type and use a local reference
         return checkAndImportType(type.topLevelClass()) ? type.localName() : type.toString();
     }
+
+    private static StringBuilder annotate(final String ref, final String... annotations) {
+        final StringBuilder sb = new StringBuilder();
+        if (annotations.length == 0) {
+            return sb.append(ref);
+        }
+
+        final int dot = ref.lastIndexOf('.');
+        if (dot != -1) {
+            sb.append(ref, 0, dot + 1);
+        }
+        for (String annotation : annotations) {
+            sb.append('@').append(annotation).append(' ');
+        }
+
+        return sb.append(ref, dot + 1, ref.length());
+    }
 }
index ba99afceaa2f367773d2b89597c94f90dba19499..6b954361439f9967456a68ebab6387c33f487794 100644 (file)
@@ -12,13 +12,16 @@ import org.opendaylight.mdsal.binding.model.api.AnnotationType
 import org.opendaylight.mdsal.binding.model.api.Constant
 import org.opendaylight.mdsal.binding.model.api.Enumeration
 import org.opendaylight.mdsal.binding.model.api.GeneratedType
+import org.opendaylight.mdsal.binding.model.api.JavaTypeName
 import org.opendaylight.mdsal.binding.model.api.MethodSignature
+import org.opendaylight.mdsal.binding.model.api.Type
 import org.opendaylight.mdsal.binding.model.util.TypeConstants
 
 /**
  * Template for generating JAVA interfaces.
  */
 class InterfaceTemplate extends BaseTemplate {
+    static val JavaTypeName NULLABLE = JavaTypeName.create("org.eclipse.jdt.annotation", "Nullable")
 
     /**
      * List of constant instances which are generated as JAVA public static final attributes.
@@ -163,17 +166,29 @@ class InterfaceTemplate extends BaseTemplate {
     def private generateMethods() '''
         «IF !methods.empty»
             «FOR m : methods SEPARATOR "\n"»
-                «IF !m.isAccessor»
-                    «m.comment.asJavadoc»
-                «ELSE»
-                    «formatDataForJavaDoc(m, "@return " + asCode(m.returnType.fullyQualifiedName) + " "
+                «val accessor = m.isAccessor»
+                «val ret = m.returnType»
+                «IF accessor»
+                    «formatDataForJavaDoc(m, "@return " + asCode(ret.fullyQualifiedName) + " "
                     + asCode(propertyNameFromGetter(m)) + ", or " + asCode("null") + " if not present")»
+                «ELSE»
+                    «m.comment.asJavadoc»
                 «ENDIF»
                 «m.annotations.generateAnnotations»
-                «m.returnType.importedName» «m.name»(«m.parameters.generateParameters»);
+                «nullableType(ret, accessor)» «m.name»(«m.parameters.generateParameters»);
             «ENDFOR»
         «ENDIF»
     '''
 
-}
+    def private String nullableType(Type type, boolean accessor) {
+        if (accessor && type.isObject) {
+            return importedName(type, NULLABLE.importedName)
+        }
+        return type.importedName
+    }
 
+    def private static boolean isObject(Type type) {
+        // The return type has a package, so it's not a primitive type
+        return !type.getPackageName().isEmpty()
+    }
+}
index 5387a82c5340020f862c7635de89588cdd4b8f91..a414f707bd4d035c9b09e0178198ec2f2adac62f 100644 (file)
@@ -62,8 +62,8 @@ class JavaFileTemplate {
                 .collect(Collectors.joining());
     }
 
-    final String importedName(final Type intype) {
-        return javaType.getReferenceString(intype);
+    final String importedName(final Type intype, final String... annotations) {
+        return javaType.getReferenceString(intype, annotations);
     }
 
     final String importedName(final Class<?> cls) {
index 794ad9d3a778e4177093470037cb15208a3bc0ec..2033d6dc2b77dbf7f4156815e7562020b73a817f 100644 (file)
@@ -33,7 +33,6 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.stream.Collectors;
-import javax.annotation.Nullable;
 import org.junit.Test;
 import org.opendaylight.yangtools.yang.binding.ChildOf;
 import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext;
@@ -470,7 +469,7 @@ public class CompilationTest extends BaseCompilationTest {
             throw new AssertionError("Method getId() not found", e);
         }
 
-        assertEquals(ImmutableSet.of(RoutingContext.class, Nullable.class), Arrays.stream(getId.getAnnotations())
+        assertEquals(ImmutableSet.of(RoutingContext.class), Arrays.stream(getId.getAnnotations())
             .map(Annotation::annotationType).collect(Collectors.toSet()));
         CompilationTestUtils.cleanUp(sourcesOutputDir, compiledOutputDir);
     }