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$; }
}