-
Notifications
You must be signed in to change notification settings - Fork 2
uml4net.xmi.project
The uml4net.xmi
project/library is used to read an XMI document of a UML model and return a fully dereferenced object graph of its contents. The entry point of the library is the XmiReaderBuilder
which returns an IXmiReader
that is used to read an XMI document.
The following code snippet demonstrates how the XmiReaderBuilder
is used to read an XMI document:
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.CreateLogger();
loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddSerilog();
});
var rootPath = <...path to folder where external references are stored>
var reader = XmiReaderBuilder.Create()
.UsingSettings(x => x.LocalReferenceBasePath = rootPath)
.WithLogger(loggerFactory)
.Build();
var xmiReaderResult = reader.Read(<...path to XMI file...>);
The XmiReaderResult
contains all Packages, inlcuding the root Package, that were read. The root Package is the Package, Model or Profile that is at the root of the XMI document being read.
The .UsingSettings
method is used to configure the settings. The following can be set:
- LocalReferenceBasePath: the path to the folder where external references (referenced UML models) are available.
- PathMap: a dictionary of path maps that are using in the XMI documents. a path map act as virtual path or aliase that maps to physical file locations
The use of PathMap
is demonstrated with the following code snippet:
var reader = XmiReaderBuilder.Create()
.UsingSettings(x =>
x.PathMap = usingSettings ? new Dictionary<string, string> { ["pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml"] = <...path to physical file location...> } : [])
.WithLogger(loggerFactory)
.Build();
var xmiReaderResult = reader.Read(<...path to XMI file...>);
For each concrete UML class an IXmiElementReader
is code-generated. This IXmiElementReader
is responsible for reading an XML fragment that describes that IXmiElement
and its contained IXmiElement
s. The read method of the IXmiElementReader
class returns an instance of the IXmiElement
that it is responsible for. For each owned attribute (i.e. each contained property) the IXmiElementReaderFacade
is responsible for resolving the correct IXmiElementReader
.
The
IXmiElementReaderFacade
and theIXmiElementReader
implementations are all code-generated using the uml4net libraries and are stored in the AutoGenXmiReaders project folder.
Reading starts at the XmiReader.Read
method where a Model, Package or StereoType are read. The code snippet below from XmiReader.Read
shows the pattern that is repeated accross all the generated IXmiElementReader
implementations.
private void Read(Stream stream, string documentName, XmiReaderResult xmiReaderResult, bool isRoot)
{
var settings = new XmlReaderSettings();
stream.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(stream, detectEncodingFromByteOrderMarks: true))
using (var xmlReader = XmlReader.Create(reader, settings))
{
var defaultLineInfo = xmlReader as IXmlLineInfo;
while (xmlReader.Read())
{
if (xmlReader.NodeType == XmlNodeType.Element)
{
switch (xmlReader.LocalName)
{
case "Package":
var package = (IPackage)xmiElementReaderFacade.QueryXmiElement(xmlReader, documentName, xmlReader.NamespaceURI, cache, this.loggerFactory, "uml:Package");
xmiReaderResult.Packages.Add(package);
if (isRoot)
{
xmiReaderResult.Root = package;
}
break;
case "Model":
var model = (IModel)xmiElementReaderFacade.QueryXmiElement(xmlReader, documentName, xmlReader.NamespaceURI, this.cache, this.loggerFactory, "uml:Model");
xmiReaderResult.Packages.Add(model);
if (isRoot)
{
xmiReaderResult.Root = model;
}
break;
case "Profile":
var profile = (IProfile)xmiElementReaderFacade.QueryXmiElement(xmlReader, documentName, xmlReader.NamespaceURI, this.cache, this.loggerFactory, "uml:Profile");
xmiReaderResult.Packages.Add(profile);
if (isRoot)
{
xmiReaderResult.Root = profile;
}
break;
default:
this.logger.LogWarning("XmiReader: {LocalName} at line:position {Line}:{Position} was not read", xmlReader.LocalName, defaultLineInfo.LineNumber, defaultLineInfo.LinePosition);
break;
}
}
}
}
this.TryResolveExternalReferences(xmiReaderResult, documentName);
if (isRoot)
{
this.assembler.Synchronize();
}
}
The XmiReader
is responsible for starting the process to resolve external references and to start the Assembler.Synchronize
process. External references are those XmiElement
s that are part of another XMI document. A good example is the PrimitiveTypes.xmi
document which is referenced from the UML.xmi
document. The PrimitiveTypes.xmi
file contains primitive types such as Boolean and Integer that are used in the UML.xmi
file.
The
ExternalReferenceResolver
can resolve all external references that are available at theLocalReferenceBasePath
that are referenced from any XMI model file that is found, also during the process where external references are resolved.
The Assembler
works together with the XmiElementCache
to dereference an object graph. This is necessary when the reference properties of the IXmiElement
instances (the UML classes) are not pointers to other instances, but are encoded in the SingleValueReferencePropertyIdentifiers
or MultiValueReferencePropertyIdentifiers
AND all the instances are stored in the IXmiElementCache
.
The Synchronize
method that iterates through all the entries in the SingleValueReferencePropertyIdentifiers
and MultiValueReferencePropertyIdentifiers
of all the XmiElement
that are stored in the XmiElementCache
and updates the single-valued and multi-valued properties of these instances. Both the SingleValueReferencePropertyIdentifiers
and the MultiValueReferencePropertyIdentifiers
properties are Dictionaries where each entry is a key-value pair that contains the naem of the property, the values are either single or multiple unique identifiers, the XmiElement.XmiId
property, of other XmiElement
instances.
copyright @ Starion Group S.A.