MetaDataEvolutionValidator { @SuppressWarnings("PMD.CompareObjectsWithEquals") public void validateUnion(@Nonnull Descriptor oldUnionDescriptor, @Nonnull Descriptor newUnionDescriptor) { if (oldUnionDescriptor == newUnionDescriptor) { return; } final BiMap<Descriptor, Descriptor> updatedDescriptors = HashBiMap.create(oldUnionDescriptor.getFields().size()); final Set<Pair<Descriptor, Descriptor>> seenDescriptors = new HashSet<>(); for (FieldDescriptor oldUnionField : oldUnionDescriptor.getFields()) { if (!oldUnionField.getType().equals(FieldDescriptor.Type.MESSAGE)) { throw new MetaDataException("field in union is not a message type", LogMessageKeys.FIELD_NAME, oldUnionField.getName()); } int fieldNumber = oldUnionField.getNumber(); FieldDescriptor newUnionField = newUnionDescriptor.findFieldByNumber(fieldNumber); if (newUnionField != null) { if (!newUnionField.getType().equals(FieldDescriptor.Type.MESSAGE)) { throw new MetaDataException("field in new union is not a message type", LogMessageKeys.FIELD_NAME, newUnionField.getName()); } Descriptor oldRecord = oldUnionField.getMessageType(); Descriptor newRecord = newUnionField.getMessageType(); Descriptor alreadySeenNewRecord = updatedDescriptors.get(oldRecord); if (alreadySeenNewRecord != null) { if (alreadySeenNewRecord != newRecord) { throw new MetaDataException("record type corresponds to multiple types in new meta-data", LogMessageKeys.OLD_RECORD_TYPE, oldRecord.getName(), LogMessageKeys.NEW_RECORD_TYPE, newRecord.getName() + " & " + alreadySeenNewRecord.getName()); } } else { if (updatedDescriptors.containsValue(newRecord)) { final Descriptor alreadySeenOldRecord = updatedDescriptors.inverse().get(newRecord); throw new MetaDataException("record type corresponds to multiple types in old meta-data", LogMessageKeys.OLD_RECORD_TYPE, oldRecord.getName() + " & " + alreadySeenOldRecord.getName(), LogMessageKeys.NEW_RECORD_TYPE, newRecord.getName()); } } updatedDescriptors.put(oldRecord, newRecord); validateMessage(oldRecord, newRecord, seenDescriptors); } else { throw new MetaDataException("record type removed from union", LogMessageKeys.RECORD_TYPE, oldUnionField.getMessageType()); } } } private  MetaDataEvolutionValidator(); private  MetaDataEvolutionValidator(@Nonnull Builder builder); void validate(@Nonnull RecordMetaData oldMetaData, @Nonnull RecordMetaData newMetaData); @SuppressWarnings("PMD.CompareObjectsWithEquals") void validateUnion(@Nonnull Descriptor oldUnionDescriptor, @Nonnull Descriptor newUnionDescriptor); @Nonnull IndexMaintainerRegistry getIndexMaintainerRegistry(); boolean allowsNoVersionChange(); boolean allowsNoSinceVersion(); boolean allowsIndexRebuilds(); boolean allowsMissingFormerIndexNames(); boolean allowsOlderFormerIndexAddedVersions(); boolean allowsUnsplitToSplit(); boolean disallowsTypeRenames(); @Nonnull Builder asBuilder(); @Nonnull static Builder newBuilder(); @Nonnull static MetaDataEvolutionValidator getDefaultInstance();  }
@Test public void selfReferenceChanged() { final Descriptor selfReferenceUnion = TestSelfReferenceProto.RecordTypeUnion.getDescriptor(); final Descriptor unspooledUnion = TestSelfReferenceUnspooledProto.RecordTypeUnion.getDescriptor(); validator.validateUnion(selfReferenceUnion, unspooledUnion); assertInvalid("field removed", unspooledUnion, selfReferenceUnion); FileDescriptor updatedUnspooledFile = mutateFile(TestSelfReferenceUnspooledProto.getDescriptor(), fileBuilder -> fileBuilder.getMessageTypeBuilderList().forEach(message -> { if (message.getName().equals("Node")) { message.removeField(0); } }) ); assertNull(updatedUnspooledFile.findMessageTypeByName("Node").findFieldByName("rec_no")); assertInvalid("field removed", TestSelfReferenceUnspooledProto.getDescriptor(), updatedUnspooledFile); }
@Test public void nestedTypesMerged() { validator.validateUnion(TestUnmergedNestedTypesProto.RecordTypeUnion.getDescriptor(), TestMergedNestedTypesProto.RecordTypeUnion.getDescriptor()); FileDescriptor updatedMergedFile = mutateField("OneTrueNested", "b", TestMergedNestedTypesProto.getDescriptor(), field -> field.setName("c")); assertInvalid("field renamed", TestUnmergedNestedTypesProto.getDescriptor(), updatedMergedFile); }
@Test public void nestedTypesSplit() { validator.validateUnion(TestMergedNestedTypesProto.RecordTypeUnion.getDescriptor(), TestSplitNestedTypesProto.RecordTypeUnion.getDescriptor()); FileDescriptor updatedSplitFile = mutateField("NestedB", "b", TestSplitNestedTypesProto.getDescriptor(), field -> field.setType(DescriptorProtos.FieldDescriptorProto.Type.TYPE_BYTES)); assertInvalid("field type changed", TestUnmergedNestedTypesProto.getDescriptor(), updatedSplitFile); }