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 b06c2bcb8f..65e1ef067f 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 @@ * .named("User") * *

+ * 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. + *

+ * 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") + * + *

* Message types are built using {@link #buildMessage()} and function just like * group builders. *

@@ -131,7 +190,7 @@ public class Types {
    * @param 

The type that this builder will return from * {@link #named(String)} when the type is built. */ - public abstract static class Builder { + public abstract static class Builder { protected final P parent; protected final Class returnClass; @@ -148,7 +207,6 @@ public abstract static class Builder { * @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 @@ protected Builder(Class

returnClass) { 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 @@ protected final T repetition(Type.Repetition repetition) { * @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 T as(OriginalType type) { * @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 P named(String name) { 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

The type that this builder will return from - * {@link #named(String)} when the type is built. - */ - public static class PrimitiveBuilder

extends Builder, P> { + public abstract static class + BasePrimitiveBuilder> + extends Builder { 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 static class PrimitiveBuilder

extends Builder, P> 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

returnType, PrimitiveTypeName type) { + private BasePrimitiveBuilder(Class

returnType, PrimitiveTypeName type) { super(returnType); this.primitiveType = type; } @Override - protected PrimitiveBuilder

self() { - return this; - } + protected abstract THIS self(); /** * Adds the length for a FIXED_LEN_BYTE_ARRAY. @@ -276,7 +331,7 @@ protected PrimitiveBuilder

self() { * @param length an int length * @return this builder for method chaining */ - public PrimitiveBuilder

length(int length) { + public BasePrimitiveBuilder length(int length) { this.length = length; return this; } @@ -291,7 +346,7 @@ public PrimitiveBuilder

length(int length) { * @param precision an int precision value for the DECIMAL * @return this builder for method chaining */ - public PrimitiveBuilder

precision(int precision) { + public BasePrimitiveBuilder precision(int precision) { this.precision = precision; return this; } @@ -309,7 +364,7 @@ public PrimitiveBuilder

precision(int precision) { * @param scale an int scale value for the DECIMAL * @return this builder for method chaining */ - public PrimitiveBuilder

scale(int scale) { + public BasePrimitiveBuilder scale(int scale) { this.scale = scale; return this; } @@ -418,32 +473,47 @@ protected DecimalMetadata decimalMetadata() { } /** - * A builder for {@link GroupType} objects. + * A builder for {@link PrimitiveType} objects. * * @param

The type that this builder will return from * {@link #named(String)} when the type is built. */ - public static class GroupBuilder

extends Builder, P> { + public static class PrimitiveBuilder

extends BasePrimitiveBuilder> { + + private PrimitiveBuilder(P parent, PrimitiveTypeName type) { + super(parent, type); + } + + private PrimitiveBuilder(Class

returnType, PrimitiveTypeName type) { + super(returnType, type); + } + + @Override + protected PrimitiveBuilder

self() { + return this; + } + } + + public abstract static class BaseGroupBuilder> + extends Builder { protected final List fields; - private GroupBuilder(P parent) { + private BaseGroupBuilder(P parent) { super(parent); this.fields = new ArrayList(); } - private GroupBuilder(Class

returnType) { + private BaseGroupBuilder(Class

returnType) { super(returnType); this.fields = new ArrayList(); } @Override - protected GroupBuilder

self() { - return this; - } + protected abstract THIS self(); - public PrimitiveBuilder> primitive( + public PrimitiveBuilder primitive( PrimitiveTypeName type, Type.Repetition repetition) { - return new PrimitiveBuilder>(this, type) + return new PrimitiveBuilder (self(), type) .repetition(repetition); } @@ -455,9 +525,9 @@ public PrimitiveBuilder> primitive( * @return a primitive builder for {@code type} that will return this * builder for additional fields. */ - public PrimitiveBuilder> required( + public PrimitiveBuilder required( PrimitiveTypeName type) { - return new PrimitiveBuilder>(this, type) + return new PrimitiveBuilder(self(), type) .repetition(Type.Repetition.REQUIRED); } @@ -469,9 +539,9 @@ public PrimitiveBuilder> required( * @return a primitive builder for {@code type} that will return this * builder for additional fields. */ - public PrimitiveBuilder> optional( + public PrimitiveBuilder optional( PrimitiveTypeName type) { - return new PrimitiveBuilder>(this, type) + return new PrimitiveBuilder(self(), type) .repetition(Type.Repetition.OPTIONAL); } @@ -483,14 +553,14 @@ public PrimitiveBuilder> optional( * @return a primitive builder for {@code type} that will return this * builder for additional fields. */ - public PrimitiveBuilder> repeated( + public PrimitiveBuilder repeated( PrimitiveTypeName type) { - return new PrimitiveBuilder>(this, type) + return new PrimitiveBuilder(self(), type) .repetition(Type.Repetition.REPEATED); } - public GroupBuilder> group(Type.Repetition repetition) { - return new GroupBuilder>(this) + public GroupBuilder group(Type.Repetition repetition) { + return new GroupBuilder(self()) .repetition(repetition); } @@ -500,8 +570,8 @@ public GroupBuilder> group(Type.Repetition repetition) { * @return a group builder that will return this builder for additional * fields. */ - public GroupBuilder> requiredGroup() { - return new GroupBuilder>(this) + public GroupBuilder requiredGroup() { + return new GroupBuilder(self()) .repetition(Type.Repetition.REQUIRED); } @@ -511,8 +581,8 @@ public GroupBuilder> requiredGroup() { * @return a group builder that will return this builder for additional * fields. */ - public GroupBuilder> optionalGroup() { - return new GroupBuilder>(this) + public GroupBuilder optionalGroup() { + return new GroupBuilder(self()) .repetition(Type.Repetition.OPTIONAL); } @@ -522,8 +592,8 @@ public GroupBuilder> optionalGroup() { * @return a group builder that will return this builder for additional * fields. */ - public GroupBuilder> repeatedGroup() { - return new GroupBuilder>(this) + public GroupBuilder repeatedGroup() { + return new GroupBuilder(self()) .repetition(Type.Repetition.REPEATED); } @@ -532,9 +602,9 @@ public GroupBuilder> repeatedGroup() { * * @return this builder for additional fields. */ - public GroupBuilder

addField(Type type) { + public THIS addField(Type type) { fields.add(type); - return this; + return self(); } /** @@ -542,11 +612,9 @@ public GroupBuilder

addField(Type type) { * * @return this builder for additional fields. */ - public GroupBuilder

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 @@ protected GroupType build(String name) { "Cannot build an empty group"); return new GroupType(repetition, name, originalType, fields, id); } + + public MapBuilder map( + Type.Repetition repetition) { + return new MapBuilder(self()).repetition(repetition); + } + + public MapBuilder requiredMap() { + return new MapBuilder(self()) + .repetition(Type.Repetition.REQUIRED); + } + + public MapBuilder optionalMap() { + return new MapBuilder(self()) + .repetition(Type.Repetition.OPTIONAL); + } + + public ListBuilder list(Type.Repetition repetition) { + return new ListBuilder(self()).repetition(repetition); + } + + public ListBuilder requiredList() { + return list(Type.Repetition.REQUIRED); + } + + public ListBuilder optionalList() { + return list(Type.Repetition.OPTIONAL); + } + } + + /** + * A builder for {@link GroupType} objects. + * + * @param

The type that this builder will return from + * {@link #named(String)} when the type is built. + */ + public static class GroupBuilder

extends BaseGroupBuilder> { + + private GroupBuilder(P parent) { + super(parent); + } + + private GroupBuilder(Class

returnType) { + super(returnType); + } + + @Override + protected GroupBuilder

self() { + return this; + } + } + + public abstract static class BaseMapBuilder> + extends Builder { + private static final Type STRING_KEY = Types + .required(PrimitiveTypeName.BINARY).as(OriginalType.UTF8).named("key"); + + public static class KeyBuilder> extends + BasePrimitiveBuilder> { + private final M mapBuilder; + + public KeyBuilder(M mapBuilder, PrimitiveTypeName type) { + super(mapBuilder.parent, type); + this.mapBuilder = mapBuilder; + repetition(Type.Repetition.REQUIRED); + } + + public ValueBuilder value(PrimitiveTypeName type, + Type.Repetition repetition) { + mapBuilder.setKeyType(build("key")); + return new ValueBuilder(mapBuilder, type).repetition(repetition); + } + + public ValueBuilder requiredValue(PrimitiveTypeName type) { + return value(type, Type.Repetition.REQUIRED); + } + + public ValueBuilder optionalValue(PrimitiveTypeName type) { + return value(type, Type.Repetition.OPTIONAL); + } + + public GroupValueBuilder groupValue(Type.Repetition repetition) { + mapBuilder.setKeyType(build("key")); + return new GroupValueBuilder(mapBuilder).repetition(repetition); + } + + public GroupValueBuilder requiredGroupValue() { + return groupValue(Type.Repetition.REQUIRED); + } + + public GroupValueBuilder optionalGroupValue() { + return groupValue(Type.Repetition.OPTIONAL); + } + + public MapValueBuilder mapValue(Type.Repetition repetition) { + mapBuilder.setKeyType(build("key")); + return new MapValueBuilder(mapBuilder).repetition(repetition); + } + + public MapValueBuilder requiredMapValue() { + return mapValue(Type.Repetition.REQUIRED); + } + + public MapValueBuilder optionalMapValue() { + return mapValue(Type.Repetition.OPTIONAL); + } + + public ListValueBuilder listValue(Type.Repetition repetition) { + mapBuilder.setKeyType(build("key")); + return new ListValueBuilder(mapBuilder).repetition(repetition); + } + + public ListValueBuilder requiredListValue() { + return listValue(Type.Repetition.REQUIRED); + } + + public ListValueBuilder 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 self() { + return this; + } + } + + public static class ValueBuilder> + extends BasePrimitiveBuilder> { + 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 self() { + return this; + } + } + + public static class GroupKeyBuilder> + extends BaseGroupBuilder> { + private final M mapBuilder; + + public GroupKeyBuilder(M mapBuilder) { + super(mapBuilder.parent); + this.mapBuilder = mapBuilder; + repetition(Type.Repetition.REQUIRED); + } + + @Override + protected GroupKeyBuilder self() { + return this; + } + + public ValueBuilder value(PrimitiveTypeName type, + Type.Repetition repetition) { + mapBuilder.setKeyType(build("key")); + return new ValueBuilder(mapBuilder, type).repetition(repetition); + } + + public ValueBuilder requiredValue(PrimitiveTypeName type) { + return value(type, Type.Repetition.REQUIRED); + } + + public ValueBuilder optionalValue(PrimitiveTypeName type) { + return value(type, Type.Repetition.OPTIONAL); + } + + public GroupValueBuilder groupValue(Type.Repetition repetition) { + mapBuilder.setKeyType(build("key")); + return new GroupValueBuilder(mapBuilder).repetition(repetition); + } + + public GroupValueBuilder requiredGroupValue() { + return groupValue(Type.Repetition.REQUIRED); + } + + public GroupValueBuilder optionalGroupValue() { + return groupValue(Type.Repetition.OPTIONAL); + } + + public MapValueBuilder mapValue(Type.Repetition repetition) { + mapBuilder.setKeyType(build("key")); + return new MapValueBuilder(mapBuilder).repetition(repetition); + } + + public MapValueBuilder requiredMapValue() { + return mapValue(Type.Repetition.REQUIRED); + } + + public MapValueBuilder optionalMapValue() { + return mapValue(Type.Repetition.OPTIONAL); + } + + public ListValueBuilder listValue(Type.Repetition repetition) { + mapBuilder.setKeyType(build("key")); + return new ListValueBuilder(mapBuilder).repetition(repetition); + } + + public ListValueBuilder requiredListValue() { + return listValue(Type.Repetition.REQUIRED); + } + + public ListValueBuilder 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> + extends BaseGroupBuilder> { + 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 self() { + return this; + } + } + + public static class MapValueBuilder> + extends BaseMapBuilder> { + 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 self() { + return this; + } + } + + public static class ListValueBuilder> + extends BaseListBuilder> { + 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 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

returnType) { + super(returnType); + } + + @Override + protected abstract THIS self(); + + public KeyBuilder key(PrimitiveTypeName type) { + return new KeyBuilder(self(), type); + } + + public THIS key(Type type) { + setKeyType(type); + return self(); + } + + public GroupKeyBuilder groupKey() { + return new GroupKeyBuilder(self()); + } + + public ValueBuilder value(PrimitiveTypeName type, + Type.Repetition repetition) { + return new ValueBuilder(self(), type).repetition(repetition); + } + + public ValueBuilder requiredValue(PrimitiveTypeName type) { + return value(type, Type.Repetition.REQUIRED); + } + + public ValueBuilder optionalValue(PrimitiveTypeName type) { + return value(type, Type.Repetition.OPTIONAL); + } + + public GroupValueBuilder groupValue(Type.Repetition repetition) { + return new GroupValueBuilder(self()).repetition(repetition); + } + + public GroupValueBuilder requiredGroupValue() { + return groupValue(Type.Repetition.REQUIRED); + } + + public GroupValueBuilder optionalGroupValue() { + return groupValue(Type.Repetition.OPTIONAL); + } + + public MapValueBuilder mapValue(Type.Repetition repetition) { + return new MapValueBuilder(self()).repetition(repetition); + } + + public MapValueBuilder requiredMapValue() { + return mapValue(Type.Repetition.REQUIRED); + } + + public MapValueBuilder optionalMapValue() { + return mapValue(Type.Repetition.OPTIONAL); + } + + public ListValueBuilder listValue(Type.Repetition repetition) { + return new ListValueBuilder(self()).repetition(repetition); + } + + public ListValueBuilder requiredListValue() { + return listValue(Type.Repetition.REQUIRED); + } + + public ListValueBuilder 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

extends BaseMapBuilder> { + public MapBuilder(P parent) { + super(parent); + } + + private MapBuilder(Class

returnType) { + super(returnType); + } + + @Override + protected MapBuilder

self() { + return this; + } + } + + public abstract static class BaseListBuilder> + extends Builder { + private Type elementType = null; + private P parent; + + public BaseListBuilder(P parent) { + super(parent); + this.parent = parent; + } + + public BaseListBuilder(Class

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> + extends BasePrimitiveBuilder> { + private final BaseListBuilder listBuilder; + + public ElementBuilder(L listBuilder, PrimitiveTypeName type) { + super(((BaseListBuilder) listBuilder).parent, type); + this.listBuilder = listBuilder; + } + + public LP named(String name) { + listBuilder.setElementType(build("element")); + return listBuilder.named(name); + } + + @Override + protected ElementBuilder self() { + return this; + } + } + + public static class GroupElementBuilder> + extends BaseGroupBuilder> { + private final L listBuilder; + + public GroupElementBuilder(L listBuilder) { + super(((BaseListBuilder) listBuilder).parent); + this.listBuilder = listBuilder; + } + + public LP named(String name) { + listBuilder.setElementType(build("element")); + return listBuilder.named(name); + } + + @Override + protected GroupElementBuilder self() { + return this; + } + } + + public static class MapElementBuilder> + extends BaseMapBuilder> { + + private final L listBuilder; + + public MapElementBuilder(L listBuilder) { + super(((BaseListBuilder) listBuilder).parent); + this.listBuilder = listBuilder; + } + + @Override + protected MapElementBuilder self() { + return this; + } + + @Override + public LP named(String name) { + listBuilder.setElementType(build("element")); + return listBuilder.named(name); + } + } + + public static class ListElementBuilder> + extends BaseListBuilder> { + + private final L listBuilder; + + public ListElementBuilder(L listBuilder) { + super(((BaseListBuilder) listBuilder).parent); + this.listBuilder = listBuilder; + } + + @Override + protected ListElementBuilder 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 element(PrimitiveTypeName type, + Type.Repetition repetition) { + return new ElementBuilder(self(), type).repetition(repetition); + } + + public ElementBuilder requiredElement(PrimitiveTypeName type) { + return element(type, Type.Repetition.REQUIRED); + } + + public ElementBuilder optionalElement(PrimitiveTypeName type) { + return element(type, Type.Repetition.OPTIONAL); + } + + public GroupElementBuilder groupElement(Type.Repetition repetition) { + return new GroupElementBuilder(self()).repetition(repetition); + } + + public GroupElementBuilder requiredGroupElement() { + return groupElement(Type.Repetition.REQUIRED); + } + + public GroupElementBuilder optionalGroupElement() { + return groupElement(Type.Repetition.OPTIONAL); + } + + public MapElementBuilder mapElement(Type.Repetition repetition) { + return new MapElementBuilder(self()).repetition(repetition); + } + + public MapElementBuilder requiredMapElement() { + return mapElement(Type.Repetition.REQUIRED); + } + + public MapElementBuilder optionalMapElement() { + return mapElement(Type.Repetition.OPTIONAL); + } + + public ListElementBuilder listElement(Type.Repetition repetition) { + return new ListElementBuilder(self()).repetition(repetition); + } + + public ListElementBuilder requiredListElement() { + return listElement(Type.Repetition.REQUIRED); + } + + public ListElementBuilder optionalListElement() { + return listElement(Type.Repetition.OPTIONAL); + } + + public BaseListBuilder element(Type type) { + setElementType(type); + return self(); + } + } + + public static class ListBuilder

extends BaseListBuilder> { + public ListBuilder(P parent) { + super(parent); + } + + public ListBuilder(Class

returnType) { + super(returnType); + } + + @Override + protected ListBuilder

self() { + return this; + } } public static class MessageTypeBuilder extends GroupBuilder { @@ -588,6 +1266,45 @@ public static MessageTypeBuilder buildMessage() { return new MessageTypeBuilder(); } + public static PrimitiveBuilder primitive(PrimitiveTypeName type, + Type.Repetition repetition) { + return new PrimitiveBuilder(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 required(PrimitiveTypeName type) { + return new PrimitiveBuilder(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 optional(PrimitiveTypeName type) { + return new PrimitiveBuilder(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 repeated(PrimitiveTypeName type) { + return new PrimitiveBuilder(PrimitiveType.class, type) + .repetition(Type.Repetition.REPEATED); + } + public static GroupBuilder buildGroup( Type.Repetition repetition) { return new GroupBuilder(GroupType.class).repetition(repetition); @@ -623,46 +1340,29 @@ public static GroupBuilder repeatedGroup() { .repetition(Type.Repetition.REPEATED); } - public static PrimitiveBuilder primitive( - PrimitiveTypeName type, Type.Repetition repetition) { - return new PrimitiveBuilder(PrimitiveType.class, type) - .repetition(repetition); + + public static MapBuilder map(Type.Repetition repetition) { + return new MapBuilder(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 required( - PrimitiveTypeName type) { - return new PrimitiveBuilder(PrimitiveType.class, type) - .repetition(Type.Repetition.REQUIRED); + public static MapBuilder 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 optional( - PrimitiveTypeName type) { - return new PrimitiveBuilder(PrimitiveType.class, type) - .repetition(Type.Repetition.OPTIONAL); + public static MapBuilder 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 repeated( - PrimitiveTypeName type) { - return new PrimitiveBuilder(PrimitiveType.class, type) - .repetition(Type.Repetition.REPEATED); + public static ListBuilder list(Type.Repetition repetition) { + return new ListBuilder(GroupType.class).repetition(repetition); + } + + public static ListBuilder requiredList() { + return list(Type.Repetition.REQUIRED); + } + + public static ListBuilder optionalList() { + return list(Type.Repetition.OPTIONAL); } } 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 bdf7429e02..4230561e93 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 Type call() throws Exception { }); } + @Test + public void testRequiredMap() { + List typeList = new ArrayList(); + 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 typeList = new ArrayList(); + 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 typeList = new ArrayList(); + 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 typeList = new ArrayList(); + 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 typeList = new ArrayList(); + + List keyFields = new ArrayList(); + keyFields.add(new PrimitiveType(OPTIONAL, INT64, "first")); + keyFields.add(new PrimitiveType(OPTIONAL, DOUBLE, "second")); + typeList.add(new GroupType(REQUIRED, "key", keyFields)); + + List valueFields = new ArrayList(); + 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 typeList = new ArrayList(); + + List keyFields = new ArrayList(); + keyFields.add(new PrimitiveType(OPTIONAL, INT64, "first")); + keyFields.add(new PrimitiveType(OPTIONAL, DOUBLE, "second")); + typeList.add(new GroupType(REQUIRED, "key", keyFields)); + + List valueFields = new ArrayList(); + 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 typeList = new ArrayList(); + + List keyFields = new ArrayList(); + 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 typeList = new ArrayList(); + + List keyFields = new ArrayList(); + 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 typeList = new ArrayList(); + + List keyFields = new ArrayList(); + keyFields.add(new PrimitiveType(OPTIONAL, INT64, "first")); + keyFields.add(new PrimitiveType(OPTIONAL, DOUBLE, "second")); + typeList.add(new PrimitiveType(REQUIRED, INT64, "key")); + + List valueFields = new ArrayList(); + 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 typeList = new ArrayList(); + + typeList.add(new PrimitiveType(REQUIRED, INT64, "key")); + + List valueFields = new ArrayList(); + 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 typeList = new ArrayList(); + + List innerFields = new ArrayList(); + innerFields.add(new PrimitiveType(REQUIRED, FLOAT, "inner_key_1")); + innerFields.add(new PrimitiveType(OPTIONAL, INT32, "inner_key_2")); + + List keyFields = new ArrayList(); + keyFields.add(new PrimitiveType(OPTIONAL, INT64, "first")); + keyFields.add(new GroupType(REQUIRED, "second", innerFields)); + typeList.add(new GroupType(REQUIRED, "key", keyFields)); + + List valueFields = new ArrayList(); + 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 typeList = new ArrayList(); + + 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 typeList = new ArrayList(); + + 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 typeList = new ArrayList(); + + List innerMapTypeList = new ArrayList(); + 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 typeList = new ArrayList(); + + List innerMapTypeList = new ArrayList(); + 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 typeList = new ArrayList(); + + 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 typeList = new ArrayList(); + + 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 typeList = new ArrayList(); + + List innerMapTypeList = new ArrayList(); + 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 typeList = new ArrayList(); + + List innerMapTypeList = new ArrayList(); + 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 typeList = new ArrayList(); + + 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 typeList = new ArrayList(); + + 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 fields = new ArrayList(); + 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 fields = new ArrayList(); + 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 fields = new ArrayList(); + 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 fields = new ArrayList(); + 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 fields = new ArrayList(); + 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 fields = new ArrayList(); + 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 fields = new ArrayList(); + 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 innerFields = new ArrayList(); + innerFields.add(new PrimitiveType(REQUIRED, DOUBLE, "key")); + innerFields.add(new PrimitiveType(REQUIRED, INT32, "value")); + + List fields = new ArrayList(); + 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 innerFields = new ArrayList(); + innerFields.add(new PrimitiveType(REQUIRED, DOUBLE, "key")); + innerFields.add(new PrimitiveType(REQUIRED, INT32, "value")); + + List fields = new ArrayList(); + 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