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

dkulp 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 fa1a8ef  [AVRO-2369] Add a way to specify a null default for unions 
with a null.
fa1a8ef is described below

commit fa1a8ef805111c4bb0d6e5fc59e635cf72d33d14
Author: Daniel Kulp <[email protected]>
AuthorDate: Fri Apr 5 15:04:57 2019 -0400

    [AVRO-2369] Add a way to specify a null default for unions with a null.
---
 .../src/main/java/org/apache/avro/Protocol.java    |   6 +-
 .../avro/src/main/java/org/apache/avro/Schema.java |  48 +++--
 .../main/java/org/apache/avro/SchemaBuilder.java   | 217 +++++++++++----------
 .../org/apache/avro/TestCircularReferences.java    |  10 +-
 .../java/org/apache/avro/TestSchemaBuilder.java    |  19 +-
 .../avro/file/TestSeekableByteArrayInput.java      |   2 +-
 6 files changed, 166 insertions(+), 136 deletions(-)

diff --git a/lang/java/avro/src/main/java/org/apache/avro/Protocol.java 
b/lang/java/avro/src/main/java/org/apache/avro/Protocol.java
index 71f8548..a2f3a7c 100644
--- a/lang/java/avro/src/main/java/org/apache/avro/Protocol.java
+++ b/lang/java/avro/src/main/java/org/apache/avro/Protocol.java
@@ -35,6 +35,7 @@ import java.util.Set;
 import java.util.HashSet;
 
 import org.apache.avro.Schema.Field;
+import org.apache.avro.Schema.Field.Order;
 
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonParser;
@@ -404,7 +405,7 @@ public class Protocol extends JsonProperties {
 
   /**
    * Render this as <a href="http://json.org/";>JSON</a>.
-   * 
+   *
    * @param pretty if true, pretty-print JSON.
    */
   public String toString(boolean pretty) {
@@ -587,7 +588,8 @@ public class Protocol extends JsonProperties {
       JsonNode fieldDocNode = field.get("doc");
       if (fieldDocNode != null)
         fieldDoc = fieldDocNode.textValue();
-      Field newField = new Field(name, Schema.parse(fieldTypeNode, types), 
fieldDoc, field.get("default"));
+      Field newField = new Field(name, Schema.parse(fieldTypeNode, types), 
fieldDoc, field.get("default"), true,
+          Order.ASCENDING);
       Set<String> aliases = Schema.parseAliases(field);
       if (aliases != null) { // add aliases
         for (String alias : aliases)
diff --git a/lang/java/avro/src/main/java/org/apache/avro/Schema.java 
b/lang/java/avro/src/main/java/org/apache/avro/Schema.java
index 374917c..587634d 100644
--- a/lang/java/avro/src/main/java/org/apache/avro/Schema.java
+++ b/lang/java/avro/src/main/java/org/apache/avro/Schema.java
@@ -24,6 +24,8 @@ import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.DoubleNode;
+import com.fasterxml.jackson.databind.node.NullNode;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -448,7 +450,7 @@ public abstract class Schema extends JsonProperties {
 
         @Override
         protected Field createField(String name, Schema schema, String doc, 
JsonNode defaultValue) {
-          return new Field(name, schema, doc, defaultValue);
+          return new Field(name, schema, doc, defaultValue, true, 
Order.ASCENDING);
         }
 
         @Override
@@ -469,6 +471,12 @@ public abstract class Schema extends JsonProperties {
       }
     };
 
+    /**
+     * For Schema unions with a "null" type as the first entry, this can be 
used to
+     * specify that the default for the union is null.
+     */
+    public static final Object NULL_DEFAULT_VALUE = new Object();
+
     private final String name; // name of the field.
     private int position = -1;
     private final Schema schema;
@@ -477,21 +485,13 @@ public abstract class Schema extends JsonProperties {
     private final Order order;
     private Set<String> aliases;
 
-    Field(String name, Schema schema, String doc, JsonNode defaultValue) {
-      this(name, schema, doc, defaultValue, true, Order.ASCENDING);
-    }
-
-    Field(String name, Schema schema, String doc, JsonNode defaultValue, Order 
order) {
-      this(name, schema, doc, defaultValue, true, order);
-    }
-
     Field(String name, Schema schema, String doc, JsonNode defaultValue, 
boolean validateDefault, Order order) {
       super(FIELD_RESERVED);
       this.name = validateName(name);
       this.schema = schema;
       this.doc = doc;
       this.defaultValue = validateDefault ? validateDefault(name, schema, 
defaultValue) : defaultValue;
-      this.order = order;
+      this.order = Objects.requireNonNull(order, "Order cannot be null");
     }
 
     /**
@@ -501,18 +501,34 @@ public abstract class Schema extends JsonProperties {
      * {@code aliases}.
      */
     public Field(Field field, Schema schema) {
-      this(field.name, schema, field.doc, field.defaultValue, field.order);
+      this(field.name, schema, field.doc, field.defaultValue, true, 
field.order);
       putAll(field);
       if (field.aliases != null)
         aliases = new LinkedHashSet<>(field.aliases);
     }
 
     /**
+     *
+     */
+    public Field(String name, Schema schema) {
+      this(name, schema, (String) null, (JsonNode) null, true, 
Order.ASCENDING);
+    }
+
+    /**
+     *
+     */
+    public Field(String name, Schema schema, String doc) {
+      this(name, schema, doc, (JsonNode) null, true, Order.ASCENDING);
+    }
+
+    /**
      * @param defaultValue the default value for this field specified using the
      *                     mapping in {@link JsonProperties}
      */
     public Field(String name, Schema schema, String doc, Object defaultValue) {
-      this(name, schema, doc, defaultValue, Order.ASCENDING);
+      this(name, schema, doc,
+          defaultValue == NULL_DEFAULT_VALUE ? NullNode.getInstance() : 
JacksonUtils.toJsonNode(defaultValue), true,
+          Order.ASCENDING);
     }
 
     /**
@@ -520,7 +536,9 @@ public abstract class Schema extends JsonProperties {
      *                     mapping in {@link JsonProperties}
      */
     public Field(String name, Schema schema, String doc, Object defaultValue, 
Order order) {
-      this(name, schema, doc, JacksonUtils.toJsonNode(defaultValue), order);
+      this(name, schema, doc,
+          defaultValue == NULL_DEFAULT_VALUE ? NullNode.getInstance() : 
JacksonUtils.toJsonNode(defaultValue), true,
+          Objects.requireNonNull(order));
     }
 
     public String name() {
@@ -1595,7 +1613,7 @@ public abstract class Schema extends JsonProperties {
               && (Type.FLOAT.equals(fieldSchema.getType()) || 
Type.DOUBLE.equals(fieldSchema.getType()))
               && defaultValue.isTextual())
             defaultValue = new 
DoubleNode(Double.valueOf(defaultValue.textValue()));
-          Field f = new Field(fieldName, fieldSchema, fieldDoc, defaultValue, 
order);
+          Field f = new Field(fieldName, fieldSchema, fieldDoc, defaultValue, 
true, order);
           Iterator<String> i = field.fieldNames();
           while (i.hasNext()) { // add field props
             String prop = i.next();
@@ -1768,7 +1786,7 @@ public abstract class Schema extends JsonProperties {
       for (Field f : s.getFields()) {
         Schema fSchema = applyAliases(f.schema, seen, aliases, fieldAliases);
         String fName = getFieldAlias(name, f.name, fieldAliases);
-        Field newF = new Field(fName, fSchema, f.doc, f.defaultValue, f.order);
+        Field newF = new Field(fName, fSchema, f.doc, f.defaultValue, true, 
f.order);
         newF.putAll(f); // copy props
         newFields.add(newF);
       }
diff --git a/lang/java/avro/src/main/java/org/apache/avro/SchemaBuilder.java 
b/lang/java/avro/src/main/java/org/apache/avro/SchemaBuilder.java
index bb851d5..20b543b 100644
--- a/lang/java/avro/src/main/java/org/apache/avro/SchemaBuilder.java
+++ b/lang/java/avro/src/main/java/org/apache/avro/SchemaBuilder.java
@@ -36,6 +36,7 @@ import org.apache.avro.util.internal.JacksonUtils;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.NullNode;
 import com.fasterxml.jackson.databind.node.TextNode;
 
 /**
@@ -77,11 +78,11 @@ import com.fasterxml.jackson.databind.node.TextNode;
  * <p/>
  * <h6>Selecting and Building an Avro Type</h6> The API analogy for the right
  * hand side of the Avro Schema JSON
- * 
+ *
  * <pre>
  * "type":
  * </pre>
- * 
+ *
  * is a {@link TypeBuilder}, {@link FieldTypeBuilder}, or
  * {@link UnionFieldTypeBuilder}, depending on the context. These types all
  * share a similar API for selecting and building types.
@@ -89,22 +90,22 @@ import com.fasterxml.jackson.databind.node.TextNode;
  * <h5>Primitive Types</h5> All Avro primitive types are trivial to configure. 
A
  * primitive type in Avro JSON can be declared two ways, one that supports
  * custom properties and one that does not:
- * 
+ *
  * <pre>
  * {"type":"int"}
  * {"type":{"name":"int"}}
  * {"type":{"name":"int", "customProp":"val"}}
  * </pre>
- * 
+ *
  * The analogous code form for the above three JSON lines are the below three
  * lines:
- * 
+ *
  * <pre>
  *  .intType()
  *  .intBuilder().endInt()
  *  .intBuilder().prop("customProp", "val").endInt()
  * </pre>
- * 
+ *
  * Every primitive type has a shortcut to create the trivial type, and a 
builder
  * when custom properties are required. The first line above is a shortcut for
  * the second, analogous to the JSON case.
@@ -123,7 +124,7 @@ import com.fasterxml.jackson.databind.node.TextNode;
  * <li>{@link EnumBuilder#symbols(String...)}</li>
  * <li>{@link RecordBuilder#fields()}</li> Example use of a named type with all
  * optional parameters:
- * 
+ *
  * <pre>
  * .enumeration("Suit").namespace("org.apache.test")
  *   .aliases("org.apache.test.OldSuit")
@@ -131,9 +132,9 @@ import com.fasterxml.jackson.databind.node.TextNode;
  *   .prop("customProp", "val")
  *   .symbols("SPADES", "HEARTS", "DIAMONDS", "CLUBS")
  * </pre>
- * 
+ *
  * Which is equivalent to the JSON:
- * 
+ *
  * <pre>
  * { "type":"enum",
  *   "name":"Suit", "namespace":"org.apache.test",
@@ -143,7 +144,7 @@ import com.fasterxml.jackson.databind.node.TextNode;
  *   "symbols":["SPADES", "HEARTS", "DIAMONDS", "CLUBS"]
  * }
  * </pre>
- * 
+ *
  * <h6>Nested Types</h6> The Avro nested types, map and array, can have custom
  * properties like all avro types, are not named, and must specify a nested
  * type. After configuration of optional properties, an array or map builds or
@@ -170,16 +171,16 @@ import com.fasterxml.jackson.databind.node.TextNode;
  * <h6>Unions</h6> Union types are built via {@link TypeBuilder#unionOf()} or
  * {@link FieldTypeBuilder#unionOf()} in the context of type selection. This
  * chains together multiple types, in union order. For example:
- * 
+ *
  * <pre>
  * .unionOf()
  *   .fixed("IPv4").size(4).and()
  *   .fixed("IPv6").size(16).and()
  *   .nullType().endUnion()
  * </pre>
- * 
+ *
  * is equivalent to the Avro schema JSON:
- * 
+ *
  * <pre>
  * [
  *   {"type":"fixed", "name":"IPv4", "size":4},
@@ -187,21 +188,21 @@ import com.fasterxml.jackson.databind.node.TextNode;
  *   "null"
  * ]
  * </pre>
- * 
+ *
  * In a field context, the first type of a union defines what default type is
  * allowed.
  * </p>
  * Unions have two shortcuts for common cases. nullable() creates a union of a
  * type and null. In a field type context, optional() is available and creates 
a
  * union of null and a type, with a null default. The below two are equivalent:
- * 
+ *
  * <pre>
  *   .unionOf().intType().and().nullType().endUnion()
  *   .nullable().intType()
  * </pre>
- * 
+ *
  * The below two field declarations are equivalent:
- * 
+ *
  * <pre>
  *   
.name("f").type().unionOf().nullType().and().longType().endUnion().nullDefault()
  *   .name("f").type().optional().longType()
@@ -209,13 +210,13 @@ import com.fasterxml.jackson.databind.node.TextNode;
  *
  * <h6>Explicit Types and Types by Name</h6> Types can also be specified
  * explicitly by passing in a Schema, or by name:
- * 
+ *
  * <pre>
  *   .type(Schema.create(Schema.Type.INT)) // explicitly specified
  *   .type("MD5")                       // reference by full name or short name
  *   .type("MD5", "org.apache.avro.test")  // reference by name and namespace
  * </pre>
- * 
+ *
  * When a type is specified by name, and the namespace is absent or null, the
  * namespace is inherited from the enclosing context. A namespace will 
propagate
  * as a default to child fields, nested types, or later defined types in a
@@ -249,11 +250,11 @@ public class SchemaBuilder {
   /**
    * Create a builder for an Avro record with the specified name. This is
    * equivalent to:
-   * 
+   *
    * <pre>
    * builder().record(name);
    * </pre>
-   * 
+   *
    * @param name the record name
    */
   public static RecordBuilder<Schema> record(String name) {
@@ -263,11 +264,11 @@ public class SchemaBuilder {
   /**
    * Create a builder for an Avro enum with the specified name and symbols
    * (values). This is equivalent to:
-   * 
+   *
    * <pre>
    * builder().enumeration(name);
    * </pre>
-   * 
+   *
    * @param name the enum name
    */
   public static EnumBuilder<Schema> enumeration(String name) {
@@ -277,11 +278,11 @@ public class SchemaBuilder {
   /**
    * Create a builder for an Avro fixed type with the specified name and size.
    * This is equivalent to:
-   * 
+   *
    * <pre>
    * builder().fixed(name);
    * </pre>
-   * 
+   *
    * @param name the fixed name
    */
   public static FixedBuilder<Schema> fixed(String name) {
@@ -290,7 +291,7 @@ public class SchemaBuilder {
 
   /**
    * Create a builder for an Avro array This is equivalent to:
-   * 
+   *
    * <pre>
    * builder().array();
    * </pre>
@@ -301,7 +302,7 @@ public class SchemaBuilder {
 
   /**
    * Create a builder for an Avro map This is equivalent to:
-   * 
+   *
    * <pre>
    * builder().map();
    * </pre>
@@ -312,7 +313,7 @@ public class SchemaBuilder {
 
   /**
    * Create a builder for an Avro union This is equivalent to:
-   * 
+   *
    * <pre>
    * builder().unionOf();
    * </pre>
@@ -323,17 +324,17 @@ public class SchemaBuilder {
 
   /**
    * Create a builder for a union of a type and null. This is a shortcut for:
-   * 
+   *
    * <pre>
    * builder().nullable();
    * </pre>
-   * 
+   *
    * and the following two lines are equivalent:
-   * 
+   *
    * <pre>
    * nullable().intType();
    * </pre>
-   * 
+   *
    * <pre>
    * unionOf().intType().and().nullType().endUnion();
    * </pre>
@@ -1015,7 +1016,7 @@ public class SchemaBuilder {
 
     /**
      * A plain boolean type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * booleanBuilder().endBoolean();
      * </pre>
@@ -1034,7 +1035,7 @@ public class SchemaBuilder {
 
     /**
      * A plain int type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * intBuilder().endInt();
      * </pre>
@@ -1053,7 +1054,7 @@ public class SchemaBuilder {
 
     /**
      * A plain long type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * longBuilder().endLong();
      * </pre>
@@ -1072,7 +1073,7 @@ public class SchemaBuilder {
 
     /**
      * A plain float type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * floatBuilder().endFloat();
      * </pre>
@@ -1091,7 +1092,7 @@ public class SchemaBuilder {
 
     /**
      * A plain double type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * doubleBuilder().endDouble();
      * </pre>
@@ -1110,7 +1111,7 @@ public class SchemaBuilder {
 
     /**
      * A plain string type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * stringBuilder().endString();
      * </pre>
@@ -1129,7 +1130,7 @@ public class SchemaBuilder {
 
     /**
      * A plain bytes type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * bytesBuilder().endBytes();
      * </pre>
@@ -1148,7 +1149,7 @@ public class SchemaBuilder {
 
     /**
      * A plain null type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * nullBuilder().endNull();
      * </pre>
@@ -1167,13 +1168,13 @@ public class SchemaBuilder {
 
     /**
      * Build an Avro map type Example usage:
-     * 
+     *
      * <pre>
      * map().values().intType()
      * </pre>
-     * 
+     *
      * Equivalent to Avro JSON Schema:
-     * 
+     *
      * <pre>
      * {"type":"map", "values":"int"}
      * </pre>
@@ -1184,13 +1185,13 @@ public class SchemaBuilder {
 
     /**
      * Build an Avro array type Example usage:
-     * 
+     *
      * <pre>
      * array().items().longType()
      * </pre>
-     * 
+     *
      * Equivalent to Avro JSON Schema:
-     * 
+     *
      * <pre>
      * {"type":"array", "values":"long"}
      * </pre>
@@ -1201,13 +1202,13 @@ public class SchemaBuilder {
 
     /**
      * Build an Avro fixed type. Example usage:
-     * 
+     *
      * <pre>
      * fixed("com.foo.IPv4").size(4)
      * </pre>
-     * 
+     *
      * Equivalent to Avro JSON Schema:
-     * 
+     *
      * <pre>
      * {"type":"fixed", "name":"com.foo.IPv4", "size":4}
      * </pre>
@@ -1218,14 +1219,14 @@ public class SchemaBuilder {
 
     /**
      * Build an Avro enum type. Example usage:
-     * 
+     *
      * <pre>
      * enumeration("Suits").namespace("org.cards").doc("card suit 
names").defaultSymbol("HEART").symbols("HEART", "SPADE",
      *     "DIAMOND", "CLUB")
      * </pre>
-     * 
+     *
      * Equivalent to Avro JSON Schema:
-     * 
+     *
      * <pre>
      * {"type":"enum", "name":"Suits", "namespace":"org.cards",
      *  "doc":"card suit names", "symbols":[
@@ -1238,14 +1239,14 @@ public class SchemaBuilder {
 
     /**
      * Build an Avro record type. Example usage:
-     * 
+     *
      * <pre>
      * 
record("com.foo.Foo").fields().name("field1").typeInt().intDefault(1).name("field2").typeString().noDefault()
      *     .name("field3").optional().typeFixed("FooFixed").size(4).endRecord()
      * </pre>
-     * 
+     *
      * Equivalent to Avro JSON Schema:
-     * 
+     *
      * <pre>
      * {"type":"record", "name":"com.foo.Foo", "fields": [
      *   {"name":"field1", "type":"int", "default":1},
@@ -1262,7 +1263,7 @@ public class SchemaBuilder {
 
     /**
      * Build an Avro union schema type. Example usage:
-     * 
+     *
      * <pre>
      * unionOf().stringType().and().bytesType().endUnion()
      * </pre>
@@ -1275,11 +1276,11 @@ public class SchemaBuilder {
      * A shortcut for building a union of a type and null.
      * <p/>
      * For example, the code snippets below are equivalent:
-     * 
+     *
      * <pre>
      * nullable().booleanType()
      * </pre>
-     * 
+     *
      * <pre>
      * unionOf().booleanType().and().nullType().endUnion()
      * </pre>
@@ -1334,13 +1335,13 @@ public class SchemaBuilder {
    * configuring a default for the field.
    * <p/>
    * For example, an int field with default value 1:
-   * 
+   *
    * <pre>
    * intSimple().withDefault(1);
    * </pre>
-   * 
+   *
    * or an array with items that are optional int types:
-   * 
+   *
    * <pre>
    * array().items().optional().intType();
    * </pre>
@@ -1358,7 +1359,7 @@ public class SchemaBuilder {
 
     /**
      * A plain boolean type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * booleanBuilder().endBoolean();
      * </pre>
@@ -1377,7 +1378,7 @@ public class SchemaBuilder {
 
     /**
      * A plain int type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * intBuilder().endInt();
      * </pre>
@@ -1396,7 +1397,7 @@ public class SchemaBuilder {
 
     /**
      * A plain long type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * longBuilder().endLong();
      * </pre>
@@ -1415,7 +1416,7 @@ public class SchemaBuilder {
 
     /**
      * A plain float type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * floatBuilder().endFloat();
      * </pre>
@@ -1434,7 +1435,7 @@ public class SchemaBuilder {
 
     /**
      * A plain double type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * doubleBuilder().endDouble();
      * </pre>
@@ -1453,7 +1454,7 @@ public class SchemaBuilder {
 
     /**
      * A plain string type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * stringBuilder().endString();
      * </pre>
@@ -1472,7 +1473,7 @@ public class SchemaBuilder {
 
     /**
      * A plain bytes type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * bytesBuilder().endBytes();
      * </pre>
@@ -1491,7 +1492,7 @@ public class SchemaBuilder {
 
     /**
      * A plain null type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * nullBuilder().endNull();
      * </pre>
@@ -1560,11 +1561,11 @@ public class SchemaBuilder {
      * value of the non-null type.
      * <p/>
      * For example, the two code snippets below are equivalent:
-     * 
+     *
      * <pre>
      * nullable().booleanType().booleanDefault(true)
      * </pre>
-     * 
+     *
      * <pre>
      * unionOf().booleanType().and().nullType().endUnion().booleanDefault(true)
      * </pre>
@@ -1577,11 +1578,11 @@ public class SchemaBuilder {
      * A shortcut for building a union of null and a type, with a null default.
      * <p/>
      * For example, the two code snippets below are equivalent:
-     * 
+     *
      * <pre>
      * optional().booleanType()
      * </pre>
-     * 
+     *
      * <pre>
      * unionOf().nullType().and().booleanType().endUnion().nullDefault()
      * </pre>
@@ -1606,7 +1607,7 @@ public class SchemaBuilder {
 
     /**
      * A plain boolean type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * booleanBuilder().endBoolean();
      * </pre>
@@ -1625,7 +1626,7 @@ public class SchemaBuilder {
 
     /**
      * A plain int type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * intBuilder().endInt();
      * </pre>
@@ -1644,7 +1645,7 @@ public class SchemaBuilder {
 
     /**
      * A plain long type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * longBuilder().endLong();
      * </pre>
@@ -1663,7 +1664,7 @@ public class SchemaBuilder {
 
     /**
      * A plain float type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * floatBuilder().endFloat();
      * </pre>
@@ -1682,7 +1683,7 @@ public class SchemaBuilder {
 
     /**
      * A plain double type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * doubleBuilder().endDouble();
      * </pre>
@@ -1701,7 +1702,7 @@ public class SchemaBuilder {
 
     /**
      * A plain string type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * stringBuilder().endString();
      * </pre>
@@ -1720,7 +1721,7 @@ public class SchemaBuilder {
 
     /**
      * A plain bytes type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * bytesBuilder().endBytes();
      * </pre>
@@ -1739,7 +1740,7 @@ public class SchemaBuilder {
 
     /**
      * A plain null type without custom properties. This is equivalent to:
-     * 
+     *
      * <pre>
      * nullBuilder().endNull();
      * </pre>
@@ -1822,7 +1823,7 @@ public class SchemaBuilder {
 
     /**
      * Add a field with the given name.
-     * 
+     *
      * @return A {@link FieldBuilder} for the given name.
      */
     public FieldBuilder<R> name(String fieldName) {
@@ -1833,7 +1834,7 @@ public class SchemaBuilder {
      * Shortcut for creating a boolean field with the given name and no 
default.
      * <p/>
      * This is equivalent to:
-     * 
+     *
      * <pre>
      * name(fieldName).type().booleanType().noDefault()
      * </pre>
@@ -1847,7 +1848,7 @@ public class SchemaBuilder {
      * with null default.
      * <p/>
      * This is equivalent to:
-     * 
+     *
      * <pre>
      * name(fieldName).type().optional().booleanType()
      * </pre>
@@ -1874,7 +1875,7 @@ public class SchemaBuilder {
      * Shortcut for creating an int field with the given name and no default.
      * <p/>
      * This is equivalent to:
-     * 
+     *
      * <pre>
      * name(fieldName).type().intType().noDefault()
      * </pre>
@@ -1888,7 +1889,7 @@ public class SchemaBuilder {
      * null default.
      * <p/>
      * This is equivalent to:
-     * 
+     *
      * <pre>
      * name(fieldName).type().optional().intType()
      * </pre>
@@ -1902,7 +1903,7 @@ public class SchemaBuilder {
      * int default.
      * <p/>
      * This is equivalent to:
-     * 
+     *
      * <pre>
      * name(fieldName).type().nullable().intType().intDefault(defaultVal)
      * </pre>
@@ -1915,7 +1916,7 @@ public class SchemaBuilder {
      * Shortcut for creating a long field with the given name and no default.
      * <p/>
      * This is equivalent to:
-     * 
+     *
      * <pre>
      * name(fieldName).type().longType().noDefault()
      * </pre>
@@ -1929,7 +1930,7 @@ public class SchemaBuilder {
      * null default.
      * <p/>
      * This is equivalent to:
-     * 
+     *
      * <pre>
      * name(fieldName).type().optional().longType()
      * </pre>
@@ -1943,7 +1944,7 @@ public class SchemaBuilder {
      * long default.
      * <p/>
      * This is equivalent to:
-     * 
+     *
      * <pre>
      * name(fieldName).type().nullable().longType().longDefault(defaultVal)
      * </pre>
@@ -1956,7 +1957,7 @@ public class SchemaBuilder {
      * Shortcut for creating a float field with the given name and no default.
      * <p/>
      * This is equivalent to:
-     * 
+     *
      * <pre>
      * name(fieldName).type().floatType().noDefault()
      * </pre>
@@ -1970,7 +1971,7 @@ public class SchemaBuilder {
      * null default.
      * <p/>
      * This is equivalent to:
-     * 
+     *
      * <pre>
      * name(fieldName).type().optional().floatType()
      * </pre>
@@ -1984,7 +1985,7 @@ public class SchemaBuilder {
      * a float default.
      * <p/>
      * This is equivalent to:
-     * 
+     *
      * <pre>
      * name(fieldName).type().nullable().floatType().floatDefault(defaultVal)
      * </pre>
@@ -1997,7 +1998,7 @@ public class SchemaBuilder {
      * Shortcut for creating a double field with the given name and no default.
      * <p/>
      * This is equivalent to:
-     * 
+     *
      * <pre>
      * name(fieldName).type().doubleType().noDefault()
      * </pre>
@@ -2011,7 +2012,7 @@ public class SchemaBuilder {
      * with null default.
      * <p/>
      * This is equivalent to:
-     * 
+     *
      * <pre>
      * name(fieldName).type().optional().doubleType()
      * </pre>
@@ -2025,7 +2026,7 @@ public class SchemaBuilder {
      * with a double default.
      * <p/>
      * This is equivalent to:
-     * 
+     *
      * <pre>
      * name(fieldName).type().nullable().doubleType().doubleDefault(defaultVal)
      * </pre>
@@ -2038,7 +2039,7 @@ public class SchemaBuilder {
      * Shortcut for creating a string field with the given name and no default.
      * <p/>
      * This is equivalent to:
-     * 
+     *
      * <pre>
      * name(fieldName).type().stringType().noDefault()
      * </pre>
@@ -2052,7 +2053,7 @@ public class SchemaBuilder {
      * with null default.
      * <p/>
      * This is equivalent to:
-     * 
+     *
      * <pre>
      * name(fieldName).type().optional().stringType()
      * </pre>
@@ -2066,7 +2067,7 @@ public class SchemaBuilder {
      * with a string default.
      * <p/>
      * This is equivalent to:
-     * 
+     *
      * <pre>
      * name(fieldName).type().nullable().stringType().stringDefault(defaultVal)
      * </pre>
@@ -2079,7 +2080,7 @@ public class SchemaBuilder {
      * Shortcut for creating a bytes field with the given name and no default.
      * <p/>
      * This is equivalent to:
-     * 
+     *
      * <pre>
      * name(fieldName).type().bytesType().noDefault()
      * </pre>
@@ -2093,7 +2094,7 @@ public class SchemaBuilder {
      * null default.
      * <p/>
      * This is equivalent to:
-     * 
+     *
      * <pre>
      * name(fieldName).type().optional().bytesType()
      * </pre>
@@ -2107,7 +2108,7 @@ public class SchemaBuilder {
      * a bytes default.
      * <p/>
      * This is equivalent to:
-     * 
+     *
      * <pre>
      * name(fieldName).type().nullable().bytesType().bytesDefault(defaultVal)
      * </pre>
@@ -2137,11 +2138,11 @@ public class SchemaBuilder {
    *
    * Usage is to first configure any of the optional parameters and then to 
call
    * one of the type methods to complete the field. For example
-   * 
+   *
    * <pre>
    *   .namespace("org.apache.example").orderDescending().type()
    * </pre>
-   * 
+   *
    * Optional parameters for a field are namespace, doc, order, and aliases.
    */
   public final static class FieldBuilder<R> extends 
NamedBuilder<FieldBuilder<R>> {
@@ -2174,7 +2175,7 @@ public class SchemaBuilder {
     /**
      * Final step in configuring this field, finalizing name, namespace, 
alias, and
      * order.
-     * 
+     *
      * @return A builder for the field's type and default value.
      */
     public FieldTypeBuilder<R> type() {
@@ -2225,16 +2226,16 @@ public class SchemaBuilder {
     }
 
     private FieldAssembler<R> completeField(Schema schema, Object defaultVal) {
-      JsonNode defaultNode = toJsonNode(defaultVal);
+      JsonNode defaultNode = defaultVal == null ? NullNode.getInstance() : 
toJsonNode(defaultVal);
       return completeField(schema, defaultNode);
     }
 
     private FieldAssembler<R> completeField(Schema schema) {
-      return completeField(schema, null);
+      return completeField(schema, (JsonNode) null);
     }
 
     private FieldAssembler<R> completeField(Schema schema, JsonNode 
defaultVal) {
-      Field field = new Field(name(), schema, doc(), defaultVal, order);
+      Field field = new Field(name(), schema, doc(), defaultVal, true, order);
       addPropsTo(field);
       addAliasesTo(field);
       return fields.addField(field);
diff --git 
a/lang/java/avro/src/test/java/org/apache/avro/TestCircularReferences.java 
b/lang/java/avro/src/test/java/org/apache/avro/TestCircularReferences.java
index e5ef87a..dbf5d39 100644
--- a/lang/java/avro/src/test/java/org/apache/avro/TestCircularReferences.java
+++ b/lang/java/avro/src/test/java/org/apache/avro/TestCircularReferences.java
@@ -289,14 +289,14 @@ public class TestCircularReferences {
     Reference parentRef = new Reference("parent");
 
     List<Schema.Field> childFields = new ArrayList<>();
-    childFields.add(new Schema.Field("c", Schema.create(Schema.Type.STRING), 
null, null));
-    childFields.add(new Schema.Field("parent", parentRefSchema, null, null));
+    childFields.add(new Schema.Field("c", Schema.create(Schema.Type.STRING)));
+    childFields.add(new Schema.Field("parent", parentRefSchema));
     Schema childSchema = parentRef.addToSchema(Schema.createRecord("Child", 
null, null, false, childFields));
 
     List<Schema.Field> parentFields = new ArrayList<>();
-    parentFields.add(new Schema.Field("id", Schema.create(Schema.Type.LONG), 
null, null));
-    parentFields.add(new Schema.Field("p", Schema.create(Schema.Type.STRING), 
null, null));
-    parentFields.add(new Schema.Field("child", childSchema, null, null));
+    parentFields.add(new Schema.Field("id", Schema.create(Schema.Type.LONG)));
+    parentFields.add(new Schema.Field("p", Schema.create(Schema.Type.STRING)));
+    parentFields.add(new Schema.Field("child", childSchema));
     parentSchema.setFields(parentFields);
     Referenceable idRef = new Referenceable("id");
 
diff --git 
a/lang/java/avro/src/test/java/org/apache/avro/TestSchemaBuilder.java 
b/lang/java/avro/src/test/java/org/apache/avro/TestSchemaBuilder.java
index a969466..f440087 100644
--- a/lang/java/avro/src/test/java/org/apache/avro/TestSchemaBuilder.java
+++ b/lang/java/avro/src/test/java/org/apache/avro/TestSchemaBuilder.java
@@ -44,23 +44,32 @@ public class TestSchemaBuilder {
   public void testRecord() {
     Schema schema = 
SchemaBuilder.record("myrecord").namespace("org.example").aliases("oldrecord").fields().name("f0")
         
.aliases("f0alias").type().stringType().noDefault().name("f1").doc("This is 
f1").type().longType().noDefault()
-        
.name("f2").type().nullable().booleanType().booleanDefault(true).endRecord();
+        
.name("f2").type().nullable().booleanType().booleanDefault(true).name("f3").type().unionOf().nullType().and()
+        .booleanType().endUnion().nullDefault().endRecord();
 
     Assert.assertEquals("myrecord", schema.getName());
     Assert.assertEquals("org.example", schema.getNamespace());
     Assert.assertEquals("org.example.oldrecord", 
schema.getAliases().iterator().next());
     Assert.assertFalse(schema.isError());
     List<Schema.Field> fields = schema.getFields();
-    Assert.assertEquals(3, fields.size());
-    Assert.assertEquals(new Schema.Field("f0", 
Schema.create(Schema.Type.STRING), null, null), fields.get(0));
+    Assert.assertEquals(4, fields.size());
+    Assert.assertEquals(new Schema.Field("f0", 
Schema.create(Schema.Type.STRING)), fields.get(0));
     Assert.assertTrue(fields.get(0).aliases().contains("f0alias"));
-    Assert.assertEquals(new Schema.Field("f1", 
Schema.create(Schema.Type.LONG), "This is f1", null), fields.get(1));
+    Assert.assertEquals(new Schema.Field("f1", 
Schema.create(Schema.Type.LONG), "This is f1"), fields.get(1));
 
     List<Schema> types = new ArrayList<>();
     types.add(Schema.create(Schema.Type.BOOLEAN));
     types.add(Schema.create(Schema.Type.NULL));
     Schema optional = Schema.createUnion(types);
     Assert.assertEquals(new Schema.Field("f2", optional, null, true), 
fields.get(2));
+
+    List<Schema> types2 = new ArrayList<>();
+    types2.add(Schema.create(Schema.Type.NULL));
+    types2.add(Schema.create(Schema.Type.BOOLEAN));
+    Schema optional2 = Schema.createUnion(types2);
+
+    Assert.assertNotEquals(new Schema.Field("f3", optional2, null, (Object) 
null), fields.get(3));
+    Assert.assertEquals(new Schema.Field("f3", optional2, null, 
Schema.Field.NULL_DEFAULT_VALUE), fields.get(3));
   }
 
   @Test
@@ -426,7 +435,7 @@ public class TestSchemaBuilder {
     Assert.assertEquals("LongList", schema.getName());
     List<Schema.Field> fields = schema.getFields();
     Assert.assertEquals(2, fields.size());
-    Assert.assertEquals(new Schema.Field("value", 
Schema.create(Schema.Type.LONG), null, null), fields.get(0));
+    Assert.assertEquals(new Schema.Field("value", 
Schema.create(Schema.Type.LONG), null), fields.get(0));
 
     Assert.assertEquals(Schema.Type.UNION, fields.get(1).schema().getType());
 
diff --git 
a/lang/java/avro/src/test/java/org/apache/avro/file/TestSeekableByteArrayInput.java
 
b/lang/java/avro/src/test/java/org/apache/avro/file/TestSeekableByteArrayInput.java
index 327d66d..8ec113c 100644
--- 
a/lang/java/avro/src/test/java/org/apache/avro/file/TestSeekableByteArrayInput.java
+++ 
b/lang/java/avro/src/test/java/org/apache/avro/file/TestSeekableByteArrayInput.java
@@ -47,7 +47,7 @@ public class TestSeekableByteArrayInput {
   private Schema getTestSchema() throws Exception {
     Schema schema = Schema.createRecord("TestRecord", "this is a test record", 
"org.apache.avro.file", false);
     List<Field> fields = new ArrayList<>();
-    fields.add(new Field("name", Schema.create(Type.STRING), "this is a test 
field", null));
+    fields.add(new Field("name", Schema.create(Type.STRING), "this is a test 
field"));
     schema.setFields(fields);
     return schema;
   }

Reply via email to