From b1c813aaba417d4fc30119a1430ebe435956893e Mon Sep 17 00:00:00 2001 From: samatstarion Date: Sat, 16 Nov 2024 14:50:36 +0100 Subject: [PATCH] [Refactor] XmiReader to return xmiReaderResult --- .../ClassExtensionsTestFixture.cs | 11 +- .../ClassifierExtensionsTestFixture.cs | 11 +- .../HtmlReportGeneratorTestFixture.cs | 78 ++++ .../Generators/ModelInspectorTestFixture.cs | 12 +- .../Generators/HandleBarsReportGenerator.cs | 63 ++- .../Generators/HtmlReportGenerator.cs | 41 +- .../Generators/ModelInspector.cs | 4 +- .../Generators/ReportGenerator.cs | 7 +- .../Payload/HandlebarsPayload.cs | 52 ++- .../Templates/uml-to-html-docs.hbs | 434 ++++++++++++++++++ uml4net.Reporting/uml4net.Reporting.csproj | 9 +- .../SysML2.XmiReaderTestFixture.cs | 6 +- uml4net.xmi.Tests/UMLXmiReaderTestFixture.cs | 12 +- uml4net.xmi/Readers/IXmiReader.cs | 14 +- uml4net.xmi/Readers/Packages/ModelReader.cs | 4 + uml4net.xmi/Readers/Packages/PackageReader.cs | 2 + uml4net.xmi/Readers/XmiReader.cs | 72 ++- uml4net.xmi/Readers/XmiReaderResult.cs | 44 ++ 18 files changed, 771 insertions(+), 105 deletions(-) create mode 100644 uml4net.Reporting.Tests/Generators/HtmlReportGeneratorTestFixture.cs create mode 100644 uml4net.Reporting/Templates/uml-to-html-docs.hbs create mode 100644 uml4net.xmi/Readers/XmiReaderResult.cs diff --git a/uml4net.Extensions.Tests/ClassExtensionsTestFixture.cs b/uml4net.Extensions.Tests/ClassExtensionsTestFixture.cs index d5f005ac..1d183ad7 100644 --- a/uml4net.Extensions.Tests/ClassExtensionsTestFixture.cs +++ b/uml4net.Extensions.Tests/ClassExtensionsTestFixture.cs @@ -33,15 +33,14 @@ namespace uml4net.Extensions.Tests using uml4net.POCO.Packages; using uml4net.POCO.StructuredClassifiers; using uml4net.xmi; + using xmi.Readers; [TestFixture] public class ClassExtensionsTestFixture { private ILoggerFactory loggerFactory; - private IEnumerable packages; - - private IPackage rootPackage; + private XmiReaderResult xmiReaderResult; [OneTimeSetUp] public void OneTimeSetUp() @@ -67,15 +66,13 @@ public void SetUp() .WithLogger(this.loggerFactory) .Build(); - this.packages = reader.Read(Path.Combine(rootPath, "UML.xmi")); - - this.rootPackage = this.packages.Single(); + this.xmiReaderResult = reader.Read(Path.Combine(rootPath, "UML.xmi")); } [Test] public void Verify_that_QueryAllProperties_returns_expected_result() { - var commonStructuresPackage = this.rootPackage.NestedPackage.Single(x => x.Name == "CommonStructure"); + var commonStructuresPackage = this.xmiReaderResult.Root.NestedPackage.Single(x => x.Name == "CommonStructure"); var dependency = commonStructuresPackage.PackagedElement.OfType().Single(x => x.Name == "Dependency"); diff --git a/uml4net.Extensions.Tests/ClassifierExtensionsTestFixture.cs b/uml4net.Extensions.Tests/ClassifierExtensionsTestFixture.cs index 2fa5e59b..1aaed6c7 100644 --- a/uml4net.Extensions.Tests/ClassifierExtensionsTestFixture.cs +++ b/uml4net.Extensions.Tests/ClassifierExtensionsTestFixture.cs @@ -33,15 +33,14 @@ namespace uml4net.Extensions.Tests using uml4net.POCO.Packages; using uml4net.POCO.StructuredClassifiers; using uml4net.xmi; + using xmi.Readers; [TestFixture] public class ClassifierExtensionsTestFixture { private ILoggerFactory loggerFactory; - private IEnumerable packages; - - private IPackage rootPackage; + private XmiReaderResult xmiReaderResult; [OneTimeSetUp] public void OneTimeSetUp() @@ -67,15 +66,13 @@ public void SetUp() .WithLogger(this.loggerFactory) .Build(); - this.packages = reader.Read(Path.Combine(rootPath, "UML.xmi")); - - this.rootPackage = this.packages.Single(); + this.xmiReaderResult = reader.Read(Path.Combine(rootPath, "UML.xmi")); } [Test] public void Verify_that_QueryAllGeneralClassifiers_returns_expected_result() { - var commonStructuresPackage = this.rootPackage.NestedPackage.Single(x => x.Name == "CommonStructure"); + var commonStructuresPackage = this.xmiReaderResult.Root.NestedPackage.Single(x => x.Name == "CommonStructure"); var dependency = commonStructuresPackage.PackagedElement.OfType().Single(x => x.Name == "Dependency"); diff --git a/uml4net.Reporting.Tests/Generators/HtmlReportGeneratorTestFixture.cs b/uml4net.Reporting.Tests/Generators/HtmlReportGeneratorTestFixture.cs new file mode 100644 index 00000000..ee8c1065 --- /dev/null +++ b/uml4net.Reporting.Tests/Generators/HtmlReportGeneratorTestFixture.cs @@ -0,0 +1,78 @@ +// ------------------------------------------------------------------------------------------------- +// +// +// Copyright 2019-2024 Starion Group S.A. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, softwareUseCases +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +// ------------------------------------------------------------------------------------------------ + +namespace uml4net.Reporting.Tests.Generators +{ + using System.IO; + using System.Linq; + using Microsoft.Extensions.Logging; + + using NUnit.Framework; + using POCO.Packages; + using Reporting.Generators; + using Serilog; + using xmi; + + [TestFixture] + public class HtmlReportGeneratorTestFixture + { + private ILoggerFactory loggerFactory; + + private HtmlReportGenerator htmlReportGenerator; + + private string modelPath; + + private FileInfo modelFileInfo; + + private FileInfo reportFileInfo; + + [OneTimeSetUp] + public void OneTimeSetUp() + { + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Verbose() + .WriteTo.Console() + .CreateLogger(); + + this.loggerFactory = LoggerFactory.Create(builder => + { + builder.AddSerilog(); + }); + } + + [SetUp] + public void SetUp() + { + this.modelPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData", "UML.xmi"); + this.modelFileInfo = new FileInfo(modelPath); + + var reportPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "html-report.html"); + this.reportFileInfo = new FileInfo(reportPath); + } + + [Test] + public void Verify_that_the_report_generator_generates_a_report() + { + this.htmlReportGenerator = new HtmlReportGenerator(this.loggerFactory); + + Assert.That(() => this.htmlReportGenerator.GenerateReport(modelFileInfo, reportFileInfo), Throws.Nothing); + } + } +} \ No newline at end of file diff --git a/uml4net.Reporting.Tests/Generators/ModelInspectorTestFixture.cs b/uml4net.Reporting.Tests/Generators/ModelInspectorTestFixture.cs index 611767fc..493ef290 100644 --- a/uml4net.Reporting.Tests/Generators/ModelInspectorTestFixture.cs +++ b/uml4net.Reporting.Tests/Generators/ModelInspectorTestFixture.cs @@ -78,13 +78,11 @@ public void Verify_that_Inspection_report_can_be_executed() .WithLogger(this.loggerFactory) .Build(); - var packages = reader.Read(Path.Combine(rootPath, "UML.xmi")); - - var rootPackage = packages.Single(); + var xmiReaderResult = reader.Read(Path.Combine(rootPath, "UML.xmi")); this.modelInspector = new ModelInspector(this.loggerFactory); - var report = this.modelInspector.Inspect(rootPackage, true); + var report = this.modelInspector.Inspect(xmiReaderResult.Root, true); Log.Logger.Information(report); @@ -109,11 +107,9 @@ public void Verify_that_inspect_class_returns_expected_result() .WithLogger(this.loggerFactory) .Build(); - var packages = reader.Read(Path.Combine(rootPath, "UML.xmi")); - - var rootPackage = packages.Single(); + var xmiReaderResult = reader.Read(Path.Combine(rootPath, "UML.xmi")); - var classificationPackage = rootPackage.PackagedElement.OfType().Single(x => x.Name == "Classification") ; + var classificationPackage = xmiReaderResult.Root.PackagedElement.OfType().Single(x => x.Name == "Classification") ; this.modelInspector = new ModelInspector(this.loggerFactory); diff --git a/uml4net.Reporting/Generators/HandleBarsReportGenerator.cs b/uml4net.Reporting/Generators/HandleBarsReportGenerator.cs index 98260c36..abe963a1 100644 --- a/uml4net.Reporting/Generators/HandleBarsReportGenerator.cs +++ b/uml4net.Reporting/Generators/HandleBarsReportGenerator.cs @@ -27,12 +27,15 @@ namespace uml4net.Reporting.Generators using HandlebarsDotNet.Helpers; using Microsoft.Extensions.Logging; - using Payload; - using POCO.Packages; - using POCO.StructuredClassifiers; - using Resources; + + using uml4net.POCO.Packages; + using uml4net.POCO.StructuredClassifiers; using uml4net.POCO.SimpleClassifiers; + using uml4net.Reporting.Payload; + using uml4net.Reporting.Resources; + using uml4net.xmi.Readers; + /// /// Abstract super class from which all generators /// need to derive @@ -72,7 +75,7 @@ protected HandleBarsReportGenerator(ILoggerFactory loggerFactory = null) : base( protected virtual void RegisterHelpers() { uml4net.HandleBars.StringHelper.RegisterStringHelper(this.Handlebars); - //uml4net.HandleBars.StructuralFeatureHelper.RegisterStructuralFeatureHelper(this.Handlebars); + uml4net.HandleBars.PropertyHelper.RegisterStructuralFeatureHelper(this.Handlebars); uml4net.HandleBars.GeneralizationHelper.RegisterGeneralizationHelper(this.Handlebars); uml4net.HandleBars.DocumentationHelper.RegisteredDocumentationHelper(this.Handlebars); } @@ -100,39 +103,49 @@ protected void RegisterEmbeddedTemplate(string name) } /// - /// Creates a based on the provided root + /// Creates a based on the provided root /// - /// - /// the subject root + /// + /// the subject /// /// /// an instance of /// - protected static HandlebarsPayload CreateHandlebarsPayload(IPackage rootPackage) + protected static HandlebarsPayload CreateHandlebarsPayload(XmiReaderResult xmiReaderResult) { - var packages = rootPackage.QueryPackages(); - - var enums = new List(); + var enumerations = new List(); + var primitiveTypes = new List(); var dataTypes = new List(); - var eClasses = new List(); + var classes = new List(); + var interfaces = new List(); + + foreach (var package in xmiReaderResult.Packages) + { + var containedPackages = package.QueryPackages(); + + foreach (var containedPackage in containedPackages) + { + enumerations.AddRange(containedPackage.PackagedElement.OfType()); + + primitiveTypes.AddRange(containedPackage.PackagedElement.OfType()); - //foreach (var package in packages) - //{ - // enums.AddRange(package.EClassifiers.OfType()); + dataTypes.AddRange(containedPackage.PackagedElement + .OfType() + .Where(x => x is not IEnumeration && x is not IPrimitiveType)); - // dataTypes.AddRange(package.EClassifiers - // .OfType() - // .Where(x => !(x is EEnum)) - // .OrderBy(x => x.Name)); + classes.AddRange(containedPackage.PackagedElement.OfType()); - // eClasses.AddRange(package.EClassifiers.OfType()); - //} + interfaces.AddRange(containedPackage.PackagedElement.OfType()); + } + } - var orderedEnums = enums.OrderBy(x => x.Name); + var orderedEnumerations = enumerations.OrderBy(x => x.Name); + var orderedPrimitiveTypes = primitiveTypes.OrderBy(x => x.Name); var orderedDataTypes = dataTypes.OrderBy(x => x.Name); - var orderedClasses = eClasses.OrderBy(x => x.Name); + var orderedClasses = classes.OrderBy(x => x.Name); + var orderedInterfaces = interfaces.OrderBy(x => x.Name); - var payload = new HandlebarsPayload(rootPackage, orderedEnums, orderedDataTypes, orderedClasses); + var payload = new HandlebarsPayload(xmiReaderResult.Root, xmiReaderResult.Packages, orderedEnumerations, orderedPrimitiveTypes, orderedDataTypes, orderedClasses, orderedInterfaces); return payload; } diff --git a/uml4net.Reporting/Generators/HtmlReportGenerator.cs b/uml4net.Reporting/Generators/HtmlReportGenerator.cs index d2a996ad..45d86870 100644 --- a/uml4net.Reporting/Generators/HtmlReportGenerator.cs +++ b/uml4net.Reporting/Generators/HtmlReportGenerator.cs @@ -21,6 +21,7 @@ namespace uml4net.Reporting.Generators { using System; + using System.Diagnostics; using System.IO; using Microsoft.Extensions.Logging; @@ -70,7 +71,26 @@ public string QueryReportType() /// public string GenerateReport(FileInfo modelPath) { - throw new NotImplementedException(); + if (modelPath == null) + { + throw new ArgumentNullException(nameof(modelPath)); + } + + var sw = Stopwatch.StartNew(); + + this.logger.LogInformation("Start Generating HTML report tables"); + + var template = this.Templates["uml-to-html-docs"]; + + var xmiReaderResult = this.LoadPackages(modelPath, modelPath.Directory); + + var payload = CreateHandlebarsPayload(xmiReaderResult); + + var generatedHtml = template(payload); + + this.logger.LogInformation("Generated HTML report in {ElapsedTime} [ms]", sw.ElapsedMilliseconds); + + return generatedHtml; } /// @@ -84,7 +104,24 @@ public string GenerateReport(FileInfo modelPath) /// public void GenerateReport(FileInfo modelPath, FileInfo outputPath) { - throw new NotImplementedException(); + if (outputPath == null) + { + throw new ArgumentNullException(nameof(outputPath)); + } + + var sw = Stopwatch.StartNew(); + + var generatedHtml = this.GenerateReport(modelPath); + + if (outputPath.Exists) + { + outputPath.Delete(); + } + + using var writer = outputPath.CreateText(); + writer.Write(generatedHtml); + + this.logger.LogInformation("Generated and saved HTML report in {ElapsedTime} [ms]", sw.ElapsedMilliseconds); } /// diff --git a/uml4net.Reporting/Generators/ModelInspector.cs b/uml4net.Reporting/Generators/ModelInspector.cs index 76cd6442..41ede3cc 100644 --- a/uml4net.Reporting/Generators/ModelInspector.cs +++ b/uml4net.Reporting/Generators/ModelInspector.cs @@ -463,11 +463,11 @@ public void GenerateReport(FileInfo modelPath, FileInfo outputPath) this.logger.LogInformation("Start Generating Inspection Report"); - var packages = this.LoadPackages(modelPath, modelPath.Directory); + var xmiReaderResult = this.LoadPackages(modelPath, modelPath.Directory); var result = new StringBuilder(); - foreach (var package in packages) + foreach (var package in xmiReaderResult.Packages) { result.Append(this.ReportHeader()); result.Append(this.Inspect(package, true)); diff --git a/uml4net.Reporting/Generators/ReportGenerator.cs b/uml4net.Reporting/Generators/ReportGenerator.cs index 5f0807b5..a20277c4 100644 --- a/uml4net.Reporting/Generators/ReportGenerator.cs +++ b/uml4net.Reporting/Generators/ReportGenerator.cs @@ -30,6 +30,7 @@ namespace uml4net.Reporting.Generators using uml4net.POCO.Packages; using uml4net.xmi; + using xmi.Readers; /// /// abstract class from which all report generators need to derive @@ -83,7 +84,7 @@ protected ReportGenerator(ILoggerFactory loggerFactory = null) /// /// a list of s /// - protected IEnumerable LoadPackages(FileInfo modelPath, DirectoryInfo rootDirectory) + protected XmiReaderResult LoadPackages(FileInfo modelPath, DirectoryInfo rootDirectory) { this.logger.LogInformation("Loading UML model from {0}", modelPath.FullName); @@ -92,9 +93,7 @@ protected IEnumerable LoadPackages(FileInfo modelPath, DirectoryInfo r .WithLogger(this.loggerFactory) .Build(); - var packages = reader.Read(modelPath.FullName); - - return packages; + return reader.Read(modelPath.FullName); } } } \ No newline at end of file diff --git a/uml4net.Reporting/Payload/HandlebarsPayload.cs b/uml4net.Reporting/Payload/HandlebarsPayload.cs index a1ad7c9e..ce903e0c 100644 --- a/uml4net.Reporting/Payload/HandlebarsPayload.cs +++ b/uml4net.Reporting/Payload/HandlebarsPayload.cs @@ -29,7 +29,7 @@ namespace uml4net.Reporting.Payload using POCO.StructuredClassifiers; /// - /// represents the payload for the generators that require all , + /// represents the payload for the generators that require all , /// and /// public class HandlebarsPayload @@ -38,34 +38,61 @@ public class HandlebarsPayload /// initializes an instance of the class. /// /// - /// The root of the UML model + /// The Root which is that that was + /// initially loaded /// - /// - /// the s in the UML model + /// + /// The top level s of the UML models + /// + /// + /// the s in the UML models + /// + /// + /// the s in the UML models /// /// - /// the s in the UML model + /// the s in the UML models /// /// - /// the es in the UML model + /// the es in the UML models + /// + /// + /// the es in the UML models /// - public HandlebarsPayload(IPackage rootPackage, IEnumerable enums, IEnumerable dataTypes, IEnumerable classes) + public HandlebarsPayload(IPackage rootPackage, IEnumerable packages, IEnumerable enumerations, + IEnumerable primitiveTypes, IEnumerable dataTypes, IEnumerable classes, + IEnumerable interfaces) { this.RootPackage = rootPackage; - this.Enums = enums.ToArray(); + this.Packages = packages.ToArray(); + this.Enumerations = enumerations.ToArray(); + this.PrimitiveTypes = primitiveTypes.ToArray(); this.DataTypes = dataTypes.ToArray(); this.Classes = classes.ToArray(); + this.Interfaces = interfaces.ToArray(); } /// - /// Gets the root + /// Gets the Root which is that that was + /// initially loaded /// public IPackage RootPackage { get; private set; } + /// + /// Gets the top level s, the shall be contained in the + /// array + /// + public IPackage[] Packages { get; private set; } + /// /// Gets the array of /// - public IEnumeration[] Enums { get; private set; } + public IEnumeration[] Enumerations { get; private set; } + + /// + /// Gets the array of + /// + public IPrimitiveType[] PrimitiveTypes { get; private set; } /// /// Gets the array of @@ -77,6 +104,11 @@ public HandlebarsPayload(IPackage rootPackage, IEnumerable enums, /// public IClass[] Classes { get; private set; } + /// + /// Gets the array of + /// + public IInterface[] Interfaces { get; private set; } + /// /// Gets the version of the reporting library /// diff --git a/uml4net.Reporting/Templates/uml-to-html-docs.hbs b/uml4net.Reporting/Templates/uml-to-html-docs.hbs new file mode 100644 index 00000000..f3a8eb86 --- /dev/null +++ b/uml4net.Reporting/Templates/uml-to-html-docs.hbs @@ -0,0 +1,434 @@ + + + + + + + + + + + + {{ this.RootPackage.Name }} - UML Model Documentation + + +
+
+ +
+
+ + Starion Group + +

Generated with uml4net version {{ this.Version }} on {{ DateTime.Now "yyyy-MM-dd HH:mm:ss" }}

+
+ + + + + + + + + + + + + +
Name:{{ this.RootPackage.Name }}
URI:{{ this.RootPackage.URI }}
+ +

1. Enumeration Types

+ {{#each this.Enumerations as | enumeration |}} +

{{enumeration.Name}}

+

Definition

+ {{ #RawDocumentation enumeration }} +

Enumeration Literals

+ + + + + + + + + {{#each enumeration.OwnedLiteral as | enumerationLiteral |}} + + + + + {{/each}} + +
NameDescription
{{enumerationLiteral.Name}} + {{ #RawDocumentation enumerationLiteral }} +
+ {{/each}} + +

2. Primitive Types

+ {{#each this.PrimitiveTypes as | primitiveType |}} +

{{primitiveType.Name}}

+

Definition

+ {{ #RawDocumentation primitiveType }} + {{/each}} + +

3. Data Types

+ {{#each this.DataTypes as | datatype |}} +

{{datatype.Name}}

+

Definition

+ {{ #RawDocumentation datatype }} + {{/each}} + +

4. Classes

+ {{#each this.Classes as | class |}} + +

{{class.Name}}

+

Definition

+ {{ #RawDocumentation class }} +

Features

+ + + + + + + + + + + + + + + + + + + + + +
NameDescription
Is Abstract {{#if class.IsAbstract}} TRUE {{else}} FALSE {{/if}}
Generalizations + {{#each class.SuperClass as | superClass |}} + {{superClass.Name}} + {{/each}} +
Specializations TBD + +
+

Properties

+ + + + + + + + + + + + {{#each this.QueryAllProperties as | property |}} + + + + + + + + {{/each}} + +
NameTypeDefaultDescriptionInheritance
{{ property.Name }} + {{Property.QueryTypeName property}} [{{ property.Lower }}..{{ property.Upper }}] + {{#if property.IsDerived }} {derived} {{/if}} + {{#if (Property.QueryIsContainment property) }} {composite} {{/if}} + {{ property.DefaultValueLiteral }}{{ #RawDocumentation property }}{{property.Owner.Name}}
+ {{/each}} + + + +

4. Interfaces

+ {{#each this.Interfaces as | interface |}} + +

{{interface.Name}}

+

Definition

+ {{ #RawDocumentation interface }} +

Features

+ + + + + + + + + + + + + +
NameDescription
Generalizations + {{#each interface.ESuperTypes as | superInterface |}} + {{superInterface.Name}} + {{/each}} +
+

Properties

+ + + + + + + + + + + + {{#each this.QueryAllProperties as | property |}} + + + + + + + + {{/each}} + +
NameTypeDefaultDescriptionInheritance
{{ property.Name }} + {{Property.QueryTypeName property}} [{{ property.Lower }}..{{ property.Upper }}] + {{#if property.IsDerived }} {derived} {{/if}} + {{#if (Property.QueryIsContainment property) }} {composite} {{/if}} + {{ property.DefaultValueLiteral }}{{ #RawDocumentation property }}{{property.Owner.Name}}
+ {{/each}} +
+
+
+ + \ No newline at end of file diff --git a/uml4net.Reporting/uml4net.Reporting.csproj b/uml4net.Reporting/uml4net.Reporting.csproj index f0010ed8..16ef625a 100644 --- a/uml4net.Reporting/uml4net.Reporting.csproj +++ b/uml4net.Reporting/uml4net.Reporting.csproj @@ -27,12 +27,19 @@ + + + + + + + + - diff --git a/uml4net.xmi.Tests/SysML2.XmiReaderTestFixture.cs b/uml4net.xmi.Tests/SysML2.XmiReaderTestFixture.cs index 996a878b..b4b34f0d 100644 --- a/uml4net.xmi.Tests/SysML2.XmiReaderTestFixture.cs +++ b/uml4net.xmi.Tests/SysML2.XmiReaderTestFixture.cs @@ -45,11 +45,11 @@ public async Task Verify_that_SysML_XMI_can_be_read() { using var reader = XmiReaderBuilder.Create().WithLogger(this.loggerFactory).Build(); - var packages = reader.Read(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData", "SysML.uml")); + var xmiReaderResult = reader.Read(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData", "SysML.uml")); - Assert.That(packages.Count(), Is.EqualTo(1)); + Assert.That(xmiReaderResult.Packages.Count(), Is.EqualTo(1)); - var model = packages.First() as IModel; + var model = xmiReaderResult.Root as IModel; Assert.That(model.XmiId, Is.EqualTo("_kUROkM9FEe6Zc_le1peNgQ")); Assert.That(model.Name, Is.EqualTo("sysml")); diff --git a/uml4net.xmi.Tests/UMLXmiReaderTestFixture.cs b/uml4net.xmi.Tests/UMLXmiReaderTestFixture.cs index d3c714e0..7debd561 100644 --- a/uml4net.xmi.Tests/UMLXmiReaderTestFixture.cs +++ b/uml4net.xmi.Tests/UMLXmiReaderTestFixture.cs @@ -51,11 +51,11 @@ public void Verify_that_UML_PrimitiveTypes__XMI_can_be_read() .WithLogger(this.loggerFactory) .Build(); - var packages = reader.Read(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData", "PrimitiveTypes.xmi")); + var xmiReaderResult = reader.Read(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData", "PrimitiveTypes.xmi")); - Assert.That(packages.Count(), Is.EqualTo(1)); + Assert.That(xmiReaderResult.Packages.Count, Is.EqualTo(1)); - var package = packages.First(); + var package = xmiReaderResult.Packages.First(); Assert.That(package.XmiId, Is.EqualTo("_0")); Assert.That(package.Name, Is.EqualTo("PrimitiveTypes")); @@ -72,11 +72,11 @@ public void Verify_that_UML_XMI_can_be_read() .WithLogger(this.loggerFactory) .Build(); - var packages = reader.Read(Path.Combine(rootPath, "UML.xmi")); + var xmiReaderResult = reader.Read(Path.Combine(rootPath, "UML.xmi")); - Assert.That(packages.Count(), Is.EqualTo(1)); + Assert.That(xmiReaderResult.Packages.Count(), Is.EqualTo(2)); - var package = packages.First(); + var package = xmiReaderResult.Root; Assert.That(package.XmiId, Is.EqualTo("_0")); Assert.That(package.XmiType, Is.EqualTo("uml:Package")); diff --git a/uml4net.xmi/Readers/IXmiReader.cs b/uml4net.xmi/Readers/IXmiReader.cs index a72a63b3..282c622b 100644 --- a/uml4net.xmi/Readers/IXmiReader.cs +++ b/uml4net.xmi/Readers/IXmiReader.cs @@ -21,10 +21,8 @@ namespace uml4net.xmi.Readers { using System; - using System.Collections.Generic; using System.IO; - using uml4net.POCO.Packages; - + /// /// The purpose of the is to provide a means to read (deserialize) /// an UML 2.5.1 model from XMI @@ -38,10 +36,10 @@ public interface IXmiReader : IDisposable /// The URI of the XMI file to be read. /// /// - /// An representing the deserialized packages from the XMI file. + /// An representing the deserialized packages from the XMI file. /// - IEnumerable Read(string fileUri); - + XmiReaderResult Read(string fileUri); + /// /// Reads the content of a UML XMI 2.5.1 stream. /// @@ -49,8 +47,8 @@ public interface IXmiReader : IDisposable /// The that contains the XMI content to be read. /// /// - /// An representing the deserialized packages from the XMI stream. + /// An representing the deserialized packages from the XMI stream. /// - IEnumerable Read(Stream stream); + XmiReaderResult Read(Stream stream); } } \ No newline at end of file diff --git a/uml4net.xmi/Readers/Packages/ModelReader.cs b/uml4net.xmi/Readers/Packages/ModelReader.cs index 6659125e..2f99ed73 100644 --- a/uml4net.xmi/Readers/Packages/ModelReader.cs +++ b/uml4net.xmi/Readers/Packages/ModelReader.cs @@ -28,6 +28,7 @@ namespace uml4net.xmi.Readers.Packages using POCO; using uml4net.POCO.Packages; using Readers; + using System.IO.Packaging; /// /// The purpose of the is to read an instance of @@ -89,7 +90,10 @@ public override IModel Read(XmlReader xmlReader) model.Name = xmlReader.GetAttribute("name"); + model.URI = xmlReader.GetAttribute("URI"); + model.XmiId = xmlReader.GetAttribute("xmi:id") ?? model.Name; + this.Cache.Add(model.XmiId, model); while (xmlReader.Read()) diff --git a/uml4net.xmi/Readers/Packages/PackageReader.cs b/uml4net.xmi/Readers/Packages/PackageReader.cs index 7f59479c..676c1b93 100644 --- a/uml4net.xmi/Readers/Packages/PackageReader.cs +++ b/uml4net.xmi/Readers/Packages/PackageReader.cs @@ -123,6 +123,8 @@ public override IPackage Read(XmlReader xmlReader) package.Name = xmlReader.GetAttribute("name"); + package.URI = xmlReader.GetAttribute("URI"); + var visibility = xmlReader.GetAttribute("visibility"); if (!string.IsNullOrEmpty(visibility)) { diff --git a/uml4net.xmi/Readers/XmiReader.cs b/uml4net.xmi/Readers/XmiReader.cs index 80a60dbd..7fb16303 100644 --- a/uml4net.xmi/Readers/XmiReader.cs +++ b/uml4net.xmi/Readers/XmiReader.cs @@ -20,19 +20,20 @@ namespace uml4net.xmi.Readers { - using Cache; - using System.Collections.Generic; + using System; using System.Diagnostics; using System.IO; using System.Xml; using Microsoft.Extensions.Logging; - using uml4net.POCO.Packages; - using xmi; + using uml4net.POCO.Packages; + using uml4net.xmi; + using uml4net.xmi.Cache; + /// /// The purpose of the is to provide a means to read (deserialize) - /// an UML 2.5.1 model from XMI + /// a UML 2.5.1 model from XMI /// public class XmiReader : IXmiReader { @@ -98,9 +99,10 @@ public XmiReader(IAssembler assembler, IXmiReaderCache cache, ILogger /// The URI of the XMI file to be read. /// /// - /// An representing the deserialized packages from the XMI file. + /// An that contains the root and other + /// contained s representing contents of XMI file /// - public IEnumerable Read(string fileUri) + public XmiReaderResult Read(string fileUri) { using var fileStream = File.OpenRead(fileUri); @@ -122,11 +124,16 @@ public IEnumerable Read(string fileUri) /// The that contains the XMI content to be read. /// /// - /// An representing the deserialized packages from the XMI stream. + /// An that contains the root and other + /// contained s representing contents of XMI stream /// - public IEnumerable Read(Stream stream) + public XmiReaderResult Read(Stream stream) { - return this.Read(stream, true); + var xmiReaderResult = new XmiReaderResult(); + + this.Read(stream, xmiReaderResult, true); + + return xmiReaderResult; } /// @@ -135,20 +142,23 @@ public IEnumerable Read(Stream stream) /// /// The that contains the XMI content to be read. /// + /// + /// The to which the read s are added + /// /// /// A value indicating whether the reading occurs on the root node. /// /// - /// An representing the deserialized packages from the XMI stream. + /// An that contains the root and other + /// contained s representing contents of XMI stream /// - private IEnumerable Read(Stream stream, bool isRoot) + private void Read(Stream stream, XmiReaderResult xmiReaderResult, bool isRoot) { var settings = new XmlReaderSettings(); stream.Seek(0, SeekOrigin.Begin); var sw = Stopwatch.StartNew(); - var packages = new List(); using (var reader = new StreamReader(stream, detectEncodingFromByteOrderMarks: true)) @@ -160,14 +170,18 @@ private IEnumerable Read(Stream stream, bool isRoot) { if (xmlReader.NodeType == XmlNodeType.Element) { - //TODO: this should probably be a full list of all kinds of UML classes, use codegen switch (xmlReader.Name) { case "uml:Package": using (var packageXmlReader = xmlReader.ReadSubtree()) { var package = this.PackageReader.Read(packageXmlReader); - packages.Add(package); + xmiReaderResult.Packages.Add(package); + + if (isRoot) + { + xmiReaderResult.Root = package; + } } break; @@ -175,9 +189,20 @@ private IEnumerable Read(Stream stream, bool isRoot) using (var modelXmlReader = xmlReader.ReadSubtree()) { var model = this.ModelReader.Read(modelXmlReader); - packages.Add(model); + xmiReaderResult.Packages.Add(model); + + if (isRoot) + { + xmiReaderResult.Root = model; + } } + break; + case "uml:Profile": + using (var profileXmlReader = xmlReader.ReadSubtree()) + { + Console.WriteLine("profileXmlReader not yet implemented"); + } break; } } @@ -188,31 +213,34 @@ private IEnumerable Read(Stream stream, bool isRoot) this.logger.LogTrace("xml read in {time}", currentlyElapsedMilliseconds); sw.Stop(); - this.TryResolveExternalReferences(); + this.TryResolveExternalReferences(xmiReaderResult); if (isRoot) { this.assembler.Synchronize(); } - - return packages; - } /// /// Asynchronously resolves external references and updates the cache with the retrieved resources. /// - private void TryResolveExternalReferences() + /// + /// The to which the read s are added + /// + private void TryResolveExternalReferences(XmiReaderResult xmiReaderResult) { var stopwatch = Stopwatch.StartNew(); + this.logger.LogTrace("resolving the external references"); + foreach (var (context, externalResource) in this.externalReferenceResolver.TryResolve()) { this.cache.SwitchContext(context); - this.Read(externalResource, false); + this.Read(externalResource, xmiReaderResult, false); } this.logger.LogTrace("External references synchronized in {time}", stopwatch.ElapsedMilliseconds); + stopwatch.Stop(); } diff --git a/uml4net.xmi/Readers/XmiReaderResult.cs b/uml4net.xmi/Readers/XmiReaderResult.cs new file mode 100644 index 00000000..a2e74490 --- /dev/null +++ b/uml4net.xmi/Readers/XmiReaderResult.cs @@ -0,0 +1,44 @@ +// ------------------------------------------------------------------------------------------------- +// +// +// Copyright 2019-2024 Starion Group S.A. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, softwareUseCases +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +// ------------------------------------------------------------------------------------------------ + +namespace uml4net.xmi.Readers +{ + using System.Collections.Generic; + + using POCO.Packages; + + /// + /// The purpose of the is capture the result of reading a UML model + /// from an XMI file (which may reference other XMI files or resources) + /// + public class XmiReaderResult + { + /// + /// Gets or sets the root . This is the that is the container + /// of the UML model file that was initially read + /// + public IPackage Root { get; set; } + + /// + /// Gets or sets all the s that have been read, this includes the + /// + public List Packages { get; set; } = new (); + } +} \ No newline at end of file