From b73d91a974c9effcbf424e8305c0ce783fe597aa Mon Sep 17 00:00:00 2001 From: Adam Eri Date: Wed, 29 May 2024 13:57:29 +0200 Subject: [PATCH 01/29] [BMSPT-283] Added delegate methods next to in memory bcf representation. --- .../Builder/Bcf21/BcfBuilderExtensions.cs | 2 +- src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs | 6 +- .../Builder/Bcf30/BcfBuilderExtensions.cs | 20 ++++++- .../Builder/Bcf30/Interfaces/IBcfBuilder.cs | 1 + .../Bcf30/Interfaces/IBcfBuilderDelegate.cs | 26 ++++++++ .../Builder/Interfaces/IFromStreamBuilder.cs | 2 +- src/bcf-toolkit/Converter/Bcf21/Converter.cs | 4 +- src/bcf-toolkit/Converter/Bcf30/Converter.cs | 4 +- src/bcf-toolkit/Utils/BcfExtensions.cs | 59 +++++++++++++++++-- src/tests/Builder/BcfBuilderTests.cs | 42 +++++++++++-- src/tests/WorkerTests.cs | 2 +- 11 files changed, 147 insertions(+), 21 deletions(-) create mode 100644 src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilderDelegate.cs diff --git a/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs b/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs index 5e07e2b..7b7770f 100644 --- a/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs +++ b/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs @@ -7,7 +7,7 @@ namespace BcfToolkit.Builder.Bcf21; public partial class BcfBuilder { - public async Task BuildFromStream(Stream source) { + public async Task BuildInMemoryFromStream(Stream source) { _bcf.Markups = await BcfExtensions.ParseMarkups(source); _bcf.Project = await BcfExtensions.ParseProject(source); diff --git a/src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs index fcae10a..d534fee 100644 --- a/src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs @@ -16,7 +16,11 @@ public partial class BcfBuilder : IBcfBuilder< IDefaultBuilder { private readonly Bcf _bcf = new(); - public BcfBuilder() { + private readonly IBcfBuilderDelegate? _delegate; + + public BcfBuilder(IBcfBuilderDelegate? builderDelegate = null) { + this._delegate = builderDelegate; + _bcf.Version = new VersionBuilder() .WithDefaults() .Build(); diff --git a/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs b/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs index a9efeaf..36be945 100644 --- a/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs +++ b/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -8,13 +9,30 @@ namespace BcfToolkit.Builder.Bcf30; public partial class BcfBuilder { + public async Task ProcessStream(Stream source) { + if (_delegate is null) { + Console.WriteLine("IBcfBuilderDelegate is not set."); + return; + } + + await BcfExtensions.ParseMarkups(source, + _delegate.MarkupCreated); + + var extensions = await BcfExtensions.ParseExtensions(source); + _delegate.ExtensionsCreatedCreated(extensions); + + _bcf.Project = await BcfExtensions.ParseProject(source); + _bcf.Document = await BcfExtensions.ParseDocuments(source); + } + + // /// /// The method builds and returns an instance of BCF 3.0 object from the /// specified file stream. /// /// The file stream. /// Returns the built object. - public async Task BuildFromStream(Stream source) { + public async Task BuildInMemoryFromStream(Stream source) { _bcf.Markups = await BcfExtensions.ParseMarkups(source); _bcf.Extensions = await BcfExtensions.ParseExtensions(source); diff --git a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilder.cs index 349746e..5d92648 100644 --- a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilder.cs @@ -12,6 +12,7 @@ public interface IBcfBuilder< out TDocumentInfoBuilder> : IBuilder, IFromStreamBuilder { + /// /// Returns the builder object extended with a new `Markup`. /// diff --git a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilderDelegate.cs b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilderDelegate.cs new file mode 100644 index 0000000..51dbfb1 --- /dev/null +++ b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilderDelegate.cs @@ -0,0 +1,26 @@ +using BcfToolkit.Model; +using BcfToolkit.Model.Bcf30; + +namespace BcfToolkit.Builder.Bcf30.Interfaces; + +public interface IBcfBuilderDelegate { + public delegate void OnMarkupCreated(TMarkup markup) + where TMarkup : IMarkup; + + public delegate void + OnProjectCreated(ProjectInfo projectInfo) + where TProjectInfo : ProjectInfo; + + public delegate void OnExtensionsCreated( + Extensions extensions); + + public delegate void OnDocumentCreated( + Extensions documentInfo); + + public OnMarkupCreated MarkupCreated { get; } + + public OnExtensionsCreated ExtensionsCreatedCreated { get; } + + // public OnProjectCreated ProjectCreated { get; } + // public OnDocumentCreated DocumentCreatedCreated { get; } +} \ No newline at end of file diff --git a/src/bcf-toolkit/Builder/Interfaces/IFromStreamBuilder.cs b/src/bcf-toolkit/Builder/Interfaces/IFromStreamBuilder.cs index b349d16..9c89cd8 100644 --- a/src/bcf-toolkit/Builder/Interfaces/IFromStreamBuilder.cs +++ b/src/bcf-toolkit/Builder/Interfaces/IFromStreamBuilder.cs @@ -8,5 +8,5 @@ public interface IFromStreamBuilder { /// It builds and returns the specified item from a specified stream. /// /// Returns the built item. - Task BuildFromStream(Stream source); + Task BuildInMemoryFromStream(Stream source); } \ No newline at end of file diff --git a/src/bcf-toolkit/Converter/Bcf21/Converter.cs b/src/bcf-toolkit/Converter/Bcf21/Converter.cs index 83f3b45..4b658b2 100644 --- a/src/bcf-toolkit/Converter/Bcf21/Converter.cs +++ b/src/bcf-toolkit/Converter/Bcf21/Converter.cs @@ -39,7 +39,7 @@ public class Converter : IConverter { }; public async Task BcfZipToJson(Stream source, string targetPath) { - var bcf = await _builder.BuildFromStream(source); + var bcf = await _builder.BuildInMemoryFromStream(source); await FileWriter.WriteJson(bcf, targetPath); } @@ -99,7 +99,7 @@ public Task ToJson(IBcf bcf, string target) { } public async Task BuildBcfFromStream(Stream stream) { - var bcf = await _builder.BuildFromStream(stream); + var bcf = await _builder.BuildInMemoryFromStream(stream); var targetVersion = BcfVersion.TryParse(typeof(T)); var converterFn = _converterFnMapper[targetVersion]; return (T)converterFn(bcf); diff --git a/src/bcf-toolkit/Converter/Bcf30/Converter.cs b/src/bcf-toolkit/Converter/Bcf30/Converter.cs index 0b835af..eb9833f 100644 --- a/src/bcf-toolkit/Converter/Bcf30/Converter.cs +++ b/src/bcf-toolkit/Converter/Bcf30/Converter.cs @@ -43,7 +43,7 @@ public Converter() { public async Task BcfZipToJson(Stream source, string target) { var builder = new BcfBuilder(); - var bcf = await builder.BuildFromStream(source); + var bcf = await builder.BuildInMemoryFromStream(source); await FileWriter.WriteJson(bcf, target); } @@ -100,7 +100,7 @@ public Task ToJson(IBcf bcf, string target) { } public async Task BuildBcfFromStream(Stream stream) { - var bcf = await _builder.BuildFromStream(stream); + var bcf = await _builder.BuildInMemoryFromStream(stream); var targetVersion = BcfVersion.TryParse(typeof(T)); var converterFn = _converterFnMapper[targetVersion]; return (T)converterFn(bcf); diff --git a/src/bcf-toolkit/Utils/BcfExtensions.cs b/src/bcf-toolkit/Utils/BcfExtensions.cs index 45799ac..a3030fb 100644 --- a/src/bcf-toolkit/Utils/BcfExtensions.cs +++ b/src/bcf-toolkit/Utils/BcfExtensions.cs @@ -8,7 +8,9 @@ using System.Threading.Tasks; using System.Xml.Linq; using System.Xml.Serialization; +using BcfToolkit.Builder.Bcf30.Interfaces; using BcfToolkit.Model; +using BcfToolkit.Model.Bcf21; namespace BcfToolkit.Utils; @@ -18,6 +20,35 @@ namespace BcfToolkit.Utils; /// memory BCF models into BCFzip. /// public static class BcfExtensions { + /// + /// The method unzips the BCFzip from a stream, + /// and parses the markup xml files within to create an in memory + /// representation of the data. + /// Topic folder structure inside a BCFzip archive: + /// The folder name is the GUID of the topic. This GUID is in the UUID form. + /// The GUID must be all-lowercase. The folder contains the following file: + /// * markup.bcf + /// Additionally the folder can contain other files: + /// * Viewpoint files + /// * Snapshot files + /// * Bitmaps + /// Notification: This function adjusts the stream position back to 0 in order to use it again. + /// + /// The source stream of the BCFzip. + /// + /// If the delegate function is set, the caller will receive every markup + /// as they are created, without building up an in-memory representation + /// of the entire BCF document. + /// + /// Returns a Task with a List of `Markup` models. + public static async Task ParseMarkups(Stream stream, + IBcfBuilderDelegate.OnMarkupCreated onMarkupCreated) + where TMarkup : IMarkup + where TVisualizationInfo : IVisualizationInfo { + await _ParseMarkups(stream, onMarkupCreated); + } + /// /// The method unzips the BCFzip from a stream, /// and parses the markup xml files within to create an in memory @@ -38,7 +69,15 @@ public static class BcfExtensions { TVisualizationInfo>(Stream stream) where TMarkup : IMarkup where TVisualizationInfo : IVisualizationInfo { - if (stream == null || !stream.CanRead) + return await _ParseMarkups(stream); + } + + private static async Task> _ParseMarkups(Stream stream, + IBcfBuilderDelegate.OnMarkupCreated? onMarkupCreated = null) + where TMarkup : IMarkup + where TVisualizationInfo : IVisualizationInfo { + if (stream is not { CanRead: true }) throw new ArgumentException("Source stream is not readable."); var objType = typeof(TMarkup); @@ -121,7 +160,7 @@ public static class BcfExtensions { if (isLastTopicEntry) WritingOutMarkup(ref markup, ref viewpoint, ref snapshot, - currentUuid, ref markups); + currentUuid, ref markups, onMarkupCreated); } // Stream must be positioned back to 0 in order to use it again @@ -134,7 +173,8 @@ private static void WritingOutMarkup( ref TVisualizationInfo? viewpoint, ref string? snapshot, string currentUuid, - ref ConcurrentBag markups) + ref ConcurrentBag markups, + IBcfBuilderDelegate.OnMarkupCreated? onMarkupCreated = null) where TMarkup : IMarkup where TVisualizationInfo : IVisualizationInfo { // This is a new subfolder, writing out Markup. @@ -146,7 +186,12 @@ private static void WritingOutMarkup( firstViewPoint.SnapshotData = snapshot; } - markups.Add(markup); + if (onMarkupCreated is not null) { + onMarkupCreated(markup); + } + else { + markups.Add(markup); + } // Null-ing external references markup = default; @@ -285,7 +330,8 @@ public static Task WriteBcfFile(string folder, string file, T? obj) { /// /// /// - public static async Task GetVersionFromStreamArchive(Stream stream) { + public static async Task GetVersionFromStreamArchive( + Stream stream) { using var archive = new ZipArchive(stream, ZipArchiveMode.Read, true); BcfVersionEnum? version = null; @@ -298,7 +344,8 @@ public static Task WriteBcfFile(string folder, string file, T? obj) { entry.Open(), LoadOptions.None, CancellationToken.None); - version = BcfVersion.TryParse(document.Root?.Attribute("VersionId")?.Value); + version = + BcfVersion.TryParse(document.Root?.Attribute("VersionId")?.Value); stream.Position = 0; return version; } diff --git a/src/tests/Builder/BcfBuilderTests.cs b/src/tests/Builder/BcfBuilderTests.cs index 0262426..0026a98 100644 --- a/src/tests/Builder/BcfBuilderTests.cs +++ b/src/tests/Builder/BcfBuilderTests.cs @@ -1,22 +1,37 @@ +using System; using System.IO; using System.Linq; using System.Threading.Tasks; using BcfToolkit.Builder.Bcf30; +using BcfToolkit.Builder.Bcf30.Interfaces; +using BcfToolkit.Model.Bcf30; using NUnit.Framework; namespace Tests.Builder; +public class BcfBuilderDelegate : IBcfBuilderDelegate { + public IBcfBuilderDelegate.OnMarkupCreated + MarkupCreated { get; } = Console.WriteLine; + + public IBcfBuilderDelegate.OnExtensionsCreated + ExtensionsCreatedCreated { get; } = Console.WriteLine; +} + public class BcfBuilderTests { - private BcfBuilder _builder = null!; + private BcfBuilder _steamBuilder = null!; + private BcfBuilder _inMemoryBuilder = null!; [SetUp] public void Setup() { - _builder = new BcfBuilder(); + _inMemoryBuilder = new BcfBuilder(); + + var bcfBuilderDelegate = new BcfBuilderDelegate(); + _steamBuilder = new BcfBuilder(bcfBuilderDelegate); } [Test] public void BuildBcfWithComplexFields() { - var bcf = _builder + var bcf = _inMemoryBuilder .AddMarkup(m => m .SetTitle("Title") .SetGuid("3ffb4df2-0187-49a9-8a4a-23992696bafd") @@ -38,7 +53,7 @@ public void BuildBcfWithComplexFields() { [Test] public void BuildBcfWithMissingRequiredFields() { - Assert.That(() => _builder.Build(), Throws.ArgumentException); + Assert.That(() => _inMemoryBuilder.Build(), Throws.ArgumentException); } [Test] @@ -47,18 +62,33 @@ public async Task BuildBcfFromStream() { "Resources/Bcf/v3.0/UserAssignment.bcfzip", FileMode.Open, FileAccess.Read); - var bcf = await _builder.BuildFromStream(stream); + await _steamBuilder.ProcessStream(stream); + // Assert.That(1, Is.EqualTo(bcf.Markups.Count)); + // Assert.That( + // "Architect@example.com", + // Is.EqualTo(bcf.Markups.FirstOrDefault()?.Topic.AssignedTo)); + } + + [Test] + public async Task BuildInMemoryBcfFromStream() { + await using var stream = new FileStream( + "Resources/Bcf/v3.0/UserAssignment.bcfzip", + FileMode.Open, + FileAccess.Read); + var bcf = await _inMemoryBuilder.BuildInMemoryFromStream(stream); Assert.That(1, Is.EqualTo(bcf.Markups.Count)); Assert.That( "Architect@example.com", Is.EqualTo(bcf.Markups.FirstOrDefault()?.Topic.AssignedTo)); } + [Test] public async Task BuildEmptyBcfFromStream() { await using var stream = new FileStream( "Resources/Bcf/v3.0/Empty.bcfzip", FileMode.Open, FileAccess.Read); - Assert.That(() => _builder.BuildFromStream(stream), Throws.ArgumentException); + Assert.That(() => _inMemoryBuilder.BuildInMemoryFromStream(stream), + Throws.ArgumentException); } } \ No newline at end of file diff --git a/src/tests/WorkerTests.cs b/src/tests/WorkerTests.cs index b7a08c9..a9e5ba8 100644 --- a/src/tests/WorkerTests.cs +++ b/src/tests/WorkerTests.cs @@ -148,7 +148,7 @@ public async Task BcfV30ToV21StreamSamplesTests() { await using var inputStream = new FileStream(path, FileMode.Open, FileAccess.Read); - var bcf = await builder.BuildFromStream(inputStream); + var bcf = await builder.BuildInMemoryFromStream(inputStream); var stream = await _worker.ToBcfStream(bcf, BcfVersionEnum.Bcf21); var version = await BcfExtensions.GetVersionFromStreamArchive(stream); Assert.That(BcfVersionEnum.Bcf21, Is.EqualTo(version)); From 18d09f42c9ee969785658bd0b1fdb484070a2f9d Mon Sep 17 00:00:00 2001 From: Balint Bende Date: Tue, 4 Jun 2024 11:15:51 +0200 Subject: [PATCH 02/29] [BMSPT-283] added bcf builder delegate --- src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs | 6 +- .../Builder/Bcf21/BcfBuilderExtensions.cs | 15 ++++ .../Bcf21/Interfaces/IBcfBuilderDelegate.cs | 17 ++++ .../Builder/Bcf30/BcfBuilderExtensions.cs | 14 ++-- .../Bcf30/Interfaces/IBcfBuilderDelegate.cs | 6 +- src/bcf-toolkit/Program.cs | 54 ++++++++++++- src/bcf-toolkit/Utils/BcfExtensions.cs | 77 +++++++++++-------- src/tests/Builder/BcfBuilderTests.cs | 30 ++++---- src/tests/Builder/MarkupBuilderTests.cs | 2 +- 9 files changed, 159 insertions(+), 62 deletions(-) create mode 100644 src/bcf-toolkit/Builder/Bcf21/Interfaces/IBcfBuilderDelegate.cs diff --git a/src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs index dd39700..2cc0a7e 100644 --- a/src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs @@ -12,8 +12,12 @@ public partial class BcfBuilder : IBcfBuilder< ProjectExtensionBuilder>, IDefaultBuilder { private readonly Bcf _bcf = new(); + + private readonly IBcfBuilderDelegate? _delegate; - public BcfBuilder() { + public BcfBuilder(IBcfBuilderDelegate? builderDelegate = null) { + this._delegate = builderDelegate; + _bcf.Version = new VersionBuilder() .WithDefaults() .Build(); diff --git a/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs b/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs index 7b7770f..d827914 100644 --- a/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs +++ b/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; @@ -7,6 +8,20 @@ namespace BcfToolkit.Builder.Bcf21; public partial class BcfBuilder { + public async Task ProcessStream(Stream source) { + if (_delegate is null) { + Console.WriteLine("IBcfBuilderDelegate is not set."); + return; + } + + await BcfExtensions.ParseMarkups(source, _delegate.MarkupCreated); + + // var extensions = await BcfExtensions.ParseExtensions(source); + // _delegate.ExtensionsCreated(extensions); + // + // _bcf.Project = await BcfExtensions.ParseProject(source); + // _bcf.Document = await BcfExtensions.ParseDocuments(source); + } public async Task BuildInMemoryFromStream(Stream source) { _bcf.Markups = await BcfExtensions.ParseMarkups(source); diff --git a/src/bcf-toolkit/Builder/Bcf21/Interfaces/IBcfBuilderDelegate.cs b/src/bcf-toolkit/Builder/Bcf21/Interfaces/IBcfBuilderDelegate.cs new file mode 100644 index 0000000..8f59fc8 --- /dev/null +++ b/src/bcf-toolkit/Builder/Bcf21/Interfaces/IBcfBuilderDelegate.cs @@ -0,0 +1,17 @@ +using BcfToolkit.Model; +using BcfToolkit.Model.Bcf21; + +namespace BcfToolkit.Builder.Bcf21.Interfaces; + +public interface IBcfBuilderDelegate { + public delegate void OnMarkupCreated(TMarkup markup) + where TMarkup : IMarkup; + + public delegate void + OnProjectCreated(ProjectExtension projectInfo) + where TProjectExtension : ProjectExtension; + + public OnMarkupCreated MarkupCreated { get; } + + public OnProjectCreated ProjectCreated { get; } +} \ No newline at end of file diff --git a/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs b/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs index 36be945..2ed2155 100644 --- a/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs +++ b/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs @@ -15,14 +15,14 @@ public async Task ProcessStream(Stream source) { return; } - await BcfExtensions.ParseMarkups(source, - _delegate.MarkupCreated); + // await BcfExtensions.ParseMarkups(source, + // _delegate.MarkupCreated); - var extensions = await BcfExtensions.ParseExtensions(source); - _delegate.ExtensionsCreatedCreated(extensions); - - _bcf.Project = await BcfExtensions.ParseProject(source); - _bcf.Document = await BcfExtensions.ParseDocuments(source); + // var extensions = await BcfExtensions.ParseExtensions(source); + // _delegate.ExtensionsCreated(extensions); + // + // _bcf.Project = await BcfExtensions.ParseProject(source); + // _bcf.Document = await BcfExtensions.ParseDocuments(source); } // diff --git a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilderDelegate.cs b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilderDelegate.cs index 51dbfb1..831c5dc 100644 --- a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilderDelegate.cs +++ b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilderDelegate.cs @@ -19,8 +19,8 @@ public delegate void OnDocumentCreated( public OnMarkupCreated MarkupCreated { get; } - public OnExtensionsCreated ExtensionsCreatedCreated { get; } + public OnExtensionsCreated ExtensionsCreated { get; } - // public OnProjectCreated ProjectCreated { get; } - // public OnDocumentCreated DocumentCreatedCreated { get; } + public OnProjectCreated ProjectCreated { get; } + public OnDocumentCreated DocumentCreatedCreated { get; } } \ No newline at end of file diff --git a/src/bcf-toolkit/Program.cs b/src/bcf-toolkit/Program.cs index 22e1758..83f6c5b 100644 --- a/src/bcf-toolkit/Program.cs +++ b/src/bcf-toolkit/Program.cs @@ -1,12 +1,41 @@ using System; using System.Threading.Tasks; using System.CommandLine; +using System.IO; +using BcfToolkit.Builder.Bcf21; +using BcfToolkit.Builder.Bcf21.Interfaces; +using BcfToolkit.Model.Bcf21; namespace BcfToolkit; +public class BcfBuilderDelegate : IBcfBuilderDelegate { + public IBcfBuilderDelegate.OnMarkupCreated + MarkupCreated { get; } = Console.WriteLine; + + public IBcfBuilderDelegate.OnProjectCreated + ProjectCreated { get; } = Console.WriteLine; + +} + internal static class Program { private static async Task Main(string[] args) { - await HandleArguments(args); + await using var stream = new FileStream( + "/Users/balintbende/Developer/test/bcf/bcftoolkittest.bcfzip", + FileMode.Open, + FileAccess.Read); + + // var bcfBuilderDelegate = new BcfBuilderDelegate(); + // var streamBuilder = new BcfBuilder(bcfBuilderDelegate); + // await streamBuilder.ProcessStream(stream); + + var builder = new BcfBuilder(); + var bcf = await builder.BuildInMemoryFromStream(stream); + Console.WriteLine(bcf.Markups.Count); + + stream.Close(); + + // await HandleArguments(args); + Environment.Exit(0); } private static async Task HandleArguments(string[] args) { var sourcePathOption = new Option( @@ -39,8 +68,27 @@ private static async Task HandleArguments(string[] args) { private static async Task DoRootCommand(InputArguments arguments) { try { - var worker = new Worker(); - await worker.Convert(arguments.SourcePath, arguments.Target); + // var worker = new Worker(); + // await worker.Convert(arguments.SourcePath, arguments.Target); + await using var stream = new FileStream( + "/Users/balintbende/Developer/test/bcf/NBHU_BT_BEHF.bcfzip", + FileMode.Open, + FileAccess.Read); + + // var bcfBuilderDelegate = new BcfBuilderDelegate(); + // var streamBuilder = new BcfBuilder(bcfBuilderDelegate); + // await streamBuilder.ProcessStream(stream); + + + + var builder = new BcfBuilder(); + var bcf = await builder.BuildInMemoryFromStream(stream); + Console.WriteLine(bcf.Markups.Count); + + builder = null; + bcf = null; + + stream.Close(); } catch (Exception e) { var errorWriter = Console.Error; diff --git a/src/bcf-toolkit/Utils/BcfExtensions.cs b/src/bcf-toolkit/Utils/BcfExtensions.cs index a3030fb..4066dd0 100644 --- a/src/bcf-toolkit/Utils/BcfExtensions.cs +++ b/src/bcf-toolkit/Utils/BcfExtensions.cs @@ -8,9 +8,8 @@ using System.Threading.Tasks; using System.Xml.Linq; using System.Xml.Serialization; -using BcfToolkit.Builder.Bcf30.Interfaces; +using BcfToolkit.Builder.Bcf21.Interfaces; using BcfToolkit.Model; -using BcfToolkit.Model.Bcf21; namespace BcfToolkit.Utils; @@ -24,15 +23,6 @@ public static class BcfExtensions { /// The method unzips the BCFzip from a stream, /// and parses the markup xml files within to create an in memory /// representation of the data. - /// Topic folder structure inside a BCFzip archive: - /// The folder name is the GUID of the topic. This GUID is in the UUID form. - /// The GUID must be all-lowercase. The folder contains the following file: - /// * markup.bcf - /// Additionally the folder can contain other files: - /// * Viewpoint files - /// * Snapshot files - /// * Bitmaps - /// Notification: This function adjusts the stream position back to 0 in order to use it again. /// /// The source stream of the BCFzip. /// @@ -40,7 +30,6 @@ public static class BcfExtensions { /// as they are created, without building up an in-memory representation /// of the entire BCF document. /// - /// Returns a Task with a List of `Markup` models. public static async Task ParseMarkups(Stream stream, IBcfBuilderDelegate.OnMarkupCreated onMarkupCreated) @@ -49,6 +38,20 @@ public static class BcfExtensions { await _ParseMarkups(stream, onMarkupCreated); } + /// + /// The method unzips the BCFzip from a stream, + /// and parses the markup xml files within to create an in memory + /// representation of the data. + /// + /// The source stream of the BCFzip. + /// Returns a Task with a List of `Markup` models. + public static async Task> ParseMarkups(Stream stream) + where TMarkup : IMarkup + where TVisualizationInfo : IVisualizationInfo { + return await _ParseMarkups(stream); + } + /// /// The method unzips the BCFzip from a stream, /// and parses the markup xml files within to create an in memory @@ -61,19 +64,20 @@ public static class BcfExtensions { /// * Viewpoint files /// * Snapshot files /// * Bitmaps - /// Notification: This function adjusts the stream position back to 0 in order to use it again. + /// Notification: This function adjusts the stream position back to 0 in + /// order to use it again. /// /// The source stream of the BCFzip. + /// + /// If the delegate function is set, the caller will receive every markup + /// as they are created, without building up an in-memory representation + /// of the entire BCF document. + /// /// Returns a Task with a List of `Markup` models. - public static async Task> ParseMarkups(Stream stream) - where TMarkup : IMarkup - where TVisualizationInfo : IVisualizationInfo { - return await _ParseMarkups(stream); - } - - private static async Task> _ParseMarkups(Stream stream, + private static async Task> _ParseMarkups< + TMarkup, + TVisualizationInfo>( + Stream stream, IBcfBuilderDelegate.OnMarkupCreated? onMarkupCreated = null) where TMarkup : IMarkup where TVisualizationInfo : IVisualizationInfo { @@ -92,7 +96,7 @@ public static class BcfExtensions { // This iterates through the archive file-by-file and the sub-folders // being just in the names of the entries. // We know it is a new Markup, when the folder (uuid) changes. In that - // case the Markup object is created and pushed into the bac. A special + // case the Markup object is created and pushed into the bag. A special // case is the last entry in the archive, when that is reached, the // markup is created as well. @@ -116,14 +120,18 @@ public static class BcfExtensions { // This sets the folder context var uuid = entry.FullName.Split("/")[0]; - //var isTopic = Regex.IsMatch(uuid.Replace("-", ""), "^[a-fA-F0-9]+$"); var isNewTopic = !string.IsNullOrEmpty(currentUuid) && uuid != currentUuid; if (isNewTopic) - WritingOutMarkup(ref markup, ref viewpoint, ref snapshot, - currentUuid, ref markups); + WritingOutMarkup( + markup, + viewpoint, + ref snapshot, + currentUuid, + markups, + onMarkupCreated); currentUuid = uuid; @@ -141,7 +149,6 @@ public static class BcfExtensions { if (viewpoint != null) // TODO: No support for multiple viewpoints! Console.WriteLine("No support for multiple viewpoints!"); - //continue; var document = await XDocument.LoadAsync( entry.Open(), LoadOptions.None, @@ -154,13 +161,17 @@ public static class BcfExtensions { if (snapshot != null) // TODO: No support for multiple snapshots! Console.WriteLine("No support for multiple snapshots!"); - //continue; snapshot = entry.Snapshot(); } if (isLastTopicEntry) - WritingOutMarkup(ref markup, ref viewpoint, ref snapshot, - currentUuid, ref markups, onMarkupCreated); + WritingOutMarkup( + markup, + viewpoint, + ref snapshot, + currentUuid, + markups, + onMarkupCreated); } // Stream must be positioned back to 0 in order to use it again @@ -169,11 +180,11 @@ public static class BcfExtensions { } private static void WritingOutMarkup( - ref TMarkup? markup, - ref TVisualizationInfo? viewpoint, + TMarkup? markup, + TVisualizationInfo? viewpoint, ref string? snapshot, string currentUuid, - ref ConcurrentBag markups, + ConcurrentBag markups, IBcfBuilderDelegate.OnMarkupCreated? onMarkupCreated = null) where TMarkup : IMarkup where TVisualizationInfo : IVisualizationInfo { diff --git a/src/tests/Builder/BcfBuilderTests.cs b/src/tests/Builder/BcfBuilderTests.cs index 0026a98..5c3545a 100644 --- a/src/tests/Builder/BcfBuilderTests.cs +++ b/src/tests/Builder/BcfBuilderTests.cs @@ -12,13 +12,19 @@ namespace Tests.Builder; public class BcfBuilderDelegate : IBcfBuilderDelegate { public IBcfBuilderDelegate.OnMarkupCreated MarkupCreated { get; } = Console.WriteLine; - + public IBcfBuilderDelegate.OnExtensionsCreated - ExtensionsCreatedCreated { get; } = Console.WriteLine; + ExtensionsCreated { get; } = Console.WriteLine; + + public IBcfBuilderDelegate.OnProjectCreated + ProjectCreated { get; } = Console.WriteLine; + + public IBcfBuilderDelegate.OnDocumentCreated + DocumentCreatedCreated { get; } = Console.WriteLine; } public class BcfBuilderTests { - private BcfBuilder _steamBuilder = null!; + private BcfBuilder _streamBuilder = null!; private BcfBuilder _inMemoryBuilder = null!; [SetUp] @@ -26,11 +32,11 @@ public void Setup() { _inMemoryBuilder = new BcfBuilder(); var bcfBuilderDelegate = new BcfBuilderDelegate(); - _steamBuilder = new BcfBuilder(bcfBuilderDelegate); + _streamBuilder = new BcfBuilder(bcfBuilderDelegate); } [Test] - public void BuildBcfWithComplexFields() { + public void BuildBcfWithComplexFieldsTest() { var bcf = _inMemoryBuilder .AddMarkup(m => m .SetTitle("Title") @@ -52,25 +58,21 @@ public void BuildBcfWithComplexFields() { } [Test] - public void BuildBcfWithMissingRequiredFields() { + public void BuildBcfWithMissingRequiredFieldsTest() { Assert.That(() => _inMemoryBuilder.Build(), Throws.ArgumentException); } [Test] - public async Task BuildBcfFromStream() { + public async Task ProcessStreamTest() { await using var stream = new FileStream( "Resources/Bcf/v3.0/UserAssignment.bcfzip", FileMode.Open, FileAccess.Read); - await _steamBuilder.ProcessStream(stream); - // Assert.That(1, Is.EqualTo(bcf.Markups.Count)); - // Assert.That( - // "Architect@example.com", - // Is.EqualTo(bcf.Markups.FirstOrDefault()?.Topic.AssignedTo)); + await _streamBuilder.ProcessStream(stream); } [Test] - public async Task BuildInMemoryBcfFromStream() { + public async Task BuildInMemoryBcfFromStreamTest() { await using var stream = new FileStream( "Resources/Bcf/v3.0/UserAssignment.bcfzip", FileMode.Open, @@ -83,7 +85,7 @@ public async Task BuildInMemoryBcfFromStream() { } [Test] - public async Task BuildEmptyBcfFromStream() { + public async Task BuildEmptyBcfFromStreamTest() { await using var stream = new FileStream( "Resources/Bcf/v3.0/Empty.bcfzip", FileMode.Open, diff --git a/src/tests/Builder/MarkupBuilderTests.cs b/src/tests/Builder/MarkupBuilderTests.cs index db2e6cc..576f5b9 100644 --- a/src/tests/Builder/MarkupBuilderTests.cs +++ b/src/tests/Builder/MarkupBuilderTests.cs @@ -38,7 +38,7 @@ public void BuildMarkupWithRequiredFields() { .Build(); Assert.That( "3ffb4df2-0187-49a9-8a4a-23992696bafd", - Is.EqualTo(markup.GetTopic()!.Guid)); + Is.EqualTo(markup.GetTopic().Guid)); } [Test] From 6db9ad0272809215201af12764eaee5f7993457d Mon Sep 17 00:00:00 2001 From: Balint Bende Date: Mon, 3 Jun 2024 16:22:17 +0200 Subject: [PATCH 03/29] [NOISSUE] fixed bug, entries in unordered list, added file builder test --- bcf-toolkit.sln.DotSettings.user | 8 +++++++ src/bcf-toolkit/Utils/BcfExtensions.cs | 4 +++- src/tests/Builder/FileBuilderTest.cs | 31 ++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/tests/Builder/FileBuilderTest.cs diff --git a/bcf-toolkit.sln.DotSettings.user b/bcf-toolkit.sln.DotSettings.user index 42be8be..ee7c26c 100644 --- a/bcf-toolkit.sln.DotSettings.user +++ b/bcf-toolkit.sln.DotSettings.user @@ -6,6 +6,8 @@ <Solution /> </SessionState> + + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution #4" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Solution /> </SessionState> @@ -16,10 +18,16 @@ </SessionState> + <SessionState ContinuousTestingMode="0" Name="All tests from Solution #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Solution /> </SessionState> + + + + + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Solution /> </SessionState> diff --git a/src/bcf-toolkit/Utils/BcfExtensions.cs b/src/bcf-toolkit/Utils/BcfExtensions.cs index 4066dd0..1ff3a00 100644 --- a/src/bcf-toolkit/Utils/BcfExtensions.cs +++ b/src/bcf-toolkit/Utils/BcfExtensions.cs @@ -107,7 +107,9 @@ private static async Task> _ParseMarkups< var viewpoint = default(TVisualizationInfo); string? snapshot = null; - var topicEntries = archive.Entries + var topicEntries = archive + .Entries + .OrderBy(entry => entry.FullName) .Where(entry => Regex.IsMatch(entry.FullName.Split("/")[0].Replace("-", ""), "^[a-fA-F0-9]+$")) diff --git a/src/tests/Builder/FileBuilderTest.cs b/src/tests/Builder/FileBuilderTest.cs new file mode 100644 index 0000000..9b1a72e --- /dev/null +++ b/src/tests/Builder/FileBuilderTest.cs @@ -0,0 +1,31 @@ +using System; +using BcfToolkit.Builder.Bcf30; +using NUnit.Framework; + +namespace Tests.Builder; + +public class FileBuilderTest { + private FileBuilder _builder = null!; + + [SetUp] + public void Setup() { + _builder = new FileBuilder(); + } + + [Test] + public void BuildHeaderFileWithComplexFields() { + var file = _builder + .SetIfcProject("3MD_HkJ6X2EwpfIbCFm0g_") + .SetFileName("File.ifc") + .SetIsExternal(false) + .SetIfcSpatialStructureElement("3MD_HkJ6X2EwpfIbCFm0hh") + .SetDate(DateTime.Parse("2012-10-15T16:41:27+04:00").ToUniversalTime()) + .Build(); + Assert.That( + "3MD_HkJ6X2EwpfIbCFm0g_", + Is.EqualTo(file.IfcProject)); + Assert.That( + DateTime.Parse("2012-10-15T12:41:27"), + Is.EqualTo(file.Date)); + } +} \ No newline at end of file From dd34edc8c4c372a1c8d18df08c7dc242b531c36f Mon Sep 17 00:00:00 2001 From: Balint Bende Date: Mon, 3 Jun 2024 16:24:30 +0200 Subject: [PATCH 04/29] [NOISSUE] updated dotsettings --- bcf-toolkit.sln.DotSettings.user | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bcf-toolkit.sln.DotSettings.user b/bcf-toolkit.sln.DotSettings.user index ee7c26c..a1645a5 100644 --- a/bcf-toolkit.sln.DotSettings.user +++ b/bcf-toolkit.sln.DotSettings.user @@ -11,6 +11,9 @@ <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution #4" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Solution /> </SessionState> + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Solution /> +</SessionState> <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution #3" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> From 7bec3fe17fd9ab8ca7bcd51290bda12c214360d5 Mon Sep 17 00:00:00 2001 From: Balint Bende Date: Tue, 4 Jun 2024 11:17:57 +0200 Subject: [PATCH 05/29] [NOISSUE] fixed comments --- .../Builder/Bcf21/ViewPointBuilder.cs | 2 +- .../Builder/Bcf30/Interfaces/IMarkupBuilder.cs | 4 ++-- .../Bcf/v2.1/BcfVersionAndTopics.bcfzip | Bin 0 -> 85202 bytes 3 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 src/tests/Resources/Bcf/v2.1/BcfVersionAndTopics.bcfzip diff --git a/src/bcf-toolkit/Builder/Bcf21/ViewPointBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/ViewPointBuilder.cs index aef933d..8d39515 100644 --- a/src/bcf-toolkit/Builder/Bcf21/ViewPointBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/ViewPointBuilder.cs @@ -5,7 +5,7 @@ namespace BcfToolkit.Builder.Bcf21; public class ViewPointBuilder { private readonly ViewPoint _viewPoint = new(); - public ViewPointBuilder SetVisualizationInfo(Model.Bcf21.VisualizationInfo? visualizationInfo) { + public ViewPointBuilder SetVisualizationInfo(VisualizationInfo? visualizationInfo) { _viewPoint.VisualizationInfo = visualizationInfo; return this; } diff --git a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IMarkupBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IMarkupBuilder.cs index fd71d02..0ec2df7 100644 --- a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IMarkupBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IMarkupBuilder.cs @@ -159,10 +159,10 @@ public interface IMarkupBuilder< /// /// Viewpoint file name. /// Snapshot file name. - /// Snapshot data. + /// Base64 string of snapshot data. /// Index parameter for sorting. /// Guid of the viewpoint. - /// The builder for `ViewPoint`. + /// The builder for `VisualizationInfo`. /// Returns the builder object. TBuilder AddViewPoint( string viewpoint, diff --git a/src/tests/Resources/Bcf/v2.1/BcfVersionAndTopics.bcfzip b/src/tests/Resources/Bcf/v2.1/BcfVersionAndTopics.bcfzip new file mode 100644 index 0000000000000000000000000000000000000000..84b8cf64d3642671759bb9dd358d0e49d2249f2e GIT binary patch literal 85202 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%0AWS>YQ1;%n89$6 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 Date: Tue, 4 Jun 2024 11:22:35 +0200 Subject: [PATCH 06/29] [BMSPT-289] fixed location and direction instance creation in bitmap, clipping plane and line builders --- .../Builder/Bcf30/Interfaces/IViewPointBuilder.cs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/bcf-toolkit/Builder/Bcf30/Interfaces/IViewPointBuilder.cs diff --git a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IViewPointBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IViewPointBuilder.cs new file mode 100644 index 0000000..aa4a789 --- /dev/null +++ b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IViewPointBuilder.cs @@ -0,0 +1,9 @@ +namespace BcfToolkit.Builder.Bcf30.Interfaces; + +public class IViewPointBuilder { + TBuilder SetViewPoint(string viewpoint); + TBuilder SetSnapshot(string viewpoint); + TBuilder SetSnapshotData(string viewpoint); + TBuilder SetViewPoint(string viewpoint); + TBuilder SetViewPoint(string viewpoint); +} \ No newline at end of file From f5780a9cc5aee726a1107e2acb029ad02f414dbb Mon Sep 17 00:00:00 2001 From: Balint Bende Date: Tue, 4 Jun 2024 11:22:49 +0200 Subject: [PATCH 07/29] [BMSPT-289] fixed location and direction instance creation in bitmap, clipping plane and line builders --- .../Builder/Bcf21/BitmapBuilder.cs | 30 +++++++++++-------- .../Builder/Bcf21/ClippingPlaneBuilder.cs | 20 ++++++++----- src/bcf-toolkit/Builder/Bcf21/LineBuilder.cs | 20 ++++++++----- .../Builder/Bcf30/BitmapBuilder.cs | 24 +++++++++------ .../Builder/Bcf30/ClippingPlaneBuilder.cs | 16 ++++++---- src/bcf-toolkit/Builder/Bcf30/LineBuilder.cs | 16 ++++++---- 6 files changed, 77 insertions(+), 49 deletions(-) diff --git a/src/bcf-toolkit/Builder/Bcf21/BitmapBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/BitmapBuilder.cs index 11bdd67..aa2242d 100644 --- a/src/bcf-toolkit/Builder/Bcf21/BitmapBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/BitmapBuilder.cs @@ -19,25 +19,31 @@ public BitmapBuilder SetReference(string reference) { _bitmap.Reference = reference; return this; } - + public BitmapBuilder SetLocation(double x, double y, double z) { - _bitmap.Location.X = x; - _bitmap.Location.Y = y; - _bitmap.Location.Z = z; + _bitmap.Location = new Point { + X = x, + Y = y, + Z = z + }; return this; } - + public BitmapBuilder SetNormal(double x, double y, double z) { - _bitmap.Normal.X = x; - _bitmap.Normal.Y = y; - _bitmap.Normal.Z = z; + _bitmap.Normal = new Direction { + X = x, + Y = y, + Z = z + }; return this; } - + public BitmapBuilder SetUp(double x, double y, double z) { - _bitmap.Up.X = x; - _bitmap.Up.Y = y; - _bitmap.Up.Z = z; + _bitmap.Up = new Direction { + X = x, + Y = y, + Z = z + }; return this; } diff --git a/src/bcf-toolkit/Builder/Bcf21/ClippingPlaneBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/ClippingPlaneBuilder.cs index ad26655..bc80c55 100644 --- a/src/bcf-toolkit/Builder/Bcf21/ClippingPlaneBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/ClippingPlaneBuilder.cs @@ -8,18 +8,22 @@ public class ClippingPlaneBuilder : IClippingPlaneBuilder, IDefaultBuilder { private readonly ClippingPlane _clippingPlane = new(); - + public ClippingPlaneBuilder SetLocation(double x, double y, double z) { - _clippingPlane.Location.X = x; - _clippingPlane.Location.Y = y; - _clippingPlane.Location.Z = z; + _clippingPlane.Location = new Point { + X = x, + Y = y, + Z = z + }; return this; } - + public ClippingPlaneBuilder SetDirection(double x, double y, double z) { - _clippingPlane.Direction.X = x; - _clippingPlane.Direction.Y = y; - _clippingPlane.Direction.Z = z; + _clippingPlane.Direction = new Direction { + X = x, + Y = y, + Z = z + }; return this; } diff --git a/src/bcf-toolkit/Builder/Bcf21/LineBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/LineBuilder.cs index deff418..c40895c 100644 --- a/src/bcf-toolkit/Builder/Bcf21/LineBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/LineBuilder.cs @@ -8,18 +8,22 @@ public class LineBuilder : ILineBuilder, IDefaultBuilder { private readonly Line _line = new(); - + public LineBuilder SetStartPoint(double x, double y, double z) { - _line.StartPoint.X = x; - _line.StartPoint.Y = y; - _line.StartPoint.Z = z; + _line.StartPoint = new Point { + X = x, + Y = y, + Z = z + }; return this; } - + public LineBuilder SetEndPoint(double x, double y, double z) { - _line.EndPoint.X = x; - _line.EndPoint.Y = y; - _line.EndPoint.Z = z; + _line.EndPoint = new Point { + X = x, + Y = y, + Z = z + }; return this; } diff --git a/src/bcf-toolkit/Builder/Bcf30/BitmapBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/BitmapBuilder.cs index a1954be..915b275 100644 --- a/src/bcf-toolkit/Builder/Bcf30/BitmapBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/BitmapBuilder.cs @@ -21,23 +21,29 @@ public BitmapBuilder SetReference(string reference) { } public BitmapBuilder SetLocation(double x, double y, double z) { - _bitmap.Location.X = x; - _bitmap.Location.Y = y; - _bitmap.Location.Z = z; + _bitmap.Location = new Point { + X = x, + Y = y, + Z = z + }; return this; } public BitmapBuilder SetNormal(double x, double y, double z) { - _bitmap.Normal.X = x; - _bitmap.Normal.Y = y; - _bitmap.Normal.Z = z; + _bitmap.Normal = new Direction { + X = x, + Y = y, + Z = z + }; return this; } public BitmapBuilder SetUp(double x, double y, double z) { - _bitmap.Up.X = x; - _bitmap.Up.Y = y; - _bitmap.Up.Z = z; + _bitmap.Up = new Direction { + X = x, + Y = y, + Z = z + }; return this; } diff --git a/src/bcf-toolkit/Builder/Bcf30/ClippingPlaneBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/ClippingPlaneBuilder.cs index 752d1d7..1c6ebb8 100644 --- a/src/bcf-toolkit/Builder/Bcf30/ClippingPlaneBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/ClippingPlaneBuilder.cs @@ -10,16 +10,20 @@ public class ClippingPlaneBuilder : private readonly ClippingPlane _clippingPlane = new(); public ClippingPlaneBuilder SetLocation(double x, double y, double z) { - _clippingPlane.Location.X = x; - _clippingPlane.Location.Y = y; - _clippingPlane.Location.Z = z; + _clippingPlane.Location = new Point { + X = x, + Y = y, + Z = z + }; return this; } public ClippingPlaneBuilder SetDirection(double x, double y, double z) { - _clippingPlane.Direction.X = x; - _clippingPlane.Direction.Y = y; - _clippingPlane.Direction.Z = z; + _clippingPlane.Direction = new Direction { + X = x, + Y = y, + Z = z + }; return this; } diff --git a/src/bcf-toolkit/Builder/Bcf30/LineBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/LineBuilder.cs index 3be10ca..8bf01ad 100644 --- a/src/bcf-toolkit/Builder/Bcf30/LineBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/LineBuilder.cs @@ -10,16 +10,20 @@ public class LineBuilder : private readonly Line _line = new(); public LineBuilder SetStartPoint(double x, double y, double z) { - _line.StartPoint.X = x; - _line.StartPoint.Y = y; - _line.StartPoint.Z = z; + _line.StartPoint = new Point { + X = x, + Y = y, + Z = z + }; return this; } public LineBuilder SetEndPoint(double x, double y, double z) { - _line.EndPoint.X = x; - _line.EndPoint.Y = y; - _line.EndPoint.Z = z; + _line.EndPoint = new Point { + X = x, + Y = y, + Z = z + }; return this; } From 4ec88495dd39f354c07652af2ebca71245224d92 Mon Sep 17 00:00:00 2001 From: Balint Bende Date: Tue, 4 Jun 2024 16:45:21 +0200 Subject: [PATCH 08/29] [BMSPT-289] added viewpoint builder, format, unit tests --- bcf-toolkit.sln.DotSettings.user | 8 +-- .../Builder/Bcf21/BitmapBuilder.cs | 6 +-- .../Builder/Bcf21/ClippingPlaneBuilder.cs | 4 +- .../Bcf21/Interfaces/IMarkupBuilder.cs | 22 ++++++-- .../Bcf21/Interfaces/IViewPointBuilder.cs | 49 +++++++++++++++++ src/bcf-toolkit/Builder/Bcf21/LineBuilder.cs | 4 +- .../Builder/Bcf21/MarkupBuilder.cs | 11 +++- .../Builder/Bcf21/ViewPointBuilder.cs | 20 ++++++- .../Bcf30/Interfaces/IMarkupBuilder.cs | 18 ++++++- .../Bcf30/Interfaces/IViewPointBuilder.cs | 50 ++++++++++++++++-- .../Builder/Bcf30/MarkupBuilder.cs | 14 +++-- .../Builder/Bcf30/ViewPointBuilder.cs | 49 +++++++++++++++++ src/tests/Converter/Bcf21/ConverterTests.cs | 3 ++ .../Resources/Bcf/v2.1/MiniSolibri.bcfzip | Bin 0 -> 8312 bytes src/tests/WorkerTests.cs | 10 ++++ 15 files changed, 243 insertions(+), 25 deletions(-) create mode 100644 src/bcf-toolkit/Builder/Bcf21/Interfaces/IViewPointBuilder.cs create mode 100644 src/bcf-toolkit/Builder/Bcf30/ViewPointBuilder.cs create mode 100644 src/tests/Resources/Bcf/v2.1/MiniSolibri.bcfzip diff --git a/bcf-toolkit.sln.DotSettings.user b/bcf-toolkit.sln.DotSettings.user index a1645a5..9a8c03c 100644 --- a/bcf-toolkit.sln.DotSettings.user +++ b/bcf-toolkit.sln.DotSettings.user @@ -2,6 +2,7 @@ /Users/balintbende/Library/Caches/JetBrains/Rider2024.1/resharper-host/temp/Rider/vAny/CoverageData/_bcf-toolkit.-1315391344/Snapshot/snapshot.utdcvr + <SessionState ContinuousTestingMode="0" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Solution /> </SessionState> @@ -11,9 +12,7 @@ <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution #4" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Solution /> </SessionState> - <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <Solution /> -</SessionState> + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution #3" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> @@ -25,6 +24,9 @@ <SessionState ContinuousTestingMode="0" Name="All tests from Solution #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Solution /> </SessionState> + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Solution /> +</SessionState> diff --git a/src/bcf-toolkit/Builder/Bcf21/BitmapBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/BitmapBuilder.cs index aa2242d..0190e85 100644 --- a/src/bcf-toolkit/Builder/Bcf21/BitmapBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/BitmapBuilder.cs @@ -19,7 +19,7 @@ public BitmapBuilder SetReference(string reference) { _bitmap.Reference = reference; return this; } - + public BitmapBuilder SetLocation(double x, double y, double z) { _bitmap.Location = new Point { X = x, @@ -28,7 +28,7 @@ public BitmapBuilder SetLocation(double x, double y, double z) { }; return this; } - + public BitmapBuilder SetNormal(double x, double y, double z) { _bitmap.Normal = new Direction { X = x, @@ -37,7 +37,7 @@ public BitmapBuilder SetNormal(double x, double y, double z) { }; return this; } - + public BitmapBuilder SetUp(double x, double y, double z) { _bitmap.Up = new Direction { X = x, diff --git a/src/bcf-toolkit/Builder/Bcf21/ClippingPlaneBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/ClippingPlaneBuilder.cs index bc80c55..8aab3f4 100644 --- a/src/bcf-toolkit/Builder/Bcf21/ClippingPlaneBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/ClippingPlaneBuilder.cs @@ -8,7 +8,7 @@ public class ClippingPlaneBuilder : IClippingPlaneBuilder, IDefaultBuilder { private readonly ClippingPlane _clippingPlane = new(); - + public ClippingPlaneBuilder SetLocation(double x, double y, double z) { _clippingPlane.Location = new Point { X = x, @@ -17,7 +17,7 @@ public ClippingPlaneBuilder SetLocation(double x, double y, double z) { }; return this; } - + public ClippingPlaneBuilder SetDirection(double x, double y, double z) { _clippingPlane.Direction = new Direction { X = x, diff --git a/src/bcf-toolkit/Builder/Bcf21/Interfaces/IMarkupBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/Interfaces/IMarkupBuilder.cs index 5d8c361..fb91d2f 100644 --- a/src/bcf-toolkit/Builder/Bcf21/Interfaces/IMarkupBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/Interfaces/IMarkupBuilder.cs @@ -10,7 +10,8 @@ public interface IMarkupBuilder< out TBimSnippetBuilder, out TDocumentReferenceBuilder, out TCommentBuilder, - out TVisualizationInfoBuilder> : + out TVisualizationInfoBuilder, + out TViewPointBuilder> : IBuilder { /// /// Returns the builder object set with the `Guid`. @@ -156,14 +157,19 @@ public interface IMarkupBuilder< /// /// Returns the builder object extended with `ViewPoint`. + /// WARNING: This function is deprecated, please use `AddViewPoint` with + /// `ViewPointBuilder` argument instead. /// /// Viewpoint file name. /// Snapshot file name. - /// Snapshot data. + /// Base64 string of snapshot data. /// Index parameter for sorting. /// Guid of the viewpoint. - /// The builder for `ViewPoint`. + /// The builder for `VisualizationInfo`. /// Returns the builder object. + [Obsolete( + "This function is deprecated, please use `AddViewPoint` with " + + "`ViewPointBuilder` argument instead.")] TBuilder AddViewPoint( string viewpoint, string snapshot, @@ -172,6 +178,16 @@ TBuilder AddViewPoint( string guid, Action builder); + /// + /// Returns the builder object extended with a new `ViewPoint`. + /// The markup file can contain multiple viewpoints related to one or + /// more comments. A viewpoint has also the Guid attribute for identifying + /// it uniquely. + /// + /// The builder for `ViewPoint`. + /// Returns the builder object. + TBuilder AddViewPoint(Action builder); + /// /// Returns the builder object extended with `RelatedTopic`. /// diff --git a/src/bcf-toolkit/Builder/Bcf21/Interfaces/IViewPointBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/Interfaces/IViewPointBuilder.cs new file mode 100644 index 0000000..f6a76be --- /dev/null +++ b/src/bcf-toolkit/Builder/Bcf21/Interfaces/IViewPointBuilder.cs @@ -0,0 +1,49 @@ +using System; +using BcfToolkit.Builder.Interfaces; +using BcfToolkit.Model.Bcf21; + +namespace BcfToolkit.Builder.Bcf21.Interfaces; + +public interface IViewPointBuilder + : IBuilder { + /// + /// Returns the builder object set with the `ViewPoint`, which is the + /// file name of the viewpoint (.bcfv). + /// + /// Viewpoint file name. + /// Returns the builder object. + TBuilder SetViewPoint(string viewpoint); + + /// + /// Returns the builder object set with the `Snapshot`, which is the + /// file name of the snapshot (png or jpeg). + /// + /// Snapshot file name. + /// Returns the builder object. + TBuilder SetSnapshot(string snapshot); + + /// + /// Returns the builder object set with the `SnapshotData`, which is the + /// Base64 string of snapshot data. + /// + /// Base64 string of snapshot data. + /// Returns the builder object. + TBuilder SetSnapshotData(string snapshotData); + + /// + /// Returns the builder object set with the `Index`, which is the + /// Guid of the viewpoint. + /// + /// Guid of the viewpoint. + /// Returns the builder object. + TBuilder SetIndex(int index); + + /// + /// Returns the builder object set with the `VisualizationInfo`, which + /// contains information of components related to the topic, + /// camera settings, and possible markup and clipping information. + /// + /// The builder for `VisualizationInfo` + /// Returns the builder object. + TBuilder SetVisualizationInfo(Action builder); +} \ No newline at end of file diff --git a/src/bcf-toolkit/Builder/Bcf21/LineBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/LineBuilder.cs index c40895c..183ed75 100644 --- a/src/bcf-toolkit/Builder/Bcf21/LineBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/LineBuilder.cs @@ -8,7 +8,7 @@ public class LineBuilder : ILineBuilder, IDefaultBuilder { private readonly Line _line = new(); - + public LineBuilder SetStartPoint(double x, double y, double z) { _line.StartPoint = new Point { X = x, @@ -17,7 +17,7 @@ public LineBuilder SetStartPoint(double x, double y, double z) { }; return this; } - + public LineBuilder SetEndPoint(double x, double y, double z) { _line.EndPoint = new Point { X = x, diff --git a/src/bcf-toolkit/Builder/Bcf21/MarkupBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/MarkupBuilder.cs index 072f6be..825a4a2 100644 --- a/src/bcf-toolkit/Builder/Bcf21/MarkupBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/MarkupBuilder.cs @@ -13,7 +13,8 @@ public partial class MarkupBuilder : BimSnippetBuilder, DocumentReferenceBuilder, CommentBuilder, - VisualizationInfoBuilder>, + VisualizationInfoBuilder, + ViewPointBuilder>, IDefaultBuilder { private readonly Markup _markup = new(); @@ -151,6 +152,14 @@ public MarkupBuilder AddViewPoint(string viewpoint, string snapshot, return this; } + public MarkupBuilder AddViewPoint(Action builder) { + var viewPoint = + (ViewPoint)BuilderUtils + .BuildItem(builder); + _markup.Viewpoints.Add(viewPoint); + return this; + } + public MarkupBuilder AddRelatedTopic(string relatedTopicGuid) { var relatedTopic = new TopicRelatedTopic { Guid = relatedTopicGuid diff --git a/src/bcf-toolkit/Builder/Bcf21/ViewPointBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/ViewPointBuilder.cs index 8d39515..1e91cfb 100644 --- a/src/bcf-toolkit/Builder/Bcf21/ViewPointBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/ViewPointBuilder.cs @@ -1,8 +1,12 @@ +using System; +using BcfToolkit.Builder.Bcf21.Interfaces; +using BcfToolkit.Model; using BcfToolkit.Model.Bcf21; namespace BcfToolkit.Builder.Bcf21; -public class ViewPointBuilder { +public class ViewPointBuilder : IViewPointBuilder< + ViewPointBuilder, VisualizationInfoBuilder> { private readonly ViewPoint _viewPoint = new(); public ViewPointBuilder SetVisualizationInfo(VisualizationInfo? visualizationInfo) { @@ -10,6 +14,11 @@ public ViewPointBuilder SetVisualizationInfo(VisualizationInfo? visualizationInf return this; } + public ViewPointBuilder SetViewPoint(string viewpoint) { + _viewPoint.Viewpoint = viewpoint; + return this; + } + public ViewPointBuilder SetSnapshot(string snapshot) { _viewPoint.Snapshot = snapshot; return this; @@ -20,6 +29,15 @@ public ViewPointBuilder SetIndex(int index) { return this; } + public ViewPointBuilder SetVisualizationInfo( + Action builder) { + var visInfo = + (VisualizationInfo)BuilderUtils + .BuildItem(builder); + _viewPoint.VisualizationInfo = visInfo; + return this; + } + public ViewPointBuilder SetGuid(string guid) { _viewPoint.Guid = guid; return this; diff --git a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IMarkupBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IMarkupBuilder.cs index 0ec2df7..30f789f 100644 --- a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IMarkupBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IMarkupBuilder.cs @@ -10,7 +10,8 @@ public interface IMarkupBuilder< out TBimSnippetBuilder, out TDocumentReferenceBuilder, out TCommentBuilder, - out TVisualizationInfoBuilder> : + out TVisualizationInfoBuilder, + out TViewPointBuilder> : IBuilder { /// /// Returns the builder object set with the `Guid`. @@ -156,6 +157,8 @@ public interface IMarkupBuilder< /// /// Returns the builder object extended with `ViewPoint`. + /// WARNING: This function is deprecated, please use `AddViewPoint` with + /// `ViewPointBuilder` argument instead. /// /// Viewpoint file name. /// Snapshot file name. @@ -164,6 +167,9 @@ public interface IMarkupBuilder< /// Guid of the viewpoint. /// The builder for `VisualizationInfo`. /// Returns the builder object. + [Obsolete( + "This function is deprecated, please use `AddViewPoint` with " + + "`ViewPointBuilder` argument instead.")] TBuilder AddViewPoint( string viewpoint, string snapshot, @@ -172,6 +178,16 @@ TBuilder AddViewPoint( string guid, Action builder); + /// + /// Returns the builder object extended with a new `ViewPoint`. + /// The markup file can contain multiple viewpoints related to one or + /// more comments. A viewpoint has also the Guid attribute for identifying + /// it uniquely. + /// + /// The builder for `ViewPoint`. + /// Returns the builder object. + TBuilder AddViewPoint(Action builder); + /// /// Returns the builder object extended with `RelatedTopic`. /// diff --git a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IViewPointBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IViewPointBuilder.cs index aa4a789..42358a1 100644 --- a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IViewPointBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IViewPointBuilder.cs @@ -1,9 +1,49 @@ +using System; +using BcfToolkit.Builder.Interfaces; +using BcfToolkit.Model.Bcf30; + namespace BcfToolkit.Builder.Bcf30.Interfaces; -public class IViewPointBuilder { - TBuilder SetViewPoint(string viewpoint); - TBuilder SetSnapshot(string viewpoint); - TBuilder SetSnapshotData(string viewpoint); - TBuilder SetViewPoint(string viewpoint); +public interface IViewPointBuilder + : IBuilder { + /// + /// Returns the builder object set with the `ViewPoint`, which is the + /// file name of the viewpoint (.bcfv). + /// + /// Viewpoint file name. + /// Returns the builder object. TBuilder SetViewPoint(string viewpoint); + + /// + /// Returns the builder object set with the `Snapshot`, which is the + /// file name of the snapshot (png or jpeg). + /// + /// Snapshot file name. + /// Returns the builder object. + TBuilder SetSnapshot(string snapshot); + + /// + /// Returns the builder object set with the `SnapshotData`, which is the + /// Base64 string of snapshot data. + /// + /// Base64 string of snapshot data. + /// Returns the builder object. + TBuilder SetSnapshotData(string snapshotData); + + /// + /// Returns the builder object set with the `Index`, which is the + /// Guid of the viewpoint. + /// + /// Guid of the viewpoint. + /// Returns the builder object. + TBuilder SetIndex(int index); + + /// + /// Returns the builder object set with the `VisualizationInfo`, which + /// contains information of components related to the topic, + /// camera settings, and possible markup and clipping information. + /// + /// The builder for `VisualizationInfo` + /// Returns the builder object. + TBuilder SetVisualizationInfo(Action builder); } \ No newline at end of file diff --git a/src/bcf-toolkit/Builder/Bcf30/MarkupBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/MarkupBuilder.cs index 198900f..c028384 100644 --- a/src/bcf-toolkit/Builder/Bcf30/MarkupBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/MarkupBuilder.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using BcfToolkit.Builder.Bcf30.Interfaces; using BcfToolkit.Builder.Interfaces; using BcfToolkit.Model; @@ -14,7 +13,8 @@ public partial class MarkupBuilder : BimSnippetBuilder, DocumentReferenceBuilder, CommentBuilder, - VisualizationInfoBuilder>, + VisualizationInfoBuilder, + ViewPointBuilder>, IDefaultBuilder { private readonly Markup _markup = new(); @@ -157,6 +157,14 @@ public MarkupBuilder AddViewPoint( return this; } + public MarkupBuilder AddViewPoint(Action builder) { + var viewPoint = + (ViewPoint)BuilderUtils + .BuildItem(builder); + _markup.Topic.Viewpoints.Add(viewPoint); + return this; + } + public MarkupBuilder AddRelatedTopic(string relatedTopicGuid) { var topic = new TopicRelatedTopicsRelatedTopic { Guid = relatedTopicGuid @@ -184,6 +192,4 @@ public MarkupBuilder WithDefaults() { public Markup Build() { return BuilderUtils.ValidateItem(_markup); } - - } \ No newline at end of file diff --git a/src/bcf-toolkit/Builder/Bcf30/ViewPointBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/ViewPointBuilder.cs new file mode 100644 index 0000000..6dad90f --- /dev/null +++ b/src/bcf-toolkit/Builder/Bcf30/ViewPointBuilder.cs @@ -0,0 +1,49 @@ +using System; +using BcfToolkit.Builder.Bcf30.Interfaces; +using BcfToolkit.Model; +using BcfToolkit.Model.Bcf30; + +namespace BcfToolkit.Builder.Bcf30; + +public class ViewPointBuilder : IViewPointBuilder< + ViewPointBuilder, VisualizationInfoBuilder> { + private readonly ViewPoint _viewPoint = new(); + + public ViewPointBuilder SetViewPoint(string viewpoint) { + _viewPoint.Viewpoint = viewpoint; + return this; + } + + public ViewPointBuilder SetSnapshot(string snapshot) { + _viewPoint.Snapshot = snapshot; + return this; + } + + public ViewPointBuilder SetIndex(int index) { + _viewPoint.Index = index; + return this; + } + + public ViewPointBuilder SetVisualizationInfo( + Action builder) { + var visInfo = + (VisualizationInfo)BuilderUtils + .BuildItem(builder); + _viewPoint.VisualizationInfo = visInfo; + return this; + } + + public ViewPointBuilder SetGuid(string guid) { + _viewPoint.Guid = guid; + return this; + } + + public ViewPointBuilder SetSnapshotData(string? snapshotData) { + _viewPoint.SnapshotData = snapshotData; + return this; + } + + public ViewPoint Build() { + return BuilderUtils.ValidateItem(_viewPoint); + } +} \ No newline at end of file diff --git a/src/tests/Converter/Bcf21/ConverterTests.cs b/src/tests/Converter/Bcf21/ConverterTests.cs index 49314db..b7aeacd 100644 --- a/src/tests/Converter/Bcf21/ConverterTests.cs +++ b/src/tests/Converter/Bcf21/ConverterTests.cs @@ -37,6 +37,9 @@ public void BcfToJsonSampleFilesTest() { _converter.BcfZipToJson( "Resources/Bcf/v2.1/UserAssignment.bcfzip", "Resources/output/json/v2.1/UserAssignment"); + _converter.BcfZipToJson( + "Resources/Bcf/v2.1/BcfVersionAndTopics.bcfzip", + "Resources/output/json/v2.1/BcfVersionAndTopics"); } [Test] diff --git a/src/tests/Resources/Bcf/v2.1/MiniSolibri.bcfzip b/src/tests/Resources/Bcf/v2.1/MiniSolibri.bcfzip new file mode 100644 index 0000000000000000000000000000000000000000..aff6969d4b1d6a95a4e94d8dc17dc9e2a243893c GIT binary patch literal 8312 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 H^WXmfjJy5U literal 0 HcmV?d00001 diff --git a/src/tests/WorkerTests.cs b/src/tests/WorkerTests.cs index a9e5ba8..27d0313 100644 --- a/src/tests/WorkerTests.cs +++ b/src/tests/WorkerTests.cs @@ -22,6 +22,15 @@ public void Setup() { private Worker _worker; + [Test] + [Category("BCF v2.1")] + public async Task BuildBcfFromV21StreamTest() { + const string path = "Resources/Bcf/v2.1/MiniSolibri.bcfzip"; + await using var stream = + new FileStream(path, FileMode.Open, FileAccess.Read); + await _worker.BuildBcfFromStream(stream); + } + [Test] [Category("BCF v2.1")] public async Task BuildBcfFromV21StreamSamplesTests() { @@ -31,6 +40,7 @@ public async Task BuildBcfFromV21StreamSamplesTests() { "Resources/Bcf/v2.1/ExternalBIMSnippet.bcfzip", "Resources/Bcf/v2.1/MaximumInformation.bcfzip", "Resources/Bcf/v2.1/MinimumInformation.bcfzip", + "Resources/Bcf/v2.1/MiniSolibri.bcfzip", // "Resources/Bcf/v2.1/RelatedTopics.bcfzip", // comment property is empty // "Resources/Bcf/v2.1/SingleVisibleWall.bcfzip", // comment property is empty // "Resources/Bcf/v2.1/UserAssignment.bcfzip" // description is empty From 78be45ff40eec83aa292fd55ba9b7fa3837d0cac Mon Sep 17 00:00:00 2001 From: Balint Bende Date: Wed, 5 Jun 2024 14:35:11 +0200 Subject: [PATCH 09/29] [BMSPT-291] fixed possible null pointer issues --- bcf-toolkit.sln.DotSettings.user | 4 +- .../Builder/Bcf21/CommentBuilder.cs | 3 +- .../Builder/Bcf21/MarkupBuilder.cs | 1 + .../Builder/Bcf21/MarkupBuilderExtensions.cs | 4 +- .../OrthogonalCameraBuilderExtensions.cs | 27 +++++++----- .../PerspectiveCameraBuilderExtensions.cs | 27 +++++++----- .../Builder/Bcf21/ProjectExtensionBuilder.cs | 8 +--- .../Builder/Bcf21/VisualizationInfoBuilder.cs | 8 ++-- .../VisualizationInfoBuilderExtension.cs | 9 ++-- .../Builder/Bcf30/CommentBuilder.cs | 3 +- ...er.cs => ComponentColoringColorBuilder.cs} | 12 ++--- ...ComponentColoringColorBuilderExtensions.cs | 4 +- .../Builder/Bcf30/MarkupBuilder.cs | 4 +- .../Builder/Bcf30/MarkupBuilderExensions.cs | 2 +- .../Builder/Bcf30/ProjectInfoBuilder.cs | 1 + .../Builder/Bcf30/VisualizationInfoBuilder.cs | 12 ++--- .../VisualizationInfoBuilderExtensions.cs | 6 +-- .../Converter/Bcf21/SchemaConverterToBcf30.cs | 2 +- .../Converter/Bcf30/SchemaConverterToBcf21.cs | 44 +++++++------------ .../Model/Bcf21/MarkupExtensions.cs | 11 ++++- .../Model/Bcf21/ProjectExtensions.cs | 9 +++- .../Model/Bcf21/VisInfoExtensions.cs | 8 ++++ .../Model/Bcf30/MarkupExtensions.cs | 19 ++++++-- .../Model/Bcf30/VisInfoExtensions.cs | 8 ++++ 24 files changed, 132 insertions(+), 104 deletions(-) rename src/bcf-toolkit/Builder/Bcf30/{ComponentColoringComponentColoringColorBuilder.cs => ComponentColoringColorBuilder.cs} (56%) diff --git a/bcf-toolkit.sln.DotSettings.user b/bcf-toolkit.sln.DotSettings.user index 9a8c03c..7fb0c67 100644 --- a/bcf-toolkit.sln.DotSettings.user +++ b/bcf-toolkit.sln.DotSettings.user @@ -24,7 +24,7 @@ <SessionState ContinuousTestingMode="0" Name="All tests from Solution #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Solution /> </SessionState> - <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Solution /> </SessionState> @@ -33,6 +33,8 @@ + + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Solution /> </SessionState> diff --git a/src/bcf-toolkit/Builder/Bcf21/CommentBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/CommentBuilder.cs index 3c4da77..2380fd2 100644 --- a/src/bcf-toolkit/Builder/Bcf21/CommentBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/CommentBuilder.cs @@ -31,8 +31,7 @@ public CommentBuilder SetCommentProperty(string? comment) { } public CommentBuilder SetViewPointGuid(string guid) { - _comment.Viewpoint ??= new CommentViewpoint(); - _comment.Viewpoint.Guid = guid; + _comment.GetCommentViewPointInstance().Guid = guid; return this; } diff --git a/src/bcf-toolkit/Builder/Bcf21/MarkupBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/MarkupBuilder.cs index 825a4a2..d678b4c 100644 --- a/src/bcf-toolkit/Builder/Bcf21/MarkupBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/MarkupBuilder.cs @@ -19,6 +19,7 @@ public partial class MarkupBuilder : private readonly Markup _markup = new(); public MarkupBuilder() { + // this is a required field so it must be initialized _markup.Topic = new Topic(); } diff --git a/src/bcf-toolkit/Builder/Bcf21/MarkupBuilderExtensions.cs b/src/bcf-toolkit/Builder/Bcf21/MarkupBuilderExtensions.cs index 0de55dc..89ee138 100644 --- a/src/bcf-toolkit/Builder/Bcf21/MarkupBuilderExtensions.cs +++ b/src/bcf-toolkit/Builder/Bcf21/MarkupBuilderExtensions.cs @@ -5,8 +5,8 @@ namespace BcfToolkit.Builder.Bcf21; public partial class MarkupBuilder { - public MarkupBuilder AddHeaderFiles(List files) { - files.ForEach(_markup.Header.Add); + public MarkupBuilder AddHeaderFiles(List? files) { + files?.ForEach(_markup.Header.Add); return this; } diff --git a/src/bcf-toolkit/Builder/Bcf21/OrthogonalCameraBuilderExtensions.cs b/src/bcf-toolkit/Builder/Bcf21/OrthogonalCameraBuilderExtensions.cs index 59e8959..5103886 100644 --- a/src/bcf-toolkit/Builder/Bcf21/OrthogonalCameraBuilderExtensions.cs +++ b/src/bcf-toolkit/Builder/Bcf21/OrthogonalCameraBuilderExtensions.cs @@ -5,26 +5,29 @@ namespace BcfToolkit.Builder.Bcf21; public partial class OrthogonalCameraBuilder { public OrthogonalCameraBuilder SetCameraViewPoint(double x, double y, double z) { - _camera.CameraViewPoint ??= new Point(); - _camera.CameraViewPoint.X = x; - _camera.CameraViewPoint.Y = y; - _camera.CameraViewPoint.Z = z; + _camera.CameraViewPoint = new Point { + X = x, + Y = y, + Z = z + }; return this; } public OrthogonalCameraBuilder SetCameraDirection(double x, double y, double z) { - _camera.CameraDirection ??= new Direction(); - _camera.CameraDirection.X = x; - _camera.CameraDirection.Y = y; - _camera.CameraDirection.Z = z; + _camera.CameraDirection = new Direction { + X = x, + Y = y, + Z = z + }; return this; } public OrthogonalCameraBuilder SetCameraUpVector(double x, double y, double z) { - _camera.CameraUpVector ??= new Direction(); - _camera.CameraUpVector.X = x; - _camera.CameraUpVector.Y = y; - _camera.CameraUpVector.Z = z; + _camera.CameraUpVector = new Direction { + X = x, + Y = y, + Z = z + }; return this; } diff --git a/src/bcf-toolkit/Builder/Bcf21/PerspectiveCameraBuilderExtensions.cs b/src/bcf-toolkit/Builder/Bcf21/PerspectiveCameraBuilderExtensions.cs index 86482a6..f29b8f2 100644 --- a/src/bcf-toolkit/Builder/Bcf21/PerspectiveCameraBuilderExtensions.cs +++ b/src/bcf-toolkit/Builder/Bcf21/PerspectiveCameraBuilderExtensions.cs @@ -4,26 +4,29 @@ namespace BcfToolkit.Builder.Bcf21; public partial class PerspectiveCameraBuilder { public PerspectiveCameraBuilder SetCameraViewPoint(double x, double y, double z) { - _camera.CameraViewPoint ??= new Point(); - _camera.CameraViewPoint.X = x; - _camera.CameraViewPoint.Y = y; - _camera.CameraViewPoint.Z = z; + _camera.CameraViewPoint = new Point { + X = x, + Y = y, + Z = z + }; return this; } public PerspectiveCameraBuilder SetCameraDirection(double x, double y, double z) { - _camera.CameraDirection ??= new Direction(); - _camera.CameraDirection.X = x; - _camera.CameraDirection.Y = y; - _camera.CameraDirection.Z = z; + _camera.CameraDirection = new Direction { + X = x, + Y = y, + Z = z + }; return this; } public PerspectiveCameraBuilder SetCameraUpVector(double x, double y, double z) { - _camera.CameraUpVector ??= new Direction(); - _camera.CameraUpVector.X = x; - _camera.CameraUpVector.Y = y; - _camera.CameraUpVector.Z = z; + _camera.CameraUpVector = new Direction { + X = x, + Y = y, + Z = z + }; return this; } diff --git a/src/bcf-toolkit/Builder/Bcf21/ProjectExtensionBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/ProjectExtensionBuilder.cs index a12c2b0..4e6b667 100644 --- a/src/bcf-toolkit/Builder/Bcf21/ProjectExtensionBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/ProjectExtensionBuilder.cs @@ -10,17 +10,13 @@ public class ProjectExtensionBuilder : IDefaultBuilder { private readonly ProjectExtension _project = new(); - public ProjectExtensionBuilder() { - _project.Project = new Project(); - } - public ProjectExtensionBuilder SetProjectName(string name) { - _project.Project.Name = name; + _project.GetProjectInstance().Name = name; return this; } public ProjectExtensionBuilder SetProjectId(string id) { - _project.Project.ProjectId = id; + _project.GetProjectInstance().ProjectId = id; return this; } diff --git a/src/bcf-toolkit/Builder/Bcf21/VisualizationInfoBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/VisualizationInfoBuilder.cs index 8b063a8..8f1a3e1 100644 --- a/src/bcf-toolkit/Builder/Bcf21/VisualizationInfoBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/VisualizationInfoBuilder.cs @@ -29,7 +29,7 @@ public VisualizationInfoBuilder AddSelection(Action builder) { var selection = (Component)BuilderUtils.BuildItem(builder); - _visualizationInfo.Components.Selection.Add(selection); + _visualizationInfo.GetComponentsInstance().Selection.Add(selection); return this; } @@ -38,7 +38,7 @@ public VisualizationInfoBuilder SetVisibility( var visibility = (ComponentVisibility)BuilderUtils .BuildItem(builder); - _visualizationInfo.Components.Visibility = visibility; + _visualizationInfo.GetComponentsInstance().Visibility = visibility; return this; } @@ -46,7 +46,7 @@ public VisualizationInfoBuilder AddColoring(Action( builder); - _visualizationInfo.Components.Coloring.Add(color); + _visualizationInfo.GetComponentsInstance().Coloring.Add(color); return this; } @@ -94,7 +94,7 @@ public VisualizationInfoBuilder SetViewSetupHints( var viewSetupHints = (ViewSetupHints)BuilderUtils .BuildItem(builder); - _visualizationInfo.Components.ViewSetupHints = viewSetupHints; + _visualizationInfo.GetComponentsInstance().ViewSetupHints = viewSetupHints; return this; } diff --git a/src/bcf-toolkit/Builder/Bcf21/VisualizationInfoBuilderExtension.cs b/src/bcf-toolkit/Builder/Bcf21/VisualizationInfoBuilderExtension.cs index e82bb64..e66e3a6 100644 --- a/src/bcf-toolkit/Builder/Bcf21/VisualizationInfoBuilderExtension.cs +++ b/src/bcf-toolkit/Builder/Bcf21/VisualizationInfoBuilderExtension.cs @@ -8,15 +8,13 @@ namespace BcfToolkit.Builder.Bcf21; public partial class VisualizationInfoBuilder { public VisualizationInfoBuilder AddSelections(List components) { - _visualizationInfo.Components ??= new Components(); - components.ForEach(_visualizationInfo.Components.Selection.Add); + components.ForEach(_visualizationInfo.GetComponentsInstance().Selection.Add); return this; } public VisualizationInfoBuilder SetVisibility( ComponentVisibility componentVisibility) { - _visualizationInfo.Components ??= new Components(); - _visualizationInfo.Components.Visibility = componentVisibility; + _visualizationInfo.GetComponentsInstance().Visibility = componentVisibility; return this; } @@ -26,8 +24,7 @@ public VisualizationInfoBuilder AddBitmaps(List bitmaps } public VisualizationInfoBuilder AddColorings(List colorings) { - _visualizationInfo.Components ??= new Components(); - colorings.ForEach(_visualizationInfo.Components.Coloring.Add); + colorings.ForEach(_visualizationInfo.GetComponentsInstance().Coloring.Add); return this; } diff --git a/src/bcf-toolkit/Builder/Bcf30/CommentBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/CommentBuilder.cs index 1d81902..9953903 100644 --- a/src/bcf-toolkit/Builder/Bcf30/CommentBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/CommentBuilder.cs @@ -32,8 +32,7 @@ public CommentBuilder SetComment(string comment) { public CommentBuilder SetViewPointGuid(string? guid) { if (guid == null) return this; - _comment.Viewpoint ??= new CommentViewpoint(); - _comment.Viewpoint.Guid = guid; + _comment.GetCommentViewPointInstance().Guid = guid; return this; } diff --git a/src/bcf-toolkit/Builder/Bcf30/ComponentColoringComponentColoringColorBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/ComponentColoringColorBuilder.cs similarity index 56% rename from src/bcf-toolkit/Builder/Bcf30/ComponentColoringComponentColoringColorBuilder.cs rename to src/bcf-toolkit/Builder/Bcf30/ComponentColoringColorBuilder.cs index 479ca7a..e9e75df 100644 --- a/src/bcf-toolkit/Builder/Bcf30/ComponentColoringComponentColoringColorBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/ComponentColoringColorBuilder.cs @@ -6,17 +6,17 @@ namespace BcfToolkit.Builder.Bcf30; -public partial class ComponentColoringComponentColoringColorBuilder : - IComponentColoringColorBuilder, - IDefaultBuilder { +public partial class ComponentColoringColorBuilder : + IComponentColoringColorBuilder, + IDefaultBuilder { private readonly ComponentColoringColor _color = new(); - public ComponentColoringComponentColoringColorBuilder SetColor(string color) { + public ComponentColoringColorBuilder SetColor(string color) { _color.Color = color; return this; } - public ComponentColoringComponentColoringColorBuilder AddComponent(Action builder) { + public ComponentColoringColorBuilder AddComponent(Action builder) { var component = (Component)BuilderUtils.BuildItem(builder); _color.Components.Add(component); @@ -27,7 +27,7 @@ public ComponentColoringColor Build() { return BuilderUtils.ValidateItem(_color); } - public ComponentColoringComponentColoringColorBuilder WithDefaults() { + public ComponentColoringColorBuilder WithDefaults() { this .SetColor("40E0D0") .AddComponent(comp => comp.WithDefaults()); diff --git a/src/bcf-toolkit/Builder/Bcf30/ComponentColoringColorBuilderExtensions.cs b/src/bcf-toolkit/Builder/Bcf30/ComponentColoringColorBuilderExtensions.cs index ef6b0c2..a25bc88 100644 --- a/src/bcf-toolkit/Builder/Bcf30/ComponentColoringColorBuilderExtensions.cs +++ b/src/bcf-toolkit/Builder/Bcf30/ComponentColoringColorBuilderExtensions.cs @@ -3,8 +3,8 @@ namespace BcfToolkit.Builder.Bcf30; -public partial class ComponentColoringComponentColoringColorBuilder { - public ComponentColoringComponentColoringColorBuilder AddComponents(List components) { +public partial class ComponentColoringColorBuilder { + public ComponentColoringColorBuilder AddComponents(List components) { components.ForEach(_color.Components.Add); return this; } diff --git a/src/bcf-toolkit/Builder/Bcf30/MarkupBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/MarkupBuilder.cs index c028384..e9ceb10 100644 --- a/src/bcf-toolkit/Builder/Bcf30/MarkupBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/MarkupBuilder.cs @@ -19,8 +19,8 @@ public partial class MarkupBuilder : private readonly Markup _markup = new(); public MarkupBuilder() { + // this is a required field so it must be initialized _markup.Topic = new Topic(); - _markup.Header = new Header(); } public MarkupBuilder SetGuid(string guid) { @@ -42,7 +42,7 @@ public MarkupBuilder AddHeaderFile( Action builder) { var file = (File)BuilderUtils.BuildItem(builder); - _markup.Header.Files.Add(file); + _markup.GetHeaderInstance().Files.Add(file); return this; } diff --git a/src/bcf-toolkit/Builder/Bcf30/MarkupBuilderExensions.cs b/src/bcf-toolkit/Builder/Bcf30/MarkupBuilderExensions.cs index 5748579..89d4ef1 100644 --- a/src/bcf-toolkit/Builder/Bcf30/MarkupBuilderExensions.cs +++ b/src/bcf-toolkit/Builder/Bcf30/MarkupBuilderExensions.cs @@ -5,7 +5,7 @@ namespace BcfToolkit.Builder.Bcf30; public partial class MarkupBuilder { public MarkupBuilder AddHeaderFiles(List files) { - files.ForEach(_markup.Header.Files.Add); + files.ForEach(_markup.GetHeaderInstance().Files.Add); return this; } diff --git a/src/bcf-toolkit/Builder/Bcf30/ProjectInfoBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/ProjectInfoBuilder.cs index aa0eadb..6f19f8b 100644 --- a/src/bcf-toolkit/Builder/Bcf30/ProjectInfoBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/ProjectInfoBuilder.cs @@ -11,6 +11,7 @@ public class ProjectInfoBuilder : private readonly ProjectInfo _project = new(); public ProjectInfoBuilder() { + // this is a required field so it must be initialized _project.Project = new Project(); } diff --git a/src/bcf-toolkit/Builder/Bcf30/VisualizationInfoBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/VisualizationInfoBuilder.cs index d0b033c..6eb8897 100644 --- a/src/bcf-toolkit/Builder/Bcf30/VisualizationInfoBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/VisualizationInfoBuilder.cs @@ -10,7 +10,7 @@ public partial class VisualizationInfoBuilder : VisualizationInfoBuilder, ComponentBuilder, VisibilityBuilder, - ComponentColoringComponentColoringColorBuilder, + ComponentColoringColorBuilder, OrthogonalCameraBuilder, PerspectiveCameraBuilder, LineBuilder, @@ -28,7 +28,7 @@ public VisualizationInfoBuilder AddSelection(Action builder) { var selection = BuilderUtils.BuildItem(builder); - _visualizationInfo.Components.Selection.Add(selection); + _visualizationInfo.GetComponentsInstance().Selection.Add(selection); return this; } @@ -37,15 +37,15 @@ public VisualizationInfoBuilder SetVisibility( var visibility = BuilderUtils .BuildItem(builder); - _visualizationInfo.Components.Visibility = visibility; + _visualizationInfo.GetComponentsInstance().Visibility = visibility; return this; } - public VisualizationInfoBuilder AddColoring(Action builder) { + public VisualizationInfoBuilder AddColoring(Action builder) { var color = - BuilderUtils.BuildItem( + BuilderUtils.BuildItem( builder); - _visualizationInfo.Components.Coloring.Add(color); + _visualizationInfo.GetComponentsInstance().Coloring.Add(color); return this; } diff --git a/src/bcf-toolkit/Builder/Bcf30/VisualizationInfoBuilderExtensions.cs b/src/bcf-toolkit/Builder/Bcf30/VisualizationInfoBuilderExtensions.cs index d381040..0d1c2e9 100644 --- a/src/bcf-toolkit/Builder/Bcf30/VisualizationInfoBuilderExtensions.cs +++ b/src/bcf-toolkit/Builder/Bcf30/VisualizationInfoBuilderExtensions.cs @@ -5,15 +5,13 @@ namespace BcfToolkit.Builder.Bcf30; public partial class VisualizationInfoBuilder { public VisualizationInfoBuilder AddSelections(List? selections) { - _visualizationInfo.Components ??= new Components(); - selections?.ForEach(_visualizationInfo.Components.Selection.Add); + selections?.ForEach(_visualizationInfo.GetComponentsInstance().Selection.Add); return this; } public VisualizationInfoBuilder AddColorings( List? colors) { - _visualizationInfo.Components ??= new Components(); - colors?.ForEach(_visualizationInfo.Components.Coloring.Add); + colors?.ForEach(_visualizationInfo.GetComponentsInstance().Coloring.Add); return this; } diff --git a/src/bcf-toolkit/Converter/Bcf21/SchemaConverterToBcf30.cs b/src/bcf-toolkit/Converter/Bcf21/SchemaConverterToBcf30.cs index e2182f1..0ec40e9 100644 --- a/src/bcf-toolkit/Converter/Bcf21/SchemaConverterToBcf30.cs +++ b/src/bcf-toolkit/Converter/Bcf21/SchemaConverterToBcf30.cs @@ -214,7 +214,7 @@ private static Model.Bcf30.Component ConvertComponent( private static Model.Bcf30.ComponentColoringColor ConvertColor( Model.Bcf21.ComponentColoringColor from) { - var builder = new ComponentColoringComponentColoringColorBuilder(); + var builder = new ComponentColoringColorBuilder(); return builder .AddComponents(from.Component.Select(ConvertComponent).ToList()) .SetColor(from.Color) diff --git a/src/bcf-toolkit/Converter/Bcf30/SchemaConverterToBcf21.cs b/src/bcf-toolkit/Converter/Bcf30/SchemaConverterToBcf21.cs index 3e94c80..ca9d020 100644 --- a/src/bcf-toolkit/Converter/Bcf30/SchemaConverterToBcf21.cs +++ b/src/bcf-toolkit/Converter/Bcf30/SchemaConverterToBcf21.cs @@ -55,12 +55,11 @@ private static Model.Bcf21.ProjectExtension? private static Model.Bcf21.Markup ConvertMarkup(Model.Bcf30.Markup from) { var builder = new MarkupBuilder(); - return - builder + return builder .SetGuid(from.Topic.Guid) .SetTopicType(from.Topic.TopicType) .SetTopicStatus(from.Topic.TopicStatus) - .AddHeaderFiles(from.Header.Files.Select(ConvertHeaderFile).ToList()) + .AddHeaderFiles(from.Header?.Files?.Select(ConvertHeaderFile).ToList()) .AddReferenceLinks(from.Topic.ReferenceLinks.ToList()) .SetTitle(from.Topic.Title) .SetPriority(from.Topic.Priority) @@ -88,8 +87,7 @@ private static Model.Bcf21.Markup ConvertMarkup(Model.Bcf30.Markup from) { private static Model.Bcf21.HeaderFile ConvertHeaderFile(Model.Bcf30.File from) { var builder = new HeaderFileBuilder(); - return - builder + return builder .SetIfcProject(from.IfcProject) .SetIfcSpatialStructureElement(from.IfcSpatialStructureElement) .SetIsExternal(from.IsExternal) @@ -105,8 +103,7 @@ private static Model.Bcf21.HeaderFile ConvertHeaderFile(Model.Bcf30.File from) { } var builder = new BimSnippetBuilder(); - return - builder + return builder .SetSnippetType(from.SnippetType) .SetIsExternal(from.IsExternal) .SetReference(from.Reference) @@ -119,8 +116,7 @@ private static Model.Bcf21.TopicDocumentReference ConvertDocumentReference( var builder = new DocumentReferenceBuilder(); var isExternal = string.IsNullOrEmpty(from.Url); - return - builder + return builder .SetGuid(from.Guid) .SetDescription(from.Description) .SetIsExternal(isExternal) @@ -153,8 +149,7 @@ private static Model.Bcf21.ViewPoint ConvertViewPoint( var builder = new ViewPointBuilder(); - return - builder + return builder .SetVisualizationInfo(ConvertVisualizationInfo(from.VisualizationInfo)) .SetSnapshot(from.Snapshot) .SetIndex(from.Index) @@ -170,8 +165,7 @@ private static Model.Bcf21.ViewPoint ConvertViewPoint( var builder = new VisualizationInfoBuilder(); - return - builder + return builder .SetGuid(from.Guid) .AddSelections( from.Components.Selection.Select(ConvertComponent).ToList()) @@ -194,8 +188,7 @@ private static Model.Bcf21.ViewPoint ConvertViewPoint( var builder = new OrthogonalCameraBuilder(); - return - builder + return builder .SetCameraDirection(from.CameraDirection.X, from.CameraDirection.Y, from.CameraDirection.Z) .SetCameraViewPoint(from.CameraViewPoint.X, from.CameraViewPoint.Y, from.CameraViewPoint.Z) .SetCameraUpVector(from.CameraUpVector.X, from.CameraUpVector.Y, from.CameraUpVector.Z) @@ -212,8 +205,7 @@ private static Model.Bcf21.ViewPoint ConvertViewPoint( var builder = new PerspectiveCameraBuilder(); - return - builder + return builder .SetCameraDirection(from.CameraDirection.X, from.CameraDirection.Y, from.CameraDirection.Z) .SetCameraViewPoint(from.CameraViewPoint.X, from.CameraViewPoint.Y, from.CameraViewPoint.Z) .SetCameraUpVector(from.CameraUpVector.X, from.CameraUpVector.Y, from.CameraUpVector.Z) @@ -226,8 +218,7 @@ private static Model.Bcf21.ClippingPlane ConvertClippingPlane( Model.Bcf30.ClippingPlane from) { var builder = new ClippingPlaneBuilder(); - return - builder + return builder .SetLocation(from.Location.X, from.Location.Y, from.Location.Z) .SetDirection(from.Direction.X, from.Direction.Y, from.Direction.Z) .Build(); @@ -237,8 +228,7 @@ private static Model.Bcf21.Line ConvertLine(Model.Bcf30.Line from) { var builder = new LineBuilder(); - return - builder + return builder .SetStartPoint(from.StartPoint.X, from.StartPoint.Y, from.StartPoint.Z) .SetEndPoint(from.EndPoint.X, from.EndPoint.Y, from.EndPoint.Z) .Build(); @@ -248,8 +238,7 @@ private static Model.Bcf21.ComponentColoringColor ConvertColoring( Model.Bcf30.ComponentColoringColor from) { var builder = new ComponentColoringColorBuilder(); - return - builder + return builder .SetColor(from.Color) .AddComponents(from.Components.Select(ConvertComponent).ToList()) .Build(); @@ -259,8 +248,7 @@ private static Model.Bcf21.VisualizationInfoBitmap ConvertBitmap( Model.Bcf30.Bitmap from) { var builder = new BitmapBuilder(); - return - builder + return builder .SetFormat(from.Format.ToString()) .SetReference(from.Reference) .SetLocation(from.Location.X, from.Location.Y, from.Location.Z) @@ -274,8 +262,7 @@ private static Model.Bcf21.Component ConvertComponent( Model.Bcf30.Component from) { var builder = new ComponentBuilder(); - return - builder + return builder .SetIfcGuid(from.IfcGuid) .SetOriginatingSystem(from.OriginatingSystem) .SetAuthoringToolId(from.AuthoringToolId) @@ -286,8 +273,7 @@ private static Model.Bcf21.ComponentVisibility ConvertVisibility( Model.Bcf30.ComponentVisibility from) { var builder = new VisibilityBuilder(); - return - builder + return builder .SetDefaultVisibility(from.DefaultVisibility) .AddExceptions(from.Exceptions.Select(ConvertComponent).ToList()) .Build(); diff --git a/src/bcf-toolkit/Model/Bcf21/MarkupExtensions.cs b/src/bcf-toolkit/Model/Bcf21/MarkupExtensions.cs index 67b8e57..753a94d 100644 --- a/src/bcf-toolkit/Model/Bcf21/MarkupExtensions.cs +++ b/src/bcf-toolkit/Model/Bcf21/MarkupExtensions.cs @@ -35,8 +35,15 @@ public void SetVisualizationInfo(IVisualizationInfo? visInfo) { public string? SnapshotData { get; set; } } -public partial class VisualizationInfo : IVisualizationInfo { } public partial class HeaderFile : IHeaderFile { } public partial class BimSnippet : IBimSnippet { } public partial class TopicDocumentReference : IDocReference { } -public partial class Comment : IComment { } \ No newline at end of file + +public partial class Comment : IComment { + // This method that controls the access to the `CommentViewpoint` instance. + // On the first run, it creates an instance of the object. On subsequent runs, + // it returns the existing object. + public CommentViewpoint GetCommentViewPointInstance() { + return Viewpoint ??= new CommentViewpoint(); + } +} \ No newline at end of file diff --git a/src/bcf-toolkit/Model/Bcf21/ProjectExtensions.cs b/src/bcf-toolkit/Model/Bcf21/ProjectExtensions.cs index 2075ad9..9f5519d 100644 --- a/src/bcf-toolkit/Model/Bcf21/ProjectExtensions.cs +++ b/src/bcf-toolkit/Model/Bcf21/ProjectExtensions.cs @@ -1,3 +1,10 @@ namespace BcfToolkit.Model.Bcf21; -public partial class ProjectExtension : IProject { } \ No newline at end of file +public partial class ProjectExtension : IProject { + // This method that controls the access to the `Project` instance. + // On the first run, it creates an instance of the object. On subsequent runs, + // it returns the existing object. + public Project GetProjectInstance() { + return Project ??= new Project(); + } +} \ No newline at end of file diff --git a/src/bcf-toolkit/Model/Bcf21/VisInfoExtensions.cs b/src/bcf-toolkit/Model/Bcf21/VisInfoExtensions.cs index d2a3242..5f39d81 100644 --- a/src/bcf-toolkit/Model/Bcf21/VisInfoExtensions.cs +++ b/src/bcf-toolkit/Model/Bcf21/VisInfoExtensions.cs @@ -1,5 +1,13 @@ namespace BcfToolkit.Model.Bcf21; +public partial class VisualizationInfo : IVisualizationInfo { + // This method that controls the access to the `Components` instance. + // On the first run, it creates an instance of the object. On subsequent runs, + // it returns the existing object. + public Components GetComponentsInstance() { + return Components ??= new Components(); + } +} public partial class ViewSetupHints : IViewSetupHints { } public partial class Component : IComponent { } public partial class ComponentVisibility : IVisibility { } diff --git a/src/bcf-toolkit/Model/Bcf30/MarkupExtensions.cs b/src/bcf-toolkit/Model/Bcf30/MarkupExtensions.cs index dd1a716..48a8dd8 100644 --- a/src/bcf-toolkit/Model/Bcf30/MarkupExtensions.cs +++ b/src/bcf-toolkit/Model/Bcf30/MarkupExtensions.cs @@ -12,6 +12,13 @@ public ITopic GetTopic() { public IViewPoint? GetFirstViewPoint() { return Topic.Viewpoints?.FirstOrDefault(); } + + // This method that controls the access to the `Header` instance. + // On the first run, it creates an instance of the object. On subsequent runs, + // it returns the existing object. + public Header GetHeaderInstance() { + return Header ??= new Header(); + } } public partial class Topic : ITopic { } @@ -34,9 +41,15 @@ public void SetVisualizationInfo(IVisualizationInfo? visInfo) { [JsonProperty("snapshot_data")] public string? SnapshotData { get; set; } } - -public partial class VisualizationInfo : IVisualizationInfo { } public partial class File : IHeaderFile { } public partial class BimSnippet : IBimSnippet { } public partial class DocumentReference : IDocReference { } -public partial class Comment : IComment { } \ No newline at end of file + +public partial class Comment : IComment { + // This method that controls the access to the `CommentViewpoint` instance. + // On the first run, it creates an instance of the object. On subsequent runs, + // it returns the existing object. + public CommentViewpoint GetCommentViewPointInstance() { + return Viewpoint ??= new CommentViewpoint(); + } +} \ No newline at end of file diff --git a/src/bcf-toolkit/Model/Bcf30/VisInfoExtensions.cs b/src/bcf-toolkit/Model/Bcf30/VisInfoExtensions.cs index dfdb8b9..8b8f915 100644 --- a/src/bcf-toolkit/Model/Bcf30/VisInfoExtensions.cs +++ b/src/bcf-toolkit/Model/Bcf30/VisInfoExtensions.cs @@ -1,5 +1,13 @@ namespace BcfToolkit.Model.Bcf30; +public partial class VisualizationInfo : IVisualizationInfo { + // This method that controls the access to the `Components` instance. + // On the first run, it creates an instance of the object. On subsequent runs, + // it returns the existing object. + public Components GetComponentsInstance() { + return Components ??= new Components(); + } +} public partial class ViewSetupHints : IViewSetupHints { } public partial class Component : IComponent { } public partial class ComponentVisibility : IVisibility { } From eed0a80120a89b9cb475d5beac496eb980e43443 Mon Sep 17 00:00:00 2001 From: Adam Eri Date: Mon, 10 Jun 2024 15:43:57 +0200 Subject: [PATCH 10/29] Fixing incomplete byte read for images --- src/bcf-toolkit/Utils/ZipArchiveEntryExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bcf-toolkit/Utils/ZipArchiveEntryExtensions.cs b/src/bcf-toolkit/Utils/ZipArchiveEntryExtensions.cs index bf280fd..6411ae3 100644 --- a/src/bcf-toolkit/Utils/ZipArchiveEntryExtensions.cs +++ b/src/bcf-toolkit/Utils/ZipArchiveEntryExtensions.cs @@ -100,9 +100,9 @@ public static string Snapshot(this ZipArchiveEntry entry) { var extension = entry.FullName.Split(".").Last(); var mime = $"data:image/{extension};base64"; var buffer = new byte[entry.Length]; - var image = entry + entry .Open() - .Read(buffer, 0, buffer.Length); + .ReadExactly(buffer, 0, buffer.Length); var base64String = Convert.ToBase64String(buffer); return $"{mime},{base64String}"; } From 639cd552a32ad75b558c55e54da63817332d3d2a Mon Sep 17 00:00:00 2001 From: Adam Eri Date: Mon, 10 Jun 2024 15:44:06 +0200 Subject: [PATCH 11/29] lint --- src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs | 4 ++-- .../Builder/Bcf21/BcfBuilderExtensions.cs | 4 ++-- .../Bcf21/Interfaces/IBcfBuilderDelegate.cs | 2 +- src/bcf-toolkit/Program.cs | 22 +++++++++---------- src/bcf-toolkit/Utils/BcfExtensions.cs | 16 +++++++------- src/tests/Builder/BcfBuilderTests.cs | 10 ++++----- 6 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs index 2cc0a7e..440b726 100644 --- a/src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs @@ -12,12 +12,12 @@ public partial class BcfBuilder : IBcfBuilder< ProjectExtensionBuilder>, IDefaultBuilder { private readonly Bcf _bcf = new(); - + private readonly IBcfBuilderDelegate? _delegate; public BcfBuilder(IBcfBuilderDelegate? builderDelegate = null) { this._delegate = builderDelegate; - + _bcf.Version = new VersionBuilder() .WithDefaults() .Build(); diff --git a/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs b/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs index d827914..07d0d9a 100644 --- a/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs +++ b/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs @@ -13,9 +13,9 @@ public async Task ProcessStream(Stream source) { Console.WriteLine("IBcfBuilderDelegate is not set."); return; } - + await BcfExtensions.ParseMarkups(source, _delegate.MarkupCreated); - + // var extensions = await BcfExtensions.ParseExtensions(source); // _delegate.ExtensionsCreated(extensions); // diff --git a/src/bcf-toolkit/Builder/Bcf21/Interfaces/IBcfBuilderDelegate.cs b/src/bcf-toolkit/Builder/Bcf21/Interfaces/IBcfBuilderDelegate.cs index 8f59fc8..e6aa568 100644 --- a/src/bcf-toolkit/Builder/Bcf21/Interfaces/IBcfBuilderDelegate.cs +++ b/src/bcf-toolkit/Builder/Bcf21/Interfaces/IBcfBuilderDelegate.cs @@ -12,6 +12,6 @@ public delegate void where TProjectExtension : ProjectExtension; public OnMarkupCreated MarkupCreated { get; } - + public OnProjectCreated ProjectCreated { get; } } \ No newline at end of file diff --git a/src/bcf-toolkit/Program.cs b/src/bcf-toolkit/Program.cs index 83f6c5b..abe0b7c 100644 --- a/src/bcf-toolkit/Program.cs +++ b/src/bcf-toolkit/Program.cs @@ -11,10 +11,10 @@ namespace BcfToolkit; public class BcfBuilderDelegate : IBcfBuilderDelegate { public IBcfBuilderDelegate.OnMarkupCreated MarkupCreated { get; } = Console.WriteLine; - + public IBcfBuilderDelegate.OnProjectCreated ProjectCreated { get; } = Console.WriteLine; - + } internal static class Program { @@ -23,17 +23,17 @@ private static async Task Main(string[] args) { "/Users/balintbende/Developer/test/bcf/bcftoolkittest.bcfzip", FileMode.Open, FileAccess.Read); - + // var bcfBuilderDelegate = new BcfBuilderDelegate(); // var streamBuilder = new BcfBuilder(bcfBuilderDelegate); // await streamBuilder.ProcessStream(stream); - + var builder = new BcfBuilder(); var bcf = await builder.BuildInMemoryFromStream(stream); Console.WriteLine(bcf.Markups.Count); - + stream.Close(); - + // await HandleArguments(args); Environment.Exit(0); } @@ -74,20 +74,20 @@ private static async Task DoRootCommand(InputArguments arguments) { "/Users/balintbende/Developer/test/bcf/NBHU_BT_BEHF.bcfzip", FileMode.Open, FileAccess.Read); - + // var bcfBuilderDelegate = new BcfBuilderDelegate(); // var streamBuilder = new BcfBuilder(bcfBuilderDelegate); // await streamBuilder.ProcessStream(stream); - - + + var builder = new BcfBuilder(); var bcf = await builder.BuildInMemoryFromStream(stream); Console.WriteLine(bcf.Markups.Count); - + builder = null; bcf = null; - + stream.Close(); } catch (Exception e) { diff --git a/src/bcf-toolkit/Utils/BcfExtensions.cs b/src/bcf-toolkit/Utils/BcfExtensions.cs index 1ff3a00..35fa33c 100644 --- a/src/bcf-toolkit/Utils/BcfExtensions.cs +++ b/src/bcf-toolkit/Utils/BcfExtensions.cs @@ -51,7 +51,7 @@ public static class BcfExtensions { where TVisualizationInfo : IVisualizationInfo { return await _ParseMarkups(stream); } - + /// /// The method unzips the BCFzip from a stream, /// and parses the markup xml files within to create an in memory @@ -128,11 +128,11 @@ private static async Task> _ParseMarkups< if (isNewTopic) WritingOutMarkup( - markup, - viewpoint, + markup, + viewpoint, ref snapshot, currentUuid, - markups, + markups, onMarkupCreated); currentUuid = uuid; @@ -168,11 +168,11 @@ private static async Task> _ParseMarkups< if (isLastTopicEntry) WritingOutMarkup( - markup, - viewpoint, + markup, + viewpoint, ref snapshot, - currentUuid, - markups, + currentUuid, + markups, onMarkupCreated); } diff --git a/src/tests/Builder/BcfBuilderTests.cs b/src/tests/Builder/BcfBuilderTests.cs index 5c3545a..b10a7cd 100644 --- a/src/tests/Builder/BcfBuilderTests.cs +++ b/src/tests/Builder/BcfBuilderTests.cs @@ -12,14 +12,14 @@ namespace Tests.Builder; public class BcfBuilderDelegate : IBcfBuilderDelegate { public IBcfBuilderDelegate.OnMarkupCreated MarkupCreated { get; } = Console.WriteLine; - + public IBcfBuilderDelegate.OnExtensionsCreated ExtensionsCreated { get; } = Console.WriteLine; - - public IBcfBuilderDelegate.OnProjectCreated + + public IBcfBuilderDelegate.OnProjectCreated ProjectCreated { get; } = Console.WriteLine; - - public IBcfBuilderDelegate.OnDocumentCreated + + public IBcfBuilderDelegate.OnDocumentCreated DocumentCreatedCreated { get; } = Console.WriteLine; } From 6e7b79876c7d5e3e2f29bae6f5fbb814b064c112 Mon Sep 17 00:00:00 2001 From: Adam Eri Date: Wed, 12 Jun 2024 13:28:40 +0200 Subject: [PATCH 12/29] Reorganised tests to separate the delegate pattern tests --- .../Builder/Bcf30/BcfBuilderExtensions.cs | 4 +- .../Bcf30/Interfaces/IBcfBuilderDelegate.cs | 12 ++--- src/tests/Builder/BcfBuilderStreamTests.cs | 45 +++++++++++++++++++ src/tests/Builder/BcfBuilderTests.cs | 41 +++-------------- src/tests/tests.csproj | 1 + 5 files changed, 60 insertions(+), 43 deletions(-) create mode 100644 src/tests/Builder/BcfBuilderStreamTests.cs diff --git a/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs b/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs index 2ed2155..57c5df7 100644 --- a/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs +++ b/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs @@ -15,8 +15,8 @@ public async Task ProcessStream(Stream source) { return; } - // await BcfExtensions.ParseMarkups(source, - // _delegate.MarkupCreated); + await BcfExtensions.ParseMarkups(source, + _delegate.MarkupCreated); // var extensions = await BcfExtensions.ParseExtensions(source); // _delegate.ExtensionsCreated(extensions); diff --git a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilderDelegate.cs b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilderDelegate.cs index 831c5dc..a7d9676 100644 --- a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilderDelegate.cs +++ b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilderDelegate.cs @@ -6,21 +6,21 @@ namespace BcfToolkit.Builder.Bcf30.Interfaces; public interface IBcfBuilderDelegate { public delegate void OnMarkupCreated(TMarkup markup) where TMarkup : IMarkup; - + public delegate void OnProjectCreated(ProjectInfo projectInfo) where TProjectInfo : ProjectInfo; - + public delegate void OnExtensionsCreated( Extensions extensions); - + public delegate void OnDocumentCreated( Extensions documentInfo); - + public OnMarkupCreated MarkupCreated { get; } - + public OnExtensionsCreated ExtensionsCreated { get; } - + public OnProjectCreated ProjectCreated { get; } public OnDocumentCreated DocumentCreatedCreated { get; } } \ No newline at end of file diff --git a/src/tests/Builder/BcfBuilderStreamTests.cs b/src/tests/Builder/BcfBuilderStreamTests.cs new file mode 100644 index 0000000..49ed74e --- /dev/null +++ b/src/tests/Builder/BcfBuilderStreamTests.cs @@ -0,0 +1,45 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using BcfToolkit; +using NUnit.Framework; +using BcfToolkit.Builder.Bcf30; +using Moq; + +namespace Tests.Builder; + +using BcfToolkit.Builder.Bcf30.Interfaces; +using BcfToolkit.Model.Bcf30; + +public class BcfBuilderStreamTests { + private BcfBuilder _streamBuilder = null!; + + // private class BcfBuilderDelegate : IBcfBuilderDelegate { + // public IBcfBuilderDelegate.OnMarkupCreated + // OnMarkupCreated { get; } = Console.WriteLine; + // + // public IBcfBuilderDelegate.OnExtensionsCreated + // ExtensionsCreated { get; } = Console.WriteLine; + // + // public IBcfBuilderDelegate.OnProjectCreated + // ProjectCreated { get; } = Console.WriteLine; + // + // public IBcfBuilderDelegate.OnDocumentCreated + // DocumentCreatedCreated { get; } = Console.WriteLine; + // } + + [Test] + public async Task ProcessStreamTest() { + var bcfBuilderDelegateMock = new Mock(); + _streamBuilder = new BcfBuilder(bcfBuilderDelegateMock.Object); + + await using var stream = new FileStream( + "Resources/Bcf/v3.0/UserAssignment.bcfzip", + FileMode.Open, + FileAccess.Read); + await _streamBuilder.ProcessStream(stream); + + bcfBuilderDelegateMock + .Verify(d => d.MarkupCreated, Times.Once); + } +} \ No newline at end of file diff --git a/src/tests/Builder/BcfBuilderTests.cs b/src/tests/Builder/BcfBuilderTests.cs index b10a7cd..a94c704 100644 --- a/src/tests/Builder/BcfBuilderTests.cs +++ b/src/tests/Builder/BcfBuilderTests.cs @@ -1,40 +1,19 @@ -using System; using System.IO; using System.Linq; using System.Threading.Tasks; using BcfToolkit.Builder.Bcf30; -using BcfToolkit.Builder.Bcf30.Interfaces; -using BcfToolkit.Model.Bcf30; using NUnit.Framework; namespace Tests.Builder; -public class BcfBuilderDelegate : IBcfBuilderDelegate { - public IBcfBuilderDelegate.OnMarkupCreated - MarkupCreated { get; } = Console.WriteLine; - - public IBcfBuilderDelegate.OnExtensionsCreated - ExtensionsCreated { get; } = Console.WriteLine; - - public IBcfBuilderDelegate.OnProjectCreated - ProjectCreated { get; } = Console.WriteLine; - - public IBcfBuilderDelegate.OnDocumentCreated - DocumentCreatedCreated { get; } = Console.WriteLine; -} - public class BcfBuilderTests { - private BcfBuilder _streamBuilder = null!; private BcfBuilder _inMemoryBuilder = null!; - + [SetUp] public void Setup() { _inMemoryBuilder = new BcfBuilder(); - - var bcfBuilderDelegate = new BcfBuilderDelegate(); - _streamBuilder = new BcfBuilder(bcfBuilderDelegate); } - + [Test] public void BuildBcfWithComplexFieldsTest() { var bcf = _inMemoryBuilder @@ -56,21 +35,13 @@ public void BuildBcfWithComplexFieldsTest() { Assert.That(true, Is.EqualTo(bcf.Extensions.TopicTypesSpecified)); Assert.That("Project", Is.EqualTo(bcf.Project?.Project.Name)); } - + [Test] public void BuildBcfWithMissingRequiredFieldsTest() { Assert.That(() => _inMemoryBuilder.Build(), Throws.ArgumentException); } - - [Test] - public async Task ProcessStreamTest() { - await using var stream = new FileStream( - "Resources/Bcf/v3.0/UserAssignment.bcfzip", - FileMode.Open, - FileAccess.Read); - await _streamBuilder.ProcessStream(stream); - } - + + [Test] public async Task BuildInMemoryBcfFromStreamTest() { await using var stream = new FileStream( @@ -83,7 +54,7 @@ public async Task BuildInMemoryBcfFromStreamTest() { "Architect@example.com", Is.EqualTo(bcf.Markups.FirstOrDefault()?.Topic.AssignedTo)); } - + [Test] public async Task BuildEmptyBcfFromStreamTest() { await using var stream = new FileStream( diff --git a/src/tests/tests.csproj b/src/tests/tests.csproj index 2983214..882bbd6 100644 --- a/src/tests/tests.csproj +++ b/src/tests/tests.csproj @@ -12,6 +12,7 @@ + From cd29e7b1ad5f7bc6d6f25a91578c7a5efe20f0c9 Mon Sep 17 00:00:00 2001 From: Balint Bende Date: Mon, 10 Jun 2024 16:43:44 +0200 Subject: [PATCH 13/29] [BMSPT-292] organized schema xsd resources --- src/bcf-toolkit/Resources/{ => Schemas}/Bcf21/markup.xsd | 0 src/bcf-toolkit/Resources/{ => Schemas}/Bcf21/project.xsd | 0 src/bcf-toolkit/Resources/{ => Schemas}/Bcf21/version.xsd | 0 src/bcf-toolkit/Resources/{ => Schemas}/Bcf21/visinfo.xsd | 0 .../Resources/{Bcf30/Schemas => Schemas/Bcf30}/documents.xsd | 0 .../Resources/{Bcf30/Schemas => Schemas/Bcf30}/extensions.xsd | 0 .../Resources/{Bcf30/Schemas => Schemas/Bcf30}/markup.xsd | 0 .../Resources/{Bcf30/Schemas => Schemas/Bcf30}/project.xsd | 0 .../Resources/{Bcf30/Schemas => Schemas/Bcf30}/shared-types.xsd | 0 .../Resources/{Bcf30/Schemas => Schemas/Bcf30}/version.xsd | 0 .../Resources/{Bcf30/Schemas => Schemas/Bcf30}/visinfo.xsd | 0 11 files changed, 0 insertions(+), 0 deletions(-) rename src/bcf-toolkit/Resources/{ => Schemas}/Bcf21/markup.xsd (100%) rename src/bcf-toolkit/Resources/{ => Schemas}/Bcf21/project.xsd (100%) rename src/bcf-toolkit/Resources/{ => Schemas}/Bcf21/version.xsd (100%) rename src/bcf-toolkit/Resources/{ => Schemas}/Bcf21/visinfo.xsd (100%) rename src/bcf-toolkit/Resources/{Bcf30/Schemas => Schemas/Bcf30}/documents.xsd (100%) rename src/bcf-toolkit/Resources/{Bcf30/Schemas => Schemas/Bcf30}/extensions.xsd (100%) rename src/bcf-toolkit/Resources/{Bcf30/Schemas => Schemas/Bcf30}/markup.xsd (100%) rename src/bcf-toolkit/Resources/{Bcf30/Schemas => Schemas/Bcf30}/project.xsd (100%) rename src/bcf-toolkit/Resources/{Bcf30/Schemas => Schemas/Bcf30}/shared-types.xsd (100%) rename src/bcf-toolkit/Resources/{Bcf30/Schemas => Schemas/Bcf30}/version.xsd (100%) rename src/bcf-toolkit/Resources/{Bcf30/Schemas => Schemas/Bcf30}/visinfo.xsd (100%) diff --git a/src/bcf-toolkit/Resources/Bcf21/markup.xsd b/src/bcf-toolkit/Resources/Schemas/Bcf21/markup.xsd similarity index 100% rename from src/bcf-toolkit/Resources/Bcf21/markup.xsd rename to src/bcf-toolkit/Resources/Schemas/Bcf21/markup.xsd diff --git a/src/bcf-toolkit/Resources/Bcf21/project.xsd b/src/bcf-toolkit/Resources/Schemas/Bcf21/project.xsd similarity index 100% rename from src/bcf-toolkit/Resources/Bcf21/project.xsd rename to src/bcf-toolkit/Resources/Schemas/Bcf21/project.xsd diff --git a/src/bcf-toolkit/Resources/Bcf21/version.xsd b/src/bcf-toolkit/Resources/Schemas/Bcf21/version.xsd similarity index 100% rename from src/bcf-toolkit/Resources/Bcf21/version.xsd rename to src/bcf-toolkit/Resources/Schemas/Bcf21/version.xsd diff --git a/src/bcf-toolkit/Resources/Bcf21/visinfo.xsd b/src/bcf-toolkit/Resources/Schemas/Bcf21/visinfo.xsd similarity index 100% rename from src/bcf-toolkit/Resources/Bcf21/visinfo.xsd rename to src/bcf-toolkit/Resources/Schemas/Bcf21/visinfo.xsd diff --git a/src/bcf-toolkit/Resources/Bcf30/Schemas/documents.xsd b/src/bcf-toolkit/Resources/Schemas/Bcf30/documents.xsd similarity index 100% rename from src/bcf-toolkit/Resources/Bcf30/Schemas/documents.xsd rename to src/bcf-toolkit/Resources/Schemas/Bcf30/documents.xsd diff --git a/src/bcf-toolkit/Resources/Bcf30/Schemas/extensions.xsd b/src/bcf-toolkit/Resources/Schemas/Bcf30/extensions.xsd similarity index 100% rename from src/bcf-toolkit/Resources/Bcf30/Schemas/extensions.xsd rename to src/bcf-toolkit/Resources/Schemas/Bcf30/extensions.xsd diff --git a/src/bcf-toolkit/Resources/Bcf30/Schemas/markup.xsd b/src/bcf-toolkit/Resources/Schemas/Bcf30/markup.xsd similarity index 100% rename from src/bcf-toolkit/Resources/Bcf30/Schemas/markup.xsd rename to src/bcf-toolkit/Resources/Schemas/Bcf30/markup.xsd diff --git a/src/bcf-toolkit/Resources/Bcf30/Schemas/project.xsd b/src/bcf-toolkit/Resources/Schemas/Bcf30/project.xsd similarity index 100% rename from src/bcf-toolkit/Resources/Bcf30/Schemas/project.xsd rename to src/bcf-toolkit/Resources/Schemas/Bcf30/project.xsd diff --git a/src/bcf-toolkit/Resources/Bcf30/Schemas/shared-types.xsd b/src/bcf-toolkit/Resources/Schemas/Bcf30/shared-types.xsd similarity index 100% rename from src/bcf-toolkit/Resources/Bcf30/Schemas/shared-types.xsd rename to src/bcf-toolkit/Resources/Schemas/Bcf30/shared-types.xsd diff --git a/src/bcf-toolkit/Resources/Bcf30/Schemas/version.xsd b/src/bcf-toolkit/Resources/Schemas/Bcf30/version.xsd similarity index 100% rename from src/bcf-toolkit/Resources/Bcf30/Schemas/version.xsd rename to src/bcf-toolkit/Resources/Schemas/Bcf30/version.xsd diff --git a/src/bcf-toolkit/Resources/Bcf30/Schemas/visinfo.xsd b/src/bcf-toolkit/Resources/Schemas/Bcf30/visinfo.xsd similarity index 100% rename from src/bcf-toolkit/Resources/Bcf30/Schemas/visinfo.xsd rename to src/bcf-toolkit/Resources/Schemas/Bcf30/visinfo.xsd From ac7add0d19a2a7f109da478e3b56a290c18c1d80 Mon Sep 17 00:00:00 2001 From: Balint Bende Date: Mon, 10 Jun 2024 16:52:22 +0200 Subject: [PATCH 14/29] [BMSPT-292] added nullable types bcf 2.1 --- src/bcf-toolkit/Model/Bcf21/Markup.cs | 440 +++++++++++------ src/bcf-toolkit/Model/Bcf21/Project.cs | 12 +- src/bcf-toolkit/Model/Bcf21/Version.cs | 36 +- ...VisualizationInformation.cs => VisInfo.cs} | 461 +++++++++++------- 4 files changed, 595 insertions(+), 354 deletions(-) rename src/bcf-toolkit/Model/Bcf21/{VisualizationInformation.cs => VisInfo.cs} (81%) diff --git a/src/bcf-toolkit/Model/Bcf21/Markup.cs b/src/bcf-toolkit/Model/Bcf21/Markup.cs index 05d1849..936ab04 100644 --- a/src/bcf-toolkit/Model/Bcf21/Markup.cs +++ b/src/bcf-toolkit/Model/Bcf21/Markup.cs @@ -7,11 +7,13 @@ // //------------------------------------------------------------------------------ -// This code was generated by XmlSchemaClassGenerator version 2.0.517.0. - +// This code was generated by XmlSchemaClassGenerator version 2.1.1144.0 using the following command: +// xscgen -n BcfToolkit.Model.Bcf21 --nullable --output ./Model/Bcf21 ./Resources/Schemas/Bcf21/markup.xsd namespace BcfToolkit.Model.Bcf21 { - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Header", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -22,18 +24,18 @@ public partial class Header [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _file; - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("File", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] - public System.Collections.ObjectModel.Collection File - { - get - { - return this._file; - } - private set - { - this._file = value; - } + public System.Collections.ObjectModel.Collection File + { + get + { + return _file; + } + private set + { + _file = value; + } } /// @@ -45,7 +47,7 @@ public Header() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("HeaderFile", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -56,14 +58,37 @@ public partial class HeaderFile : IFileAttributes [System.Xml.Serialization.XmlElementAttribute("Filename", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string Filename { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] [System.Xml.Serialization.XmlElementAttribute("Date", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, DataType="dateTime")] - public System.DateTime Date { get; set; } + public System.DateTime DateValue { get; set; } /// /// Gets or sets a value indicating whether the Date property is specified. /// [System.Xml.Serialization.XmlIgnoreAttribute()] - public bool DateSpecified { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public bool DateValueSpecified { get; set; } + + [System.Xml.Serialization.XmlIgnoreAttribute()] + public System.Nullable Date + { + get + { + if (this.DateValueSpecified) + { + return this.DateValue; + } + else + { + return null; + } + } + set + { + this.DateValue = value.GetValueOrDefault(); + this.DateValueSpecified = value.HasValue; + } + } [System.Xml.Serialization.XmlElementAttribute("Reference", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string Reference { get; set; } @@ -95,20 +120,20 @@ public partial class HeaderFile : IFileAttributes [System.ComponentModel.DefaultValueAttribute(true)] [System.Xml.Serialization.XmlAttributeAttribute("isExternal")] - public bool IsExternal - { - get - { - return this._isExternal; - } - set - { - this._isExternal = value; - } + public bool IsExternal + { + get + { + return _isExternal; + } + set + { + _isExternal = value; + } } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] public partial interface IFileAttributes { @@ -148,7 +173,7 @@ bool IsExternal } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ViewPoint", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -158,29 +183,52 @@ public partial class ViewPoint [System.Xml.Serialization.XmlElementAttribute("Viewpoint", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string Viewpoint { get; set; } - + [System.Xml.Serialization.XmlElementAttribute("Snapshot", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string Snapshot { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] [System.Xml.Serialization.XmlElementAttribute("Index", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] - public int Index { get; set; } + public int IndexValue { get; set; } /// /// Gets or sets a value indicating whether the Index property is specified. /// [System.Xml.Serialization.XmlIgnoreAttribute()] - public bool IndexSpecified { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public bool IndexValueSpecified { get; set; } + + [System.Xml.Serialization.XmlIgnoreAttribute()] + public System.Nullable Index + { + get + { + if (this.IndexValueSpecified) + { + return this.IndexValue; + } + else + { + return null; + } + } + set + { + this.IndexValue = value.GetValueOrDefault(); + this.IndexValueSpecified = value.HasValue; + } + } /// /// Pattern: [a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}. /// [System.ComponentModel.DataAnnotations.RegularExpressionAttribute("[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}")] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlAttributeAttribute("Guid")] public string Guid { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("BimSnippet", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -188,15 +236,15 @@ public partial class ViewPoint public partial class BimSnippet { - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Reference", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string Reference { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("ReferenceSchema", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string ReferenceSchema { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlAttributeAttribute("SnippetType")] public string SnippetType { get; set; } @@ -205,20 +253,20 @@ public partial class BimSnippet [System.ComponentModel.DefaultValueAttribute(false)] [System.Xml.Serialization.XmlAttributeAttribute("isExternal")] - public bool IsExternal - { - get - { - return this._isExternal; - } - set - { - this._isExternal = value; - } + public bool IsExternal + { + get + { + return _isExternal; + } + set + { + _isExternal = value; + } } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Topic", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -230,16 +278,16 @@ public partial class Topic private System.Collections.ObjectModel.Collection _referenceLink; [System.Xml.Serialization.XmlElementAttribute("ReferenceLink", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] - public System.Collections.ObjectModel.Collection ReferenceLink - { - get - { - return this._referenceLink; - } - private set - { - this._referenceLink = value; - } + public System.Collections.ObjectModel.Collection ReferenceLink + { + get + { + return _referenceLink; + } + private set + { + _referenceLink = value; + } } /// @@ -265,38 +313,61 @@ public Topic() this._relatedTopic = new System.Collections.ObjectModel.Collection(); } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Title", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string Title { get; set; } [System.Xml.Serialization.XmlElementAttribute("Priority", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string Priority { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] [System.Xml.Serialization.XmlElementAttribute("Index", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] - public int Index { get; set; } + public int IndexValue { get; set; } /// /// Gets or sets a value indicating whether the Index property is specified. /// [System.Xml.Serialization.XmlIgnoreAttribute()] - public bool IndexSpecified { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public bool IndexValueSpecified { get; set; } [System.Xml.Serialization.XmlIgnoreAttribute()] - private System.Collections.ObjectModel.Collection _labels; - - [System.Xml.Serialization.XmlElementAttribute("Labels", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] - public System.Collections.ObjectModel.Collection Labels + public System.Nullable Index { get { - return this._labels; + if (this.IndexValueSpecified) + { + return this.IndexValue; + } + else + { + return null; + } } - private set + set { - this._labels = value; + this.IndexValue = value.GetValueOrDefault(); + this.IndexValueSpecified = value.HasValue; } } + [System.Xml.Serialization.XmlIgnoreAttribute()] + private System.Collections.ObjectModel.Collection _labels; + + [System.Xml.Serialization.XmlElementAttribute("Labels", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] + public System.Collections.ObjectModel.Collection Labels + { + get + { + return _labels; + } + private set + { + _labels = value; + } + } + /// /// Gets a value indicating whether the Labels collection is empty. /// @@ -309,34 +380,80 @@ public bool LabelsSpecified } } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("CreationDate", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, DataType="dateTime")] public System.DateTime CreationDate { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("CreationAuthor", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string CreationAuthor { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] [System.Xml.Serialization.XmlElementAttribute("ModifiedDate", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, DataType="dateTime")] - public System.DateTime ModifiedDate { get; set; } + public System.DateTime ModifiedDateValue { get; set; } /// /// Gets or sets a value indicating whether the ModifiedDate property is specified. /// [System.Xml.Serialization.XmlIgnoreAttribute()] - public bool ModifiedDateSpecified { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public bool ModifiedDateValueSpecified { get; set; } + + [System.Xml.Serialization.XmlIgnoreAttribute()] + public System.Nullable ModifiedDate + { + get + { + if (this.ModifiedDateValueSpecified) + { + return this.ModifiedDateValue; + } + else + { + return null; + } + } + set + { + this.ModifiedDateValue = value.GetValueOrDefault(); + this.ModifiedDateValueSpecified = value.HasValue; + } + } [System.Xml.Serialization.XmlElementAttribute("ModifiedAuthor", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string ModifiedAuthor { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] [System.Xml.Serialization.XmlElementAttribute("DueDate", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, DataType="dateTime")] - public System.DateTime DueDate { get; set; } + public System.DateTime DueDateValue { get; set; } /// /// Gets or sets a value indicating whether the DueDate property is specified. /// [System.Xml.Serialization.XmlIgnoreAttribute()] - public bool DueDateSpecified { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public bool DueDateValueSpecified { get; set; } + + [System.Xml.Serialization.XmlIgnoreAttribute()] + public System.Nullable DueDate + { + get + { + if (this.DueDateValueSpecified) + { + return this.DueDateValue; + } + else + { + return null; + } + } + set + { + this.DueDateValue = value.GetValueOrDefault(); + this.DueDateValueSpecified = value.HasValue; + } + } [System.Xml.Serialization.XmlElementAttribute("AssignedTo", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string AssignedTo { get; set; } @@ -354,16 +471,16 @@ public bool LabelsSpecified private System.Collections.ObjectModel.Collection _documentReference; [System.Xml.Serialization.XmlElementAttribute("DocumentReference", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] - public System.Collections.ObjectModel.Collection DocumentReference - { - get - { - return this._documentReference; - } - private set - { - this._documentReference = value; - } + public System.Collections.ObjectModel.Collection DocumentReference + { + get + { + return _documentReference; + } + private set + { + _documentReference = value; + } } /// @@ -382,16 +499,16 @@ public bool DocumentReferenceSpecified private System.Collections.ObjectModel.Collection _relatedTopic; [System.Xml.Serialization.XmlElementAttribute("RelatedTopic", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] - public System.Collections.ObjectModel.Collection RelatedTopic - { - get - { - return this._relatedTopic; - } - private set - { - this._relatedTopic = value; - } + public System.Collections.ObjectModel.Collection RelatedTopic + { + get + { + return _relatedTopic; + } + private set + { + _relatedTopic = value; + } } /// @@ -410,7 +527,7 @@ public bool RelatedTopicSpecified /// Pattern: [a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}. /// [System.ComponentModel.DataAnnotations.RegularExpressionAttribute("[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}")] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlAttributeAttribute("Guid")] public string Guid { get; set; } @@ -421,7 +538,7 @@ public bool RelatedTopicSpecified public string TopicStatus { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("TopicDocumentReference", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -447,20 +564,20 @@ public partial class TopicDocumentReference : IDocumentReference [System.ComponentModel.DefaultValueAttribute(false)] [System.Xml.Serialization.XmlAttributeAttribute("isExternal")] - public bool IsExternal - { - get - { - return this._isExternal; - } - set - { - this._isExternal = value; - } + public bool IsExternal + { + get + { + return _isExternal; + } + set + { + _isExternal = value; + } } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] public partial interface IDocumentReference { @@ -482,7 +599,7 @@ bool IsExternal } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("TopicRelatedTopic", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -494,12 +611,12 @@ public partial class TopicRelatedTopic /// Pattern: [a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}. /// [System.ComponentModel.DataAnnotations.RegularExpressionAttribute("[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}")] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlAttributeAttribute("Guid")] public string Guid { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Comment", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -507,29 +624,52 @@ public partial class TopicRelatedTopic public partial class Comment { - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Date", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, DataType="dateTime")] public System.DateTime Date { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Author", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string Author { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Comment", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string CommentProperty { get; set; } [System.Xml.Serialization.XmlElementAttribute("Viewpoint", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public CommentViewpoint Viewpoint { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] [System.Xml.Serialization.XmlElementAttribute("ModifiedDate", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, DataType="dateTime")] - public System.DateTime ModifiedDate { get; set; } + public System.DateTime ModifiedDateValue { get; set; } /// /// Gets or sets a value indicating whether the ModifiedDate property is specified. /// [System.Xml.Serialization.XmlIgnoreAttribute()] - public bool ModifiedDateSpecified { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public bool ModifiedDateValueSpecified { get; set; } + + [System.Xml.Serialization.XmlIgnoreAttribute()] + public System.Nullable ModifiedDate + { + get + { + if (this.ModifiedDateValueSpecified) + { + return this.ModifiedDateValue; + } + else + { + return null; + } + } + set + { + this.ModifiedDateValue = value.GetValueOrDefault(); + this.ModifiedDateValueSpecified = value.HasValue; + } + } [System.Xml.Serialization.XmlElementAttribute("ModifiedAuthor", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string ModifiedAuthor { get; set; } @@ -538,12 +678,12 @@ public partial class Comment /// Pattern: [a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}. /// [System.ComponentModel.DataAnnotations.RegularExpressionAttribute("[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}")] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlAttributeAttribute("Guid")] public string Guid { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("CommentViewpoint", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -555,12 +695,12 @@ public partial class CommentViewpoint /// Pattern: [a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}. /// [System.ComponentModel.DataAnnotations.RegularExpressionAttribute("[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}")] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlAttributeAttribute("Guid")] public string Guid { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Markup", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -574,16 +714,16 @@ public partial class Markup [System.Xml.Serialization.XmlArrayAttribute("Header", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] [System.Xml.Serialization.XmlArrayItemAttribute("File", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] - public System.Collections.ObjectModel.Collection Header - { - get - { - return this._header; - } - private set - { - this._header = value; - } + public System.Collections.ObjectModel.Collection Header + { + get + { + return _header; + } + private set + { + _header = value; + } } /// @@ -608,7 +748,7 @@ public Markup() this._viewpoints = new System.Collections.ObjectModel.Collection(); } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Topic", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public Topic Topic { get; set; } @@ -616,16 +756,16 @@ public Markup() private System.Collections.ObjectModel.Collection _comment; [System.Xml.Serialization.XmlElementAttribute("Comment", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] - public System.Collections.ObjectModel.Collection Comment - { - get - { - return this._comment; - } - private set - { - this._comment = value; - } + public System.Collections.ObjectModel.Collection Comment + { + get + { + return _comment; + } + private set + { + _comment = value; + } } /// @@ -644,16 +784,16 @@ public bool CommentSpecified private System.Collections.ObjectModel.Collection _viewpoints; [System.Xml.Serialization.XmlElementAttribute("Viewpoints", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] - public System.Collections.ObjectModel.Collection Viewpoints - { - get - { - return this._viewpoints; - } - private set - { - this._viewpoints = value; - } + public System.Collections.ObjectModel.Collection Viewpoints + { + get + { + return _viewpoints; + } + private set + { + _viewpoints = value; + } } /// diff --git a/src/bcf-toolkit/Model/Bcf21/Project.cs b/src/bcf-toolkit/Model/Bcf21/Project.cs index 15b2133..d195127 100644 --- a/src/bcf-toolkit/Model/Bcf21/Project.cs +++ b/src/bcf-toolkit/Model/Bcf21/Project.cs @@ -7,13 +7,13 @@ // //------------------------------------------------------------------------------ -// This code was generated by XmlSchemaClassGenerator version 2.0.878.0 using the following command: -// xscgen -n bcf.bcf21 project.xsd +// This code was generated by XmlSchemaClassGenerator version 2.1.1144.0 using the following command: +// xscgen -n BcfToolkit.Model.Bcf21 --nullable --output ./Model/Bcf21 ./Resources/Schemas/Bcf21/project.xsd namespace BcfToolkit.Model.Bcf21 { - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Project", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -24,12 +24,12 @@ public partial class Project [System.Xml.Serialization.XmlElementAttribute("Name", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string Name { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlAttributeAttribute("ProjectId")] public string ProjectId { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ProjectExtension", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -41,7 +41,7 @@ public partial class ProjectExtension [System.Xml.Serialization.XmlElementAttribute("Project", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public Project Project { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("ExtensionSchema", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string ExtensionSchema { get; set; } } diff --git a/src/bcf-toolkit/Model/Bcf21/Version.cs b/src/bcf-toolkit/Model/Bcf21/Version.cs index 690e04b..ea5e354 100644 --- a/src/bcf-toolkit/Model/Bcf21/Version.cs +++ b/src/bcf-toolkit/Model/Bcf21/Version.cs @@ -7,19 +7,25 @@ // //------------------------------------------------------------------------------ -// This code was generated by XmlSchemaClassGenerator version 2.0.517.0. -namespace BcfToolkit.Model.Bcf21 { - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] - [System.SerializableAttribute()] - [System.Xml.Serialization.XmlTypeAttribute("Version", Namespace="", AnonymousType=true)] - [System.Diagnostics.DebuggerStepThroughAttribute()] - [System.ComponentModel.DesignerCategoryAttribute("code")] - [System.Xml.Serialization.XmlRootAttribute("Version", Namespace="")] - public partial class Version { - [System.Xml.Serialization.XmlElementAttribute("DetailedVersion", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] - public string DetailedVersion { get; set; } +// This code was generated by XmlSchemaClassGenerator version 2.1.1144.0 using the following command: +// xscgen -n BcfToolkit.Model.Bcf21 --nullable --output ./Model/Bcf21 ./Resources/Schemas/Bcf21/version.xsd +namespace BcfToolkit.Model.Bcf21 +{ + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] + [System.SerializableAttribute()] + [System.Xml.Serialization.XmlTypeAttribute("Version", Namespace="", AnonymousType=true)] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlRootAttribute("Version", Namespace="")] + public partial class Version + { - [System.Xml.Serialization.XmlAttributeAttribute("VersionId")] - public string VersionId { get; set; } - } -} \ No newline at end of file + [System.Xml.Serialization.XmlElementAttribute("DetailedVersion", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] + public string DetailedVersion { get; set; } + + [System.Xml.Serialization.XmlAttributeAttribute("VersionId")] + public string VersionId { get; set; } + } +} diff --git a/src/bcf-toolkit/Model/Bcf21/VisualizationInformation.cs b/src/bcf-toolkit/Model/Bcf21/VisInfo.cs similarity index 81% rename from src/bcf-toolkit/Model/Bcf21/VisualizationInformation.cs rename to src/bcf-toolkit/Model/Bcf21/VisInfo.cs index 1fe607a..54cd0cf 100644 --- a/src/bcf-toolkit/Model/Bcf21/VisualizationInformation.cs +++ b/src/bcf-toolkit/Model/Bcf21/VisInfo.cs @@ -7,12 +7,13 @@ // //------------------------------------------------------------------------------ -// This code was generated by XmlSchemaClassGenerator version 2.0.517.0. +// This code was generated by XmlSchemaClassGenerator version 2.1.1144.0 using the following command: +// xscgen -n BcfToolkit.Model.Bcf21 --nullable --output ./Model/Bcf21 ./Resources/Schemas/Bcf21/visinfo.xsd namespace BcfToolkit.Model.Bcf21 { - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("OrthogonalCamera", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -20,15 +21,15 @@ namespace BcfToolkit.Model.Bcf21 public partial class OrthogonalCamera { - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("CameraViewPoint")] public Point CameraViewPoint { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("CameraDirection")] public Direction CameraDirection { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("CameraUpVector")] public Direction CameraUpVector { get; set; } @@ -36,12 +37,12 @@ public partial class OrthogonalCamera /// view's visible size in meters /// [System.ComponentModel.DescriptionAttribute("view\'s visible size in meters")] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("ViewToWorldScale")] public double ViewToWorldScale { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Point", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -49,20 +50,20 @@ public partial class OrthogonalCamera public partial class Point { - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("X")] public double X { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Y")] public double Y { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Z")] public double Z { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Direction", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -70,20 +71,20 @@ public partial class Point public partial class Direction { - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("X")] public double X { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Y")] public double Y { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Z")] public double Z { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("PerspectiveCamera", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -91,21 +92,21 @@ public partial class Direction public partial class PerspectiveCamera { - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("CameraViewPoint")] public Point CameraViewPoint { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("CameraDirection")] public Direction CameraDirection { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("CameraUpVector")] public Direction CameraUpVector { get; set; } /// - /// It is currently limited to a value between 45 and 60 degrees. - /// This limitation will be dropped in the next release and viewers + /// It is currently limited to a value between 45 and 60 degrees. + /// This limitation will be dropped in the next release and viewers /// should be expect values outside this range in current implementations. /// Minimum inclusive value: 45. /// Maximum inclusive value: 60. @@ -114,12 +115,12 @@ public partial class PerspectiveCamera "l be dropped in the next release and viewers should be expect values outside thi" + "s range in current implementations.")] [System.ComponentModel.DataAnnotations.RangeAttribute(typeof(double), "45", "60")] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("FieldOfView")] public double FieldOfView { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Components", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -135,16 +136,16 @@ public partial class Components [System.Xml.Serialization.XmlArrayAttribute("Selection")] [System.Xml.Serialization.XmlArrayItemAttribute("Component")] - public System.Collections.ObjectModel.Collection Selection - { - get - { - return this._selection; - } - private set - { - this._selection = value; - } + public System.Collections.ObjectModel.Collection Selection + { + get + { + return _selection; + } + private set + { + _selection = value; + } } /// @@ -168,7 +169,7 @@ public Components() this._coloring = new System.Collections.ObjectModel.Collection(); } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Visibility")] public ComponentVisibility Visibility { get; set; } @@ -177,16 +178,16 @@ public Components() [System.Xml.Serialization.XmlArrayAttribute("Coloring")] [System.Xml.Serialization.XmlArrayItemAttribute("Color")] - public System.Collections.ObjectModel.Collection Coloring - { - get - { - return this._coloring; - } - private set - { - this._coloring = value; - } + public System.Collections.ObjectModel.Collection Coloring + { + get + { + return _coloring; + } + private set + { + _coloring = value; + } } /// @@ -202,7 +203,7 @@ public bool ColoringSpecified } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ViewSetupHints", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -210,35 +211,104 @@ public bool ColoringSpecified public partial class ViewSetupHints { + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] [System.Xml.Serialization.XmlAttributeAttribute("SpacesVisible")] - public bool SpacesVisible { get; set; } + public bool SpacesVisibleValue { get; set; } /// /// Gets or sets a value indicating whether the SpacesVisible property is specified. /// [System.Xml.Serialization.XmlIgnoreAttribute()] - public bool SpacesVisibleSpecified { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public bool SpacesVisibleValueSpecified { get; set; } + [System.Xml.Serialization.XmlIgnoreAttribute()] + public System.Nullable SpacesVisible + { + get + { + if (this.SpacesVisibleValueSpecified) + { + return this.SpacesVisibleValue; + } + else + { + return null; + } + } + set + { + this.SpacesVisibleValue = value.GetValueOrDefault(); + this.SpacesVisibleValueSpecified = value.HasValue; + } + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] [System.Xml.Serialization.XmlAttributeAttribute("SpaceBoundariesVisible")] - public bool SpaceBoundariesVisible { get; set; } + public bool SpaceBoundariesVisibleValue { get; set; } /// /// Gets or sets a value indicating whether the SpaceBoundariesVisible property is specified. /// [System.Xml.Serialization.XmlIgnoreAttribute()] - public bool SpaceBoundariesVisibleSpecified { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public bool SpaceBoundariesVisibleValueSpecified { get; set; } + + [System.Xml.Serialization.XmlIgnoreAttribute()] + public System.Nullable SpaceBoundariesVisible + { + get + { + if (this.SpaceBoundariesVisibleValueSpecified) + { + return this.SpaceBoundariesVisibleValue; + } + else + { + return null; + } + } + set + { + this.SpaceBoundariesVisibleValue = value.GetValueOrDefault(); + this.SpaceBoundariesVisibleValueSpecified = value.HasValue; + } + } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] [System.Xml.Serialization.XmlAttributeAttribute("OpeningsVisible")] - public bool OpeningsVisible { get; set; } + public bool OpeningsVisibleValue { get; set; } /// /// Gets or sets a value indicating whether the OpeningsVisible property is specified. /// [System.Xml.Serialization.XmlIgnoreAttribute()] - public bool OpeningsVisibleSpecified { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public bool OpeningsVisibleValueSpecified { get; set; } + + [System.Xml.Serialization.XmlIgnoreAttribute()] + public System.Nullable OpeningsVisible + { + get + { + if (this.OpeningsVisibleValueSpecified) + { + return this.OpeningsVisibleValue; + } + else + { + return null; + } + } + set + { + this.OpeningsVisibleValue = value.GetValueOrDefault(); + this.OpeningsVisibleValueSpecified = value.HasValue; + } + } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ComponentSelection", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -249,18 +319,18 @@ public partial class ComponentSelection [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _component; - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Component")] - public System.Collections.ObjectModel.Collection Component - { - get - { - return this._component; - } - private set - { - this._component = value; - } + public System.Collections.ObjectModel.Collection Component + { + get + { + return _component; + } + private set + { + _component = value; + } } /// @@ -272,7 +342,7 @@ public ComponentSelection() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Component", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -298,7 +368,7 @@ public partial class Component public string IfcGuid { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ComponentVisibility", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -311,16 +381,16 @@ public partial class ComponentVisibility [System.Xml.Serialization.XmlArrayAttribute("Exceptions")] [System.Xml.Serialization.XmlArrayItemAttribute("Component")] - public System.Collections.ObjectModel.Collection Exceptions - { - get - { - return this._exceptions; - } - private set - { - this._exceptions = value; - } + public System.Collections.ObjectModel.Collection Exceptions + { + get + { + return _exceptions; + } + private set + { + _exceptions = value; + } } /// @@ -343,17 +413,40 @@ public ComponentVisibility() this._exceptions = new System.Collections.ObjectModel.Collection(); } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] [System.Xml.Serialization.XmlAttributeAttribute("DefaultVisibility")] - public bool DefaultVisibility { get; set; } + public bool DefaultVisibilityValue { get; set; } /// /// Gets or sets a value indicating whether the DefaultVisibility property is specified. /// [System.Xml.Serialization.XmlIgnoreAttribute()] - public bool DefaultVisibilitySpecified { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public bool DefaultVisibilityValueSpecified { get; set; } + + [System.Xml.Serialization.XmlIgnoreAttribute()] + public System.Nullable DefaultVisibility + { + get + { + if (this.DefaultVisibilityValueSpecified) + { + return this.DefaultVisibilityValue; + } + else + { + return null; + } + } + set + { + this.DefaultVisibilityValue = value.GetValueOrDefault(); + this.DefaultVisibilityValueSpecified = value.HasValue; + } + } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ComponentVisibilityExceptions", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -364,18 +457,18 @@ public partial class ComponentVisibilityExceptions [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _component; - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Component")] - public System.Collections.ObjectModel.Collection Component - { - get - { - return this._component; - } - private set - { - this._component = value; - } + public System.Collections.ObjectModel.Collection Component + { + get + { + return _component; + } + private set + { + _component = value; + } } /// @@ -387,7 +480,7 @@ public ComponentVisibilityExceptions() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ComponentColoring", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -398,18 +491,18 @@ public partial class ComponentColoring [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _color; - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Color")] - public System.Collections.ObjectModel.Collection Color - { - get - { - return this._color; - } - private set - { - this._color = value; - } + public System.Collections.ObjectModel.Collection Color + { + get + { + return _color; + } + private set + { + _color = value; + } } /// @@ -421,7 +514,7 @@ public ComponentColoring() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ComponentColoringColor", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -432,18 +525,18 @@ public partial class ComponentColoringColor [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _component; - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Component")] - public System.Collections.ObjectModel.Collection Component - { - get - { - return this._component; - } - private set - { - this._component = value; - } + public System.Collections.ObjectModel.Collection Component + { + get + { + return _component; + } + private set + { + _component = value; + } } /// @@ -462,7 +555,7 @@ public ComponentColoringColor() public string Color { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Line", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -470,16 +563,16 @@ public ComponentColoringColor() public partial class Line { - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("StartPoint")] public Point StartPoint { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("EndPoint")] public Point EndPoint { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ClippingPlane", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -487,31 +580,33 @@ public partial class Line public partial class ClippingPlane { - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Location")] public Point Location { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Direction")] public Direction Direction { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("BitmapFormat", Namespace="")] public enum BitmapFormat { - PNG, + [System.Xml.Serialization.XmlEnumAttribute("PNG")] + Png, - JPG, + [System.Xml.Serialization.XmlEnumAttribute("JPG")] + Jpg, } /// /// VisualizationInfo documentation /// [System.ComponentModel.DescriptionAttribute("VisualizationInfo documentation")] - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("VisualizationInfo", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -534,16 +629,16 @@ public partial class VisualizationInfo [System.Xml.Serialization.XmlArrayAttribute("Lines")] [System.Xml.Serialization.XmlArrayItemAttribute("Line")] - public System.Collections.ObjectModel.Collection Lines - { - get - { - return this._lines; - } - private set - { - this._lines = value; - } + public System.Collections.ObjectModel.Collection Lines + { + get + { + return _lines; + } + private set + { + _lines = value; + } } /// @@ -573,16 +668,16 @@ public VisualizationInfo() [System.Xml.Serialization.XmlArrayAttribute("ClippingPlanes")] [System.Xml.Serialization.XmlArrayItemAttribute("ClippingPlane")] - public System.Collections.ObjectModel.Collection ClippingPlanes - { - get - { - return this._clippingPlanes; - } - private set - { - this._clippingPlanes = value; - } + public System.Collections.ObjectModel.Collection ClippingPlanes + { + get + { + return _clippingPlanes; + } + private set + { + _clippingPlanes = value; + } } /// @@ -601,16 +696,16 @@ public bool ClippingPlanesSpecified private System.Collections.ObjectModel.Collection _bitmap; [System.Xml.Serialization.XmlElementAttribute("Bitmap")] - public System.Collections.ObjectModel.Collection Bitmap - { - get - { - return this._bitmap; - } - private set - { - this._bitmap = value; - } + public System.Collections.ObjectModel.Collection Bitmap + { + get + { + return _bitmap; + } + private set + { + _bitmap = value; + } } /// @@ -629,12 +724,12 @@ public bool BitmapSpecified /// Pattern: [a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}. /// [System.ComponentModel.DataAnnotations.RegularExpressionAttribute("[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}")] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlAttributeAttribute("Guid")] public string Guid { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("VisualizationInfoLines", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -645,18 +740,18 @@ public partial class VisualizationInfoLines [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _line; - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Line")] - public System.Collections.ObjectModel.Collection Line - { - get - { - return this._line; - } - private set - { - this._line = value; - } + public System.Collections.ObjectModel.Collection Line + { + get + { + return _line; + } + private set + { + _line = value; + } } /// @@ -668,7 +763,7 @@ public VisualizationInfoLines() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("VisualizationInfoClippingPlanes", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -680,16 +775,16 @@ public partial class VisualizationInfoClippingPlanes private System.Collections.ObjectModel.Collection _clippingPlane; [System.Xml.Serialization.XmlElementAttribute("ClippingPlane")] - public System.Collections.ObjectModel.Collection ClippingPlane - { - get - { - return this._clippingPlane; - } - private set - { - this._clippingPlane = value; - } + public System.Collections.ObjectModel.Collection ClippingPlane + { + get + { + return _clippingPlane; + } + private set + { + _clippingPlane = value; + } } /// @@ -713,7 +808,7 @@ public VisualizationInfoClippingPlanes() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.517.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("VisualizationInfoBitmap", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -721,27 +816,27 @@ public VisualizationInfoClippingPlanes() public partial class VisualizationInfoBitmap { - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Bitmap")] public BitmapFormat Bitmap { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Reference")] public string Reference { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Location")] public Point Location { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Normal")] public Direction Normal { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Up")] public Direction Up { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Height")] public double Height { get; set; } } From eba078d4e00118800db3ccb10e9b66fc1589beb7 Mon Sep 17 00:00:00 2001 From: Balint Bende Date: Mon, 10 Jun 2024 17:05:02 +0200 Subject: [PATCH 15/29] [BMSPT-292] added nullable types bcf 3.0 --- src/bcf-toolkit/Model/Bcf21/Markup.cs | 222 +++++++++---------- src/bcf-toolkit/Model/Bcf21/Project.cs | 2 +- src/bcf-toolkit/Model/Bcf21/VisInfo.cs | 246 ++++++++++----------- src/bcf-toolkit/Model/Bcf30/Documents.cs | 16 +- src/bcf-toolkit/Model/Bcf30/Extensions.cs | 88 +------- src/bcf-toolkit/Model/Bcf30/Markup.cs | 256 ++++++++++++++++------ src/bcf-toolkit/Model/Bcf30/Project.cs | 12 +- src/bcf-toolkit/Model/Bcf30/Version.cs | 8 +- src/bcf-toolkit/Model/Bcf30/Visinfo.cs | 106 ++++----- 9 files changed, 504 insertions(+), 452 deletions(-) diff --git a/src/bcf-toolkit/Model/Bcf21/Markup.cs b/src/bcf-toolkit/Model/Bcf21/Markup.cs index 936ab04..6fe8fd4 100644 --- a/src/bcf-toolkit/Model/Bcf21/Markup.cs +++ b/src/bcf-toolkit/Model/Bcf21/Markup.cs @@ -8,7 +8,7 @@ //------------------------------------------------------------------------------ // This code was generated by XmlSchemaClassGenerator version 2.1.1144.0 using the following command: -// xscgen -n BcfToolkit.Model.Bcf21 --nullable --output ./Model/Bcf21 ./Resources/Schemas/Bcf21/markup.xsd +// xscgen -n BcfToolkit.Model.Bcf21 --nullable markup.xsd namespace BcfToolkit.Model.Bcf21 { @@ -26,16 +26,16 @@ public partial class Header [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("File", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] - public System.Collections.ObjectModel.Collection File - { - get - { - return _file; - } - private set - { - _file = value; - } + public System.Collections.ObjectModel.Collection File + { + get + { + return _file; + } + private set + { + _file = value; + } } /// @@ -120,16 +120,16 @@ public System.Nullable Date [System.ComponentModel.DefaultValueAttribute(true)] [System.Xml.Serialization.XmlAttributeAttribute("isExternal")] - public bool IsExternal - { - get - { - return _isExternal; - } - set - { - _isExternal = value; - } + public bool IsExternal + { + get + { + return _isExternal; + } + set + { + _isExternal = value; + } } } @@ -253,16 +253,16 @@ public partial class BimSnippet [System.ComponentModel.DefaultValueAttribute(false)] [System.Xml.Serialization.XmlAttributeAttribute("isExternal")] - public bool IsExternal - { - get - { - return _isExternal; - } - set - { - _isExternal = value; - } + public bool IsExternal + { + get + { + return _isExternal; + } + set + { + _isExternal = value; + } } } @@ -278,16 +278,16 @@ public partial class Topic private System.Collections.ObjectModel.Collection _referenceLink; [System.Xml.Serialization.XmlElementAttribute("ReferenceLink", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] - public System.Collections.ObjectModel.Collection ReferenceLink - { - get - { - return _referenceLink; - } - private set - { - _referenceLink = value; - } + public System.Collections.ObjectModel.Collection ReferenceLink + { + get + { + return _referenceLink; + } + private set + { + _referenceLink = value; + } } /// @@ -356,16 +356,16 @@ public System.Nullable Index private System.Collections.ObjectModel.Collection _labels; [System.Xml.Serialization.XmlElementAttribute("Labels", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] - public System.Collections.ObjectModel.Collection Labels - { - get - { - return _labels; - } - private set - { - _labels = value; - } + public System.Collections.ObjectModel.Collection Labels + { + get + { + return _labels; + } + private set + { + _labels = value; + } } /// @@ -471,16 +471,16 @@ public System.Nullable DueDate private System.Collections.ObjectModel.Collection _documentReference; [System.Xml.Serialization.XmlElementAttribute("DocumentReference", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] - public System.Collections.ObjectModel.Collection DocumentReference - { - get - { - return _documentReference; - } - private set - { - _documentReference = value; - } + public System.Collections.ObjectModel.Collection DocumentReference + { + get + { + return _documentReference; + } + private set + { + _documentReference = value; + } } /// @@ -499,16 +499,16 @@ public bool DocumentReferenceSpecified private System.Collections.ObjectModel.Collection _relatedTopic; [System.Xml.Serialization.XmlElementAttribute("RelatedTopic", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] - public System.Collections.ObjectModel.Collection RelatedTopic - { - get - { - return _relatedTopic; - } - private set - { - _relatedTopic = value; - } + public System.Collections.ObjectModel.Collection RelatedTopic + { + get + { + return _relatedTopic; + } + private set + { + _relatedTopic = value; + } } /// @@ -564,16 +564,16 @@ public partial class TopicDocumentReference : IDocumentReference [System.ComponentModel.DefaultValueAttribute(false)] [System.Xml.Serialization.XmlAttributeAttribute("isExternal")] - public bool IsExternal - { - get - { - return _isExternal; - } - set - { - _isExternal = value; - } + public bool IsExternal + { + get + { + return _isExternal; + } + set + { + _isExternal = value; + } } } @@ -714,16 +714,16 @@ public partial class Markup [System.Xml.Serialization.XmlArrayAttribute("Header", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] [System.Xml.Serialization.XmlArrayItemAttribute("File", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] - public System.Collections.ObjectModel.Collection Header - { - get - { - return _header; - } - private set - { - _header = value; - } + public System.Collections.ObjectModel.Collection Header + { + get + { + return _header; + } + private set + { + _header = value; + } } /// @@ -756,16 +756,16 @@ public Markup() private System.Collections.ObjectModel.Collection _comment; [System.Xml.Serialization.XmlElementAttribute("Comment", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] - public System.Collections.ObjectModel.Collection Comment - { - get - { - return _comment; - } - private set - { - _comment = value; - } + public System.Collections.ObjectModel.Collection Comment + { + get + { + return _comment; + } + private set + { + _comment = value; + } } /// @@ -784,16 +784,16 @@ public bool CommentSpecified private System.Collections.ObjectModel.Collection _viewpoints; [System.Xml.Serialization.XmlElementAttribute("Viewpoints", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] - public System.Collections.ObjectModel.Collection Viewpoints - { - get - { - return _viewpoints; - } - private set - { - _viewpoints = value; - } + public System.Collections.ObjectModel.Collection Viewpoints + { + get + { + return _viewpoints; + } + private set + { + _viewpoints = value; + } } /// diff --git a/src/bcf-toolkit/Model/Bcf21/Project.cs b/src/bcf-toolkit/Model/Bcf21/Project.cs index d195127..6fce840 100644 --- a/src/bcf-toolkit/Model/Bcf21/Project.cs +++ b/src/bcf-toolkit/Model/Bcf21/Project.cs @@ -8,7 +8,7 @@ //------------------------------------------------------------------------------ // This code was generated by XmlSchemaClassGenerator version 2.1.1144.0 using the following command: -// xscgen -n BcfToolkit.Model.Bcf21 --nullable --output ./Model/Bcf21 ./Resources/Schemas/Bcf21/project.xsd +// xscgen -n BcfToolkit.Model.Bcf21 --nullable project.xsd namespace BcfToolkit.Model.Bcf21 { diff --git a/src/bcf-toolkit/Model/Bcf21/VisInfo.cs b/src/bcf-toolkit/Model/Bcf21/VisInfo.cs index 54cd0cf..60b584b 100644 --- a/src/bcf-toolkit/Model/Bcf21/VisInfo.cs +++ b/src/bcf-toolkit/Model/Bcf21/VisInfo.cs @@ -8,7 +8,7 @@ //------------------------------------------------------------------------------ // This code was generated by XmlSchemaClassGenerator version 2.1.1144.0 using the following command: -// xscgen -n BcfToolkit.Model.Bcf21 --nullable --output ./Model/Bcf21 ./Resources/Schemas/Bcf21/visinfo.xsd +// xscgen -n BcfToolkit.Model.Bcf21 --nullable visinfo.xsd namespace BcfToolkit.Model.Bcf21 { @@ -105,8 +105,8 @@ public partial class PerspectiveCamera public Direction CameraUpVector { get; set; } /// - /// It is currently limited to a value between 45 and 60 degrees. - /// This limitation will be dropped in the next release and viewers + /// It is currently limited to a value between 45 and 60 degrees. + /// This limitation will be dropped in the next release and viewers /// should be expect values outside this range in current implementations. /// Minimum inclusive value: 45. /// Maximum inclusive value: 60. @@ -136,16 +136,16 @@ public partial class Components [System.Xml.Serialization.XmlArrayAttribute("Selection")] [System.Xml.Serialization.XmlArrayItemAttribute("Component")] - public System.Collections.ObjectModel.Collection Selection - { - get - { - return _selection; - } - private set - { - _selection = value; - } + public System.Collections.ObjectModel.Collection Selection + { + get + { + return _selection; + } + private set + { + _selection = value; + } } /// @@ -178,16 +178,16 @@ public Components() [System.Xml.Serialization.XmlArrayAttribute("Coloring")] [System.Xml.Serialization.XmlArrayItemAttribute("Color")] - public System.Collections.ObjectModel.Collection Coloring - { - get - { - return _coloring; - } - private set - { - _coloring = value; - } + public System.Collections.ObjectModel.Collection Coloring + { + get + { + return _coloring; + } + private set + { + _coloring = value; + } } /// @@ -321,16 +321,16 @@ public partial class ComponentSelection [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Component")] - public System.Collections.ObjectModel.Collection Component - { - get - { - return _component; - } - private set - { - _component = value; - } + public System.Collections.ObjectModel.Collection Component + { + get + { + return _component; + } + private set + { + _component = value; + } } /// @@ -381,16 +381,16 @@ public partial class ComponentVisibility [System.Xml.Serialization.XmlArrayAttribute("Exceptions")] [System.Xml.Serialization.XmlArrayItemAttribute("Component")] - public System.Collections.ObjectModel.Collection Exceptions - { - get - { - return _exceptions; - } - private set - { - _exceptions = value; - } + public System.Collections.ObjectModel.Collection Exceptions + { + get + { + return _exceptions; + } + private set + { + _exceptions = value; + } } /// @@ -459,16 +459,16 @@ public partial class ComponentVisibilityExceptions [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Component")] - public System.Collections.ObjectModel.Collection Component - { - get - { - return _component; - } - private set - { - _component = value; - } + public System.Collections.ObjectModel.Collection Component + { + get + { + return _component; + } + private set + { + _component = value; + } } /// @@ -493,16 +493,16 @@ public partial class ComponentColoring [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Color")] - public System.Collections.ObjectModel.Collection Color - { - get - { - return _color; - } - private set - { - _color = value; - } + public System.Collections.ObjectModel.Collection Color + { + get + { + return _color; + } + private set + { + _color = value; + } } /// @@ -527,16 +527,16 @@ public partial class ComponentColoringColor [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Component")] - public System.Collections.ObjectModel.Collection Component - { - get - { - return _component; - } - private set - { - _component = value; - } + public System.Collections.ObjectModel.Collection Component + { + get + { + return _component; + } + private set + { + _component = value; + } } /// @@ -629,16 +629,16 @@ public partial class VisualizationInfo [System.Xml.Serialization.XmlArrayAttribute("Lines")] [System.Xml.Serialization.XmlArrayItemAttribute("Line")] - public System.Collections.ObjectModel.Collection Lines - { - get - { - return _lines; - } - private set - { - _lines = value; - } + public System.Collections.ObjectModel.Collection Lines + { + get + { + return _lines; + } + private set + { + _lines = value; + } } /// @@ -668,16 +668,16 @@ public VisualizationInfo() [System.Xml.Serialization.XmlArrayAttribute("ClippingPlanes")] [System.Xml.Serialization.XmlArrayItemAttribute("ClippingPlane")] - public System.Collections.ObjectModel.Collection ClippingPlanes - { - get - { - return _clippingPlanes; - } - private set - { - _clippingPlanes = value; - } + public System.Collections.ObjectModel.Collection ClippingPlanes + { + get + { + return _clippingPlanes; + } + private set + { + _clippingPlanes = value; + } } /// @@ -696,16 +696,16 @@ public bool ClippingPlanesSpecified private System.Collections.ObjectModel.Collection _bitmap; [System.Xml.Serialization.XmlElementAttribute("Bitmap")] - public System.Collections.ObjectModel.Collection Bitmap - { - get - { - return _bitmap; - } - private set - { - _bitmap = value; - } + public System.Collections.ObjectModel.Collection Bitmap + { + get + { + return _bitmap; + } + private set + { + _bitmap = value; + } } /// @@ -742,16 +742,16 @@ public partial class VisualizationInfoLines [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Line")] - public System.Collections.ObjectModel.Collection Line - { - get - { - return _line; - } - private set - { - _line = value; - } + public System.Collections.ObjectModel.Collection Line + { + get + { + return _line; + } + private set + { + _line = value; + } } /// @@ -775,16 +775,16 @@ public partial class VisualizationInfoClippingPlanes private System.Collections.ObjectModel.Collection _clippingPlane; [System.Xml.Serialization.XmlElementAttribute("ClippingPlane")] - public System.Collections.ObjectModel.Collection ClippingPlane - { - get - { - return _clippingPlane; - } - private set - { - _clippingPlane = value; - } + public System.Collections.ObjectModel.Collection ClippingPlane + { + get + { + return _clippingPlane; + } + private set + { + _clippingPlane = value; + } } /// diff --git a/src/bcf-toolkit/Model/Bcf30/Documents.cs b/src/bcf-toolkit/Model/Bcf30/Documents.cs index 8be51b9..82a4184 100644 --- a/src/bcf-toolkit/Model/Bcf30/Documents.cs +++ b/src/bcf-toolkit/Model/Bcf30/Documents.cs @@ -7,13 +7,13 @@ // //------------------------------------------------------------------------------ -// This code was generated by XmlSchemaClassGenerator version 2.0.878.0 using the following command: -// xscgen -n bcf.bcf30 documents.xsd +// This code was generated by XmlSchemaClassGenerator version 2.1.1144.0 using the following command: +// xscgen -n BcfToolkit.Model.Bcf30 --nullable documents.xsd namespace BcfToolkit.Model.Bcf30 { - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Document", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -25,7 +25,7 @@ public partial class Document : IDocumentAttributes /// Minimum length: 1. /// [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Filename", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string Filename { get; set; } @@ -40,12 +40,12 @@ public partial class Document : IDocumentAttributes /// Pattern: [a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}. /// [System.ComponentModel.DataAnnotations.RegularExpressionAttribute("[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}")] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlAttributeAttribute("Guid")] public string Guid { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] public partial interface IDocumentAttributes { @@ -60,7 +60,7 @@ string Guid } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("DocumentInfo", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -107,7 +107,7 @@ public DocumentInfo() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("DocumentInfoDocuments", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] diff --git a/src/bcf-toolkit/Model/Bcf30/Extensions.cs b/src/bcf-toolkit/Model/Bcf30/Extensions.cs index 0d31513..8fe0bbd 100644 --- a/src/bcf-toolkit/Model/Bcf30/Extensions.cs +++ b/src/bcf-toolkit/Model/Bcf30/Extensions.cs @@ -7,13 +7,13 @@ // //------------------------------------------------------------------------------ -// This code was generated by XmlSchemaClassGenerator version 2.0.878.0 using the following command: -// xscgen -n bcf.bcf30 extensions.xsd +// This code was generated by XmlSchemaClassGenerator version 2.1.1144.0 using the following command: +// xscgen -n BcfToolkit.Model.Bcf30 --nullable extensions.xsd namespace BcfToolkit.Model.Bcf30 { - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Extensions", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -25,10 +25,6 @@ public partial class Extensions [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _topicTypes; - /// - /// Minimum length: 1. - /// - // [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] [System.Xml.Serialization.XmlArrayAttribute("TopicTypes", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] [System.Xml.Serialization.XmlArrayItemAttribute("TopicType", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public System.Collections.ObjectModel.Collection TopicTypes @@ -72,11 +68,6 @@ public Extensions() [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _topicStatuses; - /// - /// Minimum length: 1. - /// - // NOTIFICATION: this data annotation wrongly works - // [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] [System.Xml.Serialization.XmlArrayAttribute("TopicStatuses", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] [System.Xml.Serialization.XmlArrayItemAttribute("TopicStatus", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public System.Collections.ObjectModel.Collection TopicStatuses @@ -106,11 +97,6 @@ public bool TopicStatusesSpecified [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _priorities; - /// - /// Minimum length: 1. - /// - // NOTIFICATION: this data annotation wrongly works - // [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] [System.Xml.Serialization.XmlArrayAttribute("Priorities", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] [System.Xml.Serialization.XmlArrayItemAttribute("Priority", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public System.Collections.ObjectModel.Collection Priorities @@ -140,11 +126,6 @@ public bool PrioritiesSpecified [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _topicLabels; - /// - /// Minimum length: 1. - /// - // NOTIFICATION: this data annotation wrongly works - // [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] [System.Xml.Serialization.XmlArrayAttribute("TopicLabels", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] [System.Xml.Serialization.XmlArrayItemAttribute("TopicLabel", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public System.Collections.ObjectModel.Collection TopicLabels @@ -174,11 +155,6 @@ public bool TopicLabelsSpecified [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _users; - /// - /// Minimum length: 1. - /// - // NOTIFICATION: this data annotation wrongly works - // [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] [System.Xml.Serialization.XmlArrayAttribute("Users", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] [System.Xml.Serialization.XmlArrayItemAttribute("User", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public System.Collections.ObjectModel.Collection Users @@ -208,11 +184,6 @@ public bool UsersSpecified [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _snippetTypes; - /// - /// Minimum length: 1. - /// - // NOTIFICATION: this data annotation wrongly works - // [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] [System.Xml.Serialization.XmlArrayAttribute("SnippetTypes", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] [System.Xml.Serialization.XmlArrayItemAttribute("SnippetType", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public System.Collections.ObjectModel.Collection SnippetTypes @@ -242,11 +213,6 @@ public bool SnippetTypesSpecified [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _stages; - /// - /// Minimum length: 1. - /// - // NOTIFICATION: this data annotation wrongly works - // [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] [System.Xml.Serialization.XmlArrayAttribute("Stages", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] [System.Xml.Serialization.XmlArrayItemAttribute("Stage", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public System.Collections.ObjectModel.Collection Stages @@ -274,7 +240,7 @@ public bool StagesSpecified } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ExtensionsTopicTypes", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -285,11 +251,6 @@ public partial class ExtensionsTopicTypes [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _topicType; - /// - /// Minimum length: 1. - /// - // NOTIFICATION: this data annotation wrongly works - // [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] [System.Xml.Serialization.XmlElementAttribute("TopicType", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public System.Collections.ObjectModel.Collection TopicType { @@ -324,7 +285,7 @@ public ExtensionsTopicTypes() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ExtensionsTopicStatuses", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -335,11 +296,6 @@ public partial class ExtensionsTopicStatuses [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _topicStatus; - /// - /// Minimum length: 1. - /// - // NOTIFICATION: this data annotation wrongly works - // [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] [System.Xml.Serialization.XmlElementAttribute("TopicStatus", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public System.Collections.ObjectModel.Collection TopicStatus { @@ -374,7 +330,7 @@ public ExtensionsTopicStatuses() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ExtensionsPriorities", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -385,11 +341,6 @@ public partial class ExtensionsPriorities [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _priority; - /// - /// Minimum length: 1. - /// - // NOTIFICATION: this data annotation wrongly works - // [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] [System.Xml.Serialization.XmlElementAttribute("Priority", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public System.Collections.ObjectModel.Collection Priority { @@ -424,7 +375,7 @@ public ExtensionsPriorities() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ExtensionsTopicLabels", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -435,10 +386,6 @@ public partial class ExtensionsTopicLabels [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _topicLabel; - /// - /// Minimum length: 1. - /// - [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] [System.Xml.Serialization.XmlElementAttribute("TopicLabel", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public System.Collections.ObjectModel.Collection TopicLabel { @@ -473,7 +420,7 @@ public ExtensionsTopicLabels() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ExtensionsUsers", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -484,11 +431,6 @@ public partial class ExtensionsUsers [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _user; - /// - /// Minimum length: 1. - /// - // NOTIFICATION: this data annotation wrongly works - // [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] [System.Xml.Serialization.XmlElementAttribute("User", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public System.Collections.ObjectModel.Collection User { @@ -523,7 +465,7 @@ public ExtensionsUsers() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ExtensionsSnippetTypes", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -534,11 +476,6 @@ public partial class ExtensionsSnippetTypes [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _snippetType; - /// - /// Minimum length: 1. - /// - // NOTIFICATION: this data annotation wrongly works - // [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] [System.Xml.Serialization.XmlElementAttribute("SnippetType", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public System.Collections.ObjectModel.Collection SnippetType { @@ -573,7 +510,7 @@ public ExtensionsSnippetTypes() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ExtensionsStages", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -584,11 +521,6 @@ public partial class ExtensionsStages [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _stage; - /// - /// Minimum length: 1. - /// - // NOTIFICATION: this data annotation wrongly works - // [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] [System.Xml.Serialization.XmlElementAttribute("Stage", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public System.Collections.ObjectModel.Collection Stage { diff --git a/src/bcf-toolkit/Model/Bcf30/Markup.cs b/src/bcf-toolkit/Model/Bcf30/Markup.cs index 9faea00..fc5ff32 100644 --- a/src/bcf-toolkit/Model/Bcf30/Markup.cs +++ b/src/bcf-toolkit/Model/Bcf30/Markup.cs @@ -7,13 +7,13 @@ // //------------------------------------------------------------------------------ -// This code was generated by XmlSchemaClassGenerator version 2.0.878.0 using the following command: -// xscgen -n bcf.bcf30 markup.xsd +// This code was generated by XmlSchemaClassGenerator version 2.1.1144.0 using the following command: +// xscgen -n BcfToolkit.Model.Bcf30 --nullable markup.xsd namespace BcfToolkit.Model.Bcf30 { - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Header", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -59,7 +59,7 @@ public Header() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("HeaderFiles", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -104,7 +104,7 @@ public HeaderFiles() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("File", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -119,14 +119,37 @@ public partial class File : IFileAttributes [System.Xml.Serialization.XmlElementAttribute("Filename", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string Filename { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] [System.Xml.Serialization.XmlElementAttribute("Date", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, DataType="dateTime")] - public System.DateTime Date { get; set; } + public System.DateTime DateValue { get; set; } /// /// Gets or sets a value indicating whether the Date property is specified. /// [System.Xml.Serialization.XmlIgnoreAttribute()] - public bool DateSpecified { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public bool DateValueSpecified { get; set; } + + [System.Xml.Serialization.XmlIgnoreAttribute()] + public System.Nullable Date + { + get + { + if (this.DateValueSpecified) + { + return this.DateValue; + } + else + { + return null; + } + } + set + { + this.DateValue = value.GetValueOrDefault(); + this.DateValueSpecified = value.HasValue; + } + } /// /// Minimum length: 1. @@ -175,7 +198,7 @@ public bool IsExternal } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] public partial interface IFileAttributes { @@ -215,7 +238,7 @@ bool IsExternal } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ViewPoint", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -237,25 +260,48 @@ public partial class ViewPoint [System.Xml.Serialization.XmlElementAttribute("Snapshot", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string Snapshot { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] [System.Xml.Serialization.XmlElementAttribute("Index", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] - public int Index { get; set; } + public int IndexValue { get; set; } /// /// Gets or sets a value indicating whether the Index property is specified. /// [System.Xml.Serialization.XmlIgnoreAttribute()] - public bool IndexSpecified { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public bool IndexValueSpecified { get; set; } + + [System.Xml.Serialization.XmlIgnoreAttribute()] + public System.Nullable Index + { + get + { + if (this.IndexValueSpecified) + { + return this.IndexValue; + } + else + { + return null; + } + } + set + { + this.IndexValue = value.GetValueOrDefault(); + this.IndexValueSpecified = value.HasValue; + } + } /// /// Pattern: [a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}. /// [System.ComponentModel.DataAnnotations.RegularExpressionAttribute("[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}")] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlAttributeAttribute("Guid")] public string Guid { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("BimSnippet", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -267,7 +313,7 @@ public partial class BimSnippet /// Minimum length: 1. /// [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Reference", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string Reference { get; set; } @@ -275,7 +321,7 @@ public partial class BimSnippet /// Minimum length: 1. /// [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("ReferenceSchema", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string ReferenceSchema { get; set; } @@ -283,7 +329,7 @@ public partial class BimSnippet /// Minimum length: 1. /// [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlAttributeAttribute("SnippetType")] public string SnippetType { get; set; } @@ -305,7 +351,7 @@ public bool IsExternal } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Topic", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -316,11 +362,6 @@ public partial class Topic [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _referenceLinks; - /// - /// Minimum length: 1. - /// - // NOTIFICATION: this data annotation wrongly works - // [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] [System.Xml.Serialization.XmlArrayAttribute("ReferenceLinks", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] [System.Xml.Serialization.XmlArrayItemAttribute("ReferenceLink", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public System.Collections.ObjectModel.Collection ReferenceLinks @@ -364,7 +405,7 @@ public Topic() /// Minimum length: 1. /// [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Title", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string Title { get; set; } @@ -375,23 +416,41 @@ public Topic() [System.Xml.Serialization.XmlElementAttribute("Priority", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string Priority { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] [System.Xml.Serialization.XmlElementAttribute("Index", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] - public int Index { get; set; } + public int IndexValue { get; set; } /// /// Gets or sets a value indicating whether the Index property is specified. /// [System.Xml.Serialization.XmlIgnoreAttribute()] - public bool IndexSpecified { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public bool IndexValueSpecified { get; set; } + + [System.Xml.Serialization.XmlIgnoreAttribute()] + public System.Nullable Index + { + get + { + if (this.IndexValueSpecified) + { + return this.IndexValue; + } + else + { + return null; + } + } + set + { + this.IndexValue = value.GetValueOrDefault(); + this.IndexValueSpecified = value.HasValue; + } + } [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _labels; - /// - /// Minimum length: 1. - /// - // NOTIFICATION: this data annotation wrongly works - // [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] [System.Xml.Serialization.XmlArrayAttribute("Labels", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] [System.Xml.Serialization.XmlArrayItemAttribute("Label", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public System.Collections.ObjectModel.Collection Labels @@ -418,7 +477,7 @@ public bool LabelsSpecified } } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("CreationDate", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, DataType="dateTime")] public System.DateTime CreationDate { get; set; } @@ -426,18 +485,41 @@ public bool LabelsSpecified /// Minimum length: 1. /// [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("CreationAuthor", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string CreationAuthor { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] [System.Xml.Serialization.XmlElementAttribute("ModifiedDate", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, DataType="dateTime")] - public System.DateTime ModifiedDate { get; set; } + public System.DateTime ModifiedDateValue { get; set; } /// /// Gets or sets a value indicating whether the ModifiedDate property is specified. /// [System.Xml.Serialization.XmlIgnoreAttribute()] - public bool ModifiedDateSpecified { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public bool ModifiedDateValueSpecified { get; set; } + + [System.Xml.Serialization.XmlIgnoreAttribute()] + public System.Nullable ModifiedDate + { + get + { + if (this.ModifiedDateValueSpecified) + { + return this.ModifiedDateValue; + } + else + { + return null; + } + } + set + { + this.ModifiedDateValue = value.GetValueOrDefault(); + this.ModifiedDateValueSpecified = value.HasValue; + } + } /// /// Minimum length: 1. @@ -446,14 +528,37 @@ public bool LabelsSpecified [System.Xml.Serialization.XmlElementAttribute("ModifiedAuthor", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string ModifiedAuthor { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] [System.Xml.Serialization.XmlElementAttribute("DueDate", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, DataType="dateTime")] - public System.DateTime DueDate { get; set; } + public System.DateTime DueDateValue { get; set; } /// /// Gets or sets a value indicating whether the DueDate property is specified. /// [System.Xml.Serialization.XmlIgnoreAttribute()] - public bool DueDateSpecified { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public bool DueDateValueSpecified { get; set; } + + [System.Xml.Serialization.XmlIgnoreAttribute()] + public System.Nullable DueDate + { + get + { + if (this.DueDateValueSpecified) + { + return this.DueDateValue; + } + else + { + return null; + } + } + set + { + this.DueDateValue = value.GetValueOrDefault(); + this.DueDateValueSpecified = value.HasValue; + } + } /// /// Minimum length: 1. @@ -599,7 +704,7 @@ public bool ViewpointsSpecified /// Pattern: [a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}. /// [System.ComponentModel.DataAnnotations.RegularExpressionAttribute("[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}")] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlAttributeAttribute("Guid")] public string Guid { get; set; } @@ -614,7 +719,7 @@ public bool ViewpointsSpecified /// Minimum length: 1. /// [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlAttributeAttribute("TopicType")] public string TopicType { get; set; } @@ -622,12 +727,12 @@ public bool ViewpointsSpecified /// Minimum length: 1. /// [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlAttributeAttribute("TopicStatus")] public string TopicStatus { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("TopicReferenceLinks", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -638,10 +743,6 @@ public partial class TopicReferenceLinks [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _referenceLink; - /// - /// Minimum length: 1. - /// - [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] [System.Xml.Serialization.XmlElementAttribute("ReferenceLink", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public System.Collections.ObjectModel.Collection ReferenceLink { @@ -676,7 +777,7 @@ public TopicReferenceLinks() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("TopicLabels", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -687,10 +788,6 @@ public partial class TopicLabels [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _label; - /// - /// Minimum length: 1. - /// - [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] [System.Xml.Serialization.XmlElementAttribute("Label", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public System.Collections.ObjectModel.Collection Label { @@ -725,7 +822,7 @@ public TopicLabels() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("TopicDocumentReferences", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -770,7 +867,7 @@ public TopicDocumentReferences() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("DocumentReference", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -803,12 +900,12 @@ public partial class DocumentReference : IDocumentReferenceAttributes /// Pattern: [a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}. /// [System.ComponentModel.DataAnnotations.RegularExpressionAttribute("[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}")] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlAttributeAttribute("Guid")] public string Guid { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] public partial interface IDocumentReferenceAttributes { @@ -823,7 +920,7 @@ string Guid } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("TopicRelatedTopics", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -868,7 +965,7 @@ public TopicRelatedTopics() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("TopicRelatedTopicsRelatedTopic", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -880,12 +977,12 @@ public partial class TopicRelatedTopicsRelatedTopic /// Pattern: [a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}. /// [System.ComponentModel.DataAnnotations.RegularExpressionAttribute("[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}")] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlAttributeAttribute("Guid")] public string Guid { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("TopicComments", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -930,7 +1027,7 @@ public TopicComments() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Comment", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -938,7 +1035,7 @@ public TopicComments() public partial class Comment { - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Date", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, DataType="dateTime")] public System.DateTime Date { get; set; } @@ -946,7 +1043,7 @@ public partial class Comment /// Minimum length: 1. /// [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Author", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string Author { get; set; } @@ -960,14 +1057,37 @@ public partial class Comment [System.Xml.Serialization.XmlElementAttribute("Viewpoint", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public CommentViewpoint Viewpoint { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] [System.Xml.Serialization.XmlElementAttribute("ModifiedDate", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, DataType="dateTime")] - public System.DateTime ModifiedDate { get; set; } + public System.DateTime ModifiedDateValue { get; set; } /// /// Gets or sets a value indicating whether the ModifiedDate property is specified. /// [System.Xml.Serialization.XmlIgnoreAttribute()] - public bool ModifiedDateSpecified { get; set; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public bool ModifiedDateValueSpecified { get; set; } + + [System.Xml.Serialization.XmlIgnoreAttribute()] + public System.Nullable ModifiedDate + { + get + { + if (this.ModifiedDateValueSpecified) + { + return this.ModifiedDateValue; + } + else + { + return null; + } + } + set + { + this.ModifiedDateValue = value.GetValueOrDefault(); + this.ModifiedDateValueSpecified = value.HasValue; + } + } /// /// Minimum length: 1. @@ -980,12 +1100,12 @@ public partial class Comment /// Pattern: [a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}. /// [System.ComponentModel.DataAnnotations.RegularExpressionAttribute("[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}")] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlAttributeAttribute("Guid")] public string Guid { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("CommentViewpoint", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -997,12 +1117,12 @@ public partial class CommentViewpoint /// Pattern: [a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}. /// [System.ComponentModel.DataAnnotations.RegularExpressionAttribute("[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}")] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlAttributeAttribute("Guid")] public string Guid { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("TopicViewpoints", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -1047,7 +1167,7 @@ public TopicViewpoints() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Markup", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -1059,7 +1179,7 @@ public partial class Markup [System.Xml.Serialization.XmlElementAttribute("Header", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public Header Header { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Topic", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public Topic Topic { get; set; } } diff --git a/src/bcf-toolkit/Model/Bcf30/Project.cs b/src/bcf-toolkit/Model/Bcf30/Project.cs index 8bb2893..2828a11 100644 --- a/src/bcf-toolkit/Model/Bcf30/Project.cs +++ b/src/bcf-toolkit/Model/Bcf30/Project.cs @@ -7,13 +7,13 @@ // //------------------------------------------------------------------------------ -// This code was generated by XmlSchemaClassGenerator version 2.0.878.0 using the following command: -// xscgen -n bcf.bcf30 project.xsd +// This code was generated by XmlSchemaClassGenerator version 2.1.1144.0 using the following command: +// xscgen -n BcfToolkit.Model.Bcf30 --nullable project.xsd namespace BcfToolkit.Model.Bcf30 { - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Project", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -32,12 +32,12 @@ public partial class Project /// Minimum length: 1. /// [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlAttributeAttribute("ProjectId")] public string ProjectId { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ProjectInfo", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -46,7 +46,7 @@ public partial class Project public partial class ProjectInfo { - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Project", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public Project Project { get; set; } } diff --git a/src/bcf-toolkit/Model/Bcf30/Version.cs b/src/bcf-toolkit/Model/Bcf30/Version.cs index 2f7897c..7d0e2c0 100644 --- a/src/bcf-toolkit/Model/Bcf30/Version.cs +++ b/src/bcf-toolkit/Model/Bcf30/Version.cs @@ -7,13 +7,13 @@ // //------------------------------------------------------------------------------ -// This code was generated by XmlSchemaClassGenerator version 2.0.878.0 using the following command: -// xscgen -n bcf.bcf30 version.xsd +// This code was generated by XmlSchemaClassGenerator version 2.1.1144.0 using the following command: +// xscgen -n BcfToolkit.Model.Bcf30 --nullable version.xsd namespace BcfToolkit.Model.Bcf30 { - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Version", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -22,7 +22,7 @@ namespace BcfToolkit.Model.Bcf30 public partial class Version { - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlAttributeAttribute("VersionId")] public string VersionId { get; set; } } diff --git a/src/bcf-toolkit/Model/Bcf30/Visinfo.cs b/src/bcf-toolkit/Model/Bcf30/Visinfo.cs index e701b96..227fd18 100644 --- a/src/bcf-toolkit/Model/Bcf30/Visinfo.cs +++ b/src/bcf-toolkit/Model/Bcf30/Visinfo.cs @@ -7,13 +7,13 @@ // //------------------------------------------------------------------------------ -// This code was generated by XmlSchemaClassGenerator version 2.0.878.0 using the following command: -// xscgen -n bcf.bcf30 visinfo.xsd +// This code was generated by XmlSchemaClassGenerator version 2.1.1144.0 using the following command: +// xscgen -n BcfToolkit.Model.Bcf30 --nullable visinfo.xsd namespace BcfToolkit.Model.Bcf30 { - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("OrthogonalCamera", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -21,15 +21,15 @@ namespace BcfToolkit.Model.Bcf30 public partial class OrthogonalCamera { - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("CameraViewPoint")] public Point CameraViewPoint { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("CameraDirection")] public Direction CameraDirection { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("CameraUpVector")] public Direction CameraUpVector { get; set; } @@ -37,7 +37,7 @@ public partial class OrthogonalCamera /// view's visible vertical size in meters /// [System.ComponentModel.DescriptionAttribute("view\'s visible vertical size in meters")] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("ViewToWorldScale")] public double ViewToWorldScale { get; set; } @@ -46,12 +46,12 @@ public partial class OrthogonalCamera /// Minimum exclusive value: 0. /// [System.ComponentModel.DescriptionAttribute("Proportional relationship between the width and the height of the view (w/h).")] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("AspectRatio")] public double AspectRatio { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Point", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -59,20 +59,20 @@ public partial class OrthogonalCamera public partial class Point { - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("X")] public double X { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Y")] public double Y { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Z")] public double Z { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Direction", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -80,20 +80,20 @@ public partial class Point public partial class Direction { - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("X")] public double X { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Y")] public double Y { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Z")] public double Z { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("PerspectiveCamera", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -101,15 +101,15 @@ public partial class Direction public partial class PerspectiveCamera { - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("CameraViewPoint")] public Point CameraViewPoint { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("CameraDirection")] public Direction CameraDirection { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("CameraUpVector")] public Direction CameraUpVector { get; set; } @@ -119,7 +119,7 @@ public partial class PerspectiveCamera /// Maximum exclusive value: 180. /// [System.ComponentModel.DescriptionAttribute("Vertical field of view, in degrees.")] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("FieldOfView")] public double FieldOfView { get; set; } @@ -128,12 +128,12 @@ public partial class PerspectiveCamera /// Minimum exclusive value: 0. /// [System.ComponentModel.DescriptionAttribute("Proportional relationship between the width and the height of the view (w/h).")] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("AspectRatio")] public double AspectRatio { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Components", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -212,7 +212,7 @@ public bool ColoringSpecified } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ComponentSelection", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -257,7 +257,7 @@ public ComponentSelection() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Component", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -291,7 +291,7 @@ public partial class Component public string IfcGuid { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ComponentVisibility", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -357,7 +357,7 @@ public bool DefaultVisibility } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ViewSetupHints", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -417,7 +417,7 @@ public bool OpeningsVisible } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ComponentVisibilityExceptions", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -462,7 +462,7 @@ public ComponentVisibilityExceptions() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ComponentColoring", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -507,7 +507,7 @@ public ComponentColoring() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ComponentColoringColor", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -518,7 +518,7 @@ public partial class ComponentColoringColor [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _components; - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlArrayAttribute("Components")] [System.Xml.Serialization.XmlArrayItemAttribute("Component")] public System.Collections.ObjectModel.Collection Components @@ -545,12 +545,12 @@ public ComponentColoringColor() /// Pattern: [0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?. /// [System.ComponentModel.DataAnnotations.RegularExpressionAttribute("[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?")] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlAttributeAttribute("Color", Namespace="", Form=System.Xml.Schema.XmlSchemaForm.Qualified)] public string Color { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ComponentColoringColorComponents", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -561,7 +561,7 @@ public partial class ComponentColoringColorComponents [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _component; - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Component")] public System.Collections.ObjectModel.Collection Component { @@ -584,7 +584,7 @@ public ComponentColoringColorComponents() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Line", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -592,16 +592,16 @@ public ComponentColoringColorComponents() public partial class Line { - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("StartPoint")] public Point StartPoint { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("EndPoint")] public Point EndPoint { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("ClippingPlane", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -609,16 +609,16 @@ public partial class Line public partial class ClippingPlane { - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Location")] public Point Location { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Direction")] public Direction Direction { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("Bitmap", Namespace="")] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -626,7 +626,7 @@ public partial class ClippingPlane public partial class Bitmap { - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Format")] public BitmapFormat Format { get; set; } @@ -634,28 +634,28 @@ public partial class Bitmap /// Minimum length: 1. /// [System.ComponentModel.DataAnnotations.MinLengthAttribute(1)] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Reference")] public string Reference { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Location")] public Point Location { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Normal")] public Direction Normal { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Up")] public Direction Up { get; set; } - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Height")] public double Height { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("BitmapFormat", Namespace="")] public enum BitmapFormat @@ -672,7 +672,7 @@ public enum BitmapFormat /// VisualizationInfo documentation /// [System.ComponentModel.DescriptionAttribute("VisualizationInfo documentation")] - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("VisualizationInfo", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -791,12 +791,12 @@ public bool BitmapsSpecified /// Pattern: [a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}. /// [System.ComponentModel.DataAnnotations.RegularExpressionAttribute("[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}")] - [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlAttributeAttribute("Guid")] public string Guid { get; set; } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("VisualizationInfoLines", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -841,7 +841,7 @@ public VisualizationInfoLines() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("VisualizationInfoClippingPlanes", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] @@ -886,7 +886,7 @@ public VisualizationInfoClippingPlanes() } } - [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.878.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1144.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("VisualizationInfoBitmaps", Namespace="", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] From d50ac3c595c4537a85c79359768db7d803ca7606 Mon Sep 17 00:00:00 2001 From: Balint Bende Date: Tue, 11 Jun 2024 15:33:49 +0200 Subject: [PATCH 16/29] [BMSPT-292] fixed builder nullable inputs, fixed unit tests --- bcf-toolkit.sln.DotSettings.user | 31 +++++------- .../Builder/Bcf21/CommentBuilder.cs | 2 +- .../Builder/Bcf21/HeaderFileBuilder.cs | 2 +- .../Bcf21/Interfaces/ICommentBuilder.cs | 2 +- .../Bcf21/Interfaces/IHeaderFileBuilder.cs | 2 +- .../Bcf21/Interfaces/IMarkupBuilder.cs | 6 +-- .../Bcf21/Interfaces/IViewPointBuilder.cs | 2 +- .../Builder/Bcf21/MarkupBuilder.cs | 6 +-- .../Builder/Bcf21/ViewPointBuilder.cs | 2 +- .../Builder/Bcf30/CommentBuilder.cs | 2 +- src/bcf-toolkit/Builder/Bcf30/FileBuilder.cs | 2 +- .../Bcf30/Interfaces/ICommentBuilder.cs | 2 +- .../Builder/Bcf30/Interfaces/IFileBuilder.cs | 2 +- .../Bcf30/Interfaces/IMarkupBuilder.cs | 6 +-- .../Interfaces/IViewSetupHintsBuilder.cs | 6 +-- .../Bcf30/Interfaces/IVisibilityBuilder.cs | 2 +- .../Builder/Bcf30/MarkupBuilder.cs | 6 +-- .../Builder/Bcf30/ViewSetupHintsBuilder.cs | 12 ++--- .../Builder/Bcf30/VisibilityBuilder.cs | 4 +- src/tests/Converter/Bcf21/ConverterTests.cs | 4 +- src/tests/Utils/Bcf/BcfExtensionsTests.cs | 49 +++++++++++-------- 21 files changed, 78 insertions(+), 74 deletions(-) diff --git a/bcf-toolkit.sln.DotSettings.user b/bcf-toolkit.sln.DotSettings.user index 7fb0c67..0e32ff4 100644 --- a/bcf-toolkit.sln.DotSettings.user +++ b/bcf-toolkit.sln.DotSettings.user @@ -1,32 +1,30 @@  /Users/balintbende/Library/Caches/JetBrains/Rider2024.1/resharper-host/temp/Rider/vAny/CoverageData/_bcf-toolkit.-1315391344/Snapshot/snapshot.utdcvr + <SessionState ContinuousTestingMode="0" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Solution /> +</SessionState> + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Solution /> +</SessionState> + + + + + + + - <SessionState ContinuousTestingMode="0" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <Solution /> -</SessionState> - <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution #4" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <Solution /> -</SessionState> - <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution #3" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <Solution /> -</SessionState> - <SessionState ContinuousTestingMode="0" Name="All tests from Solution #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <Solution /> -</SessionState> - <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <Solution /> -</SessionState> @@ -35,9 +33,6 @@ - <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <Solution /> -</SessionState> diff --git a/src/bcf-toolkit/Builder/Bcf21/CommentBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/CommentBuilder.cs index 2380fd2..d75c772 100644 --- a/src/bcf-toolkit/Builder/Bcf21/CommentBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/CommentBuilder.cs @@ -35,7 +35,7 @@ public CommentBuilder SetViewPointGuid(string guid) { return this; } - public CommentBuilder SetModifiedDate(DateTime date) { + public CommentBuilder SetModifiedDate(DateTime? date) { _comment.ModifiedDate = date; return this; } diff --git a/src/bcf-toolkit/Builder/Bcf21/HeaderFileBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/HeaderFileBuilder.cs index 821ac1b..2d63aff 100644 --- a/src/bcf-toolkit/Builder/Bcf21/HeaderFileBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/HeaderFileBuilder.cs @@ -27,7 +27,7 @@ public HeaderFileBuilder SetFileName(string fileName) { return this; } - public HeaderFileBuilder SetDate(DateTime date) { + public HeaderFileBuilder SetDate(DateTime? date) { _file.Date = date; return this; } diff --git a/src/bcf-toolkit/Builder/Bcf21/Interfaces/ICommentBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/Interfaces/ICommentBuilder.cs index c2d2c31..b33f0cb 100644 --- a/src/bcf-toolkit/Builder/Bcf21/Interfaces/ICommentBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/Interfaces/ICommentBuilder.cs @@ -45,7 +45,7 @@ public interface ICommentBuilder : IBuilder { /// /// The date when comment was modified. /// Returns the builder object. - TBuilder SetModifiedDate(DateTime date); + TBuilder SetModifiedDate(DateTime? date); /// /// Returns the builder object set with the `ModifiedAuthor`. diff --git a/src/bcf-toolkit/Builder/Bcf21/Interfaces/IHeaderFileBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/Interfaces/IHeaderFileBuilder.cs index 7f25b8a..3a3b174 100644 --- a/src/bcf-toolkit/Builder/Bcf21/Interfaces/IHeaderFileBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/Interfaces/IHeaderFileBuilder.cs @@ -46,7 +46,7 @@ public interface IHeaderFileBuilder : IBuilder { /// /// Date of the BIM file. /// Returns the builder object. - TBuilder SetDate(DateTime date); + TBuilder SetDate(DateTime? date); /// /// Returns the builder object set with the `Reference`. diff --git a/src/bcf-toolkit/Builder/Bcf21/Interfaces/IMarkupBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/Interfaces/IMarkupBuilder.cs index fb91d2f..682ae19 100644 --- a/src/bcf-toolkit/Builder/Bcf21/Interfaces/IMarkupBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/Interfaces/IMarkupBuilder.cs @@ -67,7 +67,7 @@ public interface IMarkupBuilder< /// /// Number to maintain the order of the topics. /// Returns the builder object. - TBuilder SetIndex(int index); + TBuilder SetIndex(int? index); /// /// Returns the builder object extended with `Label`. @@ -95,7 +95,7 @@ public interface IMarkupBuilder< /// /// Date when the topic was last modified. /// Returns the builder object. - TBuilder SetModifiedDate(DateTime date); + TBuilder SetModifiedDate(DateTime? date); /// /// Returns the builder object set with the `ModifiedAuthor`. @@ -111,7 +111,7 @@ public interface IMarkupBuilder< /// Date until when the topics issue needs to be resolved. /// /// Returns the builder object. - TBuilder SetDueDate(DateTime date); + TBuilder SetDueDate(DateTime? date); /// /// Returns the builder object set with the `AssignedTo`. diff --git a/src/bcf-toolkit/Builder/Bcf21/Interfaces/IViewPointBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/Interfaces/IViewPointBuilder.cs index f6a76be..c6fd766 100644 --- a/src/bcf-toolkit/Builder/Bcf21/Interfaces/IViewPointBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/Interfaces/IViewPointBuilder.cs @@ -36,7 +36,7 @@ public interface IViewPointBuilder /// /// Guid of the viewpoint. /// Returns the builder object. - TBuilder SetIndex(int index); + TBuilder SetIndex(int? index); /// /// Returns the builder object set with the `VisualizationInfo`, which diff --git a/src/bcf-toolkit/Builder/Bcf21/MarkupBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/MarkupBuilder.cs index d678b4c..173317d 100644 --- a/src/bcf-toolkit/Builder/Bcf21/MarkupBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/MarkupBuilder.cs @@ -61,7 +61,7 @@ public MarkupBuilder SetPriority(string priority) { return this; } - public MarkupBuilder SetIndex(int index) { + public MarkupBuilder SetIndex(int? index) { _markup.Topic.Index = index; return this; } @@ -81,7 +81,7 @@ public MarkupBuilder SetCreationAuthor(string user) { return this; } - public MarkupBuilder SetModifiedDate(DateTime date) { + public MarkupBuilder SetModifiedDate(DateTime? date) { _markup.Topic.ModifiedDate = date; return this; } @@ -91,7 +91,7 @@ public MarkupBuilder SetModifiedAuthor(string user) { return this; } - public MarkupBuilder SetDueDate(DateTime date) { + public MarkupBuilder SetDueDate(DateTime? date) { _markup.Topic.DueDate = date; return this; } diff --git a/src/bcf-toolkit/Builder/Bcf21/ViewPointBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/ViewPointBuilder.cs index 1e91cfb..cd25405 100644 --- a/src/bcf-toolkit/Builder/Bcf21/ViewPointBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/ViewPointBuilder.cs @@ -24,7 +24,7 @@ public ViewPointBuilder SetSnapshot(string snapshot) { return this; } - public ViewPointBuilder SetIndex(int index) { + public ViewPointBuilder SetIndex(int? index) { _viewPoint.Index = index; return this; } diff --git a/src/bcf-toolkit/Builder/Bcf30/CommentBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/CommentBuilder.cs index 9953903..9883d04 100644 --- a/src/bcf-toolkit/Builder/Bcf30/CommentBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/CommentBuilder.cs @@ -36,7 +36,7 @@ public CommentBuilder SetViewPointGuid(string? guid) { return this; } - public CommentBuilder SetModifiedDate(DateTime date) { + public CommentBuilder SetModifiedDate(DateTime? date) { _comment.ModifiedDate = date; return this; } diff --git a/src/bcf-toolkit/Builder/Bcf30/FileBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/FileBuilder.cs index c64d980..2bc848f 100644 --- a/src/bcf-toolkit/Builder/Bcf30/FileBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/FileBuilder.cs @@ -27,7 +27,7 @@ public FileBuilder SetFileName(string fileName) { return this; } - public FileBuilder SetDate(DateTime date) { + public FileBuilder SetDate(DateTime? date) { _file.Date = date; return this; } diff --git a/src/bcf-toolkit/Builder/Bcf30/Interfaces/ICommentBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/Interfaces/ICommentBuilder.cs index 31f2408..99f1720 100644 --- a/src/bcf-toolkit/Builder/Bcf30/Interfaces/ICommentBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/Interfaces/ICommentBuilder.cs @@ -45,7 +45,7 @@ public interface ICommentBuilder : IBuilder { /// /// The date when comment was modified. /// Returns the builder object. - TBuilder SetModifiedDate(DateTime date); + TBuilder SetModifiedDate(DateTime? date); /// /// Returns the builder object set with the `ModifiedAuthor`. diff --git a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IFileBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IFileBuilder.cs index 8b0703a..e5960e7 100644 --- a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IFileBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IFileBuilder.cs @@ -46,7 +46,7 @@ public interface IFileBuilder : IBuilder { /// /// Date of the BIM file. /// Returns the builder object. - TBuilder SetDate(DateTime date); + TBuilder SetDate(DateTime? date); /// /// Returns the builder object set with the `Reference`. diff --git a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IMarkupBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IMarkupBuilder.cs index 30f789f..86f0a68 100644 --- a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IMarkupBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IMarkupBuilder.cs @@ -67,7 +67,7 @@ public interface IMarkupBuilder< /// /// Number to maintain the order of the topics. /// Returns the builder object. - TBuilder SetIndex(int index); + TBuilder SetIndex(int? index); /// /// Returns the builder object extended with `Label`. @@ -95,7 +95,7 @@ public interface IMarkupBuilder< /// /// Date when the topic was last modified. /// Returns the builder object. - TBuilder SetModifiedDate(DateTime date); + TBuilder SetModifiedDate(DateTime? date); /// /// Returns the builder object set with the `ModifiedAuthor`. @@ -111,7 +111,7 @@ public interface IMarkupBuilder< /// Date until when the topics issue needs to be resolved. /// /// Returns the builder object. - TBuilder SetDueDate(DateTime date); + TBuilder SetDueDate(DateTime? date); /// /// Returns the builder object set with the `AssignedTo`. diff --git a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IViewSetupHintsBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IViewSetupHintsBuilder.cs index 96ba0c4..258fed2 100644 --- a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IViewSetupHintsBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IViewSetupHintsBuilder.cs @@ -10,7 +10,7 @@ public interface /// /// The visibility of spaces by default. /// Returns the builder object. - TBuilder SetSpaceVisible(bool spaceVisible); + TBuilder SetSpaceVisible(bool? spaceVisible); /// /// Returns the builder object set with the `SpaceBoundariesVisible`. @@ -19,7 +19,7 @@ public interface /// The visibility of space boundaries by default. /// /// Returns the builder object. - TBuilder SetSpaceBoundariesVisible(bool spaceBoundariesVisible); + TBuilder SetSpaceBoundariesVisible(bool? spaceBoundariesVisible); /// /// Returns the builder object set with the `OpeningVisible`. @@ -28,5 +28,5 @@ public interface /// The visibility of openings by default. /// /// Returns the builder object. - TBuilder SetOpeningVisible(bool openingVisible); + TBuilder SetOpeningVisible(bool? openingVisible); } \ No newline at end of file diff --git a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IVisibilityBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IVisibilityBuilder.cs index c103a32..a0271ad 100644 --- a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IVisibilityBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IVisibilityBuilder.cs @@ -14,7 +14,7 @@ public interface IVisibilityBuilder< /// /// The default visibility of the components. /// Returns the builder object. - TBuilder SetDefaultVisibility(bool visibility); + TBuilder SetDefaultVisibility(bool? visibility); /// /// Returns the builder object extended with a new `Exception`. diff --git a/src/bcf-toolkit/Builder/Bcf30/MarkupBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/MarkupBuilder.cs index e9ceb10..f94c3bc 100644 --- a/src/bcf-toolkit/Builder/Bcf30/MarkupBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/MarkupBuilder.cs @@ -61,7 +61,7 @@ public MarkupBuilder SetPriority(string priority) { return this; } - public MarkupBuilder SetIndex(int index) { + public MarkupBuilder SetIndex(int? index) { _markup.Topic.Index = index; return this; } @@ -81,7 +81,7 @@ public MarkupBuilder SetCreationAuthor(string user) { return this; } - public MarkupBuilder SetModifiedDate(DateTime date) { + public MarkupBuilder SetModifiedDate(DateTime? date) { _markup.Topic.ModifiedDate = date; return this; } @@ -91,7 +91,7 @@ public MarkupBuilder SetModifiedAuthor(string user) { return this; } - public MarkupBuilder SetDueDate(DateTime date) { + public MarkupBuilder SetDueDate(DateTime? date) { _markup.Topic.DueDate = date; return this; } diff --git a/src/bcf-toolkit/Builder/Bcf30/ViewSetupHintsBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/ViewSetupHintsBuilder.cs index 70bd11f..d0b121a 100644 --- a/src/bcf-toolkit/Builder/Bcf30/ViewSetupHintsBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/ViewSetupHintsBuilder.cs @@ -7,19 +7,19 @@ public class ViewSetupHintsBuilder : IViewSetupHintsBuilder { private readonly ViewSetupHints _viewSetupHints = new(); - public ViewSetupHintsBuilder SetSpaceVisible(bool spaceVisible) { - _viewSetupHints.SpacesVisible = spaceVisible; + public ViewSetupHintsBuilder SetSpaceVisible(bool? spaceVisible) { + _viewSetupHints.SpacesVisible = spaceVisible.GetValueOrDefault(); return this; } public ViewSetupHintsBuilder SetSpaceBoundariesVisible( - bool spaceBoundariesVisible) { - _viewSetupHints.SpaceBoundariesVisible = spaceBoundariesVisible; + bool? spaceBoundariesVisible) { + _viewSetupHints.SpaceBoundariesVisible = spaceBoundariesVisible.GetValueOrDefault(); return this; } - public ViewSetupHintsBuilder SetOpeningVisible(bool openingVisible) { - _viewSetupHints.OpeningsVisible = openingVisible; + public ViewSetupHintsBuilder SetOpeningVisible(bool? openingVisible) { + _viewSetupHints.OpeningsVisible = openingVisible.GetValueOrDefault(); return this; } diff --git a/src/bcf-toolkit/Builder/Bcf30/VisibilityBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/VisibilityBuilder.cs index 7aa4070..b6a458f 100644 --- a/src/bcf-toolkit/Builder/Bcf30/VisibilityBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/VisibilityBuilder.cs @@ -9,8 +9,8 @@ public partial class VisibilityBuilder : IVisibilityBuilder { private readonly ComponentVisibility _visibility = new(); - public VisibilityBuilder SetDefaultVisibility(bool visibility) { - _visibility.DefaultVisibility = visibility; + public VisibilityBuilder SetDefaultVisibility(bool? visibility) { + _visibility.DefaultVisibility = visibility.GetValueOrDefault(); return this; } diff --git a/src/tests/Converter/Bcf21/ConverterTests.cs b/src/tests/Converter/Bcf21/ConverterTests.cs index b7aeacd..c691265 100644 --- a/src/tests/Converter/Bcf21/ConverterTests.cs +++ b/src/tests/Converter/Bcf21/ConverterTests.cs @@ -79,8 +79,8 @@ public async Task ShouldReturnBcfFileStreamTest() { [Test] public void BcfToJsonMissingRequiredPropertyTest() { Assert.That(async () => await _converter.BcfZipToJson( - "Resources/Bcf/v2.1/RelatedTopics.bcfzip", - "Resources/output/json/v2.1/RelatedTopics"), Throws.ArgumentException); + "Resources/Bcf/v2.1/MissingTitle.bcfzip", + "Resources/output/json/v2.1/MissingTitle"), Throws.ArgumentException); } [Test] diff --git a/src/tests/Utils/Bcf/BcfExtensionsTests.cs b/src/tests/Utils/Bcf/BcfExtensionsTests.cs index 9befb59..64e974d 100644 --- a/src/tests/Utils/Bcf/BcfExtensionsTests.cs +++ b/src/tests/Utils/Bcf/BcfExtensionsTests.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -97,7 +98,7 @@ public async Task ParseBcfMultipleMarkupsTest() { [Test] [Category("BCF v2.1")] public void ParseBcfNoMarkupsTest() { - string filePath = "Resources/Bcf/v2.1/NoMakrups.bcfzip"; + const string filePath = "Resources/Bcf/v2.1/NoMakrups.bcfzip"; Assert.That(async () => { await using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); @@ -107,32 +108,40 @@ public void ParseBcfNoMarkupsTest() { /// /// The topic should have a related topic, and that is available. - /// UPDATE: required property is missing Comment /// [Test] [Category("BCF v2.1")] - public void ParseBcfRelatedTopics21Test() { - string filePath = "Resources/Bcf/v2.1/RelatedTopics.bcfzip"; - - Assert.That(async () => { - await using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); - await BcfToolkit.Utils.BcfExtensions.ParseMarkups(fileStream); - }, Throws.Exception); + public async Task ParseBcfRelatedTopics21Test() { + const string filePath = "Resources/Bcf/v2.1/RelatedTopics.bcfzip"; + await using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); + var markups = await BcfToolkit.Utils.BcfExtensions.ParseMarkups(fileStream); + var relatedTopicId = markups.FirstOrDefault()?.Topic.RelatedTopic + .FirstOrDefault()?.Guid; + var secondTopicId = markups.ElementAt(1).Topic.Guid; + Assert.That(relatedTopicId, Is.EqualTo(secondTopicId)); } /// /// Nothing should be selected and only a wall is visible. - /// UPDATE: required property is missing Comment /// [Test] [Category("BCF v2.1")] - public void ParseBcfSingleVisibleWallTest() { - string filePath = "Resources/Bcf/v2.1/SingleVisibleWall.bcfzip"; - - Assert.That(async () => { - await using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); - await BcfToolkit.Utils.BcfExtensions.ParseMarkups(fileStream); - }, Throws.Exception); + public async Task ParseBcfSingleVisibleWallTest() { + const string filePath = "Resources/Bcf/v2.1/SingleVisibleWall.bcfzip"; + await using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); + var markups = + await BcfToolkit.Utils.BcfExtensions + .ParseMarkups(fileStream); + var visibility = + markups.FirstOrDefault()? + .Viewpoints.FirstOrDefault()? + .VisualizationInfo? + .Components + .Visibility; + + Assert.That(visibility?.DefaultVisibility, Is.EqualTo(false)); + Assert.That(visibility?.Exceptions.FirstOrDefault()?.IfcGuid, + Is.EqualTo("1E8YkwPMfB$h99jtn_uAjI")); } @@ -234,10 +243,10 @@ public async Task ParseBcfDueDateTest() { await BcfToolkit.Utils.BcfExtensions .ParseMarkups( stream); - var markup = markups.FirstOrDefault()!; + var markup = markups.FirstOrDefault(); Assert.That(1, Is.EqualTo(markups.Count)); - Assert.That("2021-03-15T11:00:00.000Z", - Is.EqualTo(markup.Topic.DueDate.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"))); + Assert.That(DateTime.Parse("2021-03-15T11:00:00.000Z").ToUniversalTime(), + Is.EqualTo(markup?.Topic.DueDate)); } /// From 21e82f94104b0872e64c1959e1e2dcbc0544f6a1 Mon Sep 17 00:00:00 2001 From: BalintBende <53426892+BalintBende@users.noreply.github.com> Date: Thu, 13 Jun 2024 17:57:28 +0200 Subject: [PATCH 17/29] Feature/bmspt 275 bcfzip with stream (#17) * [BMSPT-275] implementation of bcf zip creation with memory stream * [BMSPT-275] refactor & reorganization * [BMSPT-275] added unit tests * Simplified IConverter API. Added support for predefined output streams. * [BMSPT-297] Checking incoming streams if they can be used for the given use case. Added documentation. --------- Co-authored-by: DanielLepold Co-authored-by: Adam Eri --- README.md | 91 ++++++-------- bcf-toolkit.sln.DotSettings.user | 21 +++- src/bcf-toolkit/Converter/Bcf21/Converter.cs | 77 +++++++----- src/bcf-toolkit/Converter/Bcf21/FileWriter.cs | 95 +++++++++++++-- src/bcf-toolkit/Converter/Bcf30/Converter.cs | 91 ++++++++------ src/bcf-toolkit/Converter/Bcf30/FileWriter.cs | 115 ++++++++++++++++-- src/bcf-toolkit/Converter/IConverter.cs | 47 +++++-- src/bcf-toolkit/Model/Bcf21/Root.cs | 2 +- src/bcf-toolkit/Model/Bcf30/Root.cs | 6 +- src/bcf-toolkit/README.md | 23 +++- src/bcf-toolkit/Utils/BcfExtensions.cs | 23 +++- src/bcf-toolkit/Utils/ZipArchiveExtensions.cs | 23 ++++ src/bcf-toolkit/Worker.cs | 24 ++-- src/tests/Converter/Bcf21/ConverterTests.cs | 39 +++--- src/tests/Converter/Bcf21/FileWriterTest.cs | 45 +++++++ src/tests/Converter/Bcf30/ConverterTests.cs | 45 +++---- src/tests/Converter/Bcf30/FileWriterTests.cs | 46 +++++++ 17 files changed, 593 insertions(+), 220 deletions(-) create mode 100644 src/bcf-toolkit/Utils/ZipArchiveExtensions.cs create mode 100644 src/tests/Converter/Bcf21/FileWriterTest.cs create mode 100644 src/tests/Converter/Bcf30/FileWriterTests.cs diff --git a/README.md b/README.md index f3c1180..1891614 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,19 @@ -# bcf-toolkit - -A .NET library and a command line utility for converting `BCF` (Building -Collaboration Format) files into `json` and vice versa. - -The tool converts `BCF` information across formats and versions. - -## Requirements - -- dotnet 8 - -### CLI - -The command line interface accepts 2 arguments: - * the source bcf file or json folder [REQUIRED] - * the target bcf file or json folder [REQUIRED] - ~~* the target version of bcf [OPTIONAL]~~ - -The json representation is one file for every `Markup`, while the BCF format -is a zipped file as per the standard. +This C# NuGet library allows you to easily build up and convert data into BCF +files. It gives you a straightforward API to build your BCF objects exactly how +you want in your order. -``` -~ bcf-toolkit /path/to/source.bcfzip /path/to/target/json/folder - -~ bcf-toolkit /path/to/source/json/folder /path/to/target.bcfzip -``` - -### As A Library -This C# NuGet library allows the user to easily build up and convert data into -BCF files. It gives a straightforward API to build the BCF objects exactly in -the order of the user's choice. - -#### Installation -`Smino.Bcf.Toolkit` library can be installed via NuGet Package Manager or by adding -it to the project's .csproj file. +## Installation +You can install the `BcfToolkit` library via NuGet Package Manager or by adding +it to your project's .csproj file. ``` nuget install Smino.Bcf.Toolkit ``` -#### Usage -##### Creating BCF objects -To create a BCF Model, `BcfBuilder` class can be used. Then, various -functions provided by the builder can be used to fulfill the BCF model objects. +## Usage + +### Creating BCF objects +To create a BCF Model, `BcfBuilder` class can be used. Then, various +functions provided by the builder can be used to fulfill the BCF model objects. Here are some examples: @@ -99,9 +72,9 @@ var bcf = await builder .BuildFromStream(stream); ``` -The default builders can be used if the user prefers not to deal with filling -the required fields. The `builder.WithDefaults()` function serves this. -However in certain cases the user may need to replace the component IDs of IFC +The default builders can be used if the user prefers not to deal with filling +the required fields. The `builder.WithDefaults()` function serves this. +However in certain cases the user may need to replace the component IDs of IFC objects with the actual GUIDs during the build process. ```csharp @@ -112,9 +85,10 @@ var bcf = builder .WithDefaults() .Build(); ``` -##### Using BCF worker -The worker is implemented to use predefined workflows to convert `BCF` files -into `json` and back. The function decides which workflow must be used according + +### Using BCF worker +The worker is implemented to use predefined workflows to convert `BCF` files +into `json` and back. The function decides which workflow must be used according to the source and target. If the source ends with `.bcfzip` the converter uses the `BcfZipToJson` for example. @@ -124,7 +98,7 @@ using BcfToolkit; var worker = new Worker(); await worker.Convert(source, target); ``` -The exact converter can be called directly as well for both converting +The exact converter can be called directly as well for both converting directions, `BCF` into `json` and back. ```csharp @@ -142,7 +116,7 @@ converter.JsonToBcfZip(source, target); ``` Furthermore `BCF` archive can be consumed as a stream. The version of the source -is established by the code, then the class lets the nested converter object to +is established by the code, then the class lets the nested converter object to do the conversion to BCF 3.0 accordingly. ```csharp @@ -151,7 +125,7 @@ using BcfToolkit; await using var stream = new FileStream(source, FileMode.Open, FileAccess.Read); var worker = new Worker(); -var bcf = await worker.BuildBcfFromStream(stream); +var bcf = await worker.BcfFromStream(stream); ``` The worker can return a file stream from the specified instance of the bcf @@ -162,14 +136,28 @@ stream after the usage is the responsibility of the caller. using BcfToolkit; var worker = new Worker(); -var stream = await worker.ToBcfStream(bcf, BcfVersionEnum.Bcf30); +var stream = await worker.ToBcf(bcf, BcfVersionEnum.Bcf30); // custom code to use the stream... await stream.FlushAsync(); ``` +It is also possible to define the output stream to which the results will +be written. + +```csharp +using BcfToolkit; + +var worker = new Worker(); +var outputStream = new MemoryStream(); +worker.ToBcf(bcf, BcfVersionEnum.Bcf30, outputStream); +// custom code to use the stream... +await outputStream.FlushAsync(); +``` + + ## File Structure -The structure of the BCF is per [the standard][3]. There is, however, no +The structure of the BCF is per [the standard][3]. There is, however, no standard for the JSON format other than the [BCF API specification][4]. The naming convention for this converter is taken from the BCF API, but the @@ -194,7 +182,7 @@ named using the `uuid` of the `Topic` within. ## Development -The development of the tool is ongoing, the table below shows the currently +The development of the tool is ongoing, the table below shows the currently completed features. | | BCF 2.0 | BCF 2.1 | BCF 3.0 | JSON 2.0 | JSON 2.1 | JSON 3.0 | @@ -216,9 +204,6 @@ The models for the BCF in-memory representation were auto-created from the To publish, run the script at `dist/publish.sh`. -### TODO - -- profile memory and CPU usage ### Contribution diff --git a/bcf-toolkit.sln.DotSettings.user b/bcf-toolkit.sln.DotSettings.user index 0e32ff4..f50ce56 100644 --- a/bcf-toolkit.sln.DotSettings.user +++ b/bcf-toolkit.sln.DotSettings.user @@ -20,10 +20,26 @@ + + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution #3" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Solution /> +</SessionState> + <SessionState ContinuousTestingMode="0" Name="All tests from Solution #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Solution /> +</SessionState> + <SessionState ContinuousTestingMode="0" Name="All tests from Solution #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Solution /> +</SessionState> + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution #3" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Solution /> +</SessionState> + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Solution /> +</SessionState> @@ -33,7 +49,10 @@ - + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Solution /> +</SessionState> + diff --git a/src/bcf-toolkit/Converter/Bcf21/Converter.cs b/src/bcf-toolkit/Converter/Bcf21/Converter.cs index 4b658b2..afbcd96 100644 --- a/src/bcf-toolkit/Converter/Bcf21/Converter.cs +++ b/src/bcf-toolkit/Converter/Bcf21/Converter.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.IO.Compression; using System.Threading.Tasks; using BcfToolkit.Builder.Bcf21; using BcfToolkit.Utils; @@ -15,14 +16,13 @@ namespace BcfToolkit.Converter.Bcf21; /// JSON, and BCFzip. /// public class Converter : IConverter { - private BcfBuilder _builder = new(); /// /// Defines the converter function, which must be used for converting the /// BCF object to the targeted version. /// - private readonly Dictionary> _converterFnMapper = + private readonly Dictionary> _converterFn = new() { [BcfVersionEnum.Bcf21] = b => b, [BcfVersionEnum.Bcf30] = SchemaConverterToBcf30.Convert @@ -32,29 +32,42 @@ public class Converter : IConverter { /// Defines the file writer function which must be used for write the BCF /// object to the targeted version. /// - private readonly Dictionary>> _writerFnMapper = - new() { - [BcfVersionEnum.Bcf21] = FileWriter.WriteBcf, - [BcfVersionEnum.Bcf30] = Bcf30.FileWriter.WriteBcf - }; - - public async Task BcfZipToJson(Stream source, string targetPath) { + private readonly Dictionary>> + _writerFn = + new() { + [BcfVersionEnum.Bcf21] = FileWriter.SerializeAndWriteBcf, + [BcfVersionEnum.Bcf30] = Bcf30.FileWriter.SerializeAndWriteBcf + }; + + /// + /// Defines the stream writer function which must be used for write the BCF + /// object to the targeted version. + /// + private readonly Dictionary> + _streamWriterFn = + new() { + [BcfVersionEnum.Bcf21] = FileWriter.SerializeAndWriteBcfToStream, + [BcfVersionEnum.Bcf30] = Bcf30.FileWriter.SerializeAndWriteBcfToStream + }; + + public async Task BcfToJson(Stream source, string targetPath) { var bcf = await _builder.BuildInMemoryFromStream(source); await FileWriter.WriteJson(bcf, targetPath); } - public async Task BcfZipToJson(string sourcePath, string targetPath) { + public async Task BcfToJson(string sourcePath, string targetPath) { try { await using var fileStream = new FileStream(sourcePath, FileMode.Open, FileAccess.Read); - await BcfZipToJson(fileStream, targetPath); + await BcfToJson(fileStream, targetPath); } catch (Exception ex) { - throw new ArgumentException($"Source path is not readable. {ex.Message}", ex); + throw new ArgumentException($"Source path is not readable. {ex.Message}", + ex); } } - public async Task JsonToBcfZip(string source, string target) { + public async Task JsonToBcf(string source, string target) { // Project is optional var projectPath = $"{source}/project.json"; var project = Path.Exists(projectPath) @@ -69,39 +82,43 @@ public async Task JsonToBcfZip(string source, string target) { Version = new Version() }; - await FileWriter.WriteBcf(bcf, target); + await FileWriter.SerializeAndWriteBcfToFolder(bcf, target); } - public async Task ToBcfStream(IBcf bcf, BcfVersionEnum targetVersion) { - var converterFn = _converterFnMapper[targetVersion]; + public async Task ToBcf(IBcf bcf, BcfVersionEnum targetVersion) { + var converterFn = _converterFn[targetVersion]; var convertedBcf = converterFn((Bcf)bcf); - var workingDir = Directory.GetCurrentDirectory(); - var tmpBcfTargetPath = workingDir + $"/{Guid.NewGuid()}.bcfzip"; - var writerFn = _writerFnMapper[targetVersion]; + var writerFn = _writerFn[targetVersion]; + return await writerFn(convertedBcf); + } + + public void ToBcf(IBcf bcf, BcfVersionEnum targetVersion, Stream stream) { - // keep the tmp files till the stream is created - var tmpFolder = await writerFn(convertedBcf, tmpBcfTargetPath, false); - var stream = new FileStream(tmpBcfTargetPath, FileMode.Open, FileAccess.Read); + if (!stream.CanWrite) { + throw new ArgumentException("Stream is not writable."); + } - Directory.Delete(tmpFolder, true); - File.Delete(tmpBcfTargetPath); + var converterFn = _converterFn[targetVersion]; + var convertedBcf = converterFn((Bcf)bcf); - return stream; + var writerFn = _streamWriterFn[targetVersion]; + var zip = new ZipArchive(stream, ZipArchiveMode.Create, true); + writerFn(convertedBcf, zip); } - public Task ToBcfZip(IBcf bcf, string target) { - return FileWriter.WriteBcf((Bcf)bcf, target); + public Task ToBcf(IBcf bcf, string target) { + return FileWriter.SerializeAndWriteBcfToFolder((Bcf)bcf, target); } public Task ToJson(IBcf bcf, string target) { return FileWriter.WriteJson((Bcf)bcf, target); } - - public async Task BuildBcfFromStream(Stream stream) { + + public async Task BcfFromStream(Stream stream) { var bcf = await _builder.BuildInMemoryFromStream(stream); var targetVersion = BcfVersion.TryParse(typeof(T)); - var converterFn = _converterFnMapper[targetVersion]; + var converterFn = _converterFn[targetVersion]; return (T)converterFn(bcf); } } \ No newline at end of file diff --git a/src/bcf-toolkit/Converter/Bcf21/FileWriter.cs b/src/bcf-toolkit/Converter/Bcf21/FileWriter.cs index 253dc4b..c1b40d8 100644 --- a/src/bcf-toolkit/Converter/Bcf21/FileWriter.cs +++ b/src/bcf-toolkit/Converter/Bcf21/FileWriter.cs @@ -16,7 +16,7 @@ public static class FileWriter { /// /// The method writes the BCF object to json file. /// - /// The BCF object which will be written. + /// The `Bcf` object that should be written. /// The target path where the json files will be saved. /// public static Task WriteJson(Bcf bcf, string target) { @@ -41,8 +41,11 @@ public static Task WriteJson(Bcf bcf, string target) { } /// - /// The method writes the BCF content from the given objects to the - /// specified target and compresses it. + /// The method serializes the BCF content to xml from the given object, + /// 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) @@ -50,27 +53,95 @@ public static Task WriteJson(Bcf bcf, string target) { /// 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! + /// + /// The `BCF` object that should be written. + /// It returns a stream of the archive. + public static async Task SerializeAndWriteBcf(IBcf bcf) { + var workingDir = Directory.GetCurrentDirectory(); + var tmpBcfTargetPath = workingDir + $"/{Guid.NewGuid()}.bcfzip"; + var tmpFolder = + await SerializeAndWriteBcfToFolder(bcf, tmpBcfTargetPath, false); + var fileStream = + new FileStream(tmpBcfTargetPath, FileMode.Open, FileAccess.Read); + + Directory.Delete(tmpFolder, true); + File.Delete(tmpBcfTargetPath); + + return fileStream; + } + + /// + /// The method serializes the BCF content to xml from the given object, + /// creates a zip entry from to the specified stream. + /// + /// The `Bcf` object that should be written. + /// The zip archive which the object is written in. + /// Generated stream from bcf zip. + /// + public static void SerializeAndWriteBcfToStream(IBcf bcf, + ZipArchive zip) { + 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) { + var guid = markup.GetTopic()?.Guid; + if (guid == null) { + Console.WriteLine(" - Topic Guid is missing, skipping markup"); + continue; + } + + var topicFolder = $"{guid}"; + + zip.SerializeAndCreateEntry($"{topicFolder}/markup.bcf", markup); + + var visInfo = + (VisualizationInfo)markup.GetFirstViewPoint()?.GetVisualizationInfo()!; + zip.SerializeAndCreateEntry($"{topicFolder}/viewpoint.bcf", visInfo); + + var snapshotFileName = markup.GetFirstViewPoint()?.Snapshot; + var base64String = markup.GetFirstViewPoint()?.SnapshotData; + if (snapshotFileName == null || base64String == null) continue; + const string pattern = @"^data:image\/[a-zA-Z]+;base64,"; + var result = Regex.Replace(base64String, pattern, string.Empty); + var bytes = Convert.FromBase64String(result); + zip.SerializeAndCreateEntry($"{topicFolder}/{snapshotFileName}", bytes); + } + + zip.SerializeAndCreateEntry("project.bcfp", bcfObject.Project); + } + + /// + /// The method writes the BCF content from the given objects to the + /// specified target and compresses it. The folder is deleted /// /// The BCF object. /// The target file name of the BCFzip. - /// Should delete the generated tmp folder now or later - /// Generated temp folder path + /// Should delete the generated tmp folder now or later. + /// Generated temp folder path. /// - public static async Task WriteBcf(IBcf bcf, string target, bool delete = true) { + public static async Task SerializeAndWriteBcfToFolder( + IBcf bcf, + string target, + bool delete = true) { var targetFolder = Path.GetDirectoryName(target); if (targetFolder == null) throw new ApplicationException( $"Target folder not found ${targetFolder}"); // Creating a tmp folder for the intermediate files. - var tmpFolder = $"{targetFolder}/tmp{Path.GetFileNameWithoutExtension(target)}"; + var tmpFolder = + $"{targetFolder}/tmp{Path.GetFileNameWithoutExtension(target)}"; if (Directory.Exists(tmpFolder)) Directory.Delete(tmpFolder, true); Directory.CreateDirectory(tmpFolder); var bcfObject = (Bcf)bcf; var writeTasks = new List(); - writeTasks.Add(BcfExtensions.WriteBcfFile( + writeTasks.Add(BcfExtensions.SerializeAndWriteXmlFile( tmpFolder, "bcf.version", new Version())); @@ -87,7 +158,7 @@ public static async Task WriteBcf(IBcf bcf, string target, bool delete = var topicFolder = $"{tmpFolder}/{guid}"; Directory.CreateDirectory(topicFolder); - writeTasks.Add(BcfExtensions.WriteBcfFile( + writeTasks.Add(BcfExtensions.SerializeAndWriteXmlFile( topicFolder, "markup.bcf", markup)); @@ -95,13 +166,15 @@ public static async Task WriteBcf(IBcf bcf, string target, bool delete = var visInfo = (VisualizationInfo)markup.GetFirstViewPoint()?.GetVisualizationInfo()!; writeTasks.Add( - BcfExtensions.WriteBcfFile( + BcfExtensions.SerializeAndWriteXmlFile( topicFolder, "viewpoint.bcfv", visInfo)); var snapshotFileName = markup.GetFirstViewPoint()?.Snapshot; + var base64String = markup.GetFirstViewPoint()?.SnapshotData; + if (snapshotFileName == null || base64String == null) continue; const string pattern = @"^data:image\/[a-zA-Z]+;base64,"; var result = Regex.Replace(base64String, @@ -112,7 +185,7 @@ public static async Task WriteBcf(IBcf bcf, string target, bool delete = } writeTasks.Add( - BcfExtensions.WriteBcfFile( + BcfExtensions.SerializeAndWriteXmlFile( tmpFolder, "project.bcfp", bcfObject.Project)); diff --git a/src/bcf-toolkit/Converter/Bcf30/Converter.cs b/src/bcf-toolkit/Converter/Bcf30/Converter.cs index eb9833f..8dd3bb9 100644 --- a/src/bcf-toolkit/Converter/Bcf30/Converter.cs +++ b/src/bcf-toolkit/Converter/Bcf30/Converter.cs @@ -1,12 +1,12 @@ using System; using System.Collections.Generic; using System.IO; +using System.IO.Compression; using System.Threading.Tasks; using BcfToolkit.Builder.Bcf30; using BcfToolkit.Utils; using BcfToolkit.Model; using BcfToolkit.Model.Bcf30; -using File = System.IO.File; using Version = BcfToolkit.Model.Bcf30.Version; namespace BcfToolkit.Converter.Bcf30; @@ -16,50 +16,61 @@ namespace BcfToolkit.Converter.Bcf30; /// and back. /// public class Converter : IConverter { - - private BcfBuilder _builder = new(); + private readonly BcfBuilder _builder = new(); /// /// Defines the converter function, which must be used for converting the /// BCF object to the targeted version. /// - - private readonly Dictionary> _converterFnMapper = new(); - + private readonly Dictionary> _converterFn = + new() { + [BcfVersionEnum.Bcf21] = SchemaConverterToBcf21.Convert, + [BcfVersionEnum.Bcf30] = b => b + }; /// /// Defines the file writer function which must be used for write the BCF /// object to the targeted version. /// - private readonly Dictionary>> _writerFnMapper = new(); - - public Converter() { - _converterFnMapper[BcfVersionEnum.Bcf21] = SchemaConverterToBcf21.Convert; - _converterFnMapper[BcfVersionEnum.Bcf30] = b => b; - - _writerFnMapper[BcfVersionEnum.Bcf21] = Bcf21.FileWriter.WriteBcf; - _writerFnMapper[BcfVersionEnum.Bcf30] = FileWriter.WriteBcf; - } + private readonly Dictionary>> + _fileWriterFn = + new() { + [BcfVersionEnum.Bcf21] = Bcf21.FileWriter.SerializeAndWriteBcf, + [BcfVersionEnum.Bcf30] = FileWriter.SerializeAndWriteBcf + }; - public async Task BcfZipToJson(Stream source, string target) { + /// + /// Defines the stream writer function which must be used for write the BCF + /// object to the targeted version. + /// + private readonly Dictionary> + _streamWriterFn = + new() { + [BcfVersionEnum.Bcf21] = Bcf21.FileWriter.SerializeAndWriteBcfToStream, + [BcfVersionEnum.Bcf30] = FileWriter.SerializeAndWriteBcfToStream + }; + + public async Task BcfToJson(Stream source, string target) { var builder = new BcfBuilder(); var bcf = await builder.BuildInMemoryFromStream(source); await FileWriter.WriteJson(bcf, target); } - public async Task BcfZipToJson(string source, string target) { - await using var fileStream = new FileStream(source, FileMode.Open, FileAccess.Read); - await BcfZipToJson(fileStream, target); + public async Task BcfToJson(string source, string target) { + await using var fileStream = + new FileStream(source, FileMode.Open, FileAccess.Read); + await BcfToJson(fileStream, target); } - public async Task JsonToBcfZip(string source, string target) { + public async Task JsonToBcf(string source, string target) { // Project and DocumentInfo are optional var extensions = await JsonExtensions.ParseObject($"{source}/extensions.json"); var project = await JsonExtensions.ParseObject($"{source}/project.json"); var documents = - await JsonExtensions.ParseObject($"{source}/documents.json"); + await JsonExtensions.ParseObject( + $"{source}/documents.json"); var markups = await JsonExtensions.ParseMarkups(source); var bcf = new Bcf { @@ -70,39 +81,43 @@ public async Task JsonToBcfZip(string source, string target) { Version = new Version() }; - await FileWriter.WriteBcf(bcf, target); + await FileWriter.SerializeAndWriteBcfToFolder(bcf, target); } - public async Task ToBcfStream(IBcf bcf, BcfVersionEnum targetVersion) { - var converterFn = _converterFnMapper[targetVersion]; + public async Task ToBcf(IBcf bcf, BcfVersionEnum targetVersion) { + var converterFn = _converterFn[targetVersion]; var convertedBcf = converterFn((Bcf)bcf); - var workingDir = Directory.GetCurrentDirectory(); - var tmpBcfTargetPath = workingDir + $"/{Guid.NewGuid()}.bcfzip"; - var writerFn = _writerFnMapper[targetVersion]; + var writerFn = _fileWriterFn[targetVersion]; + return await writerFn(convertedBcf); + } - // keep the tmp files till the stream is created - var tmpFolder = await writerFn(convertedBcf, tmpBcfTargetPath, false); - var stream = new FileStream(tmpBcfTargetPath, FileMode.Open, FileAccess.Read); + public void ToBcf(IBcf bcf, BcfVersionEnum targetVersion, Stream stream) { - Directory.Delete(tmpFolder, true); - File.Delete(tmpBcfTargetPath); + if (!stream.CanWrite) { + throw new ArgumentException("Stream is not writable."); + } - return stream; + var converterFn = _converterFn[targetVersion]; + var convertedBcf = converterFn((Bcf)bcf); + + var writerFn = _streamWriterFn[targetVersion]; + var zip = new ZipArchive(stream, ZipArchiveMode.Create, true); + writerFn(convertedBcf, zip); } - public Task ToBcfZip(IBcf bcf, string target) { - return FileWriter.WriteBcf((Bcf)bcf, target); + public Task ToBcf(IBcf bcf, string target) { + return FileWriter.SerializeAndWriteBcfToFolder((Bcf)bcf, target); } public Task ToJson(IBcf bcf, string target) { return FileWriter.WriteJson((Bcf)bcf, target); } - - public async Task BuildBcfFromStream(Stream stream) { + + public async Task BcfFromStream(Stream stream) { var bcf = await _builder.BuildInMemoryFromStream(stream); var targetVersion = BcfVersion.TryParse(typeof(T)); - var converterFn = _converterFnMapper[targetVersion]; + var converterFn = _converterFn[targetVersion]; return (T)converterFn(bcf); } } \ No newline at end of file diff --git a/src/bcf-toolkit/Converter/Bcf30/FileWriter.cs b/src/bcf-toolkit/Converter/Bcf30/FileWriter.cs index fa4fef6..826aa9b 100644 --- a/src/bcf-toolkit/Converter/Bcf30/FileWriter.cs +++ b/src/bcf-toolkit/Converter/Bcf30/FileWriter.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; +using System.Xml.Serialization; using BcfToolkit.Model; using BcfToolkit.Model.Bcf30; using BcfToolkit.Utils; @@ -17,7 +18,7 @@ public static class FileWriter { /// /// The method writes the BCF object to json file. /// - /// The BCF object which will be written. + /// The `Bcf` object that should be written. /// The target path where the json files will be saved. /// public static Task WriteJson(Bcf bcf, string target) { @@ -48,6 +49,41 @@ public static Task WriteJson(Bcf bcf, string target) { return Task.WhenAll(tasks); } + /// + /// The method serializes the BCF content to xml from the given object, + /// 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 + /// * extensions.xml + /// * documents.xml (optional) + /// + /// WARNING: Disposing the stream is the responsibility of the user! + /// + /// The `BCF` object that should be written. + /// Should the archive be saved in the tmp folder. + /// It returns a stream of the archive. + public static async Task SerializeAndWriteBcf(IBcf bcf) { + var workingDir = Directory.GetCurrentDirectory(); + var tmpBcfTargetPath = workingDir + $"/{Guid.NewGuid()}.bcfzip"; + var tmpFolder = + await SerializeAndWriteBcfToFolder(bcf, tmpBcfTargetPath, false); + var fileStream = + new FileStream(tmpBcfTargetPath, FileMode.Open, FileAccess.Read); + + Directory.Delete(tmpFolder, true); + File.Delete(tmpBcfTargetPath); + + return fileStream; + } + /// /// The method writes the BCF content from the given objects to the /// specified target and compresses it. @@ -60,20 +96,68 @@ public static Task WriteJson(Bcf bcf, string target) { /// * bcf.version /// * extensions.xml /// * documents.xml (optional) + /// + /// + /// The `BCF` object that should be written.. + /// The zip archive which the object is written in. + /// Memory stream of the bcfzip + /// + /// + public static void SerializeAndWriteBcfToStream(IBcf bcf, ZipArchive zip) { + 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) { + var guid = markup.GetTopic()?.Guid; + if (guid == null) { + Console.WriteLine(" - Topic Guid is missing, skipping markup"); + continue; + } + + var topicFolder = $"{guid}"; + + zip.SerializeAndCreateEntry($"{topicFolder}/markup.bcf", markup); + + var visInfo = + (VisualizationInfo)markup.GetFirstViewPoint()?.GetVisualizationInfo()!; + zip.SerializeAndCreateEntry($"{topicFolder}/viewpoint.bcf", visInfo); + + // Write snapshot + var snapshotFileName = markup.GetFirstViewPoint()?.Snapshot; + var base64String = markup.GetFirstViewPoint()?.SnapshotData; + if (snapshotFileName == null || base64String == null) continue; + const string pattern = @"^data:image\/[a-zA-Z]+;base64,"; + var result = Regex.Replace(base64String, pattern, string.Empty); + var bytes = Convert.FromBase64String(result); + zip.SerializeAndCreateEntry($"{topicFolder}/{snapshotFileName}", bytes); + } + + zip.SerializeAndCreateEntry("extensions.xml", bcfObject.Extensions); + zip.SerializeAndCreateEntry("project.bcfp", bcfObject.Project); + zip.SerializeAndCreateEntry("documents.xml", bcfObject.Document); + } + + /// + /// The method writes the BCF content from the given objects to the + /// specified target and compresses it. The folder is deleted /// /// The BCF object. /// The target file name of the BCFzip. - /// Should delete the generated tmp folder now or later. - /// Temp folder path + /// Should delete the generated tmp folder now or later + /// Generated temp folder path /// - public static async Task WriteBcf(IBcf bcf, string target, bool delete = true) { + public static async Task SerializeAndWriteBcfToFolder(IBcf bcf, + string target, bool delete = true) { var targetFolder = Path.GetDirectoryName(target); if (targetFolder == null) throw new ApplicationException( $"Target folder not found ${targetFolder}"); // Will create a tmp folder for the intermediate files. - var tmpFolder = $"{targetFolder}/tmp{Path.GetFileNameWithoutExtension(target)}"; + var tmpFolder = + $"{targetFolder}/tmp{Path.GetFileNameWithoutExtension(target)}"; if (Directory.Exists(tmpFolder)) Directory.Delete(tmpFolder, true); Directory.CreateDirectory(tmpFolder); @@ -81,7 +165,8 @@ public static async Task WriteBcf(IBcf bcf, string target, bool delete = var writeTasks = new List(); - writeTasks.Add(BcfExtensions.WriteBcfFile(tmpFolder, "bcf.version", new Version())); + writeTasks.Add(BcfExtensions.SerializeAndWriteXmlFile(tmpFolder, + "bcf.version", new Version())); // Writing markup files to disk, one markup per folder. foreach (var markup in bcfObject.Markups) { @@ -95,11 +180,13 @@ public static async Task WriteBcf(IBcf bcf, string target, bool delete = var topicFolder = $"{tmpFolder}/{guid}"; Directory.CreateDirectory(topicFolder); - writeTasks.Add(BcfExtensions.WriteBcfFile(topicFolder, "markup.bcf", markup)); + writeTasks.Add( + BcfExtensions.SerializeAndWriteXmlFile(topicFolder, "markup.bcf", + markup)); var visInfo = (VisualizationInfo)markup.GetFirstViewPoint()?.GetVisualizationInfo()!; - writeTasks.Add(BcfExtensions.WriteBcfFile( + writeTasks.Add(BcfExtensions.SerializeAndWriteXmlFile( topicFolder, "viewpoint.bcfv", visInfo)); @@ -107,16 +194,20 @@ public static async Task WriteBcf(IBcf bcf, string target, bool delete = var snapshotFileName = markup.GetFirstViewPoint()?.Snapshot; var base64String = markup.GetFirstViewPoint()?.SnapshotData; if (snapshotFileName == null || base64String == null) continue; + const string pattern = @"^data:image\/[a-zA-Z]+;base64,"; var result = Regex.Replace(base64String, - @"^data:image\/[a-zA-Z]+;base64,", string.Empty); + pattern, string.Empty); writeTasks.Add(File.WriteAllBytesAsync( $"{topicFolder}/{snapshotFileName}", Convert.FromBase64String(result))); } - writeTasks.Add(BcfExtensions.WriteBcfFile(tmpFolder, "extensions.xml", bcfObject.Extensions)); - writeTasks.Add(BcfExtensions.WriteBcfFile(tmpFolder, "project.bcfp", bcfObject.Project)); - writeTasks.Add(BcfExtensions.WriteBcfFile(tmpFolder, "documents.xml", bcfObject.Document)); + writeTasks.Add(BcfExtensions.SerializeAndWriteXmlFile(tmpFolder, + "extensions.xml", bcfObject.Extensions)); + writeTasks.Add(BcfExtensions.SerializeAndWriteXmlFile(tmpFolder, + "project.bcfp", bcfObject.Project)); + writeTasks.Add(BcfExtensions.SerializeAndWriteXmlFile(tmpFolder, + "documents.xml", bcfObject.Document)); await Task.WhenAll(writeTasks); diff --git a/src/bcf-toolkit/Converter/IConverter.cs b/src/bcf-toolkit/Converter/IConverter.cs index a2a527e..e3fe53f 100644 --- a/src/bcf-toolkit/Converter/IConverter.cs +++ b/src/bcf-toolkit/Converter/IConverter.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using System.Threading.Tasks; using BcfToolkit.Model; @@ -9,39 +10,63 @@ namespace BcfToolkit.Converter; /// public interface IConverter { /// - /// The method parses the BCFzip archive, then writes to json. + /// The method parses the BCFzip archive, then writes to a json file. /// /// The source path to the BCFzip. /// The target path where the JSON is written. /// - Task BcfZipToJson(string source, string target); + Task BcfToJson(string source, string target); /// - /// The method parses the BCFzip archive stream, then writes to json. + /// The method parses the BCFzip archive stream, then writes to a json file. /// /// The file stream of the source BCFzip. /// The target path where the JSON is written. /// - Task BcfZipToJson(Stream source, string target); + Task BcfToJson(Stream source, string target); /// - /// The method reads the json, then creates and writes to BCFzip. + /// The method reads the json, then creates and writes to a BCFzip file. /// /// The source folder to the JSON files. /// The target path where the BCF is written. /// - Task JsonToBcfZip(string source, string target); + Task JsonToBcf(string source, string target); /// /// The method converts the specified BCF object to the given version, then - /// returns a stream from the BCF zip archive. + /// 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 the stream is the responsibility of the user! + /// WARNING: Disposing of the stream is the responsibility of the user. /// /// The BCF object. /// The BCF version. /// Returns the file stream of the BCF zip archive. - Task ToBcfStream(IBcf bcf, BcfVersionEnum targetVersion); + Task ToBcf(IBcf bcf, BcfVersionEnum targetVersion); + + /// + /// 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. + /// + /// + /// The BCF object. + /// The BCF version. + /// The output stream, which should be writable. + /// + /// Throws an error if the stream is not writable. + /// + /// + void ToBcf(IBcf bcf, BcfVersionEnum targetVersion, Stream stream); /// /// The method writes the specified BCF model to BCFzip files. @@ -49,7 +74,7 @@ public interface IConverter { /// The `IBcf` interface of the BCF. /// The target path where the BCF is written. /// - Task ToBcfZip(IBcf bcf, string target); + Task ToBcf(IBcf bcf, string target); /// /// The method writes the specified BCF model to JSON files. @@ -67,5 +92,5 @@ public interface IConverter { /// /// Returns the `Bcf` object which is specified as a type parameter. /// - Task BuildBcfFromStream(Stream stream); + Task BcfFromStream(Stream stream); } \ No newline at end of file diff --git a/src/bcf-toolkit/Model/Bcf21/Root.cs b/src/bcf-toolkit/Model/Bcf21/Root.cs index 097856e..8070f93 100644 --- a/src/bcf-toolkit/Model/Bcf21/Root.cs +++ b/src/bcf-toolkit/Model/Bcf21/Root.cs @@ -7,7 +7,7 @@ public class Root : IRoot { public Task WriteBcf(string folder) { return Task.Run(async () => { - await Utils.BcfExtensions.WriteBcfFile(folder, "project.bcfp", Project); + await Utils.BcfExtensions.SerializeAndWriteXmlFile(folder, "project.bcfp", Project); }); } } \ No newline at end of file diff --git a/src/bcf-toolkit/Model/Bcf30/Root.cs b/src/bcf-toolkit/Model/Bcf30/Root.cs index dd19578..cc99b6c 100644 --- a/src/bcf-toolkit/Model/Bcf30/Root.cs +++ b/src/bcf-toolkit/Model/Bcf30/Root.cs @@ -13,11 +13,11 @@ public class Root : IRoot { public Task WriteBcf(string folder) { return Task.WhenAll( Task.Run(() => - Utils.BcfExtensions.WriteBcfFile(folder, "extensions.xml", Extensions)), + Utils.BcfExtensions.SerializeAndWriteXmlFile(folder, "extensions.xml", Extensions)), Task.Run(() => - Utils.BcfExtensions.WriteBcfFile(folder, "project.bcfp", Project)), + Utils.BcfExtensions.SerializeAndWriteXmlFile(folder, "project.bcfp", Project)), Task.Run(() => - Utils.BcfExtensions.WriteBcfFile(folder, "documents.xml", Document)) + Utils.BcfExtensions.SerializeAndWriteXmlFile(folder, "documents.xml", Document)) ); } } \ No newline at end of file diff --git a/src/bcf-toolkit/README.md b/src/bcf-toolkit/README.md index 99f3ec4..1891614 100644 --- a/src/bcf-toolkit/README.md +++ b/src/bcf-toolkit/README.md @@ -10,6 +10,7 @@ nuget install Smino.Bcf.Toolkit ``` ## Usage + ### Creating BCF objects To create a BCF Model, `BcfBuilder` class can be used. Then, various functions provided by the builder can be used to fulfill the BCF model objects. @@ -84,6 +85,7 @@ var bcf = builder .WithDefaults() .Build(); ``` + ### Using BCF worker The worker is implemented to use predefined workflows to convert `BCF` files into `json` and back. The function decides which workflow must be used according @@ -123,7 +125,7 @@ using BcfToolkit; await using var stream = new FileStream(source, FileMode.Open, FileAccess.Read); var worker = new Worker(); -var bcf = await worker.BuildBcfFromStream(stream); +var bcf = await worker.BcfFromStream(stream); ``` The worker can return a file stream from the specified instance of the bcf @@ -134,11 +136,25 @@ stream after the usage is the responsibility of the caller. using BcfToolkit; var worker = new Worker(); -var stream = await worker.ToBcfStream(bcf, BcfVersionEnum.Bcf30); +var stream = await worker.ToBcf(bcf, BcfVersionEnum.Bcf30); // custom code to use the stream... await stream.FlushAsync(); ``` +It is also possible to define the output stream to which the results will +be written. + +```csharp +using BcfToolkit; + +var worker = new Worker(); +var outputStream = new MemoryStream(); +worker.ToBcf(bcf, BcfVersionEnum.Bcf30, outputStream); +// custom code to use the stream... +await outputStream.FlushAsync(); +``` + + ## File Structure The structure of the BCF is per [the standard][3]. There is, however, no @@ -188,9 +204,6 @@ The models for the BCF in-memory representation were auto-created from the To publish, run the script at `dist/publish.sh`. -### TODO - -- profile memory and CPU usage ### Contribution diff --git a/src/bcf-toolkit/Utils/BcfExtensions.cs b/src/bcf-toolkit/Utils/BcfExtensions.cs index 35fa33c..28f49be 100644 --- a/src/bcf-toolkit/Utils/BcfExtensions.cs +++ b/src/bcf-toolkit/Utils/BcfExtensions.cs @@ -10,6 +10,7 @@ using System.Xml.Serialization; using BcfToolkit.Builder.Bcf21.Interfaces; using BcfToolkit.Model; +using BcfToolkit.Model.Bcf21; namespace BcfToolkit.Utils; @@ -322,14 +323,15 @@ private static Task ParseRequired( } /// - /// The method serializes and writes the specified type object into a BCF file. + /// The method serializes and writes the specified type object into a file. /// - /// The target folder. + /// The target folder where the file is written in. /// The target file name. /// The object which will be written. /// Generic type parameter. /// - public static Task WriteBcfFile(string folder, string file, T? obj) { + public static Task SerializeAndWriteXmlFile(string folder, string file, + T? obj) { return Task.Run(async () => { if (obj != null) { await using var writer = File.CreateText($"{folder}/{file}"); @@ -341,10 +343,21 @@ public static Task WriteBcfFile(string folder, string file, T? obj) { /// /// The method opens the archive stream and returns the BCF version. /// - /// - /// + /// + /// TODO: needs seekable stream + /// + /// + /// Throws an exception if the provided stream is not seekable. + /// + /// Returns the BcfVersionEnum enum. public static async Task GetVersionFromStreamArchive( Stream stream) { + + + if (!stream.CanRead || !stream.CanSeek) { + throw new ArgumentException("Stream is not Readable or Seekable"); + } + using var archive = new ZipArchive(stream, ZipArchiveMode.Read, true); BcfVersionEnum? version = null; diff --git a/src/bcf-toolkit/Utils/ZipArchiveExtensions.cs b/src/bcf-toolkit/Utils/ZipArchiveExtensions.cs new file mode 100644 index 0000000..93bd349 --- /dev/null +++ b/src/bcf-toolkit/Utils/ZipArchiveExtensions.cs @@ -0,0 +1,23 @@ +using System.IO; +using System.IO.Compression; +using System.Xml.Serialization; + +namespace BcfToolkit.Utils; + +public static class ZipArchiveExtensions { + /// + /// The method serializes the specified type object, creates a new entry + /// and writes to the zip archive. + /// + /// The zip archive which the object is written in. + /// The name of the target entry. + /// The object which will be written. + /// Generic type parameter. + public static void SerializeAndCreateEntry(this ZipArchive @this, string entry, T? obj) { + if (obj == null) return; + var zipEntry = @this.CreateEntry(entry); + using var entryStream = zipEntry.Open(); + using var writer = new StreamWriter(entryStream); + new XmlSerializer(typeof(T)).Serialize(writer, obj); + } +} \ No newline at end of file diff --git a/src/bcf-toolkit/Worker.cs b/src/bcf-toolkit/Worker.cs index d4c80ae..cb49575 100644 --- a/src/bcf-toolkit/Worker.cs +++ b/src/bcf-toolkit/Worker.cs @@ -96,11 +96,11 @@ private void InitConverter(BcfVersionEnum? version) { public async Task Convert(string source, string target) { if (source.EndsWith("bcfzip")) { await InitConverterFromArchive(source); - await _converter.BcfZipToJson(source, target); + await _converter.BcfToJson(source, target); } else { await InitConverterFromJson(source); - await _converter.JsonToBcfZip(source, target); + await _converter.JsonToBcf(source, target); } } @@ -112,7 +112,7 @@ public async Task Convert(string source, string target) { /// public Task ToBcfZip(IBcf bcf, string target) { InitConverterFromType(bcf); - return _converter.ToBcfZip(bcf, target); + return _converter.ToBcf(bcf, target); } /// @@ -130,26 +130,32 @@ public Task ToJson(IBcf bcf, string target) { /// The method builds and returns an instance of BCF 3.0 object from the /// specified file stream, independently the input version. /// - /// + /// The stream of the source BCF zip archive. /// public async Task BuildBcfFromStream(Stream stream) { await InitConverterFromStreamArchive(stream); - return await _converter.BuildBcfFromStream(stream); + return await _converter.BcfFromStream(stream); } /// /// The method converts the specified BCF object to the given version, then - /// returns a stream from the BCF zip archive. + /// returns a stream from the BCF zip archive. It either saves the bcf files + /// locally into a tmp folder or creates a zip entry from a memory stream + /// based on the input. /// /// WARNING: Disposing the stream is the responsibility of the user! /// /// The BCF object. - /// The BCF version. + /// The target BCF version. + /// Should the archive be saved in the tmp folder. /// /// Returns the file stream of the BCF zip archive. /// - public async Task ToBcfStream(IBcf bcf, BcfVersionEnum targetVersion) { + public async Task ToBcfStream( + IBcf bcf, + BcfVersionEnum targetVersion, + bool writeToTmp = true) { InitConverterFromType(bcf); - return await _converter.ToBcfStream(bcf, targetVersion); + return await _converter.ToBcf(bcf, targetVersion); } } \ No newline at end of file diff --git a/src/tests/Converter/Bcf21/ConverterTests.cs b/src/tests/Converter/Bcf21/ConverterTests.cs index c691265..c4091bd 100644 --- a/src/tests/Converter/Bcf21/ConverterTests.cs +++ b/src/tests/Converter/Bcf21/ConverterTests.cs @@ -23,21 +23,21 @@ public void Setup() { [Test] public void BcfToJsonSampleFilesTest() { - _converter.BcfZipToJson("Resources/Bcf/v2.1/AllPartsVisible.bcfzip", + _converter.BcfToJson("Resources/Bcf/v2.1/AllPartsVisible.bcfzip", "Resources/output/json/v2.1/AllPartsVisible"); - _converter.BcfZipToJson( + _converter.BcfToJson( "Resources/Bcf/v2.1/ComponentSelection.bcfzip", "Resources/output/json/v2.1/ComponentSelection"); - _converter.BcfZipToJson( + _converter.BcfToJson( "Resources/Bcf/v2.1/ExternalBIMSnippet.bcfzip", "Resources/output/json/v2.1/ExternalBIMSnippet"); - _converter.BcfZipToJson( + _converter.BcfToJson( "Resources/Bcf/v2.1/MaximumInformation.bcfzip", "Resources/output/json/v2.1/MaximumInformation"); - _converter.BcfZipToJson( + _converter.BcfToJson( "Resources/Bcf/v2.1/UserAssignment.bcfzip", "Resources/output/json/v2.1/UserAssignment"); - _converter.BcfZipToJson( + _converter.BcfToJson( "Resources/Bcf/v2.1/BcfVersionAndTopics.bcfzip", "Resources/output/json/v2.1/BcfVersionAndTopics"); } @@ -45,7 +45,7 @@ public void BcfToJsonSampleFilesTest() { [Test] public Task JsonToBcfSampleFilesTest() { var tasks = new List { - _converter.JsonToBcfZip( + _converter.JsonToBcf( "Resources/Json/v2.1/AllPartsVisible", "Resources/output/bcf/v2.1/AllPartsVisible.bcfzip") }; @@ -68,24 +68,25 @@ public async Task ShouldReturnBcfFileStreamTest() { Markups = markups }; - var stream = await _converter.ToBcfStream(bcf, BcfVersionEnum.Bcf21); + var outputStream = new MemoryStream(); + _converter.ToBcf(bcf, BcfVersionEnum.Bcf21, outputStream); - ClassicAssert.IsNotNull(stream); - ClassicAssert.IsTrue(stream.CanRead); + ClassicAssert.IsNotNull(outputStream); + ClassicAssert.IsTrue(outputStream.CanRead); - await stream.DisposeAsync(); + await outputStream.DisposeAsync(); } [Test] public void BcfToJsonMissingRequiredPropertyTest() { - Assert.That(async () => await _converter.BcfZipToJson( - "Resources/Bcf/v2.1/MissingTitle.bcfzip", - "Resources/output/json/v2.1/MissingTitle"), Throws.ArgumentException); + Assert.That(async () => await _converter.BcfToJson( + "Resources/Bcf/v2.1/RelatedTopics.bcfzip", + "Resources/output/json/v2.1/RelatedTopics"), Throws.ArgumentException); } [Test] public void BcfToJsonWrongPathTest() { - Assert.That(async () => await _converter.BcfZipToJson( + Assert.That(async () => await _converter.BcfToJson( "Resources/Bcf/v3.0/Wrong.bcfzip", "Resources/output/json/v2.1/Wrong"), Throws.ArgumentException); } @@ -108,7 +109,7 @@ public Task WriteBcfWithMinimumInformationTest() { var bcf = new Bcf { Markups = markups }; - return _converter.ToBcfZip(bcf, "Resources/output/Bcf/v2.1/MinimumInformation.bcfzip"); + return _converter.ToBcf(bcf, "Resources/output/Bcf/v2.1/MinimumInformation.bcfzip"); } /// @@ -149,7 +150,7 @@ public Task WriteBcfWithoutTopicGuidTest() { var bcf = new Bcf { Markups = markups }; - return _converter.ToBcfZip(bcf, "Resources/output/Bcf/v2.1/WithoutTopicGuid.bcfzip"); + return _converter.ToBcf(bcf, "Resources/output/Bcf/v2.1/WithoutTopicGuid.bcfzip"); } /// @@ -163,7 +164,7 @@ public async Task BuildSimpleV21BcfFromStreamTest() { "Resources/Bcf/v2.1/MinimumInformation.bcfzip", FileMode.Open, FileAccess.Read); - var bcf = await _converter.BuildBcfFromStream(stream); + var bcf = await _converter.BcfFromStream(stream); Assert.That(typeof(Bcf), Is.EqualTo(bcf.GetType())); Assert.That(1, Is.EqualTo(bcf.Markups.Count)); Assert.That("2.1", Is.EqualTo(bcf.Version?.VersionId)); @@ -180,7 +181,7 @@ public async Task BuildSimpleV30BcfFromStreamTest() { "Resources/Bcf/v2.1/MinimumInformation.bcfzip", FileMode.Open, FileAccess.Read); - var bcf = await _converter.BuildBcfFromStream(stream); + var bcf = await _converter.BcfFromStream(stream); Assert.That(typeof(BcfToolkit.Model.Bcf30.Bcf), Is.EqualTo(bcf.GetType())); Assert.That(1, Is.EqualTo(bcf.Markups.Count)); Assert.That("OPEN", Is.EqualTo(bcf.Extensions.TopicStatuses.FirstOrDefault())); diff --git a/src/tests/Converter/Bcf21/FileWriterTest.cs b/src/tests/Converter/Bcf21/FileWriterTest.cs new file mode 100644 index 0000000..5b2aa28 --- /dev/null +++ b/src/tests/Converter/Bcf21/FileWriterTest.cs @@ -0,0 +1,45 @@ +using System.Linq; +using System.Threading.Tasks; +using BcfToolkit.Builder.Bcf21; +using BcfToolkit.Converter.Bcf21; +using NUnit.Framework; + +namespace Tests.Converter.Bcf21; +[TestFixture] +public class FileWriterTests { + [Test] + public async Task WriteBcfWithCreatingZipEntryFromStreamTest() { + var builder = new BcfBuilder(); + var bcf = + builder.WithDefaults() + .Build(); + + var stream = await FileWriter.SerializeAndWriteBcf(bcf); + + var bcfResultBuilder = new BcfBuilder(); + var bcfResult = await bcfResultBuilder + .BuildFromStream(stream); + + Assert.That( + bcf.Markups.FirstOrDefault()?.Topic.Title, + Is.EqualTo(bcfResult.Markups.FirstOrDefault()?.Topic.Title)); + } + + [Test] + public async Task WriteBcfWithSavingXmlTmpTest() { + var builder = new BcfBuilder(); + var bcf = + builder.WithDefaults() + .Build(); + + var stream = await FileWriter.SerializeAndWriteBcf(bcf); + + var bcfResultBuilder = new BcfBuilder(); + var bcfResult = await bcfResultBuilder + .BuildFromStream(stream); + + Assert.That( + bcf.Markups.FirstOrDefault()?.Topic.Title, + Is.EqualTo(bcfResult.Markups.FirstOrDefault()?.Topic.Title)); + } +} \ No newline at end of file diff --git a/src/tests/Converter/Bcf30/ConverterTests.cs b/src/tests/Converter/Bcf30/ConverterTests.cs index a71a4f5..18976a0 100644 --- a/src/tests/Converter/Bcf30/ConverterTests.cs +++ b/src/tests/Converter/Bcf30/ConverterTests.cs @@ -25,37 +25,37 @@ public void Setup() { [Test] [Category("BCF v3.0")] public void BcfToJsonSampleFilesTest() { - _converter.BcfZipToJson( + _converter.BcfToJson( "Resources/Bcf/v3.0/ComponentSelection.bcfzip", "Resources/output/json/v3.0/ComponentSelection"); - _converter.BcfZipToJson( + _converter.BcfToJson( "Resources/Bcf/v3.0/DocumentReferenceExternal.bcfzip", "Resources/output/json/v3.0/DocumentReferenceExternal"); - _converter.BcfZipToJson( + _converter.BcfToJson( "Resources/Bcf/v3.0/DocumentReferenceInternal.bcfzip", "Resources/output/json/v3.0/DocumentReferenceInternal"); - _converter.BcfZipToJson( + _converter.BcfToJson( "Resources/Bcf/v3.0/DueDate.bcfzip", "Resources/output/json/v3.0/DueDate"); - _converter.BcfZipToJson( + _converter.BcfToJson( "Resources/Bcf/v3.0/Labels.bcfzip", "Resources/output/json/v3.0/Labels"); - _converter.BcfZipToJson( + _converter.BcfToJson( "Resources/Bcf/v3.0/Milestone.bcfzip", "Resources/output/json/v3.0/Milestone"); - _converter.BcfZipToJson( + _converter.BcfToJson( "Resources/Bcf/v3.0/RelatedTopics.bcfzip", "Resources/output/json/v3.0/RelatedTopics"); - _converter.BcfZipToJson( + _converter.BcfToJson( "Resources/Bcf/v3.0/SingleInvisibleWall.bcfzip", "Resources/output/json/v3.0/SingleInvisibleWall"); - _converter.BcfZipToJson( + _converter.BcfToJson( "Resources/Bcf/v3.0/TestBcf30.bcfzip", "Resources/output/json/v3.0/TestBcf30"); - _converter.BcfZipToJson( + _converter.BcfToJson( "Resources/Bcf/v3.0/TopicsWithDifferentModelsVisible.bcfzip", "Resources/output/json/v3.0/TopicsWithDifferentModelsVisible"); - _converter.BcfZipToJson( + _converter.BcfToJson( "Resources/Bcf/v3.0/UserAssignment.bcfzip", "Resources/output/json/v3.0/UserAssignment"); } @@ -64,7 +64,7 @@ public void BcfToJsonSampleFilesTest() { [Category("BCF v3.0")] public Task JsonToBcfSampleFilesTest() { var tasks = new List { - _converter.JsonToBcfZip( + _converter.JsonToBcf( "Resources/Json/v3.0/DocumentReferenceInternal", "Resources/output/Bcf/v3.0/DocumentReferenceInternal.bcfzip"), }; @@ -92,19 +92,20 @@ public async Task ShouldReturnBcfFileStreamTest() { Document = documentInfo }; - var streamResult = await _converter.ToBcfStream(bcf, BcfVersionEnum.Bcf30); + var outputStream = new MemoryStream(); + _converter.ToBcf(bcf, BcfVersionEnum.Bcf30, outputStream); - ClassicAssert.IsNotNull(streamResult); - ClassicAssert.IsTrue(streamResult.CanRead); + ClassicAssert.IsNotNull(outputStream); + ClassicAssert.IsTrue(outputStream.CanRead); - await streamResult.DisposeAsync(); + await outputStream.DisposeAsync(); } [Test] [Category("BCF v3.0")] public void BcfToJsonBackwardCompatibilityTest() { // 2.1 -> 3.0 is not backward compatible - Assert.That(async () => await _converter.BcfZipToJson( + Assert.That(async () => await _converter.BcfToJson( "Resources/Bcf/v2.1/AllPartsVisible.bcfzip", "Resources/output/Json/v3.0/AllPartsVisible"), Throws.Exception); } @@ -112,7 +113,7 @@ public void BcfToJsonBackwardCompatibilityTest() { [Test] [Category("BCF v3.0")] public void BcfToJsonWrongPathTest() { - Assert.That(async () => await _converter.BcfZipToJson( + Assert.That(async () => await _converter.BcfToJson( "Resources/Bcf/v3.0/Wrong.bcfzip", "Resources/output/json/v3.0/Wrong"), Throws.Exception); } @@ -120,7 +121,7 @@ public void BcfToJsonWrongPathTest() { [Test] [Category("BCF v3.0")] public void JsonToBcfMissingBcfRootTest() { - Assert.That(async () => await _converter.BcfZipToJson( + Assert.That(async () => await _converter.BcfToJson( "Resources/json/v3.0/MissingBcfRoot.bcfzip", "Resources/output/Bcf/v3.0/MissingBcfRoot"), Throws.Exception); } @@ -143,7 +144,7 @@ public Task WriteBcfWithMinimumInformationTest() { var bcf = new Bcf { Markups = markups }; - return _converter.ToBcfZip(bcf, "Resources/output/Bcf/v3.0/MinimumInformation.bcfzip"); + return _converter.ToBcf(bcf, "Resources/output/Bcf/v3.0/MinimumInformation.bcfzip"); } /// @@ -184,7 +185,7 @@ public Task WriteBcfWithoutTopicGuidTest() { var bcf = new Bcf { Markups = markups }; - return _converter.ToBcfZip(bcf, "Resources/output/Bcf/v3.0/WithoutTopicGuid.bcfzip"); + return _converter.ToBcf(bcf, "Resources/output/Bcf/v3.0/WithoutTopicGuid.bcfzip"); } /// @@ -198,7 +199,7 @@ public async Task BuildSimpleV30BcfFromStreamTest() { "Resources/Bcf/v3.0/ComponentSelection.bcfzip", FileMode.Open, FileAccess.Read); - var bcf = await _converter.BuildBcfFromStream(stream); + var bcf = await _converter.BcfFromStream(stream); Assert.That(typeof(Bcf), Is.EqualTo(bcf.GetType())); Assert.That(1, Is.EqualTo(bcf.Markups.Count)); Assert.That("3.0", Is.EqualTo(bcf.Version?.VersionId)); diff --git a/src/tests/Converter/Bcf30/FileWriterTests.cs b/src/tests/Converter/Bcf30/FileWriterTests.cs new file mode 100644 index 0000000..490494b --- /dev/null +++ b/src/tests/Converter/Bcf30/FileWriterTests.cs @@ -0,0 +1,46 @@ + +using System.Linq; +using System.Threading.Tasks; +using BcfToolkit.Builder.Bcf30; +using BcfToolkit.Converter.Bcf30; +using NUnit.Framework; + +namespace Tests.Converter.Bcf30; +[TestFixture] +public class FileWriterTests { + [Test] + public async Task WriteBcfWithCreatingZipEntryFromStreamTest() { + var builder = new BcfBuilder(); + var bcf = + builder.WithDefaults() + .Build(); + + var stream = await FileWriter.SerializeAndWriteBcf(bcf); + + var bcfResultBuilder = new BcfBuilder(); + var bcfResult = await bcfResultBuilder + .BuildFromStream(stream); + + Assert.That( + bcf.Markups.FirstOrDefault()?.Topic.Title, + Is.EqualTo(bcfResult.Markups.FirstOrDefault()?.Topic.Title)); + } + + [Test] + public async Task WriteBcfWithSavingXmlTmpTest() { + var builder = new BcfBuilder(); + var bcf = + builder.WithDefaults() + .Build(); + + var stream = await FileWriter.SerializeAndWriteBcf(bcf); + + var bcfResultBuilder = new BcfBuilder(); + var bcfResult = await bcfResultBuilder + .BuildFromStream(stream); + + Assert.That( + bcf.Markups.FirstOrDefault()?.Topic.Title, + Is.EqualTo(bcfResult.Markups.FirstOrDefault()?.Topic.Title)); + } +} \ No newline at end of file From aec19732ae7122dd76b688e766197f86c0c32ff4 Mon Sep 17 00:00:00 2001 From: Adam Eri Date: Thu, 13 Jun 2024 18:02:59 +0200 Subject: [PATCH 18/29] Fixing merge conflict --- src/tests/Converter/Bcf21/ConverterTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/Converter/Bcf21/ConverterTests.cs b/src/tests/Converter/Bcf21/ConverterTests.cs index c4091bd..1424565 100644 --- a/src/tests/Converter/Bcf21/ConverterTests.cs +++ b/src/tests/Converter/Bcf21/ConverterTests.cs @@ -80,8 +80,8 @@ public async Task ShouldReturnBcfFileStreamTest() { [Test] public void BcfToJsonMissingRequiredPropertyTest() { Assert.That(async () => await _converter.BcfToJson( - "Resources/Bcf/v2.1/RelatedTopics.bcfzip", - "Resources/output/json/v2.1/RelatedTopics"), Throws.ArgumentException); + "Resources/Bcf/v2.1/MissingTitle.bcfzip", + "Resources/output/json/v2.1/MissingTitle"), Throws.ArgumentException); } [Test] From 386aa937b3c46431bab518ff89d42fd09acdc623 Mon Sep 17 00:00:00 2001 From: Adam Eri Date: Mon, 17 Jun 2024 15:55:53 +0200 Subject: [PATCH 19/29] [BMSPT-293] Added cancellation token to Converter stream operations. (#18) * [BMSPT-293] Added cancellation token to Converter stream operations. * [BMSPT-293] Added cancellation token to Converter stream operations. --------- Co-authored-by: Adam Eri --- src/bcf-toolkit/Converter/Bcf21/Converter.cs | 29 ++++++++++++-- src/bcf-toolkit/Converter/Bcf21/FileWriter.cs | 38 +++++++++++++----- src/bcf-toolkit/Converter/Bcf30/Converter.cs | 26 ++++++++++-- src/bcf-toolkit/Converter/Bcf30/FileWriter.cs | 32 +++++++++++---- src/bcf-toolkit/Converter/IConverter.cs | 40 +++++++++++++++++++ src/tests/Converter/Bcf21/FileWriterTest.cs | 5 ++- 6 files changed, 141 insertions(+), 29 deletions(-) diff --git a/src/bcf-toolkit/Converter/Bcf21/Converter.cs b/src/bcf-toolkit/Converter/Bcf21/Converter.cs index afbcd96..60fb2d2 100644 --- a/src/bcf-toolkit/Converter/Bcf21/Converter.cs +++ b/src/bcf-toolkit/Converter/Bcf21/Converter.cs @@ -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; @@ -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. /// - private readonly Dictionary>> + private readonly Dictionary>> _writerFn = new() { [BcfVersionEnum.Bcf21] = FileWriter.SerializeAndWriteBcf, @@ -43,7 +45,8 @@ private readonly Dictionary>> /// Defines the stream writer function which must be used for write the BCF /// object to the targeted version. /// - private readonly Dictionary> + private readonly Dictionary> _streamWriterFn = new() { [BcfVersionEnum.Bcf21] = FileWriter.SerializeAndWriteBcfToStream, @@ -86,15 +89,29 @@ public async Task JsonToBcf(string source, string target) { } public async Task ToBcf(IBcf bcf, BcfVersionEnum targetVersion) { + return await this.ToBcf(bcf: bcf, targetVersion: targetVersion, + cancellationToken: null); + } + + public async Task 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."); } @@ -102,9 +119,13 @@ public void ToBcf(IBcf bcf, BcfVersionEnum targetVersion, Stream stream) { 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) { diff --git a/src/bcf-toolkit/Converter/Bcf21/FileWriter.cs b/src/bcf-toolkit/Converter/Bcf21/FileWriter.cs index c1b40d8..216056e 100644 --- a/src/bcf-toolkit/Converter/Bcf21/FileWriter.cs +++ b/src/bcf-toolkit/Converter/Bcf21/FileWriter.cs @@ -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; @@ -45,7 +46,7 @@ public static Task WriteJson(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) @@ -53,16 +54,19 @@ public static Task WriteJson(Bcf bcf, string target) { /// 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! /// /// The `BCF` object that should be written. + /// /// It returns a stream of the archive. - public static async Task SerializeAndWriteBcf(IBcf bcf) { + public static async Task 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); @@ -78,16 +82,21 @@ public static async Task SerializeAndWriteBcf(IBcf bcf) { /// /// The `Bcf` object that should be written. /// The zip archive which the object is written in. + /// /// Generated stream from bcf zip. /// 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"); @@ -121,12 +130,14 @@ public static void SerializeAndWriteBcfToStream(IBcf bcf, /// The BCF object. /// The target file name of the BCFzip. /// Should delete the generated tmp folder now or later. + /// /// Generated temp folder path. /// public static async Task 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( @@ -140,14 +151,19 @@ public static async Task SerializeAndWriteBcfToFolder( var bcfObject = (Bcf)bcf; - var writeTasks = new List(); - writeTasks.Add(BcfExtensions.SerializeAndWriteXmlFile( - tmpFolder, - "bcf.version", - new Version())); + var writeTasks = new List { + 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( diff --git a/src/bcf-toolkit/Converter/Bcf30/Converter.cs b/src/bcf-toolkit/Converter/Bcf30/Converter.cs index 8dd3bb9..bd5cd19 100644 --- a/src/bcf-toolkit/Converter/Bcf30/Converter.cs +++ b/src/bcf-toolkit/Converter/Bcf30/Converter.cs @@ -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.Bcf30; using BcfToolkit.Utils; @@ -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. /// - private readonly Dictionary>> + private readonly Dictionary>> _fileWriterFn = new() { [BcfVersionEnum.Bcf21] = Bcf21.FileWriter.SerializeAndWriteBcf, @@ -43,7 +45,8 @@ private readonly Dictionary>> /// Defines the stream writer function which must be used for write the BCF /// object to the targeted version. /// - private readonly Dictionary> + private readonly Dictionary> _streamWriterFn = new() { [BcfVersionEnum.Bcf21] = Bcf21.FileWriter.SerializeAndWriteBcfToStream, @@ -85,15 +88,26 @@ await JsonExtensions.ParseObject( } public async Task ToBcf(IBcf bcf, BcfVersionEnum targetVersion) { + return await this.ToBcf(bcf: bcf, targetVersion: targetVersion, + cancellationToken: null); + } + + public async Task ToBcf(IBcf bcf, BcfVersionEnum targetVersion, + CancellationToken? cancellationToken) { var converterFn = _converterFn[targetVersion]; var convertedBcf = converterFn((Bcf)bcf); var writerFn = _fileWriterFn[targetVersion]; - return await writerFn(convertedBcf); + return await writerFn(convertedBcf, cancellationToken); } public void ToBcf(IBcf bcf, BcfVersionEnum targetVersion, Stream stream) { + this.ToBcf(bcf: bcf, targetVersion: targetVersion, stream: stream, + cancellationToken: null); + } + public void ToBcf(IBcf bcf, BcfVersionEnum targetVersion, Stream stream, + CancellationToken? cancellationToken) { if (!stream.CanWrite) { throw new ArgumentException("Stream is not writable."); } @@ -101,9 +115,13 @@ public void ToBcf(IBcf bcf, BcfVersionEnum targetVersion, Stream stream) { 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) { diff --git a/src/bcf-toolkit/Converter/Bcf30/FileWriter.cs b/src/bcf-toolkit/Converter/Bcf30/FileWriter.cs index 826aa9b..6d3364a 100644 --- a/src/bcf-toolkit/Converter/Bcf30/FileWriter.cs +++ b/src/bcf-toolkit/Converter/Bcf30/FileWriter.cs @@ -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; @@ -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! /// /// The `BCF` object that should be written. - /// Should the archive be saved in the tmp folder. + /// /// It returns a stream of the archive. - public static async Task SerializeAndWriteBcf(IBcf bcf) { + public static async Task SerializeAndWriteBcf(IBcf bcf, + CancellationToken? cancellationToken = null) { var workingDir = Directory.GetCurrentDirectory(); var tmpBcfTargetPath = workingDir + $"/{Guid.NewGuid()}.bcfzip"; var tmpFolder = @@ -96,20 +98,26 @@ public static async Task SerializeAndWriteBcf(IBcf bcf) { /// * bcf.version /// * extensions.xml /// * documents.xml (optional) - /// + /// /// /// The `BCF` object that should be written.. /// The zip archive which the object is written in. + /// /// Memory stream of the bcfzip /// - /// - 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"); @@ -146,10 +154,14 @@ public static void SerializeAndWriteBcfToStream(IBcf bcf, ZipArchive zip) { /// The BCF object. /// The target file name of the BCFzip. /// Should delete the generated tmp folder now or later + /// /// Generated temp folder path /// - public static async Task SerializeAndWriteBcfToFolder(IBcf bcf, - string target, bool delete = true) { + public static async Task SerializeAndWriteBcfToFolder( + IBcf bcf, + string target, + bool delete = true, + CancellationToken? cancellationToken = null) { var targetFolder = Path.GetDirectoryName(target); if (targetFolder == null) throw new ApplicationException( @@ -170,6 +182,10 @@ public static async Task 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( diff --git a/src/bcf-toolkit/Converter/IConverter.cs b/src/bcf-toolkit/Converter/IConverter.cs index e3fe53f..0569769 100644 --- a/src/bcf-toolkit/Converter/IConverter.cs +++ b/src/bcf-toolkit/Converter/IConverter.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Threading; using System.Threading.Tasks; using BcfToolkit.Model; @@ -50,6 +51,25 @@ public interface IConverter { /// Returns the file stream of the BCF zip archive. Task ToBcf(IBcf bcf, BcfVersionEnum targetVersion); + /// + /// 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. + /// + /// The BCF object. + /// The BCF version. + /// + /// Returns the file stream of the BCF zip archive. + Task ToBcf(IBcf bcf, BcfVersionEnum targetVersion, + CancellationToken? cancellationToken); + /// /// The method converts the specified BCF object to the given version, then /// writes it to the specified stream. @@ -68,6 +88,26 @@ public interface IConverter { /// void ToBcf(IBcf bcf, BcfVersionEnum targetVersion, Stream stream); + /// + /// 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. + /// + /// The BCF object. + /// The BCF version. + /// + /// The output stream, which should be writable. + /// + /// + /// + /// Throws an error if the stream is not writable. + /// + void ToBcf(IBcf bcf, BcfVersionEnum targetVersion, Stream stream, + CancellationToken? cancellationToken); + /// /// The method writes the specified BCF model to BCFzip files. /// diff --git a/src/tests/Converter/Bcf21/FileWriterTest.cs b/src/tests/Converter/Bcf21/FileWriterTest.cs index 5b2aa28..29d5742 100644 --- a/src/tests/Converter/Bcf21/FileWriterTest.cs +++ b/src/tests/Converter/Bcf21/FileWriterTest.cs @@ -5,6 +5,7 @@ using NUnit.Framework; namespace Tests.Converter.Bcf21; + [TestFixture] public class FileWriterTests { [Test] @@ -14,7 +15,7 @@ public async Task WriteBcfWithCreatingZipEntryFromStreamTest() { builder.WithDefaults() .Build(); - var stream = await FileWriter.SerializeAndWriteBcf(bcf); + var stream = await FileWriter.SerializeAndWriteBcf(bcf, null); var bcfResultBuilder = new BcfBuilder(); var bcfResult = await bcfResultBuilder @@ -32,7 +33,7 @@ public async Task WriteBcfWithSavingXmlTmpTest() { builder.WithDefaults() .Build(); - var stream = await FileWriter.SerializeAndWriteBcf(bcf); + var stream = await FileWriter.SerializeAndWriteBcf(bcf, null); var bcfResultBuilder = new BcfBuilder(); var bcfResult = await bcfResultBuilder From 12a18d85f02702b2d1b94c7dbc7bdef67088a085 Mon Sep 17 00:00:00 2001 From: Balint Bende Date: Tue, 18 Jun 2024 13:50:26 +0200 Subject: [PATCH 20/29] extended bcf builder with new functions, project, document, formating, refactoring, extended worker with converter ToBcf fns, supporting cancellation token and output stream --- bcf-toolkit.sln.DotSettings.user | 5 +- src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs | 4 +- .../Builder/Bcf30/BcfBuilderExtensions.cs | 15 +++ .../Builder/Bcf30/Interfaces/IBcfBuilder.cs | 4 +- src/bcf-toolkit/Converter/Bcf21/Converter.cs | 31 +++-- .../Converter/Bcf21/SchemaConverterToBcf30.cs | 4 +- src/bcf-toolkit/Converter/Bcf30/Converter.cs | 31 +++-- src/bcf-toolkit/Converter/IConverter.cs | 5 +- src/bcf-toolkit/Worker.cs | 111 ++++++++++++++---- src/tests/Builder/BcfBuilderTests.cs | 2 +- src/tests/WorkerTests.cs | 14 +-- 11 files changed, 164 insertions(+), 62 deletions(-) diff --git a/bcf-toolkit.sln.DotSettings.user b/bcf-toolkit.sln.DotSettings.user index f50ce56..0f3f760 100644 --- a/bcf-toolkit.sln.DotSettings.user +++ b/bcf-toolkit.sln.DotSettings.user @@ -3,6 +3,7 @@ <SessionState ContinuousTestingMode="0" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Solution /> </SessionState> + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Solution /> </SessionState> @@ -33,9 +34,7 @@ <SessionState ContinuousTestingMode="0" Name="All tests from Solution #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Solution /> </SessionState> - <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution #3" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <Solution /> -</SessionState> + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Solution /> diff --git a/src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs index d534fee..33a3677 100644 --- a/src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs @@ -33,7 +33,7 @@ public BcfBuilder AddMarkup(Action builder) { return this; } - public BcfBuilder SetProjectInfo(Action builder) { + public BcfBuilder SetProject(Action builder) { var projectInfo = BuilderUtils.BuildItem(builder); _bcf.Project = projectInfo; @@ -47,7 +47,7 @@ public BcfBuilder SetExtensions(Action builder) { return this; } - public BcfBuilder SetDocumentInfo(Action builder) { + public BcfBuilder SetDocument(Action builder) { var documentInfo = BuilderUtils.BuildItem(builder); _bcf.Document = documentInfo; diff --git a/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs b/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs index 57c5df7..6c6b777 100644 --- a/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs +++ b/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs @@ -102,4 +102,19 @@ private void UpdateExtensions(Topic topic) { extensions.Users.ForEach(u => _bcf.Extensions.Users.AddIfNotExists(u)); } + + public BcfBuilder SetExtensions(Extensions extensions) { + _bcf.Extensions = extensions; + return this; + } + + public BcfBuilder SetProject(ProjectInfo? project) { + _bcf.Project = project; + return this; + } + + public BcfBuilder SetDocument(DocumentInfo? document) { + _bcf.Document = document; + return this; + } } \ No newline at end of file diff --git a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilder.cs index 5d92648..c80b33c 100644 --- a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilder.cs @@ -25,7 +25,7 @@ public interface IBcfBuilder< /// /// Builder of the project info. /// Returns the builder object. - TBuilder SetProjectInfo(Action builder); + TBuilder SetProject(Action builder); /// /// Returns the builder object set with the `Extensions`. @@ -39,5 +39,5 @@ public interface IBcfBuilder< /// /// Builder of the document info. /// Returns the builder object. - TBuilder SetDocumentInfo(Action builder); + TBuilder SetDocument(Action builder); } \ No newline at end of file diff --git a/src/bcf-toolkit/Converter/Bcf21/Converter.cs b/src/bcf-toolkit/Converter/Bcf21/Converter.cs index 60fb2d2..1cb3ea9 100644 --- a/src/bcf-toolkit/Converter/Bcf21/Converter.cs +++ b/src/bcf-toolkit/Converter/Bcf21/Converter.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.IO.Compression; +using System.Linq; using System.Threading; using System.Threading.Tasks; using BcfToolkit.Builder.Bcf21; @@ -33,7 +34,8 @@ public class Converter : IConverter { /// Defines the file writer function which must be used for write the BCF /// object to the targeted version. /// - private readonly Dictionary>> _writerFn = new() { @@ -45,7 +47,8 @@ public class Converter : IConverter { /// Defines the stream writer function which must be used for write the BCF /// object to the targeted version. /// - private readonly Dictionary> _streamWriterFn = new() { @@ -75,25 +78,28 @@ public async Task JsonToBcf(string source, string target) { var projectPath = $"{source}/project.json"; var project = Path.Exists(projectPath) ? await JsonExtensions.ParseObject(projectPath) - : new ProjectExtension(); + : null; var markups = await JsonExtensions.ParseMarkups(source); - var bcf = new Bcf { - Markups = markups, - Project = project, - Version = new Version() - }; + var bcf = _builder + .AddMarkups(markups.ToList()) + .SetProject(project) + .Build(); await FileWriter.SerializeAndWriteBcfToFolder(bcf, target); } public async Task ToBcf(IBcf bcf, BcfVersionEnum targetVersion) { - return await this.ToBcf(bcf: bcf, targetVersion: targetVersion, + return await this.ToBcf( + bcf: bcf, + targetVersion: targetVersion, cancellationToken: null); } - public async Task ToBcf(IBcf bcf, BcfVersionEnum targetVersion, + public async Task ToBcf( + IBcf bcf, + BcfVersionEnum targetVersion, CancellationToken? cancellationToken) { var converterFn = _converterFn[targetVersion]; var convertedBcf = converterFn((Bcf)bcf); @@ -110,7 +116,10 @@ public void ToBcf(IBcf bcf, BcfVersionEnum targetVersion, Stream stream) { this.ToBcf(bcf, targetVersion, stream, null); } - public void ToBcf(IBcf bcf, BcfVersionEnum targetVersion, Stream stream, + public void ToBcf( + IBcf bcf, + BcfVersionEnum targetVersion, + Stream stream, CancellationToken? cancellationToken) { if (!stream.CanWrite) { throw new ArgumentException("Stream is not writable."); diff --git a/src/bcf-toolkit/Converter/Bcf21/SchemaConverterToBcf30.cs b/src/bcf-toolkit/Converter/Bcf21/SchemaConverterToBcf30.cs index 0ec40e9..fde2374 100644 --- a/src/bcf-toolkit/Converter/Bcf21/SchemaConverterToBcf30.cs +++ b/src/bcf-toolkit/Converter/Bcf21/SchemaConverterToBcf30.cs @@ -19,14 +19,14 @@ public static Model.Bcf30.Bcf Convert(Model.Bcf21.Bcf from) { var builder = new BcfBuilder(); builder .AddMarkups(from.Markups.Select(ConvertMarkup).ToList(), true) - .SetDocumentInfo(UpdateDocumentInfo(from.Markups + .SetDocument(UpdateDocumentInfo(from.Markups .SelectMany(m => m.Topic.DocumentReference) .Where(r => !r.IsExternal) .ToList())); var project = from.Project; if (project != null) - builder.SetProjectInfo(p => p + builder.SetProject(p => p .SetProjectId(project.Project.ProjectId) .SetProjectName(project.Project.Name)); diff --git a/src/bcf-toolkit/Converter/Bcf30/Converter.cs b/src/bcf-toolkit/Converter/Bcf30/Converter.cs index bd5cd19..bf36397 100644 --- a/src/bcf-toolkit/Converter/Bcf30/Converter.cs +++ b/src/bcf-toolkit/Converter/Bcf30/Converter.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.IO.Compression; +using System.Linq; using System.Threading; using System.Threading.Tasks; using BcfToolkit.Builder.Bcf30; @@ -33,7 +34,8 @@ public class Converter : IConverter { /// Defines the file writer function which must be used for write the BCF /// object to the targeted version. /// - private readonly Dictionary>> _fileWriterFn = new() { @@ -45,7 +47,8 @@ public class Converter : IConverter { /// Defines the stream writer function which must be used for write the BCF /// object to the targeted version. /// - private readonly Dictionary> _streamWriterFn = new() { @@ -66,7 +69,7 @@ public async Task BcfToJson(string source, string target) { } public async Task JsonToBcf(string source, string target) { - // Project and DocumentInfo are optional + // Project and Document are optional var extensions = await JsonExtensions.ParseObject($"{source}/extensions.json"); var project = @@ -76,13 +79,12 @@ await JsonExtensions.ParseObject( $"{source}/documents.json"); var markups = await JsonExtensions.ParseMarkups(source); - var bcf = new Bcf { - Markups = markups, - Extensions = extensions, - Project = project, - Document = documents, - Version = new Version() - }; + var bcf = _builder + .AddMarkups(markups.ToList()) + .SetExtensions(extensions) + .SetProject(project) + .SetDocument(documents) + .Build(); await FileWriter.SerializeAndWriteBcfToFolder(bcf, target); } @@ -92,7 +94,9 @@ public async Task ToBcf(IBcf bcf, BcfVersionEnum targetVersion) { cancellationToken: null); } - public async Task ToBcf(IBcf bcf, BcfVersionEnum targetVersion, + public async Task ToBcf( + IBcf bcf, + BcfVersionEnum targetVersion, CancellationToken? cancellationToken) { var converterFn = _converterFn[targetVersion]; var convertedBcf = converterFn((Bcf)bcf); @@ -106,7 +110,10 @@ public void ToBcf(IBcf bcf, BcfVersionEnum targetVersion, Stream stream) { cancellationToken: null); } - public void ToBcf(IBcf bcf, BcfVersionEnum targetVersion, Stream stream, + public void ToBcf( + IBcf bcf, + BcfVersionEnum targetVersion, + Stream stream, CancellationToken? cancellationToken) { if (!stream.CanWrite) { throw new ArgumentException("Stream is not writable."); diff --git a/src/bcf-toolkit/Converter/IConverter.cs b/src/bcf-toolkit/Converter/IConverter.cs index 0569769..79a4d2f 100644 --- a/src/bcf-toolkit/Converter/IConverter.cs +++ b/src/bcf-toolkit/Converter/IConverter.cs @@ -125,8 +125,9 @@ void ToBcf(IBcf bcf, BcfVersionEnum targetVersion, Stream stream, Task ToJson(IBcf bcf, string target); /// - /// The function builds a BCF in memory representation of the specified - /// target version from the given stream. + /// The function builds a BCF in-memory representation of the specified + /// target version from the provided stream. The type of the target BCF + /// must be specified as a type parameter. /// /// The BCF file stream. /// diff --git a/src/bcf-toolkit/Worker.cs b/src/bcf-toolkit/Worker.cs index cb49575..e453a98 100644 --- a/src/bcf-toolkit/Worker.cs +++ b/src/bcf-toolkit/Worker.cs @@ -2,6 +2,7 @@ using System; using System.IO; +using System.Threading; using System.Threading.Tasks; using BcfToolkit.Converter; using BcfToolkit.Model; @@ -14,12 +15,13 @@ namespace BcfToolkit; /// The `Worker` class defines the converter strategy for a specific /// version of BCF and performs the conversion according to the chosen type of /// conversion. -/// Types of the conversion: +/// Types of the conversions: /// - BCF to json /// - json to BCF -/// - in memory model to BCF -/// - in memory model to JSON -/// - file stream to in memory model +/// - in-memory model to BCF +/// - in-memory model to JSON +/// - in-memory model to file stream +/// - file stream to in-memory model /// public class Worker { /// @@ -105,18 +107,18 @@ public async Task Convert(string source, string target) { } /// - /// The method writes the specified BCF models to BCF files. + /// The method writes the specified BCF model to BCF file. /// /// The `IBcf` interface of the BCF. /// The target path where the BCF is written. /// - public Task ToBcfZip(IBcf bcf, string target) { + public Task ToBcf(IBcf bcf, string target) { InitConverterFromType(bcf); return _converter.ToBcf(bcf, target); } /// - /// The method writes the specified BCF models to JSON files. + /// The method writes the specified BCF model to JSON files. /// /// The `IBcf` interface of the BCF. /// The target path where the JSON is written. @@ -127,35 +129,104 @@ public Task ToJson(IBcf bcf, string target) { } /// - /// The method builds and returns an instance of BCF 3.0 object from the - /// specified file stream, independently the input version. + /// The method builds and returns an instance of the latest (BCF 3.0) object + /// from the specified file stream, independently the input version. It + /// converts the BCF from the given version to the latest. /// /// The stream of the source BCF zip archive. - /// - public async Task BuildBcfFromStream(Stream stream) { + /// Returns the in-memory BCF model. + public async Task BcfFromStream(Stream stream) { await InitConverterFromStreamArchive(stream); - return await _converter.BcfFromStream(stream); + await _converter.BcfFromStream(stream); } /// /// The method converts the specified BCF object to the given version, then - /// returns a stream from the BCF zip archive. It either saves the bcf files - /// locally into a tmp folder or creates a zip entry from a memory stream - /// based on the input. + /// returns a stream from the BCF zip archive. It saves the bcf files + /// locally into a tmp folder. + /// + /// IMPORTANT: It is the user's responsibility to dispose of the stream + /// after use. Failure to do so may result in resource leaks and potential + /// application instability. + /// + /// The BCF object. + /// The target BCF version. + /// + /// Returns the file stream of the BCF zip archive. + /// + public async Task ToBcf(IBcf bcf, BcfVersionEnum targetVersion) { + InitConverterFromType(bcf); + return await _converter.ToBcf(bcf, targetVersion); + } + + /// + /// The method converts the specified BCF object to the given version with + /// cancellation token, then returns a stream from the BCF zip archive. It + /// saves the bcf files locally into a tmp folder. /// - /// WARNING: Disposing the stream is the responsibility of the user! + /// IMPORTANT: It is the user's responsibility to dispose of the stream + /// after use. Failure to do so may result in resource leaks and potential + /// application instability. /// /// The BCF object. /// The target BCF version. - /// Should the archive be saved in the tmp folder. + /// + /// A token to monitor for cancellation requests. + /// /// /// Returns the file stream of the BCF zip archive. /// - public async Task ToBcfStream( + public async Task ToBcf( IBcf bcf, BcfVersionEnum targetVersion, - bool writeToTmp = true) { + CancellationToken cancellationToken) { InitConverterFromType(bcf); - return await _converter.ToBcf(bcf, targetVersion); + return await _converter.ToBcf(bcf, targetVersion, cancellationToken); + } + + /// + /// The method converts the given BCF object to the given version, then + /// writes the BCF zip archive 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. + /// + /// The BCF object. + /// The target BCF version. + /// The output stream, which should be writable. + public void ToBcf( + IBcf bcf, + BcfVersionEnum targetVersion, + Stream stream) { + InitConverterFromType(bcf); + _converter.ToBcf(bcf, targetVersion, stream); + } + + /// + /// The method converts the given BCF object to the given version, with + /// cancellation token, then writes the BCF zip archive 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. + /// + /// The BCF object. + /// The target BCF version. + /// The output stream, which should be writable. + /// + /// A token to monitor for cancellation requests. + /// + /// + /// Returns the file stream of the BCF zip archive. + /// + public void ToBcf( + IBcf bcf, + BcfVersionEnum targetVersion, + Stream stream, + CancellationToken cancellationToken) { + InitConverterFromType(bcf); + _converter.ToBcf(bcf, targetVersion, stream, cancellationToken); } } \ No newline at end of file diff --git a/src/tests/Builder/BcfBuilderTests.cs b/src/tests/Builder/BcfBuilderTests.cs index a94c704..976888c 100644 --- a/src/tests/Builder/BcfBuilderTests.cs +++ b/src/tests/Builder/BcfBuilderTests.cs @@ -27,7 +27,7 @@ public void BuildBcfWithComplexFieldsTest() { .AddTopicType("ERROR") .AddTopicStatus("OPEN") .AddPriority("HIGH")) - .SetProjectInfo(p => p + .SetProject(p => p .SetProjectId("3ZSh2muKX7S8MCESk95seC") .SetProjectName("Project")) .Build(); diff --git a/src/tests/WorkerTests.cs b/src/tests/WorkerTests.cs index 27d0313..8190472 100644 --- a/src/tests/WorkerTests.cs +++ b/src/tests/WorkerTests.cs @@ -28,7 +28,7 @@ public async Task BuildBcfFromV21StreamTest() { const string path = "Resources/Bcf/v2.1/MiniSolibri.bcfzip"; await using var stream = new FileStream(path, FileMode.Open, FileAccess.Read); - await _worker.BuildBcfFromStream(stream); + await _worker.BcfFromStream(stream); } [Test] @@ -49,7 +49,7 @@ public async Task BuildBcfFromV21StreamSamplesTests() { var tasks = samples.Select(async path => { await using var stream = new FileStream(path, FileMode.Open, FileAccess.Read); - await _worker.BuildBcfFromStream(stream); + await _worker.BcfFromStream(stream); }).ToArray(); await Task.WhenAll(tasks); @@ -62,7 +62,7 @@ public async Task BuildBcfFromV30StreamTest() { "Resources/Bcf/v3.0/DocumentReferenceExternal.bcfzip", FileMode.Open, FileAccess.Read); - await _worker.BuildBcfFromStream(stream); + await _worker.BcfFromStream(stream); } [Test] @@ -130,7 +130,7 @@ public async Task BcfV21StreamFromV30ObjectTest() { private async Task CheckBcfStreamVersion( IBcf bcf, BcfVersionEnum expectedVersion) { - var stream = await _worker.ToBcfStream(bcf, expectedVersion); + var stream = await _worker.ToBcf(bcf, expectedVersion); var version = await BcfExtensions.GetVersionFromStreamArchive(stream); Assert.That(expectedVersion, Is.EqualTo(version)); await stream.FlushAsync(); @@ -159,7 +159,7 @@ public async Task BcfV30ToV21StreamSamplesTests() { new FileStream(path, FileMode.Open, FileAccess.Read); var bcf = await builder.BuildInMemoryFromStream(inputStream); - var stream = await _worker.ToBcfStream(bcf, BcfVersionEnum.Bcf21); + var stream = await _worker.ToBcf(bcf, BcfVersionEnum.Bcf21); var version = await BcfExtensions.GetVersionFromStreamArchive(stream); Assert.That(BcfVersionEnum.Bcf21, Is.EqualTo(version)); await stream.FlushAsync(); @@ -266,7 +266,7 @@ public async Task ToBcfZipV21Tests() { .SetCreationAuthor("Creator")) .Build(); - await _worker.ToBcfZip(bcf, "Resources/output/bcf/v2.1/BcfZipTest21.bcfzip"); + await _worker.ToBcf(bcf, "Resources/output/bcf/v2.1/BcfZipTest21.bcfzip"); } [Test] @@ -284,7 +284,7 @@ public async Task ToBcfZipV30Tests() { .SetExtensions(e => e.WithDefaults()) .Build(); - await _worker.ToBcfZip(bcf, "Resources/output/bcf/v3.0/BcfZipTest30.bcfzip"); + await _worker.ToBcf(bcf, "Resources/output/bcf/v3.0/BcfZipTest30.bcfzip"); } [Test] From 4df1e0b2bb38424b9d6befc0f8bd81f275af69a3 Mon Sep 17 00:00:00 2001 From: Balint Bende Date: Tue, 18 Jun 2024 14:18:35 +0200 Subject: [PATCH 21/29] Updated Readme files --- README.md | 55 +++++++++++++++++++++++++++++---------- src/bcf-toolkit/README.md | 55 +++++++++++++++++++++++++++++---------- 2 files changed, 82 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 1891614..7a39069 100644 --- a/README.md +++ b/README.md @@ -87,10 +87,12 @@ var bcf = builder ``` ### Using BCF worker -The worker is implemented to use predefined workflows to convert `BCF` files -into `json` and back. The function decides which workflow must be used according -to the source and target. If the source ends with `.bcfzip` the converter uses -the `BcfZipToJson` for example. +The `BCF` worker is designed to facilitate the conversion of `BCF` files to +`JSON` and vice versa using predefined workflows. The appropriate workflow is +selected based on the file extensions of the source and target. For example, if +the source file ends with `.bcfzip`, the `BcfZipToJson` converter is used. + +#### Basic conversion ```csharp using BcfToolkit; @@ -98,8 +100,12 @@ using BcfToolkit; var worker = new Worker(); await worker.Convert(source, target); ``` -The exact converter can be called directly as well for both converting -directions, `BCF` into `json` and back. + +#### Direct converter usage +You can also call specific converters directly for converting between `BCF` and +`JSON`. + +##### BCF to JSON ```csharp using BcfToolkit.Converter.Bcf30; @@ -108,6 +114,8 @@ var converter = new Converter() converter.BcfZipToJson(source, target); ``` +##### JSON to BCF + ```csharp using BcfToolkit.Converter.Bcf30; @@ -115,9 +123,12 @@ var converter = new Converter() converter.JsonToBcfZip(source, target); ``` -Furthermore `BCF` archive can be consumed as a stream. The version of the source -is established by the code, then the class lets the nested converter object to -do the conversion to BCF 3.0 accordingly. +#### Stream-based conversion +The library supports consuming `BCF` archives as streams. The code determines +the version of the source and delegates the conversion to the appropriate nested +converter object, converting it to `BCF` 3.0 as needed. + +##### BCF from Stream ```csharp using BcfToolkit; @@ -128,9 +139,10 @@ var worker = new Worker(); var bcf = await worker.BcfFromStream(stream); ``` -The worker can return a file stream from the specified instance of the bcf -object. The function converts the bcf to the target version. Disposing the -stream after the usage is the responsibility of the caller. +##### BCF to Stream +The worker can generate a file stream from a specified `BCF` object, converting +it to the desired version. +> IMPORTANT: The user is responsible for disposing of the stream after use. ```csharp using BcfToolkit; @@ -141,8 +153,8 @@ var stream = await worker.ToBcf(bcf, BcfVersionEnum.Bcf30); await stream.FlushAsync(); ``` -It is also possible to define the output stream to which the results will -be written. +##### Specifying Output Stream +You can define an output stream to which the conversion results will be written. ```csharp using BcfToolkit; @@ -154,6 +166,21 @@ worker.ToBcf(bcf, BcfVersionEnum.Bcf30, outputStream); await outputStream.FlushAsync(); ``` +#### Cancellation Support +The worker supports `CancellationToken` to allow for the cancellation of +asynchronous operations. + +```csharp +using BcfToolkit; + +var worker = new Worker(); +var outputStream = new MemoryStream(); +var source = new CancellationTokenSource(); +var token = source.Token; +worker.ToBcf(bcf, BcfVersionEnum.Bcf30, outputStream, token); +// custom code to use the stream... +await outputStream.FlushAsync(); +``` ## File Structure diff --git a/src/bcf-toolkit/README.md b/src/bcf-toolkit/README.md index 1891614..e42f69e 100644 --- a/src/bcf-toolkit/README.md +++ b/src/bcf-toolkit/README.md @@ -87,10 +87,12 @@ var bcf = builder ``` ### Using BCF worker -The worker is implemented to use predefined workflows to convert `BCF` files -into `json` and back. The function decides which workflow must be used according -to the source and target. If the source ends with `.bcfzip` the converter uses -the `BcfZipToJson` for example. +The `BCF` worker is designed to facilitate the conversion of `BCF` files to +`JSON` and vice versa using predefined workflows. The appropriate workflow is +selected based on the file extensions of the source and target. For example, if +the source file ends with `.bcfzip`, the `BcfZipToJson` converter is used. + +#### Basic conversion ```csharp using BcfToolkit; @@ -98,8 +100,12 @@ using BcfToolkit; var worker = new Worker(); await worker.Convert(source, target); ``` -The exact converter can be called directly as well for both converting -directions, `BCF` into `json` and back. + +#### Direct converter usage +You can also call specific converters directly for converting between `BCF` and +`JSON`. + +##### BCF to JSON ```csharp using BcfToolkit.Converter.Bcf30; @@ -108,6 +114,8 @@ var converter = new Converter() converter.BcfZipToJson(source, target); ``` +##### JSON to BCF + ```csharp using BcfToolkit.Converter.Bcf30; @@ -115,9 +123,12 @@ var converter = new Converter() converter.JsonToBcfZip(source, target); ``` -Furthermore `BCF` archive can be consumed as a stream. The version of the source -is established by the code, then the class lets the nested converter object to -do the conversion to BCF 3.0 accordingly. +#### Stream-based conversion +The library supports consuming `BCF` archives as streams. The code determines +the version of the source and delegates the conversion to the appropriate nested +converter object, converting it to `BCF` 3.0 as needed. + +##### BCF from Stream ```csharp using BcfToolkit; @@ -128,9 +139,10 @@ var worker = new Worker(); var bcf = await worker.BcfFromStream(stream); ``` -The worker can return a file stream from the specified instance of the bcf -object. The function converts the bcf to the target version. Disposing the -stream after the usage is the responsibility of the caller. +##### BCF to Stream +The worker can generate a file stream from a specified `BCF` object, converting +it to the desired version. +> IMPORTANT: The user is responsible for disposing of the stream after use. ```csharp using BcfToolkit; @@ -141,8 +153,8 @@ var stream = await worker.ToBcf(bcf, BcfVersionEnum.Bcf30); await stream.FlushAsync(); ``` -It is also possible to define the output stream to which the results will -be written. +##### Specifying Output Stream +You can define an output stream to which the conversion results will be written. ```csharp using BcfToolkit; @@ -154,6 +166,21 @@ worker.ToBcf(bcf, BcfVersionEnum.Bcf30, outputStream); await outputStream.FlushAsync(); ``` +#### Cancellation Support +The worker supports `CancellationToken` to allow for the cancellation of +asynchronous operations. + +```csharp +using BcfToolkit; + +var worker = new Worker(); +var outputStream = new MemoryStream(); +var source = new CancellationTokenSource(); +var token = source.Token; +worker.ToBcf(bcf, BcfVersionEnum.Bcf30, outputStream, token); +// custom code to use the stream... +await outputStream.FlushAsync(); +``` ## File Structure From c9767eb3b90034a31350f28aea0ccd1f24fb13de Mon Sep 17 00:00:00 2001 From: Balint Bende Date: Tue, 25 Jun 2024 11:44:26 +0200 Subject: [PATCH 22/29] [NOISSUE] fixed viewpoint extension in FileWriter --- bcf-toolkit.sln.DotSettings.user | 28 ++++++------------- src/bcf-toolkit/Converter/Bcf30/FileWriter.cs | 2 +- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/bcf-toolkit.sln.DotSettings.user b/bcf-toolkit.sln.DotSettings.user index 0f3f760..e9f9703 100644 --- a/bcf-toolkit.sln.DotSettings.user +++ b/bcf-toolkit.sln.DotSettings.user @@ -1,12 +1,11 @@  /Users/balintbende/Library/Caches/JetBrains/Rider2024.1/resharper-host/temp/Rider/vAny/CoverageData/_bcf-toolkit.-1315391344/Snapshot/snapshot.utdcvr - <SessionState ContinuousTestingMode="0" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Solution /> </SessionState> - <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <Solution /> -</SessionState> + + @@ -22,23 +21,11 @@ - <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution #3" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <Solution /> -</SessionState> - <SessionState ContinuousTestingMode="0" Name="All tests from Solution #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <Solution /> -</SessionState> - <SessionState ContinuousTestingMode="0" Name="All tests from Solution #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <Solution /> -</SessionState> - <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <Solution /> -</SessionState> @@ -48,9 +35,12 @@ - <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <Solution /> -</SessionState> + + + + + + diff --git a/src/bcf-toolkit/Converter/Bcf30/FileWriter.cs b/src/bcf-toolkit/Converter/Bcf30/FileWriter.cs index 6d3364a..980234e 100644 --- a/src/bcf-toolkit/Converter/Bcf30/FileWriter.cs +++ b/src/bcf-toolkit/Converter/Bcf30/FileWriter.cs @@ -130,7 +130,7 @@ public static void SerializeAndWriteBcfToStream(IBcf bcf, ZipArchive zip, var visInfo = (VisualizationInfo)markup.GetFirstViewPoint()?.GetVisualizationInfo()!; - zip.SerializeAndCreateEntry($"{topicFolder}/viewpoint.bcf", visInfo); + zip.SerializeAndCreateEntry($"{topicFolder}/viewpoint.bcfv", visInfo); // Write snapshot var snapshotFileName = markup.GetFirstViewPoint()?.Snapshot; From 81ecdb0cdc4721afe73f6cbdea95ed686444202c Mon Sep 17 00:00:00 2001 From: Adam Eri Date: Wed, 29 May 2024 13:57:29 +0200 Subject: [PATCH 23/29] [BMSPT-283] Added delegate methods next to in memory bcf representation. --- .../Builder/Bcf21/BcfBuilderExtensions.cs | 3 +- .../Builder/Bcf30/BcfBuilderExtensions.cs | 8 +++- .../Bcf30/Interfaces/IBcfBuilderDelegate.cs | 14 +++---- src/bcf-toolkit/Converter/Bcf30/Converter.cs | 3 +- src/bcf-toolkit/Utils/BcfExtensions.cs | 36 +++-------------- src/tests/Builder/BcfBuilderTests.cs | 40 +++++++++++++++++-- src/tests/WorkerTests.cs | 1 - 7 files changed, 58 insertions(+), 47 deletions(-) diff --git a/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs b/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs index 07d0d9a..3c5e9f5 100644 --- a/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs +++ b/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs @@ -14,7 +14,7 @@ public async Task ProcessStream(Stream source) { return; } - await BcfExtensions.ParseMarkups(source, _delegate.MarkupCreated); + // await BcfExtensions.ParseMarkups(source, _delegate.MarkupCreated); // var extensions = await BcfExtensions.ParseExtensions(source); // _delegate.ExtensionsCreated(extensions); @@ -22,6 +22,7 @@ public async Task ProcessStream(Stream source) { // _bcf.Project = await BcfExtensions.ParseProject(source); // _bcf.Document = await BcfExtensions.ParseDocuments(source); } + public async Task BuildInMemoryFromStream(Stream source) { _bcf.Markups = await BcfExtensions.ParseMarkups(source); diff --git a/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs b/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs index 6c6b777..67ba91f 100644 --- a/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs +++ b/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs @@ -15,14 +15,18 @@ public async Task ProcessStream(Stream source) { return; } - await BcfExtensions.ParseMarkups(source, - _delegate.MarkupCreated); + // await BcfExtensions.ParseMarkups(source, + // _delegate.MarkupCreated); // var extensions = await BcfExtensions.ParseExtensions(source); // _delegate.ExtensionsCreated(extensions); // // _bcf.Project = await BcfExtensions.ParseProject(source); // _bcf.Document = await BcfExtensions.ParseDocuments(source); + // var extensions = await BcfExtensions.ParseExtensions(source); + + // _bcf.Project = await BcfExtensions.ParseProject(source); + // _bcf.Document = await BcfExtensions.ParseDocuments(source); } // diff --git a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilderDelegate.cs b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilderDelegate.cs index a7d9676..750e35c 100644 --- a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilderDelegate.cs +++ b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilderDelegate.cs @@ -7,20 +7,20 @@ public interface IBcfBuilderDelegate { public delegate void OnMarkupCreated(TMarkup markup) where TMarkup : IMarkup; - public delegate void - OnProjectCreated(ProjectInfo projectInfo) + public delegate void OnProjectCreated( + TProjectInfo projectInfo) where TProjectInfo : ProjectInfo; public delegate void OnExtensionsCreated( - Extensions extensions); + TExtensions extensions) + where TExtensions : Extensions; - public delegate void OnDocumentCreated( - Extensions documentInfo); + public delegate void OnDocumentCreated( + TDocumentInfo documentInfo) + where TDocumentInfo : DocumentInfo; public OnMarkupCreated MarkupCreated { get; } - public OnExtensionsCreated ExtensionsCreated { get; } - public OnProjectCreated ProjectCreated { get; } public OnDocumentCreated DocumentCreatedCreated { get; } } \ No newline at end of file diff --git a/src/bcf-toolkit/Converter/Bcf30/Converter.cs b/src/bcf-toolkit/Converter/Bcf30/Converter.cs index bf36397..a5ce1c3 100644 --- a/src/bcf-toolkit/Converter/Bcf30/Converter.cs +++ b/src/bcf-toolkit/Converter/Bcf30/Converter.cs @@ -57,8 +57,7 @@ private readonly Dictionary< }; public async Task BcfToJson(Stream source, string target) { - var builder = new BcfBuilder(); - var bcf = await builder.BuildInMemoryFromStream(source); + var bcf = await _builder.BuildInMemoryFromStream(source); await FileWriter.WriteJson(bcf, target); } diff --git a/src/bcf-toolkit/Utils/BcfExtensions.cs b/src/bcf-toolkit/Utils/BcfExtensions.cs index 28f49be..c42d6f4 100644 --- a/src/bcf-toolkit/Utils/BcfExtensions.cs +++ b/src/bcf-toolkit/Utils/BcfExtensions.cs @@ -8,9 +8,8 @@ using System.Threading.Tasks; using System.Xml.Linq; using System.Xml.Serialization; -using BcfToolkit.Builder.Bcf21.Interfaces; +using BcfToolkit.Builder.Bcf30.Interfaces; using BcfToolkit.Model; -using BcfToolkit.Model.Bcf21; namespace BcfToolkit.Utils; @@ -53,32 +52,8 @@ public static class BcfExtensions { return await _ParseMarkups(stream); } - /// - /// The method unzips the BCFzip from a stream, - /// and parses the markup xml files within to create an in memory - /// representation of the data. - /// Topic folder structure inside a BCFzip archive: - /// The folder name is the GUID of the topic. This GUID is in the UUID form. - /// The GUID must be all-lowercase. The folder contains the following file: - /// * markup.bcf - /// Additionally the folder can contain other files: - /// * Viewpoint files - /// * Snapshot files - /// * Bitmaps - /// Notification: This function adjusts the stream position back to 0 in - /// order to use it again. - /// - /// The source stream of the BCFzip. - /// - /// If the delegate function is set, the caller will receive every markup - /// as they are created, without building up an in-memory representation - /// of the entire BCF document. - /// - /// Returns a Task with a List of `Markup` models. - private static async Task> _ParseMarkups< - TMarkup, - TVisualizationInfo>( - Stream stream, + private static async Task> _ParseMarkups(Stream stream, IBcfBuilderDelegate.OnMarkupCreated? onMarkupCreated = null) where TMarkup : IMarkup where TVisualizationInfo : IVisualizationInfo { @@ -352,12 +327,11 @@ public static Task SerializeAndWriteXmlFile(string folder, string file, /// Returns the BcfVersionEnum enum. public static async Task GetVersionFromStreamArchive( Stream stream) { - - + if (!stream.CanRead || !stream.CanSeek) { throw new ArgumentException("Stream is not Readable or Seekable"); } - + using var archive = new ZipArchive(stream, ZipArchiveMode.Read, true); BcfVersionEnum? version = null; diff --git a/src/tests/Builder/BcfBuilderTests.cs b/src/tests/Builder/BcfBuilderTests.cs index 976888c..5b3a493 100644 --- a/src/tests/Builder/BcfBuilderTests.cs +++ b/src/tests/Builder/BcfBuilderTests.cs @@ -1,21 +1,41 @@ +using System; using System.IO; using System.Linq; using System.Threading.Tasks; using BcfToolkit.Builder.Bcf30; +using BcfToolkit.Builder.Bcf30.Interfaces; +using BcfToolkit.Model.Bcf30; using NUnit.Framework; namespace Tests.Builder; +public class BcfBuilderDelegate : IBcfBuilderDelegate { + public IBcfBuilderDelegate.OnMarkupCreated + MarkupCreated { get; } = Console.WriteLine; + + public IBcfBuilderDelegate.OnExtensionsCreated + ExtensionsCreated { get; } = Console.WriteLine; + + public IBcfBuilderDelegate.OnProjectCreated + ProjectCreated { get; } = Console.WriteLine; + + public IBcfBuilderDelegate.OnDocumentCreated + DocumentCreatedCreated { get; } = Console.WriteLine; +} + public class BcfBuilderTests { private BcfBuilder _inMemoryBuilder = null!; - + private BcfBuilder _steamBuilder = null!; + [SetUp] public void Setup() { _inMemoryBuilder = new BcfBuilder(); + var bcfBuilderDelegate = new BcfBuilderDelegate(); + _steamBuilder = new BcfBuilder(bcfBuilderDelegate); } [Test] - public void BuildBcfWithComplexFieldsTest() { + public void BuildBcfWithComplexFields() { var bcf = _inMemoryBuilder .AddMarkup(m => m .SetTitle("Title") @@ -37,13 +57,27 @@ public void BuildBcfWithComplexFieldsTest() { } [Test] - public void BuildBcfWithMissingRequiredFieldsTest() { + public void BuildBcfWithMissingRequiredFields() { Assert.That(() => _inMemoryBuilder.Build(), Throws.ArgumentException); } [Test] public async Task BuildInMemoryBcfFromStreamTest() { + await using var stream = new FileStream( + "Resources/Bcf/v3.0/UserAssignment.bcfzip", + FileMode.Open, + FileAccess.Read); + + await _steamBuilder.ProcessStream(stream); + // Assert.That(1, Is.EqualTo(bcf.Markups.Count)); + // Assert.That( + // "Architect@example.com", + // Is.EqualTo(bcf.Markups.FirstOrDefault()?.Topic.AssignedTo)); + } + + [Test] + public async Task BuildInMemoryBcfFromStream() { await using var stream = new FileStream( "Resources/Bcf/v3.0/UserAssignment.bcfzip", FileMode.Open, diff --git a/src/tests/WorkerTests.cs b/src/tests/WorkerTests.cs index 8190472..2c8ce85 100644 --- a/src/tests/WorkerTests.cs +++ b/src/tests/WorkerTests.cs @@ -157,7 +157,6 @@ public async Task BcfV30ToV21StreamSamplesTests() { var builder = new BcfToolkit.Builder.Bcf30.BcfBuilder(); await using var inputStream = new FileStream(path, FileMode.Open, FileAccess.Read); - var bcf = await builder.BuildInMemoryFromStream(inputStream); var stream = await _worker.ToBcf(bcf, BcfVersionEnum.Bcf21); var version = await BcfExtensions.GetVersionFromStreamArchive(stream); From 2da6f9f4216e4f485c95f2179d5d248a656d13a1 Mon Sep 17 00:00:00 2001 From: Balint Bende Date: Tue, 6 Aug 2024 14:56:09 +0200 Subject: [PATCH 24/29] [BMSPT-283] on markup created --- src/bcf-toolkit/Converter/Bcf21/Converter.cs | 9 ++++++-- .../Converter/Bcf21/SchemaConverterToBcf30.cs | 22 ++++++++++++++++++- src/bcf-toolkit/Program.cs | 7 ------ src/bcf-toolkit/Worker.cs | 4 ++-- src/tests/Converter/Bcf21/FileWriterTest.cs | 4 ++-- src/tests/Converter/Bcf30/FileWriterTests.cs | 4 ++-- 6 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/bcf-toolkit/Converter/Bcf21/Converter.cs b/src/bcf-toolkit/Converter/Bcf21/Converter.cs index 1cb3ea9..7910106 100644 --- a/src/bcf-toolkit/Converter/Bcf21/Converter.cs +++ b/src/bcf-toolkit/Converter/Bcf21/Converter.cs @@ -9,7 +9,6 @@ using BcfToolkit.Utils; using BcfToolkit.Model; using BcfToolkit.Model.Bcf21; -using Version = BcfToolkit.Model.Bcf21.Version; namespace BcfToolkit.Converter.Bcf21; @@ -19,7 +18,7 @@ namespace BcfToolkit.Converter.Bcf21; /// public class Converter : IConverter { private BcfBuilder _builder = new(); - + /// /// Defines the converter function, which must be used for converting the /// BCF object to the targeted version. @@ -151,4 +150,10 @@ public async Task BcfFromStream(Stream stream) { var converterFn = _converterFn[targetVersion]; return (T)converterFn(bcf); } + + public async Task ProcessStream(Stream stream) { + var targetVersion = BcfVersion.TryParse(typeof(T)); + + await _builder.ProcessStream(stream); + } } \ No newline at end of file diff --git a/src/bcf-toolkit/Converter/Bcf21/SchemaConverterToBcf30.cs b/src/bcf-toolkit/Converter/Bcf21/SchemaConverterToBcf30.cs index fde2374..050a882 100644 --- a/src/bcf-toolkit/Converter/Bcf21/SchemaConverterToBcf30.cs +++ b/src/bcf-toolkit/Converter/Bcf21/SchemaConverterToBcf30.cs @@ -1,10 +1,23 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Threading.Tasks; using BcfToolkit.Builder.Bcf30; namespace BcfToolkit.Converter.Bcf21; +public class BcfBuilderDelegate : BcfToolkit.Builder.Bcf21.Interfaces.IBcfBuilderDelegate { + public BcfToolkit.Builder.Bcf21.Interfaces.IBcfBuilderDelegate.OnMarkupCreated + MarkupCreated { get; } = markup => { + var converted = SchemaConverterToBcf30.ConvertMarkup(markup); + }; + + public BcfToolkit.Builder.Bcf21.Interfaces.IBcfBuilderDelegate.OnProjectCreated + ProjectCreated { get; } = Console.WriteLine; + +} + public static class SchemaConverterToBcf30 { /// @@ -32,8 +45,15 @@ public static Model.Bcf30.Bcf Convert(Model.Bcf21.Bcf from) { return builder.Build(); } + + public static async Task ProcessStream(Stream stream) { + var del = new BcfBuilderDelegate(); + var fromBuilder = new BcfToolkit.Builder.Bcf21.BcfBuilder(del); + await fromBuilder.ProcessStream(stream); + tobecontinued + } - private static Model.Bcf30.Markup ConvertMarkup(Model.Bcf21.Markup from) { + public static Model.Bcf30.Markup ConvertMarkup(Model.Bcf21.Markup from) { var builder = new MarkupBuilder(); var topic = from.Topic; builder diff --git a/src/bcf-toolkit/Program.cs b/src/bcf-toolkit/Program.cs index abe0b7c..00fabd7 100644 --- a/src/bcf-toolkit/Program.cs +++ b/src/bcf-toolkit/Program.cs @@ -8,14 +8,7 @@ namespace BcfToolkit; -public class BcfBuilderDelegate : IBcfBuilderDelegate { - public IBcfBuilderDelegate.OnMarkupCreated - MarkupCreated { get; } = Console.WriteLine; - public IBcfBuilderDelegate.OnProjectCreated - ProjectCreated { get; } = Console.WriteLine; - -} internal static class Program { private static async Task Main(string[] args) { diff --git a/src/bcf-toolkit/Worker.cs b/src/bcf-toolkit/Worker.cs index e453a98..b510653 100644 --- a/src/bcf-toolkit/Worker.cs +++ b/src/bcf-toolkit/Worker.cs @@ -135,9 +135,9 @@ public Task ToJson(IBcf bcf, string target) { /// /// The stream of the source BCF zip archive. /// Returns the in-memory BCF model. - public async Task BcfFromStream(Stream stream) { + public async Task BcfFromStream(Stream stream) { await InitConverterFromStreamArchive(stream); - await _converter.BcfFromStream(stream); + return await _converter.BcfFromStream(stream); } /// diff --git a/src/tests/Converter/Bcf21/FileWriterTest.cs b/src/tests/Converter/Bcf21/FileWriterTest.cs index 29d5742..69463f4 100644 --- a/src/tests/Converter/Bcf21/FileWriterTest.cs +++ b/src/tests/Converter/Bcf21/FileWriterTest.cs @@ -19,7 +19,7 @@ public async Task WriteBcfWithCreatingZipEntryFromStreamTest() { var bcfResultBuilder = new BcfBuilder(); var bcfResult = await bcfResultBuilder - .BuildFromStream(stream); + .BuildInMemoryFromStream(stream); Assert.That( bcf.Markups.FirstOrDefault()?.Topic.Title, @@ -37,7 +37,7 @@ public async Task WriteBcfWithSavingXmlTmpTest() { var bcfResultBuilder = new BcfBuilder(); var bcfResult = await bcfResultBuilder - .BuildFromStream(stream); + .BuildInMemoryFromStream(stream); Assert.That( bcf.Markups.FirstOrDefault()?.Topic.Title, diff --git a/src/tests/Converter/Bcf30/FileWriterTests.cs b/src/tests/Converter/Bcf30/FileWriterTests.cs index 490494b..77de516 100644 --- a/src/tests/Converter/Bcf30/FileWriterTests.cs +++ b/src/tests/Converter/Bcf30/FileWriterTests.cs @@ -19,7 +19,7 @@ public async Task WriteBcfWithCreatingZipEntryFromStreamTest() { var bcfResultBuilder = new BcfBuilder(); var bcfResult = await bcfResultBuilder - .BuildFromStream(stream); + .BuildInMemoryFromStream(stream); Assert.That( bcf.Markups.FirstOrDefault()?.Topic.Title, @@ -37,7 +37,7 @@ public async Task WriteBcfWithSavingXmlTmpTest() { var bcfResultBuilder = new BcfBuilder(); var bcfResult = await bcfResultBuilder - .BuildFromStream(stream); + .BuildInMemoryFromStream(stream); Assert.That( bcf.Markups.FirstOrDefault()?.Topic.Title, From f7c34889f6aa711087634455e951e074fcde7beb Mon Sep 17 00:00:00 2001 From: Balint Bende Date: Tue, 6 Aug 2024 14:57:01 +0200 Subject: [PATCH 25/29] [BMSPT-283] builder delegate --- src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs | 10 +------ .../Builder/Bcf21/BcfBuilderExtensions.cs | 10 +++++++ src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs | 1 + .../Builder/Interfaces/IBcfBuilderDelegate.cs | 17 ++++++++++++ src/bcf-toolkit/Program.cs | 26 ++++++++++++++----- 5 files changed, 48 insertions(+), 16 deletions(-) create mode 100644 src/bcf-toolkit/Builder/Interfaces/IBcfBuilderDelegate.cs diff --git a/src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs index 440b726..fc6f51d 100644 --- a/src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs @@ -13,15 +13,7 @@ public partial class BcfBuilder : IBcfBuilder< IDefaultBuilder { private readonly Bcf _bcf = new(); - private readonly IBcfBuilderDelegate? _delegate; - - public BcfBuilder(IBcfBuilderDelegate? builderDelegate = null) { - this._delegate = builderDelegate; - - _bcf.Version = new VersionBuilder() - .WithDefaults() - .Build(); - } + public BcfBuilder AddMarkup(Action builder) { var markup = diff --git a/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs b/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs index 3c5e9f5..4cfdbb2 100644 --- a/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs +++ b/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs @@ -4,10 +4,20 @@ using System.Threading.Tasks; using BcfToolkit.Model.Bcf21; using BcfToolkit.Utils; +using IBcfBuilderDelegate = BcfToolkit.Builder.Bcf21.Interfaces.IBcfBuilderDelegate; namespace BcfToolkit.Builder.Bcf21; public partial class BcfBuilder { + private readonly IBcfBuilderDelegate? _delegate; + + public BcfBuilder(IBcfBuilderDelegate? builderDelegate = null) { + this._delegate = builderDelegate; + + _bcf.Version = new VersionBuilder() + .WithDefaults() + .Build(); + } public async Task ProcessStream(Stream source) { if (_delegate is null) { Console.WriteLine("IBcfBuilderDelegate is not set."); diff --git a/src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs index 33a3677..bfffbb9 100644 --- a/src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs @@ -3,6 +3,7 @@ using BcfToolkit.Builder.Interfaces; using BcfToolkit.Model.Bcf30; using Bcf = BcfToolkit.Model.Bcf30.Bcf; +using IBcfBuilderDelegate = BcfToolkit.Builder.Bcf30.Interfaces.IBcfBuilderDelegate; using Markup = BcfToolkit.Model.Bcf30.Markup; namespace BcfToolkit.Builder.Bcf30; diff --git a/src/bcf-toolkit/Builder/Interfaces/IBcfBuilderDelegate.cs b/src/bcf-toolkit/Builder/Interfaces/IBcfBuilderDelegate.cs new file mode 100644 index 0000000..ac50391 --- /dev/null +++ b/src/bcf-toolkit/Builder/Interfaces/IBcfBuilderDelegate.cs @@ -0,0 +1,17 @@ +using BcfToolkit.Model; +using BcfToolkit.Model.Bcf21; + +namespace BcfToolkit.Builder.Interfaces; + +public interface IBcfBuilderDelegate { + public delegate void OnMarkupCreated(TMarkup markup) + where TMarkup : IMarkup; + + public delegate void + OnProjectCreated(ProjectExtension projectInfo) + where TProject : IProject; + + public OnMarkupCreated MarkupCreated { get; } + + public OnProjectCreated ProjectCreated { get; } +} \ No newline at end of file diff --git a/src/bcf-toolkit/Program.cs b/src/bcf-toolkit/Program.cs index 00fabd7..3e824ea 100644 --- a/src/bcf-toolkit/Program.cs +++ b/src/bcf-toolkit/Program.cs @@ -3,13 +3,25 @@ using System.CommandLine; using System.IO; using BcfToolkit.Builder.Bcf21; -using BcfToolkit.Builder.Bcf21.Interfaces; +using BcfToolkit.Builder.Interfaces; +using BcfToolkit.Model; using BcfToolkit.Model.Bcf21; namespace BcfToolkit; +public class BcfBuilderDelegate : IBcfBuilderDelegate { + public IBcfBuilderDelegate.OnMarkupCreated + MarkupCreated { get; } = m => { + var d = ((Markup)m).Topic.Description; + Console.WriteLine(d); + }; + public IBcfBuilderDelegate.OnProjectCreated + ProjectCreated { get; } = Console.WriteLine; + +} + internal static class Program { private static async Task Main(string[] args) { await using var stream = new FileStream( @@ -17,13 +29,13 @@ private static async Task Main(string[] args) { FileMode.Open, FileAccess.Read); - // var bcfBuilderDelegate = new BcfBuilderDelegate(); - // var streamBuilder = new BcfBuilder(bcfBuilderDelegate); - // await streamBuilder.ProcessStream(stream); + var bcfBuilderDelegate = new BcfBuilderDelegate(); + var streamBuilder = new BcfBuilder(bcfBuilderDelegate); + await streamBuilder.ProcessStream(stream); - var builder = new BcfBuilder(); - var bcf = await builder.BuildInMemoryFromStream(stream); - Console.WriteLine(bcf.Markups.Count); + // var builder = new BcfBuilder(); + // var bcf = await builder.BuildInMemoryFromStream(stream); + // Console.WriteLine(bcf.Markups.Count); stream.Close(); From 9b8583f7df1187bc5d3feaa6ade807120db75a3d Mon Sep 17 00:00:00 2001 From: Balint Bende Date: Mon, 12 Aug 2024 11:52:28 +0200 Subject: [PATCH 26/29] [BMSPT-283] builder delegate draft --- bcf-toolkit.sln.DotSettings.user | 5 ++++ src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs | 2 -- .../Builder/Bcf21/BcfBuilderExtensions.cs | 7 +++-- .../Bcf21/Interfaces/IBcfBuilderDelegate.cs | 17 ------------ src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs | 21 ++++++++------- .../Builder/Bcf30/BcfBuilderExtensions.cs | 13 ++++++++-- .../Bcf30/Interfaces/IBcfBuilderDelegate.cs | 26 ------------------- .../Builder/Interfaces/IBcfBuilderDelegate.cs | 24 +++++++++++------ src/bcf-toolkit/Converter/Bcf21/Converter.cs | 7 +++-- src/bcf-toolkit/Converter/Bcf30/Converter.cs | 12 +++++++-- src/bcf-toolkit/Program.cs | 7 +++++ src/bcf-toolkit/Utils/BcfExtensions.cs | 16 +++++++----- 12 files changed, 76 insertions(+), 81 deletions(-) delete mode 100644 src/bcf-toolkit/Builder/Bcf21/Interfaces/IBcfBuilderDelegate.cs delete mode 100644 src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilderDelegate.cs diff --git a/bcf-toolkit.sln.DotSettings.user b/bcf-toolkit.sln.DotSettings.user index 28cad9c..13a66ad 100644 --- a/bcf-toolkit.sln.DotSettings.user +++ b/bcf-toolkit.sln.DotSettings.user @@ -2,6 +2,11 @@ /Users/balintbende/Library/Caches/JetBrains/Rider2024.1/resharper-host/temp/Rider/vAny/CoverageData/_bcf-toolkit.-1315391344/Snapshot/snapshot.utdcvr <SessionState ContinuousTestingMode="0" Name="All tests from Solution #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Solution /> +</SessionState> + <SessionState ContinuousTestingMode="0" Name="BuildBcfFromV30StreamTest" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <TestAncestor> + <TestId>NUnit3x::8113526D-8A68-4E3E-B4DB-CE235875DDD1::net8.0::Tests.WorkerTests.BuildBcfFromV30StreamTest</TestId> + </TestAncestor> </SessionState> <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution #3" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Solution /> diff --git a/src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs index 67d055d..c7c4c62 100644 --- a/src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs @@ -12,9 +12,7 @@ public partial class BcfBuilder : IBcfBuilder< ProjectExtensionBuilder>, IDefaultBuilder { private readonly Bcf _bcf = new(); - - public BcfBuilder AddMarkup(Action builder) { var markup = (Markup)BuilderUtils.BuildItem(builder); diff --git a/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs b/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs index cd7eed4..d82c1d4 100644 --- a/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs +++ b/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.IO; using System.IO.Compression; using System.Linq; @@ -8,14 +7,14 @@ using BcfToolkit.Model; using BcfToolkit.Model.Bcf21; using BcfToolkit.Utils; -using IBcfBuilderDelegate = BcfToolkit.Builder.Bcf21.Interfaces.IBcfBuilderDelegate; +using Interfaces_IBcfBuilderDelegate = BcfToolkit.Builder.Interfaces.IBcfBuilderDelegate; namespace BcfToolkit.Builder.Bcf21; public partial class BcfBuilder { - private readonly IBcfBuilderDelegate? _delegate; + private readonly Interfaces_IBcfBuilderDelegate? _delegate; - public BcfBuilder(IBcfBuilderDelegate? builderDelegate = null) { + public BcfBuilder(Interfaces_IBcfBuilderDelegate builderDelegate = null) { this._delegate = builderDelegate; _bcf.Version = new VersionBuilder() diff --git a/src/bcf-toolkit/Builder/Bcf21/Interfaces/IBcfBuilderDelegate.cs b/src/bcf-toolkit/Builder/Bcf21/Interfaces/IBcfBuilderDelegate.cs deleted file mode 100644 index e6aa568..0000000 --- a/src/bcf-toolkit/Builder/Bcf21/Interfaces/IBcfBuilderDelegate.cs +++ /dev/null @@ -1,17 +0,0 @@ -using BcfToolkit.Model; -using BcfToolkit.Model.Bcf21; - -namespace BcfToolkit.Builder.Bcf21.Interfaces; - -public interface IBcfBuilderDelegate { - public delegate void OnMarkupCreated(TMarkup markup) - where TMarkup : IMarkup; - - public delegate void - OnProjectCreated(ProjectExtension projectInfo) - where TProjectExtension : ProjectExtension; - - public OnMarkupCreated MarkupCreated { get; } - - public OnProjectCreated ProjectCreated { get; } -} \ No newline at end of file diff --git a/src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs index bfffbb9..36a04d2 100644 --- a/src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs @@ -3,7 +3,6 @@ using BcfToolkit.Builder.Interfaces; using BcfToolkit.Model.Bcf30; using Bcf = BcfToolkit.Model.Bcf30.Bcf; -using IBcfBuilderDelegate = BcfToolkit.Builder.Bcf30.Interfaces.IBcfBuilderDelegate; using Markup = BcfToolkit.Model.Bcf30.Markup; namespace BcfToolkit.Builder.Bcf30; @@ -15,16 +14,11 @@ public partial class BcfBuilder : IBcfBuilder< ExtensionsBuilder, DocumentInfoBuilder>, IDefaultBuilder { + private readonly Bcf _bcf = new(); - - private readonly IBcfBuilderDelegate? _delegate; - - public BcfBuilder(IBcfBuilderDelegate? builderDelegate = null) { - this._delegate = builderDelegate; - - _bcf.Version = new VersionBuilder() - .WithDefaults() - .Build(); + + public BcfBuilder() { + SetVersion(); } public BcfBuilder AddMarkup(Action builder) { @@ -55,6 +49,13 @@ public BcfBuilder SetDocument(Action builder) { return this; } + public BcfBuilder SetVersion() { + _bcf.Version = new VersionBuilder() + .WithDefaults() + .Build(); + return this; + } + public BcfBuilder WithDefaults() { this .AddMarkup(m => m.WithDefaults()) diff --git a/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs b/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs index 67ba91f..b0b63cb 100644 --- a/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs +++ b/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs @@ -3,20 +3,29 @@ using System.IO; using System.Linq; using System.Threading.Tasks; +using BcfToolkit.Builder.Interfaces; using BcfToolkit.Model.Bcf30; using BcfToolkit.Utils; namespace BcfToolkit.Builder.Bcf30; public partial class BcfBuilder { + + private IBcfBuilderDelegate? _delegate; + + public void SetDelegate(IBcfBuilderDelegate? builderDelegate) { + this._delegate = builderDelegate; + } + public async Task ProcessStream(Stream source) { if (_delegate is null) { Console.WriteLine("IBcfBuilderDelegate is not set."); return; } - // await BcfExtensions.ParseMarkups(source, - // _delegate.MarkupCreated); + await BcfExtensions.ParseMarkups( + source, + _delegate.MarkupCreated); // var extensions = await BcfExtensions.ParseExtensions(source); // _delegate.ExtensionsCreated(extensions); diff --git a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilderDelegate.cs b/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilderDelegate.cs deleted file mode 100644 index 750e35c..0000000 --- a/src/bcf-toolkit/Builder/Bcf30/Interfaces/IBcfBuilderDelegate.cs +++ /dev/null @@ -1,26 +0,0 @@ -using BcfToolkit.Model; -using BcfToolkit.Model.Bcf30; - -namespace BcfToolkit.Builder.Bcf30.Interfaces; - -public interface IBcfBuilderDelegate { - public delegate void OnMarkupCreated(TMarkup markup) - where TMarkup : IMarkup; - - public delegate void OnProjectCreated( - TProjectInfo projectInfo) - where TProjectInfo : ProjectInfo; - - public delegate void OnExtensionsCreated( - TExtensions extensions) - where TExtensions : Extensions; - - public delegate void OnDocumentCreated( - TDocumentInfo documentInfo) - where TDocumentInfo : DocumentInfo; - - public OnMarkupCreated MarkupCreated { get; } - public OnExtensionsCreated ExtensionsCreated { get; } - public OnProjectCreated ProjectCreated { get; } - public OnDocumentCreated DocumentCreatedCreated { get; } -} \ No newline at end of file diff --git a/src/bcf-toolkit/Builder/Interfaces/IBcfBuilderDelegate.cs b/src/bcf-toolkit/Builder/Interfaces/IBcfBuilderDelegate.cs index ac50391..5c6d188 100644 --- a/src/bcf-toolkit/Builder/Interfaces/IBcfBuilderDelegate.cs +++ b/src/bcf-toolkit/Builder/Interfaces/IBcfBuilderDelegate.cs @@ -1,17 +1,25 @@ -using BcfToolkit.Model; -using BcfToolkit.Model.Bcf21; +using BcfToolkit.Model.Interfaces; namespace BcfToolkit.Builder.Interfaces; public interface IBcfBuilderDelegate { public delegate void OnMarkupCreated(TMarkup markup) where TMarkup : IMarkup; - - public delegate void - OnProjectCreated(ProjectExtension projectInfo) - where TProject : IProject; - + + public delegate void OnProjectCreated( + TProjectInfo projectInfo) + where TProjectInfo : IProject; + + public delegate void OnExtensionsCreated( + TExtensions extensions) + where TExtensions : IExtensions; + + public delegate void OnDocumentCreated( + TDocumentInfo documentInfo) + where TDocumentInfo : IDocumentInfo; + public OnMarkupCreated MarkupCreated { get; } - + public OnExtensionsCreated ExtensionsCreated { get; } public OnProjectCreated ProjectCreated { get; } + public OnDocumentCreated DocumentCreatedCreated { get; } } \ No newline at end of file diff --git a/src/bcf-toolkit/Converter/Bcf21/Converter.cs b/src/bcf-toolkit/Converter/Bcf21/Converter.cs index ecfb8c4..33e3a84 100644 --- a/src/bcf-toolkit/Converter/Bcf21/Converter.cs +++ b/src/bcf-toolkit/Converter/Bcf21/Converter.cs @@ -147,15 +147,14 @@ public Task ToJson(IBcf bcf, string target) { public async Task BcfFromStream(Stream stream) { var bcf = await _builder.BuildInMemoryFromStream(stream); - var targetVersion = BcfVersion.TryParse(typeof(T)); var converterFn = _converterFn[targetVersion]; return (T)converterFn(bcf); } - public async Task ProcessStream(Stream stream) { - var targetVersion = BcfVersion.TryParse(typeof(T)); - + public async Task ProcessStream(Stream stream) { + // var targetVersion = BcfVersion.TryParse(typeof(T)); + _builder.Set await _builder.ProcessStream(stream); } } \ No newline at end of file diff --git a/src/bcf-toolkit/Converter/Bcf30/Converter.cs b/src/bcf-toolkit/Converter/Bcf30/Converter.cs index fa2e0e7..a67456c 100644 --- a/src/bcf-toolkit/Converter/Bcf30/Converter.cs +++ b/src/bcf-toolkit/Converter/Bcf30/Converter.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using BcfToolkit.Builder.Bcf30; +using BcfToolkit.Builder.Interfaces; using BcfToolkit.Utils; using BcfToolkit.Model; using BcfToolkit.Model.Bcf30; @@ -58,7 +59,7 @@ private readonly Dictionary< public async Task BcfToJson(Stream source, string target) { var builder = new BcfBuilder(); - var bcf = await builder.BuildFromStream(source); + var bcf = await builder.BuildInMemoryFromStream(source); await FileWriter.WriteJson(bcf, target); } @@ -140,9 +141,16 @@ public Task ToJson(IBcf bcf, string target) { } public async Task BcfFromStream(Stream stream) { - var bcf = await _builder.BuildFromStream(stream); + var bcf = await _builder.BuildInMemoryFromStream(stream); var targetVersion = BcfVersion.TryParse(typeof(T)); var converterFn = _converterFn[targetVersion]; return (T)converterFn(bcf); } + + public async Task ProcessStream( + Stream stream, + IBcfBuilderDelegate builderDelegate) { + _builder.SetDelegate(builderDelegate); + await _builder.ProcessStream(stream); + } } \ No newline at end of file diff --git a/src/bcf-toolkit/Program.cs b/src/bcf-toolkit/Program.cs index cc128ec..c78506c 100644 --- a/src/bcf-toolkit/Program.cs +++ b/src/bcf-toolkit/Program.cs @@ -18,10 +18,17 @@ public IBcfBuilderDelegate.OnMarkupCreated Console.WriteLine(d); }; + public IBcfBuilderDelegate.OnExtensionsCreated ExtensionsCreated { + get; + } + public IBcfBuilderDelegate.OnProjectCreated ProjectCreated { get; } = Console.WriteLine; + public IBcfBuilderDelegate.OnDocumentCreated DocumentCreatedCreated { + get; + } } internal static class Program { diff --git a/src/bcf-toolkit/Utils/BcfExtensions.cs b/src/bcf-toolkit/Utils/BcfExtensions.cs index b9555f1..b22f09e 100644 --- a/src/bcf-toolkit/Utils/BcfExtensions.cs +++ b/src/bcf-toolkit/Utils/BcfExtensions.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; using System.Xml.Linq; using System.Xml.Serialization; -using BcfToolkit.Builder.Bcf30.Interfaces; +using BcfToolkit.Builder.Interfaces; using BcfToolkit.Model; using BcfToolkit.Model.Interfaces; @@ -55,8 +55,10 @@ public static async Task> ParseMarkups< return await _ParseMarkups(stream); } - private static async Task> _ParseMarkups(Stream stream, + private static async Task> _ParseMarkups< + TMarkup, + TVisualizationInfo>( + Stream stream, IBcfBuilderDelegate.OnMarkupCreated? onMarkupCreated = null) where TMarkup : IMarkup where TVisualizationInfo : IVisualizationInfo { @@ -170,9 +172,11 @@ private static void WritingOutMarkup( if (markup != null) { markup.SetViewPoints(visInfos, snapshots); - onMarkupCreated?.Invoke(markup); - - markups.Add(markup); + // If a delegate is provided, invoke it without adding markup to the BCF + if(onMarkupCreated is not null) + onMarkupCreated.Invoke(markup); + else + markups.Add(markup); // Null-ing external references markup = default; From 6e81181a2516e645c4b56daf6f334d04b3105a73 Mon Sep 17 00:00:00 2001 From: Balint Bende Date: Thu, 5 Sep 2024 16:19:04 +0200 Subject: [PATCH 27/29] [BMSPT-283] fixed BCF21 version, added proces stream fns to bcf builder and converter, fixed and extended tests, extended readme --- README.md | 36 +++++ bcf-toolkit.sln.DotSettings.user | 45 +++--- src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs | 13 +- .../Builder/Bcf21/BcfBuilderExtensions.cs | 28 ++-- .../Builder/Bcf21/VisibilityBuilder.cs | 1 - src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs | 4 +- .../Builder/Bcf30/BcfBuilderExtensions.cs | 40 ++--- .../Builder/Interfaces/IBcfBuilderDelegate.cs | 8 +- src/bcf-toolkit/Converter/Bcf21/Converter.cs | 18 ++- src/bcf-toolkit/Converter/Bcf21/FileWriter.cs | 6 +- .../Converter/Bcf21/SchemaConverterToBcf30.cs | 2 +- src/bcf-toolkit/Converter/Bcf30/Converter.cs | 10 +- src/bcf-toolkit/Converter/Bcf30/FileWriter.cs | 4 +- .../Converter/Bcf30/SchemaConverterToBcf21.cs | 6 +- src/bcf-toolkit/Converter/IConverter.cs | 14 ++ .../Model/Bcf30/MarkupExtensions.cs | 2 +- src/bcf-toolkit/Program.cs | 69 +-------- src/bcf-toolkit/Utils/BcfExtensions.cs | 144 ++++++++++++++++-- ...derTests.cs => BcfBuilderInMemoryTests.cs} | 6 +- .../Builder/Bcf21/BcfBuilderStreamTest.cs | 48 ++++++ ...derTests.cs => BcfBuilderInMemoryTests.cs} | 47 +----- .../Builder/Bcf30/BcfBuilderStreamTest.cs | 48 ++++++ src/tests/Builder/BcfBuilderStreamTests.cs | 45 ------ src/tests/Converter/Bcf21/ConverterTests.cs | 6 +- src/tests/Converter/Bcf30/FileWriterTests.cs | 4 +- .../Json/v2.1/AllPartsVisible/imagebase64.txt | 1 + src/tests/Utils/BcfExtensionsTests.cs | 2 +- src/tests/Utils/JsonExtensionsTests.cs | 2 +- .../Utils/ZipArchiveEntryExtensionsTests.cs | 2 +- src/tests/WorkerTests.cs | 2 +- 30 files changed, 391 insertions(+), 272 deletions(-) rename src/tests/Builder/Bcf21/{BcfBuilderTests.cs => BcfBuilderInMemoryTests.cs} (97%) create mode 100644 src/tests/Builder/Bcf21/BcfBuilderStreamTest.cs rename src/tests/Builder/Bcf30/{BcfBuilderTests.cs => BcfBuilderInMemoryTests.cs} (58%) create mode 100644 src/tests/Builder/Bcf30/BcfBuilderStreamTest.cs delete mode 100644 src/tests/Builder/BcfBuilderStreamTests.cs create mode 100644 src/tests/Resources/Json/v2.1/AllPartsVisible/imagebase64.txt diff --git a/README.md b/README.md index 7a39069..526bfe2 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,42 @@ worker.ToBcf(bcf, BcfVersionEnum.Bcf30, outputStream, token); await outputStream.FlushAsync(); ``` +#### Stream processing with delegate callbacks +The converters support processing BCF file streams with delegate-based callback +functions. This allows for the incremental construction of an in-memory BCF +representation, where a delegate function is invoked after each part of the BCF +is processed. It allows a more equal memory usage. + +To use this feature, the user must create a custom delegate class which +implements the `IBcfBuilderDelegate` interface. This interface defines the +callback methods that will be triggered after each part of the BCF is built. + +Implement custom delegate class: +```csharp +public class BcfBuilderDelegate : IBcfBuilderDelegate { + public IBcfBuilderDelegate.OnMarkupCreated + MarkupCreated { get; } = Console.WriteLine; + + public IBcfBuilderDelegate.OnExtensionsCreated + ExtensionsCreated { get; } = Console.WriteLine; + + public IBcfBuilderDelegate.OnProjectCreated + ProjectCreated { get; } = Console.WriteLine; + + public IBcfBuilderDelegate.OnDocumentCreated + DocumentCreatedCreated { get; } = Console.WriteLine; +} +``` + +Process BCF stream: +```csharp +var bcfBuilderDelegate = new BcfBuilderDelegate(); +builder = new BcfBuilder(); +builder.SetDelegate(bcfBuilderDelegate); +await using var stream = new FileStream(source, FileMode.Open, FileAccess.Read); +await builder.ProcessStream(stream); +``` + ## File Structure The structure of the BCF is per [the standard][3]. There is, however, no diff --git a/bcf-toolkit.sln.DotSettings.user b/bcf-toolkit.sln.DotSettings.user index 13a66ad..faa1d46 100644 --- a/bcf-toolkit.sln.DotSettings.user +++ b/bcf-toolkit.sln.DotSettings.user @@ -1,30 +1,12 @@  /Users/balintbende/Library/Caches/JetBrains/Rider2024.1/resharper-host/temp/Rider/vAny/CoverageData/_bcf-toolkit.-1315391344/Snapshot/snapshot.utdcvr - <SessionState ContinuousTestingMode="0" Name="All tests from Solution #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <Solution /> -</SessionState> - <SessionState ContinuousTestingMode="0" Name="BuildBcfFromV30StreamTest" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <TestAncestor> - <TestId>NUnit3x::8113526D-8A68-4E3E-B4DB-CE235875DDD1::net8.0::Tests.WorkerTests.BuildBcfFromV30StreamTest</TestId> - </TestAncestor> -</SessionState> - <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution #3" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Solution /> </SessionState> - <SessionState ContinuousTestingMode="0" Name="BuildMaximumInformationBcfFromStreamTest" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <TestAncestor> - <TestId>NUnit3x::8113526D-8A68-4E3E-B4DB-CE235875DDD1::net8.0::tests.Builder.Bcf21.BcfBuilderTests.BuildMaximumInformationBcfFromStreamTest</TestId> - </TestAncestor> -</SessionState> - <SessionState ContinuousTestingMode="0" Name="BuildEmptyBcfFromStream" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <TestAncestor> - <TestId>NUnit3x::8113526D-8A68-4E3E-B4DB-CE235875DDD1::net8.0::tests.Builder.Bcf21.BcfBuilderTests.BuildEmptyBcfFromStream</TestId> - </TestAncestor> -</SessionState> @@ -40,9 +22,6 @@ - <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <Solution /> -</SessionState> @@ -50,11 +29,23 @@ - <SessionState ContinuousTestingMode="0" IsActive="True" Name="WorkerTests" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <TestAncestor> - <TestId>NUnit3x::8113526D-8A68-4E3E-B4DB-CE235875DDD1::net8.0::Tests.WorkerTests</TestId> - </TestAncestor> -</SessionState> + + + + + + + + + + + + + + + + + diff --git a/src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs index c7c4c62..3a22240 100644 --- a/src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/BcfBuilder.cs @@ -12,7 +12,11 @@ public partial class BcfBuilder : IBcfBuilder< ProjectExtensionBuilder>, IDefaultBuilder { private readonly Bcf _bcf = new(); - + + public BcfBuilder() { + SetVersion(); + } + public BcfBuilder AddMarkup(Action builder) { var markup = (Markup)BuilderUtils.BuildItem(builder); @@ -28,6 +32,13 @@ public BcfBuilder SetProject(Action builder) { return this; } + public BcfBuilder SetVersion() { + _bcf.Version = new VersionBuilder() + .WithDefaults() + .Build(); + return this; + } + public BcfBuilder WithDefaults() { this.AddMarkup(m => m.WithDefaults()); return this; diff --git a/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs b/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs index d82c1d4..11748c7 100644 --- a/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs +++ b/src/bcf-toolkit/Builder/Bcf21/BcfBuilderExtensions.cs @@ -4,36 +4,32 @@ using System.IO.Compression; using System.Linq; using System.Threading.Tasks; +using BcfToolkit.Builder.Interfaces; using BcfToolkit.Model; using BcfToolkit.Model.Bcf21; using BcfToolkit.Utils; -using Interfaces_IBcfBuilderDelegate = BcfToolkit.Builder.Interfaces.IBcfBuilderDelegate; namespace BcfToolkit.Builder.Bcf21; public partial class BcfBuilder { - private readonly Interfaces_IBcfBuilderDelegate? _delegate; - - public BcfBuilder(Interfaces_IBcfBuilderDelegate builderDelegate = null) { + private IBcfBuilderDelegate? _delegate; + + public void SetDelegate(IBcfBuilderDelegate? builderDelegate) { this._delegate = builderDelegate; - - _bcf.Version = new VersionBuilder() - .WithDefaults() - .Build(); } - public async Task ProcessStream(Stream source) { + + public Task ProcessStream(Stream source) { if (_delegate is null) { Console.WriteLine("IBcfBuilderDelegate is not set."); - return; + return Task.CompletedTask; } - // await BcfExtensions.ParseMarkups(source, _delegate.MarkupCreated); + var tasks = new List { + BcfExtensions.ParseMarkups(source, _delegate.MarkupCreated), + BcfExtensions.ParseProject(source, _delegate.ProjectCreated) + }; - // var extensions = await BcfExtensions.ParseExtensions(source); - // _delegate.ExtensionsCreated(extensions); - // - // _bcf.Project = await BcfExtensions.ParseProject(source); - // _bcf.Document = await BcfExtensions.ParseDocuments(source); + return Task.WhenAll(tasks); } public async Task BuildInMemoryFromStream(Stream source) { diff --git a/src/bcf-toolkit/Builder/Bcf21/VisibilityBuilder.cs b/src/bcf-toolkit/Builder/Bcf21/VisibilityBuilder.cs index 5eeecb8..8e5c3ef 100644 --- a/src/bcf-toolkit/Builder/Bcf21/VisibilityBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf21/VisibilityBuilder.cs @@ -1,6 +1,5 @@ using System; using BcfToolkit.Builder.Bcf21.Interfaces; -using BcfToolkit.Model; using BcfToolkit.Model.Bcf21; using BcfToolkit.Model.Interfaces; diff --git a/src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs b/src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs index 36a04d2..18df091 100644 --- a/src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs +++ b/src/bcf-toolkit/Builder/Bcf30/BcfBuilder.cs @@ -14,9 +14,9 @@ public partial class BcfBuilder : IBcfBuilder< ExtensionsBuilder, DocumentInfoBuilder>, IDefaultBuilder { - + private readonly Bcf _bcf = new(); - + public BcfBuilder() { SetVersion(); } diff --git a/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs b/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs index b0b63cb..85bb31a 100644 --- a/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs +++ b/src/bcf-toolkit/Builder/Bcf30/BcfBuilderExtensions.cs @@ -10,32 +10,35 @@ namespace BcfToolkit.Builder.Bcf30; public partial class BcfBuilder { - + private IBcfBuilderDelegate? _delegate; - + public void SetDelegate(IBcfBuilderDelegate? builderDelegate) { this._delegate = builderDelegate; } - - public async Task ProcessStream(Stream source) { + + public Task ProcessStream(Stream source) { if (_delegate is null) { Console.WriteLine("IBcfBuilderDelegate is not set."); - return; + return Task.CompletedTask; } - await BcfExtensions.ParseMarkups( - source, - _delegate.MarkupCreated); - - // var extensions = await BcfExtensions.ParseExtensions(source); - // _delegate.ExtensionsCreated(extensions); - // - // _bcf.Project = await BcfExtensions.ParseProject(source); - // _bcf.Document = await BcfExtensions.ParseDocuments(source); - // var extensions = await BcfExtensions.ParseExtensions(source); + var tasks = new List { + BcfExtensions.ParseMarkups( + source, + _delegate.MarkupCreated), + BcfExtensions.ParseExtensions( + source, + _delegate.ExtensionsCreated), + BcfExtensions.ParseProject( + source, + _delegate.ProjectCreated), + BcfExtensions.ParseDocuments( + source, + _delegate.DocumentCreatedCreated) + }; - // _bcf.Project = await BcfExtensions.ParseProject(source); - // _bcf.Document = await BcfExtensions.ParseDocuments(source); + return Task.WhenAll(tasks); } // @@ -46,8 +49,7 @@ await BcfExtensions.ParseMarkups( /// The file stream. /// Returns the built object. public async Task BuildInMemoryFromStream(Stream source) { - _bcf.Markups = - await BcfExtensions.ParseMarkups(source); + _bcf.Markups = await BcfExtensions.ParseMarkups(source); _bcf.Extensions = await BcfExtensions.ParseExtensions(source); _bcf.Project = await BcfExtensions.ParseProject(source); _bcf.Document = await BcfExtensions.ParseDocuments(source); diff --git a/src/bcf-toolkit/Builder/Interfaces/IBcfBuilderDelegate.cs b/src/bcf-toolkit/Builder/Interfaces/IBcfBuilderDelegate.cs index 5c6d188..a86ea1a 100644 --- a/src/bcf-toolkit/Builder/Interfaces/IBcfBuilderDelegate.cs +++ b/src/bcf-toolkit/Builder/Interfaces/IBcfBuilderDelegate.cs @@ -5,19 +5,19 @@ namespace BcfToolkit.Builder.Interfaces; public interface IBcfBuilderDelegate { public delegate void OnMarkupCreated(TMarkup markup) where TMarkup : IMarkup; - + public delegate void OnProjectCreated( TProjectInfo projectInfo) where TProjectInfo : IProject; - + public delegate void OnExtensionsCreated( TExtensions extensions) where TExtensions : IExtensions; - + public delegate void OnDocumentCreated( TDocumentInfo documentInfo) where TDocumentInfo : IDocumentInfo; - + public OnMarkupCreated MarkupCreated { get; } public OnExtensionsCreated ExtensionsCreated { get; } public OnProjectCreated ProjectCreated { get; } diff --git a/src/bcf-toolkit/Converter/Bcf21/Converter.cs b/src/bcf-toolkit/Converter/Bcf21/Converter.cs index 33e3a84..a9066a8 100644 --- a/src/bcf-toolkit/Converter/Bcf21/Converter.cs +++ b/src/bcf-toolkit/Converter/Bcf21/Converter.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using BcfToolkit.Builder.Bcf21; +using BcfToolkit.Builder.Interfaces; using BcfToolkit.Utils; using BcfToolkit.Model; using BcfToolkit.Model.Bcf21; @@ -19,7 +20,7 @@ namespace BcfToolkit.Converter.Bcf21; /// public class Converter : IConverter { private BcfBuilder _builder = new(); - + /// /// Defines the converter function, which must be used for converting the /// BCF object to the targeted version. @@ -57,7 +58,7 @@ private readonly Dictionary< }; public async Task BcfToJson(Stream source, string targetPath) { - var bcf = await _builder.BuildInMemoryFromStream(source); + var bcf = await _builder.BuildInMemoryFromStream(source); await FileWriter.WriteBcfToJson(bcf, targetPath); } @@ -144,17 +145,18 @@ public Task ToBcf(IBcf bcf, string target) { public Task ToJson(IBcf bcf, string target) { return FileWriter.WriteBcfToJson((Bcf)bcf, target); } - + public async Task BcfFromStream(Stream stream) { var bcf = await _builder.BuildInMemoryFromStream(stream); var targetVersion = BcfVersion.TryParse(typeof(T)); var converterFn = _converterFn[targetVersion]; return (T)converterFn(bcf); } - - public async Task ProcessStream(Stream stream) { - // var targetVersion = BcfVersion.TryParse(typeof(T)); - _builder.Set - await _builder.ProcessStream(stream); + + public Task ProcessStream( + Stream stream, + IBcfBuilderDelegate builderDelegate) { + _builder.SetDelegate(builderDelegate); + return _builder.ProcessStream(stream); } } \ No newline at end of file diff --git a/src/bcf-toolkit/Converter/Bcf21/FileWriter.cs b/src/bcf-toolkit/Converter/Bcf21/FileWriter.cs index c78090a..07e55b6 100644 --- a/src/bcf-toolkit/Converter/Bcf21/FileWriter.cs +++ b/src/bcf-toolkit/Converter/Bcf21/FileWriter.cs @@ -107,7 +107,7 @@ public static void SerializeAndWriteBcfToStream( } var topicFolder = $"{guid}"; - + zip.CreateEntryFromObject($"{topicFolder}/markup.bcf", markup); foreach (var viewpoint in markup.Viewpoints) { @@ -194,13 +194,13 @@ public static async Task SerializeAndWriteBcfToFolder( topicFolder, "markup.bcf", markup)); - + foreach (var viewpoint in markup.Viewpoints) { writeTasks.Add(BcfExtensions.SerializeAndWriteXmlFile( topicFolder, viewpoint.Viewpoint, viewpoint.VisualizationInfo)); - + var snapshotFileName = viewpoint.Snapshot; var snapshotBase64String = viewpoint.SnapshotData?.Data; if (string.IsNullOrEmpty(snapshotFileName) || snapshotBase64String == null) diff --git a/src/bcf-toolkit/Converter/Bcf21/SchemaConverterToBcf30.cs b/src/bcf-toolkit/Converter/Bcf21/SchemaConverterToBcf30.cs index b72c384..ed804b1 100644 --- a/src/bcf-toolkit/Converter/Bcf21/SchemaConverterToBcf30.cs +++ b/src/bcf-toolkit/Converter/Bcf21/SchemaConverterToBcf30.cs @@ -54,7 +54,7 @@ public static Model.Bcf30.Bcf Convert(Model.Bcf21.Bcf from) { return builder.Build(); } - + // public static async Task ProcessStream(Stream stream) { // var del = new BcfBuilderDelegate(); // var fromBuilder = new BcfToolkit.Builder.Bcf21.BcfBuilder(del); diff --git a/src/bcf-toolkit/Converter/Bcf30/Converter.cs b/src/bcf-toolkit/Converter/Bcf30/Converter.cs index a67456c..f16914e 100644 --- a/src/bcf-toolkit/Converter/Bcf30/Converter.cs +++ b/src/bcf-toolkit/Converter/Bcf30/Converter.cs @@ -139,18 +139,18 @@ public Task ToBcf(IBcf bcf, string target) { public Task ToJson(IBcf bcf, string target) { return FileWriter.WriteJson((Bcf)bcf, target); } - + public async Task BcfFromStream(Stream stream) { var bcf = await _builder.BuildInMemoryFromStream(stream); var targetVersion = BcfVersion.TryParse(typeof(T)); var converterFn = _converterFn[targetVersion]; return (T)converterFn(bcf); } - - public async Task ProcessStream( - Stream stream, + + public Task ProcessStream( + Stream stream, IBcfBuilderDelegate builderDelegate) { _builder.SetDelegate(builderDelegate); - await _builder.ProcessStream(stream); + return _builder.ProcessStream(stream); } } \ No newline at end of file diff --git a/src/bcf-toolkit/Converter/Bcf30/FileWriter.cs b/src/bcf-toolkit/Converter/Bcf30/FileWriter.cs index 2fd04f2..022fddd 100644 --- a/src/bcf-toolkit/Converter/Bcf30/FileWriter.cs +++ b/src/bcf-toolkit/Converter/Bcf30/FileWriter.cs @@ -110,7 +110,7 @@ public static async Task SerializeAndWriteBcf(IBcf bcf, public static void SerializeAndWriteBcfToStream(IBcf bcf, ZipArchive zip, CancellationToken? cancellationToken = null) { var bcfObject = (Bcf)bcf; - + zip.CreateEntryFromObject("bcf.version", new Version()); // Writing markup files to zip arhive, one markup per entry. @@ -206,7 +206,7 @@ public static async Task SerializeAndWriteBcfToFolder( writeTasks.Add( BcfExtensions.SerializeAndWriteXmlFile(topicFolder, "markup.bcf", markup)); - + foreach (var viewpoint in markup.Topic.Viewpoints) { writeTasks.Add(BcfExtensions.SerializeAndWriteXmlFile( topicFolder, diff --git a/src/bcf-toolkit/Converter/Bcf30/SchemaConverterToBcf21.cs b/src/bcf-toolkit/Converter/Bcf30/SchemaConverterToBcf21.cs index 29aa44c..6c93cb2 100644 --- a/src/bcf-toolkit/Converter/Bcf30/SchemaConverterToBcf21.cs +++ b/src/bcf-toolkit/Converter/Bcf30/SchemaConverterToBcf21.cs @@ -206,15 +206,15 @@ private static Model.Bcf21.ViewPoint ConvertViewPoint( return builder .SetCameraDirection( - from.CameraDirection.X, + from.CameraDirection.X, from.CameraDirection.Y, from.CameraDirection.Z) .SetCameraViewPoint( - from.CameraViewPoint.X, + from.CameraViewPoint.X, from.CameraViewPoint.Y, from.CameraViewPoint.Z) .SetCameraUpVector( - from.CameraUpVector.X, + from.CameraUpVector.X, from.CameraUpVector.Y, from.CameraUpVector.Z) .SetFieldOfView(from.FieldOfView) diff --git a/src/bcf-toolkit/Converter/IConverter.cs b/src/bcf-toolkit/Converter/IConverter.cs index 20bddd7..5b9ca2e 100644 --- a/src/bcf-toolkit/Converter/IConverter.cs +++ b/src/bcf-toolkit/Converter/IConverter.cs @@ -2,6 +2,7 @@ using System.IO; using System.Threading; using System.Threading.Tasks; +using BcfToolkit.Builder.Interfaces; using BcfToolkit.Model; using BcfToolkit.Model.Interfaces; @@ -135,4 +136,17 @@ void ToBcf(IBcf bcf, BcfVersionEnum targetVersion, Stream stream, /// Returns the `Bcf` object which is specified as a type parameter. /// Task BcfFromStream(Stream stream); + + /// + /// The function processes BCF file stream and constructs an in-memory + /// representation of the BCF. As each part of the BCF is built the + /// associated delegate function is invoked. + /// + /// The BCF file stream. + /// + /// The delegate object containing callback functions that are invoked + /// after each part of the BCF is constructed. + /// + /// + public Task ProcessStream(Stream stream, IBcfBuilderDelegate builderDelegate); } \ No newline at end of file diff --git a/src/bcf-toolkit/Model/Bcf30/MarkupExtensions.cs b/src/bcf-toolkit/Model/Bcf30/MarkupExtensions.cs index 2ca9218..4c13b8f 100644 --- a/src/bcf-toolkit/Model/Bcf30/MarkupExtensions.cs +++ b/src/bcf-toolkit/Model/Bcf30/MarkupExtensions.cs @@ -14,7 +14,7 @@ public ITopic GetTopic() { public IViewPoint? GetFirstViewPoint() { return Topic.Viewpoints?.FirstOrDefault(); } - + public void SetViewPoints( Dictionary? visInfos, Dictionary? snapshots) where TVisualizationInfo : IVisualizationInfo { diff --git a/src/bcf-toolkit/Program.cs b/src/bcf-toolkit/Program.cs index c78506c..48b0e62 100644 --- a/src/bcf-toolkit/Program.cs +++ b/src/bcf-toolkit/Program.cs @@ -1,55 +1,14 @@ using System; using System.Threading.Tasks; using System.CommandLine; -using System.IO; -using BcfToolkit.Builder.Bcf21; -using BcfToolkit.Builder.Interfaces; -using BcfToolkit.Model.Bcf21; -using BcfToolkit.Model.Interfaces; using Serilog; using Serilog.Events; namespace BcfToolkit; -public class BcfBuilderDelegate : IBcfBuilderDelegate { - public IBcfBuilderDelegate.OnMarkupCreated - MarkupCreated { get; } = m => { - var d = ((Markup)m).Topic.Description; - Console.WriteLine(d); - }; - - public IBcfBuilderDelegate.OnExtensionsCreated ExtensionsCreated { - get; - } - - - public IBcfBuilderDelegate.OnProjectCreated - ProjectCreated { get; } = Console.WriteLine; - - public IBcfBuilderDelegate.OnDocumentCreated DocumentCreatedCreated { - get; - } -} - internal static class Program { private static async Task Main(string[] args) { - await using var stream = new FileStream( - "/Users/balintbende/Developer/test/bcf/bcftoolkittest.bcfzip", - FileMode.Open, - FileAccess.Read); - - var bcfBuilderDelegate = new BcfBuilderDelegate(); - var streamBuilder = new BcfBuilder(bcfBuilderDelegate); - await streamBuilder.ProcessStream(stream); - - // var builder = new BcfBuilder(); - // var bcf = await builder.BuildInMemoryFromStream(stream); - // Console.WriteLine(bcf.Markups.Count); - - stream.Close(); - - // await HandleArguments(args); - Environment.Exit(0); + await HandleArguments(args); } private static async Task HandleArguments(string[] args) { // Logger setup @@ -62,7 +21,7 @@ private static async Task HandleArguments(string[] args) { .CreateLogger(); Log.Configure(Serilog.Log.Debug, null, null, Serilog.Log.Error); - + var sourcePathOption = new Option( name: "--source", description: "The absolute path of the source file.") { IsRequired = true }; @@ -93,33 +52,13 @@ private static async Task HandleArguments(string[] args) { private static async Task DoRootCommand(InputArguments arguments) { try { - // var worker = new Worker(); - // await worker.Convert(arguments.SourcePath, arguments.Target); - await using var stream = new FileStream( - "/Users/balintbende/Developer/test/bcf/NBHU_BT_BEHF.bcfzip", - FileMode.Open, - FileAccess.Read); - - // var bcfBuilderDelegate = new BcfBuilderDelegate(); - // var streamBuilder = new BcfBuilder(bcfBuilderDelegate); - // await streamBuilder.ProcessStream(stream); - - - - var builder = new BcfBuilder(); - var bcf = await builder.BuildInMemoryFromStream(stream); - Console.WriteLine(bcf.Markups.Count); - - builder = null; - bcf = null; - - stream.Close(); + var worker = new Worker(); + await worker.Convert(arguments.SourcePath, arguments.Target); } catch (Exception e) { Log.Error(e.Message); Environment.Exit(9); } - Environment.Exit(0); } } \ No newline at end of file diff --git a/src/bcf-toolkit/Utils/BcfExtensions.cs b/src/bcf-toolkit/Utils/BcfExtensions.cs index b22f09e..5aa2718 100644 --- a/src/bcf-toolkit/Utils/BcfExtensions.cs +++ b/src/bcf-toolkit/Utils/BcfExtensions.cs @@ -55,6 +55,23 @@ public static async Task> ParseMarkups< return await _ParseMarkups(stream); } + /// + /// The method unzips the BCFzip from a stream, + /// and parses the markup xml files within to create an in memory + /// representation of the data. + /// + /// The source stream of the BCFzip. + /// + /// If the delegate function is set, the caller will receive every markup + /// as they are created, without building up an in-memory representation + /// of the entire BCF document. + /// + /// + /// + /// + /// + /// Exception is thrown if the source stream is not readable. + /// private static async Task> _ParseMarkups< TMarkup, TVisualizationInfo>( @@ -108,7 +125,7 @@ private static async Task> _ParseMarkups< !string.IsNullOrEmpty(currentUuid) && uuid != currentUuid; if (isNewTopic) - WritingOutMarkup( + AddOrInvokeMarkup( ref markup, ref visInfos, ref snapshots, @@ -145,7 +162,7 @@ private static async Task> _ParseMarkups< } if (isLastTopicEntry) - WritingOutMarkup( + AddOrInvokeMarkup( ref markup, ref visInfos, ref snapshots, @@ -159,7 +176,19 @@ private static async Task> _ParseMarkups< return markups; } - private static void WritingOutMarkup( + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + private static void AddOrInvokeMarkup( ref TMarkup? markup, ref Dictionary? visInfos, ref Dictionary? snapshots, @@ -173,8 +202,8 @@ private static void WritingOutMarkup( markup.SetViewPoints(visInfos, snapshots); // If a delegate is provided, invoke it without adding markup to the BCF - if(onMarkupCreated is not null) - onMarkupCreated.Invoke(markup); + if (onMarkupCreated is not null) + onMarkupCreated?.Invoke(markup); else markups.Add(markup); @@ -199,8 +228,41 @@ private static void WritingOutMarkup( /// /// The file stream of the BCFzip. /// Returns a Task with an `Extensions` model. - public static Task ParseExtensions(Stream stream) { - return ParseRequired(stream, entry => entry.IsExtensions()); + public static Task ParseExtensions(Stream stream) + where TExtensions : IExtensions { + return _ParseExtensions(stream); + } + + /// + /// The method unzips the BCFzip from a file stream, + /// and parses the `extensions.xml` file within to create an in memory + /// representation of the data. + /// This is a required in the BCF archive. + /// HISTORY: New file in BCF 3.0. + /// An XML file defining the extensions of a project. + /// + /// The file stream of the BCFzip. + /// + /// If the delegate function is set, the caller will receive an extensions + /// as it is created, without building up an in-memory representation + /// of the entire BCF document. + /// + /// Returns a Task with an `Extensions` model. + public static Task ParseExtensions( + Stream stream, + IBcfBuilderDelegate.OnExtensionsCreated? onExtensionsCreated) + where TExtensions : IExtensions { + return _ParseExtensions(stream, onExtensionsCreated); + } + + private static async Task _ParseExtensions( + Stream stream, + IBcfBuilderDelegate.OnExtensionsCreated? onExtensionsCreated = null) + where TExtensions : IExtensions { + var extensions = + await ParseRequired(stream, entry => entry.IsExtensions()); + onExtensionsCreated?.Invoke(extensions); + return extensions; } /// @@ -214,8 +276,55 @@ public static Task ParseExtensions(Stream stream) { /// /// The stream of the BCFzip. /// Returns a Task with an `ProjectInfo` model. - public static Task ParseProject(Stream stream) { - return ParseOptional(stream, entry => entry.IsBcfProject()); + public static Task ParseProject(Stream stream) + where TProjectInfo : IProject { + return _ParseProject(stream); + } + + /// + /// The method unzips the BCFzip from a file stream, + /// and parses the `project.bcfp` file within to create an in memory + /// representation of the data. + /// This is an optional file in the BCF archive. + /// HISTORY: From BCF 2.0. + /// The project file contains reference information about the project + /// the topics belong to. + /// + /// The stream of the BCFzip. + /// + /// If the delegate function is set, the caller will receive a project + /// as it is created, without building up an in-memory representation + /// of the entire BCF document. + /// + /// Returns a Task with an `ProjectInfo` model. + public static Task ParseProject( + Stream stream, + IBcfBuilderDelegate.OnProjectCreated onProjectCreated) + where TProjectInfo : IProject { + return _ParseProject(stream, onProjectCreated); + } + + private static async Task _ParseProject( + Stream stream, + IBcfBuilderDelegate.OnProjectCreated? onProjectCreated = null) + where TProjectInfo : IProject { + var project = await ParseOptional(stream, entry => entry.IsBcfProject()); + if (project is not null) + onProjectCreated?.Invoke(project); + return project; + } + + public static Task ParseDocuments( + Stream stream) + where TDocumentInfo : IDocumentInfo { + return _ParseDocuments(stream); + } + + public static Task ParseDocuments( + Stream stream, + IBcfBuilderDelegate.OnDocumentCreated? onDocumentCreated) + where TDocumentInfo : IDocumentInfo { + return _ParseDocuments(stream, onDocumentCreated); } /// @@ -229,13 +338,15 @@ public static Task ParseExtensions(Stream stream) { /// document guid. The actual filename is stored in the documents.xml. /// /// The `documents.xml` and documents folder are optional in the BCF archive. - /// + /// /// HISTORY: New in BCF 3.0. /// /// The stream of to the BCFzip. + /// /// Returns a Task with an `DocumentInfo` model. - public static async Task - ParseDocuments(Stream stream) + private static async Task _ParseDocuments( + Stream stream, + IBcfBuilderDelegate.OnDocumentCreated? onDocumentCreated = null) where TDocumentInfo : IDocumentInfo { if (stream is null || !stream.CanRead) throw new ArgumentException("Source stream is not readable."); @@ -267,13 +378,14 @@ public static async Task // Stream must be positioned back to 0 in order to use it again stream.Position = 0; + onDocumentCreated?.Invoke(documentInfo); return documentInfo; } - private static Task ParseRequired( + private static async Task ParseRequired( Stream stream, Func filterFn) { - var obj = ParseObject(stream, filterFn); + var obj = await ParseObject(stream, filterFn); if (obj is null) throw new InvalidDataException($"{typeof(T)} is not found in BCF."); return obj; @@ -357,11 +469,11 @@ public static Task SerializeAndWriteXmlFile(string folder, string file, /// Returns the BcfVersionEnum enum. public static async Task GetVersionFromStreamArchive( Stream stream) { - + if (!stream.CanRead || !stream.CanSeek) { throw new ArgumentException("Stream is not Readable or Seekable"); } - + using var archive = new ZipArchive(stream, ZipArchiveMode.Read, true); BcfVersionEnum? version = null; diff --git a/src/tests/Builder/Bcf21/BcfBuilderTests.cs b/src/tests/Builder/Bcf21/BcfBuilderInMemoryTests.cs similarity index 97% rename from src/tests/Builder/Bcf21/BcfBuilderTests.cs rename to src/tests/Builder/Bcf21/BcfBuilderInMemoryTests.cs index 855845b..eadb88b 100644 --- a/src/tests/Builder/Bcf21/BcfBuilderTests.cs +++ b/src/tests/Builder/Bcf21/BcfBuilderInMemoryTests.cs @@ -9,9 +9,9 @@ using BcfToolkit.Model.Bcf21; using NUnit.Framework; -namespace tests.Builder.Bcf21; +namespace Tests.Builder.Bcf21; -public class BcfBuilderTests { +public class BcfBuilderInMemoryTests { private BcfBuilder _builder = null!; @@ -153,7 +153,7 @@ public async Task BuildMaximumInformationBcfFromStreamTest() { "Resources/Bcf/v2.1/MaximumInformation.bcfzip", FileMode.Open, FileAccess.Read); - var bcf = await _builder.BuildFromStream(stream); + var bcf = await _builder.BuildInMemoryFromStream(stream); Assert.That(bcf.Markups.Count, Is.EqualTo(2)); var markup = bcf .Markups diff --git a/src/tests/Builder/Bcf21/BcfBuilderStreamTest.cs b/src/tests/Builder/Bcf21/BcfBuilderStreamTest.cs new file mode 100644 index 0000000..b931684 --- /dev/null +++ b/src/tests/Builder/Bcf21/BcfBuilderStreamTest.cs @@ -0,0 +1,48 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using BcfToolkit.Builder.Bcf21; +using BcfToolkit.Builder.Interfaces; +using BcfToolkit.Model.Bcf21; +using BcfToolkit.Model.Interfaces; +using NUnit.Framework; + +namespace Tests.Builder.Bcf21; + +public class BcfBuilderDelegate : IBcfBuilderDelegate { + public IBcfBuilderDelegate.OnMarkupCreated + MarkupCreated { get; } = m => { + var markup = (Markup)m; + Console.WriteLine(markup.Topic.Guid); + }; + + public IBcfBuilderDelegate.OnExtensionsCreated + ExtensionsCreated { get; } = Console.WriteLine; + + public IBcfBuilderDelegate.OnProjectCreated + ProjectCreated { get; } = Console.WriteLine; + + public IBcfBuilderDelegate.OnDocumentCreated + DocumentCreatedCreated { get; } = Console.WriteLine; +} + +public class BcfBuilderStreamTest { + private BcfBuilder _streamBuilder = null!; + + [SetUp] + public void Setup() { + var bcfBuilderDelegate = new BcfBuilderDelegate(); + _streamBuilder = new BcfBuilder(); + _streamBuilder.SetDelegate(bcfBuilderDelegate); + } + + [Test] + public async Task ProcessBcfStreamTest() { + await using var stream = new FileStream( + "Resources/Bcf/v2.1/MaximumInformation.bcfzip", + FileMode.Open, + FileAccess.Read); + + await _streamBuilder.ProcessStream(stream); + } +} \ No newline at end of file diff --git a/src/tests/Builder/Bcf30/BcfBuilderTests.cs b/src/tests/Builder/Bcf30/BcfBuilderInMemoryTests.cs similarity index 58% rename from src/tests/Builder/Bcf30/BcfBuilderTests.cs rename to src/tests/Builder/Bcf30/BcfBuilderInMemoryTests.cs index e071847..d7d113c 100644 --- a/src/tests/Builder/Bcf30/BcfBuilderTests.cs +++ b/src/tests/Builder/Bcf30/BcfBuilderInMemoryTests.cs @@ -1,39 +1,19 @@ -using System; using System.IO; using System.Linq; using System.Threading.Tasks; using BcfToolkit.Builder.Bcf30; -using BcfToolkit.Builder.Bcf30.Interfaces; -using BcfToolkit.Model.Bcf30; using NUnit.Framework; -namespace tests.Builder.Bcf30; +namespace Tests.Builder.Bcf30; -public class BcfBuilderDelegate : IBcfBuilderDelegate { - public IBcfBuilderDelegate.OnMarkupCreated - MarkupCreated { get; } = Console.WriteLine; - - public IBcfBuilderDelegate.OnExtensionsCreated - ExtensionsCreated { get; } = Console.WriteLine; - - public IBcfBuilderDelegate.OnProjectCreated - ProjectCreated { get; } = Console.WriteLine; - - public IBcfBuilderDelegate.OnDocumentCreated - DocumentCreatedCreated { get; } = Console.WriteLine; -} - -public class BcfBuilderTests { +public class BcfBuilderInMemoryTests { private BcfBuilder _inMemoryBuilder = null!; - private BcfBuilder _steamBuilder = null!; [SetUp] public void Setup() { _inMemoryBuilder = new BcfBuilder(); - var bcfBuilderDelegate = new BcfBuilderDelegate(); - _steamBuilder = new BcfBuilder(bcfBuilderDelegate); } - + [Test] public void BuildBcfWithComplexFields() { var bcf = _inMemoryBuilder @@ -55,26 +35,11 @@ public void BuildBcfWithComplexFields() { Assert.That(true, Is.EqualTo(bcf.Extensions.TopicTypesSpecified)); Assert.That("Project", Is.EqualTo(bcf.Project?.Project.Name)); } - + [Test] public void BuildBcfWithMissingRequiredFields() { Assert.That(() => _inMemoryBuilder.Build(), Throws.ArgumentException); } - - - [Test] - public async Task BuildInMemoryBcfFromStreamTest() { - await using var stream = new FileStream( - "Resources/Bcf/v3.0/UserAssignment.bcfzip", - FileMode.Open, - FileAccess.Read); - - await _steamBuilder.ProcessStream(stream); - // Assert.That(1, Is.EqualTo(bcf.Markups.Count)); - // Assert.That( - // "Architect@example.com", - // Is.EqualTo(bcf.Markups.FirstOrDefault()?.Topic.AssignedTo)); - } [Test] public async Task BuildInMemoryBcfFromStream() { @@ -88,7 +53,7 @@ public async Task BuildInMemoryBcfFromStream() { "Architect@example.com", Is.EqualTo(bcf.Markups.FirstOrDefault()?.Topic.AssignedTo)); } - + [Test] public async Task BuildEmptyBcfFromStreamTest() { await using var stream = new FileStream( @@ -96,6 +61,6 @@ public async Task BuildEmptyBcfFromStreamTest() { FileMode.Open, FileAccess.Read); Assert.That(() => _inMemoryBuilder.BuildInMemoryFromStream(stream), - Throws.ArgumentException); + Throws.Exception); } } \ No newline at end of file diff --git a/src/tests/Builder/Bcf30/BcfBuilderStreamTest.cs b/src/tests/Builder/Bcf30/BcfBuilderStreamTest.cs new file mode 100644 index 0000000..5fa4668 --- /dev/null +++ b/src/tests/Builder/Bcf30/BcfBuilderStreamTest.cs @@ -0,0 +1,48 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using BcfToolkit.Builder.Bcf30; +using BcfToolkit.Builder.Interfaces; +using BcfToolkit.Model.Bcf30; +using BcfToolkit.Model.Interfaces; +using NUnit.Framework; + +namespace Tests.Builder.Bcf30; + +public class BcfBuilderDelegate : IBcfBuilderDelegate { + public IBcfBuilderDelegate.OnMarkupCreated + MarkupCreated { get; } = m => { + var markup = (Markup)m; + Console.WriteLine(markup.Topic.Guid); + }; + + public IBcfBuilderDelegate.OnExtensionsCreated + ExtensionsCreated { get; } = Console.WriteLine; + + public IBcfBuilderDelegate.OnProjectCreated + ProjectCreated { get; } = Console.WriteLine; + + public IBcfBuilderDelegate.OnDocumentCreated + DocumentCreatedCreated { get; } = Console.WriteLine; +} + +public class BcfBuilderStreamTest { + private BcfBuilder _streamBuilder = null!; + + [SetUp] + public void Setup() { + var bcfBuilderDelegate = new BcfBuilderDelegate(); + _streamBuilder = new BcfBuilder(); + _streamBuilder.SetDelegate(bcfBuilderDelegate); + } + + [Test] + public async Task ProcessBcfStreamTest() { + await using var stream = new FileStream( + "Resources/Bcf/v3.0/UserAssignment.bcfzip", + FileMode.Open, + FileAccess.Read); + + await _streamBuilder.ProcessStream(stream); + } +} \ No newline at end of file diff --git a/src/tests/Builder/BcfBuilderStreamTests.cs b/src/tests/Builder/BcfBuilderStreamTests.cs deleted file mode 100644 index 49ed74e..0000000 --- a/src/tests/Builder/BcfBuilderStreamTests.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.IO; -using System.Threading.Tasks; -using BcfToolkit; -using NUnit.Framework; -using BcfToolkit.Builder.Bcf30; -using Moq; - -namespace Tests.Builder; - -using BcfToolkit.Builder.Bcf30.Interfaces; -using BcfToolkit.Model.Bcf30; - -public class BcfBuilderStreamTests { - private BcfBuilder _streamBuilder = null!; - - // private class BcfBuilderDelegate : IBcfBuilderDelegate { - // public IBcfBuilderDelegate.OnMarkupCreated - // OnMarkupCreated { get; } = Console.WriteLine; - // - // public IBcfBuilderDelegate.OnExtensionsCreated - // ExtensionsCreated { get; } = Console.WriteLine; - // - // public IBcfBuilderDelegate.OnProjectCreated - // ProjectCreated { get; } = Console.WriteLine; - // - // public IBcfBuilderDelegate.OnDocumentCreated - // DocumentCreatedCreated { get; } = Console.WriteLine; - // } - - [Test] - public async Task ProcessStreamTest() { - var bcfBuilderDelegateMock = new Mock(); - _streamBuilder = new BcfBuilder(bcfBuilderDelegateMock.Object); - - await using var stream = new FileStream( - "Resources/Bcf/v3.0/UserAssignment.bcfzip", - FileMode.Open, - FileAccess.Read); - await _streamBuilder.ProcessStream(stream); - - bcfBuilderDelegateMock - .Verify(d => d.MarkupCreated, Times.Once); - } -} \ No newline at end of file diff --git a/src/tests/Converter/Bcf21/ConverterTests.cs b/src/tests/Converter/Bcf21/ConverterTests.cs index 4faa3d7..2ed276f 100644 --- a/src/tests/Converter/Bcf21/ConverterTests.cs +++ b/src/tests/Converter/Bcf21/ConverterTests.cs @@ -165,9 +165,9 @@ public async Task BuildSimpleV21BcfFromStreamTest() { FileMode.Open, FileAccess.Read); var bcf = await _converter.BcfFromStream(stream); - Assert.That(typeof(Bcf), Is.EqualTo(bcf.GetType())); - Assert.That(1, Is.EqualTo(bcf.Markups.Count)); - Assert.That("2.1", Is.EqualTo(bcf.Version?.VersionId)); + Assert.That(bcf.GetType(), Is.EqualTo(typeof(Bcf))); + Assert.That(bcf.Markups.Count, Is.EqualTo(1)); + Assert.That(bcf.Version?.VersionId, Is.EqualTo("2.1")); } /// diff --git a/src/tests/Converter/Bcf30/FileWriterTests.cs b/src/tests/Converter/Bcf30/FileWriterTests.cs index 3d22132..0df62ec 100644 --- a/src/tests/Converter/Bcf30/FileWriterTests.cs +++ b/src/tests/Converter/Bcf30/FileWriterTests.cs @@ -29,7 +29,7 @@ public async Task WriteBcfWithCreatingZipEntryFromStreamTest() { var stream = await FileWriter.SerializeAndWriteBcf(bcf); var bcfResultBuilder = new BcfBuilder(); - var bcfResult = await bcfResultBuilder.BuildFromStream(stream); + var bcfResult = await bcfResultBuilder.BuildInMemoryFromStream(stream); Assert.That( bcf.Markups.FirstOrDefault()?.Topic.Title, @@ -50,7 +50,7 @@ public async Task WriteBcfWithSavingXmlTmpTest() { var bcfResultBuilder = new BcfBuilder(); var bcfResult = await bcfResultBuilder - .BuildFromStream(stream); + .BuildInMemoryFromStream(stream); Assert.That( bcf.Markups.FirstOrDefault()?.Topic.Title, diff --git a/src/tests/Resources/Json/v2.1/AllPartsVisible/imagebase64.txt b/src/tests/Resources/Json/v2.1/AllPartsVisible/imagebase64.txt new file mode 100644 index 0000000..cac630e --- /dev/null +++ b/src/tests/Resources/Json/v2.1/AllPartsVisible/imagebase64.txt @@ -0,0 +1 @@ +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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA diff --git a/src/tests/Utils/BcfExtensionsTests.cs b/src/tests/Utils/BcfExtensionsTests.cs index 7483edd..fafb933 100644 --- a/src/tests/Utils/BcfExtensionsTests.cs +++ b/src/tests/Utils/BcfExtensionsTests.cs @@ -7,7 +7,7 @@ using bcf21 = BcfToolkit.Model.Bcf21; using bcf30 = BcfToolkit.Model.Bcf30; -namespace tests.Utils; +namespace Tests.Utils; [TestFixture] public class BcfExtensionsTests { diff --git a/src/tests/Utils/JsonExtensionsTests.cs b/src/tests/Utils/JsonExtensionsTests.cs index 0b1c281..5c0da3b 100644 --- a/src/tests/Utils/JsonExtensionsTests.cs +++ b/src/tests/Utils/JsonExtensionsTests.cs @@ -5,7 +5,7 @@ using bcf21 = BcfToolkit.Model.Bcf21; using bcf30 = BcfToolkit.Model.Bcf30; -namespace tests.Utils; +namespace Tests.Utils; public class JsonExtensionsTests { /// diff --git a/src/tests/Utils/ZipArchiveEntryExtensionsTests.cs b/src/tests/Utils/ZipArchiveEntryExtensionsTests.cs index d47a9d6..0e413b5 100644 --- a/src/tests/Utils/ZipArchiveEntryExtensionsTests.cs +++ b/src/tests/Utils/ZipArchiveEntryExtensionsTests.cs @@ -5,7 +5,7 @@ using BcfToolkit.Utils; using NUnit.Framework; -namespace tests.Utils; +namespace Tests.Utils; [TestFixture] public class ZipArchiveEntryExtensionsTests { diff --git a/src/tests/WorkerTests.cs b/src/tests/WorkerTests.cs index 938c225..58acd5b 100644 --- a/src/tests/WorkerTests.cs +++ b/src/tests/WorkerTests.cs @@ -142,7 +142,7 @@ private async Task CheckBcfStreamVersion( Assert.That(expectedVersion, Is.EqualTo(version)); await stream.FlushAsync(); } - + // [Test] // [Category("BCF v3.0")] // public async Task BcfV30ToV21StreamSamplesTests() { From 93499c1ff5909d3b5e38960803e2226efc6b11c3 Mon Sep 17 00:00:00 2001 From: Balint Bende Date: Thu, 5 Sep 2024 16:27:08 +0200 Subject: [PATCH 28/29] [BMSPT-283] deleted bcf21 process stream test --- bcf-toolkit.sln.DotSettings.user | 3 +- .../Builder/Bcf21/BcfBuilderStreamTest.cs | 48 ------------------- 2 files changed, 2 insertions(+), 49 deletions(-) delete mode 100644 src/tests/Builder/Bcf21/BcfBuilderStreamTest.cs diff --git a/bcf-toolkit.sln.DotSettings.user b/bcf-toolkit.sln.DotSettings.user index faa1d46..f56e38a 100644 --- a/bcf-toolkit.sln.DotSettings.user +++ b/bcf-toolkit.sln.DotSettings.user @@ -1,6 +1,7 @@  /Users/balintbende/Library/Caches/JetBrains/Rider2024.1/resharper-host/temp/Rider/vAny/CoverageData/_bcf-toolkit.-1315391344/Snapshot/snapshot.utdcvr - <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Solution /> </SessionState> diff --git a/src/tests/Builder/Bcf21/BcfBuilderStreamTest.cs b/src/tests/Builder/Bcf21/BcfBuilderStreamTest.cs deleted file mode 100644 index b931684..0000000 --- a/src/tests/Builder/Bcf21/BcfBuilderStreamTest.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.IO; -using System.Threading.Tasks; -using BcfToolkit.Builder.Bcf21; -using BcfToolkit.Builder.Interfaces; -using BcfToolkit.Model.Bcf21; -using BcfToolkit.Model.Interfaces; -using NUnit.Framework; - -namespace Tests.Builder.Bcf21; - -public class BcfBuilderDelegate : IBcfBuilderDelegate { - public IBcfBuilderDelegate.OnMarkupCreated - MarkupCreated { get; } = m => { - var markup = (Markup)m; - Console.WriteLine(markup.Topic.Guid); - }; - - public IBcfBuilderDelegate.OnExtensionsCreated - ExtensionsCreated { get; } = Console.WriteLine; - - public IBcfBuilderDelegate.OnProjectCreated - ProjectCreated { get; } = Console.WriteLine; - - public IBcfBuilderDelegate.OnDocumentCreated - DocumentCreatedCreated { get; } = Console.WriteLine; -} - -public class BcfBuilderStreamTest { - private BcfBuilder _streamBuilder = null!; - - [SetUp] - public void Setup() { - var bcfBuilderDelegate = new BcfBuilderDelegate(); - _streamBuilder = new BcfBuilder(); - _streamBuilder.SetDelegate(bcfBuilderDelegate); - } - - [Test] - public async Task ProcessBcfStreamTest() { - await using var stream = new FileStream( - "Resources/Bcf/v2.1/MaximumInformation.bcfzip", - FileMode.Open, - FileAccess.Read); - - await _streamBuilder.ProcessStream(stream); - } -} \ No newline at end of file From 7c099c785cea42a56e39e05bfea2684990d0c0b1 Mon Sep 17 00:00:00 2001 From: Balint Bende Date: Fri, 6 Sep 2024 14:13:49 +0200 Subject: [PATCH 29/29] [BMSPT-283] added nullable Orthogonal and Perspective camera in Visinfo --- bcf-toolkit.sln.DotSettings.user | 5 +++-- src/bcf-toolkit/Model/Bcf21/VisInfo.cs | 6 ++++-- src/bcf-toolkit/Model/Bcf30/Visinfo.cs | 6 ++++-- src/tests/Converter/Bcf30/ConverterTests.cs | 3 +++ .../Resources/Bcf/v3.0/SingleVisibleWall.bcfzip | Bin 0 -> 15337 bytes 5 files changed, 14 insertions(+), 6 deletions(-) create mode 100644 src/tests/Resources/Bcf/v3.0/SingleVisibleWall.bcfzip diff --git a/bcf-toolkit.sln.DotSettings.user b/bcf-toolkit.sln.DotSettings.user index f56e38a..937d229 100644 --- a/bcf-toolkit.sln.DotSettings.user +++ b/bcf-toolkit.sln.DotSettings.user @@ -1,7 +1,6 @@  /Users/balintbende/Library/Caches/JetBrains/Rider2024.1/resharper-host/temp/Rider/vAny/CoverageData/_bcf-toolkit.-1315391344/Snapshot/snapshot.utdcvr - - <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Solution /> </SessionState> @@ -76,6 +75,8 @@ + + diff --git a/src/bcf-toolkit/Model/Bcf21/VisInfo.cs b/src/bcf-toolkit/Model/Bcf21/VisInfo.cs index 0780ec1..39199f6 100644 --- a/src/bcf-toolkit/Model/Bcf21/VisInfo.cs +++ b/src/bcf-toolkit/Model/Bcf21/VisInfo.cs @@ -621,10 +621,12 @@ public partial class VisualizationInfo public Components Components { get; set; } [System.Xml.Serialization.XmlElementAttribute("OrthogonalCamera")] - public OrthogonalCamera OrthogonalCamera { get; set; } +#nullable enable + public OrthogonalCamera? OrthogonalCamera { get; set; } [System.Xml.Serialization.XmlElementAttribute("PerspectiveCamera")] - public PerspectiveCamera PerspectiveCamera { get; set; } + public PerspectiveCamera? PerspectiveCamera { get; set; } +#nullable disable [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _lines; diff --git a/src/bcf-toolkit/Model/Bcf30/Visinfo.cs b/src/bcf-toolkit/Model/Bcf30/Visinfo.cs index 227fd18..86f2d25 100644 --- a/src/bcf-toolkit/Model/Bcf30/Visinfo.cs +++ b/src/bcf-toolkit/Model/Bcf30/Visinfo.cs @@ -684,11 +684,13 @@ public partial class VisualizationInfo [System.Xml.Serialization.XmlElementAttribute("Components")] public Components Components { get; set; } +#nullable enable [System.Xml.Serialization.XmlElementAttribute("OrthogonalCamera")] - public OrthogonalCamera OrthogonalCamera { get; set; } + public OrthogonalCamera? OrthogonalCamera { get; set; } [System.Xml.Serialization.XmlElementAttribute("PerspectiveCamera")] - public PerspectiveCamera PerspectiveCamera { get; set; } + public PerspectiveCamera? PerspectiveCamera { get; set; } +#nullable disable [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection _lines; diff --git a/src/tests/Converter/Bcf30/ConverterTests.cs b/src/tests/Converter/Bcf30/ConverterTests.cs index e880407..1e6ac89 100644 --- a/src/tests/Converter/Bcf30/ConverterTests.cs +++ b/src/tests/Converter/Bcf30/ConverterTests.cs @@ -49,6 +49,9 @@ public void BcfToJsonSampleFilesTest() { _converter.BcfToJson( "Resources/Bcf/v3.0/SingleInvisibleWall.bcfzip", "Resources/output/json/v3.0/SingleInvisibleWall"); + _converter.BcfToJson( + "Resources/Bcf/v3.0/SingleVisibleWall.bcfzip", + "Resources/output/json/v3.0/SingleVisibleWall"); _converter.BcfToJson( "Resources/Bcf/v3.0/TestBcf30.bcfzip", "Resources/output/json/v3.0/TestBcf30"); diff --git a/src/tests/Resources/Bcf/v3.0/SingleVisibleWall.bcfzip b/src/tests/Resources/Bcf/v3.0/SingleVisibleWall.bcfzip new file mode 100644 index 0000000000000000000000000000000000000000..e497b834896827df6e1a07c0f15b0f464bd98bcf GIT binary patch literal 15337 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 HKS%!qG;PCI literal 0 HcmV?d00001