This is an automated email from the ASF dual-hosted git repository.

rskraba pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/avro.git


The following commit(s) were added to refs/heads/master by this push:
     new d5621e9f8 AVRO-2774: Generate @Overrides annotations (#1755)
d5621e9f8 is described below

commit d5621e9f8da61c02e4339c11b6852ad059dd72b3
Author: clesaec <[email protected]>
AuthorDate: Fri Jul 8 00:16:28 2022 +0200

    AVRO-2774: Generate @Overrides annotations (#1755)
    
    * AVRO-2774 : add override and test it
    
    * AVRO-2774 : remove sun deps for java17
---
 .../avro/specific/SpecificExceptionBase.java       |   7 +-
 .../specific/templates/java/classic/enum.vm        |   2 +
 .../specific/templates/java/classic/record.vm      |   6 +
 .../org/apache/avro/specific/TestSpecificData.java | 158 +++++++++++++++++++++
 .../avro/examples/baseball/FieldTest.java          |   6 +
 .../avro/examples/baseball/Player.java             |   6 +
 .../avro/examples/baseball/Position.java           |   2 +
 .../output/AddExtraOptionalGettersTest.java        |   6 +
 .../src/test/compiler/output/NoSettersTest.java    |   6 +
 .../output/OptionalGettersAllFieldsTest.java       |   6 +
 .../output/OptionalGettersNullableFieldsTest.java  |   6 +
 .../tools/src/test/compiler/output/Player.java     |   6 +
 .../tools/src/test/compiler/output/Position.java   |   2 +
 13 files changed, 218 insertions(+), 1 deletion(-)

diff --git 
a/lang/java/avro/src/main/java/org/apache/avro/specific/SpecificExceptionBase.java
 
b/lang/java/avro/src/main/java/org/apache/avro/specific/SpecificExceptionBase.java
index 82c23f129..64667ba24 100644
--- 
a/lang/java/avro/src/main/java/org/apache/avro/specific/SpecificExceptionBase.java
+++ 
b/lang/java/avro/src/main/java/org/apache/avro/specific/SpecificExceptionBase.java
@@ -62,7 +62,7 @@ public abstract class SpecificExceptionBase extends 
AvroRemoteException implemen
       return false; // not a record
     if (this.getClass() != that.getClass())
       return false; // not same schema
-    return SpecificData.get().compare(this, that, this.getSchema()) == 0;
+    return this.getSpecificData().compare(this, that, this.getSchema()) == 0;
   }
 
   @Override
@@ -76,4 +76,9 @@ public abstract class SpecificExceptionBase extends 
AvroRemoteException implemen
   @Override
   public abstract void readExternal(ObjectInput in) throws IOException;
 
+  public SpecificData getSpecificData() {
+    // Default implementation for backwards compatibility, overridden in 
generated
+    // code
+    return SpecificData.get();
+  }
 }
diff --git 
a/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic/enum.vm
 
b/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic/enum.vm
index fa5aa3f0a..c15b7ecd1 100644
--- 
a/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic/enum.vm
+++ 
b/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic/enum.vm
@@ -30,5 +30,7 @@ public enum ${this.mangleTypeIdentifier($schema.getName())} 
implements org.apach
   ;
   public static final org.apache.avro.Schema SCHEMA$ = new 
org.apache.avro.Schema.Parser().parse("${this.javaEscape($schema.toString())}");
   public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; }
+
+  @Override
   public org.apache.avro.Schema getSchema() { return SCHEMA$; }
 }
diff --git 
a/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic/record.vm
 
b/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic/record.vm
index 441552199..1e054d3ec 100755
--- 
a/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic/record.vm
+++ 
b/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic/record.vm
@@ -177,9 +177,14 @@ public class 
${this.mangleTypeIdentifier($schema.getName())}#if ($schema.isError
 #end
 
 #end
+  @Override
   public org.apache.avro.specific.SpecificData getSpecificData() { return 
MODEL$; }
+
+  @Override
   public org.apache.avro.Schema getSchema() { return SCHEMA$; }
+
   // Used by DatumWriter.  Applications should not call.
+  @Override
   public java.lang.Object get(int field$) {
     switch (field$) {
 #set ($i = 0)
@@ -207,6 +212,7 @@ public class 
${this.mangleTypeIdentifier($schema.getName())}#if ($schema.isError
 
 #end
   // Used by DatumReader.  Applications should not call.
+  @Override
   @SuppressWarnings(value="unchecked")
   public void put(int field$, java.lang.Object value$) {
     switch (field$) {
diff --git 
a/lang/java/compiler/src/test/java/org/apache/avro/specific/TestSpecificData.java
 
b/lang/java/compiler/src/test/java/org/apache/avro/specific/TestSpecificData.java
index 78dedfad0..e0dcdf0f0 100644
--- 
a/lang/java/compiler/src/test/java/org/apache/avro/specific/TestSpecificData.java
+++ 
b/lang/java/compiler/src/test/java/org/apache/avro/specific/TestSpecificData.java
@@ -17,15 +17,37 @@ package org.apache.avro.specific;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.net.URL;
 import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
 import javax.tools.JavaCompiler;
 import javax.tools.JavaFileObject;
 import javax.tools.StandardJavaFileManager;
 import javax.tools.ToolProvider;
 
+import com.sun.source.util.JavacTask;
+
 import org.apache.avro.Schema;
 import org.apache.avro.compiler.specific.SpecificCompiler;
 import org.apache.avro.generic.GenericData;
@@ -62,6 +84,20 @@ public class TestSpecificData {
     JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
     StandardJavaFileManager fileManager = javac.getStandardFileManager(null, 
null, null);
     Iterable<? extends JavaFileObject> units = 
fileManager.getJavaFileObjects("target/foo/Bar.java");
+
+    JavaCompiler.CompilationTask task1 = javac.getTask(null, fileManager, 
null, null, null, units);
+    JavacTask jcTask = (JavacTask) task1;
+
+    Iterable<? extends Element> analyze = jcTask.analyze();
+
+    GeneratedCodeController ctrl = new GeneratedCodeController();
+    for (Element el : analyze) {
+      if (el.getKind() == ElementKind.CLASS) {
+        List<String> accept = el.accept(ctrl, 0);
+        Assert.assertTrue(accept.stream().collect(Collectors.joining("\n\t")), 
accept.isEmpty());
+      }
+    }
+
     javac.getTask(null, fileManager, null, null, null, units).call();
     fileManager.close();
 
@@ -90,4 +126,126 @@ public class TestSpecificData {
       Assert.fail(ex.getMessage());
     }
   }
+
+  static class GeneratedCodeController implements ElementVisitor<List<String>, 
Integer> {
+
+    @Override
+    public List<String> visit(Element e, Integer integer) {
+      return null;
+    }
+
+    @Override
+    public List<String> visit(Element e) {
+      return this.visit(e, 1);
+    }
+
+    @Override
+    public List<String> visitPackage(PackageElement e, Integer integer) {
+      return e.getEnclosedElements().stream().map((Element sub) -> 
sub.accept(this, 1)).filter(Objects::nonNull)
+          .flatMap(List::stream).collect(Collectors.toList());
+    }
+
+    @Override
+    public List<String> visitType(TypeElement e, Integer integer) {
+      List<TypeMirror> interfaces = this.allInterfaces(e);
+
+      List<Method> methods = interfaces.stream().filter((TypeMirror tm) -> 
tm.getKind() == TypeKind.DECLARED)
+          .map(TypeMirror::toString).map((String typeName) -> {
+            try {
+              return 
Thread.currentThread().getContextClassLoader().loadClass(typeName);
+            } catch (ClassNotFoundException ex) {
+              return null;
+            }
+          
}).filter(Objects::nonNull).map(Class::getMethods).flatMap(Arrays::stream)
+          .filter((Method m) -> Modifier.isPublic(m.getModifiers()) && 
!Modifier.isStatic(m.getModifiers())
+              && !Modifier.isFinal(m.getModifiers()) && m.getDeclaringClass() 
!= Object.class)
+          .collect(Collectors.toList());
+
+      Stream<String> errors = e.getEnclosedElements().stream()
+          .filter((Element el) -> el.getKind() == 
ElementKind.METHOD).map(ExecutableElement.class::cast)
+          .filter((ExecutableElement declM) -> 
GeneratedCodeController.findFirst(declM, methods) != null)
+          .filter((ExecutableElement declM) -> 
declM.getAnnotation(Override.class) == null)
+          .map((ExecutableElement declM) -> "'" + 
declM.getReturnType().toString() + " " + declM.getSimpleName()
+              + "(...)' method doesn't have @Override annotation");
+
+      Stream<String> subError = e.getEnclosedElements().stream().map((Element 
sub) -> sub.accept(this, 1))
+          .filter(Objects::nonNull).flatMap(List::stream);
+      return Stream.concat(errors, subError).collect(Collectors.toList());
+    }
+
+    private List<TypeMirror> allInterfaces(TypeElement e) {
+      List<TypeMirror> allInterfaces = new ArrayList<>(e.getInterfaces());
+
+      TypeMirror superclass = e.getSuperclass();
+      if (superclass != null && !Objects.equals(superclass.toString(), 
"java.lang.Object")) {
+        allInterfaces.add(superclass);
+
+        if (superclass.getKind() == TypeKind.DECLARED) {
+          final Element element = ((DeclaredType) superclass).asElement();
+
+          if (element instanceof TypeElement) {
+            allInterfaces((TypeElement) element);
+          }
+        }
+      }
+      return allInterfaces;
+    }
+
+    private static Method findFirst(ExecutableElement ref, List<Method> 
methods) {
+      return methods.stream().filter((Method m) -> 
GeneratedCodeController.areMethodSame(ref, m)).findFirst()
+          .orElse(null);
+    }
+
+    private static boolean areMethodSame(ExecutableElement declaredMethod, 
Method interfaceMethod) {
+      boolean res = Objects.equals(declaredMethod.getSimpleName().toString(), 
interfaceMethod.getName());
+      if (!res) {
+        return false;
+      }
+
+      TypeMirror type = declaredMethod.getReturnType();
+      if (!type.toString().equals(interfaceMethod.getReturnType().getName())) {
+        try {
+          Class<?> declaredReturnedType = 
Thread.currentThread().getContextClassLoader().loadClass(type.toString());
+          res &= 
interfaceMethod.getReturnType().isAssignableFrom(declaredReturnedType);
+        } catch (ClassNotFoundException ex) {
+          return false;
+        }
+      }
+      List<? extends VariableElement> parameters = 
declaredMethod.getParameters();
+      Class<?>[] parameterTypes = interfaceMethod.getParameterTypes();
+      if (parameters.size() != parameterTypes.length) {
+        return false;
+      }
+      for (int i = 0; i < parameterTypes.length; i++) {
+        res &= areEquivalent(parameters.get(i), parameterTypes[i]);
+      }
+      return res;
+    }
+
+    private static boolean areEquivalent(VariableElement sourceParam, Class<?> 
typeParam) {
+      // sourceParam.getSimpleName()
+      // Type type = sourceParam.type;
+      return Objects.equals(sourceParam.getSimpleName(), typeParam.getName());
+    }
+
+    @Override
+    public List<String> visitVariable(VariableElement e, Integer integer) {
+      return Collections.emptyList();
+    }
+
+    @Override
+    public List<String> visitExecutable(ExecutableElement e, Integer integer) {
+      return null;
+    }
+
+    @Override
+    public List<String> visitTypeParameter(TypeParameterElement e, Integer 
integer) {
+      return Collections.emptyList();
+    }
+
+    @Override
+    public List<String> visitUnknown(Element e, Integer integer) {
+      return Collections.emptyList();
+    }
+  }
 }
diff --git 
a/lang/java/tools/src/test/compiler/output-string/avro/examples/baseball/FieldTest.java
 
b/lang/java/tools/src/test/compiler/output-string/avro/examples/baseball/FieldTest.java
index 59b8058c0..95606eb20 100644
--- 
a/lang/java/tools/src/test/compiler/output-string/avro/examples/baseball/FieldTest.java
+++ 
b/lang/java/tools/src/test/compiler/output-string/avro/examples/baseball/FieldTest.java
@@ -111,9 +111,14 @@ public class FieldTest extends 
org.apache.avro.specific.SpecificRecordBase imple
     this.timeMicros = 
timeMicros.truncatedTo(java.time.temporal.ChronoUnit.MICROS);
   }
 
+  @Override
   public org.apache.avro.specific.SpecificData getSpecificData() { return 
MODEL$; }
+
+  @Override
   public org.apache.avro.Schema getSchema() { return SCHEMA$; }
+
   // Used by DatumWriter.  Applications should not call.
+  @Override
   public java.lang.Object get(int field$) {
     switch (field$) {
     case 0: return number;
@@ -143,6 +148,7 @@ public class FieldTest extends 
org.apache.avro.specific.SpecificRecordBase imple
   }
 
   // Used by DatumReader.  Applications should not call.
+  @Override
   @SuppressWarnings(value="unchecked")
   public void put(int field$, java.lang.Object value$) {
     switch (field$) {
diff --git 
a/lang/java/tools/src/test/compiler/output-string/avro/examples/baseball/Player.java
 
b/lang/java/tools/src/test/compiler/output-string/avro/examples/baseball/Player.java
index 1785559ba..6a946b6bc 100644
--- 
a/lang/java/tools/src/test/compiler/output-string/avro/examples/baseball/Player.java
+++ 
b/lang/java/tools/src/test/compiler/output-string/avro/examples/baseball/Player.java
@@ -99,9 +99,14 @@ public class Player extends 
org.apache.avro.specific.SpecificRecordBase implemen
     this.position = position;
   }
 
+  @Override
   public org.apache.avro.specific.SpecificData getSpecificData() { return 
MODEL$; }
+
+  @Override
   public org.apache.avro.Schema getSchema() { return SCHEMA$; }
+
   // Used by DatumWriter.  Applications should not call.
+  @Override
   public java.lang.Object get(int field$) {
     switch (field$) {
     case 0: return number;
@@ -113,6 +118,7 @@ public class Player extends 
org.apache.avro.specific.SpecificRecordBase implemen
   }
 
   // Used by DatumReader.  Applications should not call.
+  @Override
   @SuppressWarnings(value="unchecked")
   public void put(int field$, java.lang.Object value$) {
     switch (field$) {
diff --git 
a/lang/java/tools/src/test/compiler/output-string/avro/examples/baseball/Position.java
 
b/lang/java/tools/src/test/compiler/output-string/avro/examples/baseball/Position.java
index a4504bbc4..3e6abc502 100644
--- 
a/lang/java/tools/src/test/compiler/output-string/avro/examples/baseball/Position.java
+++ 
b/lang/java/tools/src/test/compiler/output-string/avro/examples/baseball/Position.java
@@ -9,5 +9,7 @@ public enum Position implements 
org.apache.avro.generic.GenericEnumSymbol<Positi
   P, C, B1, B2, B3, SS, LF, CF, RF, DH  ;
   public static final org.apache.avro.Schema SCHEMA$ = new 
org.apache.avro.Schema.Parser().parse("{\"type\":\"enum\",\"name\":\"Position\",\"namespace\":\"avro.examples.baseball\",\"symbols\":[\"P\",\"C\",\"B1\",\"B2\",\"B3\",\"SS\",\"LF\",\"CF\",\"RF\",\"DH\"]}");
   public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; }
+
+  @Override
   public org.apache.avro.Schema getSchema() { return SCHEMA$; }
 }
diff --git 
a/lang/java/tools/src/test/compiler/output/AddExtraOptionalGettersTest.java 
b/lang/java/tools/src/test/compiler/output/AddExtraOptionalGettersTest.java
index 5e0e6dd04..d003777c3 100644
--- a/lang/java/tools/src/test/compiler/output/AddExtraOptionalGettersTest.java
+++ b/lang/java/tools/src/test/compiler/output/AddExtraOptionalGettersTest.java
@@ -92,9 +92,14 @@ public class AddExtraOptionalGettersTest extends 
org.apache.avro.specific.Specif
     this.favorite_number = favorite_number;
   }
 
+  @Override
   public org.apache.avro.specific.SpecificData getSpecificData() { return 
MODEL$; }
+
+  @Override
   public org.apache.avro.Schema getSchema() { return SCHEMA$; }
+
   // Used by DatumWriter.  Applications should not call.
+  @Override
   public java.lang.Object get(int field$) {
     switch (field$) {
     case 0: return name;
@@ -104,6 +109,7 @@ public class AddExtraOptionalGettersTest extends 
org.apache.avro.specific.Specif
   }
 
   // Used by DatumReader.  Applications should not call.
+  @Override
   @SuppressWarnings(value="unchecked")
   public void put(int field$, java.lang.Object value$) {
     switch (field$) {
diff --git a/lang/java/tools/src/test/compiler/output/NoSettersTest.java 
b/lang/java/tools/src/test/compiler/output/NoSettersTest.java
index 550cfec6e..7af126007 100644
--- a/lang/java/tools/src/test/compiler/output/NoSettersTest.java
+++ b/lang/java/tools/src/test/compiler/output/NoSettersTest.java
@@ -92,9 +92,14 @@ public class NoSettersTest extends 
org.apache.avro.specific.SpecificRecordBase i
     this.favorite_number = favorite_number;
   }
 
+  @Override
   public org.apache.avro.specific.SpecificData getSpecificData() { return 
MODEL$; }
+
+  @Override
   public org.apache.avro.Schema getSchema() { return SCHEMA$; }
+
   // Used by DatumWriter.  Applications should not call.
+  @Override
   public java.lang.Object get(int field$) {
     switch (field$) {
     case 0: return name;
@@ -104,6 +109,7 @@ public class NoSettersTest extends 
org.apache.avro.specific.SpecificRecordBase i
   }
 
   // Used by DatumReader.  Applications should not call.
+  @Override
   @SuppressWarnings(value="unchecked")
   public void put(int field$, java.lang.Object value$) {
     switch (field$) {
diff --git 
a/lang/java/tools/src/test/compiler/output/OptionalGettersAllFieldsTest.java 
b/lang/java/tools/src/test/compiler/output/OptionalGettersAllFieldsTest.java
index bf2be16e5..58bd90282 100644
--- a/lang/java/tools/src/test/compiler/output/OptionalGettersAllFieldsTest.java
+++ b/lang/java/tools/src/test/compiler/output/OptionalGettersAllFieldsTest.java
@@ -98,9 +98,14 @@ public class OptionalGettersAllFieldsTest extends 
org.apache.avro.specific.Speci
     this.nullable_favorite_number = nullable_favorite_number;
   }
 
+  @Override
   public org.apache.avro.specific.SpecificData getSpecificData() { return 
MODEL$; }
+
+  @Override
   public org.apache.avro.Schema getSchema() { return SCHEMA$; }
+
   // Used by DatumWriter.  Applications should not call.
+  @Override
   public java.lang.Object get(int field$) {
     switch (field$) {
     case 0: return name;
@@ -112,6 +117,7 @@ public class OptionalGettersAllFieldsTest extends 
org.apache.avro.specific.Speci
   }
 
   // Used by DatumReader.  Applications should not call.
+  @Override
   @SuppressWarnings(value="unchecked")
   public void put(int field$, java.lang.Object value$) {
     switch (field$) {
diff --git 
a/lang/java/tools/src/test/compiler/output/OptionalGettersNullableFieldsTest.java
 
b/lang/java/tools/src/test/compiler/output/OptionalGettersNullableFieldsTest.java
index 0f9c2e694..a09d1080c 100644
--- 
a/lang/java/tools/src/test/compiler/output/OptionalGettersNullableFieldsTest.java
+++ 
b/lang/java/tools/src/test/compiler/output/OptionalGettersNullableFieldsTest.java
@@ -98,9 +98,14 @@ public class OptionalGettersNullableFieldsTest extends 
org.apache.avro.specific.
     this.nullable_favorite_number = nullable_favorite_number;
   }
 
+  @Override
   public org.apache.avro.specific.SpecificData getSpecificData() { return 
MODEL$; }
+
+  @Override
   public org.apache.avro.Schema getSchema() { return SCHEMA$; }
+
   // Used by DatumWriter.  Applications should not call.
+  @Override
   public java.lang.Object get(int field$) {
     switch (field$) {
     case 0: return name;
@@ -112,6 +117,7 @@ public class OptionalGettersNullableFieldsTest extends 
org.apache.avro.specific.
   }
 
   // Used by DatumReader.  Applications should not call.
+  @Override
   @SuppressWarnings(value="unchecked")
   public void put(int field$, java.lang.Object value$) {
     switch (field$) {
diff --git a/lang/java/tools/src/test/compiler/output/Player.java 
b/lang/java/tools/src/test/compiler/output/Player.java
index 059371e57..0134ca55d 100644
--- a/lang/java/tools/src/test/compiler/output/Player.java
+++ b/lang/java/tools/src/test/compiler/output/Player.java
@@ -99,9 +99,14 @@ public class Player extends 
org.apache.avro.specific.SpecificRecordBase implemen
     this.position = position;
   }
 
+  @Override
   public org.apache.avro.specific.SpecificData getSpecificData() { return 
MODEL$; }
+
+  @Override
   public org.apache.avro.Schema getSchema() { return SCHEMA$; }
+
   // Used by DatumWriter.  Applications should not call.
+  @Override
   public java.lang.Object get(int field$) {
     switch (field$) {
     case 0: return number;
@@ -113,6 +118,7 @@ public class Player extends 
org.apache.avro.specific.SpecificRecordBase implemen
   }
 
   // Used by DatumReader.  Applications should not call.
+  @Override
   @SuppressWarnings(value="unchecked")
   public void put(int field$, java.lang.Object value$) {
     switch (field$) {
diff --git a/lang/java/tools/src/test/compiler/output/Position.java 
b/lang/java/tools/src/test/compiler/output/Position.java
index a4504bbc4..3e6abc502 100644
--- a/lang/java/tools/src/test/compiler/output/Position.java
+++ b/lang/java/tools/src/test/compiler/output/Position.java
@@ -9,5 +9,7 @@ public enum Position implements 
org.apache.avro.generic.GenericEnumSymbol<Positi
   P, C, B1, B2, B3, SS, LF, CF, RF, DH  ;
   public static final org.apache.avro.Schema SCHEMA$ = new 
org.apache.avro.Schema.Parser().parse("{\"type\":\"enum\",\"name\":\"Position\",\"namespace\":\"avro.examples.baseball\",\"symbols\":[\"P\",\"C\",\"B1\",\"B2\",\"B3\",\"SS\",\"LF\",\"CF\",\"RF\",\"DH\"]}");
   public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; }
+
+  @Override
   public org.apache.avro.Schema getSchema() { return SCHEMA$; }
 }

Reply via email to