diff --git a/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java b/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java
index f6f842035fa..117fd2ed6ee 100644
--- a/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java
+++ b/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java
@@ -122,6 +122,7 @@ void addLogicalTypeConversions(SpecificData specificData) {
private boolean gettersReturnOptional = false;
private boolean optionalGettersForNullableFieldsOnly = false;
private boolean createSetters = true;
+ private boolean createNullSafeAnnotations = false;
private boolean createAllArgsConstructor = true;
private String outputCharacterEncoding;
private boolean enableDecimalLogicalType = false;
@@ -262,6 +263,17 @@ public void setCreateSetters(boolean createSetters) {
this.createSetters = createSetters;
}
+ public boolean isCreateNullSafeAnnotations() {
+ return this.createNullSafeAnnotations;
+ }
+
+ /**
+ * Set to true to add jetbrains @Nullable and @NotNull annotations
+ */
+ public void setCreateNullSafeAnnotations(boolean createNullSafeAnnotations) {
+ this.createNullSafeAnnotations = createNullSafeAnnotations;
+ }
+
public boolean isCreateOptionalGetters() {
return this.createOptionalGetters;
}
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 5e7f2550be7..2e3bb075961 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
@@ -161,7 +161,7 @@ public class ${this.mangleTypeIdentifier($schema.getName())} extends ${this.getS
#end
#end
*/
- public ${this.mangleTypeIdentifier($schema.getName())}(#foreach($field in $schema.getFields())${this.javaType($field.schema())} ${this.mangle($field.name())}#if($foreach.count < $schema.getFields().size()), #end#end) {
+ public ${this.mangleTypeIdentifier($schema.getName())}(#foreach($field in $schema.getFields())#if(${this.createNullSafeAnnotations})#if(${field.schema().isNullable()})@org.jetbrains.annotations.Nullable#else@org.jetbrains.annotations.NotNull#end #end${this.javaType($field.schema())} ${this.mangle($field.name())}#if($foreach.count < $schema.getFields().size()), #end#end) {
#foreach ($field in $schema.getFields())
${this.generateSetterCode($field.schema(), ${this.mangle($field.name())}, ${this.mangle($field.name())})}
#end
@@ -243,6 +243,13 @@ public class ${this.mangleTypeIdentifier($schema.getName())} extends ${this.getS
#else * @return The value of the '${this.mangle($field.name(), $schema.isError())}' field.
#end
*/
+#if(${this.createNullSafeAnnotations})
+#if (${field.schema().isNullable()})
+ @org.jetbrains.annotations.Nullable
+#else
+ @org.jetbrains.annotations.NotNull
+#end
+#end
public ${this.javaUnbox($field.schema(), false)} ${this.generateGetMethod($schema, $field)}() {
return ${this.mangle($field.name(), $schema.isError())};
}
@@ -267,7 +274,7 @@ public class ${this.mangleTypeIdentifier($schema.getName())} extends ${this.getS
#end
* @param value the value to set.
*/
- public void ${this.generateSetMethod($schema, $field)}(${this.javaUnbox($field.schema(), false)} value) {
+ public void ${this.generateSetMethod($schema, $field)}(#if(${this.createNullSafeAnnotations})#if(${field.schema().isNullable()})@org.jetbrains.annotations.Nullable#else@org.jetbrains.annotations.NotNull#end #end${this.javaUnbox($field.schema(), false)} value) {
${this.generateSetterCode($field.schema(), ${this.mangle($field.name(), $schema.isError())}, "value")}
}
#end
@@ -423,7 +430,7 @@ public class ${this.mangleTypeIdentifier($schema.getName())} extends ${this.getS
* @param value The value of '${this.mangle($field.name(), $schema.isError())}'.
* @return This builder.
*/
- public #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangleTypeIdentifier($schema.getName())}.Builder ${this.generateSetMethod($schema, $field)}(${this.javaUnbox($field.schema(), false)} value) {
+ public #if ($schema.getNamespace())$this.mangle($schema.getNamespace()).#end${this.mangleTypeIdentifier($schema.getName())}.Builder ${this.generateSetMethod($schema, $field)}(#if(${this.createNullSafeAnnotations})#if(${field.schema().isNullable()})@org.jetbrains.annotations.Nullable#else@org.jetbrains.annotations.NotNull#end #end${this.javaUnbox($field.schema(), false)} value) {
validate(fields()[$field.pos()], value);
#if (${this.hasBuilder($field.schema())})
this.${this.mangle($field.name(), $schema.isError())}Builder = null;
diff --git a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/AbstractAvroMojo.java b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/AbstractAvroMojo.java
index e34dd1b01fd..7dc203b0357 100644
--- a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/AbstractAvroMojo.java
+++ b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/AbstractAvroMojo.java
@@ -182,6 +182,19 @@ public abstract class AbstractAvroMojo extends AbstractMojo {
*/
protected boolean createSetters;
+ /**
+ * The createNullSafeAnnotations parameter adds JetBrains {@literal @}Nullable
+ * and {@literal @}NotNull annotations for fhe fields of the record. The default
+ * is to not include annotations.
+ *
+ * @parameter property="createNullSafeAnnotations"
+ *
+ * @see
+ * JetBrains nullability annotations
+ */
+ protected boolean createNullSafeAnnotations = false;
+
/**
* A set of fully qualified class names of custom
* {@link org.apache.avro.Conversion} implementations to add to the compiler.
@@ -391,6 +404,7 @@ private void doCompile(File sourceFileForModificationDetection, SpecificCompiler
compiler.setGettersReturnOptional(gettersReturnOptional);
compiler.setOptionalGettersForNullableFieldsOnly(optionalGettersForNullableFieldsOnly);
compiler.setCreateSetters(createSetters);
+ compiler.setCreateNullSafeAnnotations(createNullSafeAnnotations);
compiler.setEnableDecimalLogicalType(enableDecimalLogicalType);
try {
for (String customConversion : customConversions) {
diff --git a/lang/java/tools/src/main/java/org/apache/avro/tool/SpecificCompilerTool.java b/lang/java/tools/src/main/java/org/apache/avro/tool/SpecificCompilerTool.java
index 04eb5466bac..d82929e940f 100644
--- a/lang/java/tools/src/main/java/org/apache/avro/tool/SpecificCompilerTool.java
+++ b/lang/java/tools/src/main/java/org/apache/avro/tool/SpecificCompilerTool.java
@@ -50,7 +50,7 @@ public int run(InputStream in, PrintStream out, PrintStream err, List or
if (origArgs.size() < 3) {
System.err
.println("Usage: [-encoding ] [-string] [-bigDecimal] [-fieldVisibility ] "
- + "[-noSetters] [-addExtraOptionalGetters] [-optionalGetters ] "
+ + "[-noSetters] [-nullSafeAnnotations] [-addExtraOptionalGetters] [-optionalGetters ] "
+ "[-templateDir ] (schema|protocol) input... outputdir");
System.err.println(" input - input files or directories");
System.err.println(" outputdir - directory to write generated java");
@@ -58,6 +58,7 @@ public int run(InputStream in, PrintStream out, PrintStream err, List or
System.err.println(" -string - use java.lang.String instead of Utf8");
System.err.println(" -fieldVisibility [private|public] - use either and default private");
System.err.println(" -noSetters - do not generate setters");
+ System.err.println(" -nullSafeAnnotations - add @Nullable and @NotNull annotations");
System.err
.println(" -addExtraOptionalGetters - generate extra getters with this format: 'getOptional'");
System.err.println(
@@ -72,6 +73,7 @@ public int run(InputStream in, PrintStream out, PrintStream err, List or
compilerOpts.stringType = StringType.CharSequence;
compilerOpts.useLogicalDecimal = false;
compilerOpts.createSetters = true;
+ compilerOpts.createNullSafeAnnotations = false;
compilerOpts.optionalGettersType = Optional.empty();
compilerOpts.addExtraOptionalGetters = false;
compilerOpts.encoding = Optional.empty();
@@ -85,6 +87,11 @@ public int run(InputStream in, PrintStream out, PrintStream err, List or
args.remove(args.indexOf("-noSetters"));
}
+ if (args.contains("-nullSafeAnnotations")) {
+ compilerOpts.createNullSafeAnnotations = true;
+ args.remove(args.indexOf("-nullSafeAnnotations"));
+ }
+
if (args.contains("-addExtraOptionalGetters")) {
compilerOpts.addExtraOptionalGetters = true;
args.remove(args.indexOf("-addExtraOptionalGetters"));
@@ -172,6 +179,7 @@ private void executeCompiler(SpecificCompiler compiler, CompilerOptions opts, Fi
throws IOException {
compiler.setStringType(opts.stringType);
compiler.setCreateSetters(opts.createSetters);
+ compiler.setCreateNullSafeAnnotations(opts.createNullSafeAnnotations);
opts.optionalGettersType.ifPresent(choice -> {
compiler.setGettersReturnOptional(true);
@@ -267,6 +275,7 @@ private static class CompilerOptions {
Optional fieldVisibility;
boolean useLogicalDecimal;
boolean createSetters;
+ boolean createNullSafeAnnotations;
boolean addExtraOptionalGetters;
Optional optionalGettersType;
Optional templateDir;
diff --git a/lang/java/tools/src/test/compiler/input/nullsafeannotationsfieldstest.avsc b/lang/java/tools/src/test/compiler/input/nullsafeannotationsfieldstest.avsc
new file mode 100644
index 00000000000..04ef43e34ef
--- /dev/null
+++ b/lang/java/tools/src/test/compiler/input/nullsafeannotationsfieldstest.avsc
@@ -0,0 +1,8 @@
+{"type":"record", "name":"NullSafeAnnotationsFieldsTest", "namespace": "avro.examples.baseball", "doc":"Test that @org.jetbrains.annotations.Nullable and @org.jetbrains.annotations.NotNull annotations are created for all fields",
+ "fields": [
+ {"name": "name", "type": "string"},
+ {"name": "nullable_name", "type": ["string", "null"]},
+ {"name": "favorite_number", "type": "int"},
+ {"name": "nullable_favorite_number", "type": ["int", "null"]}
+ ]
+}
diff --git a/lang/java/tools/src/test/compiler/output-string/avro/examples/baseball/NullSafeAnnotationsFieldsTest.java b/lang/java/tools/src/test/compiler/output-string/avro/examples/baseball/NullSafeAnnotationsFieldsTest.java
new file mode 100644
index 00000000000..bf4d4fccc9c
--- /dev/null
+++ b/lang/java/tools/src/test/compiler/output-string/avro/examples/baseball/NullSafeAnnotationsFieldsTest.java
@@ -0,0 +1,595 @@
+/**
+ * Autogenerated by Avro
+ *
+ * DO NOT EDIT DIRECTLY
+ */
+package avro.examples.baseball;
+
+import org.apache.avro.generic.GenericArray;
+import org.apache.avro.specific.SpecificData;
+import org.apache.avro.util.Utf8;
+import org.apache.avro.message.BinaryMessageEncoder;
+import org.apache.avro.message.BinaryMessageDecoder;
+import org.apache.avro.message.SchemaStore;
+
+/** Test that @org.jetbrains.annotations.Nullable and @org.jetbrains.annotations.NotNull annotations are created for all fields */
+@org.apache.avro.specific.AvroGenerated
+public class NullSafeAnnotationsFieldsTest extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord {
+ private static final long serialVersionUID = 2020521726426674816L;
+
+
+ public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"NullSafeAnnotationsFieldsTest\",\"namespace\":\"avro.examples.baseball\",\"doc\":\"Test that @org.jetbrains.annotations.Nullable and @org.jetbrains.annotations.NotNull annotations are created for all fields\",\"fields\":[{\"name\":\"name\",\"type\":{\"type\":\"string\",\"avro.java.string\":\"String\"}},{\"name\":\"nullable_name\",\"type\":[{\"type\":\"string\",\"avro.java.string\":\"String\"},\"null\"]},{\"name\":\"favorite_number\",\"type\":\"int\"},{\"name\":\"nullable_favorite_number\",\"type\":[\"int\",\"null\"]}]}");
+ public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; }
+
+ private static final SpecificData MODEL$ = new SpecificData();
+
+ private static final BinaryMessageEncoder ENCODER =
+ new BinaryMessageEncoder<>(MODEL$, SCHEMA$);
+
+ private static final BinaryMessageDecoder DECODER =
+ new BinaryMessageDecoder<>(MODEL$, SCHEMA$);
+
+ /**
+ * Return the BinaryMessageEncoder instance used by this class.
+ * @return the message encoder used by this class
+ */
+ public static BinaryMessageEncoder getEncoder() {
+ return ENCODER;
+ }
+
+ /**
+ * Return the BinaryMessageDecoder instance used by this class.
+ * @return the message decoder used by this class
+ */
+ public static BinaryMessageDecoder getDecoder() {
+ return DECODER;
+ }
+
+ /**
+ * Create a new BinaryMessageDecoder instance for this class that uses the specified {@link SchemaStore}.
+ * @param resolver a {@link SchemaStore} used to find schemas by fingerprint
+ * @return a BinaryMessageDecoder instance for this class backed by the given SchemaStore
+ */
+ public static BinaryMessageDecoder createDecoder(SchemaStore resolver) {
+ return new BinaryMessageDecoder<>(MODEL$, SCHEMA$, resolver);
+ }
+
+ /**
+ * Serializes this NullSafeAnnotationsFieldsTest to a ByteBuffer.
+ * @return a buffer holding the serialized data for this instance
+ * @throws java.io.IOException if this instance could not be serialized
+ */
+ public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException {
+ return ENCODER.encode(this);
+ }
+
+ /**
+ * Deserializes a NullSafeAnnotationsFieldsTest from a ByteBuffer.
+ * @param b a byte buffer holding serialized data for an instance of this class
+ * @return a NullSafeAnnotationsFieldsTest instance decoded from the given buffer
+ * @throws java.io.IOException if the given bytes could not be deserialized into an instance of this class
+ */
+ public static NullSafeAnnotationsFieldsTest fromByteBuffer(
+ java.nio.ByteBuffer b) throws java.io.IOException {
+ return DECODER.decode(b);
+ }
+
+ private java.lang.String name;
+ private java.lang.String nullable_name;
+ private int favorite_number;
+ private java.lang.Integer nullable_favorite_number;
+
+ /**
+ * Default constructor. Note that this does not initialize fields
+ * to their default values from the schema. If that is desired then
+ * one should use newBuilder()
.
+ */
+ public NullSafeAnnotationsFieldsTest() {}
+
+ /**
+ * All-args constructor.
+ * @param name The new value for name
+ * @param nullable_name The new value for nullable_name
+ * @param favorite_number The new value for favorite_number
+ * @param nullable_favorite_number The new value for nullable_favorite_number
+ */
+ public NullSafeAnnotationsFieldsTest(@org.jetbrains.annotations.NotNull java.lang.String name, @org.jetbrains.annotations.Nullable java.lang.String nullable_name, @org.jetbrains.annotations.NotNull java.lang.Integer favorite_number, @org.jetbrains.annotations.Nullable java.lang.Integer nullable_favorite_number) {
+ this.name = name;
+ this.nullable_name = nullable_name;
+ this.favorite_number = favorite_number;
+ 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;
+ case 1: return nullable_name;
+ case 2: return favorite_number;
+ case 3: return nullable_favorite_number;
+ default: throw new IndexOutOfBoundsException("Invalid index: " + field$);
+ }
+ }
+
+ // Used by DatumReader. Applications should not call.
+ @Override
+ @SuppressWarnings(value="unchecked")
+ public void put(int field$, java.lang.Object value$) {
+ switch (field$) {
+ case 0: name = value$ != null ? value$.toString() : null; break;
+ case 1: nullable_name = value$ != null ? value$.toString() : null; break;
+ case 2: favorite_number = (java.lang.Integer)value$; break;
+ case 3: nullable_favorite_number = (java.lang.Integer)value$; break;
+ default: throw new IndexOutOfBoundsException("Invalid index: " + field$);
+ }
+ }
+
+ /**
+ * Gets the value of the 'name' field.
+ * @return The value of the 'name' field.
+ */
+ @org.jetbrains.annotations.NotNull
+ public java.lang.String getName() {
+ return name;
+ }
+
+
+ /**
+ * Sets the value of the 'name' field.
+ * @param value the value to set.
+ */
+ public void setName(@org.jetbrains.annotations.NotNull java.lang.String value) {
+ this.name = value;
+ }
+
+ /**
+ * Gets the value of the 'nullable_name' field.
+ * @return The value of the 'nullable_name' field.
+ */
+ @org.jetbrains.annotations.Nullable
+ public java.lang.String getNullableName() {
+ return nullable_name;
+ }
+
+
+ /**
+ * Sets the value of the 'nullable_name' field.
+ * @param value the value to set.
+ */
+ public void setNullableName(@org.jetbrains.annotations.Nullable java.lang.String value) {
+ this.nullable_name = value;
+ }
+
+ /**
+ * Gets the value of the 'favorite_number' field.
+ * @return The value of the 'favorite_number' field.
+ */
+ @org.jetbrains.annotations.NotNull
+ public int getFavoriteNumber() {
+ return favorite_number;
+ }
+
+
+ /**
+ * Sets the value of the 'favorite_number' field.
+ * @param value the value to set.
+ */
+ public void setFavoriteNumber(@org.jetbrains.annotations.NotNull int value) {
+ this.favorite_number = value;
+ }
+
+ /**
+ * Gets the value of the 'nullable_favorite_number' field.
+ * @return The value of the 'nullable_favorite_number' field.
+ */
+ @org.jetbrains.annotations.Nullable
+ public java.lang.Integer getNullableFavoriteNumber() {
+ return nullable_favorite_number;
+ }
+
+
+ /**
+ * Sets the value of the 'nullable_favorite_number' field.
+ * @param value the value to set.
+ */
+ public void setNullableFavoriteNumber(@org.jetbrains.annotations.Nullable java.lang.Integer value) {
+ this.nullable_favorite_number = value;
+ }
+
+ /**
+ * Creates a new NullSafeAnnotationsFieldsTest RecordBuilder.
+ * @return A new NullSafeAnnotationsFieldsTest RecordBuilder
+ */
+ public static avro.examples.baseball.NullSafeAnnotationsFieldsTest.Builder newBuilder() {
+ return new avro.examples.baseball.NullSafeAnnotationsFieldsTest.Builder();
+ }
+
+ /**
+ * Creates a new NullSafeAnnotationsFieldsTest RecordBuilder by copying an existing Builder.
+ * @param other The existing builder to copy.
+ * @return A new NullSafeAnnotationsFieldsTest RecordBuilder
+ */
+ public static avro.examples.baseball.NullSafeAnnotationsFieldsTest.Builder newBuilder(avro.examples.baseball.NullSafeAnnotationsFieldsTest.Builder other) {
+ if (other == null) {
+ return new avro.examples.baseball.NullSafeAnnotationsFieldsTest.Builder();
+ } else {
+ return new avro.examples.baseball.NullSafeAnnotationsFieldsTest.Builder(other);
+ }
+ }
+
+ /**
+ * Creates a new NullSafeAnnotationsFieldsTest RecordBuilder by copying an existing NullSafeAnnotationsFieldsTest instance.
+ * @param other The existing instance to copy.
+ * @return A new NullSafeAnnotationsFieldsTest RecordBuilder
+ */
+ public static avro.examples.baseball.NullSafeAnnotationsFieldsTest.Builder newBuilder(avro.examples.baseball.NullSafeAnnotationsFieldsTest other) {
+ if (other == null) {
+ return new avro.examples.baseball.NullSafeAnnotationsFieldsTest.Builder();
+ } else {
+ return new avro.examples.baseball.NullSafeAnnotationsFieldsTest.Builder(other);
+ }
+ }
+
+ /**
+ * RecordBuilder for NullSafeAnnotationsFieldsTest instances.
+ */
+ @org.apache.avro.specific.AvroGenerated
+ public static class Builder extends org.apache.avro.specific.SpecificRecordBuilderBase
+ implements org.apache.avro.data.RecordBuilder {
+
+ private java.lang.String name;
+ private java.lang.String nullable_name;
+ private int favorite_number;
+ private java.lang.Integer nullable_favorite_number;
+
+ /** Creates a new Builder */
+ private Builder() {
+ super(SCHEMA$, MODEL$);
+ }
+
+ /**
+ * Creates a Builder by copying an existing Builder.
+ * @param other The existing Builder to copy.
+ */
+ private Builder(avro.examples.baseball.NullSafeAnnotationsFieldsTest.Builder other) {
+ super(other);
+ if (isValidValue(fields()[0], other.name)) {
+ this.name = data().deepCopy(fields()[0].schema(), other.name);
+ fieldSetFlags()[0] = other.fieldSetFlags()[0];
+ }
+ if (isValidValue(fields()[1], other.nullable_name)) {
+ this.nullable_name = data().deepCopy(fields()[1].schema(), other.nullable_name);
+ fieldSetFlags()[1] = other.fieldSetFlags()[1];
+ }
+ if (isValidValue(fields()[2], other.favorite_number)) {
+ this.favorite_number = data().deepCopy(fields()[2].schema(), other.favorite_number);
+ fieldSetFlags()[2] = other.fieldSetFlags()[2];
+ }
+ if (isValidValue(fields()[3], other.nullable_favorite_number)) {
+ this.nullable_favorite_number = data().deepCopy(fields()[3].schema(), other.nullable_favorite_number);
+ fieldSetFlags()[3] = other.fieldSetFlags()[3];
+ }
+ }
+
+ /**
+ * Creates a Builder by copying an existing NullSafeAnnotationsFieldsTest instance
+ * @param other The existing instance to copy.
+ */
+ private Builder(avro.examples.baseball.NullSafeAnnotationsFieldsTest other) {
+ super(SCHEMA$, MODEL$);
+ if (isValidValue(fields()[0], other.name)) {
+ this.name = data().deepCopy(fields()[0].schema(), other.name);
+ fieldSetFlags()[0] = true;
+ }
+ if (isValidValue(fields()[1], other.nullable_name)) {
+ this.nullable_name = data().deepCopy(fields()[1].schema(), other.nullable_name);
+ fieldSetFlags()[1] = true;
+ }
+ if (isValidValue(fields()[2], other.favorite_number)) {
+ this.favorite_number = data().deepCopy(fields()[2].schema(), other.favorite_number);
+ fieldSetFlags()[2] = true;
+ }
+ if (isValidValue(fields()[3], other.nullable_favorite_number)) {
+ this.nullable_favorite_number = data().deepCopy(fields()[3].schema(), other.nullable_favorite_number);
+ fieldSetFlags()[3] = true;
+ }
+ }
+
+ /**
+ * Gets the value of the 'name' field.
+ * @return The value.
+ */
+ public java.lang.String getName() {
+ return name;
+ }
+
+
+ /**
+ * Sets the value of the 'name' field.
+ * @param value The value of 'name'.
+ * @return This builder.
+ */
+ public avro.examples.baseball.NullSafeAnnotationsFieldsTest.Builder setName(@org.jetbrains.annotations.NotNull java.lang.String value) {
+ validate(fields()[0], value);
+ this.name = value;
+ fieldSetFlags()[0] = true;
+ return this;
+ }
+
+ /**
+ * Checks whether the 'name' field has been set.
+ * @return True if the 'name' field has been set, false otherwise.
+ */
+ public boolean hasName() {
+ return fieldSetFlags()[0];
+ }
+
+
+ /**
+ * Clears the value of the 'name' field.
+ * @return This builder.
+ */
+ public avro.examples.baseball.NullSafeAnnotationsFieldsTest.Builder clearName() {
+ name = null;
+ fieldSetFlags()[0] = false;
+ return this;
+ }
+
+ /**
+ * Gets the value of the 'nullable_name' field.
+ * @return The value.
+ */
+ public java.lang.String getNullableName() {
+ return nullable_name;
+ }
+
+
+ /**
+ * Sets the value of the 'nullable_name' field.
+ * @param value The value of 'nullable_name'.
+ * @return This builder.
+ */
+ public avro.examples.baseball.NullSafeAnnotationsFieldsTest.Builder setNullableName(@org.jetbrains.annotations.Nullable java.lang.String value) {
+ validate(fields()[1], value);
+ this.nullable_name = value;
+ fieldSetFlags()[1] = true;
+ return this;
+ }
+
+ /**
+ * Checks whether the 'nullable_name' field has been set.
+ * @return True if the 'nullable_name' field has been set, false otherwise.
+ */
+ public boolean hasNullableName() {
+ return fieldSetFlags()[1];
+ }
+
+
+ /**
+ * Clears the value of the 'nullable_name' field.
+ * @return This builder.
+ */
+ public avro.examples.baseball.NullSafeAnnotationsFieldsTest.Builder clearNullableName() {
+ nullable_name = null;
+ fieldSetFlags()[1] = false;
+ return this;
+ }
+
+ /**
+ * Gets the value of the 'favorite_number' field.
+ * @return The value.
+ */
+ public int getFavoriteNumber() {
+ return favorite_number;
+ }
+
+
+ /**
+ * Sets the value of the 'favorite_number' field.
+ * @param value The value of 'favorite_number'.
+ * @return This builder.
+ */
+ public avro.examples.baseball.NullSafeAnnotationsFieldsTest.Builder setFavoriteNumber(@org.jetbrains.annotations.NotNull int value) {
+ validate(fields()[2], value);
+ this.favorite_number = value;
+ fieldSetFlags()[2] = true;
+ return this;
+ }
+
+ /**
+ * Checks whether the 'favorite_number' field has been set.
+ * @return True if the 'favorite_number' field has been set, false otherwise.
+ */
+ public boolean hasFavoriteNumber() {
+ return fieldSetFlags()[2];
+ }
+
+
+ /**
+ * Clears the value of the 'favorite_number' field.
+ * @return This builder.
+ */
+ public avro.examples.baseball.NullSafeAnnotationsFieldsTest.Builder clearFavoriteNumber() {
+ fieldSetFlags()[2] = false;
+ return this;
+ }
+
+ /**
+ * Gets the value of the 'nullable_favorite_number' field.
+ * @return The value.
+ */
+ public java.lang.Integer getNullableFavoriteNumber() {
+ return nullable_favorite_number;
+ }
+
+
+ /**
+ * Sets the value of the 'nullable_favorite_number' field.
+ * @param value The value of 'nullable_favorite_number'.
+ * @return This builder.
+ */
+ public avro.examples.baseball.NullSafeAnnotationsFieldsTest.Builder setNullableFavoriteNumber(@org.jetbrains.annotations.Nullable java.lang.Integer value) {
+ validate(fields()[3], value);
+ this.nullable_favorite_number = value;
+ fieldSetFlags()[3] = true;
+ return this;
+ }
+
+ /**
+ * Checks whether the 'nullable_favorite_number' field has been set.
+ * @return True if the 'nullable_favorite_number' field has been set, false otherwise.
+ */
+ public boolean hasNullableFavoriteNumber() {
+ return fieldSetFlags()[3];
+ }
+
+
+ /**
+ * Clears the value of the 'nullable_favorite_number' field.
+ * @return This builder.
+ */
+ public avro.examples.baseball.NullSafeAnnotationsFieldsTest.Builder clearNullableFavoriteNumber() {
+ nullable_favorite_number = null;
+ fieldSetFlags()[3] = false;
+ return this;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public NullSafeAnnotationsFieldsTest build() {
+ try {
+ NullSafeAnnotationsFieldsTest record = new NullSafeAnnotationsFieldsTest();
+ record.name = fieldSetFlags()[0] ? this.name : (java.lang.String) defaultValue(fields()[0]);
+ record.nullable_name = fieldSetFlags()[1] ? this.nullable_name : (java.lang.String) defaultValue(fields()[1]);
+ record.favorite_number = fieldSetFlags()[2] ? this.favorite_number : (java.lang.Integer) defaultValue(fields()[2]);
+ record.nullable_favorite_number = fieldSetFlags()[3] ? this.nullable_favorite_number : (java.lang.Integer) defaultValue(fields()[3]);
+ return record;
+ } catch (org.apache.avro.AvroMissingFieldException e) {
+ throw e;
+ } catch (java.lang.Exception e) {
+ throw new org.apache.avro.AvroRuntimeException(e);
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static final org.apache.avro.io.DatumWriter
+ WRITER$ = (org.apache.avro.io.DatumWriter)MODEL$.createDatumWriter(SCHEMA$);
+
+ @Override public void writeExternal(java.io.ObjectOutput out)
+ throws java.io.IOException {
+ WRITER$.write(this, SpecificData.getEncoder(out));
+ }
+
+ @SuppressWarnings("unchecked")
+ private static final org.apache.avro.io.DatumReader
+ READER$ = (org.apache.avro.io.DatumReader)MODEL$.createDatumReader(SCHEMA$);
+
+ @Override public void readExternal(java.io.ObjectInput in)
+ throws java.io.IOException {
+ READER$.read(this, SpecificData.getDecoder(in));
+ }
+
+ @Override protected boolean hasCustomCoders() { return true; }
+
+ @Override public void customEncode(org.apache.avro.io.Encoder out)
+ throws java.io.IOException
+ {
+ out.writeString(this.name);
+
+ if (this.nullable_name == null) {
+ out.writeIndex(1);
+ out.writeNull();
+ } else {
+ out.writeIndex(0);
+ out.writeString(this.nullable_name);
+ }
+
+ out.writeInt(this.favorite_number);
+
+ if (this.nullable_favorite_number == null) {
+ out.writeIndex(1);
+ out.writeNull();
+ } else {
+ out.writeIndex(0);
+ out.writeInt(this.nullable_favorite_number);
+ }
+
+ }
+
+ @Override public void customDecode(org.apache.avro.io.ResolvingDecoder in)
+ throws java.io.IOException
+ {
+ org.apache.avro.Schema.Field[] fieldOrder = in.readFieldOrderIfDiff();
+ if (fieldOrder == null) {
+ this.name = in.readString();
+
+ if (in.readIndex() != 0) {
+ in.readNull();
+ this.nullable_name = null;
+ } else {
+ this.nullable_name = in.readString();
+ }
+
+ this.favorite_number = in.readInt();
+
+ if (in.readIndex() != 0) {
+ in.readNull();
+ this.nullable_favorite_number = null;
+ } else {
+ this.nullable_favorite_number = in.readInt();
+ }
+
+ } else {
+ for (int i = 0; i < 4; i++) {
+ switch (fieldOrder[i].pos()) {
+ case 0:
+ this.name = in.readString();
+ break;
+
+ case 1:
+ if (in.readIndex() != 0) {
+ in.readNull();
+ this.nullable_name = null;
+ } else {
+ this.nullable_name = in.readString();
+ }
+ break;
+
+ case 2:
+ this.favorite_number = in.readInt();
+ break;
+
+ case 3:
+ if (in.readIndex() != 0) {
+ in.readNull();
+ this.nullable_favorite_number = null;
+ } else {
+ this.nullable_favorite_number = in.readInt();
+ }
+ break;
+
+ default:
+ throw new java.io.IOException("Corrupt ResolvingDecoder.");
+ }
+ }
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/lang/java/tools/src/test/java/org/apache/avro/tool/TestSpecificCompilerTool.java b/lang/java/tools/src/test/java/org/apache/avro/tool/TestSpecificCompilerTool.java
index bb66545dbb0..03768fea4b6 100644
--- a/lang/java/tools/src/test/java/org/apache/avro/tool/TestSpecificCompilerTool.java
+++ b/lang/java/tools/src/test/java/org/apache/avro/tool/TestSpecificCompilerTool.java
@@ -58,6 +58,8 @@ public class TestSpecificCompilerTool {
"avro/examples/baseball/Position.java");
private static final File TEST_EXPECTED_STRING_PLAYER = new File(TEST_EXPECTED_STRING_OUTPUT_DIR,
"avro/examples/baseball/Player.java");
+ private static final File TEST_EXPECTED_STRING_NULL_SAFE_ANNOTATIONS_TEST = new File(TEST_EXPECTED_STRING_OUTPUT_DIR,
+ "avro/examples/baseball/NullSafeAnnotationsFieldsTest.java");
private static final File TEST_EXPECTED_STRING_FIELDTEST = new File(TEST_EXPECTED_STRING_OUTPUT_DIR,
"avro/examples/baseball/FieldTest.java");
private static final File TEST_EXPECTED_STRING_PROTO = new File(TEST_EXPECTED_STRING_OUTPUT_DIR,
@@ -83,6 +85,8 @@ public class TestSpecificCompilerTool {
"avro/examples/baseball/Position.java");
private static final File TEST_OUTPUT_STRING_FIELDTEST = new File(TEST_OUTPUT_STRING_DIR,
"avro/examples/baseball/FieldTest.java");
+ private static final File TEST_OUTPUT_STRING_NULL_SAFE_ANNOTATIONS_TEST = new File(TEST_OUTPUT_STRING_DIR,
+ "avro/examples/baseball/NullSafeAnnotationsFieldsTest.java");
private static final File TEST_OUTPUT_STRING_PROTO = new File(TEST_OUTPUT_STRING_DIR,
"avro/examples/baseball/Proto.java");
@@ -109,6 +113,15 @@ void compileSchemaWithOptionalGettersForNullableFieldsOnly() throws Exception {
assertFileMatch(TEST_EXPECTED_OPTIONAL_GETTERS_FOR_NULLABLE_FIELDS, TEST_OUTPUT_OPTIONAL_GETTERS_NULLABLE_FIELDS);
}
+ @Test
+ void compileSchemaWithNullSafeAnnotationsFields() throws Exception {
+
+ TEST_OUTPUT_STRING_NULL_SAFE_ANNOTATIONS_TEST.delete();
+ doCompile(new String[] { "-encoding", "UTF-8", "-nullSafeAnnotations", "-string", "schema",
+ TEST_INPUT_DIR.toString() + "/nullsafeannotationsfieldstest.avsc", TEST_OUTPUT_STRING_DIR.getPath() });
+ assertFileMatch(TEST_EXPECTED_STRING_NULL_SAFE_ANNOTATIONS_TEST, TEST_OUTPUT_STRING_NULL_SAFE_ANNOTATIONS_TEST);
+ }
+
@Test
void compileSchemaWithOptionalGettersForAllFields() throws Exception {
diff --git a/pom.xml b/pom.xml
index 6868dacf780..f70a664e9ed 100644
--- a/pom.xml
+++ b/pom.xml
@@ -396,6 +396,7 @@
lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithLogicalTypes.java
lang/java/avro/src/test/java/org/apache/avro/specific/TestRecordWithoutLogicalTypes.java
lang/java/ipc-netty/src/test/resources/**/*.txt
+ lang/java/tools/src/test/compiler/output-string/avro/examples/baseball/NullSafeAnnotationsFieldsTest.java
lang/java/tools/src/test/compiler/output-string/avro/examples/baseball/Player.java
lang/java/tools/src/test/compiler/output-string/avro/examples/baseball/Position.java
lang/java/tools/src/test/compiler/output-string/avro/examples/baseball/FieldTest.java