parquet-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From b...@apache.org
Subject parquet-mr git commit: PARQUET-223: Add builders for MAP and LIST types
Date Tue, 26 May 2015 21:32:01 GMT
Repository: parquet-mr
Updated Branches:
  refs/heads/master 8769d0f2c -> dd92a9db6


PARQUET-223: Add builders for MAP and LIST types

As of now, Parquet does not provide builders for Maps and Lists. This leaves margin for user errors. Having Map and List builders will make it easier for users to build these types.

Author: asingh <asingh@cloudera.com>

Closes #148 from SinghAsDev/map and squashes the following commits:

cc7da06 [asingh] Pull changes made by Ryan
825b5b8 [asingh] Remove non-functional changes
bec675b [asingh] Remove required and optional version of methods that take pre-built Type
6dcaa78 [asingh] Address review comments and some clean up
544d1e4 [asingh] Add key(Type) and value(Type) variants to MapBuilder
f2a1697 [asingh] Add listKey support
68c06f5 [asingh] Add support for null value in MapBuilder
f31f2b0 [asingh] Add more tests to cover list and map value types in map builder
f035439 [asingh] Add Map and List value types to map
1afa2c7 [asingh] Address review comments
484495b [asingh] PARQUET-223: Add builders for MAP and LIST types


Project: http://git-wip-us.apache.org/repos/asf/parquet-mr/repo
Commit: http://git-wip-us.apache.org/repos/asf/parquet-mr/commit/dd92a9db
Tree: http://git-wip-us.apache.org/repos/asf/parquet-mr/tree/dd92a9db
Diff: http://git-wip-us.apache.org/repos/asf/parquet-mr/diff/dd92a9db

Branch: refs/heads/master
Commit: dd92a9db6b288def8159f30336f6793239882c9d
Parents: 8769d0f
Author: asingh <asingh@cloudera.com>
Authored: Tue May 26 14:31:51 2015 -0700
Committer: Ryan Blue <blue@apache.org>
Committed: Tue May 26 14:31:51 2015 -0700

----------------------------------------------------------------------
 .../java/org/apache/parquet/schema/Types.java   | 878 +++++++++++++++++--
 .../apache/parquet/schema/TestTypeBuilders.java | 769 ++++++++++++++++
 2 files changed, 1558 insertions(+), 89 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/parquet-mr/blob/dd92a9db/parquet-column/src/main/java/org/apache/parquet/schema/Types.java
----------------------------------------------------------------------
diff --git a/parquet-column/src/main/java/org/apache/parquet/schema/Types.java b/parquet-column/src/main/java/org/apache/parquet/schema/Types.java
index b06c2bc..65e1ef0 100644
--- a/parquet-column/src/main/java/org/apache/parquet/schema/Types.java
+++ b/parquet-column/src/main/java/org/apache/parquet/schema/Types.java
@@ -19,6 +19,7 @@
 package org.apache.parquet.schema;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 import org.apache.parquet.Preconditions;
@@ -89,6 +90,64 @@ import org.apache.parquet.schema.Type.ID;
  *        .named("User")
  * </pre>
  * <p>
+ * Maps are built similarly, using {@code requiredMap()} (or the optionalMap()
+ * version) to return a map builder. Map builders provide {@code key} to add
+ * a primitive as key or a {@code groupKey} to add a group as key. {@code key()}
+ * returns a MapKey builder, which extends a primitive builder. On the other hand,
+ * {@code groupKey()} returns a MapGroupKey builder, which extends a group builder.
+ * A key in a map is always required.
+ * <p>
+ * Once a key is built, a primitive map value can be built using {@code requiredValue()}
+ * (or the optionalValue() version) that returns MapValue builder. A group map value
+ * can be built using {@code requiredGroupValue()} (or the optionalGroupValue()
+ * version) that returns MapGroupValue builder.
+ *
+ *   // required group zipMap (MAP) {
+ *   //   repeated group map (MAP_KEY_VALUE) {
+ *   //     required float key
+ *   //     optional int32 value
+ *   //   }
+ *   // }
+ *   Types.requiredMap()
+ *            .key(FLOAT)
+ *            .optionalValue(INT32)
+ *        .named("zipMap")
+ *
+ *
+ *   // required group zipMap (MAP) {
+ *   //   repeated group map (MAP_KEY_VALUE) {
+ *   //     required group key {
+ *   //       optional int64 first;
+ *   //       required group second {
+ *   //         required float inner_id_1;
+ *   //         optional int32 inner_id_2;
+ *   //       }
+ *   //     }
+ *   //     optional group value {
+ *   //       optional group localGeoInfo {
+ *   //         required float inner_value_1;
+ *   //         optional int32 inner_value_2;
+ *   //       }
+ *   //       optional int32 zipcode;
+ *   //     }
+ *   //   }
+ *   // }
+ *   Types.requiredMap()
+ *            .groupKey()
+ *              .optional(INT64).named("id")
+ *              .requiredGroup()
+ *                .required(FLOAT).named("inner_id_1")
+ *                .required(FLOAT).named("inner_id_2")
+ *              .named("second")
+ *            .optionalGroup()
+ *              .optionalGroup()
+ *                .required(FLOAT).named("inner_value_1")
+ *                .optional(INT32).named("inner_value_2")
+ *              .named("localGeoInfo")
+ *              .optional(INT32).named("zipcode")
+ *        .named("zipMap")
+ * </pre>
+ * <p>
  * Message types are built using {@link #buildMessage()} and function just like
  * group builders.
  * <pre>
@@ -131,7 +190,7 @@ public class Types {
    * @param <P> The type that this builder will return from
    *          {@link #named(String)} when the type is built.
    */
-  public abstract static class Builder<T extends Builder, P> {
+  public abstract static class Builder<THIS extends Builder, P> {
     protected final P parent;
     protected final Class<? extends P> returnClass;
 
@@ -148,7 +207,6 @@ public class Types {
      * @param parent a non-null object to return from {@link #named(String)}
      */
     protected Builder(P parent) {
-      Preconditions.checkNotNull(parent, "Parent cannot be null");
       this.parent = parent;
       this.returnClass = null;
     }
@@ -167,9 +225,9 @@ public class Types {
       this.parent = null;
     }
 
-    protected abstract T self();
+    protected abstract THIS self();
 
-    protected final T repetition(Type.Repetition repetition) {
+    protected final THIS repetition(Type.Repetition repetition) {
       Preconditions.checkArgument(!repetitionAlreadySet,
           "Repetition has already been set");
       Preconditions.checkNotNull(repetition, "Repetition cannot be null");
@@ -190,7 +248,7 @@ public class Types {
      * @param type an {@code OriginalType}
      * @return this builder for method chaining
      */
-    public T as(OriginalType type) {
+    public THIS as(OriginalType type) {
       this.originalType = type;
       return self();
     }
@@ -203,7 +261,7 @@ public class Types {
      * @param id the id of the field
      * @return this builder for method chaining
      */
-    public T id(int id) {
+    public THIS id(int id) {
       this.id = new ID(id);
       return self();
     }
@@ -227,27 +285,26 @@ public class Types {
 
       Type type = build(name);
       if (parent != null) {
-        // if the parent is a GroupBuilder, add type to it
-        if (GroupBuilder.class.isAssignableFrom(parent.getClass())) {
-          GroupBuilder.class.cast(parent).addField(type);
+        // if the parent is a BaseGroupBuilder, add type to it
+        if (BaseGroupBuilder.class.isAssignableFrom(parent.getClass())) {
+          BaseGroupBuilder.class.cast(parent).addField(type);
         }
         return parent;
-      } else {
+      } else if (returnClass != null) {
         // no parent indicates that the Type object should be returned
         // the constructor check guarantees that returnClass is a Type
         return returnClass.cast(type);
+      } else {
+        throw new IllegalStateException(
+            "[BUG] Parent and return type are null: must override named");
       }
     }
 
   }
 
-  /**
-   * A builder for {@link PrimitiveType} objects.
-   *
-   * @param <P> The type that this builder will return from
-   *          {@link #named(String)} when the type is built.
-   */
-  public static class PrimitiveBuilder<P> extends Builder<PrimitiveBuilder<P>, P> {
+  public abstract static class
+      BasePrimitiveBuilder<P, THIS extends BasePrimitiveBuilder<P, THIS>>
+      extends Builder<THIS, P> {
     private static final long MAX_PRECISION_INT32 = maxPrecision(4);
     private static final long MAX_PRECISION_INT64 = maxPrecision(8);
     private final PrimitiveTypeName primitiveType;
@@ -255,20 +312,18 @@ public class Types {
     private int precision = NOT_SET;
     private int scale = NOT_SET;
 
-    private PrimitiveBuilder(P parent, PrimitiveTypeName type) {
+    private BasePrimitiveBuilder(P parent, PrimitiveTypeName type) {
       super(parent);
       this.primitiveType = type;
     }
 
-    private PrimitiveBuilder(Class<P> returnType, PrimitiveTypeName type) {
+    private BasePrimitiveBuilder(Class<P> returnType, PrimitiveTypeName type) {
       super(returnType);
       this.primitiveType = type;
     }
 
     @Override
-    protected PrimitiveBuilder<P> self() {
-      return this;
-    }
+    protected abstract THIS self();
 
     /**
      * Adds the length for a FIXED_LEN_BYTE_ARRAY.
@@ -276,7 +331,7 @@ public class Types {
      * @param length an int length
      * @return this builder for method chaining
      */
-    public PrimitiveBuilder<P> length(int length) {
+    public BasePrimitiveBuilder<P, THIS> length(int length) {
       this.length = length;
       return this;
     }
@@ -291,7 +346,7 @@ public class Types {
      * @param precision an int precision value for the DECIMAL
      * @return this builder for method chaining
      */
-    public PrimitiveBuilder<P> precision(int precision) {
+    public BasePrimitiveBuilder<P, THIS> precision(int precision) {
       this.precision = precision;
       return this;
     }
@@ -309,7 +364,7 @@ public class Types {
      * @param scale an int scale value for the DECIMAL
      * @return this builder for method chaining
      */
-    public PrimitiveBuilder<P> scale(int scale) {
+    public BasePrimitiveBuilder<P, THIS> scale(int scale) {
       this.scale = scale;
       return this;
     }
@@ -418,32 +473,47 @@ public class Types {
   }
 
   /**
-   * A builder for {@link GroupType} objects.
+   * A builder for {@link PrimitiveType} objects.
    *
    * @param <P> The type that this builder will return from
    *          {@link #named(String)} when the type is built.
    */
-  public static class GroupBuilder<P> extends Builder<GroupBuilder<P>, P> {
+  public static class PrimitiveBuilder<P> extends BasePrimitiveBuilder<P, PrimitiveBuilder<P>> {
+
+    private PrimitiveBuilder(P parent, PrimitiveTypeName type) {
+      super(parent, type);
+    }
+
+    private PrimitiveBuilder(Class<P> returnType, PrimitiveTypeName type) {
+      super(returnType, type);
+    }
+
+    @Override
+    protected PrimitiveBuilder<P> self() {
+      return this;
+    }
+  }
+
+  public abstract static class BaseGroupBuilder<P, THIS extends BaseGroupBuilder<P, THIS>>
+      extends Builder<THIS, P> {
     protected final List<Type> fields;
 
-    private GroupBuilder(P parent) {
+    private BaseGroupBuilder(P parent) {
       super(parent);
       this.fields = new ArrayList<Type>();
     }
 
-    private GroupBuilder(Class<P> returnType) {
+    private BaseGroupBuilder(Class<P> returnType) {
       super(returnType);
       this.fields = new ArrayList<Type>();
     }
 
     @Override
-    protected GroupBuilder<P> self() {
-      return this;
-    }
+    protected abstract THIS self();
 
-    public PrimitiveBuilder<GroupBuilder<P>> primitive(
+    public PrimitiveBuilder<THIS> primitive(
         PrimitiveTypeName type, Type.Repetition repetition) {
-      return new PrimitiveBuilder<GroupBuilder<P>>(this, type)
+      return new PrimitiveBuilder<THIS> (self(), type)
           .repetition(repetition);
     }
 
@@ -455,9 +525,9 @@ public class Types {
      * @return a primitive builder for {@code type} that will return this
      *          builder for additional fields.
      */
-    public PrimitiveBuilder<GroupBuilder<P>> required(
+    public PrimitiveBuilder<THIS> required(
         PrimitiveTypeName type) {
-      return new PrimitiveBuilder<GroupBuilder<P>>(this, type)
+      return new PrimitiveBuilder<THIS>(self(), type)
           .repetition(Type.Repetition.REQUIRED);
     }
 
@@ -469,9 +539,9 @@ public class Types {
      * @return a primitive builder for {@code type} that will return this
      *          builder for additional fields.
      */
-    public PrimitiveBuilder<GroupBuilder<P>> optional(
+    public PrimitiveBuilder<THIS> optional(
         PrimitiveTypeName type) {
-      return new PrimitiveBuilder<GroupBuilder<P>>(this, type)
+      return new PrimitiveBuilder<THIS>(self(), type)
           .repetition(Type.Repetition.OPTIONAL);
     }
 
@@ -483,14 +553,14 @@ public class Types {
      * @return a primitive builder for {@code type} that will return this
      *          builder for additional fields.
      */
-    public PrimitiveBuilder<GroupBuilder<P>> repeated(
+    public PrimitiveBuilder<THIS> repeated(
         PrimitiveTypeName type) {
-      return new PrimitiveBuilder<GroupBuilder<P>>(this, type)
+      return new PrimitiveBuilder<THIS>(self(), type)
           .repetition(Type.Repetition.REPEATED);
     }
 
-    public GroupBuilder<GroupBuilder<P>> group(Type.Repetition repetition) {
-      return new GroupBuilder<GroupBuilder<P>>(this)
+    public GroupBuilder<THIS> group(Type.Repetition repetition) {
+      return new GroupBuilder<THIS>(self())
           .repetition(repetition);
     }
 
@@ -500,8 +570,8 @@ public class Types {
      * @return a group builder that will return this builder for additional
      *          fields.
      */
-    public GroupBuilder<GroupBuilder<P>> requiredGroup() {
-      return new GroupBuilder<GroupBuilder<P>>(this)
+    public GroupBuilder<THIS> requiredGroup() {
+      return new GroupBuilder<THIS>(self())
           .repetition(Type.Repetition.REQUIRED);
     }
 
@@ -511,8 +581,8 @@ public class Types {
      * @return a group builder that will return this builder for additional
      *          fields.
      */
-    public GroupBuilder<GroupBuilder<P>> optionalGroup() {
-      return new GroupBuilder<GroupBuilder<P>>(this)
+    public GroupBuilder<THIS> optionalGroup() {
+      return new GroupBuilder<THIS>(self())
           .repetition(Type.Repetition.OPTIONAL);
     }
 
@@ -522,8 +592,8 @@ public class Types {
      * @return a group builder that will return this builder for additional
      *          fields.
      */
-    public GroupBuilder<GroupBuilder<P>> repeatedGroup() {
-      return new GroupBuilder<GroupBuilder<P>>(this)
+    public GroupBuilder<THIS> repeatedGroup() {
+      return new GroupBuilder<THIS>(self())
           .repetition(Type.Repetition.REPEATED);
     }
 
@@ -532,9 +602,9 @@ public class Types {
      *
      * @return this builder for additional fields.
      */
-    public GroupBuilder<P> addField(Type type) {
+    public THIS addField(Type type) {
       fields.add(type);
-      return this;
+      return self();
     }
 
     /**
@@ -542,11 +612,9 @@ public class Types {
      *
      * @return this builder for additional fields.
      */
-    public GroupBuilder<P> addFields(Type... types) {
-      for (Type type : types) {
-        fields.add(type);
-      }
-      return this;
+    public THIS addFields(Type... types) {
+      Collections.addAll(fields, types);
+      return self();
     }
 
     @Override
@@ -555,6 +623,616 @@ public class Types {
           "Cannot build an empty group");
       return new GroupType(repetition, name, originalType, fields, id);
     }
+
+    public MapBuilder<THIS> map(
+        Type.Repetition repetition) {
+      return new MapBuilder<THIS>(self()).repetition(repetition);
+    }
+
+    public MapBuilder<THIS> requiredMap() {
+      return new MapBuilder<THIS>(self())
+          .repetition(Type.Repetition.REQUIRED);
+    }
+
+    public MapBuilder<THIS> optionalMap() {
+      return new MapBuilder<THIS>(self())
+          .repetition(Type.Repetition.OPTIONAL);
+    }
+
+    public ListBuilder<THIS> list(Type.Repetition repetition) {
+      return new ListBuilder<THIS>(self()).repetition(repetition);
+    }
+
+    public ListBuilder<THIS> requiredList() {
+      return list(Type.Repetition.REQUIRED);
+    }
+
+    public ListBuilder<THIS> optionalList() {
+      return list(Type.Repetition.OPTIONAL);
+    }
+  }
+
+  /**
+   * A builder for {@link GroupType} objects.
+   *
+   * @param <P> The type that this builder will return from
+   *          {@link #named(String)} when the type is built.
+   */
+  public static class GroupBuilder<P> extends BaseGroupBuilder<P, GroupBuilder<P>> {
+
+    private GroupBuilder(P parent) {
+      super(parent);
+    }
+
+    private GroupBuilder(Class<P> returnType) {
+      super(returnType);
+    }
+
+    @Override
+    protected GroupBuilder<P> self() {
+      return this;
+    }
+  }
+
+  public abstract static class BaseMapBuilder<P, THIS extends BaseMapBuilder<P, THIS>>
+      extends Builder<THIS, P> {
+    private static final Type STRING_KEY = Types
+        .required(PrimitiveTypeName.BINARY).as(OriginalType.UTF8).named("key");
+
+    public static class KeyBuilder<MP, M extends BaseMapBuilder<MP, M>> extends
+        BasePrimitiveBuilder<MP, KeyBuilder<MP, M>> {
+      private final M mapBuilder;
+
+      public KeyBuilder(M mapBuilder, PrimitiveTypeName type) {
+        super(mapBuilder.parent, type);
+        this.mapBuilder = mapBuilder;
+        repetition(Type.Repetition.REQUIRED);
+      }
+
+      public ValueBuilder<MP, M> value(PrimitiveTypeName type,
+                                       Type.Repetition repetition) {
+        mapBuilder.setKeyType(build("key"));
+        return new ValueBuilder<MP, M>(mapBuilder, type).repetition(repetition);
+      }
+
+      public ValueBuilder<MP, M> requiredValue(PrimitiveTypeName type) {
+        return value(type, Type.Repetition.REQUIRED);
+      }
+
+      public ValueBuilder<MP, M> optionalValue(PrimitiveTypeName type) {
+        return value(type, Type.Repetition.OPTIONAL);
+      }
+
+      public GroupValueBuilder<MP, M> groupValue(Type.Repetition repetition) {
+        mapBuilder.setKeyType(build("key"));
+        return new GroupValueBuilder<MP, M>(mapBuilder).repetition(repetition);
+      }
+
+      public GroupValueBuilder<MP, M> requiredGroupValue() {
+        return groupValue(Type.Repetition.REQUIRED);
+      }
+
+      public GroupValueBuilder<MP, M> optionalGroupValue() {
+        return groupValue(Type.Repetition.OPTIONAL);
+      }
+
+      public MapValueBuilder<MP, M> mapValue(Type.Repetition repetition) {
+        mapBuilder.setKeyType(build("key"));
+        return new MapValueBuilder<MP, M>(mapBuilder).repetition(repetition);
+      }
+
+      public MapValueBuilder<MP, M> requiredMapValue() {
+        return mapValue(Type.Repetition.REQUIRED);
+      }
+
+      public MapValueBuilder<MP, M> optionalMapValue() {
+        return mapValue(Type.Repetition.OPTIONAL);
+      }
+
+      public ListValueBuilder<MP, M> listValue(Type.Repetition repetition) {
+        mapBuilder.setKeyType(build("key"));
+        return new ListValueBuilder<MP, M>(mapBuilder).repetition(repetition);
+      }
+
+      public ListValueBuilder<MP, M> requiredListValue() {
+        return listValue(Type.Repetition.REQUIRED);
+      }
+
+      public ListValueBuilder<MP, M> optionalListValue() {
+        return listValue(Type.Repetition.OPTIONAL);
+      }
+
+      public M value(Type type) {
+        mapBuilder.setKeyType(build("key"));
+        mapBuilder.setValueType(type);
+        return this.mapBuilder;
+      }
+
+      @Override
+      public MP named(String name) {
+        mapBuilder.setKeyType(build("key"));
+        return mapBuilder.named(name);
+      }
+
+      @Override
+      protected KeyBuilder<MP, M> self() {
+        return this;
+      }
+    }
+
+    public static class ValueBuilder<MP, M extends BaseMapBuilder<MP, M>>
+        extends BasePrimitiveBuilder<MP, ValueBuilder<MP, M>> {
+      private final M mapBuilder;
+
+      public ValueBuilder(M mapBuilder, PrimitiveTypeName type) {
+        super(mapBuilder.parent, type);
+        this.mapBuilder = mapBuilder;
+      }
+
+      public MP named(String name) {
+        mapBuilder.setValueType(build("value"));
+        return mapBuilder.named(name);
+      }
+
+      @Override
+      protected ValueBuilder<MP, M> self() {
+        return this;
+      }
+    }
+
+    public static class GroupKeyBuilder<MP, M extends BaseMapBuilder<MP, M>>
+        extends BaseGroupBuilder<MP, GroupKeyBuilder<MP, M>> {
+      private final M mapBuilder;
+
+      public GroupKeyBuilder(M mapBuilder) {
+        super(mapBuilder.parent);
+        this.mapBuilder = mapBuilder;
+        repetition(Type.Repetition.REQUIRED);
+      }
+
+      @Override
+      protected GroupKeyBuilder<MP, M> self() {
+        return this;
+      }
+
+      public ValueBuilder<MP, M> value(PrimitiveTypeName type,
+                                       Type.Repetition repetition) {
+        mapBuilder.setKeyType(build("key"));
+        return new ValueBuilder<MP, M>(mapBuilder, type).repetition(repetition);
+      }
+
+      public ValueBuilder<MP, M> requiredValue(PrimitiveTypeName type) {
+        return value(type, Type.Repetition.REQUIRED);
+      }
+
+      public ValueBuilder<MP, M> optionalValue(PrimitiveTypeName type) {
+        return value(type, Type.Repetition.OPTIONAL);
+      }
+
+      public GroupValueBuilder<MP, M> groupValue(Type.Repetition repetition) {
+        mapBuilder.setKeyType(build("key"));
+        return new GroupValueBuilder<MP, M>(mapBuilder).repetition(repetition);
+      }
+
+      public GroupValueBuilder<MP, M> requiredGroupValue() {
+        return groupValue(Type.Repetition.REQUIRED);
+      }
+
+      public GroupValueBuilder<MP, M> optionalGroupValue() {
+        return groupValue(Type.Repetition.OPTIONAL);
+      }
+
+      public MapValueBuilder<MP, M> mapValue(Type.Repetition repetition) {
+        mapBuilder.setKeyType(build("key"));
+        return new MapValueBuilder<MP, M>(mapBuilder).repetition(repetition);
+      }
+
+      public MapValueBuilder<MP, M> requiredMapValue() {
+        return mapValue(Type.Repetition.REQUIRED);
+      }
+
+      public MapValueBuilder<MP, M> optionalMapValue() {
+        return mapValue(Type.Repetition.OPTIONAL);
+      }
+
+      public ListValueBuilder<MP, M> listValue(Type.Repetition repetition) {
+        mapBuilder.setKeyType(build("key"));
+        return new ListValueBuilder<MP, M>(mapBuilder).repetition(repetition);
+      }
+
+      public ListValueBuilder<MP, M> requiredListValue() {
+        return listValue(Type.Repetition.REQUIRED);
+      }
+
+      public ListValueBuilder<MP, M> optionalListValue() {
+        return listValue(Type.Repetition.OPTIONAL);
+      }
+
+      public M value(Type type) {
+        mapBuilder.setKeyType(build("key"));
+        mapBuilder.setValueType(type);
+        return this.mapBuilder;
+      }
+
+      @Override
+      public MP named(String name) {
+        mapBuilder.setKeyType(build("key"));
+        return mapBuilder.named(name);
+      }
+    }
+
+    public static class GroupValueBuilder<MP, M extends BaseMapBuilder<MP, M>>
+        extends BaseGroupBuilder<MP, GroupValueBuilder<MP, M>> {
+      private final M mapBuilder;
+
+      public GroupValueBuilder(M mapBuilder) {
+        super(mapBuilder.parent);
+        this.mapBuilder = mapBuilder;
+      }
+
+      public MP named(String name) {
+        mapBuilder.setValueType(build("value"));
+        return mapBuilder.named(name);
+      }
+
+      @Override
+      protected GroupValueBuilder<MP, M> self() {
+        return this;
+      }
+    }
+
+    public static class MapValueBuilder<MP, M extends BaseMapBuilder<MP, M>>
+        extends BaseMapBuilder<MP, MapValueBuilder<MP, M>> {
+      private final M mapBuilder;
+
+      public MapValueBuilder(M mapBuilder) {
+        super(mapBuilder.parent);
+        this.mapBuilder = mapBuilder;
+      }
+
+      public MP named(String name) {
+        mapBuilder.setValueType(build("value"));
+        return mapBuilder.named(name);
+      }
+
+      @Override
+      protected MapValueBuilder<MP, M> self() {
+        return this;
+      }
+    }
+
+    public static class ListValueBuilder<MP, M extends BaseMapBuilder<MP, M>>
+        extends BaseListBuilder<MP, ListValueBuilder<MP, M>> {
+      private final M mapBuilder;
+
+      public ListValueBuilder(M mapBuilder) {
+        super(mapBuilder.parent);
+        this.mapBuilder = mapBuilder;
+      }
+
+      public MP named(String name) {
+        mapBuilder.setValueType(build("value"));
+        return mapBuilder.named(name);
+      }
+
+      @Override
+      protected ListValueBuilder<MP, M> self() {
+        return this;
+      }
+    }
+
+    protected void setKeyType(Type keyType) {
+      Preconditions.checkState(this.keyType == null,
+          "Only one key type can be built with a MapBuilder");
+      this.keyType = keyType;
+    }
+
+    protected void setValueType(Type valueType) {
+      Preconditions.checkState(this.valueType == null,
+          "Only one key type can be built with a ValueBuilder");
+      this.valueType = valueType;
+    }
+
+    private Type keyType = null;
+    private Type valueType = null;
+
+    public BaseMapBuilder(P parent) {
+      super(parent);
+    }
+
+    private BaseMapBuilder(Class<P> returnType) {
+      super(returnType);
+    }
+
+    @Override
+    protected abstract THIS self();
+
+    public KeyBuilder<P, THIS> key(PrimitiveTypeName type) {
+      return new KeyBuilder<P, THIS>(self(), type);
+    }
+
+    public THIS key(Type type) {
+      setKeyType(type);
+      return self();
+    }
+
+    public GroupKeyBuilder<P, THIS> groupKey() {
+      return new GroupKeyBuilder<P, THIS>(self());
+    }
+
+    public ValueBuilder<P, THIS> value(PrimitiveTypeName type,
+                                       Type.Repetition repetition) {
+      return new ValueBuilder<P, THIS>(self(), type).repetition(repetition);
+    }
+
+    public ValueBuilder<P, THIS> requiredValue(PrimitiveTypeName type) {
+      return value(type, Type.Repetition.REQUIRED);
+    }
+
+    public ValueBuilder<P, THIS> optionalValue(PrimitiveTypeName type) {
+      return value(type, Type.Repetition.OPTIONAL);
+    }
+
+    public GroupValueBuilder<P, THIS> groupValue(Type.Repetition repetition) {
+      return new GroupValueBuilder<P, THIS>(self()).repetition(repetition);
+    }
+
+    public GroupValueBuilder<P, THIS> requiredGroupValue() {
+      return groupValue(Type.Repetition.REQUIRED);
+    }
+
+    public GroupValueBuilder<P, THIS> optionalGroupValue() {
+      return groupValue(Type.Repetition.OPTIONAL);
+    }
+
+    public MapValueBuilder<P, THIS> mapValue(Type.Repetition repetition) {
+      return new MapValueBuilder<P, THIS>(self()).repetition(repetition);
+    }
+
+    public MapValueBuilder<P, THIS> requiredMapValue() {
+      return mapValue(Type.Repetition.REQUIRED);
+    }
+
+    public MapValueBuilder<P, THIS> optionalMapValue() {
+      return mapValue(Type.Repetition.OPTIONAL);
+    }
+
+    public ListValueBuilder<P, THIS> listValue(Type.Repetition repetition) {
+      return new ListValueBuilder<P, THIS>(self()).repetition(repetition);
+    }
+
+    public ListValueBuilder<P, THIS> requiredListValue() {
+      return listValue(Type.Repetition.REQUIRED);
+    }
+
+    public ListValueBuilder<P, THIS> optionalListValue() {
+      return listValue(Type.Repetition.OPTIONAL);
+    }
+
+    public THIS value(Type type) {
+      setValueType(type);
+      return self();
+    }
+
+    @Override
+    protected Type build(String name) {
+      Preconditions.checkState(originalType == null,
+          "MAP is already a logical type and can't be changed.");
+      if (keyType == null) {
+        keyType = STRING_KEY;
+      }
+      if (valueType != null) {
+        return buildGroup(repetition).as(OriginalType.MAP)
+            .repeatedGroup().addFields(keyType, valueType).named("map")
+            .named(name);
+      } else {
+        return buildGroup(repetition).as(OriginalType.MAP)
+            .repeatedGroup().addFields(keyType).named("map")
+            .named(name);
+      }
+    }
+  }
+
+  public static class MapBuilder<P> extends BaseMapBuilder<P, MapBuilder<P>> {
+    public MapBuilder(P parent) {
+      super(parent);
+    }
+
+    private MapBuilder(Class<P> returnType) {
+      super(returnType);
+    }
+
+    @Override
+    protected MapBuilder<P> self() {
+      return this;
+    }
+  }
+
+  public abstract static class BaseListBuilder<P, THIS extends BaseListBuilder<P, THIS>>
+      extends Builder<THIS, P> {
+    private Type elementType = null;
+    private P parent;
+
+    public BaseListBuilder(P parent) {
+      super(parent);
+      this.parent = parent;
+    }
+
+    public BaseListBuilder(Class<P> returnType) {
+      super(returnType);
+    }
+
+    public void setElementType(Type elementType) {
+      Preconditions.checkState(this.elementType == null,
+          "Only one element can be built with a ListBuilder");
+      this.elementType = elementType;
+    }
+
+    public static class ElementBuilder<LP, L extends BaseListBuilder<LP, L>>
+        extends BasePrimitiveBuilder<LP, ElementBuilder<LP, L>> {
+      private final BaseListBuilder<LP, L> listBuilder;
+
+      public ElementBuilder(L listBuilder, PrimitiveTypeName type) {
+        super(((BaseListBuilder<LP, L>) listBuilder).parent, type);
+        this.listBuilder = listBuilder;
+      }
+
+      public LP named(String name) {
+        listBuilder.setElementType(build("element"));
+        return listBuilder.named(name);
+      }
+
+      @Override
+      protected ElementBuilder<LP, L> self() {
+        return this;
+      }
+    }
+
+    public static class GroupElementBuilder<LP, L extends BaseListBuilder<LP, L>>
+        extends BaseGroupBuilder<LP, GroupElementBuilder<LP, L>> {
+      private final L listBuilder;
+
+      public GroupElementBuilder(L listBuilder) {
+        super(((BaseListBuilder<LP, L>) listBuilder).parent);
+        this.listBuilder = listBuilder;
+      }
+
+      public LP named(String name) {
+        listBuilder.setElementType(build("element"));
+        return listBuilder.named(name);
+      }
+
+      @Override
+      protected GroupElementBuilder<LP, L> self() {
+        return this;
+      }
+    }
+
+    public static class MapElementBuilder<LP, L extends BaseListBuilder<LP, L>>
+        extends BaseMapBuilder<LP, MapElementBuilder<LP, L>> {
+
+      private final L listBuilder;
+
+      public MapElementBuilder(L listBuilder) {
+        super(((BaseListBuilder<LP, L>) listBuilder).parent);
+        this.listBuilder = listBuilder;
+      }
+
+      @Override
+      protected MapElementBuilder<LP, L> self() {
+        return this;
+      }
+
+      @Override
+      public LP named(String name) {
+        listBuilder.setElementType(build("element"));
+        return listBuilder.named(name);
+      }
+    }
+
+    public static class ListElementBuilder<LP, L extends BaseListBuilder<LP, L>>
+        extends BaseListBuilder<LP, ListElementBuilder<LP, L>> {
+
+      private final L listBuilder;
+
+      public ListElementBuilder(L listBuilder) {
+        super(((BaseListBuilder<LP, L>) listBuilder).parent);
+        this.listBuilder = listBuilder;
+      }
+
+      @Override
+      protected ListElementBuilder<LP, L> self() {
+        return this;
+      }
+
+      @Override
+      public LP named(String name) {
+        listBuilder.setElementType(build("element"));
+        return listBuilder.named(name);
+      }
+    }
+
+    @Override
+    protected abstract THIS self();
+
+    @Override
+    protected Type build(String name) {
+      Preconditions.checkState(originalType == null,
+          "LIST is already the logical type and can't be changed");
+      Preconditions.checkNotNull(elementType, "List element type");
+      return buildGroup(repetition).as(OriginalType.LIST)
+          .repeatedGroup().addFields(elementType).named("list")
+          .named(name);
+    }
+
+    public ElementBuilder<P, THIS> element(PrimitiveTypeName type,
+                                           Type.Repetition repetition) {
+      return new ElementBuilder<P, THIS>(self(), type).repetition(repetition);
+    }
+
+    public ElementBuilder<P, THIS> requiredElement(PrimitiveTypeName type) {
+      return element(type, Type.Repetition.REQUIRED);
+    }
+
+    public ElementBuilder<P, THIS> optionalElement(PrimitiveTypeName type) {
+      return element(type, Type.Repetition.OPTIONAL);
+    }
+
+    public GroupElementBuilder<P, THIS> groupElement(Type.Repetition repetition) {
+      return new GroupElementBuilder<P, THIS>(self()).repetition(repetition);
+    }
+
+    public GroupElementBuilder<P, THIS> requiredGroupElement() {
+      return groupElement(Type.Repetition.REQUIRED);
+    }
+
+    public GroupElementBuilder<P, THIS> optionalGroupElement() {
+      return groupElement(Type.Repetition.OPTIONAL);
+    }
+
+    public MapElementBuilder<P, THIS> mapElement(Type.Repetition repetition) {
+      return new MapElementBuilder<P, THIS>(self()).repetition(repetition);
+    }
+
+    public MapElementBuilder<P, THIS> requiredMapElement() {
+      return mapElement(Type.Repetition.REQUIRED);
+    }
+
+    public MapElementBuilder<P, THIS> optionalMapElement() {
+      return mapElement(Type.Repetition.OPTIONAL);
+    }
+
+    public ListElementBuilder<P, THIS> listElement(Type.Repetition repetition) {
+      return new ListElementBuilder<P, THIS>(self()).repetition(repetition);
+    }
+
+    public ListElementBuilder<P, THIS> requiredListElement() {
+      return listElement(Type.Repetition.REQUIRED);
+    }
+
+    public ListElementBuilder<P, THIS> optionalListElement() {
+      return listElement(Type.Repetition.OPTIONAL);
+    }
+
+    public BaseListBuilder<P, THIS> element(Type type) {
+      setElementType(type);
+      return self();
+    }
+  }
+
+  public static class ListBuilder<P> extends BaseListBuilder<P, ListBuilder<P>> {
+    public ListBuilder(P parent) {
+      super(parent);
+    }
+
+    public ListBuilder(Class<P> returnType) {
+      super(returnType);
+    }
+
+    @Override
+    protected ListBuilder<P> self() {
+      return this;
+    }
   }
 
   public static class MessageTypeBuilder extends GroupBuilder<MessageType> {
@@ -588,6 +1266,45 @@ public class Types {
     return new MessageTypeBuilder();
   }
 
+  public static PrimitiveBuilder<PrimitiveType> primitive(PrimitiveTypeName type,
+                                                          Type.Repetition repetition) {
+    return new PrimitiveBuilder<PrimitiveType>(PrimitiveType.class, type)
+        .repetition(repetition);
+  }
+
+  /**
+   * Returns a builder to construct a required {@link PrimitiveType}.
+   *
+   * @param type a {@link PrimitiveTypeName} for the constructed type
+   * @return a {@link PrimitiveBuilder}
+   */
+  public static PrimitiveBuilder<PrimitiveType> required(PrimitiveTypeName type) {
+    return new PrimitiveBuilder<PrimitiveType>(PrimitiveType.class, type)
+        .repetition(Type.Repetition.REQUIRED);
+  }
+
+  /**
+   * Returns a builder to construct an optional {@link PrimitiveType}.
+   *
+   * @param type a {@link PrimitiveTypeName} for the constructed type
+   * @return a {@link PrimitiveBuilder}
+   */
+  public static PrimitiveBuilder<PrimitiveType> optional(PrimitiveTypeName type) {
+    return new PrimitiveBuilder<PrimitiveType>(PrimitiveType.class, type)
+        .repetition(Type.Repetition.OPTIONAL);
+  }
+
+  /**
+   * Returns a builder to construct a repeated {@link PrimitiveType}.
+   *
+   * @param type a {@link PrimitiveTypeName} for the constructed type
+   * @return a {@link PrimitiveBuilder}
+   */
+  public static PrimitiveBuilder<PrimitiveType> repeated(PrimitiveTypeName type) {
+    return new PrimitiveBuilder<PrimitiveType>(PrimitiveType.class, type)
+        .repetition(Type.Repetition.REPEATED);
+  }
+
   public static GroupBuilder<GroupType> buildGroup(
       Type.Repetition repetition) {
     return new GroupBuilder<GroupType>(GroupType.class).repetition(repetition);
@@ -623,46 +1340,29 @@ public class Types {
         .repetition(Type.Repetition.REPEATED);
   }
 
-  public static PrimitiveBuilder<PrimitiveType> primitive(
-      PrimitiveTypeName type, Type.Repetition repetition) {
-    return new PrimitiveBuilder<PrimitiveType>(PrimitiveType.class, type)
-        .repetition(repetition);
+
+  public static MapBuilder<GroupType> map(Type.Repetition repetition) {
+    return new MapBuilder<GroupType>(GroupType.class).repetition(repetition);
   }
 
-  /**
-   * Returns a builder to construct a required {@link PrimitiveType}.
-   *
-   * @param type a {@link PrimitiveTypeName} for the constructed type
-   * @return a {@link PrimitiveBuilder}
-   */
-  public static PrimitiveBuilder<PrimitiveType> required(
-      PrimitiveTypeName type) {
-    return new PrimitiveBuilder<PrimitiveType>(PrimitiveType.class, type)
-        .repetition(Type.Repetition.REQUIRED);
+  public static MapBuilder<GroupType> requiredMap() {
+    return map(Type.Repetition.REQUIRED);
   }
 
-  /**
-   * Returns a builder to construct an optional {@link PrimitiveType}.
-   *
-   * @param type a {@link PrimitiveTypeName} for the constructed type
-   * @return a {@link PrimitiveBuilder}
-   */
-  public static PrimitiveBuilder<PrimitiveType> optional(
-      PrimitiveTypeName type) {
-    return new PrimitiveBuilder<PrimitiveType>(PrimitiveType.class, type)
-        .repetition(Type.Repetition.OPTIONAL);
+  public static MapBuilder<GroupType> optionalMap() {
+    return map(Type.Repetition.OPTIONAL);
   }
 
-  /**
-   * Returns a builder to construct a repeated {@link PrimitiveType}.
-   *
-   * @param type a {@link PrimitiveTypeName} for the constructed type
-   * @return a {@link PrimitiveBuilder}
-   */
-  public static PrimitiveBuilder<PrimitiveType> repeated(
-      PrimitiveTypeName type) {
-    return new PrimitiveBuilder<PrimitiveType>(PrimitiveType.class, type)
-        .repetition(Type.Repetition.REPEATED);
+  public static ListBuilder<GroupType> list(Type.Repetition repetition) {
+    return new ListBuilder<GroupType>(GroupType.class).repetition(repetition);
+  }
+
+  public static ListBuilder<GroupType> requiredList() {
+    return list(Type.Repetition.REQUIRED);
+  }
+
+  public static ListBuilder<GroupType> optionalList() {
+    return list(Type.Repetition.OPTIONAL);
   }
 
 }

http://git-wip-us.apache.org/repos/asf/parquet-mr/blob/dd92a9db/parquet-column/src/test/java/org/apache/parquet/schema/TestTypeBuilders.java
----------------------------------------------------------------------
diff --git a/parquet-column/src/test/java/org/apache/parquet/schema/TestTypeBuilders.java b/parquet-column/src/test/java/org/apache/parquet/schema/TestTypeBuilders.java
index bdf7429..4230561 100644
--- a/parquet-column/src/test/java/org/apache/parquet/schema/TestTypeBuilders.java
+++ b/parquet-column/src/test/java/org/apache/parquet/schema/TestTypeBuilders.java
@@ -18,6 +18,8 @@
  */
 package org.apache.parquet.schema;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.Callable;
 import org.junit.Assert;
 import org.junit.Ignore;
@@ -599,6 +601,773 @@ public class TestTypeBuilders {
         });
   }
 
+  @Test
+  public void testRequiredMap() {
+    List<Type> typeList = new ArrayList<Type>();
+    typeList.add(new PrimitiveType(REQUIRED, INT64, "key"));
+    typeList.add(new PrimitiveType(REQUIRED, INT64, "value"));
+    GroupType expected = new GroupType(REQUIRED, "myMap", OriginalType.MAP, new GroupType(REPEATED,
+        "map",
+        typeList));
+    GroupType actual = Types.requiredMap()
+        .key(INT64)
+        .requiredValue(INT64)
+        .named("myMap");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testOptionalMap() {
+    List<Type> typeList = new ArrayList<Type>();
+    typeList.add(new PrimitiveType(REQUIRED, INT64, "key"));
+    typeList.add(new PrimitiveType(REQUIRED, INT64, "value"));
+    GroupType expected = new GroupType(OPTIONAL, "myMap", OriginalType.MAP, new GroupType(REPEATED,
+        "map",
+        typeList));
+    GroupType actual = Types.optionalMap()
+        .key(INT64)
+        .requiredValue(INT64)
+        .named("myMap");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testMapWithRequiredValue() {
+    List<Type> typeList = new ArrayList<Type>();
+    typeList.add(new PrimitiveType(REQUIRED, INT64, "key"));
+    typeList.add(new PrimitiveType(REQUIRED, INT64, "value"));
+    GroupType map = new GroupType(REQUIRED, "myMap", OriginalType.MAP, new GroupType(REPEATED, "map",
+        typeList));
+    MessageType expected = new MessageType("mapParent", map);
+    GroupType actual = Types.buildMessage().requiredMap()
+        .key(INT64)
+        .requiredValue(INT64)
+        .named("myMap").named("mapParent");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testMapWithOptionalValue() {
+    List<Type> typeList = new ArrayList<Type>();
+    typeList.add(new PrimitiveType(REQUIRED, INT64, "key"));
+    typeList.add(new PrimitiveType(OPTIONAL, INT64, "value"));
+    GroupType map = new GroupType(REQUIRED, "myMap", OriginalType.MAP, new GroupType(REPEATED, "map",
+        typeList));
+    MessageType expected = new MessageType("mapParent", map);
+    GroupType actual = Types.buildMessage().requiredMap()
+        .key(INT64)
+        .optionalValue(INT64)
+        .named("myMap").named("mapParent");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testMapWithGroupKeyAndOptionalGroupValue() {
+    List<Type> typeList = new ArrayList<Type>();
+
+    List<Type> keyFields = new ArrayList<Type>();
+    keyFields.add(new PrimitiveType(OPTIONAL, INT64, "first"));
+    keyFields.add(new PrimitiveType(OPTIONAL, DOUBLE, "second"));
+    typeList.add(new GroupType(REQUIRED, "key", keyFields));
+
+    List<Type> valueFields = new ArrayList<Type>();
+    valueFields.add(new PrimitiveType(OPTIONAL, DOUBLE, "one"));
+    valueFields.add(new PrimitiveType(OPTIONAL, INT32, "two"));
+    typeList.add(new GroupType(OPTIONAL, "value", valueFields));
+
+    GroupType map = new GroupType(OPTIONAL, "myMap", OriginalType.MAP, new GroupType(REPEATED, "map",
+        typeList));
+
+    GroupType actual = Types.optionalMap()
+        .groupKey()
+          .optional(INT64).named("first")
+          .optional(DOUBLE).named("second")
+        .optionalGroupValue()
+          .optional(DOUBLE).named("one")
+          .optional(INT32).named("two")
+        .named("myMap");
+    Assert.assertEquals(map, actual);
+  }
+
+  @Test
+  public void testMapWithGroupKeyAndRequiredGroupValue() {
+    List<Type> typeList = new ArrayList<Type>();
+
+    List<Type> keyFields = new ArrayList<Type>();
+    keyFields.add(new PrimitiveType(OPTIONAL, INT64, "first"));
+    keyFields.add(new PrimitiveType(OPTIONAL, DOUBLE, "second"));
+    typeList.add(new GroupType(REQUIRED, "key", keyFields));
+
+    List<Type> valueFields = new ArrayList<Type>();
+    valueFields.add(new PrimitiveType(OPTIONAL, DOUBLE, "one"));
+    valueFields.add(new PrimitiveType(OPTIONAL, INT32, "two"));
+    typeList.add(new GroupType(REQUIRED, "value", valueFields));
+
+    GroupType map = new GroupType(OPTIONAL, "myMap", OriginalType.MAP, new GroupType(REPEATED, "map",
+        typeList));
+
+    MessageType expected = new MessageType("mapParent", map);
+    GroupType actual =
+        Types.buildMessage()
+            .optionalMap()
+              .groupKey()
+                .optional(INT64).named("first")
+                .optional(DOUBLE).named("second")
+              .requiredGroupValue()
+                .optional(DOUBLE).named("one")
+                .optional(INT32).named("two")
+              .named("myMap")
+            .named("mapParent");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testMapWithGroupKeyAndOptionalValue() {
+    List<Type> typeList = new ArrayList<Type>();
+
+    List<Type> keyFields = new ArrayList<Type>();
+    keyFields.add(new PrimitiveType(OPTIONAL, INT64, "first"));
+    keyFields.add(new PrimitiveType(OPTIONAL, DOUBLE, "second"));
+    typeList.add(new GroupType(REQUIRED, "key", keyFields));
+
+    typeList.add(new PrimitiveType(OPTIONAL, DOUBLE, "value"));
+
+    GroupType map = new GroupType(OPTIONAL, "myMap", OriginalType.MAP, new GroupType(REPEATED, "map",
+        typeList));
+
+    MessageType expected = new MessageType("mapParent", map);
+    GroupType actual =
+        Types.buildMessage()
+            .optionalMap()
+              .groupKey()
+                .optional(INT64).named("first")
+                .optional(DOUBLE).named("second")
+              .optionalValue(DOUBLE).named("myMap")
+            .named("mapParent");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testMapWithGroupKeyAndRequiredValue() {
+    List<Type> typeList = new ArrayList<Type>();
+
+    List<Type> keyFields = new ArrayList<Type>();
+    keyFields.add(new PrimitiveType(OPTIONAL, INT64, "first"));
+    keyFields.add(new PrimitiveType(OPTIONAL, DOUBLE, "second"));
+    typeList.add(new GroupType(REQUIRED, "key", keyFields));
+
+    typeList.add(new PrimitiveType(REQUIRED, DOUBLE, "value"));
+
+    GroupType map = new GroupType(OPTIONAL, "myMap", OriginalType.MAP, new GroupType(REPEATED, "map",
+        typeList));
+
+    MessageType expected = new MessageType("mapParent", map);
+    GroupType actual =
+        Types.buildMessage()
+            .optionalMap()
+              .groupKey()
+                .optional(INT64).named("first")
+                .optional(DOUBLE).named("second")
+              .requiredValue(DOUBLE).named("myMap")
+            .named("mapParent");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testMapWithOptionalGroupValue() {
+    List<Type> typeList = new ArrayList<Type>();
+
+    List<Type> keyFields = new ArrayList<Type>();
+    keyFields.add(new PrimitiveType(OPTIONAL, INT64, "first"));
+    keyFields.add(new PrimitiveType(OPTIONAL, DOUBLE, "second"));
+    typeList.add(new PrimitiveType(REQUIRED, INT64, "key"));
+
+    List<Type> valueFields = new ArrayList<Type>();
+    valueFields.add(new PrimitiveType(OPTIONAL, DOUBLE, "one"));
+    valueFields.add(new PrimitiveType(OPTIONAL, INT32, "two"));
+    typeList.add(new GroupType(OPTIONAL, "value", valueFields));
+
+    GroupType map = new GroupType(OPTIONAL, "myMap", OriginalType.MAP, new GroupType(REPEATED, "map",
+        typeList));
+
+    MessageType expected = new MessageType("mapParent", map);
+    GroupType actual =
+        Types.buildMessage()
+            .optionalMap()
+              .key(INT64)
+              .optionalGroupValue()
+                .optional(DOUBLE).named("one")
+                .optional(INT32).named("two")
+              .named("myMap")
+            .named("mapParent");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testMapWithRequiredGroupValue() {
+    List<Type> typeList = new ArrayList<Type>();
+
+    typeList.add(new PrimitiveType(REQUIRED, INT64, "key"));
+
+    List<Type> valueFields = new ArrayList<Type>();
+    valueFields.add(new PrimitiveType(OPTIONAL, DOUBLE, "one"));
+    valueFields.add(new PrimitiveType(OPTIONAL, INT32, "two"));
+    typeList.add(new GroupType(REQUIRED, "value", valueFields));
+
+    GroupType map = new GroupType(OPTIONAL, "myMap", OriginalType.MAP, new GroupType(REPEATED, "map",
+        typeList));
+
+    MessageType expected = new MessageType("mapParent", map);
+    GroupType actual =
+        Types.buildMessage()
+            .optionalMap()
+              .key(INT64)
+              .requiredGroupValue()
+                .optional(DOUBLE).named("one")
+                .optional(INT32).named("two")
+              .named("myMap")
+            .named("mapParent");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testMapWithNestedGroupKeyAndNestedGroupValue() {
+    List<Type> typeList = new ArrayList<Type>();
+
+    List<Type> innerFields = new ArrayList<Type>();
+    innerFields.add(new PrimitiveType(REQUIRED, FLOAT, "inner_key_1"));
+    innerFields.add(new PrimitiveType(OPTIONAL, INT32, "inner_key_2"));
+
+    List<Type> keyFields = new ArrayList<Type>();
+    keyFields.add(new PrimitiveType(OPTIONAL, INT64, "first"));
+    keyFields.add(new GroupType(REQUIRED, "second", innerFields));
+    typeList.add(new GroupType(REQUIRED, "key", keyFields));
+
+    List<Type> valueFields = new ArrayList<Type>();
+    valueFields.add(new GroupType(OPTIONAL, "one", innerFields));
+    valueFields.add(new PrimitiveType(OPTIONAL, INT32, "two"));
+    typeList.add(new GroupType(OPTIONAL, "value", valueFields));
+
+    GroupType map = new GroupType(REQUIRED, "myMap", OriginalType.MAP, new GroupType(REPEATED, "map",
+        typeList));
+
+    MessageType expected = new MessageType("mapParent", map);
+    GroupType actual =
+        Types.buildMessage()
+            .requiredMap()
+              .groupKey()
+                .optional(INT64).named("first")
+                .requiredGroup()
+                  .required(FLOAT).named("inner_key_1")
+                  .optional(INT32).named("inner_key_2")
+                .named("second")
+              .optionalGroupValue()
+                .optionalGroup()
+                  .required(FLOAT).named("inner_key_1")
+                  .optional(INT32).named("inner_key_2")
+                .named("one")
+                .optional(INT32).named("two")
+              .named("myMap")
+            .named("mapParent");
+    Assert.assertEquals(expected, actual);
+  }
+
+    @Test
+    public void testMapWithRequiredListValue() {
+        List<Type> typeList = new ArrayList<Type>();
+
+        typeList.add(new PrimitiveType(REQUIRED, INT64, "key"));
+        typeList.add(new GroupType(REQUIRED, "value", OriginalType.LIST,
+                new GroupType(REPEATED,
+                        "list",
+                        new PrimitiveType(OPTIONAL, INT64, "element"))));
+
+        GroupType map = new GroupType(OPTIONAL, "myMap", OriginalType.MAP, new GroupType(REPEATED, "map",
+                typeList));
+
+        MessageType expected = new MessageType("mapParent", map);
+        GroupType actual =
+                Types.buildMessage()
+                  .optionalMap()
+                    .key(INT64)
+                    .requiredListValue()
+                      .optionalElement(INT64)
+                  .named("myMap")
+                .named("mapParent");
+        Assert.assertEquals(expected, actual);
+    }
+
+    @Test
+    public void testMapWithOptionalListValue() {
+        List<Type> typeList = new ArrayList<Type>();
+
+        typeList.add(new PrimitiveType(REQUIRED, INT64, "key"));
+        typeList.add(new GroupType(OPTIONAL, "value", OriginalType.LIST,
+                new GroupType(REPEATED,
+                        "list",
+                        new PrimitiveType(OPTIONAL, INT64, "element"))));
+
+        GroupType map = new GroupType(OPTIONAL, "myMap", OriginalType.MAP, new GroupType(REPEATED, "map",
+                typeList));
+
+        MessageType expected = new MessageType("mapParent", map);
+        GroupType actual =
+                Types.buildMessage()
+                  .optionalMap()
+                    .key(INT64)
+                    .optionalListValue()
+                      .optionalElement(INT64)
+                  .named("myMap")
+                .named("mapParent");
+        Assert.assertEquals(expected, actual);
+    }
+
+  @Test
+  public void testMapWithRequiredMapValue() {
+    List<Type> typeList = new ArrayList<Type>();
+
+    List<Type> innerMapTypeList = new ArrayList<Type>();
+    innerMapTypeList.add(new PrimitiveType(REQUIRED, INT64, "key"));
+    innerMapTypeList.add(new PrimitiveType(REQUIRED, INT64, "value"));
+
+    typeList.add(new PrimitiveType(REQUIRED, INT64, "key"));
+    typeList.add(new GroupType(REQUIRED, "value", OriginalType.MAP,
+        new GroupType(REPEATED, "map", innerMapTypeList)));
+
+    GroupType map = new GroupType(OPTIONAL, "myMap", OriginalType.MAP, new GroupType(REPEATED, "map",
+        typeList));
+
+    MessageType expected = new MessageType("mapParent", map);
+    GroupType actual =
+        Types.buildMessage()
+          .optionalMap()
+            .key(INT64)
+            .requiredMapValue()
+              .key(INT64)
+              .requiredValue(INT64)
+          .named("myMap")
+        .named("mapParent");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testMapWithOptionalMapValue() {
+    List<Type> typeList = new ArrayList<Type>();
+
+    List<Type> innerMapTypeList = new ArrayList<Type>();
+    innerMapTypeList.add(new PrimitiveType(REQUIRED, INT64, "key"));
+    innerMapTypeList.add(new PrimitiveType(REQUIRED, INT64, "value"));
+
+    typeList.add(new PrimitiveType(REQUIRED, INT64, "key"));
+    typeList.add(new GroupType(OPTIONAL, "value", OriginalType.MAP,
+        new GroupType(REPEATED, "map", innerMapTypeList)));
+
+    GroupType map = new GroupType(OPTIONAL, "myMap", OriginalType.MAP, new GroupType(REPEATED, "map",
+        typeList));
+
+    MessageType expected = new MessageType("mapParent", map);
+    GroupType actual =
+      Types.buildMessage()
+        .optionalMap()
+          .key(INT64)
+          .optionalMapValue()
+            .key(INT64)
+            .requiredValue(INT64)
+      .named("myMap")
+    .named("mapParent");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testMapWithGroupKeyAndRequiredListValue() {
+    List<Type> typeList = new ArrayList<Type>();
+
+    typeList.add(new GroupType(REQUIRED, "key", new PrimitiveType(REQUIRED, INT64,
+        "first"
+        )));
+    typeList.add(new GroupType(REQUIRED, "value", OriginalType.LIST,
+        new GroupType(REPEATED,
+            "list",
+            new PrimitiveType(OPTIONAL, INT64, "element"))));
+
+    GroupType map = new GroupType(OPTIONAL, "myMap", OriginalType.MAP, new GroupType(REPEATED, "map",
+        typeList));
+
+    MessageType expected = new MessageType("mapParent", map);
+    GroupType actual =
+      Types.buildMessage()
+        .optionalMap()
+          .groupKey()
+            .required(INT64)
+            .named("first")
+          .requiredListValue()
+            .optionalElement(INT64)
+      .named("myMap")
+    .named("mapParent");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testMapWithGroupKeyAndOptionalListValue() {
+    List<Type> typeList = new ArrayList<Type>();
+
+    typeList.add(new GroupType(REQUIRED, "key", new PrimitiveType(REQUIRED, INT64,
+        "first"
+    )));
+    typeList.add(new GroupType(OPTIONAL, "value", OriginalType.LIST,
+        new GroupType(REPEATED,
+            "list",
+            new PrimitiveType(OPTIONAL, INT64, "element"))));
+
+    GroupType map = new GroupType(OPTIONAL, "myMap", OriginalType.MAP, new GroupType(REPEATED, "map",
+        typeList));
+
+    MessageType expected = new MessageType("mapParent", map);
+    GroupType actual =
+      Types.buildMessage()
+        .optionalMap()
+          .groupKey()
+            .required(INT64)
+          .named("first")
+          .optionalListValue()
+            .optionalElement(INT64)
+        .named("myMap")
+      .named("mapParent");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testMapWithGroupKeyAndRequiredMapValue() {
+    List<Type> typeList = new ArrayList<Type>();
+
+    List<Type> innerMapTypeList = new ArrayList<Type>();
+    innerMapTypeList.add(new PrimitiveType(REQUIRED, INT64, "key"));
+    innerMapTypeList.add(new PrimitiveType(REQUIRED, INT64, "value"));
+
+
+    typeList.add(new GroupType(REQUIRED, "key", new PrimitiveType(REQUIRED, INT64,
+        "first"
+    )));
+    typeList.add(new GroupType(REQUIRED, "value", OriginalType.MAP,
+        new GroupType(REPEATED, "map", innerMapTypeList)));
+
+    GroupType map = new GroupType(OPTIONAL, "myMap", OriginalType.MAP, new GroupType(REPEATED, "map",
+        typeList));
+
+    MessageType expected = new MessageType("mapParent", map);
+    GroupType actual =
+      Types.buildMessage()
+        .optionalMap()
+          .groupKey()
+            .required(INT64)
+          .named("first")
+          .requiredMapValue()
+            .key(INT64)
+            .requiredValue(INT64)
+        .named("myMap")
+      .named("mapParent");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testMapWithGroupKeyAndOptionalMapValue() {
+    List<Type> typeList = new ArrayList<Type>();
+
+    List<Type> innerMapTypeList = new ArrayList<Type>();
+    innerMapTypeList.add(new PrimitiveType(REQUIRED, INT64, "key"));
+    innerMapTypeList.add(new PrimitiveType(REQUIRED, INT64, "value"));
+
+
+    typeList.add(new GroupType(REQUIRED, "key", new PrimitiveType(REQUIRED, INT64,
+        "first"
+    )));
+    typeList.add(new GroupType(OPTIONAL, "value", OriginalType.MAP,
+        new GroupType(REPEATED, "map", innerMapTypeList)));
+
+    GroupType map = new GroupType(OPTIONAL, "myMap", OriginalType.MAP, new GroupType(REPEATED, "map",
+        typeList));
+
+    MessageType expected = new MessageType("mapParent", map);
+    GroupType actual =
+      Types.buildMessage()
+        .optionalMap()
+          .groupKey()
+            .required(INT64)
+          .named("first")
+          .optionalMapValue()
+            .key(INT64)
+            .requiredValue(INT64)
+        .named("myMap")
+      .named("mapParent");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testMapWithNullValue() {
+    List<Type> typeList = new ArrayList<Type>();
+
+    typeList.add(new PrimitiveType(REQUIRED, INT64, "key"));
+    GroupType map = new GroupType(OPTIONAL, "myMap", OriginalType.MAP, new GroupType(REPEATED, "map",
+        typeList));
+
+    MessageType expected = new MessageType("mapParent", map);
+    GroupType actual =
+      Types.buildMessage()
+        .optionalMap()
+          .key(INT64)
+        .named("myMap")
+      .named("mapParent");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testMapWithDefaultKeyAndNullValue() {
+    List<Type> typeList = new ArrayList<Type>();
+
+    typeList.add(new PrimitiveType(REQUIRED, BINARY, "key", OriginalType.UTF8));
+    GroupType map = new GroupType(OPTIONAL, "myMap", OriginalType.MAP, new GroupType(REPEATED, "map",
+        typeList));
+
+    MessageType expected = new MessageType("mapParent", map);
+    GroupType actual =
+        Types.buildMessage()
+          .optionalMap()
+          .named("myMap")
+        .named("mapParent");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testMapWithPreBuiltKeyAndValueTypes() {
+    Type keyType = Types.required(INT64).named("key");
+    Type valueType = Types.required(BOOLEAN).named("value");
+
+    GroupType map = new GroupType(REQUIRED, "myMap", OriginalType.MAP,
+        new GroupType(REPEATED, "map", new Type[] {
+            keyType,
+            valueType
+        }));
+    MessageType expected = new MessageType("mapParent", map);
+
+    GroupType actual = Types.buildMessage()
+        .requiredMap()
+          .key(keyType)
+          .value(valueType)
+        .named("myMap")
+      .named("mapParent");
+
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testListWithRequiredPreBuiltElement() {
+    GroupType expected = new GroupType(REQUIRED, "myList", OriginalType.LIST,
+        new GroupType(REPEATED,
+            "list",
+            new PrimitiveType(REQUIRED, INT64, "element")));
+    Type element = Types.primitive(INT64, REQUIRED).named("element");
+    Type actual = Types.requiredList()
+          .element(element)
+        .named("myList");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testRequiredList() {
+    GroupType expected = new GroupType(REQUIRED, "myList", OriginalType.LIST,
+        new GroupType(REPEATED,
+            "list",
+            new PrimitiveType(OPTIONAL, INT64, "element")));
+    Type actual = Types.requiredList()
+        .optionalElement(INT64)
+        .named("myList");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testOptionalList() {
+    GroupType expected = new GroupType(OPTIONAL, "myList", OriginalType.LIST,
+        new GroupType(REPEATED,
+            "list",
+            new PrimitiveType(OPTIONAL, INT64, "element")));
+    Type actual = Types.optionalList()
+        .optionalElement(INT64)
+        .named("myList");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testListOfReqGroup() {
+    List<Type> fields = new ArrayList<Type>();
+    fields.add(new PrimitiveType(OPTIONAL, BOOLEAN, "field"));
+    GroupType expected = new GroupType(REQUIRED, "myList", OriginalType.LIST,
+        new GroupType(REPEATED,
+            "list",
+            new GroupType(REQUIRED, "element", fields)));
+    Type actual = Types.requiredList()
+        .requiredGroupElement()
+          .optional(BOOLEAN)
+          .named("field")
+        .named("myList");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testListOfOptionalGroup() {
+    List<Type> fields = new ArrayList<Type>();
+    fields.add(new PrimitiveType(OPTIONAL, BOOLEAN, "field"));
+    GroupType expected = new GroupType(REQUIRED, "myList", OriginalType.LIST,
+        new GroupType(REPEATED,
+            "list",
+            new GroupType(OPTIONAL, "element", fields)));
+    Type actual = Types.requiredList()
+          .optionalGroupElement()
+            .optional(BOOLEAN)
+            .named("field")
+        .named("myList");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testRequiredNestedList() {
+    List<Type> fields = new ArrayList<Type>();
+    fields.add(new GroupType(REQUIRED, "element", OriginalType.LIST,
+        new GroupType(REPEATED,
+            "list",
+            new PrimitiveType(OPTIONAL, DOUBLE, "element"))));
+    GroupType expected = new GroupType(OPTIONAL, "myList", OriginalType.LIST,
+        new GroupType(REPEATED,
+            "list",
+            fields));
+
+    Type actual =
+        Types.optionalList()
+          .requiredListElement()
+            .optionalElement(DOUBLE)
+        .named("myList");
+
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testOptionalNestedList() {
+    List<Type> fields = new ArrayList<Type>();
+    fields.add(new GroupType(OPTIONAL, "element", OriginalType.LIST,
+        new GroupType(REPEATED,
+            "list",
+            new PrimitiveType(OPTIONAL, DOUBLE, "element"))));
+    GroupType expected = new GroupType(OPTIONAL, "myList", OriginalType.LIST,
+        new GroupType(REPEATED,
+            "list",
+            fields));
+
+    Type actual =
+        Types.optionalList()
+        .optionalListElement()
+          .optionalElement(DOUBLE)
+        .named("myList");
+
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testRequiredListWithinGroup() {
+    List<Type> fields = new ArrayList<Type>();
+    fields.add(new GroupType(REQUIRED, "element", OriginalType.LIST,
+        new GroupType(REPEATED,
+            "list",
+            new PrimitiveType(OPTIONAL, INT64, "element"))));
+    GroupType expected = new GroupType(REQUIRED, "topGroup", fields);
+    Type actual = Types.requiredGroup()
+          .requiredList()
+            .optionalElement(INT64).named("element")
+        .named("topGroup");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testOptionalListWithinGroup() {
+    List<Type> fields = new ArrayList<Type>();
+    fields.add(new GroupType(OPTIONAL, "element", OriginalType.LIST,
+        new GroupType(REPEATED,
+            "list",
+            new PrimitiveType(OPTIONAL, INT64, "element"))));
+    GroupType expected = new GroupType(REQUIRED, "topGroup", fields);
+    Type actual = Types.requiredGroup()
+        .optionalList()
+        .optionalElement(INT64).named("element")
+        .named("topGroup");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testOptionalListWithinGroupWithReqElement() {
+    List<Type> fields = new ArrayList<Type>();
+    fields.add(new GroupType(OPTIONAL, "element", OriginalType.LIST,
+        new GroupType(REPEATED,
+            "list",
+            new PrimitiveType(REQUIRED, INT64, "element"))));
+    GroupType expected = new GroupType(REQUIRED, "topGroup", fields);
+    Type actual =
+        Types.requiredGroup()
+          .optionalList()
+            .requiredElement(INT64).named("element")
+          .named("topGroup");
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testRequiredMapWithinList() {
+    List<Type> innerFields = new ArrayList<Type>();
+    innerFields.add(new PrimitiveType(REQUIRED, DOUBLE, "key"));
+    innerFields.add(new PrimitiveType(REQUIRED, INT32, "value"));
+
+    List<Type> fields = new ArrayList<Type>();
+    fields.add(new GroupType(REQUIRED, "element", OriginalType.MAP,
+        new GroupType(REPEATED,
+            "map",
+            innerFields)));
+    GroupType expected = new GroupType(OPTIONAL, "myList", OriginalType.LIST,
+        new GroupType(REPEATED,
+            "list",
+            fields));
+
+    Type actual =
+        Types.optionalList()
+          .requiredMapElement()
+            .key(DOUBLE)
+            .requiredValue(INT32)
+        .named("myList");
+
+    Assert.assertEquals(expected, actual);
+  }
+
+  @Test
+  public void testOptionalMapWithinList() {
+    List<Type> innerFields = new ArrayList<Type>();
+    innerFields.add(new PrimitiveType(REQUIRED, DOUBLE, "key"));
+    innerFields.add(new PrimitiveType(REQUIRED, INT32, "value"));
+
+    List<Type> fields = new ArrayList<Type>();
+    fields.add(new GroupType(OPTIONAL, "element", OriginalType.MAP,
+        new GroupType(REPEATED,
+            "map",
+            innerFields)));
+    GroupType expected = new GroupType(OPTIONAL, "myList", OriginalType.LIST,
+        new GroupType(REPEATED,
+            "list",
+            fields));
+
+    Type actual =
+        Types.optionalList()
+          .optionalMapElement()
+            .key(DOUBLE)
+            .requiredValue(INT32)
+        .named("myList");
+
+    Assert.assertEquals(expected, actual);
+  }
+
 
   /**
    * A convenience method to avoid a large number of @Test(expected=...) tests


Mime
View raw message