avro-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From b...@apache.org
Subject svn commit: r1681906 [2/2] - in /avro/trunk: ./ lang/java/avro/src/main/java/org/apache/avro/ lang/java/avro/src/main/java/org/apache/avro/generic/ lang/java/avro/src/main/java/org/apache/avro/reflect/ lang/java/avro/src/test/java/org/apache/avro/ lang...
Date Wed, 27 May 2015 04:48:59 GMT
Added: avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflectLogicalTypes.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflectLogicalTypes.java?rev=1681906&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflectLogicalTypes.java
(added)
+++ avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflectLogicalTypes.java
Wed May 27 04:48:58 2015
@@ -0,0 +1,811 @@
+package org.apache.avro.reflect;
+
+import java.io.File;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+import org.apache.avro.Conversion;
+import org.apache.avro.Conversions;
+import org.apache.avro.LogicalType;
+import org.apache.avro.LogicalTypes;
+import org.apache.avro.Schema;
+import org.apache.avro.SchemaBuilder;
+import org.apache.avro.file.DataFileReader;
+import org.apache.avro.file.DataFileWriter;
+import org.apache.avro.file.FileReader;
+import org.apache.avro.generic.GenericData;
+import org.apache.avro.generic.GenericRecord;
+import org.apache.avro.generic.IndexedRecord;
+import org.apache.avro.io.DatumReader;
+import org.apache.avro.io.DatumWriter;
+import org.apache.avro.specific.SpecificData;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * Tests various logical types
+ * * string => UUID
+ * * fixed and bytes => Decimal
+ * * record => Pair
+ */
+public class TestReflectLogicalTypes {
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  public static final ReflectData REFLECT = new ReflectData();
+
+  @BeforeClass
+  public static void addUUID() {
+    REFLECT.addLogicalTypeConversion(new Conversions.UUIDConversion());
+    REFLECT.addLogicalTypeConversion(new Conversions.DecimalConversion());
+  }
+
+  @Test
+  public void testReflectedSchema() {
+    Schema expected = SchemaBuilder.record(RecordWithUUIDList.class.getName())
+        .fields()
+        .name("uuids").type().array().items().stringType().noDefault()
+        .endRecord();
+    expected.getField("uuids").schema().addProp(
+        SpecificData.CLASS_PROP, List.class.getName());
+    LogicalTypes.uuid().addToSchema(
+        expected.getField("uuids").schema().getElementType());
+
+    Schema actual = REFLECT.getSchema(RecordWithUUIDList.class);
+
+    Assert.assertEquals("Should use the UUID logical type", expected, actual);
+  }
+
+  // this can be static because the schema only comes from reflection
+  public static class DecimalRecordBytes {
+    // scale is required and will not be set by the conversion
+    @AvroSchema("{" +
+        "\"type\": \"bytes\"," +
+        "\"logicalType\": \"decimal\"," +
+        "\"precision\": 9," +
+        "\"scale\": 2" +
+        "}")
+    private BigDecimal decimal;
+
+    @Override
+    public boolean equals(Object other) {
+      if (this == other) {
+        return true;
+      }
+
+      if (other == null || getClass() != other.getClass()) {
+        return false;
+      }
+
+      DecimalRecordBytes that = (DecimalRecordBytes) other;
+      if (decimal == null) {
+        return (that.decimal == null);
+      }
+
+      return decimal.equals(that.decimal);
+    }
+
+    @Override
+    public int hashCode() {
+      return decimal != null ? decimal.hashCode() : 0;
+    }
+  }
+
+  @Test
+  public void testDecimalBytes() throws IOException {
+    Schema schema = REFLECT.getSchema(DecimalRecordBytes.class);
+    Assert.assertEquals("Should have the correct record name",
+        "org.apache.avro.reflect.TestReflectLogicalTypes$",
+        schema.getNamespace());
+    Assert.assertEquals("Should have the correct record name",
+        "DecimalRecordBytes",
+        schema.getName());
+    Assert.assertEquals("Should have the correct logical type",
+        LogicalTypes.decimal(9, 2),
+        LogicalTypes.fromSchema(schema.getField("decimal").schema()));
+
+    DecimalRecordBytes record = new DecimalRecordBytes();
+    record.decimal = new BigDecimal("3.14");
+
+    File test = write(REFLECT, schema, record);
+    Assert.assertEquals("Should match the decimal after round trip",
+        Arrays.asList(record),
+        read(REFLECT.createDatumReader(schema), test));
+  }
+
+  // this can be static because the schema only comes from reflection
+  public static class DecimalRecordFixed {
+    // scale is required and will not be set by the conversion
+    @AvroSchema("{" +
+        "\"name\": \"decimal_9\"," +
+        "\"type\": \"fixed\"," +
+        "\"size\": 4," +
+        "\"logicalType\": \"decimal\"," +
+        "\"precision\": 9," +
+        "\"scale\": 2" +
+        "}")
+    private BigDecimal decimal;
+
+    @Override
+    public boolean equals(Object other) {
+      if (this == other) {
+        return true;
+      }
+
+      if (other == null || getClass() != other.getClass()) {
+        return false;
+      }
+
+      DecimalRecordFixed that = (DecimalRecordFixed) other;
+      if (decimal == null) {
+        return (that.decimal == null);
+      }
+
+      return decimal.equals(that.decimal);
+    }
+
+    @Override
+    public int hashCode() {
+      return decimal != null ? decimal.hashCode() : 0;
+    }
+  }
+
+  @Test
+  public void testDecimalFixed() throws IOException {
+    Schema schema = REFLECT.getSchema(DecimalRecordFixed.class);
+    Assert.assertEquals("Should have the correct record name",
+        "org.apache.avro.reflect.TestReflectLogicalTypes$",
+        schema.getNamespace());
+    Assert.assertEquals("Should have the correct record name",
+        "DecimalRecordFixed",
+        schema.getName());
+    Assert.assertEquals("Should have the correct logical type",
+        LogicalTypes.decimal(9, 2),
+        LogicalTypes.fromSchema(schema.getField("decimal").schema()));
+
+    DecimalRecordFixed record = new DecimalRecordFixed();
+    record.decimal = new BigDecimal("3.14");
+
+    File test = write(REFLECT, schema, record);
+    Assert.assertEquals("Should match the decimal after round trip",
+        Arrays.asList(record),
+        read(REFLECT.createDatumReader(schema), test));
+  }
+
+  public static class Pair<X, Y> {
+    private final X first;
+    private final Y second;
+
+    private Pair(X first, Y second) {
+      this.first = first;
+      this.second = second;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (this == other) {
+        return true;
+      }
+
+      if (other == null || getClass() != other.getClass()) {
+        return false;
+      }
+
+      Pair<?, ?> that = (Pair<?, ?>) other;
+      if (first == null) {
+        if (that.first != null) {
+          return false;
+        }
+      } else if (first.equals(that.first)) {
+        return false;
+      }
+
+      if (second == null) {
+        if (that.second != null) {
+          return false;
+        }
+      } else if (second.equals(that.second)) {
+        return false;
+      }
+
+      return true;
+    }
+
+    @Override
+    public int hashCode() {
+      return Arrays.hashCode(new Object[] {first, second});
+    }
+
+    public static <X, Y> Pair<X, Y> of(X first, Y second) {
+      return new Pair<X, Y>(first, second);
+    }
+  }
+
+  public static class PairRecord {
+    @AvroSchema("{" +
+        "\"name\": \"Pair\"," +
+        "\"type\": \"record\"," +
+        "\"fields\": [" +
+        "    {\"name\": \"x\", \"type\": \"long\"}," +
+        "    {\"name\": \"y\", \"type\": \"long\"}" +
+        "  ]," +
+        "\"logicalType\": \"pair\"" +
+        "}")
+    Pair<Long, Long> pair;
+  }
+
+  @Test
+  @SuppressWarnings("unchecked")
+  public void testPairRecord() throws IOException {
+    ReflectData model = new ReflectData();
+    model.addLogicalTypeConversion(new Conversion<Pair>() {
+      @Override
+      public Class<Pair> getConvertedType() {
+        return Pair.class;
+      }
+
+      @Override
+      public String getLogicalTypeName() {
+        return "pair";
+      }
+
+      @Override
+      public Pair fromRecord(IndexedRecord value, Schema schema, LogicalType type) {
+        return Pair.of(value.get(0), value.get(1));
+      }
+
+      @Override
+      public IndexedRecord toRecord(Pair value, Schema schema, LogicalType type) {
+        GenericData.Record record = new GenericData.Record(schema);
+        record.put(0, value.first);
+        record.put(1, value.second);
+        return record;
+      }
+    });
+
+    LogicalTypes.register("pair", new LogicalTypes.LogicalTypeFactory() {
+      private final LogicalType PAIR = new LogicalType("pair");
+      @Override
+      public LogicalType fromSchema(Schema schema) {
+        return PAIR;
+      }
+    });
+
+    Schema schema = model.getSchema(PairRecord.class);
+    Assert.assertEquals("Should have the correct record name",
+        "org.apache.avro.reflect.TestReflectLogicalTypes$",
+        schema.getNamespace());
+    Assert.assertEquals("Should have the correct record name",
+        "PairRecord",
+        schema.getName());
+    Assert.assertEquals("Should have the correct logical type",
+        "pair",
+        LogicalTypes.fromSchema(schema.getField("pair").schema()).getName());
+
+    PairRecord record = new PairRecord();
+    record.pair = Pair.of(34L, 35L);
+    List<PairRecord> expected = new ArrayList<PairRecord>();
+    expected.add(record);
+
+    File test = write(model, schema, record);
+    Pair<Long, Long> actual = ((PairRecord) TestReflectLogicalTypes.
+        <PairRecord>read(model.createDatumReader(schema), test).get(0)).pair;
+    Assert.assertEquals("Data should match after serialization round-trip",
+        34L, (long) actual.first);
+    Assert.assertEquals("Data should match after serialization round-trip",
+        35L, (long) actual.second);
+  }
+
+  @Test
+  public void testReadUUID() throws IOException {
+    Schema uuidSchema = SchemaBuilder.record(RecordWithUUID.class.getName())
+        .fields().requiredString("uuid").endRecord();
+    LogicalTypes.uuid().addToSchema(uuidSchema.getField("uuid").schema());
+
+    UUID u1 = UUID.randomUUID();
+    UUID u2 = UUID.randomUUID();
+
+    RecordWithStringUUID r1 = new RecordWithStringUUID();
+    r1.uuid = u1.toString();
+    RecordWithStringUUID r2 = new RecordWithStringUUID();
+    r2.uuid = u2.toString();
+
+    List<RecordWithUUID> expected = Arrays.asList(
+        new RecordWithUUID(), new RecordWithUUID());
+    expected.get(0).uuid = u1;
+    expected.get(1).uuid = u2;
+
+    File test = write(
+        ReflectData.get().getSchema(RecordWithStringUUID.class), r1, r2);
+
+    Assert.assertEquals("Should convert Strings to UUIDs",
+        expected, read(REFLECT.createDatumReader(uuidSchema), test));
+
+    // verify that the field's type overrides the logical type
+    Schema uuidStringSchema = SchemaBuilder
+        .record(RecordWithStringUUID.class.getName())
+        .fields().requiredString("uuid").endRecord();
+    LogicalTypes.uuid().addToSchema(uuidStringSchema.getField("uuid").schema());
+
+    Assert.assertEquals("Should not convert to UUID if accessor is String",
+        Arrays.asList(r1, r2),
+        read(REFLECT.createDatumReader(uuidStringSchema), test));
+  }
+
+  @Test
+  public void testWriteUUID() throws IOException {
+    Schema uuidSchema = SchemaBuilder.record(RecordWithUUID.class.getName())
+        .fields().requiredString("uuid").endRecord();
+    LogicalTypes.uuid().addToSchema(uuidSchema.getField("uuid").schema());
+
+    UUID u1 = UUID.randomUUID();
+    UUID u2 = UUID.randomUUID();
+
+    RecordWithUUID r1 = new RecordWithUUID();
+    r1.uuid = u1;
+    RecordWithUUID r2 = new RecordWithUUID();
+    r2.uuid = u2;
+
+    List<RecordWithStringUUID> expected = Arrays.asList(
+        new RecordWithStringUUID(), new RecordWithStringUUID());
+    expected.get(0).uuid = u1.toString();
+    expected.get(1).uuid = u2.toString();
+
+    File test = write(REFLECT, uuidSchema, r1, r2);
+
+    // verify that the field's type overrides the logical type
+    Schema uuidStringSchema = SchemaBuilder
+        .record(RecordWithStringUUID.class.getName())
+        .fields().requiredString("uuid").endRecord();
+
+    Assert.assertEquals("Should read uuid as String without UUID conversion",
+        expected,
+        read(REFLECT.createDatumReader(uuidStringSchema), test));
+
+    LogicalTypes.uuid().addToSchema(uuidStringSchema.getField("uuid").schema());
+    Assert.assertEquals("Should read uuid as String without UUID logical type",
+        expected,
+        read(ReflectData.get().createDatumReader(uuidStringSchema), test));
+  }
+
+  @Test
+  public void testWriteNullableUUID() throws IOException {
+    Schema nullableUuidSchema = SchemaBuilder.record(RecordWithUUID.class.getName())
+        .fields().optionalString("uuid").endRecord();
+    LogicalTypes.uuid().addToSchema(
+        nullableUuidSchema.getField("uuid").schema().getTypes().get(1));
+
+    UUID u1 = UUID.randomUUID();
+    UUID u2 = UUID.randomUUID();
+
+    RecordWithUUID r1 = new RecordWithUUID();
+    r1.uuid = u1;
+    RecordWithUUID r2 = new RecordWithUUID();
+    r2.uuid = u2;
+
+    List<RecordWithStringUUID> expected = Arrays.asList(
+        new RecordWithStringUUID(), new RecordWithStringUUID());
+    expected.get(0).uuid = u1.toString();
+    expected.get(1).uuid = u2.toString();
+
+    File test = write(REFLECT, nullableUuidSchema, r1, r2);
+
+    // verify that the field's type overrides the logical type
+    Schema nullableUuidStringSchema = SchemaBuilder
+        .record(RecordWithStringUUID.class.getName())
+        .fields().optionalString("uuid").endRecord();
+
+    Assert.assertEquals("Should read uuid as String without UUID conversion",
+        expected,
+        read(ReflectData.get().createDatumReader(nullableUuidStringSchema), test));
+  }
+
+  @Test
+  public void testWriteNullableUUIDReadRequiredString() throws IOException {
+    Schema nullableUuidSchema = SchemaBuilder.record(RecordWithUUID.class.getName())
+        .fields().optionalString("uuid").endRecord();
+    LogicalTypes.uuid().addToSchema(
+        nullableUuidSchema.getField("uuid").schema().getTypes().get(1));
+
+    UUID u1 = UUID.randomUUID();
+    UUID u2 = UUID.randomUUID();
+
+    RecordWithUUID r1 = new RecordWithUUID();
+    r1.uuid = u1;
+    RecordWithUUID r2 = new RecordWithUUID();
+    r2.uuid = u2;
+
+    List<RecordWithStringUUID> expected = Arrays.asList(
+        new RecordWithStringUUID(), new RecordWithStringUUID());
+    expected.get(0).uuid = u1.toString();
+    expected.get(1).uuid = u2.toString();
+
+    File test = write(REFLECT, nullableUuidSchema, r1, r2);
+
+    // verify that the field's type overrides the logical type
+    Schema uuidStringSchema = SchemaBuilder
+        .record(RecordWithStringUUID.class.getName())
+        .fields().requiredString("uuid").endRecord();
+
+    Assert.assertEquals("Should read uuid as String without UUID conversion",
+        expected,
+        read(REFLECT.createDatumReader(uuidStringSchema), test));
+  }
+
+  @Test
+  public void testReadUUIDMissingLogicalTypeUnsafe() throws IOException {
+    String unsafeValue = System.getProperty("avro.disable.unsafe");
+    try {
+      // only one FieldAccess can be set per JVM
+      System.clearProperty("avro.disable.unsafe");
+      Assume.assumeTrue(
+          ReflectionUtil.getFieldAccess() instanceof FieldAccessUnsafe);
+
+      Schema uuidSchema = SchemaBuilder.record(RecordWithUUID.class.getName())
+          .fields().requiredString("uuid").endRecord();
+      LogicalTypes.uuid().addToSchema(uuidSchema.getField("uuid").schema());
+
+      UUID u1 = UUID.randomUUID();
+
+      RecordWithStringUUID r1 = new RecordWithStringUUID();
+      r1.uuid = u1.toString();
+
+      File test = write(
+          ReflectData.get().getSchema(RecordWithStringUUID.class), r1);
+
+      RecordWithUUID datum = (RecordWithUUID) read(
+          ReflectData.get().createDatumReader(uuidSchema), test).get(0);
+      Object uuid = datum.uuid;
+      Assert.assertTrue("UUID should be a String (unsafe)",
+          uuid instanceof String);
+    } finally {
+      if (unsafeValue != null) {
+        System.setProperty("avro.disable.unsafe", unsafeValue);
+      }
+    }
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void testReadUUIDMissingLogicalTypeReflect() throws IOException {
+    String unsafeValue = System.getProperty("avro.disable.unsafe");
+    try {
+      // only one FieldAccess can be set per JVM
+      System.setProperty("avro.disable.unsafe", "true");
+      Assume.assumeTrue(
+          ReflectionUtil.getFieldAccess() instanceof FieldAccessReflect);
+
+      Schema uuidSchema = SchemaBuilder.record(RecordWithUUID.class.getName())
+          .fields().requiredString("uuid").endRecord();
+      LogicalTypes.uuid().addToSchema(uuidSchema.getField("uuid").schema());
+
+      UUID u1 = UUID.randomUUID();
+
+      RecordWithStringUUID r1 = new RecordWithStringUUID();
+      r1.uuid = u1.toString();
+
+      File test = write(
+          ReflectData.get().getSchema(RecordWithStringUUID.class), r1);
+
+      read(ReflectData.get().createDatumReader(uuidSchema), test).get(0);
+    } finally {
+      if (unsafeValue != null) {
+        System.setProperty("avro.disable.unsafe", unsafeValue);
+      }
+    }
+  }
+
+  @Test(expected = DataFileWriter.AppendWriteException.class)
+  public void testWriteUUIDMissingLogicalType() throws IOException {
+    Schema uuidSchema = SchemaBuilder.record(RecordWithUUID.class.getName())
+        .fields().requiredString("uuid").endRecord();
+    LogicalTypes.uuid().addToSchema(uuidSchema.getField("uuid").schema());
+
+    UUID u1 = UUID.randomUUID();
+    UUID u2 = UUID.randomUUID();
+
+    RecordWithUUID r1 = new RecordWithUUID();
+    r1.uuid = u1;
+    RecordWithUUID r2 = new RecordWithUUID();
+    r2.uuid = u2;
+
+    // write without using REFLECT, which has the logical type
+    File test = write(uuidSchema, r1, r2);
+
+    // verify that the field's type overrides the logical type
+    Schema uuidStringSchema = SchemaBuilder
+        .record(RecordWithStringUUID.class.getName())
+        .fields().requiredString("uuid").endRecord();
+
+    // this fails with an AppendWriteException wrapping ClassCastException
+    // because the UUID isn't converted to a CharSequence expected internally
+    read(ReflectData.get().createDatumReader(uuidStringSchema), test);
+  }
+
+  @Test
+  public void testReadUUIDGenericRecord() throws IOException {
+    Schema uuidSchema = SchemaBuilder.record("RecordWithUUID")
+        .fields().requiredString("uuid").endRecord();
+    LogicalTypes.uuid().addToSchema(uuidSchema.getField("uuid").schema());
+
+    UUID u1 = UUID.randomUUID();
+    UUID u2 = UUID.randomUUID();
+
+    RecordWithStringUUID r1 = new RecordWithStringUUID();
+    r1.uuid = u1.toString();
+    RecordWithStringUUID r2 = new RecordWithStringUUID();
+    r2.uuid = u2.toString();
+
+    List<GenericData.Record> expected = Arrays.asList(
+        new GenericData.Record(uuidSchema), new GenericData.Record(uuidSchema));
+    expected.get(0).put("uuid", u1);
+    expected.get(1).put("uuid", u2);
+
+    File test = write(
+        ReflectData.get().getSchema(RecordWithStringUUID.class), r1, r2);
+
+    Assert.assertEquals("Should convert Strings to UUIDs",
+        expected, read(REFLECT.createDatumReader(uuidSchema), test));
+
+    // verify that the field's type overrides the logical type
+    Schema uuidStringSchema = SchemaBuilder
+        .record(RecordWithStringUUID.class.getName())
+        .fields().requiredString("uuid").endRecord();
+    LogicalTypes.uuid().addToSchema(uuidSchema.getField("uuid").schema());
+
+    Assert.assertEquals("Should not convert to UUID if accessor is String",
+        Arrays.asList(r1, r2),
+        read(REFLECT.createDatumReader(uuidStringSchema), test));
+  }
+
+  @Test
+  public void testReadUUIDArray() throws IOException {
+    Schema uuidArraySchema = SchemaBuilder.record(RecordWithUUIDArray.class.getName())
+        .fields()
+        .name("uuids").type().array().items().stringType().noDefault()
+        .endRecord();
+    LogicalTypes.uuid().addToSchema(
+        uuidArraySchema.getField("uuids").schema().getElementType());
+
+    UUID u1 = UUID.randomUUID();
+    UUID u2 = UUID.randomUUID();
+
+    GenericRecord r = new GenericData.Record(uuidArraySchema);
+    r.put("uuids", Arrays.asList(u1.toString(), u2.toString()));
+
+    RecordWithUUIDArray expected = new RecordWithUUIDArray();
+    expected.uuids = new UUID[] {u1, u2};
+
+    File test = write(uuidArraySchema, r);
+
+    Assert.assertEquals("Should convert Strings to UUIDs",
+        expected,
+        read(REFLECT.createDatumReader(uuidArraySchema), test).get(0));
+  }
+
+  @Test
+  public void testWriteUUIDArray() throws IOException {
+    Schema uuidArraySchema = SchemaBuilder.record(RecordWithUUIDArray.class.getName())
+        .fields()
+        .name("uuids").type().array().items().stringType().noDefault()
+        .endRecord();
+    LogicalTypes.uuid().addToSchema(
+        uuidArraySchema.getField("uuids").schema().getElementType());
+
+    Schema stringArraySchema = SchemaBuilder.record("RecordWithUUIDArray")
+        .fields()
+        .name("uuids").type().array().items().stringType().noDefault()
+        .endRecord();
+    stringArraySchema.getField("uuids").schema()
+        .addProp(SpecificData.CLASS_PROP, List.class.getName());
+
+    UUID u1 = UUID.randomUUID();
+    UUID u2 = UUID.randomUUID();
+
+    GenericRecord expected = new GenericData.Record(stringArraySchema);
+    List<String> uuids = new ArrayList<String>();
+    uuids.add(u1.toString());
+    uuids.add(u2.toString());
+    expected.put("uuids", uuids);
+
+    RecordWithUUIDArray r = new RecordWithUUIDArray();
+    r.uuids = new UUID[] {u1, u2};
+
+    File test = write(REFLECT, uuidArraySchema, r);
+
+    Assert.assertEquals("Should read UUIDs as Strings",
+        expected,
+        read(ReflectData.get().createDatumReader(stringArraySchema), test).get(0));
+  }
+
+  @Test
+  public void testReadUUIDList() throws IOException {
+    Schema uuidListSchema = SchemaBuilder.record(RecordWithUUIDList.class.getName())
+        .fields()
+        .name("uuids").type().array().items().stringType().noDefault()
+        .endRecord();
+    uuidListSchema.getField("uuids").schema().addProp(
+        SpecificData.CLASS_PROP, List.class.getName());
+    LogicalTypes.uuid().addToSchema(
+        uuidListSchema.getField("uuids").schema().getElementType());
+
+    UUID u1 = UUID.randomUUID();
+    UUID u2 = UUID.randomUUID();
+
+    GenericRecord r = new GenericData.Record(uuidListSchema);
+    r.put("uuids", Arrays.asList(u1.toString(), u2.toString()));
+
+    RecordWithUUIDList expected = new RecordWithUUIDList();
+    expected.uuids = Arrays.asList(u1, u2);
+
+    File test = write(uuidListSchema, r);
+
+    Assert.assertEquals("Should convert Strings to UUIDs",
+        expected, read(REFLECT.createDatumReader(uuidListSchema), test).get(0));
+  }
+
+  @Test
+  public void testWriteUUIDList() throws IOException {
+    Schema uuidListSchema = SchemaBuilder.record(RecordWithUUIDList.class.getName())
+        .fields()
+        .name("uuids").type().array().items().stringType().noDefault()
+        .endRecord();
+    uuidListSchema.getField("uuids").schema().addProp(
+        SpecificData.CLASS_PROP, List.class.getName());
+    LogicalTypes.uuid().addToSchema(
+        uuidListSchema.getField("uuids").schema().getElementType());
+
+    Schema stringArraySchema = SchemaBuilder.record("RecordWithUUIDArray")
+        .fields()
+        .name("uuids").type().array().items().stringType().noDefault()
+        .endRecord();
+    stringArraySchema.getField("uuids").schema()
+        .addProp(SpecificData.CLASS_PROP, List.class.getName());
+
+    UUID u1 = UUID.randomUUID();
+    UUID u2 = UUID.randomUUID();
+
+    GenericRecord expected = new GenericData.Record(stringArraySchema);
+    expected.put("uuids", Arrays.asList(u1.toString(), u2.toString()));
+
+    RecordWithUUIDList r = new RecordWithUUIDList();
+    r.uuids = Arrays.asList(u1, u2);
+
+    File test = write(REFLECT, uuidListSchema, r);
+
+    Assert.assertEquals("Should read UUIDs as Strings",
+        expected,
+        read(REFLECT.createDatumReader(stringArraySchema), test).get(0));
+  }
+
+  private static <D> List<D> read(DatumReader<D> reader, File file) throws
IOException {
+    List<D> data = new ArrayList<D>();
+    FileReader<D> fileReader = null;
+
+    try {
+      fileReader = new DataFileReader<D>(file, reader);
+      for (D datum : fileReader) {
+        data.add(datum);
+      }
+    } finally {
+      if (fileReader != null) {
+        fileReader.close();
+      }
+    }
+
+    return data;
+  }
+
+  private <D> File write(Schema schema, D... data) throws IOException {
+    return write(ReflectData.get(), schema, data);
+  }
+
+  @SuppressWarnings("unchecked")
+  private <D> File write(GenericData model, Schema schema, D... data) throws IOException
{
+    File file = temp.newFile();
+    DatumWriter<D> writer = model.createDatumWriter(schema);
+    DataFileWriter<D> fileWriter = new DataFileWriter<D>(writer);
+
+    try {
+      fileWriter.create(schema, file);
+      for (D datum : data) {
+        fileWriter.append(datum);
+      }
+    } finally {
+      fileWriter.close();
+    }
+
+    return file;
+  }
+}
+
+class RecordWithUUID {
+  UUID uuid;
+
+  @Override
+  public int hashCode() {
+    return uuid.hashCode();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null) {
+      return false;
+    }
+    if (!(obj instanceof RecordWithUUID)) {
+      return false;
+    }
+    RecordWithUUID that = (RecordWithUUID) obj;
+    return this.uuid.equals(that.uuid);
+  }
+}
+
+class RecordWithStringUUID {
+  String uuid;
+
+  @Override
+  public int hashCode() {
+    return uuid.hashCode();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null) {
+      return false;
+    }
+    if (!(obj instanceof RecordWithStringUUID)) {
+      return false;
+    }
+    RecordWithStringUUID that = (RecordWithStringUUID) obj;
+    return this.uuid.equals(that.uuid);
+  }
+}
+
+class RecordWithUUIDArray {
+  UUID[] uuids;
+
+  @Override
+  public int hashCode() {
+    return Arrays.hashCode(uuids);
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null) {
+      return false;
+    }
+    if (!(obj instanceof RecordWithUUIDArray)) {
+      return false;
+    }
+    RecordWithUUIDArray that = (RecordWithUUIDArray) obj;
+    return Arrays.equals(this.uuids, that.uuids);
+  }
+}
+
+class RecordWithUUIDList {
+  List<UUID> uuids;
+
+  @Override
+  public int hashCode() {
+    return uuids.hashCode();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null) {
+      return false;
+    }
+    if (!(obj instanceof RecordWithUUIDList)) {
+      return false;
+    }
+    RecordWithUUIDList that = (RecordWithUUIDList) obj;
+    return this.uuids.equals(that.uuids);
+  }
+}
+



Mime
View raw message