diff --git a/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/HollowObjectTypeMapper.java b/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/HollowObjectTypeMapper.java index 2189b9c86..0378289cb 100644 --- a/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/HollowObjectTypeMapper.java +++ b/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/HollowObjectTypeMapper.java @@ -698,7 +698,12 @@ public void copy(Object pojo, GenericHollowObject rec) { case ENUM_NAME: String enumNameValue = rec.getString(fieldName); if (enumNameValue != null) { - unsafe.putObject(pojo, fieldOffset, Enum.valueOf((Class) type, enumNameValue)); + try { + Object val = Enum.valueOf((Class) type, enumNameValue); + unsafe.putObject(pojo, fieldOffset, val); + } catch (IllegalArgumentException e) { + // If the enum value is not found, we leave the field as null + } } break; case REFERENCE: @@ -715,7 +720,10 @@ public void copy(Object pojo, GenericHollowObject rec) { private Object parseBoxedWrapper(GenericHollowObject record) { switch (fieldType) { case BOOLEAN: - return Boolean.valueOf(record.getBoolean(fieldName)); + if (record.isNull(fieldName)) { + return null; + } + return record.getBoolean(fieldName); case INT: int intValue = record.getInt(fieldName); if (intValue == Integer.MIN_VALUE) { @@ -767,7 +775,11 @@ private Object parseBoxedWrapper(GenericHollowObject record) { if (enumName == null) { return null; } - return Enum.valueOf((Class) clazz, enumName); + try { + return Enum.valueOf((Class) clazz, enumName); + } catch (IllegalArgumentException e) { + return null; + } case DATE_TIME: { long dateValue = record.getLong(fieldName); if (dateValue == Long.MIN_VALUE) { @@ -783,7 +795,10 @@ private Object parseBoxedWrapper(GenericHollowObject record) { private Object parseBoxedWrapper(FlatRecordTraversalObjectNode record) { switch (fieldType) { case BOOLEAN: - return record.getFieldValueBooleanBoxed(fieldName); + if (record.isFieldNull(fieldName)) { + return null; + } + return record.getFieldValueBoolean(fieldName); case INT: return record.getFieldValueIntBoxed(fieldName); case SHORT: @@ -819,7 +834,11 @@ private Object parseBoxedWrapper(FlatRecordTraversalObjectNode record) { if (enumName == null) { return null; } - return Enum.valueOf((Class) clazz, enumName); + try { + return Enum.valueOf((Class) clazz, enumName); + } catch (IllegalArgumentException e) { + return null; + } case DATE_TIME: { long dateValue = record.getFieldValueLong(fieldName); if (dateValue == Long.MIN_VALUE) { @@ -975,7 +994,12 @@ private void copy(Object obj, FlatRecordTraversalObjectNode node) { case ENUM_NAME: { String value = node.getFieldValueString(fieldName); if (value != null) { - unsafe.putObject(obj, fieldOffset, Enum.valueOf((Class) type, value)); + try { + Object val = Enum.valueOf((Class) type, value); + unsafe.putObject(obj, fieldOffset, val); + } catch (IllegalArgumentException e) { + // If the enum value is not found, we leave the field as null + } } break; } diff --git a/hollow/src/test/java/com/netflix/hollow/core/write/objectmapper/HollowObjectMapperFlatRecordParserTest.java b/hollow/src/test/java/com/netflix/hollow/core/write/objectmapper/HollowObjectMapperFlatRecordParserTest.java index c0ef890e3..f26a4ba20 100644 --- a/hollow/src/test/java/com/netflix/hollow/core/write/objectmapper/HollowObjectMapperFlatRecordParserTest.java +++ b/hollow/src/test/java/com/netflix/hollow/core/write/objectmapper/HollowObjectMapperFlatRecordParserTest.java @@ -28,6 +28,7 @@ public void setUp() { mapper.initializeTypeState(TypeWithCollections.class); mapper.initializeTypeState(VersionedType2.class); mapper.initializeTypeState(SpecialWrapperTypesTest.class); + mapper.initializeTypeState(UnknownEnumTypeTestV2.class); flatRecordWriter = new FlatRecordWriter( mapper.getStateEngine(), new FakeHollowSchemaIdentifierMapper(mapper.getStateEngine())); @@ -76,6 +77,45 @@ public void roundTripPreservesNullability() { Assert.assertEquals(obj, roundTrippedResult); } + @Test + public void unknownEnums() { + HollowObjectMapper readerMapper = new HollowObjectMapper(new HollowWriteStateEngine()); + readerMapper.initializeTypeState(UnknownEnumTypeTestV1.class); + + UnknownEnumTypeTestV2 obj = new UnknownEnumTypeTestV2(); + obj.id = 1; + obj.enumField = AnEnum.SOME_VALUE_C; + + flatRecordWriter.reset(); + mapper.writeFlat(obj, flatRecordWriter); + FlatRecord fr = flatRecordWriter.generateFlatRecord(); + + UnknownEnumTypeTestV1 result = readerMapper.readFlat(fr); + Assert.assertNull(result.enumField); + } + + @Test + public void nullableBoxedWrappers() { + TypeWithAllSimpleTypes obj = new TypeWithAllSimpleTypes(); + // set PK + obj.boxedIntegerField = 1; + obj.stringField = "hello"; + // all inlined values are null + + flatRecordWriter.reset(); + mapper.writeFlat(obj, flatRecordWriter); + FlatRecord fr = flatRecordWriter.generateFlatRecord(); + + TypeWithAllSimpleTypes result = mapper.readFlat(fr); + Assert.assertNull(result.boxedBooleanField); + Assert.assertNull(result.boxedDoubleField); + Assert.assertNull(result.boxedFloatField); + Assert.assertNull(result.boxedLongField); + Assert.assertNull(result.boxedShortField); + Assert.assertNull(result.boxedByteField); + Assert.assertNull(result.boxedCharField); + } + @Test public void testSimpleTypes() { TypeWithAllSimpleTypes typeWithAllSimpleTypes = new TypeWithAllSimpleTypes(); @@ -605,6 +645,11 @@ public static class SubValue { public String anotherValue; } + enum OlderAnEnum { + SOME_VALUE_A, + SOME_VALUE_B, + } + enum AnEnum { SOME_VALUE_A, SOME_VALUE_B, @@ -625,6 +670,22 @@ enum ComplexEnum { } } + @HollowTypeName(name = "UnknownEnumTypeTest") + @HollowPrimaryKey(fields = {"id"}) + static class UnknownEnumTypeTestV1 { + long id; + @HollowTypeName(name = "AnEnum") + OlderAnEnum enumField; + } + + @HollowTypeName(name = "UnknownEnumTypeTest") + @HollowPrimaryKey(fields = {"id"}) + static class UnknownEnumTypeTestV2 { + long id; + @HollowTypeName(name = "AnEnum") + AnEnum enumField; + } + @HollowTypeName(name = "SpecialWrapperTypesTest") @HollowPrimaryKey(fields = {"id"}) static class SpecialWrapperTypesTest {