diff --git a/uml4net.CodeGenerator.Tests/Generators/CorePocoGeneratorTestFixture.cs b/uml4net.CodeGenerator.Tests/Generators/CorePocoGeneratorTestFixture.cs
index dae2549e..30f5c58b 100644
--- a/uml4net.CodeGenerator.Tests/Generators/CorePocoGeneratorTestFixture.cs
+++ b/uml4net.CodeGenerator.Tests/Generators/CorePocoGeneratorTestFixture.cs
@@ -20,15 +20,8 @@
namespace uml4net.CodeGenerator.Tests.Generators
{
- using System.IO;
- using System.Threading.Tasks;
-
- using ECoreNetto;
-
using NUnit.Framework;
- using uml4net.CodeGenerator.Generators;
-
[TestFixture]
public class CorePocoGeneratorTestFixture
{
diff --git a/uml4net.HandleBars/GeneralizationHelper.cs b/uml4net.HandleBars/GeneralizationHelper.cs
index dc6e871e..77063b90 100644
--- a/uml4net.HandleBars/GeneralizationHelper.cs
+++ b/uml4net.HandleBars/GeneralizationHelper.cs
@@ -24,8 +24,6 @@ namespace uml4net.HandleBars
using System.Linq;
using HandlebarsDotNet;
-
- using uml4net.POCO;
using uml4net.POCO.StructuredClassifiers;
///
diff --git a/uml4net.Tests/AssemblerTestFixture.cs b/uml4net.xmi.Tests/AssemblerTestFixture.cs
similarity index 92%
rename from uml4net.Tests/AssemblerTestFixture.cs
rename to uml4net.xmi.Tests/AssemblerTestFixture.cs
index 0371b192..a66bf679 100644
--- a/uml4net.Tests/AssemblerTestFixture.cs
+++ b/uml4net.xmi.Tests/AssemblerTestFixture.cs
@@ -18,33 +18,38 @@
//
// ------------------------------------------------------------------------------------------------
-namespace uml4net.Tests
+namespace uml4net.xmi.Tests
{
+ using Cache;
+ using Microsoft.Extensions.Logging;
using NUnit.Framework;
using System;
- using POCO.CommonStructure;
- using POCO.StructuredClassifiers;
- using POCO.Values;
using System.Collections.Generic;
using uml4net.POCO;
- using Microsoft.Extensions.Logging;
+ using uml4net.POCO.CommonStructure;
+ using uml4net.POCO.StructuredClassifiers;
+ using uml4net.POCO.Values;
[TestFixture]
public class AssemblerTestFixture
{
private Assembler assembler;
- private readonly Dictionary cache = [];
+ private IXmiReaderCache cache;
[SetUp]
public void Setup()
{
- this.assembler = new Assembler(LoggerFactory.Create(builder => builder.AddConsole()));
+ var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
+
+ this.cache = new XmiReaderCache(loggerFactory.CreateLogger());
+
+ this.assembler = new Assembler(loggerFactory.CreateLogger(), this.cache);
}
[TearDown]
public void Teardown()
{
- this.cache.Clear();
+ this.cache.GlobalCache.Clear();
}
[Test]
@@ -66,7 +71,7 @@ public void Synchronize_ShouldSetSingleValueReference()
Assert.That(classElement.NameExpression, Is.Null);
- this.assembler.Synchronize(this.cache);
+ this.assembler.Synchronize();
// Assert
Assert.Multiple(() =>
@@ -97,7 +102,7 @@ public void Synchronize_ShouldSetMultiValueReference()
this.cache.Add(comment1.XmiId, comment1);
this.cache.Add(comment2.XmiId, comment2);
- this.assembler.Synchronize(this.cache);
+ this.assembler.Synchronize();
// Assert
Assert.Multiple(() =>
@@ -135,7 +140,7 @@ public void Synchronize_ShouldSetSingleValueReferenceAndMultipleValuesReference(
Assert.That(classElement.NameExpression, Is.Null);
Assert.That(classElement.OwnedComment.Count, Is.Zero);
- this.assembler.Synchronize(this.cache);
+ this.assembler.Synchronize();
// Assert
Assert.Multiple(() =>
@@ -187,12 +192,11 @@ public void Synchronize_ShouldSetExpectedReferenceToExpectedReference()
Assert.That(classElement0.OwnedComment.Count, Is.Zero);
Assert.That(classElement1.OwnedComment.Count, Is.Zero);
- this.assembler.Synchronize(this.cache);
+ this.assembler.Synchronize();
// Assert
Assert.Multiple(() =>
{
-
Assert.That(classElement0.NameExpression, Is.SameAs(stringExpression));
Assert.That(classElement1.NameExpression, Is.Null);
Assert.That(classElement0.OwnedComment.Count, Is.EqualTo(1));
@@ -217,7 +221,7 @@ public void Synchronize_ShouldThrow_WhenReferenceNotFound()
classElement.SingleValueReferencePropertyIdentifiers.Add("NameExpression", "NonExistentReference");
this.cache.Add(classElement.XmiId, classElement);
- this.assembler.Synchronize(this.cache);
+ this.assembler.Synchronize();
Assert.That(classElement.NameExpression, Is.Not.Null);
}
@@ -239,7 +243,7 @@ public void Synchronize_ShouldThrow_WhenElementNotIReferenceable()
Assert.Multiple(() =>
{
- Assert.Throws(() => this.assembler.Synchronize(this.cache));
+ Assert.Throws(() => this.assembler.Synchronize());
Assert.That(classElement.OwnedComment, Is.Empty);
Assert.That(classElement.NameExpression, Is.Null);
});
diff --git a/uml4net.xmi.Tests/SysML2.XmiReaderTestFixture.cs b/uml4net.xmi.Tests/SysML2.XmiReaderTestFixture.cs
index 96832058..3d7a9d11 100644
--- a/uml4net.xmi.Tests/SysML2.XmiReaderTestFixture.cs
+++ b/uml4net.xmi.Tests/SysML2.XmiReaderTestFixture.cs
@@ -26,8 +26,9 @@ namespace uml4net.xmi.Tests
using Microsoft.Extensions.Logging;
using NUnit.Framework;
-
+ using System.Threading.Tasks;
using uml4net.POCO.Packages;
+ using uml4net.xmi;
public class SysML2XmiReaderTestFixture
{
@@ -40,11 +41,11 @@ public void SetUp()
}
[Test]
- public void Verify_that_SysML_XMI_can_be_read()
+ public async Task Verify_that_SysML_XMI_can_be_read()
{
- var reader = new XmiReader(this.loggerFactory);
+ using var reader = XmiReaderBuilder.Create().WithLogger(this.loggerFactory).Build();
- var packages = reader.Read(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData", "SysML.uml"));
+ var packages = await reader.ReadAsync(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData", "SysML.uml"));
Assert.That(packages.Count(), Is.EqualTo(1));
diff --git a/uml4net.xmi.Tests/UMLXmiReaderTestFixture.cs b/uml4net.xmi.Tests/UMLXmiReaderTestFixture.cs
index 8d9ab835..a48e2ad4 100644
--- a/uml4net.xmi.Tests/UMLXmiReaderTestFixture.cs
+++ b/uml4net.xmi.Tests/UMLXmiReaderTestFixture.cs
@@ -26,12 +26,13 @@ namespace uml4net.xmi.Tests
using Microsoft.Extensions.Logging;
using NUnit.Framework;
-
+ using System.Threading.Tasks;
using uml4net.POCO.Values;
using uml4net.POCO.Packages;
using uml4net.POCO.SimpleClassifiers;
using uml4net.POCO.StructuredClassifiers;
-
+ using uml4net.xmi;
+
[TestFixture]
public class UMLXmiReaderTestFixture
{
@@ -44,11 +45,13 @@ public void SetUp()
}
[Test]
- public void Verify_that_UML_PrimitiveTypes__XMI_can_be_read()
+ public async Task Verify_that_UML_PrimitiveTypes__XMI_can_be_read()
{
- var reader = new XmiReader(this.loggerFactory);
+ using var reader = XmiReaderBuilder.Create()
+ .WithLogger(this.loggerFactory)
+ .Build();
- var packages = reader.Read(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData", "PrimitiveTypes.xmi.xml"));
+ var packages = await reader.ReadAsync(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData", "PrimitiveTypes.xmi.xml"));
Assert.That(packages.Count(), Is.EqualTo(1));
@@ -56,14 +59,17 @@ public void Verify_that_UML_PrimitiveTypes__XMI_can_be_read()
Assert.That(package.XmiId, Is.EqualTo("_0"));
Assert.That(package.Name, Is.EqualTo("PrimitiveTypes"));
+ Assert.That(package.PackagedElement.Count, Is.EqualTo(5));
}
[Test]
- public void Verify_that_UML_XMI_can_be_read()
+ public async Task Verify_that_UML_XMI_can_be_read()
{
- var reader = new XmiReader(this.loggerFactory);
+ using var reader = XmiReaderBuilder.Create()
+ .WithLogger(this.loggerFactory)
+ .Build();
- var packages = reader.Read(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData", "UML.xmi.xml"));
+ var packages = await reader.ReadAsync(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData", "UML.xmi.xml"));
Assert.That(packages.Count(), Is.EqualTo(1));
diff --git a/uml4net/Assembler.cs b/uml4net.xmireader/Cache/Assembler.cs
similarity index 67%
rename from uml4net/Assembler.cs
rename to uml4net.xmireader/Cache/Assembler.cs
index 5d13dc42..e93cac9b 100644
--- a/uml4net/Assembler.cs
+++ b/uml4net.xmireader/Cache/Assembler.cs
@@ -18,55 +18,56 @@
//
// ------------------------------------------------------------------------------------------------
-namespace uml4net
+namespace uml4net.xmi.Cache
{
- using Decorators;
using Microsoft.Extensions.Logging;
- using System.Collections.Generic;
using System;
using System.Collections;
+ using System.Collections.Generic;
using System.Linq;
- using POCO;
using System.Reflection;
- using Microsoft.Extensions.Logging.Abstractions;
+ using Decorators;
+ using POCO;
+ using DocumentFormat.OpenXml.InkML;
///
/// The purpose of the Assembler is to resolve all the reference properties of the objects
/// after deserialization to construct a complete object graph
///
- public class Assembler
+ public class Assembler : IAssembler
{
///
- /// The (injected) used to setup logging
+ /// The used to log
///
- private readonly ILoggerFactory loggerFactory;
+ private readonly ILogger logger;
///
- /// The used to log
+ /// The
///
- private readonly ILogger logger;
+ private readonly IXmiReaderCache cache;
///
- /// Gets
+ /// Initializes a new
///
- ///
- public Assembler(ILoggerFactory loggerFactory)
+ /// The
+ /// The
+ public Assembler(ILogger logger, IXmiReaderCache cache)
{
- this.loggerFactory = loggerFactory;
- this.logger = this.loggerFactory == null ? NullLogger.Instance : this.loggerFactory.CreateLogger();
+ this.logger = logger;
+ this.cache = cache;
}
///
- /// Synchronizes the specified cache by assigning properties to elements.
+ /// Synchronizes the by assigning properties to elements.
///
- ///
- /// A dictionary containing the elements where the keys are their identifiers and the values are the corresponding instances.
- ///
- public void Synchronize(Dictionary cache)
+ public void Synchronize()
{
- foreach (var element in cache.Values)
+ foreach (var contextEntries in this.cache.GlobalCache)
{
- this.ResolveReferences(element, cache);
+ foreach (var element in contextEntries.Value)
+ {
+ this.ResolveReferences(element.Value);
+ }
}
}
@@ -74,15 +75,13 @@ public void Synchronize(Dictionary cache)
/// Resolves single and multi-value references for the given element using the provided cache.
///
/// The element whose references are to be resolved.
- /// The cache containing the referenced elements.
- public void ResolveReferences(IXmiElement element, IDictionary cache)
+ public void ResolveReferences(IXmiElement element)
{
foreach (var property in element.SingleValueReferencePropertyIdentifiers)
{
- var referencedElement = this.GetReferencedElement(cache, property.Value, property.Key);
-
- if (referencedElement is null)
+ if (!this.TryGetReferencedElement(property.Value, out var referencedElement))
{
+ this.logger.LogWarning("The reference to [{reference}] for property [{key}] on element type [{element}] with id [{id}] was not found in the cache, probably because its type is not supported.", property.Value, property.Key, element.XmiType, element.XmiId);
continue;
}
@@ -106,7 +105,7 @@ public void ResolveReferences(IXmiElement element, IDictionary
- /// Retrieves a referenced element from the cache based on the reference key.
+ /// Attempts to retrieve the referenced element associated with the specified reference ID key.
///
- /// The cache containing the referenced elements.
- /// The key to the reference in the cache.
- /// The name of the property referring to the element.
- /// The resolved IXmiElement from the cache.
- private IXmiElement GetReferencedElement(IDictionary cache, string reference, string key)
+ /// The key representing the reference ID.
+ /// When this method returns, contains the referenced element if found; otherwise, null.
+ /// true if the referenced element was successfully retrieved; otherwise, false.
+ private bool TryGetReferencedElement(string referenceIdKey, out IXmiElement element)
{
- if (reference.Contains('#'))
- {
- this.logger.LogWarning("Referencing external type is not yet supported");
- return null;
- }
-
- if (cache.TryGetValue(reference, out var referencedElement))
- {
- return referencedElement;
- }
-
- this.logger.LogWarning("The reference with the id [{reference}] to [{key}] was not found in the cache, probably because its type is not supported.", reference, key);
- return null;
+ return this.cache.TryResolveContext(referenceIdKey, out var resolvedContextAndResource)
+ ? this.cache.TryGetValue(resolvedContextAndResource.Context, resolvedContextAndResource.ResourceId, out element)
+ : this.cache.Cache.TryGetValue(referenceIdKey, out element);
}
///
@@ -151,7 +139,7 @@ private IXmiElement GetReferencedElement(IDictionary cache,
/// The name of the property to find.
/// The expected type of the property. If null, type checking is skipped.
/// The of the found property, or null if no matching property is found.
- private PropertyInfo? FindPropertyWithAttribute(IXmiElement element, string propertyName, Type? expectedType = null)
+ private PropertyInfo FindPropertyWithAttribute(IXmiElement element, string propertyName, Type? expectedType = null)
{
return element.GetType().GetProperties()
.FirstOrDefault(x => Attribute.IsDefined(x, typeof(PropertyAttribute))
@@ -170,13 +158,13 @@ private IXmiElement GetReferencedElement(IDictionary cache,
///
/// Thrown if a reference is not found in the cache or if the type of a referenced element does not match the expected type.
///
- private List ResolveMultiValueReferences(IDictionary cache, IEnumerable propertyValues, string key, Type expectedType)
+ private List ResolveMultiValueReferences(IEnumerable propertyValues, string key, Type expectedType)
{
var resolvedReferences = new List();
foreach (var propertyValue in propertyValues)
{
- if (!cache.TryGetValue(propertyValue, out var referencedElement) || !expectedType.IsAssignableFrom(referencedElement.GetType()))
+ if (!this.TryGetReferencedElement(propertyValue, out var referencedElement) || !expectedType.IsAssignableFrom(referencedElement.GetType()))
{
this.logger.LogWarning("The reference with the id [{key}] to [{propertyValue}] was not found in the cache, probably because its type is not supported.", key, propertyValue);
continue;
diff --git a/uml4net.xmireader/Cache/ExternalReferenceResolver.cs b/uml4net.xmireader/Cache/ExternalReferenceResolver.cs
new file mode 100644
index 00000000..26be7bcf
--- /dev/null
+++ b/uml4net.xmireader/Cache/ExternalReferenceResolver.cs
@@ -0,0 +1,164 @@
+// -------------------------------------------------------------------------------------------------
+//
+//
+// 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.Cache
+{
+ using Microsoft.Extensions.Logging;
+ using Settings;
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using System.Net.Http;
+ using System.Threading.Tasks;
+
+ ///
+ /// Resolves external references for XMI elements using provided settings and cache.
+ ///
+ /// The cache containing XMI reader information.
+ /// The HTTP client used for making requests to external resources.
+ /// The settings for the XMI reader configuration.
+ /// The logger for logging information and errors.
+ public class ExternalReferenceResolver(IXmiReaderCache cache, HttpClient httpClient, IXmiReaderSettings settings, ILogger logger)
+ : IExternalReferenceResolver
+ {
+ ///
+ /// Asynchronously attempts to resolve external references and yields their context and stream.
+ ///
+ /// An asynchronous enumerable of tuples containing the context and stream of resolved references.
+ public async IAsyncEnumerable<(string Context, Stream Stream)> TryResolve()
+ {
+ foreach (var identifier in cache.Cache.Values
+ .SelectMany(cacheEntry => cacheEntry.SingleValueReferencePropertyIdentifiers.Values))
+ {
+ if(await this.TryResolve(identifier) is { Stream: { Length: >0 } } reference)
+ {
+ yield return reference;
+ }
+ }
+
+ foreach (var identifiers in cache.Cache.Values
+ .SelectMany(cacheEntry => cacheEntry.MultiValueReferencePropertyIdentifiers.Values))
+ {
+ foreach (var identifier in identifiers)
+ {
+ if (await this.TryResolve(identifier) is { Stream: { Length: > 0 } } reference)
+ {
+ yield return reference;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Asynchronously attempts to resolve an external reference identified by the specified key.
+ ///
+ /// The key representing the external reference to resolve.
+ /// A task that represents the asynchronous operation, containing a tuple with the context and stream if resolved; otherwise, a default value.
+ private async Task<(string Context, Stream Stream)> TryResolve(string key)
+ {
+ if (string.IsNullOrEmpty(key) || key.StartsWith('#') || !key.Contains('#') ||
+ !cache.TryResolveContext(key, out var resource))
+ {
+ logger.LogInformation("Invalid external resource key [{key}]", key);
+ return default;
+ }
+
+ if (cache.DoesContextExists(resource.Context, resource.ResourceId))
+ {
+ logger.LogInformation("The resource {resource} was already parsed", resource.Context);
+ return default;
+ }
+
+ try
+ {
+ var stream = default(Stream);
+
+ if (Uri.TryCreate(key, UriKind.Absolute, out var uri))
+ {
+ if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)
+ {
+ stream = await this.FetchRemoteXmi(uri);
+ }
+ if (uri.Scheme == Uri.UriSchemeFile)
+ {
+ stream = File.OpenRead(uri.LocalPath);
+ }
+ }
+ else if (key.StartsWith("pathmap://"))
+ {
+ stream = this.ResolvePathmapResource(key);
+ }
+ else if (File.Exists(resource.Context))
+ {
+ stream = File.OpenRead(resource.Context);
+ }
+ else
+ {
+ logger.LogError("Unsupported external reference specified by {context}", resource.Context);
+ return default;
+ }
+
+ return (resource.Context, stream);
+ }
+ catch (Exception exception)
+ {
+ logger.LogError(exception,"Error resolving key '{key}': {message}", key, exception.Message);
+ }
+
+ return default;
+ }
+
+ ///
+ /// Asynchronously fetches an XMI file from a remote URI.
+ ///
+ /// The URI of the remote XMI file to fetch.
+ /// A task that represents the asynchronous operation, containing a stream of the fetched XMI file if successful; otherwise, null.
+ private async Task FetchRemoteXmi(Uri uri)
+ {
+ try
+ {
+ var response = await httpClient.GetAsync(uri);
+
+ if (response.IsSuccessStatusCode)
+ {
+ return await response.Content.ReadAsStreamAsync();
+ }
+ }
+ catch (Exception exception)
+ {
+ logger.LogError(exception, "Error fetching remote XMI: {message}", exception.Message);
+ }
+
+ return default;
+ }
+
+ ///
+ /// Resolves the file path of a resource identified by the specified key and returns a stream for it.
+ ///
+ /// The key representing the resource path to resolve.
+ /// A stream of the resource if the file exists; otherwise, null.
+ private Stream ResolvePathmapResource(string key)
+ {
+ var mappedFilePath = key.Replace("pathmap://UML_PROFILES/", settings.UmlProfilesDirectoryPath);
+ return File.Exists(mappedFilePath) ? File.OpenRead(mappedFilePath) : null;
+ }
+ }
+}
diff --git a/uml4net.xmireader/Cache/IAssembler.cs b/uml4net.xmireader/Cache/IAssembler.cs
new file mode 100644
index 00000000..760ec2dc
--- /dev/null
+++ b/uml4net.xmireader/Cache/IAssembler.cs
@@ -0,0 +1,36 @@
+// -------------------------------------------------------------------------------------------------
+//
+//
+// 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.Cache
+{
+ using System.Collections.Generic;
+ using uml4net.POCO;
+
+ ///
+ /// The is the interface definition for the
+ ///
+ public interface IAssembler
+ {
+ ///
+ /// Synchronizes the by assigning properties to elements.
+ ///
+ void Synchronize();
+ }
+}
diff --git a/uml4net.xmireader/Cache/IExternalReferenceResolver.cs b/uml4net.xmireader/Cache/IExternalReferenceResolver.cs
new file mode 100644
index 00000000..7d43fbba
--- /dev/null
+++ b/uml4net.xmireader/Cache/IExternalReferenceResolver.cs
@@ -0,0 +1,37 @@
+// -------------------------------------------------------------------------------------------------
+//
+//
+// 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.Cache
+{
+ using System.Collections.Generic;
+ using System.IO;
+
+ ///
+ /// Defines a contract for resolving external references associated with XMI elements.
+ ///
+ public interface IExternalReferenceResolver
+ {
+ ///
+ /// Asynchronously attempts to resolve external references and yields their context and stream.
+ ///
+ /// An asynchronous enumerable of tuples containing the context and stream of resolved references.
+ IAsyncEnumerable<(string Context, Stream Stream)> TryResolve();
+ }
+}
diff --git a/uml4net.xmireader/Cache/IXmiReaderCache.cs b/uml4net.xmireader/Cache/IXmiReaderCache.cs
new file mode 100644
index 00000000..32b9ebb5
--- /dev/null
+++ b/uml4net.xmireader/Cache/IXmiReaderCache.cs
@@ -0,0 +1,103 @@
+// -------------------------------------------------------------------------------------------------
+//
+//
+// 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.Cache
+{
+ using POCO;
+
+ using System.Collections.Concurrent;
+ using System.Collections.Generic;
+
+ ///
+ /// Represents a cache for storing and retrieving XMI elements during the reading process.
+ ///
+ public interface IXmiReaderCache
+ {
+ ///
+ /// Provides a cache for storing XMI elements with unique IDs, organized by context.
+ /// Each is stored in a nested dictionary keyed by its context
+ /// (representing the XMI file) and the element’s unique identifier within that context.
+ ///
+ ConcurrentDictionary> GlobalCache { get; }
+
+ ///
+ /// Gets the collection of XMI elements associated with the current context. This
+ /// dictionary maps unique identifiers to their corresponding instances
+ /// within the active context.
+ ///
+ Dictionary Cache { get; }
+
+ ///
+ /// Switches the current context to a new XMI file or section, allowing elements to be stored
+ /// under a distinct key in the cache. Initializes an empty dictionary in
+ /// for the specified context if it does not exist.
+ ///
+ /// The unique identifier for the new context, typically the XMI file name.
+ void SwitchContext(string context);
+
+ ///
+ /// Adds the specified XMI element to the cache under the current context.
+ /// If the context does not already exist, the default one is used. It can be changed via .
+ ///
+ /// The unique identifier of the XMI element within the current context.
+ /// The XMI element to be added to the cache.
+ void Add(string id, IXmiElement element);
+
+ ///
+ /// Checks whether the specified context exists in the cache and optionally verifies the existence of a specific key within that context.
+ ///
+ ///
+ /// The context name to check within the cache.
+ ///
+ ///
+ /// (Optional) The specific key to check within the specified context. If null, the method only verifies if the context exists
+ /// and contains any entries.
+ ///
+ ///
+ /// true if the context is missing, contains no entries, or if a specified key exists in the context with a non-null value;
+ /// otherwise, false.
+ ///
+ bool DoesContextExists(string context, string key = null);
+
+ ///
+ /// Attempts to retrieve an instance from the cache
+ /// based on the specified and .
+ /// Returns true if both the context and key exist in the cache, otherwise false.
+ ///
+ /// The context in which the element is stored, typically representing an XMI file.
+ /// The unique identifier of the XMI element within the specified context.
+ ///
+ /// When this method returns, contains the associated with the specified
+ /// context and key, if found; otherwise, default.
+ ///
+ ///
+ /// true if the element is found in the cache with the specified context and key; otherwise, false.
+ ///
+ bool TryGetValue(string context, string key, out IXmiElement value);
+
+ ///
+ /// Attempts to resolve the context and resource ID from the specified resource key.
+ ///
+ /// The key representing the resource, which may contain context and resource ID separated by '#'.
+ /// When this method returns, contains a tuple with the context and resource ID if resolved successfully; otherwise, (null, null).
+ /// true if the context and resource ID were successfully resolved; otherwise, false.
+ bool TryResolveContext(string resourceKey, out (string Context, string ResourceId) resolvedContextAndResource);
+ }
+}
diff --git a/uml4net.xmireader/Cache/XmiReaderCache.cs b/uml4net.xmireader/Cache/XmiReaderCache.cs
new file mode 100644
index 00000000..69b84f01
--- /dev/null
+++ b/uml4net.xmireader/Cache/XmiReaderCache.cs
@@ -0,0 +1,166 @@
+// -------------------------------------------------------------------------------------------------
+//
+//
+// 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.Cache
+{
+ using Microsoft.Extensions.Logging;
+ using System.Collections.Generic;
+
+ using POCO;
+ using System.Collections.Concurrent;
+ using System;
+
+ ///
+ /// A cache specifically designed for XMI elements, organized by context, to facilitate
+ /// efficient lookups and storage during the reading of XMI files. This class provides methods
+ /// to switch contexts, manage external references, and store elements for each context.
+ ///
+ ///
+ /// Each context typically corresponds to an individual XMI file, enabling the cache to
+ /// organize elements on a per-file basis. Resolved external references are also tracked
+ /// to prevent repeated processing of the same references.
+ ///
+ internal class XmiReaderCache(ILogger logger) : IXmiReaderCache
+ {
+ private const string DefaultKey = "_";
+
+ ///
+ /// Provides a cache for storing XMI elements with unique IDs, organized by context.
+ /// Each is stored in a nested dictionary keyed by its context
+ /// (representing the XMI file) and the element’s unique identifier within that context.
+ ///
+ public ConcurrentDictionary> GlobalCache { get; } = [];
+
+ ///
+ /// Gets the collection of XMI elements associated with the current context. This
+ /// dictionary maps unique identifiers to their corresponding instances
+ /// within the active context.
+ ///
+ public Dictionary Cache => this.GlobalCache.GetOrAdd(this.currentContext, []);
+
+ ///
+ /// Holds the current context, typically representing the current XMI file being processed.
+ /// This context helps to group elements by their file of origin in the cache.
+ ///
+ private string currentContext = DefaultKey;
+
+ ///
+ /// Checks whether the specified context exists in the cache and optionally verifies the existence of a specific key within that context.
+ ///
+ ///
+ /// The context name to check within the cache.
+ ///
+ ///
+ /// (Optional) The specific key to check within the specified context. If null, the method only verifies if the context exists
+ /// and contains any entries.
+ ///
+ ///
+ /// true if the context is missing, contains no entries, or if a specified key exists in the context with a non-null value;
+ /// otherwise, false.
+ ///
+ public bool DoesContextExists(string context, string key = null)
+ {
+ if (!this.GlobalCache.TryGetValue(context, out var contextDictionary) || contextDictionary.Count == 0)
+ {
+ return false;
+ }
+
+ return key switch
+ {
+ null => true,
+ _ => contextDictionary.TryGetValue(key, out var value) && value != null
+ };
+ }
+
+ ///
+ /// Attempts to retrieve an instance from the cache
+ /// based on the specified and .
+ /// Returns true if both the context and key exist in the cache, otherwise false.
+ ///
+ /// The context in which the element is stored, typically representing an XMI file.
+ /// The unique identifier of the XMI element within the specified context.
+ ///
+ /// When this method returns, contains the associated with the specified
+ /// context and key, if found; otherwise, default.
+ ///
+ ///
+ /// true if the element is found in the cache with the specified context and key; otherwise, false.
+ ///
+ public bool TryGetValue(string context, string key, out IXmiElement value)
+ {
+ if (this.GlobalCache.TryGetValue(context, out var contextDictionary)
+ && contextDictionary.TryGetValue(key, out value))
+ {
+ return true;
+ }
+
+ value = default;
+ return false;
+ }
+
+ ///
+ /// Switches the current context to a new XMI file or section, allowing elements to be stored
+ /// under a distinct key in the cache. Initializes an empty dictionary in
+ /// for the specified context if it does not exist.
+ ///
+ /// The unique identifier for the new context, typically the XMI file name.
+ public void SwitchContext(string context)
+ {
+ this.currentContext = context;
+ this.GlobalCache.GetOrAdd(context, []);
+ }
+
+ ///
+ /// Adds the specified XMI element to the cache under the current context.
+ /// If the context does not already exist, it must be set via .
+ ///
+ /// The unique identifier of the XMI element within the current context.
+ /// The XMI element to be added to the cache.
+ public void Add(string id, IXmiElement element)
+ {
+ var result = this.Cache.TryAdd(id, element);
+
+ if (!result)
+ {
+ logger.LogError("Failed to add element type [{element}] with id [{id}]", element.GetType().Name, id);
+ }
+ }
+
+ ///
+ /// Attempts to resolve the context and resource ID from the specified resource key.
+ ///
+ /// The key representing the resource, which may contain context and resource ID separated by '#'.
+ /// When this method returns, contains a tuple with the context and resource ID if resolved successfully; otherwise, (null, null).
+ /// true if the context and resource ID were successfully resolved; otherwise, false.
+ public bool TryResolveContext(string resourceKey, out (string Context, string ResourceId) resolvedContextAndResource)
+ {
+ var referenceString = resourceKey.Split('#', StringSplitOptions.RemoveEmptyEntries);
+
+ if (referenceString.Length == 2)
+ {
+ resolvedContextAndResource = (referenceString[0], referenceString[1]);
+ return true;
+ }
+
+ resolvedContextAndResource = (null, null);
+ return false;
+ }
+ }
+}
diff --git a/uml4net.xmireader/IXmiReaderScope.cs b/uml4net.xmireader/IXmiReaderScope.cs
new file mode 100644
index 00000000..273ad937
--- /dev/null
+++ b/uml4net.xmireader/IXmiReaderScope.cs
@@ -0,0 +1,29 @@
+// -------------------------------------------------------------------------------------------------
+//
+//
+// 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
+{
+ using System;
+
+ ///
+ /// Represents a scope for managing the lifecycle of services used during the XMI reading process.
+ ///
+ public interface IXmiReaderScope : IDisposable;
+}
diff --git a/uml4net.xmireader/Classification/GeneralizationReader.cs b/uml4net.xmireader/Readers/Classification/GeneralizationReader.cs
similarity index 76%
rename from uml4net.xmireader/Classification/GeneralizationReader.cs
rename to uml4net.xmireader/Readers/Classification/GeneralizationReader.cs
index f3243de9..9b407fa5 100644
--- a/uml4net.xmireader/Classification/GeneralizationReader.cs
+++ b/uml4net.xmireader/Readers/Classification/GeneralizationReader.cs
@@ -18,41 +18,34 @@
//
// ------------------------------------------------------------------------------------------------
-namespace uml4net.xmi.Classification
+namespace uml4net.xmi.Readers.Classification
{
- using System.Collections.Generic;
using System.Xml;
using Microsoft.Extensions.Logging;
- using Microsoft.Extensions.Logging.Abstractions;
-
- using uml4net.POCO;
+ using POCO;
using uml4net.POCO.Classification;
+ using Cache;
+ using Readers;
///
/// The purpose of the is to read an instance of
/// from the XMI document
///
- public class GeneralizationReader : XmiElementReader
+ public class GeneralizationReader : XmiElementReader, IXmiElementReader
{
- ///
- /// The used to log
- ///
- private readonly ILogger logger;
-
///
/// Initializes a new instance of the class.
///
///
/// The cache in which each > is stored
///
- ///
- /// The (injected) used to setup logging
+ ///
+ /// The
///
- public GeneralizationReader(Dictionary cache, ILoggerFactory loggerFactory = null)
- : base(cache, loggerFactory)
+ public GeneralizationReader(IXmiReaderCache cache, ILogger logger)
+ : base(cache, logger)
{
- this.logger = this.loggerFactory == null ? NullLogger.Instance : this.loggerFactory.CreateLogger();
}
///
@@ -64,7 +57,7 @@ public GeneralizationReader(Dictionary cache, ILoggerFactor
///
/// an instance of
///
- public IGeneralization Read(XmlReader xmlReader)
+ public override IGeneralization Read(XmlReader xmlReader)
{
IGeneralization generalization = new Generalization();
@@ -81,7 +74,7 @@ public IGeneralization Read(XmlReader xmlReader)
generalization.XmiId = xmlReader.GetAttribute("xmi:id");
- this.cache.Add(generalization.XmiId, generalization);
+ this.Cache.Add(generalization.XmiId, generalization);
var isSubstitutable = xmlReader.GetAttribute("isSubstitutable");
if (!string.IsNullOrEmpty(isSubstitutable))
@@ -99,4 +92,4 @@ public IGeneralization Read(XmlReader xmlReader)
return generalization;
}
}
-}
\ No newline at end of file
+}
diff --git a/uml4net.xmireader/Classification/PropertyReader.cs b/uml4net.xmireader/Readers/Classification/PropertyReader.cs
similarity index 82%
rename from uml4net.xmireader/Classification/PropertyReader.cs
rename to uml4net.xmireader/Readers/Classification/PropertyReader.cs
index 60fadff1..f455facf 100644
--- a/uml4net.xmireader/Classification/PropertyReader.cs
+++ b/uml4net.xmireader/Readers/Classification/PropertyReader.cs
@@ -18,32 +18,36 @@
//
// ------------------------------------------------------------------------------------------------
-namespace uml4net.xmi.Classification
+namespace uml4net.xmi.Readers.Classification
{
+ using Cache;
using System;
using System.Collections.Generic;
using System.Xml;
using Microsoft.Extensions.Logging;
- using Microsoft.Extensions.Logging.Abstractions;
-
- using uml4net.POCO;
+ using POCO.Values;
+ using POCO;
using uml4net.POCO.Classification;
using uml4net.POCO.CommonStructure;
-
- using uml4net.xmi.CommonStructure;
- using uml4net.xmi.Values;
+
+ using Readers;
///
/// The purpose of the is to read an instance of
/// from the XMI document
///
- public class PropertyReader : XmiElementReader
+ public class PropertyReader : XmiCommentedElementReader, IXmiElementReader
{
///
- /// The used to log
+ /// The of
+ ///
+ private readonly IXmiElementReader literalIntegerReader;
+
+ ///
+ /// The of
///
- private readonly ILogger logger;
+ private readonly IXmiElementReader literalUnlimitedNaturalReader;
///
/// Initializes a new instance of the class.
@@ -51,13 +55,18 @@ public class PropertyReader : XmiElementReader
///
/// The cache in which each > is stored
///
- ///
- /// The (injected) used to setup logging
+ ///
+ /// The (injected) used to setup logging
///
- public PropertyReader(Dictionary cache, ILoggerFactory loggerFactory = null)
- : base(cache, loggerFactory)
+ /// The of
+ /// The of
+ /// The of
+ public PropertyReader(IXmiReaderCache cache, ILogger logger, IXmiElementReader commentReader,
+ IXmiElementReader literalIntegerReader, IXmiElementReader literalUnlimitedNaturalReader)
+ : base(cache, logger, commentReader)
{
- this.logger = this.loggerFactory == null ? NullLogger.Instance : this.loggerFactory.CreateLogger();
+ this.literalIntegerReader = literalIntegerReader;
+ this.literalUnlimitedNaturalReader = literalUnlimitedNaturalReader;
}
///
@@ -69,7 +78,7 @@ public PropertyReader(Dictionary cache, ILoggerFactory logg
///
/// an instance of
///
- public IProperty Read(XmlReader xmlReader)
+ public override IProperty Read(XmlReader xmlReader)
{
IProperty property = new Property();
@@ -86,7 +95,7 @@ public IProperty Read(XmlReader xmlReader)
property.XmiId = xmlReader.GetAttribute("xmi:id");
- this.cache.Add(property.XmiId, property);
+ this.Cache.Add(property.XmiId, property);
property.Name = xmlReader.GetAttribute("name");
@@ -141,7 +150,7 @@ public IProperty Read(XmlReader xmlReader)
var aggregation = xmlReader.GetAttribute("aggregation");
if (!string.IsNullOrEmpty(aggregation))
{
- property.Aggregation = (AggregationKind)Enum.Parse(typeof(AggregationKind), aggregation,true);
+ property.Aggregation = (AggregationKind)Enum.Parse(typeof(AggregationKind), aggregation, true);
}
var visibility = xmlReader.GetAttribute("visibility");
@@ -165,31 +174,28 @@ public IProperty Read(XmlReader xmlReader)
case "ownedComment":
using (var ownedCommentXmlReader = xmlReader.ReadSubtree())
{
- var commentReader = new CommentReader(this.cache, this.loggerFactory);
- var comment = commentReader.Read(ownedCommentXmlReader);
+ var comment = this.CommentReader.Read(ownedCommentXmlReader);
property.OwnedComment.Add(comment);
}
break;
case "lowerValue":
using (var lowerValueXmlReader = xmlReader.ReadSubtree())
{
- var literalIntegerReader = new LiteralIntegerReader(this.cache, this.loggerFactory);
- var literalInteger = literalIntegerReader.Read(lowerValueXmlReader);
+ var literalInteger = this.literalIntegerReader.Read(lowerValueXmlReader);
property.LowerValue = literalInteger;
}
break;
case "upperValue":
using (var upperValueXmlReader = xmlReader.ReadSubtree())
{
- var literalUnlimitedNaturalReader = new LiteralUnlimitedNaturalReader(this.cache, this.loggerFactory);
- var literalUnlimitedNatural = literalUnlimitedNaturalReader.Read(upperValueXmlReader);
+ var literalUnlimitedNatural = this.literalUnlimitedNaturalReader.Read(upperValueXmlReader);
property.UpperValue = literalUnlimitedNatural;
}
break;
case "nameExpression":
using (var nameExpressionXmlReader = xmlReader.ReadSubtree())
{
- this.logger.LogDebug("property:nameExpression not yet implemented");
+ this.Logger.LogDebug("property:nameExpression not yet implemented");
}
break;
case "subsettedProperty":
@@ -236,13 +242,13 @@ public IProperty Read(XmlReader xmlReader)
case "defaultValue":
using (var defaultValueXmlReader = xmlReader.ReadSubtree())
{
- this.logger.LogDebug("property:defaultValueXmlReader not yet implemented");
+ this.Logger.LogDebug("property:defaultValueXmlReader not yet implemented");
}
break;
case "redefinedProperty":
using (var redefinedPropertyXmlReader = xmlReader.ReadSubtree())
{
- this.logger.LogDebug("property:redefinedProperty not yet implemented");
+ this.Logger.LogDebug("property:redefinedProperty not yet implemented");
}
break;
default:
@@ -255,4 +261,4 @@ public IProperty Read(XmlReader xmlReader)
return property;
}
}
-}
\ No newline at end of file
+}
diff --git a/uml4net.xmireader/CommonStructure/CommentReader.cs b/uml4net.xmireader/Readers/CommonStructure/CommentReader.cs
similarity index 74%
rename from uml4net.xmireader/CommonStructure/CommentReader.cs
rename to uml4net.xmireader/Readers/CommonStructure/CommentReader.cs
index 06fa39f7..518358f3 100644
--- a/uml4net.xmireader/CommonStructure/CommentReader.cs
+++ b/uml4net.xmireader/Readers/CommonStructure/CommentReader.cs
@@ -18,41 +18,35 @@
//
// ------------------------------------------------------------------------------------------------
-namespace uml4net.xmi.CommonStructure
+namespace uml4net.xmi.Readers.CommonStructure
{
- using System.Collections.Generic;
using System.Xml;
using Microsoft.Extensions.Logging;
- using Microsoft.Extensions.Logging.Abstractions;
-
- using uml4net.POCO;
+ using POCO;
+ using uml4net.POCO.Classification;
using uml4net.POCO.CommonStructure;
+ using Cache;
+ using Readers;
///
/// The purpose of the is to read an instance of
/// from the XMI document
///
- public class CommentReader : XmiElementReader
+ public class CommentReader : XmiElementReader, IXmiElementReader
{
- ///
- /// The used to log
- ///
- private readonly ILogger logger;
-
///
/// Initializes a new instance of the class.
///
///
/// The cache in which each > is stored
///
- ///
- /// The (injected) used to setup logging
+ ///
+ /// The (injected) used to setup logging
///
- public CommentReader(Dictionary cache, ILoggerFactory loggerFactory = null)
- : base(cache, loggerFactory)
+ public CommentReader(IXmiReaderCache cache, ILogger logger)
+ : base(cache, logger)
{
- this.logger = this.loggerFactory == null ? NullLogger.Instance : this.loggerFactory.CreateLogger();
}
///
@@ -64,7 +58,7 @@ public CommentReader(Dictionary cache, ILoggerFactory logge
///
/// an instance of
///
- public IComment Read(XmlReader xmlReader)
+ public override IComment Read(XmlReader xmlReader)
{
IComment comment = new Comment();
@@ -81,7 +75,7 @@ public IComment Read(XmlReader xmlReader)
comment.XmiId = xmlReader.GetAttribute("xmi:id");
- this.cache.Add(comment.XmiId, comment);
+ this.Cache.Add(comment.XmiId, comment);
comment.Body = xmlReader.GetAttribute("body");
}
diff --git a/uml4net.xmireader/CommonStructure/ConstraintReader.cs b/uml4net.xmireader/Readers/CommonStructure/ConstraintReader.cs
similarity index 74%
rename from uml4net.xmireader/CommonStructure/ConstraintReader.cs
rename to uml4net.xmireader/Readers/CommonStructure/ConstraintReader.cs
index ba82cf2b..fabc3059 100644
--- a/uml4net.xmireader/CommonStructure/ConstraintReader.cs
+++ b/uml4net.xmireader/Readers/CommonStructure/ConstraintReader.cs
@@ -18,46 +18,45 @@
//
// ------------------------------------------------------------------------------------------------
-namespace uml4net.xmi.CommonStructure
+namespace uml4net.xmi.Readers.CommonStructure
{
+ using Cache;
using System;
- using System.Collections.Generic;
using System.Xml;
using Microsoft.Extensions.Logging;
- using Microsoft.Extensions.Logging.Abstractions;
-
- using uml4net.POCO;
+ using POCO.Values;
+ using POCO;
using uml4net.POCO.CommonStructure;
- using uml4net.xmi.Values;
+ using Readers;
///
/// The purpose of the is to read an instance of
/// from the XMI document
///
- public class ConstraintReader : XmiElementReader
+ public class ConstraintReader : XmiCommentedElementReader, IXmiElementReader
{
///
- /// The used to log
+ /// The of
///
- private readonly ILogger logger;
+ private readonly IXmiElementReader opaqueExpressionReader;
///
/// Initializes a new instance of the class.
///
///
- /// The cache in which each > is stored
+ /// The cache in which each > are stored
///
- ///
- /// The (injected) used to setup logging
+ ///
+ /// The
///
- public ConstraintReader(Dictionary cache, ILoggerFactory loggerFactory = null)
- : base(cache, loggerFactory)
+ /// The of
+ /// The of
+ public ConstraintReader(IXmiReaderCache cache, ILogger logger, IXmiElementReader commentReader, IXmiElementReader opaqueExpressionReader)
+ : base(cache, logger, commentReader)
{
- this.logger = this.loggerFactory == null
- ? NullLogger.Instance
- : this.loggerFactory.CreateLogger();
+ this.opaqueExpressionReader = opaqueExpressionReader;
}
///
@@ -69,7 +68,7 @@ public ConstraintReader(Dictionary cache, ILoggerFactory lo
///
/// an instance of
///
- public IConstraint Read(XmlReader xmlReader)
+ public override IConstraint Read(XmlReader xmlReader)
{
IConstraint constraint = new Constraint();
@@ -86,7 +85,7 @@ public IConstraint Read(XmlReader xmlReader)
constraint.XmiId = xmlReader.GetAttribute("xmi:id");
- this.cache.Add(constraint.XmiId, constraint);
+ this.Cache.Add(constraint.XmiId, constraint);
while (xmlReader.Read())
{
@@ -95,13 +94,12 @@ public IConstraint Read(XmlReader xmlReader)
switch (xmlReader.LocalName)
{
case "constrainedElement":
- this.logger.LogInformation("ConstraintReader.ownedRule not yet implemented");
+ this.Logger.LogInformation("ConstraintReader.ownedRule not yet implemented");
break;
case "ownedComment":
using (var ownedCommentXmlReader = xmlReader.ReadSubtree())
{
- var commentReader = new CommentReader(this.cache, this.loggerFactory);
- var comment = commentReader.Read(ownedCommentXmlReader);
+ var comment = this.CommentReader.Read(ownedCommentXmlReader);
constraint.OwnedComment.Add(comment);
}
break;
@@ -136,8 +134,7 @@ private void ReadValueSpecification(IConstraint constraint, XmlReader xmlReader)
case "uml:OpaqueExpression":
using (var opaqueExpressionXmlReader = xmlReader.ReadSubtree())
{
- var opaqueExpressionReader = new OpaqueExpressionReader(this.cache, this.loggerFactory);
- var opaqueExpression = opaqueExpressionReader.Read(opaqueExpressionXmlReader);
+ var opaqueExpression = this.opaqueExpressionReader.Read(opaqueExpressionXmlReader);
constraint.Specification = opaqueExpression;
}
break;
diff --git a/uml4net.xmireader/CommonStructure/PackageImportReader.cs b/uml4net.xmireader/Readers/CommonStructure/PackageImportReader.cs
similarity index 75%
rename from uml4net.xmireader/CommonStructure/PackageImportReader.cs
rename to uml4net.xmireader/Readers/CommonStructure/PackageImportReader.cs
index 1ada1b58..fae9d94a 100644
--- a/uml4net.xmireader/CommonStructure/PackageImportReader.cs
+++ b/uml4net.xmireader/Readers/CommonStructure/PackageImportReader.cs
@@ -18,42 +18,35 @@
//
// ------------------------------------------------------------------------------------------------
-namespace uml4net.xmi.CommonStructure
+namespace uml4net.xmi.Readers.CommonStructure
{
+ using Cache;
using System;
- using System.Collections.Generic;
using System.Xml;
using Microsoft.Extensions.Logging;
- using Microsoft.Extensions.Logging.Abstractions;
-
- using uml4net.POCO;
+ using POCO;
using uml4net.POCO.CommonStructure;
+ using Readers;
///
/// The purpose of the is to read an instance of
/// from the XMI document
///
- public class PackageImportReader : XmiElementReader
+ public class PackageImportReader : XmiElementReader, IXmiElementReader
{
- ///
- /// The used to log
- ///
- private readonly ILogger logger;
-
///
/// Initializes a new instance of the class.
///
///
/// The cache in which each > is stored
///
- ///
- /// The (injected) used to setup logging
+ ///
+ /// The
///
- public PackageImportReader(Dictionary cache, ILoggerFactory loggerFactory = null)
- : base(cache, loggerFactory)
+ public PackageImportReader(IXmiReaderCache cache, ILogger logger)
+ : base(cache, logger)
{
- this.logger = this.loggerFactory == null ? NullLogger.Instance : this.loggerFactory.CreateLogger();
}
///
@@ -65,8 +58,8 @@ public PackageImportReader(Dictionary cache, ILoggerFactory
///
/// an instance of
///
- public IPackageImport Read(XmlReader xmlReader)
- {
+ public override IPackageImport Read(XmlReader xmlReader)
+ {
var packageImport = new PackageImport();
if (xmlReader.MoveToContent() == XmlNodeType.Element)
@@ -80,7 +73,7 @@ public IPackageImport Read(XmlReader xmlReader)
}
// TODO: figure out an algorithm to interpret how to set the "importingNamespace" property
- this.logger.LogInformation("TODO: figure out an algorithm to interpret how to set the \"importingNamespace\" property");
+ this.Logger.LogInformation("TODO: figure out an algorithm to interpret how to set the \"importingNamespace\" property");
packageImport.ImportingNamespace = null;
string visibility = xmlReader.GetAttribute("visibility");
diff --git a/uml4net.xmireader/Readers/IXmiElementReader.cs b/uml4net.xmireader/Readers/IXmiElementReader.cs
new file mode 100644
index 00000000..864dd3a2
--- /dev/null
+++ b/uml4net.xmireader/Readers/IXmiElementReader.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 POCO;
+ using System.Xml;
+
+ ///
+ /// Defines a contract for reading objects
+ /// from their XML representation.
+ ///
+ /// The type of the XMI element to be read.
+ public interface IXmiElementReader where TXmiElement : IXmiElement
+ {
+ ///
+ /// Reads the object from its XML representation
+ ///
+ ///
+ /// an instance of
+ ///
+ ///
+ /// an instance of
+ ///
+ public TXmiElement Read(XmlReader xmlReader);
+ }
+}
diff --git a/uml4net.xmireader/IXmiReader.cs b/uml4net.xmireader/Readers/IXmiReader.cs
similarity index 66%
rename from uml4net.xmireader/IXmiReader.cs
rename to uml4net.xmireader/Readers/IXmiReader.cs
index d95b724f..b4a02e5f 100644
--- a/uml4net.xmireader/IXmiReader.cs
+++ b/uml4net.xmireader/Readers/IXmiReader.cs
@@ -18,39 +18,40 @@
//
// ------------------------------------------------------------------------------------------------
-namespace uml4net.xmi
+namespace uml4net.xmi.Readers
{
+ using System;
using System.Collections.Generic;
using System.IO;
-
+ using System.Threading.Tasks;
using uml4net.POCO.Packages;
///
/// The purpose of the is to provide a means to read (deserialize)
/// an UML 2.5.1 model from XMI
///
- public interface IXmiReader
+ public interface IXmiReader : IDisposable
{
///
- /// reads the content of a UML XMI 2.5.1 file
+ /// Reads the content of a UML XMI 2.5.1 file asynchronously.
///
///
- /// the path to the XMI file
+ /// The URI of the XMI file to be read.
///
///
- /// An
+ /// An representing the deserialized packages from the XMI file.
///
- IEnumerable Read(string fileUri);
-
+ Task> ReadAsync(string fileUri);
+
///
- /// reads the content of a UML XMI 2.5.1 stream
+ /// Reads the content of a UML XMI 2.5.1 stream asynchronously.
///
///
- /// the that contains the XMI content
+ /// The that contains the XMI content to be read.
///
///
- /// An
+ /// An representing the deserialized packages from the XMI stream.
///
- IEnumerable Read(Stream stream);
+ Task> ReadAsync(Stream stream);
}
}
\ No newline at end of file
diff --git a/uml4net.xmireader/Packages/ModelReader.cs b/uml4net.xmireader/Readers/Packages/ModelReader.cs
similarity index 73%
rename from uml4net.xmireader/Packages/ModelReader.cs
rename to uml4net.xmireader/Readers/Packages/ModelReader.cs
index 803552d5..8408b0ba 100644
--- a/uml4net.xmireader/Packages/ModelReader.cs
+++ b/uml4net.xmireader/Readers/Packages/ModelReader.cs
@@ -18,28 +18,27 @@
//
// ------------------------------------------------------------------------------------------------
-namespace uml4net.xmi.Packages
+namespace uml4net.xmi.Readers.Packages
{
- using System.Collections.Generic;
+ using Cache;
using System.Xml;
using Microsoft.Extensions.Logging;
- using Microsoft.Extensions.Logging.Abstractions;
-
- using uml4net.POCO;
+ using POCO.CommonStructure;
+ using POCO;
using uml4net.POCO.Packages;
- using uml4net.xmi.CommonStructure;
+ using Readers;
///
/// The purpose of the is to read an instance of
/// from the XMI document
///
- public class ModelReader : XmiElementReader
+ public class ModelReader : XmiElementReader, IXmiElementReader
{
///
- /// The used to log
+ /// The of
///
- private readonly ILogger logger;
+ private readonly IXmiElementReader packageImportReader;
///
/// Initializes a new instance of the class.
@@ -47,13 +46,14 @@ public class ModelReader : XmiElementReader
///
/// The cache in which each > is stored
///
- ///
- /// The (injected) used to setup logging
+ ///
+ /// The (injected) used to setup logging
///
- public ModelReader(Dictionary cache, ILoggerFactory loggerFactory = null)
- : base(cache, loggerFactory)
+ /// The of
+ public ModelReader(IXmiReaderCache cache, ILogger logger, IXmiElementReader packageImportReader)
+ : base(cache, logger)
{
- this.logger = this.loggerFactory == null ? NullLogger.Instance : this.loggerFactory.CreateLogger();
+ this.packageImportReader = packageImportReader;
}
///
@@ -65,15 +65,15 @@ public ModelReader(Dictionary cache, ILoggerFactory loggerF
///
/// an instance of
///
- public IModel Read(XmlReader xmlReader)
- {
+ public override IModel Read(XmlReader xmlReader)
+ {
IModel model = new Model();
if (xmlReader.MoveToContent() == XmlNodeType.Element)
{
var xmiType = xmlReader.GetAttribute("xmi:type");
- if (!string.IsNullOrEmpty(xmiType) && (xmiType != "uml:Model"))
+ if (!string.IsNullOrEmpty(xmiType) && xmiType != "uml:Model")
{
throw new XmlException($"The XmiType should be: uml:Model while it is {xmiType}");
}
@@ -86,7 +86,7 @@ public IModel Read(XmlReader xmlReader)
model.XmiId = xmlReader.GetAttribute("xmi:id");
- this.cache.Add(model.XmiId, model);
+ this.Cache.Add(model.XmiId, model);
model.Name = xmlReader.GetAttribute("name");
@@ -100,15 +100,14 @@ public IModel Read(XmlReader xmlReader)
case "packageImport":
using (var packageImportXmlReader = xmlReader.ReadSubtree())
{
- var packageImportReader = new PackageImportReader(this.cache, this.loggerFactory);
- var packageImport = packageImportReader.Read(packageImportXmlReader);
+ var packageImport = this.packageImportReader.Read(packageImportXmlReader);
model.PackageImport.Add(packageImport);
}
break;
case "packagedElement":
using (var packagedElementXmlReader = xmlReader.ReadSubtree())
{
- this.logger.LogInformation("ModelReader.packagedElement not yet implemented");
+ this.Logger.LogInformation("ModelReader.packagedElement not yet implemented");
}
break;
}
diff --git a/uml4net.xmireader/Packages/PackageReader.cs b/uml4net.xmireader/Readers/Packages/PackageReader.cs
similarity index 67%
rename from uml4net.xmireader/Packages/PackageReader.cs
rename to uml4net.xmireader/Readers/Packages/PackageReader.cs
index bb295493..ce79ba1d 100644
--- a/uml4net.xmireader/Packages/PackageReader.cs
+++ b/uml4net.xmireader/Readers/Packages/PackageReader.cs
@@ -18,33 +18,47 @@
//
// ------------------------------------------------------------------------------------------------
-namespace uml4net.xmi.Packages
+namespace uml4net.xmi.Readers.Packages
{
+ using Cache;
using System;
- using System.Collections.Generic;
using System.Xml;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
-
- using uml4net.POCO;
+ using POCO.SimpleClassifiers;
+ using POCO.StructuredClassifiers;
+ using POCO;
using uml4net.POCO.CommonStructure;
using uml4net.POCO.Packages;
- using uml4net.xmi.CommonStructure;
- using uml4net.xmi.SimpleClassifiers;
- using uml4net.xmi.StructuredClassifiers;
+ using Readers;
///
/// The purpose of the is to read an instance of
/// from the XMI document
///
- public class PackageReader : XmiElementReader
+ public class PackageReader : XmiCommentedElementReader, IXmiElementReader
{
///
- /// The used to log
+ /// The of
+ ///
+ private readonly IXmiElementReader classReader;
+
+ ///
+ /// The of
+ ///
+ private readonly IXmiElementReader enumerationReader;
+
+ ///
+ /// The of
+ ///
+ private readonly IXmiElementReader packageImportReader;
+
+ ///
+ /// The of
///
- private readonly ILogger logger;
+ private readonly IXmiElementReader primitiveReader;
///
/// Initializes a new instance of the class.
@@ -52,13 +66,22 @@ public class PackageReader : XmiElementReader
///
/// The cache in which each > is stored
///
- ///
- /// The (injected) used to setup logging
+ ///
+ /// The (injected) used to setup logging
///
- public PackageReader(Dictionary cache, ILoggerFactory loggerFactory = null)
- : base(cache, loggerFactory)
+ /// The of
+ /// The of
+ /// The of
+ /// The of
+ /// The of
+ public PackageReader(IXmiReaderCache cache, ILogger logger, IXmiElementReader commentReader, IXmiElementReader classReader,
+ IXmiElementReader enumerationReader, IXmiElementReader packageImportReader, IXmiElementReader primitiveReader)
+ : base(cache, logger, commentReader)
{
- this.logger = this.loggerFactory == null ? NullLogger.Instance : this.loggerFactory.CreateLogger();
+ this.classReader = classReader;
+ this.enumerationReader = enumerationReader;
+ this.packageImportReader = packageImportReader;
+ this.primitiveReader = primitiveReader;
}
///
@@ -70,7 +93,7 @@ public PackageReader(Dictionary cache, ILoggerFactory logge
///
/// an instance of
///
- public IPackage Read(XmlReader xmlReader)
+ public override IPackage Read(XmlReader xmlReader)
{
IPackage package = new Package();
@@ -87,7 +110,7 @@ public IPackage Read(XmlReader xmlReader)
package.XmiId = xmlReader.GetAttribute("xmi:id");
- this.cache.Add(package.XmiId, package);
+ this.Cache.Add(package.XmiId, package);
package.Name = xmlReader.GetAttribute("name");
@@ -106,35 +129,33 @@ public IPackage Read(XmlReader xmlReader)
case "ownedComment":
using (var ownedCommentXmlReader = xmlReader.ReadSubtree())
{
- var commentReader = new CommentReader(this.cache, this.loggerFactory);
- var comment = commentReader.Read(ownedCommentXmlReader);
+ var comment = this.CommentReader.Read(ownedCommentXmlReader);
package.OwnedComment.Add(comment);
}
break;
case "elementImport":
using (var elementImportXmlReader = xmlReader.ReadSubtree())
{
- this.logger.LogInformation("PackageReader.elementImport not yet implemented");
+ this.Logger.LogInformation("PackageReader.elementImport not yet implemented");
}
break;
case "ownedRule":
using (var ownedRuleXmlReader = xmlReader.ReadSubtree())
{
- this.logger.LogInformation("PackageReader.ownedRule not yet implemented");
+ this.Logger.LogInformation("PackageReader.ownedRule not yet implemented");
}
break;
case "packageImport":
using (var packageImportXmlReader = xmlReader.ReadSubtree())
{
- var packageImportReader = new PackageImportReader(this.cache, this.loggerFactory);
- var packageImport = packageImportReader.Read(packageImportXmlReader);
+ var packageImport = this.packageImportReader.Read(packageImportXmlReader);
package.PackageImport.Add(packageImport);
}
break;
case "templateBinding":
using (var templateBindingXmlReader = xmlReader.ReadSubtree())
{
- this.logger.LogInformation("PackageReader.TemplateBinding not yet implemented");
+ this.Logger.LogInformation("PackageReader.TemplateBinding not yet implemented");
}
break;
case "packagedElement":
@@ -168,37 +189,35 @@ private void ReadPackagedElements(IPackage package, XmlReader xmlReader)
case "uml:Association":
using (var associationXmlReader = xmlReader.ReadSubtree())
{
- this.logger.LogDebug("uml:Association not yet implements");
+ this.Logger.LogDebug("uml:Association not yet implements");
}
break;
case "uml:Class":
using (var classXmlReader = xmlReader.ReadSubtree())
{
- var classReader = new ClassReader(this.cache, this.loggerFactory);
- var packagedElement = classReader.Read(classXmlReader);
+ var packagedElement = this.classReader.Read(classXmlReader);
package.PackagedElement.Add(packagedElement);
}
break;
case "uml:Enumeration":
using (var enumerationXmlReader = xmlReader.ReadSubtree())
{
- var enumerationReader = new EnumerationReader(this.cache, this.loggerFactory);
- var enumeration = enumerationReader.Read(enumerationXmlReader);
+ var enumeration = this.enumerationReader.Read(enumerationXmlReader);
package.PackagedElement.Add(enumeration);
}
break;
case "uml:Package":
using (var packageXmlReader = xmlReader.ReadSubtree())
{
- var packageReader = new PackageReader(this.cache, this.loggerFactory);
- var packagedElement = packageReader.Read(packageXmlReader);
+ var packagedElement = this.Read(packageXmlReader);
package.PackagedElement.Add(packagedElement);
}
break;
case "uml:PrimitiveType":
using (var primitiveTypeXmlReader = xmlReader.ReadSubtree())
{
- this.logger.LogDebug("uml:PrimitiveType not yet implements");
+ var primitive = this.primitiveReader.Read(primitiveTypeXmlReader);
+ package.PackagedElement.Add(primitive);
}
break;
default:
diff --git a/uml4net.xmireader/SimpleClassifiers/EnumerationLiteralReader.cs b/uml4net.xmireader/Readers/SimpleClassifiers/EnumerationLiteralReader.cs
similarity index 74%
rename from uml4net.xmireader/SimpleClassifiers/EnumerationLiteralReader.cs
rename to uml4net.xmireader/Readers/SimpleClassifiers/EnumerationLiteralReader.cs
index 0ecb739e..fc734be7 100644
--- a/uml4net.xmireader/SimpleClassifiers/EnumerationLiteralReader.cs
+++ b/uml4net.xmireader/Readers/SimpleClassifiers/EnumerationLiteralReader.cs
@@ -18,44 +18,38 @@
//
// ------------------------------------------------------------------------------------------------
-namespace uml4net.xmi.SimpleClassifiers
+namespace uml4net.xmi.Readers.SimpleClassifiers
{
+ using Cache;
using System;
- using System.Collections.Generic;
using System.Xml;
using Microsoft.Extensions.Logging;
- using Microsoft.Extensions.Logging.Abstractions;
-
- using uml4net.POCO;
+ using POCO.CommonStructure;
+ using POCO;
using uml4net.POCO.SimpleClassifiers;
- using uml4net.xmi.CommonStructure;
+ using Readers;
///
/// The purpose of the is to read an instance of
/// from the XMI document
///
- public class EnumerationLiteralReader : XmiElementReader
+ public class EnumerationLiteralReader : XmiCommentedElementReader, IXmiElementReader
{
- ///
- /// The used to log
- ///
- private readonly ILogger logger;
-
///
/// Initializes a new instance of the class.
///
///
/// The cache in which each > is stored
///
- ///
- /// The (injected) used to setup logging
+ ///
+ /// The (injected) used to setup logging
///
- public EnumerationLiteralReader(Dictionary cache, ILoggerFactory loggerFactory = null)
- : base(cache, loggerFactory)
+ /// The of
+ public EnumerationLiteralReader(IXmiReaderCache cache, ILogger logger, IXmiElementReader commentReader)
+ : base(cache, logger, commentReader)
{
- this.logger = this.loggerFactory == null ? NullLogger.Instance : this.loggerFactory.CreateLogger();
}
///
@@ -67,7 +61,7 @@ public EnumerationLiteralReader(Dictionary cache, ILoggerFa
///
/// an instance of
///
- public IEnumerationLiteral Read(XmlReader xmlReader)
+ public override IEnumerationLiteral Read(XmlReader xmlReader)
{
IEnumerationLiteral enumerationLiteral = new EnumerationLiteral();
@@ -84,7 +78,7 @@ public IEnumerationLiteral Read(XmlReader xmlReader)
enumerationLiteral.XmiId = xmlReader.GetAttribute("xmi:id");
- this.cache.Add(enumerationLiteral.XmiId, enumerationLiteral);
+ this.Cache.Add(enumerationLiteral.XmiId, enumerationLiteral);
enumerationLiteral.Name = xmlReader.GetAttribute("name");
@@ -97,8 +91,7 @@ public IEnumerationLiteral Read(XmlReader xmlReader)
case "ownedComment":
using (var ownedCommentXmlReader = xmlReader.ReadSubtree())
{
- var commentReader = new CommentReader(this.cache, this.loggerFactory);
- var comment = commentReader.Read(ownedCommentXmlReader);
+ var comment = this.CommentReader.Read(ownedCommentXmlReader);
enumerationLiteral.OwnedComment.Add(comment);
}
break;
@@ -112,4 +105,4 @@ public IEnumerationLiteral Read(XmlReader xmlReader)
return enumerationLiteral;
}
}
-}
\ No newline at end of file
+}
diff --git a/uml4net.xmireader/SimpleClassifiers/EnumerationReader.cs b/uml4net.xmireader/Readers/SimpleClassifiers/EnumerationReader.cs
similarity index 71%
rename from uml4net.xmireader/SimpleClassifiers/EnumerationReader.cs
rename to uml4net.xmireader/Readers/SimpleClassifiers/EnumerationReader.cs
index 57a1ac34..b6793069 100644
--- a/uml4net.xmireader/SimpleClassifiers/EnumerationReader.cs
+++ b/uml4net.xmireader/Readers/SimpleClassifiers/EnumerationReader.cs
@@ -18,29 +18,28 @@
//
// ------------------------------------------------------------------------------------------------
-namespace uml4net.xmi.SimpleClassifiers
+namespace uml4net.xmi.Readers.SimpleClassifiers
{
using System;
- using System.Collections.Generic;
using System.Xml;
using Microsoft.Extensions.Logging;
- using Microsoft.Extensions.Logging.Abstractions;
-
- using uml4net.POCO;
+ using POCO;
+ using uml4net.POCO.CommonStructure;
using uml4net.POCO.SimpleClassifiers;
- using uml4net.xmi.CommonStructure;
+ using Cache;
+ using Readers;
///
/// The purpose of the is to read an instance of
/// from the XMI document
///
- public class EnumerationReader : XmiElementReader
+ public class EnumerationReader : XmiCommentedElementReader, IXmiElementReader
{
///
- /// The used to log
+ /// The of
///
- private readonly ILogger logger;
+ private readonly IXmiElementReader enumerationLiteralReader;
///
/// Initializes a new instance of the class.
@@ -48,13 +47,15 @@ public class EnumerationReader : XmiElementReader
///
/// The cache in which each > is stored
///
- ///
- /// The (injected) used to setup logging
+ ///
+ /// The (injected) used to setup logging
///
- public EnumerationReader(Dictionary cache, ILoggerFactory loggerFactory = null)
- : base(cache, loggerFactory)
+ /// The of
+ /// The of
+ public EnumerationReader(IXmiReaderCache cache, ILogger logger, IXmiElementReader commentReader, IXmiElementReader enumerationLiteralReader)
+ : base(cache, logger, commentReader)
{
- this.logger = this.loggerFactory == null ? NullLogger.Instance : this.loggerFactory.CreateLogger();
+ this.enumerationLiteralReader = enumerationLiteralReader;
}
///
@@ -66,7 +67,7 @@ public EnumerationReader(Dictionary cache, ILoggerFactory l
///
/// an instance of
///
- public IEnumeration Read(XmlReader xmlReader)
+ public override IEnumeration Read(XmlReader xmlReader)
{
IEnumeration enumeration = new Enumeration();
@@ -83,7 +84,7 @@ public IEnumeration Read(XmlReader xmlReader)
enumeration.XmiId = xmlReader.GetAttribute("xmi:id");
- this.cache.Add(enumeration.XmiId, enumeration);
+ this.Cache.Add(enumeration.XmiId, enumeration);
enumeration.Name = xmlReader.GetAttribute("name");
@@ -96,19 +97,17 @@ public IEnumeration Read(XmlReader xmlReader)
case "ownedComment":
using (var ownedCommentXmlReader = xmlReader.ReadSubtree())
{
- var commentReader = new CommentReader(this.cache, this.loggerFactory);
- var comment = commentReader.Read(ownedCommentXmlReader);
+ var comment = this.CommentReader.Read(ownedCommentXmlReader);
enumeration.OwnedComment.Add(comment);
}
break;
case "ownedLiteral":
using (var ownedLiteralXmlReader = xmlReader.ReadSubtree())
{
- var enumerationLiteralReader = new EnumerationLiteralReader(this.cache, this.loggerFactory);
- var enumerationLiteral = enumerationLiteralReader.Read(ownedLiteralXmlReader);
+ var enumerationLiteral = this.enumerationLiteralReader.Read(ownedLiteralXmlReader);
enumeration.OwnedLiteral.Add(enumerationLiteral);
- this.logger.LogInformation("ClassReader.ownedRule not yet implemented");
+ this.Logger.LogInformation("ClassReader.ownedRule not yet implemented");
}
break;
default:
diff --git a/uml4net.xmireader/Readers/SimpleClassifiers/PrimitiveTypeReader.cs b/uml4net.xmireader/Readers/SimpleClassifiers/PrimitiveTypeReader.cs
new file mode 100644
index 00000000..09761f32
--- /dev/null
+++ b/uml4net.xmireader/Readers/SimpleClassifiers/PrimitiveTypeReader.cs
@@ -0,0 +1,102 @@
+// -------------------------------------------------------------------------------------------------
+//
+//
+// 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.SimpleClassifiers
+{
+ using Microsoft.Extensions.Logging;
+ using uml4net.POCO.CommonStructure;
+ using uml4net.POCO.SimpleClassifiers;
+ using uml4net.xmi.Cache;
+
+ using POCO;
+ using System.Xml;
+ using System;
+
+ public class PrimitiveTypeReader : XmiCommentedElementReader, IXmiElementReader
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The cache in which each > is stored
+ ///
+ ///
+ /// The (injected) used to setup logging
+ ///
+ /// The of
+ public PrimitiveTypeReader(IXmiReaderCache cache, ILogger logger, IXmiElementReader commentReader)
+ : base(cache, logger, commentReader)
+ {
+ }
+
+ ///
+ /// Reads the object from its XML representation
+ ///
+ ///
+ /// an instance of
+ ///
+ ///
+ /// an instance of
+ ///
+ public override IPrimitiveType Read(XmlReader xmlReader)
+ {
+ IPrimitiveType primitiveType = new PrimitiveType();
+
+ if (xmlReader.MoveToContent() == XmlNodeType.Element)
+ {
+ var xmiType = xmlReader.GetAttribute("xmi:type");
+
+ if (xmiType != "uml:PrimitiveType")
+ {
+ throw new XmlException($"The XmiType should be: uml:PrimitiveType while it is {xmiType}");
+ }
+
+ primitiveType.XmiType = xmiType;
+
+ primitiveType.XmiId = xmlReader.GetAttribute("xmi:id");
+
+ this.Cache.Add(primitiveType.XmiId, primitiveType);
+
+ primitiveType.Name = xmlReader.GetAttribute("name");
+
+ while (xmlReader.Read())
+ {
+ if (xmlReader.NodeType == XmlNodeType.Element)
+ {
+ switch (xmlReader.LocalName)
+ {
+ case "ownedComment":
+ using (var ownedCommentXmlReader = xmlReader.ReadSubtree())
+ {
+ var comment = this.CommentReader.Read(ownedCommentXmlReader);
+ primitiveType.OwnedComment.Add(comment);
+ }
+ break;
+ default:
+ throw new NotImplementedException($"PrimitiveTypeReader: {xmlReader.LocalName}");
+ }
+ }
+ }
+ }
+
+ return primitiveType;
+ }
+ }
+}
diff --git a/uml4net.xmireader/StructuredClassifiers/ClassReader.cs b/uml4net.xmireader/Readers/StructuredClassifiers/ClassReader.cs
similarity index 66%
rename from uml4net.xmireader/StructuredClassifiers/ClassReader.cs
rename to uml4net.xmireader/Readers/StructuredClassifiers/ClassReader.cs
index 53c41307..1634b7b4 100644
--- a/uml4net.xmireader/StructuredClassifiers/ClassReader.cs
+++ b/uml4net.xmireader/Readers/StructuredClassifiers/ClassReader.cs
@@ -18,34 +18,42 @@
//
// ------------------------------------------------------------------------------------------------
-namespace uml4net.xmi.StructuredClassifiers
+namespace uml4net.xmi.Readers.StructuredClassifiers
{
+ using Cache;
using System;
- using System.Collections.Generic;
using System.Xml;
using Microsoft.Extensions.Logging;
- using Microsoft.Extensions.Logging.Abstractions;
-
- using uml4net.POCO;
+ using POCO.Classification;
+ using POCO;
using uml4net.POCO.CommonStructure;
using uml4net.POCO.StructuredClassifiers;
using uml4net.POCO.Packages;
- using uml4net.xmi.Classification;
- using uml4net.xmi.CommonStructure;
- using uml4net.xmi.Packages;
+ using Packages;
+ using Readers;
///
/// The purpose of the is to read an instance of
/// from the XMI document
///
- public class ClassReader : XmiElementReader
+ public class ClassReader : XmiCommentedElementReader, IXmiElementReader
{
///
- /// The used to log
+ /// The of
+ ///
+ private readonly IXmiElementReader constraintReader;
+
+ ///
+ /// The of
+ ///
+ private readonly IXmiElementReader propertyReader;
+
+ ///
+ /// The of
///
- private readonly ILogger logger;
+ private readonly IXmiElementReader generalizationReader;
///
/// Initializes a new instance of the class.
@@ -53,13 +61,20 @@ public class ClassReader : XmiElementReader
///
/// The cache in which each > is stored
///
- ///
- /// The (injected) used to setup logging
+ ///
+ /// The (injected) used to setup logging
///
- public ClassReader(Dictionary cache, ILoggerFactory loggerFactory = null)
- : base(cache, loggerFactory)
+ /// The of
+ /// The of
+ /// The of
+ /// The of
+ public ClassReader(IXmiReaderCache cache, ILogger logger, IXmiElementReader commentReader,
+ IXmiElementReader constraintReader, IXmiElementReader propertyReader, IXmiElementReader generatizationReader)
+ : base(cache, logger, commentReader)
{
- this.logger = this.loggerFactory == null ? NullLogger.Instance : this.loggerFactory.CreateLogger();
+ this.constraintReader = constraintReader;
+ this.propertyReader = propertyReader;
+ this.generalizationReader = generatizationReader;
}
///
@@ -71,7 +86,7 @@ public ClassReader(Dictionary cache, ILoggerFactory loggerF
///
/// an instance of
///
- public IClass Read(XmlReader xmlReader)
+ public override IClass Read(XmlReader xmlReader)
{
IClass @class = new Class();
@@ -88,7 +103,7 @@ public IClass Read(XmlReader xmlReader)
@class.XmiId = xmlReader.GetAttribute("xmi:id");
- this.cache.Add(@class.XmiId, @class);
+ this.Cache.Add(@class.XmiId, @class);
@class.Name = xmlReader.GetAttribute("name");
@@ -107,38 +122,34 @@ public IClass Read(XmlReader xmlReader)
case "ownedComment":
using (var ownedCommentXmlReader = xmlReader.ReadSubtree())
{
- var commentReader = new CommentReader(this.cache, this.loggerFactory);
- var comment = commentReader.Read(ownedCommentXmlReader);
+ var comment = this.CommentReader.Read(ownedCommentXmlReader);
@class.OwnedComment.Add(comment);
}
break;
case "ownedRule":
using (var ownedRuleXmlReader = xmlReader.ReadSubtree())
{
- var constraintReader = new ConstraintReader(this.cache, this.loggerFactory);
- var constraint = constraintReader.Read(ownedRuleXmlReader);
+ var constraint = this.constraintReader.Read(ownedRuleXmlReader);
@class.OwnedRule.Add(constraint);
}
break;
case "ownedAttribute":
using (var ownedAttributeXmlReader = xmlReader.ReadSubtree())
{
- var propertyReader = new PropertyReader(this.cache, this.loggerFactory);
- var property = propertyReader.Read(ownedAttributeXmlReader);
+ var property = this.propertyReader.Read(ownedAttributeXmlReader);
@class.OwnedAttribute.Add(property);
}
break;
case "ownedOperation":
using (var ownedOperationXmlReader = xmlReader.ReadSubtree())
{
- this.logger.LogInformation("ClassReader.ownedOperation not yet implemented");
+ this.Logger.LogInformation("ClassReader.ownedOperation not yet implemented");
}
break;
case "generalization":
using (var generalizationXmlReader = xmlReader.ReadSubtree())
{
- var generalizationReader = new GeneralizationReader(this.cache, this.loggerFactory);
- var generalization = generalizationReader.Read(generalizationXmlReader);
+ var generalization = this.generalizationReader.Read(generalizationXmlReader);
@class.Generalization.Add(generalization);
}
break;
@@ -152,4 +163,4 @@ public IClass Read(XmlReader xmlReader)
return @class;
}
}
-}
\ No newline at end of file
+}
diff --git a/uml4net.xmireader/Values/LiteralIntegerReader.cs b/uml4net.xmireader/Readers/Values/LiteralIntegerReader.cs
similarity index 77%
rename from uml4net.xmireader/Values/LiteralIntegerReader.cs
rename to uml4net.xmireader/Readers/Values/LiteralIntegerReader.cs
index 25674507..c753d253 100644
--- a/uml4net.xmireader/Values/LiteralIntegerReader.cs
+++ b/uml4net.xmireader/Readers/Values/LiteralIntegerReader.cs
@@ -18,8 +18,9 @@
//
// ------------------------------------------------------------------------------------------------
-namespace uml4net.xmi.Values
+namespace uml4net.xmi.Readers.Values
{
+ using Cache;
using System;
using System.Collections.Generic;
using System.Xml;
@@ -27,35 +28,30 @@ namespace uml4net.xmi.Values
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
- using uml4net.POCO;
+ using POCO;
using uml4net.POCO.CommonStructure;
using uml4net.POCO.Values;
- using uml4net.xmi.CommonStructure;
+ using Readers;
///
/// The purpose of the is to read an instance of
/// from the XMI document
///
- public class LiteralIntegerReader : XmiElementReader
+ public class LiteralIntegerReader : XmiCommentedElementReader, IXmiElementReader
{
- ///
- /// The used to log
- ///
- private readonly ILogger logger;
-
///
/// Initializes a new instance of the class.
///
///
/// The cache in which each > is stored
///
- ///
- /// The (injected) used to setup logging
+ ///
+ /// The (injected) used to setup logging
///
- public LiteralIntegerReader(Dictionary cache, ILoggerFactory loggerFactory = null)
- : base(cache, loggerFactory)
+ /// The of
+ public LiteralIntegerReader(IXmiReaderCache cache, ILogger logger, IXmiElementReader commentReader)
+ : base(cache, logger, commentReader)
{
- this.logger = this.loggerFactory == null ? NullLogger.Instance : this.loggerFactory.CreateLogger();
}
///
@@ -67,7 +63,7 @@ public LiteralIntegerReader(Dictionary cache, ILoggerFactor
///
/// an instance of
///
- public ILiteralInteger Read(XmlReader xmlReader)
+ public override ILiteralInteger Read(XmlReader xmlReader)
{
ILiteralInteger literalInteger = new LiteralInteger();
@@ -84,7 +80,7 @@ public ILiteralInteger Read(XmlReader xmlReader)
literalInteger.XmiId = xmlReader.GetAttribute("xmi:id");
- this.cache.Add(literalInteger.XmiId, literalInteger);
+ this.Cache.Add(literalInteger.XmiId, literalInteger);
var value = xmlReader.GetAttribute("value");
if (!string.IsNullOrEmpty(value))
@@ -101,8 +97,7 @@ public ILiteralInteger Read(XmlReader xmlReader)
case "ownedComment":
using (var ownedCommentXmlReader = xmlReader.ReadSubtree())
{
- var commentReader = new CommentReader(this.cache, this.loggerFactory);
- var comment = commentReader.Read(ownedCommentXmlReader);
+ var comment = this.CommentReader.Read(ownedCommentXmlReader);
literalInteger.OwnedComment.Add(comment);
}
break;
@@ -116,4 +111,4 @@ public ILiteralInteger Read(XmlReader xmlReader)
return literalInteger;
}
}
-}
\ No newline at end of file
+}
diff --git a/uml4net.xmireader/Values/LiteralUnlimitedNaturalReader.cs b/uml4net.xmireader/Readers/Values/LiteralUnlimitedNaturalReader.cs
similarity index 77%
rename from uml4net.xmireader/Values/LiteralUnlimitedNaturalReader.cs
rename to uml4net.xmireader/Readers/Values/LiteralUnlimitedNaturalReader.cs
index 8fb93ae6..e896e45a 100644
--- a/uml4net.xmireader/Values/LiteralUnlimitedNaturalReader.cs
+++ b/uml4net.xmireader/Readers/Values/LiteralUnlimitedNaturalReader.cs
@@ -18,7 +18,7 @@
//
// ------------------------------------------------------------------------------------------------
-namespace uml4net.xmi.Values
+namespace uml4net.xmi.Readers.Values
{
using System;
using System.Collections.Generic;
@@ -27,35 +27,31 @@ namespace uml4net.xmi.Values
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
- using uml4net.POCO;
+ using POCO;
using uml4net.POCO.CommonStructure;
using uml4net.POCO.Values;
- using uml4net.xmi.CommonStructure;
+ using Cache;
+ using Readers;
///
/// The purpose of the is to read an instance of
/// from the XMI document
///
- public class LiteralUnlimitedNaturalReader : XmiElementReader
+ public class LiteralUnlimitedNaturalReader : XmiCommentedElementReader, IXmiElementReader
{
- ///
- /// The used to log
- ///
- private readonly ILogger logger;
-
///
/// Initializes a new instance of the class.
///
///
/// The cache in which each > is stored
///
- ///
- /// The (injected) used to setup logging
- ///
- public LiteralUnlimitedNaturalReader(Dictionary cache, ILoggerFactory loggerFactory = null)
- : base(cache, loggerFactory)
+ ///
+ /// The (injected) used to setup logging
+ ///
+ /// The of
+ public LiteralUnlimitedNaturalReader(IXmiReaderCache cache, ILogger logger, IXmiElementReader commentReader)
+ : base(cache, logger, commentReader)
{
- this.logger = this.loggerFactory == null ? NullLogger.Instance : this.loggerFactory.CreateLogger();
}
///
@@ -67,7 +63,7 @@ public LiteralUnlimitedNaturalReader(Dictionary cache, ILog
///
/// an instance of
///
- public ILiteralUnlimitedNatural Read(XmlReader xmlReader)
+ public override ILiteralUnlimitedNatural Read(XmlReader xmlReader)
{
ILiteralUnlimitedNatural literalUnlimitedNatural = new LiteralUnlimitedNatural();
@@ -84,7 +80,7 @@ public ILiteralUnlimitedNatural Read(XmlReader xmlReader)
literalUnlimitedNatural.XmiId = xmlReader.GetAttribute("xmi:id");
- this.cache.Add(literalUnlimitedNatural.XmiId, literalUnlimitedNatural);
+ this.Cache.Add(literalUnlimitedNatural.XmiId, literalUnlimitedNatural);
var value = xmlReader.GetAttribute("value");
@@ -106,8 +102,7 @@ public ILiteralUnlimitedNatural Read(XmlReader xmlReader)
case "ownedComment":
using (var ownedCommentXmlReader = xmlReader.ReadSubtree())
{
- var commentReader = new CommentReader(this.cache, this.loggerFactory);
- var comment = commentReader.Read(ownedCommentXmlReader);
+ var comment = this.CommentReader.Read(ownedCommentXmlReader);
literalUnlimitedNatural.OwnedComment.Add(comment);
}
break;
@@ -121,4 +116,4 @@ public ILiteralUnlimitedNatural Read(XmlReader xmlReader)
return literalUnlimitedNatural;
}
}
-}
\ No newline at end of file
+}
diff --git a/uml4net.xmireader/Values/OpaqueExpressionReader.cs b/uml4net.xmireader/Readers/Values/OpaqueExpressionReader.cs
similarity index 77%
rename from uml4net.xmireader/Values/OpaqueExpressionReader.cs
rename to uml4net.xmireader/Readers/Values/OpaqueExpressionReader.cs
index 381c6110..7f652471 100644
--- a/uml4net.xmireader/Values/OpaqueExpressionReader.cs
+++ b/uml4net.xmireader/Readers/Values/OpaqueExpressionReader.cs
@@ -18,7 +18,7 @@
//
// ------------------------------------------------------------------------------------------------
-namespace uml4net.xmi.Values
+namespace uml4net.xmi.Readers.Values
{
using System;
using System.Collections.Generic;
@@ -27,35 +27,31 @@ namespace uml4net.xmi.Values
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
- using uml4net.POCO;
+ using POCO;
using uml4net.POCO.CommonStructure;
using uml4net.POCO.Values;
- using uml4net.xmi.CommonStructure;
+ using Cache;
+ using Readers;
///
/// The purpose of the is to read an instance of
/// from the XMI document
///
- public class OpaqueExpressionReader : XmiElementReader
+ public class OpaqueExpressionReader : XmiCommentedElementReader, IXmiElementReader
{
- ///
- /// The used to log
- ///
- private readonly ILogger logger;
-
///
/// Initializes a new instance of the class.
///
///
/// The cache in which each > is stored
///
- ///
- /// The (injected) used to setup logging
- ///
- public OpaqueExpressionReader(Dictionary cache, ILoggerFactory loggerFactory = null)
- : base(cache, loggerFactory)
+ ///
+ /// The (injected) used to setup logging
+ ///
+ /// The of
+ public OpaqueExpressionReader(IXmiReaderCache cache, ILogger logger, IXmiElementReader commentReader)
+ : base(cache, logger, commentReader)
{
- this.logger = this.loggerFactory == null ? NullLogger.Instance : this.loggerFactory.CreateLogger();
}
///
@@ -67,7 +63,7 @@ public OpaqueExpressionReader(Dictionary cache, ILoggerFact
///
/// an instance of
///
- public IOpaqueExpression Read(XmlReader xmlReader)
+ public override IOpaqueExpression Read(XmlReader xmlReader)
{
IOpaqueExpression opaqueExpression = new OpaqueExpression();
@@ -84,7 +80,7 @@ public IOpaqueExpression Read(XmlReader xmlReader)
opaqueExpression.XmiId = xmlReader.GetAttribute("xmi:id");
- this.cache.Add(opaqueExpression.XmiId, opaqueExpression);
+ this.Cache.Add(opaqueExpression.XmiId, opaqueExpression);
while (xmlReader.Read())
{
@@ -101,8 +97,7 @@ public IOpaqueExpression Read(XmlReader xmlReader)
case "ownedComment":
using (var ownedCommentXmlReader = xmlReader.ReadSubtree())
{
- var commentReader = new CommentReader(this.cache, this.loggerFactory);
- var comment = commentReader.Read(ownedCommentXmlReader);
+ var comment = this.CommentReader.Read(ownedCommentXmlReader);
opaqueExpression.OwnedComment.Add(comment);
}
break;
diff --git a/uml4net.xmireader/Readers/XmiCommentedElementReader.cs b/uml4net.xmireader/Readers/XmiCommentedElementReader.cs
new file mode 100644
index 00000000..7af97b7c
--- /dev/null
+++ b/uml4net.xmireader/Readers/XmiCommentedElementReader.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 Microsoft.Extensions.Logging;
+
+ using POCO;
+ using POCO.CommonStructure;
+ using Cache;
+
+ ///
+ /// An abstract class for reading XMI elements that include comments.
+ ///
+ /// The type of the XMI element to be read, must inherit from .
+ /// An instance of for caching XMI elements.
+ /// An instance of for logging.
+ /// An instance of to read comments associated with the XMI elements.
+ public abstract class XmiCommentedElementReader(IXmiReaderCache cache, ILogger> logger, IXmiElementReader commentReader)
+ : XmiElementReader(cache, logger) where TXmiElement : IXmiElement
+ {
+ ///
+ /// The of
+ ///
+ protected readonly IXmiElementReader CommentReader = commentReader;
+ }
+}
diff --git a/uml4net.xmireader/XmiElementReader.cs b/uml4net.xmireader/Readers/XmiElementReader.cs
similarity index 53%
rename from uml4net.xmireader/XmiElementReader.cs
rename to uml4net.xmireader/Readers/XmiElementReader.cs
index c7937311..8664af56 100644
--- a/uml4net.xmireader/XmiElementReader.cs
+++ b/uml4net.xmireader/Readers/XmiElementReader.cs
@@ -18,42 +18,53 @@
//
// ------------------------------------------------------------------------------------------------
-namespace uml4net.xmi
+namespace uml4net.xmi.Readers
{
- using System.Collections.Generic;
-
+ using Cache;
using Microsoft.Extensions.Logging;
-
- using uml4net.POCO;
+ using System.Xml;
+ using POCO;
///
/// The abstract super class from which eadh XMI reader needs to derive
- ///
- public abstract class XmiElementReader
+ ///
+ /// The type of the XMI element to be read.
+ public abstract class XmiElementReader where TXmiElement : IXmiElement
{
///
- /// The (injected) used to setup logging
+ /// The (injected) used to setup logging
///
- protected readonly ILoggerFactory loggerFactory;
+ protected readonly ILogger> Logger;
///
- /// The cache in which each > is stored
+ /// The (injected) used to setup logging
///
- protected readonly Dictionary cache;
+ protected readonly IXmiReaderCache Cache;
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
///
/// The cache in which each > is stored
///
- ///
- /// The (injected) used to setup logging
+ ///
+ /// The (injected) used to setup logging
///
- protected XmiElementReader(Dictionary cache, ILoggerFactory loggerFactory = null)
+ protected XmiElementReader(IXmiReaderCache cache, ILogger> logger)
{
- this.cache = cache;
- this.loggerFactory = loggerFactory;
+ this.Cache = cache;
+ this.Logger = logger;
}
+
+ ///
+ /// Reads the object from its XML representation
+ ///
+ ///
+ /// an instance of
+ ///
+ ///
+ /// an instance of
+ ///
+ public abstract TXmiElement Read(XmlReader xmlReader);
}
}
\ No newline at end of file
diff --git a/uml4net.xmireader/Readers/XmiReader.cs b/uml4net.xmireader/Readers/XmiReader.cs
new file mode 100644
index 00000000..63bff8fb
--- /dev/null
+++ b/uml4net.xmireader/Readers/XmiReader.cs
@@ -0,0 +1,234 @@
+// -------------------------------------------------------------------------------------------------
+//
+//
+// 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 Cache;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.IO;
+ using System.Xml;
+
+ using Microsoft.Extensions.Logging;
+ using System.Threading.Tasks;
+ using uml4net.POCO.Packages;
+
+ using uml4net;
+ using xmi;
+
+ ///
+ /// The purpose of the is to provide a means to read (deserialize)
+ /// an UML 2.5.1 model from XMI
+ ///
+ public class XmiReader : IXmiReader
+ {
+ ///
+ /// The
+ ///
+ private readonly IAssembler assembler;
+
+ ///
+ /// The used to log
+ ///
+ private readonly ILogger logger;
+
+ ///
+ /// The of
+ ///
+ private readonly IXmiElementReader packageReader;
+
+ ///
+ /// The of
+ ///
+ private readonly IXmiElementReader modelReader;
+
+ ///
+ /// The
+ ///
+ private readonly IExternalReferenceResolver externalReferenceResolver;
+
+ ///
+ /// The
+ ///
+ private readonly IXmiReaderScope scope;
+
+ ///
+ /// The
+ ///
+ private readonly IXmiReaderCache cache;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The
+ /// The
+ ///
+ /// The (injected) used to setup logging
+ ///
+ /// The of
+ /// The of
+ /// The
+ /// The
+ public XmiReader(IAssembler assembler, IXmiReaderCache cache, ILogger logger, IXmiElementReader packageReader,
+ IXmiElementReader modelReader, IExternalReferenceResolver externalReferenceResolver, IXmiReaderScope scope)
+ {
+ this.assembler = assembler;
+ this.cache = cache;
+ this.logger = logger;
+ this.packageReader = packageReader;
+ this.modelReader = modelReader;
+ this.externalReferenceResolver = externalReferenceResolver;
+ this.scope = scope;
+ }
+
+ ///
+ /// Reads the content of a UML XMI 2.5.1 file asynchronously.
+ ///
+ ///
+ /// The URI of the XMI file to be read.
+ ///
+ ///
+ /// An representing the deserialized packages from the XMI file.
+ ///
+ public async Task> ReadAsync(string fileUri)
+ {
+ await using var fileStream = File.OpenRead(fileUri);
+
+ var sw = Stopwatch.StartNew();
+
+ this.logger.LogTrace("start deserializing from {path}", fileUri);
+
+ var result = await this.ReadAsync(fileStream);
+
+ this.logger.LogTrace("File {path} deserialized in {time} [ms]", fileUri, sw.ElapsedMilliseconds);
+
+ return result;
+ }
+
+ ///
+ /// Reads the content of a UML XMI 2.5.1 stream asynchronously.
+ ///
+ ///
+ /// The that contains the XMI content to be read.
+ ///
+ ///
+ /// An representing the deserialized packages from the XMI stream.
+ ///
+ public async Task> ReadAsync(Stream stream)
+ {
+ return await this.Read(stream, true);
+ }
+
+ ///
+ /// Reads the content of a UML XMI 2.5.1 stream asynchronously.
+ ///
+ ///
+ /// The that contains the XMI content to be read.
+ ///
+ ///
+ /// A value indicating whether the reading occurs on the root node.
+ ///
+ ///
+ /// An representing the deserialized packages from the XMI stream.
+ ///
+ private async Task> Read(Stream stream, bool isRoot)
+ {
+ var settings = new XmlReaderSettings() { Async = true };
+
+ stream.Seek(0, SeekOrigin.Begin);
+
+ var sw = Stopwatch.StartNew();
+ var packages = new List();
+
+ using (var xmlReader = XmlReader.Create(stream, settings))
+ {
+ this.logger.LogTrace("starting to read xml");
+
+ while (await xmlReader.ReadAsync())
+ {
+ 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);
+ }
+
+ break;
+ case "uml:Model":
+ using (var modelXmlReader = xmlReader.ReadSubtree())
+ {
+ var model = this.modelReader.Read(modelXmlReader);
+ packages.Add(model);
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ var currentlyElapsedMilliseconds = sw.ElapsedMilliseconds;
+ this.logger.LogTrace("xml read in {time}", currentlyElapsedMilliseconds);
+ sw.Stop();
+
+ await this.TryResolveExternalReferences();
+
+ if (isRoot)
+ {
+ this.assembler.Synchronize();
+ }
+
+ return packages;
+
+ }
+
+ ///
+ /// Asynchronously resolves external references and updates the cache with the retrieved resources.
+ ///
+ /// A task that represents the asynchronous operation.
+ private async Task TryResolveExternalReferences()
+ {
+ var stopwatch = Stopwatch.StartNew();
+
+ await foreach (var (context, externalResource) in this.externalReferenceResolver.TryResolve())
+ {
+ this.cache.SwitchContext(context);
+ this.Read(externalResource, false);
+ }
+
+ this.logger.LogTrace("External eferences synchronized in {time}", stopwatch.ElapsedMilliseconds);
+ stopwatch.Stop();
+ }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose()
+ {
+ this.cache.Cache.Clear();
+ this.scope.Dispose();
+ }
+ }
+}
diff --git a/uml4net.xmireader/Settings/DefaultSettings.cs b/uml4net.xmireader/Settings/DefaultSettings.cs
new file mode 100644
index 00000000..86633f1a
--- /dev/null
+++ b/uml4net.xmireader/Settings/DefaultSettings.cs
@@ -0,0 +1,30 @@
+// -------------------------------------------------------------------------------------------------
+//
+//
+// 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.Settings
+{
+ public class DefaultSettings : IXmiReaderSettings
+ {
+ ///
+ /// Gets or sets the UML_PROFILES value, as a local file path to resolve pathmap references
+ ///
+ public string UmlProfilesDirectoryPath { get; set; } = "";
+ }
+}
diff --git a/uml4net.xmireader/Settings/IXmiReaderSettings.cs b/uml4net.xmireader/Settings/IXmiReaderSettings.cs
new file mode 100644
index 00000000..e629d5f4
--- /dev/null
+++ b/uml4net.xmireader/Settings/IXmiReaderSettings.cs
@@ -0,0 +1,35 @@
+// -------------------------------------------------------------------------------------------------
+//
+//
+// 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.
+//
+//
+// ------------------------------------------------------------------------------------------------
+
+using uml4net.xmi.Readers;
+
+namespace uml4net.xmi.Settings
+{
+ ///
+ /// The interface defines settings the requires in order to properly read
+ ///
+ public interface IXmiReaderSettings
+ {
+ ///
+ /// Gets or sets the UML_PROFILES value, as a local file path to resolve pathmap references
+ ///
+ string UmlProfilesDirectoryPath {get;set;}
+ }
+}
diff --git a/uml4net.xmireader/XmiReader.cs b/uml4net.xmireader/XmiReader.cs
deleted file mode 100644
index e114c394..00000000
--- a/uml4net.xmireader/XmiReader.cs
+++ /dev/null
@@ -1,161 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-//
-//
-// 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
-{
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.IO;
- using System.IO.Packaging;
- using System.Xml;
-
- using Microsoft.Extensions.Logging;
- using Microsoft.Extensions.Logging.Abstractions;
-
- using uml4net.POCO;
- using uml4net.POCO.Packages;
-
- using uml4net.xmi.Packages;
-
- ///
- /// The purpose of the is to provide a means to read (deserialize)
- /// an UML 2.5.1 model from XMI
- ///
- public class XmiReader : IXmiReader
- {
- ///
- /// The (injected) used to setup logging
- ///
- private readonly ILoggerFactory loggerFactory;
-
- ///
- /// The used to log
- ///
- private readonly ILogger logger;
-
- ///
- /// The cache in which each > is stored
- ///
- private readonly Dictionary cache;
-
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The (injected) used to setup logging
- ///
- public XmiReader(ILoggerFactory loggerFactory = null)
- {
- this.loggerFactory = loggerFactory;
-
- this.logger = this.loggerFactory == null ? NullLogger.Instance : this.loggerFactory.CreateLogger();
-
- this.cache = new Dictionary();
- }
-
- ///
- /// reads the content of a UML XMI 2.5.1 file
- ///
- ///
- /// the path to the XMI file
- ///
- ///
- /// An
- ///
- public IEnumerable Read(string fileUri)
- {
- using (var fileStream = File.OpenRead(fileUri))
- {
- var sw = Stopwatch.StartNew();
-
- this.logger.LogTrace("start deserializing from {path}", fileUri);
-
- var result = this.Read(fileStream);
-
- this.logger.LogTrace("File {path} deserialized in {time} [ms]", fileUri, sw.ElapsedMilliseconds);
-
- return result;
- }
- }
-
- ///
- /// reads the content of a UML XMI 2.5.1 stream
- ///
- ///
- /// the that contains the XMI content
- ///
- ///
- /// An
- ///
- public IEnumerable Read(Stream stream)
- {
- XmlReader xmlReader;
-
- var settings = new XmlReaderSettings();
-
- stream.Seek(0, SeekOrigin.Begin);
-
- using (xmlReader = XmlReader.Create(stream, settings))
- {
- var sw = Stopwatch.StartNew();
-
- var packages = new List();
-
- this.logger.LogTrace("starting to read xml");
-
- while (xmlReader.Read())
- {
- 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 packageReader = new PackageReader(this.cache, this.loggerFactory);
- var package = packageReader.Read(packageXmlReader);
- packages.Add(package);
- }
- break;
- case "uml:Model":
- using (var modelXmlReader = xmlReader.ReadSubtree())
- {
- var modelReader = new ModelReader(this.cache, this.loggerFactory);
- var model = modelReader.Read(modelXmlReader);
- packages.Add(model);
- }
- break;
- }
- }
- }
-
- var currentlyElapsedMilliseconds = sw.ElapsedMilliseconds;
- this.logger.LogTrace("xml read in {time}", currentlyElapsedMilliseconds);
-
- new Assembler(this.loggerFactory).Synchronize(this.cache);
- this.logger.LogTrace("elements references synchronized in {time}", sw.ElapsedMilliseconds - currentlyElapsedMilliseconds);
- sw.Stop();
-
- return packages;
- }
- }
- }
-}
diff --git a/uml4net.xmireader/XmiReaderBuilder.cs b/uml4net.xmireader/XmiReaderBuilder.cs
new file mode 100644
index 00000000..14ad2440
--- /dev/null
+++ b/uml4net.xmireader/XmiReaderBuilder.cs
@@ -0,0 +1,101 @@
+// -------------------------------------------------------------------------------------------------
+//
+//
+// 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
+{
+ using Microsoft.Extensions.DependencyInjection;
+ using Microsoft.Extensions.Logging;
+ using Settings;
+ using System;
+ using System.Net.Http;
+ using Readers;
+
+ ///
+ /// Provides builder methods to configure and create an instance of .
+ ///
+ public static class XmiReaderBuilder
+ {
+ ///
+ /// Creates a new instance of used to configure services for an XMI reader.
+ ///
+ ///
+ /// A new to configure and build the XMI reader.
+ ///
+ public static XmiReaderScope Create()
+ {
+ return new XmiReaderScope();
+ }
+
+ ///
+ /// Configures the to use the provided .
+ ///
+ /// The being configured.
+ /// The to be used by the XMI reader.
+ ///
+ /// The configured instance.
+ ///
+ public static XmiReaderScope UsingHttpClient(this XmiReaderScope scope, HttpClient client)
+ {
+ scope.ServiceCollection.AddScoped(_ => client);
+ return scope;
+ }
+
+ ///
+ /// Configures the to use the provided instance.
+ ///
+ /// The being configured.
+ /// The to be used by the XMI reader.
+ ///
+ /// The configured instance.
+ ///
+ public static XmiReaderScope UsingSettings(this XmiReaderScope scope, IXmiReaderSettings settings)
+ {
+ scope.ServiceCollection.AddScoped(_ => settings);
+ return scope;
+ }
+
+ ///
+ /// Configures the to use the provided for logging.
+ ///
+ /// The being configured.
+ /// The to be used for logging.
+ ///
+ /// The configured instance.
+ ///
+ public static XmiReaderScope WithLogger(this XmiReaderScope scope, ILoggerFactory loggerFactory)
+ {
+ scope.ServiceCollection.AddSingleton(loggerFactory);
+ return scope;
+ }
+
+ ///
+ /// Builds and configures the based on the services added to the .
+ ///
+ /// The being used to build the XMI reader.
+ ///
+ /// A fully configured instance of .
+ ///
+ public static IXmiReader Build(this XmiReaderScope scope)
+ {
+ scope.CreateScope();
+ return scope.Scope.ServiceProvider.GetRequiredService();
+ }
+ }
+}
diff --git a/uml4net.xmireader/XmiReaderScope.cs b/uml4net.xmireader/XmiReaderScope.cs
new file mode 100644
index 00000000..dbeed6b0
--- /dev/null
+++ b/uml4net.xmireader/XmiReaderScope.cs
@@ -0,0 +1,116 @@
+// -------------------------------------------------------------------------------------------------
+//
+//
+// 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
+{
+ using Cache;
+ using Microsoft.Extensions.DependencyInjection;
+ using Microsoft.Extensions.Logging;
+ using POCO.Classification;
+ using POCO.CommonStructure;
+ using POCO.Packages;
+ using POCO.SimpleClassifiers;
+ using POCO.StructuredClassifiers;
+ using POCO.Values;
+ using System;
+ using System.Net.Http;
+ using Readers;
+ using Readers.Classification;
+ using Readers.CommonStructure;
+ using Readers.Packages;
+ using Readers.SimpleClassifiers;
+ using Readers.StructuredClassifiers;
+ using Readers.Values;
+ using Settings;
+
+ ///
+ /// Represents the scope for configuring and managing services used by the XMI reader.
+ ///
+ public class XmiReaderScope : IXmiReaderScope
+ {
+ ///
+ /// Gets the service collection where all service registrations are stored.
+ ///
+ internal IServiceCollection ServiceCollection { get; } = new ServiceCollection();
+
+ ///
+ /// Gets the service provider responsible for resolving services.
+ ///
+ internal IServiceProvider ServiceProvider { get; private set; }
+
+ ///
+ /// Gets the service scope which provides a scoped lifetime for services.
+ ///
+ internal IServiceScope Scope { get; private set; }
+
+ ///
+ /// Builds the service provider and service scope from the configured service collection.
+ ///
+ internal void CreateScope()
+ {
+ this.ServiceProvider = this.ServiceCollection.BuildServiceProvider();
+ this.Scope = this.ServiceProvider.CreateScope();
+ }
+
+ ///
+ /// Initializes a new instance of the class and configures default services.
+ ///
+ internal XmiReaderScope()
+ {
+ //Overridable services
+ this.ServiceCollection.AddScoped(_ => new HttpClient());
+ this.ServiceCollection.AddScoped();
+ this.ServiceCollection.AddSingleton(LoggerFactory.Create(builder => builder.AddConsole()));
+
+ //Required services
+ this.ServiceCollection.AddScoped(typeof(ILogger<>), typeof(Logger<>));
+ this.ServiceCollection.AddScoped(x => this);
+ this.ServiceCollection.AddScoped();
+ this.ServiceCollection.AddScoped();
+ this.ServiceCollection.AddScoped();
+ this.ServiceCollection.AddScoped();
+
+ //Readers
+ this.ServiceCollection.AddScoped, GeneralizationReader>();
+ this.ServiceCollection.AddScoped, PropertyReader>();
+ this.ServiceCollection.AddScoped, CommentReader>();
+ this.ServiceCollection.AddScoped, ConstraintReader>();
+ this.ServiceCollection.AddScoped, PackageImportReader>();
+ this.ServiceCollection.AddScoped, ModelReader>();
+ this.ServiceCollection.AddScoped, PackageReader>();
+ this.ServiceCollection.AddScoped, EnumerationLiteralReader>();
+ this.ServiceCollection.AddScoped, EnumerationReader>();
+ this.ServiceCollection.AddScoped, ClassReader>();
+ this.ServiceCollection.AddScoped, LiteralIntegerReader>();
+ this.ServiceCollection.AddScoped, LiteralUnlimitedNaturalReader>();
+ this.ServiceCollection.AddScoped, OpaqueExpressionReader>();
+ this.ServiceCollection.AddScoped, PrimitiveTypeReader>();
+ }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose()
+ {
+ this.Scope.Dispose();
+ this.ServiceCollection.Clear();
+ }
+ }
+}
diff --git a/uml4net.xmireader/uml4net.xmi.csproj b/uml4net.xmireader/uml4net.xmi.csproj
index d5394c99..bc51b1e1 100644
--- a/uml4net.xmireader/uml4net.xmi.csproj
+++ b/uml4net.xmireader/uml4net.xmi.csproj
@@ -1,8 +1,8 @@
- netstandard2.0
- 11.0
+ net8.0
+ 12.0
A .NET implementation of the OMG UML v2.5.1 specification.
uml4net.xmi
Starion Group S.A.
@@ -23,8 +23,20 @@
+
+
+ <_Parameter1>$(AssemblyName).Tests
+
+
+
+
+
+
+
-
+
+
+