Skip to content

uml4net.Tools.project

samatstarion edited this page Jan 6, 2025 · 4 revisions

Introduction

The user manual of uml4net.Tools is here. This page covers the developer documentation

The uml4net.Tools project is a commandline application using the System.CommandLine and System.CommandLine.Hosting infrastructure to create Commands and Options to enable the generation of reports. The report generators from the uml4net.Reporting project are used to generate the actual reports. The uml4net.Tools project wraps them using Commands.

System.CommandLine provides essential functionality, such as parsing, invocation, and rendering support. The Command composition defines the structure and the way the app looks and feels. A ICommandHandler is assigned to a Command which is used during the invocation phase and provides model-binding functionality.

NOTE: System.CommandLine uses a naming convention to link command Options to Handler properties. Make sure the name of the Option and the Handler option property are the same. Valid names would be: --no-logo and NoLogo (for the option.Name property and the Handler property).

For more information, please read the following articles, they were a great help to us:

Commands

Each Command in the application derives from the abstract super class ReportCommand. This command adds generic options that are valid for each derived command:

public abstract class ReportCommand : Command
{
    protected ReportCommand(string name, string description = null) : base(name, description)
    {
        var noLogoOption = new Option<bool>(
            name: "--no-logo",
            description: "Suppress the logo",
            getDefaultValue: () => false);
        this.AddOption(noLogoOption);

        var inputModelFileOption = new Option<FileInfo>(
            name: "--input-model",
            description: "The path to the UML XMI file",
            getDefaultValue: () => new FileInfo("model.xmi"));
        inputModelFileOption.AddAlias("-i");
        inputModelFileOption.IsRequired = true;
        this.AddOption(inputModelFileOption);

        var pathMaps = new Option<string[]>(
            name: "--pathmaps",
            description: "Add pathmap key-value pairs");
        pathMaps.AddAlias("-m");
        pathMaps.AllowMultipleArgumentsPerToken = true;
        this.AddOption(pathMaps);

        var autoOpenReportOption = new Option<bool>(
            name: "--auto-open-report",
            description: "Open the generated report with its default application",
            getDefaultValue: () => false);
        autoOpenReportOption.AddAlias("-a");
        autoOpenReportOption.IsRequired = false;
        this.AddOption(autoOpenReportOption);
    }
}

Each concrete Command (the following are provided: HtmlReportCommand, MarkdownReportCommand, ModelInspectionCommand, XlReportCommand) derives from ReportCommand and sets its own Handler to the abstract ReportHandler. The abstract ReportHandler handles invocation of the Command.

Any new Command needs to be added to the Commands project folder.

Midlewares

From the original documentation:

While each command has a handler that System.CommandLine will route to based on input, there's also a mechanism for short-circuiting or altering the input before your application logic is invoked. In between parsing and invocation, there's a chain of responsibility, which you can customize. A number of built-in features of System.CommandLine make use of this capability. This is how the --help and --version options short-circuit calls to your handler.

Currently one middleware is implemented which checks whether the most recent version of the application is being used, the VersionCheckerMiddleware Middlware.

Any new middleware needs to be added to the Middlewares project folder.

Dependency Injection

Dependencies are wired up and injected in the Program.cs class using the DI framework of .NET. The following need to be registered:

  • services that are used to do work, i.e. generate reports
  • Command and Command handlers

The following code snippet shows how the report generators and the commands are wired up:

public static int Main(string[] args)
{
    var commandLineBuilder = BuildCommandLine()
        .UseHost(_ => Host.CreateDefaultBuilder(args)
                .ConfigureLogging(loggingBuilder =>
                    loggingBuilder.AddFilter<ConsoleLoggerProvider>(level =>
                        level == LogLevel.None))
            , builder => builder
                .ConfigureServices((services) =>
                {
                    services.AddSingleton<IXlReportGenerator, XlReportGenerator>();
                    services.AddSingleton<IModelInspector, ModelInspector>();
                    services.AddSingleton<IHtmlReportGenerator, HtmlReportGenerator>();
                    services.AddSingleton<IMarkdownReportGenerator, MarkdownReportGenerator>();

                })
                .UseCommandHandler<XlReportCommand, XlReportCommand.Handler>()
                .UseCommandHandler<ModelInspectionCommand, ModelInspectionCommand.Handler>()
                .UseCommandHandler<HtmlReportCommand, HtmlReportCommand.Handler>()
                .UseCommandHandler<MarkdownReportCommand, MarkdownReportCommand.Handler>()
            )
        .UseDefaults()

        .Build();

    return commandLineBuilder.Invoke(args);
}