Skip to content

Commit

Permalink
[BMSPT-293] Added cancellation token to Converter stream operations.
Browse files Browse the repository at this point in the history
  • Loading branch information
Adam Eri committed Jun 13, 2024
1 parent 7e13bde commit 4da1c2d
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 23 deletions.
29 changes: 25 additions & 4 deletions src/bcf-toolkit/Converter/Bcf21/Converter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Threading;
using System.Threading.Tasks;
using BcfToolkit.Builder.Bcf21;
using BcfToolkit.Utils;
Expand Down Expand Up @@ -32,7 +33,8 @@ public class Converter : IConverter {
/// Defines the file writer function which must be used for write the BCF
/// object to the targeted version.
/// </summary>
private readonly Dictionary<BcfVersionEnum, Func<IBcf, Task<Stream>>>
private readonly Dictionary<BcfVersionEnum,
Func<IBcf, CancellationToken?, Task<Stream>>>
_writerFn =
new() {
[BcfVersionEnum.Bcf21] = FileWriter.SerializeAndWriteBcf,
Expand All @@ -43,7 +45,8 @@ private readonly Dictionary<BcfVersionEnum, Func<IBcf, Task<Stream>>>
/// Defines the stream writer function which must be used for write the BCF
/// object to the targeted version.
/// </summary>
private readonly Dictionary<BcfVersionEnum, Action<IBcf, ZipArchive>>
private readonly Dictionary<BcfVersionEnum,
Action<IBcf, ZipArchive, CancellationToken?>>
_streamWriterFn =
new() {
[BcfVersionEnum.Bcf21] = FileWriter.SerializeAndWriteBcfToStream,
Expand Down Expand Up @@ -86,25 +89,43 @@ public async Task JsonToBcf(string source, string target) {
}

public async Task<Stream> ToBcf(IBcf bcf, BcfVersionEnum targetVersion) {
return await this.ToBcf(bcf: bcf, targetVersion: targetVersion,
cancellationToken: null);
}

public async Task<Stream> ToBcf(IBcf bcf, BcfVersionEnum targetVersion,
CancellationToken? cancellationToken) {
var converterFn = _converterFn[targetVersion];
var convertedBcf = converterFn((Bcf)bcf);

if (cancellationToken is { IsCancellationRequested: true }) {
return Stream.Null;
}

var writerFn = _writerFn[targetVersion];
return await writerFn(convertedBcf);
return await writerFn(convertedBcf, cancellationToken);
}

public void ToBcf(IBcf bcf, BcfVersionEnum targetVersion, Stream stream) {
this.ToBcf(bcf, targetVersion, stream, null);
}

public void ToBcf(IBcf bcf, BcfVersionEnum targetVersion, Stream stream,
CancellationToken? cancellationToken) {
if (!stream.CanWrite) {
throw new ArgumentException("Stream is not writable.");
}

var converterFn = _converterFn[targetVersion];
var convertedBcf = converterFn((Bcf)bcf);

if (cancellationToken is { IsCancellationRequested: true }) {
return;
}

var writerFn = _streamWriterFn[targetVersion];
var zip = new ZipArchive(stream, ZipArchiveMode.Create, true);
writerFn(convertedBcf, zip);
writerFn(convertedBcf, zip, cancellationToken);
}

public Task ToBcf(IBcf bcf, string target) {
Expand Down
38 changes: 27 additions & 11 deletions src/bcf-toolkit/Converter/Bcf21/FileWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.IO.Compression;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BcfToolkit.Model;
using BcfToolkit.Model.Bcf21;
Expand Down Expand Up @@ -45,24 +46,27 @@ public static Task WriteBcfToJson(Bcf bcf, string target) {
/// then either saves the xml to the target file or creates a zip entry
/// from a memory stream based on the input. It returns a stream of the
/// archive.
///
///
/// The markups will be written into the topic folder structure:
/// * markup.bcf
/// * viewpoint files (.bcfv)
/// * snapshot files (PNG, JPEG)
/// The root files depend on the version of the BCF.
/// * project.bcfp (optional)
/// * bcf.version
///
///
/// WARNING: Disposing the stream is the responsibility of the user!
/// </summary>
/// <param name="bcf">The `BCF` object that should be written.</param>
/// <param name="cancellationToken"></param>
/// <returns>It returns a stream of the archive.</returns>
public static async Task<Stream> SerializeAndWriteBcf(IBcf bcf) {
public static async Task<Stream> SerializeAndWriteBcf(IBcf bcf,
CancellationToken? cancellationToken) {
var workingDir = Directory.GetCurrentDirectory();
var tmpBcfTargetPath = workingDir + $"/{Guid.NewGuid()}.bcfzip";
var tmpFolder =
await SerializeAndWriteBcfToFolder(bcf, tmpBcfTargetPath, false);
await SerializeAndWriteBcfToFolder(bcf, tmpBcfTargetPath, false,
cancellationToken);
var fileStream =
new FileStream(tmpBcfTargetPath, FileMode.Open, FileAccess.Read);

Expand All @@ -78,16 +82,21 @@ public static async Task<Stream> SerializeAndWriteBcf(IBcf bcf) {
/// </summary>
/// <param name="bcf">The `Bcf` object that should be written.</param>
/// <param name="zip">The zip archive which the object is written in.</param>
/// <param name="cancellationToken"></param>
/// <returns>Generated stream from bcf zip.</returns>
/// <exception cref="ApplicationException"></exception>
public static void SerializeAndWriteBcfToStream(IBcf bcf,
ZipArchive zip) {
ZipArchive zip, CancellationToken? cancellationToken = null) {
var bcfObject = (Bcf)bcf;

zip.SerializeAndCreateEntry("bcf.version", new Version());

// Writing markup files to zip archive, one markup per entry.
foreach (var markup in bcfObject.Markups) {
if (cancellationToken is { IsCancellationRequested: true }) {
return;
}

var guid = markup.GetTopic()?.Guid;
if (guid == null) {
Console.WriteLine(" - Topic Guid is missing, skipping markup");
Expand Down Expand Up @@ -121,12 +130,14 @@ public static void SerializeAndWriteBcfToStream(IBcf bcf,
/// <param name="bcf">The BCF object.</param>
/// <param name="target">The target file name of the BCFzip.</param>
/// <param name="delete">Should delete the generated tmp folder now or later.</param>
/// <param name="cancellationToken"></param>
/// <returns>Generated temp folder path.</returns>
/// <exception cref="ApplicationException"></exception>
public static async Task<string> SerializeAndWriteBcfToFolder(
IBcf bcf,
string target,
bool delete = true) {
bool delete = true,
CancellationToken? cancellationToken = null) {
var targetFolder = Path.GetDirectoryName(target);
if (targetFolder == null)
throw new ApplicationException(
Expand All @@ -140,14 +151,19 @@ public static async Task<string> SerializeAndWriteBcfToFolder(

var bcfObject = (Bcf)bcf;

var writeTasks = new List<Task>();
writeTasks.Add(BcfExtensions.SerializeAndWriteXmlFile(
tmpFolder,
"bcf.version",
new Version()));
var writeTasks = new List<Task> {
BcfExtensions.SerializeAndWriteXmlFile(
tmpFolder,
"bcf.version",
new Version())
};

// Writing markup files to disk, one markup per folder.
foreach (var markup in bcfObject.Markups) {
if (cancellationToken is { IsCancellationRequested: true }) {
return string.Empty;
}

var guid = markup.GetTopic()?.Guid;
if (guid == null) {
Console.WriteLine(
Expand Down
32 changes: 24 additions & 8 deletions src/bcf-toolkit/Converter/Bcf30/FileWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.IO.Compression;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Serialization;
using BcfToolkit.Model;
Expand Down Expand Up @@ -64,13 +65,14 @@ public static Task WriteJson(Bcf bcf, string target) {
/// * bcf.version
/// * extensions.xml
/// * documents.xml (optional)
///
///
/// WARNING: Disposing the stream is the responsibility of the user!
/// </summary>
/// <param name="bcf">The `BCF` object that should be written.</param>
/// <param name="writeToTmp">Should the archive be saved in the tmp folder.</param>
/// <param name="cancellationToken"></param>
/// <returns>It returns a stream of the archive.</returns>
public static async Task<Stream> SerializeAndWriteBcf(IBcf bcf) {
public static async Task<Stream> SerializeAndWriteBcf(IBcf bcf,
CancellationToken? cancellationToken = null) {
var workingDir = Directory.GetCurrentDirectory();
var tmpBcfTargetPath = workingDir + $"/{Guid.NewGuid()}.bcfzip";
var tmpFolder =
Expand All @@ -96,20 +98,26 @@ public static async Task<Stream> SerializeAndWriteBcf(IBcf bcf) {
/// * bcf.version
/// * extensions.xml
/// * documents.xml (optional)
///
///
/// </summary>
/// <param name="bcf">The `BCF` object that should be written..</param>
/// <param name="zip">The zip archive which the object is written in.</param>
/// <param name="cancellationToken"></param>
/// <returns>Memory stream of the bcfzip </returns>
/// <exception cref="ApplicationException"></exception>
///
public static void SerializeAndWriteBcfToStream(IBcf bcf, ZipArchive zip) {
///
public static void SerializeAndWriteBcfToStream(IBcf bcf, ZipArchive zip,
CancellationToken? cancellationToken = null) {
var bcfObject = (Bcf)bcf;

zip.SerializeAndCreateEntry("bcf.version", new Version());

// Writing markup files to zip arhive, one markup per entry.
foreach (var markup in bcfObject.Markups) {
if (cancellationToken is { IsCancellationRequested: true }) {
return;
}

var guid = markup.GetTopic()?.Guid;
if (guid == null) {
Console.WriteLine(" - Topic Guid is missing, skipping markup");
Expand Down Expand Up @@ -146,10 +154,14 @@ public static void SerializeAndWriteBcfToStream(IBcf bcf, ZipArchive zip) {
/// <param name="bcf">The BCF object.</param>
/// <param name="target">The target file name of the BCFzip.</param>
/// <param name="delete">Should delete the generated tmp folder now or later</param>
/// <param name="cancellationToken"></param>
/// <returns>Generated temp folder path</returns>
/// <exception cref="ApplicationException"></exception>
public static async Task<string> SerializeAndWriteBcfToFolder(IBcf bcf,
string target, bool delete = true) {
public static async Task<string> SerializeAndWriteBcfToFolder(
IBcf bcf,
string target,
bool delete = true,
CancellationToken? cancellationToken = null) {
var targetFolder = Path.GetDirectoryName(target);
if (targetFolder == null)
throw new ApplicationException(
Expand All @@ -170,6 +182,10 @@ public static async Task<string> SerializeAndWriteBcfToFolder(IBcf bcf,

// Writing markup files to disk, one markup per folder.
foreach (var markup in bcfObject.Markups) {
if (cancellationToken is { IsCancellationRequested: true }) {
return string.Empty;
}

var guid = markup.GetTopic()?.Guid;
if (guid == null) {
Console.WriteLine(
Expand Down
40 changes: 40 additions & 0 deletions src/bcf-toolkit/Converter/IConverter.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using BcfToolkit.Model;

Expand Down Expand Up @@ -50,6 +51,25 @@ public interface IConverter {
/// <returns>Returns the file stream of the BCF zip archive.</returns>
Task<Stream> ToBcf(IBcf bcf, BcfVersionEnum targetVersion);

/// <summary>
/// The method converts the specified BCF object to the given version, then
/// returns a stream from the BCF zip archive. The method saves the bcf files
/// locally into a tmp folder or creates a zip entry from a memory stream
/// based on the input.
///
/// When the file based compression is used, there is better compression and
/// writes are done in parallel. On the other hand, it uses local file
/// storage to keep the temporary files
///
/// WARNING: Disposing of the stream is the responsibility of the user.
/// </summary>
/// <param name="bcf">The BCF object.</param>
/// <param name="targetVersion">The BCF version.</param>
/// <param name="cancellationToken"></param>
/// <returns>Returns the file stream of the BCF zip archive.</returns>
Task<Stream> ToBcf(IBcf bcf, BcfVersionEnum targetVersion,
CancellationToken? cancellationToken);

/// <summary>
/// The method converts the specified BCF object to the given version, then
/// writes it to the specified stream.
Expand All @@ -68,6 +88,26 @@ public interface IConverter {
/// </param>
void ToBcf(IBcf bcf, BcfVersionEnum targetVersion, Stream stream);

/// <summary>
/// The method converts the specified BCF object to the given version, then
/// writes it to the specified stream.
///
/// When the stream based approach is used, there is no parallel execution
/// and there is less compression, as zip entities are compressed
/// individually.
/// </summary>
/// <param name="bcf">The BCF object.</param>
/// <param name="targetVersion">The BCF version.</param>
/// <param name="stream">
/// The output stream, which should be writable.
/// </param>
/// <param name="cancellationToken"></param>
/// <exception cref="ArgumentException">
/// Throws an error if the stream is not writable.
/// </exception>
void ToBcf(IBcf bcf, BcfVersionEnum targetVersion, Stream stream,
CancellationToken? cancellationToken);

/// <summary>
/// The method writes the specified BCF model to BCFzip files.
/// </summary>
Expand Down

0 comments on commit 4da1c2d

Please sign in to comment.