Exemple #1
0
  public static List<ErrorLogger.ErrorObject> conformVirtualTracksInCPL(
      PayloadRecord cplPayloadRecord,
      List<PayloadRecord> essencesHeaderPartitionPayloads,
      boolean conformAllVirtualTracks)
      throws IOException {

    IMFErrorLogger imfErrorLogger = new IMFErrorLoggerImpl();
    List<PayloadRecord> essencesHeaderPartition =
        Collections.unmodifiableList(essencesHeaderPartitionPayloads);

    try {
      imfErrorLogger.addAllErrors(validateCPL(cplPayloadRecord));
      if (imfErrorLogger.hasFatalErrors())
        return Collections.unmodifiableList(imfErrorLogger.getErrors());

      Composition composition =
          new Composition(new ByteArrayByteRangeProvider(cplPayloadRecord.getPayload()));

      imfErrorLogger.addAllErrors(validateIMFTrackFileHeaderMetadata(essencesHeaderPartition));

      List<HeaderPartitionTuple> headerPartitionTuples = new ArrayList<>();
      for (PayloadRecord payloadRecord : essencesHeaderPartition) {
        if (payloadRecord.getPayloadAssetType()
            != PayloadRecord.PayloadAssetType.EssencePartition) {
          imfErrorLogger.addError(
              IMFErrorLogger.IMFErrors.ErrorCodes.IMF_MASTER_PACKAGE_ERROR,
              IMFErrorLogger.IMFErrors.ErrorLevels.FATAL,
              String.format(
                  "Payload asset type is %s, expected asset type %s",
                  payloadRecord.getPayloadAssetType(),
                  PayloadRecord.PayloadAssetType.EssencePartition.toString()));
          continue;
        }
        headerPartitionTuples.add(
            new HeaderPartitionTuple(
                new HeaderPartition(
                    new ByteArrayDataProvider(payloadRecord.getPayload()),
                    0L,
                    (long) payloadRecord.getPayload().length,
                    imfErrorLogger),
                new ByteArrayByteRangeProvider(payloadRecord.getPayload())));
      }

      if (imfErrorLogger.hasFatalErrors()) {
        return imfErrorLogger.getErrors();
      }

      imfErrorLogger.addAllErrors(
          composition.conformVirtualTracksInComposition(
              Collections.unmodifiableList(headerPartitionTuples), conformAllVirtualTracks));

      imfErrorLogger.addAllErrors(composition.getErrors());
    } catch (IMFException e) {
      imfErrorLogger.addAllErrors(e.getErrors());
    }

    return imfErrorLogger.getErrors();
  }
Exemple #2
0
  /**
   * A stateless method that validates an IMFEssenceComponent's header partition and verifies MXF
   * OP1A and IMF compliance. This could be utilized to perform preliminary validation of IMF
   * essences
   *
   * @param essencesHeaderPartitionPayloads - a list of IMF Essence Component header partition
   *     payloads
   * @return a list of errors encountered while performing compliance checks on the IMF Essence
   *     Component Header partition
   * @throws IOException - any I/O related error is exposed through an IOException
   */
  public static List<ErrorLogger.ErrorObject> validateIMFTrackFileHeaderMetadata(
      List<PayloadRecord> essencesHeaderPartitionPayloads) throws IOException {
    IMFErrorLogger imfErrorLogger = new IMFErrorLoggerImpl();
    List<PayloadRecord> essencesHeaderPartition =
        Collections.unmodifiableList(essencesHeaderPartitionPayloads);
    for (PayloadRecord payloadRecord : essencesHeaderPartition) {
      if (payloadRecord.getPayloadAssetType() != PayloadRecord.PayloadAssetType.EssencePartition) {
        imfErrorLogger.addError(
            IMFErrorLogger.IMFErrors.ErrorCodes.IMP_VALIDATOR_PAYLOAD_ERROR,
            IMFErrorLogger.IMFErrors.ErrorLevels.FATAL,
            String.format(
                "Payload asset type is %s, expected asset type %s",
                payloadRecord.getPayloadAssetType(),
                PayloadRecord.PayloadAssetType.EssencePartition.toString()));
        continue;
      }
      HeaderPartition headerPartition = null;
      try {
        headerPartition =
            new HeaderPartition(
                new ByteArrayDataProvider(payloadRecord.getPayload()),
                0L,
                (long) payloadRecord.getPayload().length,
                imfErrorLogger);

        MXFOperationalPattern1A.HeaderPartitionOP1A headerPartitionOP1A =
            MXFOperationalPattern1A.checkOperationalPattern1ACompliance(
                headerPartition, imfErrorLogger);
        IMFConstraints.checkIMFCompliance(headerPartitionOP1A, imfErrorLogger);
      } catch (IMFException | MXFException e) {
        if (headerPartition != null) {
          Preface preface = headerPartition.getPreface();
          GenericPackage genericPackage =
              preface.getContentStorage().getEssenceContainerDataList().get(0).getLinkedPackage();
          SourcePackage filePackage = (SourcePackage) genericPackage;
          UUID packageUUID = filePackage.getPackageMaterialNumberasUUID();
          imfErrorLogger.addError(
              new ErrorLogger.ErrorObject(
                  IMFErrorLogger.IMFErrors.ErrorCodes.IMF_ESSENCE_COMPONENT_ERROR,
                  IMFErrorLogger.IMFErrors.ErrorLevels.FATAL,
                  String.format(
                      "IMFTrackFile with ID %s has fatal errors", packageUUID.toString())));
        }
        if (e instanceof IMFException) {
          IMFException imfException = (IMFException) e;
          imfErrorLogger.addAllErrors(imfException.getErrors());
        } else if (e instanceof MXFException) {
          MXFException mxfException = (MXFException) e;
          imfErrorLogger.addAllErrors(mxfException.getErrors());
        }
      }
    }
    return imfErrorLogger.getErrors();
  }
Exemple #3
0
  /**
   * A stateless method that returns the RFC-5646 Spoken Language Tag present in the Header
   * Partition of an Audio Essence
   *
   * @param essencesHeaderPartition - a list of payloads corresponding to the Header Partitions of
   *     TrackFiles that are a part of an Audio VirtualTrack
   * @param audioVirtualTrack - the audio virtual track whose spoken language needs to be
   *     ascertained
   * @return string corresponding to the RFC-5646 language tag present in the header partition of
   *     the Audio Essence
   * @throws IOException - any I/O related error is exposed through an IOException
   */
  @Nullable
  public static String getAudioTrackSpokenLanguage(
      VirtualTrack audioVirtualTrack, List<PayloadRecord> essencesHeaderPartition)
      throws IOException {
    IMFErrorLogger imfErrorLogger = new IMFErrorLoggerImpl();
    if (audioVirtualTrack.getSequenceTypeEnum() != Composition.SequenceTypeEnum.MainAudioSequence) {
      throw new IMFException(
          String.format(
              "Virtual track that was passed in is of type %s, spoken language is "
                  + "currently supported for only %s tracks",
              audioVirtualTrack.getSequenceTypeEnum().toString(),
              Composition.SequenceTypeEnum.MainAudioSequence.toString()));
    }
    List<VirtualTrack> virtualTracks = new ArrayList<>();
    virtualTracks.add(audioVirtualTrack);
    imfErrorLogger.addAllErrors(
        checkVirtualTrackAndEssencesHeaderPartitionPayloadRecords(
            virtualTracks, essencesHeaderPartition));
    if (imfErrorLogger.hasFatalErrors()) {
      throw new IMFException(
          String.format(
              "Fatal Errors were detected when trying to verify the Virtual Track and Essence Header Partition payloads %s",
              Utilities.serializeObjectCollectionToString(imfErrorLogger.getErrors())));
    }
    Set<String> audioLanguageSet = new HashSet<>();
    for (PayloadRecord payloadRecord : essencesHeaderPartition) {
      if (payloadRecord.getPayloadAssetType() != PayloadRecord.PayloadAssetType.EssencePartition) {
        throw new IMFException(
            String.format(
                "Payload asset type is %s, expected asset type %s",
                payloadRecord.getPayloadAssetType(),
                PayloadRecord.PayloadAssetType.EssencePartition.toString()),
            imfErrorLogger);
      }
      HeaderPartition headerPartition =
          new HeaderPartition(
              new ByteArrayDataProvider(payloadRecord.getPayload()),
              0L,
              (long) payloadRecord.getPayload().length,
              imfErrorLogger);
      audioLanguageSet.add(headerPartition.getAudioEssenceSpokenLanguage());
    }

    if (audioLanguageSet.size() > 1) {
      throw new IMFException(
          String.format(
              "It seems that RFC-5646 spoken language is not consistent across "
                  + "resources of this Audio Virtual Track, found references to %s languages in the HeaderPartition",
              Utilities.serializeObjectCollectionToString(audioLanguageSet)),
          imfErrorLogger);
    }
    return audioLanguageSet.iterator().next();
  }
Exemple #4
0
 /**
  * A stateless method that will return the size of the RandomIndexPack present within a MXF file.
  * In a typical IMF workflow this would be the first method that would need to be invoked to
  * perform IMF essence component level validation
  *
  * @param essenceFooter4Bytes - the last 4 bytes of the MXF file used to infer the size of the
  *     RandomIndexPack
  * @return a long integer value representing the size of the RandomIndexPack
  */
 public static Long getRandomIndexPackSize(PayloadRecord essenceFooter4Bytes) {
   IMFErrorLogger imfErrorLogger = new IMFErrorLoggerImpl();
   if (essenceFooter4Bytes.getPayloadAssetType()
       != PayloadRecord.PayloadAssetType.EssenceFooter4Bytes) {
     throw new IMFException(
         String.format(
             "Payload asset type is %s, expected asset type %s",
             essenceFooter4Bytes.getPayloadAssetType(),
             PayloadRecord.PayloadAssetType.EssenceFooter4Bytes.toString()),
         imfErrorLogger);
   }
   return (long) (ByteBuffer.wrap(essenceFooter4Bytes.getPayload()).getInt());
 }
Exemple #5
0
 /**
  * A stateless method that will validate an IMF AssetMap document
  *
  * @param assetMapPayload - a payload record for an AssetMap document
  * @return list of error messages encountered while validating an AssetMap document
  * @throws IOException - any I/O related error is exposed through an IOException
  */
 public static List<ErrorLogger.ErrorObject> validateAssetMap(PayloadRecord assetMapPayload)
     throws IOException {
   if (assetMapPayload.getPayloadAssetType() != PayloadRecord.PayloadAssetType.AssetMap) {
     throw new IMFException(
         String.format(
             "Payload asset type is %s, expected asset type %s",
             assetMapPayload.getPayloadAssetType(),
             PayloadRecord.PayloadAssetType.AssetMap.toString()));
   }
   try {
     AssetMap assetMap =
         new AssetMap(new ByteArrayByteRangeProvider(assetMapPayload.getPayload()));
     return assetMap.getErrors();
   } catch (IMFException e) {
     return e.getErrors();
   }
 }
Exemple #6
0
  /**
   * A stateless method that will validate an IMF PackingList document
   *
   * @param pkl - a payload record for a Packing List document
   * @return list of error messages encountered while validating a Packing List document
   * @throws IOException - any I/O related error is exposed through an IOException
   */
  public static List<ErrorLogger.ErrorObject> validatePKL(PayloadRecord pkl) throws IOException {
    IMFErrorLogger imfErrorLogger = new IMFErrorLoggerImpl();

    if (pkl.getPayloadAssetType() != PayloadRecord.PayloadAssetType.PackingList) {
      throw new IMFException(
          String.format(
              "Payload asset type is %s, expected asset type %s",
              pkl.getPayloadAssetType(), PayloadRecord.PayloadAssetType.PackingList.toString()),
          imfErrorLogger);
    }
    try {
      PackingList packingList = new PackingList(new ByteArrayByteRangeProvider(pkl.getPayload()));
      imfErrorLogger.addAllErrors(packingList.getErrors());
    } catch (IMFException e) {
      imfErrorLogger.addAllErrors(e.getErrors());
    }
    return imfErrorLogger.getErrors();
  }
Exemple #7
0
  /**
   * A stateless method that will validate an IMF Composition document
   *
   * @param cpl - a payload record for a Composition document
   * @return list of error messages encountered while validating an AssetMap document
   * @throws IOException - any I/O related error is exposed through an IOException
   */
  public static List<ErrorLogger.ErrorObject> validateCPL(PayloadRecord cpl) throws IOException {
    IMFErrorLogger imfErrorLogger = new IMFErrorLoggerImpl();
    if (cpl.getPayloadAssetType() != PayloadRecord.PayloadAssetType.CompositionPlaylist) {
      throw new IMFException(
          String.format(
              "Payload asset type is %s, expected asset type %s",
              cpl.getPayloadAssetType(),
              PayloadRecord.PayloadAssetType.CompositionPlaylist.toString()));
    }

    try {
      Composition composition = new Composition(new ByteArrayByteRangeProvider(cpl.getPayload()));
      imfErrorLogger.addAllErrors(composition.getErrors());
    } catch (IMFException e) {
      imfErrorLogger.addAllErrors(e.getErrors());
    }
    return imfErrorLogger.getErrors();
  }
Exemple #8
0
  private static List<ErrorLogger.ErrorObject>
      checkVirtualTrackAndEssencesHeaderPartitionPayloadRecords(
          List<VirtualTrack> virtualTracks, List<PayloadRecord> essencesHeaderPartition)
          throws IOException {
    IMFErrorLogger imfErrorLogger = new IMFErrorLoggerImpl();
    Set<UUID> trackFileIDsSet = new HashSet<>();

    for (PayloadRecord payloadRecord : essencesHeaderPartition) {
      if (payloadRecord.getPayloadAssetType() != PayloadRecord.PayloadAssetType.EssencePartition) {
        throw new IMFException(
            String.format(
                "Payload asset type is %s, expected asset type %s",
                payloadRecord.getPayloadAssetType(),
                PayloadRecord.PayloadAssetType.EssencePartition.toString()),
            imfErrorLogger);
      }
      HeaderPartition headerPartition =
          new HeaderPartition(
              new ByteArrayDataProvider(payloadRecord.getPayload()),
              0L,
              (long) payloadRecord.getPayload().length,
              imfErrorLogger);
      try {
        /**
         * Add the Top Level Package UUID to the set of TrackFileIDs, this is required to validate
         * that the essences header partition that were passed in are in fact from the constituent
         * resources of the VirtualTack
         */
        MXFOperationalPattern1A.HeaderPartitionOP1A headerPartitionOP1A =
            MXFOperationalPattern1A.checkOperationalPattern1ACompliance(
                headerPartition, imfErrorLogger);
        IMFConstraints.HeaderPartitionIMF headerPartitionIMF =
            IMFConstraints.checkIMFCompliance(headerPartitionOP1A, imfErrorLogger);
        Preface preface =
            headerPartitionIMF.getHeaderPartitionOP1A().getHeaderPartition().getPreface();
        GenericPackage genericPackage =
            preface.getContentStorage().getEssenceContainerDataList().get(0).getLinkedPackage();
        SourcePackage filePackage = (SourcePackage) genericPackage;
        UUID packageUUID = filePackage.getPackageMaterialNumberasUUID();
        trackFileIDsSet.add(packageUUID);
      } catch (IMFException | MXFException e) {
        Preface preface = headerPartition.getPreface();
        GenericPackage genericPackage =
            preface.getContentStorage().getEssenceContainerDataList().get(0).getLinkedPackage();
        SourcePackage filePackage = (SourcePackage) genericPackage;
        UUID packageUUID = filePackage.getPackageMaterialNumberasUUID();
        imfErrorLogger.addError(
            new ErrorLogger.ErrorObject(
                IMFErrorLogger.IMFErrors.ErrorCodes.IMF_ESSENCE_COMPONENT_ERROR,
                IMFErrorLogger.IMFErrors.ErrorLevels.FATAL,
                String.format("IMFTrackFile with ID %s has fatal errors", packageUUID.toString())));
        if (e instanceof IMFException) {
          IMFException imfException = (IMFException) e;
          imfErrorLogger.addAllErrors(imfException.getErrors());
        } else if (e instanceof MXFException) {
          MXFException mxfException = (MXFException) e;
          imfErrorLogger.addAllErrors(mxfException.getErrors());
        }
      }
    }

    Set<UUID> virtualTrackResourceIDsSet = new HashSet<>();
    for (Composition.VirtualTrack virtualTrack : virtualTracks) {
      if (virtualTrack instanceof IMFEssenceComponentVirtualTrack) {
        virtualTrackResourceIDsSet.addAll(
            IMFEssenceComponentVirtualTrack.class.cast(virtualTrack).getTrackResourceIds());
      } else {
        imfErrorLogger.addError(
            IMFErrorLogger.IMFErrors.ErrorCodes.IMF_ESSENCE_COMPONENT_ERROR,
            IMFErrorLogger.IMFErrors.ErrorLevels.FATAL,
            String.format(
                "VirtualTrack with TrackId %s is not an Essence Component Virtual Track",
                virtualTrack.getTrackID().toString()));
      }
    }
    /**
     * Following check ensures that the Header Partitions corresponding to all the Resources of the
     * VirtualTracks were passed in.
     */
    Set<UUID> unreferencedResourceIDsSet = new HashSet<>();
    for (UUID uuid : virtualTrackResourceIDsSet) {
      if (!trackFileIDsSet.contains(uuid)) {
        unreferencedResourceIDsSet.add(uuid);
      }
    }
    if (unreferencedResourceIDsSet.size() > 0) {
      imfErrorLogger.addError(
          new ErrorLogger.ErrorObject(
              IMFErrorLogger.IMFErrors.ErrorCodes.IMP_VALIDATOR_PAYLOAD_ERROR,
              IMFErrorLogger.IMFErrors.ErrorLevels.FATAL,
              String.format(
                  "It seems that no EssenceHeaderPartition data was passed in for "
                      + "VirtualTrack Resource Ids %s, please verify that the correct Header Partition payloads for the "
                      + "Virtual Track were passed in",
                  Utilities.serializeObjectCollectionToString(unreferencedResourceIDsSet))));
    }

    /**
     * Following check ensures that the Header Partitions corresponding to only the Resource that
     * are a part of the VirtualTracks were passed in.
     */
    Set<UUID> unreferencedTrackFileIDsSet = new HashSet<>();
    for (UUID uuid : trackFileIDsSet) {
      if (!virtualTrackResourceIDsSet.contains(uuid)) {
        unreferencedTrackFileIDsSet.add(uuid);
      }
    }
    if (unreferencedTrackFileIDsSet.size() > 0) {
      imfErrorLogger.addError(
          new ErrorLogger.ErrorObject(
              IMFErrorLogger.IMFErrors.ErrorCodes.IMP_VALIDATOR_PAYLOAD_ERROR,
              IMFErrorLogger.IMFErrors.ErrorLevels.FATAL,
              String.format(
                  "It seems that EssenceHeaderPartition data was passed in for "
                      + "Resource Ids %s which are not part of this virtual track, please verify that only the Header "
                      + "Partition payloads for the Virtual Track were passed in",
                  Utilities.serializeObjectCollectionToString(unreferencedTrackFileIDsSet))));
    }

    return imfErrorLogger.getErrors();
  }
Exemple #9
0
  /**
   * A stateless method that will validate IMF AssetMap and PackingList documents for all the data
   * that should be cross referenced by both
   *
   * @param assetMapPayload - a payload record for an AssetMap document
   * @param pklPayloads - a list of payload records for Packing List documents referenced by the
   *     AssetMap
   * @return list of error messages encountered while validating an AssetMap document
   * @throws IOException - any I/O related error is exposed through an IOException
   */
  public static List<ErrorLogger.ErrorObject> validatePKLAndAssetMap(
      PayloadRecord assetMapPayload, List<PayloadRecord> pklPayloads) throws IOException {
    IMFErrorLogger imfErrorLogger = new IMFErrorLoggerImpl();
    List<PayloadRecord> packingListPayloadRecords = Collections.unmodifiableList(pklPayloads);

    if (assetMapPayload.getPayloadAssetType() != PayloadRecord.PayloadAssetType.AssetMap) {
      imfErrorLogger.addError(
          IMFErrorLogger.IMFErrors.ErrorCodes.IMF_AM_ERROR,
          IMFErrorLogger.IMFErrors.ErrorLevels.FATAL,
          String.format(
              "Payload asset type is %s, expected asset type %s",
              assetMapPayload.getPayloadAssetType(),
              PayloadRecord.PayloadAssetType.AssetMap.toString()));
    }

    ResourceByteRangeProvider assetMapByteRangeProvider =
        new ByteArrayByteRangeProvider(assetMapPayload.getPayload());
    AssetMap assetMapObjectModel = null;
    try {
      assetMapObjectModel = new AssetMap(assetMapByteRangeProvider);
      imfErrorLogger.addAllErrors(assetMapObjectModel.getErrors());

      if (assetMapObjectModel.getPackingListAssets().size() == 0) {
        imfErrorLogger.addError(
            IMFErrorLogger.IMFErrors.ErrorCodes.IMF_AM_ERROR,
            IMFErrorLogger.IMFErrors.ErrorLevels.FATAL,
            String.format(
                "Asset map should reference atleast one PackingList, %d " + "references found",
                assetMapObjectModel.getPackingListAssets().size()));
      }
    } catch (IMFException e) {
      imfErrorLogger.addAllErrors(e.getErrors());
    }

    List<ResourceByteRangeProvider> packingLists = new ArrayList<>();
    for (PayloadRecord payloadRecord : packingListPayloadRecords) {
      if (payloadRecord.getPayloadAssetType() != PayloadRecord.PayloadAssetType.PackingList) {
        imfErrorLogger.addError(
            IMFErrorLogger.IMFErrors.ErrorCodes.IMF_MASTER_PACKAGE_ERROR,
            IMFErrorLogger.IMFErrors.ErrorLevels.FATAL,
            String.format(
                "Payload asset type is %s, expected asset type %s",
                assetMapPayload.getPayloadAssetType(),
                PayloadRecord.PayloadAssetType.PackingList.toString()));
      } else {
        packingLists.add(new ByteArrayByteRangeProvider(payloadRecord.getPayload()));
      }
    }

    if (packingLists.size() == 0) {
      imfErrorLogger.addError(
          IMFErrorLogger.IMFErrors.ErrorCodes.IMF_MASTER_PACKAGE_ERROR,
          IMFErrorLogger.IMFErrors.ErrorLevels.FATAL,
          String.format(
              "Atleast one PackingList is expected, %d were detected", packingLists.size()));
    }

    if (imfErrorLogger.hasFatalErrors()) {
      return imfErrorLogger.getErrors();
    }

    List<PackingList> packingListObjectModels = new ArrayList<>();
    for (ResourceByteRangeProvider resourceByteRangeProvider : packingLists) {
      try {
        PackingList packingList = new PackingList(resourceByteRangeProvider);
        packingListObjectModels.add(packingList);
        imfErrorLogger.addAllErrors(packingList.getErrors());
      } catch (IMFException e) {
        imfErrorLogger.addAllErrors(e.getErrors());
        return imfErrorLogger.getErrors();
      }
    }
    List<UUID> assetUUIDsAssetMapList = new ArrayList<>();
    for (AssetMap.Asset asset : assetMapObjectModel.getAssetList()) {
      assetUUIDsAssetMapList.add(asset.getUUID());
    }

    /*
            //Sort the UUIDs in the AssetMap
            assetUUIDsAssetMapList.sort(new Comparator<UUID>() {
                                        @Override
                                        public int compare(UUID o1, UUID o2) {
                                            return o1.compareTo(o2);
                                        }
                                    });
    */

    /* Collect all the assets in all of the PKLs that are a part of this IMP delivery */
    List<UUID> assetUUIDsPackingList = new ArrayList<>();
    for (PackingList packingList : packingListObjectModels) {
      assetUUIDsPackingList.add(
          packingList
              .getUUID()); // PKL's UUID is also added to this list since that should be present in
                           // the AssetMap
      for (PackingList.Asset asset : packingList.getAssets()) {
        assetUUIDsPackingList.add(asset.getUUID());
      }
    }

    /*
            //Sort the UUIDs in the PackingList
            assetUUIDsPackingList.sort(new Comparator<UUID>() {
                @Override
                public int compare(UUID o1, UUID o2) {
                    return o1.compareTo(o2);
                }
            });
    */

    /* Check to see if all the Assets referenced in the PKL are also referenced by the Asset Map */
    Set<UUID> assetUUIDsAssetMapSet = new HashSet<>(assetUUIDsAssetMapList);
    Set<UUID> assetUUIDsPKLSet = new HashSet<>(assetUUIDsPackingList);

    StringBuilder unreferencedPKLAssetsUUIDs = new StringBuilder();
    for (UUID uuid : assetUUIDsPKLSet) {
      if (!assetUUIDsAssetMapSet.contains(uuid)) {
        unreferencedPKLAssetsUUIDs.append(uuid.toString());
        unreferencedPKLAssetsUUIDs.append(", ");
      }
    }

    if (!unreferencedPKLAssetsUUIDs.toString().isEmpty()) {
      imfErrorLogger.addError(
          IMFErrorLogger.IMFErrors.ErrorCodes.IMF_AM_ERROR,
          IMFErrorLogger.IMFErrors.ErrorLevels.FATAL,
          String.format(
              "The following UUID/s %s in the Packing list are not referenced by the AssetMap.",
              unreferencedPKLAssetsUUIDs.toString()));
      return imfErrorLogger.getErrors();
    }

    /* Check if all the assets in the AssetMap that are supposed to be PKLs have the same UUIDs as the PKLs themselves */
    Set<UUID> packingListAssetsUUIDsSet = new HashSet<>();
    for (AssetMap.Asset asset : assetMapObjectModel.getPackingListAssets()) {
      packingListAssetsUUIDsSet.add(asset.getUUID());
    }
    StringBuilder unreferencedPKLUUIDs = new StringBuilder();
    for (PackingList packingList : packingListObjectModels) {
      if (!packingListAssetsUUIDsSet.contains(packingList.getUUID())) {
        unreferencedPKLUUIDs.append(packingList.getUUID());
      }
    }
    if (!unreferencedPKLUUIDs.toString().isEmpty()) {
      imfErrorLogger.addError(
          IMFErrorLogger.IMFErrors.ErrorCodes.IMF_AM_ERROR,
          IMFErrorLogger.IMFErrors.ErrorLevels.FATAL,
          String.format(
              "The following Packing lists %s are not referenced in the AssetMap",
              unreferencedPKLUUIDs.toString()));
      return imfErrorLogger.getErrors();
    }
    return imfErrorLogger.getErrors();
  }