EntrySerializer(String fieldId, Type type, BinarySerializerRepository serializerRepository) {
   this.entryId = fieldId;
   this.type = type;
   this.metaData = ObjectUtil.checkNotNull(serializerRepository.getMetaData(type.getMetaDataId()));
   this.serializerRepository = serializerRepository;
 }
  @CheckForNull
  private GenericValue deserializeValue(
      ByteReader source, @CheckForNull Type declaredType, @CheckForNull MetaData declaredMetaData) {
    boolean optional = (declaredType == null || declaredType.isOptional());
    MetaData actualMetaData = null;
    if (declaredMetaData == null || declaredMetaData.getMetaType().isPolymorphic()) {
      if ((declaredMetaData != null) && (declaredMetaData.getMetaType() == MetaType.ENTITY)) {
        actualMetaData = BootStrap.ENTITY_ID;
      } else {
        // Polymorphic type: Read actual type or 'null'-type.
        EntityId actualTypeId = serializerRepository.getEntityIdSerializer().deserialize(source);
        if (!BootStrap.ID_NULL_REFERENCE.equals(actualTypeId)) {
          actualMetaData = serializerRepository.getMetaData(actualTypeId);
        }
      }
    } else {
      // Non-polymorphic type: Do not read actual type.
      if (!optional || source.getVarInt() != 0) {
        actualMetaData = declaredMetaData;
      }
    }

    if (actualMetaData == null) {
      if (!optional) {
        throw new SerializerException("Non-optional entry found to be null!");
      }
      return null;
    }

    switch (actualMetaData.getMetaType()) {
      case ENTITY:
        {
          EntityId entityId = serializerRepository.getEntityIdSerializer().deserialize(source);
          return BootStrap.ID_NULL_REFERENCE.equals(entityId) ? null : new EntityIdValue(entityId);
        }
      case PRIMITIVE:
        {
          Primitive primitive = Primitive.byEntityId(actualMetaData.getEntityId());
          BinarySerializer<?> serializer =
              serializerRepository.getPrimitiveSerializer(actualMetaData.getEntityId());
          return new PrimitiveValue(primitive, serializer.deserialize(source));
        }
      case VALUE_OBJECT:
        {
          BinarySerializer<ObjectInfo> serializer =
              serializerRepository.getBinarySerializer(actualMetaData.getEntityId());
          return new ValueObjectValue(serializer.deserialize(source));
        }
      case COLLECTION:
        {
          // Read element MetaDataId.
          EntityId elementMetaDataId =
              serializerRepository.getEntityIdSerializer().deserialize(source);
          // Read element count.
          int count = MathUtil.longToInt(source.getVarInt());
          // Read custom collection implementation data.
          BinarySerializer<ObjectInfo> serializer =
              serializerRepository.getBinarySerializer(actualMetaData.getEntityId());
          ObjectInfo collectionInfo = serializer.deserialize(source);
          // Read all elements.
          ImmutableArrayList.Builder<GenericValue> builder = ImmutableArrayList.newBuilder(count);
          for (int i = 0; i < count; ++i) {
            Type elementType = (declaredType == null) ? null : declaredType.getElementType();
            MetaData metaData =
                (elementType == null)
                    ? null
                    : serializerRepository.getMetaData(elementType.getMetaDataId());
            GenericValue value = deserializeValue(source, elementType, metaData);
            builder.add((value == null) ? GenericValue.nullValue() : value);
          }
          return new CollectionValue(collectionInfo, elementMetaDataId, builder.build());
        }
      default:
        throw new IllegalStateException("Unknown MetaType " + actualMetaData.getMetaType());
    }
  }