Cleaned up the code
This commit is contained in:
parent
5f1d7ef530
commit
f8fb0a431f
@ -1,12 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="..\XSDVisualiser\Models\**\*.cs" Link="Models\%(RecursiveDir)%(Filename)%(Extension)" />
|
<Compile Include="..\XSDVisualiser\Models\**\*.cs" Link="Models\%(RecursiveDir)%(Filename)%(Extension)"/>
|
||||||
<Compile Include="..\XSDVisualiser\Parsing\**\*.cs" Link="Parsing\%(RecursiveDir)%(Filename)%(Extension)" />
|
<Compile Include="..\XSDVisualiser\Parsing\**\*.cs" Link="Parsing\%(RecursiveDir)%(Filename)%(Extension)"/>
|
||||||
<Compile Include="..\XSDVisualiser\Utils\**\*.cs" Link="Utils\%(RecursiveDir)%(Filename)%(Extension)" />
|
<Compile Include="..\XSDVisualiser\Utils\**\*.cs" Link="Utils\%(RecursiveDir)%(Filename)%(Extension)"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -4,32 +4,32 @@
|
|||||||
xmlns:conv="clr-namespace:XSDVisualiser.Desktop.Converters"
|
xmlns:conv="clr-namespace:XSDVisualiser.Desktop.Converters"
|
||||||
x:Class="XSDVisualiser.Desktop.App"
|
x:Class="XSDVisualiser.Desktop.App"
|
||||||
RequestedThemeVariant="Light">
|
RequestedThemeVariant="Light">
|
||||||
<Application.Resources>
|
<Application.Resources>
|
||||||
<!-- App-level resource brushes for consistent, accessible colors -->
|
<!-- App-level resource brushes for consistent, accessible colors -->
|
||||||
<SolidColorBrush x:Key="AccentBrush" Color="#2563EB"/>
|
<SolidColorBrush x:Key="AccentBrush" Color="#2563EB" />
|
||||||
<SolidColorBrush x:Key="AccentBrushLight" Color="#DBEAFE"/>
|
<SolidColorBrush x:Key="AccentBrushLight" Color="#DBEAFE" />
|
||||||
<SolidColorBrush x:Key="PanelBackgroundBrush" Color="#FFFFFFFF"/>
|
<SolidColorBrush x:Key="PanelBackgroundBrush" Color="#FFFFFFFF" />
|
||||||
<SolidColorBrush x:Key="PanelBorderBrush" Color="#E5E7EB"/>
|
<SolidColorBrush x:Key="PanelBorderBrush" Color="#E5E7EB" />
|
||||||
<SolidColorBrush x:Key="SubtleTextBrush" Color="#475569"/>
|
<SolidColorBrush x:Key="SubtleTextBrush" Color="#475569" />
|
||||||
<SolidColorBrush x:Key="MutedTextBrush" Color="#64748B"/>
|
<SolidColorBrush x:Key="MutedTextBrush" Color="#64748B" />
|
||||||
<SolidColorBrush x:Key="SeparatorBrush" Color="#E2E8F0"/>
|
<SolidColorBrush x:Key="SeparatorBrush" Color="#E2E8F0" />
|
||||||
<SolidColorBrush x:Key="BadgeBackgroundBrush" Color="#F1F5F9"/>
|
<SolidColorBrush x:Key="BadgeBackgroundBrush" Color="#F1F5F9" />
|
||||||
|
|
||||||
<!-- Global converters -->
|
<!-- Global converters -->
|
||||||
<conv:CardinalityToLabelConverter x:Key="CardinalityToLabel"/>
|
<conv:CardinalityToLabelConverter x:Key="CardinalityToLabel" />
|
||||||
<conv:OptionalToBorderBrushConverter x:Key="OptionalToBrush"/>
|
<conv:OptionalToBorderBrushConverter x:Key="OptionalToBrush" />
|
||||||
<conv:OptionalToThicknessConverter x:Key="OptionalToThickness"/>
|
<conv:OptionalToThicknessConverter x:Key="OptionalToThickness" />
|
||||||
<conv:CollectionHasItemsConverter x:Key="HasItems"/>
|
<conv:CollectionHasItemsConverter x:Key="HasItems" />
|
||||||
</Application.Resources>
|
</Application.Resources>
|
||||||
|
|
||||||
<Application.Styles>
|
<Application.Styles>
|
||||||
<!-- Use Fluent theme for consistent UI controls -->
|
<!-- Use Fluent theme for consistent UI controls -->
|
||||||
<fluent:FluentTheme/>
|
<fluent:FluentTheme />
|
||||||
|
|
||||||
<!-- Default TextBlock accessibility improvements -->
|
<!-- Default TextBlock accessibility improvements -->
|
||||||
<Style Selector="TextBlock">
|
<Style Selector="TextBlock">
|
||||||
<Setter Property="TextWrapping" Value="Wrap"/>
|
<Setter Property="TextWrapping" Value="Wrap" />
|
||||||
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
|
<Setter Property="TextTrimming" Value="CharacterEllipsis" />
|
||||||
</Style>
|
</Style>
|
||||||
</Application.Styles>
|
</Application.Styles>
|
||||||
</Application>
|
</Application>
|
||||||
@ -2,22 +2,19 @@ using Avalonia;
|
|||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
namespace XSDVisualiser.Desktop
|
namespace XSDVisualiser.Desktop;
|
||||||
{
|
|
||||||
public partial class App : Application
|
|
||||||
{
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnFrameworkInitializationCompleted()
|
public class App : Application
|
||||||
{
|
{
|
||||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
desktop.MainWindow = new MainWindow();
|
AvaloniaXamlLoader.Load(this);
|
||||||
}
|
}
|
||||||
base.OnFrameworkInitializationCompleted();
|
|
||||||
}
|
public override void OnFrameworkInitializationCompleted()
|
||||||
|
{
|
||||||
|
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
|
desktop.MainWindow = new MainWindow();
|
||||||
|
base.OnFrameworkInitializationCompleted();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,22 +1,22 @@
|
|||||||
using System;
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Avalonia.Data.Converters;
|
using Avalonia.Data.Converters;
|
||||||
using XSDVisualiser.Models;
|
using XSDVisualiser.Core;
|
||||||
|
|
||||||
namespace XSDVisualiser.Desktop.Converters
|
namespace XSDVisualiser.Desktop.Converters;
|
||||||
|
|
||||||
|
public class CardinalityToLabelConverter : IValueConverter
|
||||||
{
|
{
|
||||||
public class CardinalityToLabelConverter : IValueConverter
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
if (value is not Occurs occ) return null;
|
||||||
{
|
|
||||||
if (value is not Occurs occ) return null;
|
|
||||||
|
|
||||||
var min = occ.Min;
|
var min = occ.Min;
|
||||||
var max = occ.MaxIsUnbounded ? "*" : occ.Max.ToString(culture);
|
var max = occ.MaxIsUnbounded ? "*" : occ.Max.ToString(culture);
|
||||||
return $"[{min}..{max}]";
|
return $"[{min}..{max}]";
|
||||||
}
|
}
|
||||||
|
|
||||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
=> throw new NotSupportedException();
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,37 +1,37 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Avalonia.Data.Converters;
|
using Avalonia.Data.Converters;
|
||||||
|
|
||||||
namespace XSDVisualiser.Desktop.Converters
|
namespace XSDVisualiser.Desktop.Converters;
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Returns true if the bound value is a collection with at least one item. Supports IEnumerable and ICollection.
|
|
||||||
/// Optional parameter "Invert" to invert the boolean result.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class CollectionHasItemsConverter : IValueConverter
|
|
||||||
{
|
|
||||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
||||||
{
|
|
||||||
var hasItems = false;
|
|
||||||
switch (value)
|
|
||||||
{
|
|
||||||
case ICollection coll:
|
|
||||||
hasItems = coll.Count > 0;
|
|
||||||
break;
|
|
||||||
case IEnumerable enumerable:
|
|
||||||
{
|
|
||||||
var enumerator = enumerable.GetEnumerator();
|
|
||||||
hasItems = enumerator.MoveNext();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var invert = parameter is string s && s.Equals("Invert", StringComparison.OrdinalIgnoreCase);
|
/// <summary>
|
||||||
return invert ? !hasItems : hasItems;
|
/// Returns true if the bound value is a collection with at least one item. Supports IEnumerable and ICollection.
|
||||||
|
/// Optional parameter "Invert" to invert the boolean result.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class CollectionHasItemsConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
var hasItems = false;
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case ICollection coll:
|
||||||
|
hasItems = coll.Count > 0;
|
||||||
|
break;
|
||||||
|
case IEnumerable enumerable:
|
||||||
|
{
|
||||||
|
var enumerator = enumerable.GetEnumerator();
|
||||||
|
hasItems = enumerator.MoveNext();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
var invert = parameter is string s && s.Equals("Invert", StringComparison.OrdinalIgnoreCase);
|
||||||
=> throw new NotSupportedException();
|
return invert ? !hasItems : hasItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,27 +1,25 @@
|
|||||||
using System;
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Avalonia.Data.Converters;
|
using Avalonia.Data.Converters;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using XSDVisualiser.Models;
|
using XSDVisualiser.Core;
|
||||||
|
|
||||||
namespace XSDVisualiser.Desktop.Converters
|
namespace XSDVisualiser.Desktop.Converters;
|
||||||
|
|
||||||
|
public class OptionalToBorderBrushConverter : IValueConverter
|
||||||
{
|
{
|
||||||
public class OptionalToBorderBrushConverter : IValueConverter
|
private static readonly IBrush RequiredBrush = new SolidColorBrush(Color.FromRgb(0x33, 0x99, 0x33));
|
||||||
|
private static readonly IBrush OptionalBrush = new SolidColorBrush(Color.FromRgb(0xCC, 0x66, 0x33));
|
||||||
|
|
||||||
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
private static readonly IBrush RequiredBrush = new SolidColorBrush(Color.FromRgb(0x33, 0x99, 0x33));
|
if (value is Occurs occ)
|
||||||
private static readonly IBrush OptionalBrush = new SolidColorBrush(Color.FromRgb(0xCC, 0x66, 0x33));
|
// Optional if minOccurs == 0
|
||||||
|
return occ.Min == 0 ? OptionalBrush : RequiredBrush;
|
||||||
|
return RequiredBrush;
|
||||||
|
}
|
||||||
|
|
||||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
if (value is Occurs occ)
|
throw new NotSupportedException();
|
||||||
{
|
|
||||||
// Optional if minOccurs == 0
|
|
||||||
return occ.Min == 0 ? OptionalBrush : RequiredBrush;
|
|
||||||
}
|
|
||||||
return RequiredBrush;
|
|
||||||
}
|
|
||||||
|
|
||||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
||||||
=> throw new NotSupportedException();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,27 +1,25 @@
|
|||||||
using System;
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Data.Converters;
|
using Avalonia.Data.Converters;
|
||||||
using XSDVisualiser.Models;
|
using XSDVisualiser.Core;
|
||||||
|
|
||||||
namespace XSDVisualiser.Desktop.Converters
|
namespace XSDVisualiser.Desktop.Converters;
|
||||||
|
|
||||||
|
public class OptionalToThicknessConverter : IValueConverter
|
||||||
{
|
{
|
||||||
public class OptionalToThicknessConverter : IValueConverter
|
private static readonly Thickness Required = new(2);
|
||||||
|
private static readonly Thickness Optional = new(2);
|
||||||
|
|
||||||
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
private static readonly Thickness Required = new Thickness(2);
|
if (value is Occurs occ)
|
||||||
private static readonly Thickness Optional = new Thickness(2);
|
// We could vary thickness; for now same thickness regardless, reserved for future styling.
|
||||||
|
return occ.Min == 0 ? Optional : Required;
|
||||||
|
return Required;
|
||||||
|
}
|
||||||
|
|
||||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
if (value is Occurs occ)
|
throw new NotSupportedException();
|
||||||
{
|
|
||||||
// We could vary thickness; for now same thickness regardless, reserved for future styling.
|
|
||||||
return occ.Min == 0 ? Optional : Required;
|
|
||||||
}
|
|
||||||
return Required;
|
|
||||||
}
|
|
||||||
|
|
||||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
||||||
=> throw new NotSupportedException();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -10,7 +10,8 @@
|
|||||||
|
|
||||||
<Grid Grid.Row="1" ColumnDefinitions="2*,Auto,*" RowDefinitions="*">
|
<Grid Grid.Row="1" ColumnDefinitions="2*,Auto,*" RowDefinitions="*">
|
||||||
<views:LeftTreeView Grid.Column="0" />
|
<views:LeftTreeView Grid.Column="0" />
|
||||||
<GridSplitter Grid.Column="1" Width="6" Margin="4,8" Background="{DynamicResource PanelBorderBrush}" ShowsPreview="True"/>
|
<GridSplitter Grid.Column="1" Width="6" Margin="4,8" Background="{DynamicResource PanelBorderBrush}"
|
||||||
|
ShowsPreview="True" />
|
||||||
<views:RightDetailsView Grid.Column="2" />
|
<views:RightDetailsView Grid.Column="2" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
|
||||||
namespace XSDVisualiser.Desktop
|
namespace XSDVisualiser.Desktop;
|
||||||
|
|
||||||
|
public partial class MainWindow : Window
|
||||||
{
|
{
|
||||||
public partial class MainWindow : Window
|
public MainWindow()
|
||||||
{
|
{
|
||||||
public MainWindow()
|
InitializeComponent();
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,21 +1,21 @@
|
|||||||
using System;
|
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.ReactiveUI;
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
namespace XSDVisualiser.Desktop
|
namespace XSDVisualiser.Desktop;
|
||||||
{
|
|
||||||
internal static class Program
|
|
||||||
{
|
|
||||||
[STAThread]
|
|
||||||
public static void Main(string[] args)
|
|
||||||
{
|
|
||||||
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static AppBuilder BuildAvaloniaApp() =>
|
internal static class Program
|
||||||
AppBuilder.Configure<App>()
|
{
|
||||||
.UsePlatformDetect()
|
[STAThread]
|
||||||
.LogToTrace()
|
public static void Main(string[] args)
|
||||||
.UseReactiveUI();
|
{
|
||||||
|
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AppBuilder BuildAvaloniaApp()
|
||||||
|
{
|
||||||
|
return AppBuilder.Configure<App>()
|
||||||
|
.UsePlatformDetect()
|
||||||
|
.LogToTrace()
|
||||||
|
.UseReactiveUI();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,80 +1,80 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using XSDVisualiser.Models;
|
using XSDVisualiser.Core;
|
||||||
|
|
||||||
namespace XSDVisualiser.Desktop.ViewModels
|
namespace XSDVisualiser.Desktop.ViewModels;
|
||||||
|
|
||||||
|
public class MainWindowViewModel(SchemaModel model) : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
public class MainWindowViewModel(SchemaModel model) : INotifyPropertyChanged
|
private string? _currentFilePath;
|
||||||
|
private SchemaModel _model = model ?? throw new ArgumentNullException(nameof(model));
|
||||||
|
private SchemaNode? _selectedNode;
|
||||||
|
private SchemaNode? _selectedRootElement;
|
||||||
|
|
||||||
|
public SchemaModel Model
|
||||||
{
|
{
|
||||||
private SchemaModel _model = model ?? throw new ArgumentNullException(nameof(model));
|
get => _model;
|
||||||
private SchemaNode? _selectedRootElement;
|
set
|
||||||
private SchemaNode? _selectedNode;
|
|
||||||
|
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
|
||||||
|
|
||||||
private void OnPropertyChanged([CallerMemberName] string? name = null)
|
|
||||||
{
|
{
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
if (ReferenceEquals(_model, value)) return;
|
||||||
|
_model = value ?? throw new ArgumentNullException(nameof(value));
|
||||||
|
OnPropertyChanged();
|
||||||
|
OnPropertyChanged(nameof(RootElements));
|
||||||
|
OnPropertyChanged(nameof(VisibleRootElements));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public SchemaModel Model
|
public IEnumerable<SchemaNode> RootElements => _model?.RootElements ?? Enumerable.Empty<SchemaNode>();
|
||||||
|
|
||||||
|
public SchemaNode? SelectedRootElement
|
||||||
|
{
|
||||||
|
get => _selectedRootElement;
|
||||||
|
set
|
||||||
{
|
{
|
||||||
get => _model;
|
if (EqualityComparer<SchemaNode?>.Default.Equals(_selectedRootElement, value)) return;
|
||||||
set
|
_selectedRootElement = value;
|
||||||
{
|
OnPropertyChanged();
|
||||||
if (ReferenceEquals(_model, value)) return;
|
OnPropertyChanged(nameof(VisibleRootElements));
|
||||||
_model = value ?? throw new ArgumentNullException(nameof(value));
|
|
||||||
OnPropertyChanged();
|
|
||||||
OnPropertyChanged(nameof(RootElements));
|
|
||||||
OnPropertyChanged(nameof(VisibleRootElements));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerable<SchemaNode> RootElements => _model?.RootElements ?? Enumerable.Empty<SchemaNode>();
|
public SchemaNode? SelectedNode
|
||||||
|
{
|
||||||
public SchemaNode? SelectedRootElement
|
get => _selectedNode;
|
||||||
|
set
|
||||||
{
|
{
|
||||||
get => _selectedRootElement;
|
if (EqualityComparer<SchemaNode?>.Default.Equals(_selectedNode, value)) return;
|
||||||
set
|
_selectedNode = value;
|
||||||
{
|
OnPropertyChanged();
|
||||||
if (EqualityComparer<SchemaNode?>.Default.Equals(_selectedRootElement, value)) return;
|
|
||||||
_selectedRootElement = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
OnPropertyChanged(nameof(VisibleRootElements));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public SchemaNode? SelectedNode
|
public IEnumerable<SchemaNode> VisibleRootElements =>
|
||||||
|
SelectedRootElement != null ? [SelectedRootElement] : RootElements;
|
||||||
|
|
||||||
|
public string? CurrentFilePath
|
||||||
|
{
|
||||||
|
get => _currentFilePath;
|
||||||
|
set
|
||||||
{
|
{
|
||||||
get => _selectedNode;
|
if (string.Equals(_currentFilePath, value, StringComparison.Ordinal)) return;
|
||||||
set
|
_currentFilePath = value;
|
||||||
{
|
OnPropertyChanged();
|
||||||
if (EqualityComparer<SchemaNode?>.Default.Equals(_selectedNode, value)) return;
|
OnPropertyChanged(nameof(CurrentFileName));
|
||||||
_selectedNode = value;
|
OnPropertyChanged(nameof(CurrentDirectory));
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerable<SchemaNode> VisibleRootElements => SelectedRootElement != null ? [SelectedRootElement] : RootElements;
|
public string? CurrentFileName =>
|
||||||
|
string.IsNullOrEmpty(_currentFilePath) ? null : Path.GetFileName(_currentFilePath);
|
||||||
|
|
||||||
private string? _currentFilePath;
|
public string? CurrentDirectory =>
|
||||||
public string? CurrentFilePath
|
string.IsNullOrEmpty(_currentFilePath) ? null : Path.GetDirectoryName(_currentFilePath);
|
||||||
{
|
|
||||||
get => _currentFilePath;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (string.Equals(_currentFilePath, value, StringComparison.Ordinal)) return;
|
|
||||||
_currentFilePath = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
OnPropertyChanged(nameof(CurrentFileName));
|
|
||||||
OnPropertyChanged(nameof(CurrentDirectory));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string? CurrentFileName => string.IsNullOrEmpty(_currentFilePath) ? null : System.IO.Path.GetFileName(_currentFilePath);
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
public string? CurrentDirectory => string.IsNullOrEmpty(_currentFilePath) ? null : System.IO.Path.GetDirectoryName(_currentFilePath);
|
|
||||||
|
private void OnPropertyChanged([CallerMemberName] string? name = null)
|
||||||
|
{
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -8,23 +8,23 @@
|
|||||||
FontSize="20"
|
FontSize="20"
|
||||||
FontWeight="SemiBold"
|
FontWeight="SemiBold"
|
||||||
Margin="0,0,12,0"
|
Margin="0,0,12,0"
|
||||||
VerticalAlignment="Center"/>
|
VerticalAlignment="Center" />
|
||||||
|
|
||||||
<AutoCompleteBox Grid.Column="1"
|
<AutoCompleteBox Grid.Column="1"
|
||||||
Margin="0,0,12,0"
|
Margin="0,0,12,0"
|
||||||
Watermark="Search and choose a root element"
|
Watermark="Search and choose a root element"
|
||||||
ItemsSource="{Binding RootElements}"
|
ItemsSource="{Binding RootElements}"
|
||||||
FilterMode="Contains"
|
FilterMode="Contains"
|
||||||
MinimumPrefixLength="0"
|
MinimumPrefixLength="0"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Center">
|
VerticalAlignment="Center">
|
||||||
<AutoCompleteBox.ItemTemplate>
|
<AutoCompleteBox.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<TextBlock Text="{Binding .}"/>
|
<TextBlock Text="{Binding .}" />
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</AutoCompleteBox.ItemTemplate>
|
</AutoCompleteBox.ItemTemplate>
|
||||||
<AutoCompleteBox.SelectedItem>
|
<AutoCompleteBox.SelectedItem>
|
||||||
<Binding Path="SelectedRootElement" Mode="TwoWay"/>
|
<Binding Path="SelectedRootElement" Mode="TwoWay" />
|
||||||
</AutoCompleteBox.SelectedItem>
|
</AutoCompleteBox.SelectedItem>
|
||||||
</AutoCompleteBox>
|
</AutoCompleteBox>
|
||||||
|
|
||||||
@ -32,7 +32,7 @@
|
|||||||
Content="Open XSD and parse"
|
Content="Open XSD and parse"
|
||||||
x:Name="OpenBtn"
|
x:Name="OpenBtn"
|
||||||
Width="220"
|
Width="220"
|
||||||
VerticalAlignment="Center"/>
|
VerticalAlignment="Center" />
|
||||||
|
|
||||||
<Border Grid.Column="3"
|
<Border Grid.Column="3"
|
||||||
Background="{DynamicResource PanelBackgroundBrush}"
|
Background="{DynamicResource PanelBackgroundBrush}"
|
||||||
@ -46,15 +46,16 @@
|
|||||||
ToolTip.Tip="{Binding CurrentFilePath}">
|
ToolTip.Tip="{Binding CurrentFilePath}">
|
||||||
<Border.ContextMenu>
|
<Border.ContextMenu>
|
||||||
<ContextMenu>
|
<ContextMenu>
|
||||||
<MenuItem Header="Copy path" Click="OnCopyPathClick"/>
|
<MenuItem Header="Copy path" Click="OnCopyPathClick" />
|
||||||
<MenuItem Header="Open containing folder" Click="OnOpenContainingFolderClick"/>
|
<MenuItem Header="Open containing folder" Click="OnOpenContainingFolderClick" />
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</Border.ContextMenu>
|
</Border.ContextMenu>
|
||||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||||
<TextBlock Text="File:" Foreground="{DynamicResource SubtleTextBrush}"/>
|
<TextBlock Text="File:" Foreground="{DynamicResource SubtleTextBrush}" />
|
||||||
<StackPanel MaxWidth="520">
|
<StackPanel MaxWidth="520">
|
||||||
<TextBlock Text="{Binding CurrentFileName}" FontWeight="SemiBold" TextTrimming="CharacterEllipsis"/>
|
<TextBlock Text="{Binding CurrentFileName}" FontWeight="SemiBold" TextTrimming="CharacterEllipsis" />
|
||||||
<TextBlock Text="{Binding CurrentDirectory}" Foreground="{DynamicResource SubtleTextBrush}" TextTrimming="CharacterEllipsis"/>
|
<TextBlock Text="{Binding CurrentDirectory}" Foreground="{DynamicResource SubtleTextBrush}"
|
||||||
|
TextTrimming="CharacterEllipsis" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
@ -1,12 +1,9 @@
|
|||||||
using System;
|
using System.Diagnostics;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Threading;
|
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
|
using Avalonia.Threading;
|
||||||
using XSDVisualiser.Core;
|
using XSDVisualiser.Core;
|
||||||
using XSDVisualiser.Models;
|
|
||||||
using XSDVisualiser.Desktop.ViewModels;
|
using XSDVisualiser.Desktop.ViewModels;
|
||||||
|
|
||||||
namespace XSDVisualiser.Desktop.Views;
|
namespace XSDVisualiser.Desktop.Views;
|
||||||
@ -19,10 +16,7 @@ public partial class HeaderView : UserControl
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_openBtn = this.FindControl<Button>("OpenBtn");
|
_openBtn = this.FindControl<Button>("OpenBtn");
|
||||||
if (_openBtn != null)
|
if (_openBtn != null) _openBtn.Click += OpenBtn_Click;
|
||||||
{
|
|
||||||
_openBtn.Click += OpenBtn_Click;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OpenBtn_Click(object? sender, RoutedEventArgs e)
|
private async void OpenBtn_Click(object? sender, RoutedEventArgs e)
|
||||||
@ -49,13 +43,10 @@ public partial class HeaderView : UserControl
|
|||||||
var file = results[0];
|
var file = results[0];
|
||||||
var path = file.TryGetLocalPath();
|
var path = file.TryGetLocalPath();
|
||||||
if (!string.IsNullOrEmpty(path))
|
if (!string.IsNullOrEmpty(path))
|
||||||
{
|
|
||||||
await ParseAndShowAsync(path!, topLevel);
|
await ParseAndShowAsync(path!, topLevel);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
Dispatcher.UIThread.Post(() => topLevel.DataContext = new MainWindowViewModel(new SchemaModel
|
||||||
Dispatcher.UIThread.Post(() => topLevel.DataContext = new MainWindowViewModel(new SchemaModel { TargetNamespace = "Selected file is not a local file. Please choose a local .xsd." }));
|
{ TargetNamespace = "Selected file is not a local file. Please choose a local .xsd." }));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task ParseAndShowAsync(string path, Window hostWindow)
|
private Task ParseAndShowAsync(string path, Window hostWindow)
|
||||||
@ -77,7 +68,8 @@ public partial class HeaderView : UserControl
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(() => hostWindow.DataContext = new MainWindowViewModel(new SchemaModel { TargetNamespace = ex.Message }));
|
Dispatcher.UIThread.Post(() =>
|
||||||
|
hostWindow.DataContext = new MainWindowViewModel(new SchemaModel { TargetNamespace = ex.Message }));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -89,10 +81,7 @@ public partial class HeaderView : UserControl
|
|||||||
if (string.IsNullOrWhiteSpace(path)) return;
|
if (string.IsNullOrWhiteSpace(path)) return;
|
||||||
|
|
||||||
var topLevel = TopLevel.GetTopLevel(this);
|
var topLevel = TopLevel.GetTopLevel(this);
|
||||||
if (topLevel?.Clipboard != null)
|
if (topLevel?.Clipboard != null) await topLevel.Clipboard.SetTextAsync(path);
|
||||||
{
|
|
||||||
await topLevel.Clipboard.SetTextAsync(path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnOpenContainingFolderClick(object? sender, RoutedEventArgs e)
|
private void OnOpenContainingFolderClick(object? sender, RoutedEventArgs e)
|
||||||
@ -100,8 +89,8 @@ public partial class HeaderView : UserControl
|
|||||||
var vm = DataContext as MainWindowViewModel;
|
var vm = DataContext as MainWindowViewModel;
|
||||||
var path = vm?.CurrentFilePath;
|
var path = vm?.CurrentFilePath;
|
||||||
if (string.IsNullOrWhiteSpace(path)) return;
|
if (string.IsNullOrWhiteSpace(path)) return;
|
||||||
var dir = System.IO.Path.GetDirectoryName(path);
|
var dir = Path.GetDirectoryName(path);
|
||||||
if (string.IsNullOrWhiteSpace(dir) || !System.IO.Directory.Exists(dir)) return;
|
if (string.IsNullOrWhiteSpace(dir) || !Directory.Exists(dir)) return;
|
||||||
TryOpenFolder(dir);
|
TryOpenFolder(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,32 +100,32 @@ public partial class HeaderView : UserControl
|
|||||||
{
|
{
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
var psi = new System.Diagnostics.ProcessStartInfo("explorer.exe", dir)
|
var psi = new ProcessStartInfo("explorer.exe", dir)
|
||||||
{
|
{
|
||||||
UseShellExecute = true
|
UseShellExecute = true
|
||||||
};
|
};
|
||||||
System.Diagnostics.Process.Start(psi);
|
Process.Start(psi);
|
||||||
}
|
}
|
||||||
else if (OperatingSystem.IsMacOS())
|
else if (OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
System.Diagnostics.Process.Start("open", dir);
|
Process.Start("open", dir);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Linux
|
// Linux
|
||||||
System.Diagnostics.Process.Start("xdg-open", dir);
|
Process.Start("xdg-open", dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var psi = new System.Diagnostics.ProcessStartInfo
|
var psi = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = dir,
|
FileName = dir,
|
||||||
UseShellExecute = true
|
UseShellExecute = true
|
||||||
};
|
};
|
||||||
System.Diagnostics.Process.Start(psi);
|
Process.Start(psi);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,65 +1,70 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:m="clr-namespace:XSDVisualiser.Models;assembly=XSDVisualiser.Core"
|
xmlns:m="clr-namespace:XSDVisualiser.Core;assembly=XSDVisualiser.Core"
|
||||||
x:Class="XSDVisualiser.Desktop.Views.LeftTreeView"
|
x:Class="XSDVisualiser.Desktop.Views.LeftTreeView"
|
||||||
x:CompileBindings="False">
|
x:CompileBindings="False">
|
||||||
<Border Background="{DynamicResource PanelBackgroundBrush}"
|
<Border Background="{DynamicResource PanelBackgroundBrush}"
|
||||||
BorderBrush="{DynamicResource PanelBorderBrush}"
|
BorderBrush="{DynamicResource PanelBorderBrush}"
|
||||||
BorderThickness="1" CornerRadius="6" Margin="0,8,8,0">
|
BorderThickness="1" CornerRadius="6" Margin="0,8,8,0">
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<TreeView x:Name="SchemaTree" ItemsSource="{Binding VisibleRootElements}" SelectedItem="{Binding SelectedNode, Mode=TwoWay}">
|
<TreeView x:Name="SchemaTree" ItemsSource="{Binding VisibleRootElements}"
|
||||||
<TreeView.DataTemplates>
|
SelectedItem="{Binding SelectedNode, Mode=TwoWay}">
|
||||||
<TreeDataTemplate DataType="{x:Type m:SchemaNode}" ItemsSource="{Binding Children}">
|
<TreeView.DataTemplates>
|
||||||
<StackPanel>
|
<TreeDataTemplate DataType="{x:Type m:SchemaNode}" ItemsSource="{Binding Children}">
|
||||||
<!-- Connector line from parent to this node (hidden for roots where ContentModel is null) -->
|
<StackPanel>
|
||||||
<Grid Margin="0,8,8,2" IsVisible="{Binding ContentModel, Converter={x:Static ObjectConverters.IsNotNull}}"
|
<!-- Connector line from parent to this node (hidden for roots where ContentModel is null) -->
|
||||||
ColumnDefinitions="16,*">
|
<Grid Margin="0,8,8,2"
|
||||||
<!-- leading small elbow -->
|
IsVisible="{Binding ContentModel, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||||
<Rectangle Grid.Column="0" Width="16" Height="2" Fill="{DynamicResource SeparatorBrush}" VerticalAlignment="Center"/>
|
ColumnDefinitions="16,*">
|
||||||
<Grid Grid.Column="1">
|
<!-- leading small elbow -->
|
||||||
<Rectangle Height="2" Fill="{DynamicResource SeparatorBrush}" VerticalAlignment="Center"/>
|
<Rectangle Grid.Column="0" Width="16" Height="2"
|
||||||
<TextBlock Text="{Binding Cardinality, Converter={StaticResource CardinalityToLabel}}"
|
Fill="{DynamicResource SeparatorBrush}" VerticalAlignment="Center" />
|
||||||
Background="{DynamicResource BadgeBackgroundBrush}" Padding="6,0"
|
<Grid Grid.Column="1">
|
||||||
Foreground="{DynamicResource SubtleTextBrush}"
|
<Rectangle Height="2" Fill="{DynamicResource SeparatorBrush}"
|
||||||
HorizontalAlignment="Center"/>
|
VerticalAlignment="Center" />
|
||||||
</Grid>
|
<TextBlock
|
||||||
</Grid>
|
Text="{Binding Cardinality, Converter={StaticResource CardinalityToLabel}}"
|
||||||
|
Background="{DynamicResource BadgeBackgroundBrush}" Padding="6,0"
|
||||||
|
Foreground="{DynamicResource SubtleTextBrush}"
|
||||||
|
HorizontalAlignment="Center" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<!-- Node content -->
|
<!-- Node content -->
|
||||||
<Border CornerRadius="4"
|
<Border CornerRadius="4"
|
||||||
BorderBrush="{Binding Cardinality, Converter={StaticResource OptionalToBrush}}"
|
BorderBrush="{Binding Cardinality, Converter={StaticResource OptionalToBrush}}"
|
||||||
BorderThickness="{Binding Cardinality, Converter={StaticResource OptionalToThickness}}"
|
BorderThickness="{Binding Cardinality, Converter={StaticResource OptionalToThickness}}"
|
||||||
Padding="8" Margin="0,0,8,6" Background="{DynamicResource PanelBackgroundBrush}">
|
Padding="8" Margin="0,0,8,6" Background="{DynamicResource PanelBackgroundBrush}">
|
||||||
<Border.ContextMenu>
|
<Border.ContextMenu>
|
||||||
<ContextMenu>
|
<ContextMenu>
|
||||||
<MenuItem Header="Copy as TSV" Click="OnCopyAsTsvClick"/>
|
<MenuItem Header="Copy as TSV" Click="OnCopyAsTsvClick" />
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</Border.ContextMenu>
|
</Border.ContextMenu>
|
||||||
<StackPanel Orientation="Vertical" Spacing="2">
|
<StackPanel Orientation="Vertical" Spacing="2">
|
||||||
<!-- Name on its own line, prominent -->
|
<!-- Name on its own line, prominent -->
|
||||||
<TextBlock Text="{Binding Name}" FontWeight="SemiBold"/>
|
<TextBlock Text="{Binding Name}" FontWeight="SemiBold" />
|
||||||
|
|
||||||
<!-- Path/Namespace on its own line -->
|
<!-- Path/Namespace on its own line -->
|
||||||
<TextBlock Text="{Binding Namespace, StringFormat=Namespace: {0}}"
|
<TextBlock Text="{Binding Namespace, StringFormat=Namespace: {0}}"
|
||||||
Foreground="{DynamicResource SubtleTextBrush}"/>
|
Foreground="{DynamicResource SubtleTextBrush}" />
|
||||||
|
|
||||||
<!-- Type info on its own line (TypeName with fallback to BuiltInType) -->
|
<!-- Type info on its own line (TypeName with fallback to BuiltInType) -->
|
||||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||||
<TextBlock Text="Type:" FontWeight="Medium"/>
|
<TextBlock Text="Type:" FontWeight="Medium" />
|
||||||
<TextBlock Text="{Binding TypeName}"/>
|
<TextBlock Text="{Binding TypeName}" />
|
||||||
<TextBlock Text="{Binding BuiltInType, StringFormat=({0})}"
|
<TextBlock Text="{Binding BuiltInType, StringFormat=({0})}"
|
||||||
Foreground="{DynamicResource MutedTextBrush}"/>
|
Foreground="{DynamicResource MutedTextBrush}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<!-- Optional: show content model as a subtle hint -->
|
<!-- Optional: show content model as a subtle hint -->
|
||||||
<TextBlock Text="{Binding ContentModel, StringFormat=Model: {0}}"
|
<TextBlock Text="{Binding ContentModel, StringFormat=Model: {0}}"
|
||||||
Foreground="{DynamicResource SubtleTextBrush}" FontWeight="SemiBold"/>
|
Foreground="{DynamicResource SubtleTextBrush}" FontWeight="SemiBold" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</TreeDataTemplate>
|
</TreeDataTemplate>
|
||||||
</TreeView.DataTemplates>
|
</TreeView.DataTemplates>
|
||||||
</TreeView>
|
</TreeView>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Border>
|
</Border>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -1,117 +1,115 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.VisualTree;
|
using Avalonia.VisualTree;
|
||||||
using XSDVisualiser.Models;
|
using XSDVisualiser.Core;
|
||||||
|
|
||||||
namespace XSDVisualiser.Desktop.Views
|
namespace XSDVisualiser.Desktop.Views;
|
||||||
|
|
||||||
|
public partial class LeftTreeView : UserControl
|
||||||
{
|
{
|
||||||
public partial class LeftTreeView : UserControl
|
public LeftTreeView()
|
||||||
{
|
{
|
||||||
public LeftTreeView()
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnCopyAsTsvClick(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
// Determine the node from the menu item's DataContext
|
||||||
|
var node = (sender as MenuItem)?.DataContext as SchemaNode;
|
||||||
|
if (node == null)
|
||||||
|
node =
|
||||||
|
(sender as Control)?.GetVisualAncestors().OfType<Control>().FirstOrDefault()?.DataContext as SchemaNode;
|
||||||
|
if (node == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var tsv = BuildTsv(node);
|
||||||
|
|
||||||
|
var topLevel = TopLevel.GetTopLevel(this);
|
||||||
|
if (topLevel?.Clipboard != null) await topLevel.Clipboard.SetTextAsync(tsv);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildTsv(SchemaNode root)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
// Header
|
||||||
|
sb.AppendLine(
|
||||||
|
"Depth\tPath\tName\tNamespace\tTypeName\tBuiltInType\tMinOccurs\tMaxOccurs\tContentModel\tConstraints\tIsNillable");
|
||||||
|
var initialPath = root.Name ?? string.Empty;
|
||||||
|
AppendNode(sb, root, 0, initialPath);
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AppendNode(StringBuilder sb, SchemaNode node, int depth, string path)
|
||||||
|
{
|
||||||
|
string San(string? s)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
if (string.IsNullOrEmpty(s)) return string.Empty;
|
||||||
|
return s.Replace("\t", " ").Replace("\r", " ").Replace("\n", " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnCopyAsTsvClick(object? sender, RoutedEventArgs e)
|
var min = node.Cardinality?.Min.ToString(CultureInfo.InvariantCulture) ?? string.Empty;
|
||||||
|
string max;
|
||||||
|
if (node.Cardinality?.MaxIsUnbounded == true)
|
||||||
|
max = "unbounded";
|
||||||
|
else
|
||||||
|
max = node.Cardinality != null ? node.Cardinality.Max.ToString(CultureInfo.InvariantCulture) : string.Empty;
|
||||||
|
|
||||||
|
var line = string.Join("\t", new[]
|
||||||
{
|
{
|
||||||
// Determine the node from the menu item's DataContext
|
depth.ToString(CultureInfo.InvariantCulture),
|
||||||
var node = (sender as MenuItem)?.DataContext as SchemaNode;
|
San(path),
|
||||||
if (node == null)
|
San(node.Name),
|
||||||
node = (sender as Control)?.GetVisualAncestors().OfType<Control>().FirstOrDefault()?.DataContext as SchemaNode;
|
San(node.Namespace),
|
||||||
if (node == null)
|
San(node.TypeName),
|
||||||
return;
|
San(node.BuiltInType),
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
San(node.ContentModel),
|
||||||
|
San(FormatConstraints(node)),
|
||||||
|
node.IsNillable ? "true" : "false"
|
||||||
|
});
|
||||||
|
sb.AppendLine(line);
|
||||||
|
|
||||||
var tsv = BuildTsv(node);
|
if (node.Children != null)
|
||||||
|
foreach (var child in node.Children)
|
||||||
var topLevel = TopLevel.GetTopLevel(this);
|
|
||||||
if (topLevel?.Clipboard != null)
|
|
||||||
{
|
{
|
||||||
await topLevel.Clipboard.SetTextAsync(tsv);
|
var nextPathSegment = child.Name ?? string.Empty;
|
||||||
|
var childPath = string.IsNullOrEmpty(San(path)) ? nextPathSegment : $"{path}/{nextPathSegment}";
|
||||||
|
AppendNode(sb, child, depth + 1, childPath);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatConstraints(SchemaNode node)
|
||||||
|
{
|
||||||
|
var cons = node.Constraints?.Constraints;
|
||||||
|
if (cons == null || cons.Count == 0) return string.Empty;
|
||||||
|
|
||||||
|
var dict = new SortedDictionary<string, SortedSet<string>>(StringComparer.Ordinal);
|
||||||
|
foreach (var c in cons)
|
||||||
|
{
|
||||||
|
var name = c.Name;
|
||||||
|
var value = c.Value;
|
||||||
|
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(value)) continue;
|
||||||
|
if (!dict.TryGetValue(name, out var set))
|
||||||
|
{
|
||||||
|
set = new SortedSet<string>(StringComparer.Ordinal);
|
||||||
|
dict[name] = set;
|
||||||
|
}
|
||||||
|
|
||||||
|
set.Add(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string BuildTsv(SchemaNode root)
|
if (dict.Count == 0) return string.Empty;
|
||||||
|
|
||||||
|
var parts = new List<string>(dict.Count);
|
||||||
|
foreach (var kvp in dict)
|
||||||
{
|
{
|
||||||
var sb = new StringBuilder();
|
var joinedValues = string.Join(",", kvp.Value);
|
||||||
// Header
|
parts.Add($"{kvp.Key}={joinedValues}");
|
||||||
sb.AppendLine("Depth\tPath\tName\tNamespace\tTypeName\tBuiltInType\tMinOccurs\tMaxOccurs\tContentModel\tConstraints\tIsNillable");
|
|
||||||
var initialPath = root.Name ?? string.Empty;
|
|
||||||
AppendNode(sb, root, 0, initialPath);
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AppendNode(StringBuilder sb, SchemaNode node, int depth, string path)
|
return string.Join(";", parts);
|
||||||
{
|
|
||||||
string San(string? s)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(s)) return string.Empty;
|
|
||||||
return s.Replace("\t", " ").Replace("\r", " ").Replace("\n", " ");
|
|
||||||
}
|
|
||||||
|
|
||||||
var min = node.Cardinality?.Min.ToString(CultureInfo.InvariantCulture) ?? string.Empty;
|
|
||||||
string max;
|
|
||||||
if (node.Cardinality?.MaxIsUnbounded == true)
|
|
||||||
max = "unbounded";
|
|
||||||
else
|
|
||||||
max = node.Cardinality != null ? node.Cardinality.Max.ToString(CultureInfo.InvariantCulture) : string.Empty;
|
|
||||||
|
|
||||||
var line = string.Join("\t", new[]
|
|
||||||
{
|
|
||||||
depth.ToString(CultureInfo.InvariantCulture),
|
|
||||||
San(path),
|
|
||||||
San(node.Name),
|
|
||||||
San(node.Namespace),
|
|
||||||
San(node.TypeName),
|
|
||||||
San(node.BuiltInType),
|
|
||||||
min,
|
|
||||||
max,
|
|
||||||
San(node.ContentModel),
|
|
||||||
San(FormatConstraints(node)),
|
|
||||||
node.IsNillable ? "true" : "false"
|
|
||||||
});
|
|
||||||
sb.AppendLine(line);
|
|
||||||
|
|
||||||
if (node.Children != null)
|
|
||||||
{
|
|
||||||
foreach (var child in node.Children)
|
|
||||||
{
|
|
||||||
var nextPathSegment = child.Name ?? string.Empty;
|
|
||||||
var childPath = string.IsNullOrEmpty(San(path)) ? nextPathSegment : $"{path}/{nextPathSegment}";
|
|
||||||
AppendNode(sb, child, depth + 1, childPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string FormatConstraints(SchemaNode node)
|
|
||||||
{
|
|
||||||
var cons = node.Constraints?.Constraints;
|
|
||||||
if (cons == null || cons.Count == 0) return string.Empty;
|
|
||||||
|
|
||||||
var dict = new System.Collections.Generic.SortedDictionary<string, System.Collections.Generic.SortedSet<string>>(System.StringComparer.Ordinal);
|
|
||||||
foreach (var c in cons)
|
|
||||||
{
|
|
||||||
var name = c.Name;
|
|
||||||
var value = c.Value;
|
|
||||||
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(value)) continue;
|
|
||||||
if (!dict.TryGetValue(name, out var set))
|
|
||||||
{
|
|
||||||
set = new System.Collections.Generic.SortedSet<string>(System.StringComparer.Ordinal);
|
|
||||||
dict[name] = set;
|
|
||||||
}
|
|
||||||
set.Add(value);
|
|
||||||
}
|
|
||||||
if (dict.Count == 0) return string.Empty;
|
|
||||||
|
|
||||||
var parts = new System.Collections.Generic.List<string>(dict.Count);
|
|
||||||
foreach (var kvp in dict)
|
|
||||||
{
|
|
||||||
var joinedValues = string.Join(",", kvp.Value);
|
|
||||||
parts.Add($"{kvp.Key}={joinedValues}");
|
|
||||||
}
|
|
||||||
return string.Join(";", parts);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,86 +1,94 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:m="clr-namespace:XSDVisualiser.Models;assembly=XSDVisualiser.Core"
|
xmlns:core="clr-namespace:XSDVisualiser.Core;assembly=XSDVisualiser.Core"
|
||||||
x:Class="XSDVisualiser.Desktop.Views.RightDetailsView"
|
x:Class="XSDVisualiser.Desktop.Views.RightDetailsView"
|
||||||
x:CompileBindings="False">
|
x:CompileBindings="False">
|
||||||
<Border Background="{DynamicResource PanelBackgroundBrush}"
|
<Border Background="{DynamicResource PanelBackgroundBrush}"
|
||||||
BorderBrush="{DynamicResource PanelBorderBrush}"
|
BorderBrush="{DynamicResource PanelBorderBrush}"
|
||||||
BorderThickness="1" CornerRadius="6" Margin="8,8,0,0">
|
BorderThickness="1" CornerRadius="6" Margin="8,8,0,0">
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<ContentControl Content="{Binding SelectedNode}">
|
<ContentControl Content="{Binding SelectedNode}">
|
||||||
<ContentControl.DataTemplates>
|
<ContentControl.DataTemplates>
|
||||||
<DataTemplate DataType="{x:Type m:SchemaNode}">
|
<DataTemplate DataType="{x:Type core:SchemaNode}">
|
||||||
<StackPanel Margin="12" Spacing="12">
|
<StackPanel Margin="12" Spacing="12">
|
||||||
<TextBlock Text="Details" FontSize="16" FontWeight="SemiBold"/>
|
<TextBlock Text="Details" FontSize="16" FontWeight="SemiBold" />
|
||||||
|
|
||||||
<!-- General info -->
|
<!-- General info -->
|
||||||
<Border BorderBrush="{DynamicResource PanelBorderBrush}" BorderThickness="1" CornerRadius="4" Padding="10">
|
<Border BorderBrush="{DynamicResource PanelBorderBrush}" BorderThickness="1"
|
||||||
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,*">
|
CornerRadius="4" Padding="10">
|
||||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Name:" FontWeight="Bold"/>
|
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,*">
|
||||||
<TextBlock Grid.Row="0" Grid.Column="1" Margin="8,0,0,0" Text="{Binding Name}"/>
|
<TextBlock Grid.Row="0" Grid.Column="0" Text="Name:" FontWeight="Bold" />
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="1" Margin="8,0,0,0" Text="{Binding Name}" />
|
||||||
|
|
||||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Built-in:" FontWeight="Bold"/>
|
<TextBlock Grid.Row="1" Grid.Column="0" Text="Built-in:" FontWeight="Bold" />
|
||||||
<TextBlock Grid.Row="1" Grid.Column="1" Margin="8,0,0,0" Text="{Binding BuiltInType}" FontWeight="Bold"/>
|
<TextBlock Grid.Row="1" Grid.Column="1" Margin="8,0,0,0"
|
||||||
|
Text="{Binding BuiltInType}" FontWeight="Bold" />
|
||||||
|
|
||||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Model:" FontWeight="Bold"/>
|
<TextBlock Grid.Row="2" Grid.Column="0" Text="Model:" FontWeight="Bold" />
|
||||||
<TextBlock Grid.Row="2" Grid.Column="1" Margin="8,0,0,0" Text="{Binding ContentModel}"/>
|
<TextBlock Grid.Row="2" Grid.Column="1" Margin="8,0,0,0"
|
||||||
|
Text="{Binding ContentModel}" />
|
||||||
|
|
||||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="Namespace:" FontWeight="Bold"/>
|
<TextBlock Grid.Row="3" Grid.Column="0" Text="Namespace:" FontWeight="Bold" />
|
||||||
<TextBlock Grid.Row="3" Grid.Column="1" Margin="8,0,0,0" Text="{Binding Namespace}"/>
|
<TextBlock Grid.Row="3" Grid.Column="1" Margin="8,0,0,0" Text="{Binding Namespace}" />
|
||||||
|
|
||||||
<TextBlock Grid.Row="4" Grid.Column="0" Text="Cardinality:" FontWeight="Bold"/>
|
<TextBlock Grid.Row="4" Grid.Column="0" Text="Cardinality:" FontWeight="Bold" />
|
||||||
<TextBlock Grid.Row="4" Grid.Column="1" Margin="8,0,0,0" Text="{Binding Cardinality, Converter={StaticResource CardinalityToLabel}}"/>
|
<TextBlock Grid.Row="4" Grid.Column="1" Margin="8,0,0,0"
|
||||||
|
Text="{Binding Cardinality, Converter={StaticResource CardinalityToLabel}}" />
|
||||||
|
|
||||||
<TextBlock Grid.Row="5" Grid.Column="0" Text="Nillable:" FontWeight="Bold"/>
|
<TextBlock Grid.Row="5" Grid.Column="0" Text="Nillable:" FontWeight="Bold" />
|
||||||
<TextBlock Grid.Row="5" Grid.Column="1" Margin="8,0,0,0" Text="{Binding IsNillable}"/>
|
<TextBlock Grid.Row="5" Grid.Column="1" Margin="8,0,0,0"
|
||||||
|
Text="{Binding IsNillable}" />
|
||||||
|
|
||||||
<TextBlock Grid.Row="6" Grid.Column="0" Text="Type:" FontWeight="Bold"/>
|
<TextBlock Grid.Row="6" Grid.Column="0" Text="Type:" FontWeight="Bold" />
|
||||||
<TextBlock Grid.Row="6" Grid.Column="1" Margin="8,0,0,0" Text="{Binding TypeName}"/>
|
<TextBlock Grid.Row="6" Grid.Column="1" Margin="8,0,0,0" Text="{Binding TypeName}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- Documentation -->
|
<!-- Documentation -->
|
||||||
<StackPanel IsVisible="{Binding Documentation, Converter={x:Static ObjectConverters.IsNotNull}}">
|
<StackPanel
|
||||||
<TextBlock Text="Documentation" FontWeight="SemiBold"/>
|
IsVisible="{Binding Documentation, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||||
<Border BorderBrush="{DynamicResource PanelBorderBrush}" BorderThickness="1" CornerRadius="4" Padding="10">
|
<TextBlock Text="Documentation" FontWeight="SemiBold" />
|
||||||
<TextBlock Text="{Binding Documentation}" TextWrapping="Wrap"/>
|
<Border BorderBrush="{DynamicResource PanelBorderBrush}" BorderThickness="1"
|
||||||
</Border>
|
CornerRadius="4" Padding="10">
|
||||||
</StackPanel>
|
<TextBlock Text="{Binding Documentation}" TextWrapping="Wrap" />
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<!-- Constraints -->
|
<!-- Constraints -->
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Text="Constraints" FontWeight="SemiBold"/>
|
<TextBlock Text="Constraints" FontWeight="SemiBold" />
|
||||||
<ContentControl Content="{Binding Constraints}">
|
<ContentControl Content="{Binding Constraints}">
|
||||||
<ContentControl.DataTemplates>
|
<ContentControl.DataTemplates>
|
||||||
<DataTemplate x:DataType="m:ConstraintSet">
|
<DataTemplate x:DataType="core:ConstraintSet">
|
||||||
<StackPanel Spacing="8">
|
<StackPanel Spacing="8">
|
||||||
<TextBlock Text="Base Type:" FontWeight="Bold"/>
|
<TextBlock Text="Base Type:" FontWeight="Bold" />
|
||||||
<TextBlock Text="{Binding BaseTypeName}"/>
|
<TextBlock Text="{Binding BaseTypeName}" />
|
||||||
|
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Text="Constraints" FontWeight="SemiBold"/>
|
<TextBlock Text="Constraints" FontWeight="SemiBold" />
|
||||||
<ItemsControl ItemsSource="{Binding Constraints}" IsVisible="{Binding Constraints, Converter={StaticResource HasItems}}">
|
<ItemsControl ItemsSource="{Binding Constraints}"
|
||||||
<ItemsControl.ItemTemplate>
|
IsVisible="{Binding Constraints, Converter={StaticResource HasItems}}">
|
||||||
<DataTemplate x:DataType="m:ConstraintEntry">
|
<ItemsControl.ItemTemplate>
|
||||||
<TextBlock>
|
<DataTemplate x:DataType="core:ConstraintEntry">
|
||||||
<Run Text="• "/>
|
<TextBlock>
|
||||||
<Run Text="{Binding Name}"/>
|
<Run Text="• " />
|
||||||
<Run Text=": "/>
|
<Run Text="{Binding Name}" />
|
||||||
<Run Text="{Binding Value}"/>
|
<Run Text=": " />
|
||||||
</TextBlock>
|
<Run Text="{Binding Value}" />
|
||||||
</DataTemplate>
|
</TextBlock>
|
||||||
</ItemsControl.ItemTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl>
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</ContentControl.DataTemplates>
|
||||||
|
</ContentControl>
|
||||||
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ContentControl.DataTemplates>
|
</ContentControl.DataTemplates>
|
||||||
</ContentControl>
|
</ContentControl>
|
||||||
</StackPanel>
|
</ScrollViewer>
|
||||||
</StackPanel>
|
</Border>
|
||||||
</DataTemplate>
|
|
||||||
</ContentControl.DataTemplates>
|
|
||||||
</ContentControl>
|
|
||||||
</ScrollViewer>
|
|
||||||
</Border>
|
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -1,12 +1,11 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
|
||||||
namespace XSDVisualiser.Desktop.Views
|
namespace XSDVisualiser.Desktop.Views;
|
||||||
|
|
||||||
|
public partial class RightDetailsView : UserControl
|
||||||
{
|
{
|
||||||
public partial class RightDetailsView : UserControl
|
public RightDetailsView()
|
||||||
{
|
{
|
||||||
public RightDetailsView()
|
InitializeComponent();
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,19 +1,19 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Avalonia" Version="11.1.3" />
|
<PackageReference Include="Avalonia" Version="11.1.3"/>
|
||||||
<PackageReference Include="Avalonia.Desktop" Version="11.1.3" />
|
<PackageReference Include="Avalonia.Desktop" Version="11.1.3"/>
|
||||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.1.3" />
|
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.1.3"/>
|
||||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.1.3" />
|
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.1.3"/>
|
||||||
<PackageReference Include="Avalonia.ReactiveUI" Version="11.1.3" />
|
<PackageReference Include="Avalonia.ReactiveUI" Version="11.1.3"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\XSDVisualiser.Core\XSDVisualiser.Core.csproj" />
|
<ProjectReference Include="..\XSDVisualiser.Core\XSDVisualiser.Core.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
21
XSDVisualiser/Models/AttributeInfo.cs
Normal file
21
XSDVisualiser/Models/AttributeInfo.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
|
namespace XSDVisualiser.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attribute definition extracted from XSD.
|
||||||
|
/// </summary>
|
||||||
|
public class AttributeInfo
|
||||||
|
{
|
||||||
|
[XmlAttribute] public string? Name { get; set; }
|
||||||
|
|
||||||
|
[XmlAttribute] public string? Namespace { get; set; }
|
||||||
|
|
||||||
|
[XmlAttribute] public string? Use { get; set; } // optional | required | prohibited
|
||||||
|
|
||||||
|
[XmlAttribute] public string? TypeName { get; set; }
|
||||||
|
|
||||||
|
[XmlAttribute] public string? BuiltInType { get; set; }
|
||||||
|
|
||||||
|
[XmlElement] public ConstraintSet? Constraints { get; set; }
|
||||||
|
}
|
||||||
10
XSDVisualiser/Models/ConstraintEntry.cs
Normal file
10
XSDVisualiser/Models/ConstraintEntry.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
|
namespace XSDVisualiser.Core;
|
||||||
|
|
||||||
|
public class ConstraintEntry
|
||||||
|
{
|
||||||
|
[XmlAttribute] public string? Name { get; set; }
|
||||||
|
|
||||||
|
[XmlAttribute] public string? Value { get; set; }
|
||||||
|
}
|
||||||
16
XSDVisualiser/Models/ConstraintSet.cs
Normal file
16
XSDVisualiser/Models/ConstraintSet.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
|
namespace XSDVisualiser.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SimpleType constraints (formerly called facets).
|
||||||
|
/// </summary>
|
||||||
|
public class ConstraintSet
|
||||||
|
{
|
||||||
|
[XmlAttribute] public string? BaseTypeName { get; set; }
|
||||||
|
|
||||||
|
// Generic catch-all list of constraints for dynamic display and tooling
|
||||||
|
[XmlArray("Constraints")]
|
||||||
|
[XmlArrayItem("Constraint")]
|
||||||
|
public List<ConstraintEntry> Constraints { get; set; } = new();
|
||||||
|
}
|
||||||
19
XSDVisualiser/Models/Occurs.cs
Normal file
19
XSDVisualiser/Models/Occurs.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
|
namespace XSDVisualiser.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Min/Max occurrences.
|
||||||
|
/// </summary>
|
||||||
|
public class Occurs
|
||||||
|
{
|
||||||
|
[XmlAttribute] public decimal Min { get; set; } = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If MaxIsUnbounded is true, Max is ignored.
|
||||||
|
/// </summary>
|
||||||
|
[XmlAttribute]
|
||||||
|
public decimal Max { get; set; } = 1;
|
||||||
|
|
||||||
|
[XmlAttribute] public bool MaxIsUnbounded { get; set; }
|
||||||
|
}
|
||||||
16
XSDVisualiser/Models/SchemaModel.cs
Normal file
16
XSDVisualiser/Models/SchemaModel.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
|
namespace XSDVisualiser.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Root of the parsed XSD representation.
|
||||||
|
/// </summary>
|
||||||
|
[XmlRoot("SchemaModel")]
|
||||||
|
public class SchemaModel
|
||||||
|
{
|
||||||
|
[XmlAttribute] public string? TargetNamespace { get; set; }
|
||||||
|
|
||||||
|
[XmlArray("RootElements")]
|
||||||
|
[XmlArrayItem("Element")]
|
||||||
|
public List<SchemaNode> RootElements { get; set; } = new();
|
||||||
|
}
|
||||||
51
XSDVisualiser/Models/SchemaNode.cs
Normal file
51
XSDVisualiser/Models/SchemaNode.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
|
namespace XSDVisualiser.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an element or complex content node in the schema.
|
||||||
|
/// </summary>
|
||||||
|
public class SchemaNode
|
||||||
|
{
|
||||||
|
[XmlAttribute] public string? Name { get; set; }
|
||||||
|
|
||||||
|
[XmlAttribute] public string? Namespace { get; set; }
|
||||||
|
|
||||||
|
[XmlAttribute] public string? TypeName { get; set; }
|
||||||
|
|
||||||
|
[XmlAttribute] public string? BuiltInType { get; set; }
|
||||||
|
|
||||||
|
[XmlAttribute] public bool IsNillable { get; set; }
|
||||||
|
|
||||||
|
[XmlElement] public Occurs Cardinality { get; set; } = new();
|
||||||
|
|
||||||
|
[XmlElement] public ConstraintSet? Constraints { get; set; }
|
||||||
|
|
||||||
|
[XmlArray("Attributes")]
|
||||||
|
[XmlArrayItem("Attribute")]
|
||||||
|
public List<AttributeInfo> Attributes { get; set; } = new();
|
||||||
|
|
||||||
|
[XmlArray("Children")]
|
||||||
|
[XmlArrayItem("Element")]
|
||||||
|
public List<SchemaNode> Children { get; set; } = new();
|
||||||
|
|
||||||
|
[XmlAttribute] public string? ContentModel { get; set; } // sequence | choice | all | simple
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Human-readable documentation extracted from xsd:annotation/xsd:documentation.
|
||||||
|
/// Prefer element-level documentation; falls back to type-level documentation.
|
||||||
|
/// </summary>
|
||||||
|
[XmlElement]
|
||||||
|
public string? Documentation { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
// Used by AutoCompleteBox to get text for filtering and matching
|
||||||
|
// Prefer Name, then TypeName, and fall back to base implementation
|
||||||
|
if (!string.IsNullOrEmpty(Name))
|
||||||
|
return Name!;
|
||||||
|
if (!string.IsNullOrEmpty(TypeName))
|
||||||
|
return TypeName!;
|
||||||
|
return base.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,140 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Xml.Serialization;
|
|
||||||
|
|
||||||
namespace XSDVisualiser.Models
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Root of the parsed XSD representation.
|
|
||||||
/// </summary>
|
|
||||||
[XmlRoot("SchemaModel")]
|
|
||||||
public class SchemaModel
|
|
||||||
{
|
|
||||||
[XmlAttribute]
|
|
||||||
public string? TargetNamespace { get; set; }
|
|
||||||
|
|
||||||
[XmlArray("RootElements")]
|
|
||||||
[XmlArrayItem("Element")]
|
|
||||||
public List<SchemaNode> RootElements { get; set; } = new();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents an element or complex content node in the schema.
|
|
||||||
/// </summary>
|
|
||||||
public class SchemaNode
|
|
||||||
{
|
|
||||||
[XmlAttribute]
|
|
||||||
public string? Name { get; set; }
|
|
||||||
|
|
||||||
[XmlAttribute]
|
|
||||||
public string? Namespace { get; set; }
|
|
||||||
|
|
||||||
[XmlAttribute]
|
|
||||||
public string? TypeName { get; set; }
|
|
||||||
|
|
||||||
[XmlAttribute]
|
|
||||||
public string? BuiltInType { get; set; }
|
|
||||||
|
|
||||||
[XmlAttribute]
|
|
||||||
public bool IsNillable { get; set; }
|
|
||||||
|
|
||||||
[XmlElement]
|
|
||||||
public Occurs Cardinality { get; set; } = new();
|
|
||||||
|
|
||||||
[XmlElement]
|
|
||||||
public ConstraintSet? Constraints { get; set; }
|
|
||||||
|
|
||||||
[XmlArray("Attributes")]
|
|
||||||
[XmlArrayItem("Attribute")]
|
|
||||||
public List<AttributeInfo> Attributes { get; set; } = new();
|
|
||||||
|
|
||||||
[XmlArray("Children")]
|
|
||||||
[XmlArrayItem("Element")]
|
|
||||||
public List<SchemaNode> Children { get; set; } = new();
|
|
||||||
|
|
||||||
[XmlAttribute]
|
|
||||||
public string? ContentModel { get; set; } // sequence | choice | all | simple
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Human-readable documentation extracted from xsd:annotation/xsd:documentation.
|
|
||||||
/// Prefer element-level documentation; falls back to type-level documentation.
|
|
||||||
/// </summary>
|
|
||||||
[XmlElement]
|
|
||||||
public string? Documentation { get; set; }
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
// Used by AutoCompleteBox to get text for filtering and matching
|
|
||||||
// Prefer Name, then TypeName, and fall back to base implementation
|
|
||||||
if (!string.IsNullOrEmpty(Name))
|
|
||||||
return Name!;
|
|
||||||
if (!string.IsNullOrEmpty(TypeName))
|
|
||||||
return TypeName!;
|
|
||||||
return base.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Min/Max occurrences.
|
|
||||||
/// </summary>
|
|
||||||
public class Occurs
|
|
||||||
{
|
|
||||||
[XmlAttribute]
|
|
||||||
public decimal Min { get; set; } = 1;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If MaxIsUnbounded is true, Max is ignored.
|
|
||||||
/// </summary>
|
|
||||||
[XmlAttribute]
|
|
||||||
public decimal Max { get; set; } = 1;
|
|
||||||
|
|
||||||
[XmlAttribute]
|
|
||||||
public bool MaxIsUnbounded { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Attribute definition extracted from XSD.
|
|
||||||
/// </summary>
|
|
||||||
public class AttributeInfo
|
|
||||||
{
|
|
||||||
[XmlAttribute]
|
|
||||||
public string? Name { get; set; }
|
|
||||||
|
|
||||||
[XmlAttribute]
|
|
||||||
public string? Namespace { get; set; }
|
|
||||||
|
|
||||||
[XmlAttribute]
|
|
||||||
public string? Use { get; set; } // optional | required | prohibited
|
|
||||||
|
|
||||||
[XmlAttribute]
|
|
||||||
public string? TypeName { get; set; }
|
|
||||||
|
|
||||||
[XmlAttribute]
|
|
||||||
public string? BuiltInType { get; set; }
|
|
||||||
|
|
||||||
[XmlElement]
|
|
||||||
public ConstraintSet? Constraints { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// SimpleType constraints (formerly called facets).
|
|
||||||
/// </summary>
|
|
||||||
public class ConstraintSet
|
|
||||||
{
|
|
||||||
[XmlAttribute]
|
|
||||||
public string? BaseTypeName { get; set; }
|
|
||||||
|
|
||||||
// Generic catch-all list of constraints for dynamic display and tooling
|
|
||||||
[XmlArray("Constraints")]
|
|
||||||
[XmlArrayItem("Constraint")]
|
|
||||||
public List<ConstraintEntry> Constraints { get; set; } = new();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ConstraintEntry
|
|
||||||
{
|
|
||||||
[XmlAttribute]
|
|
||||||
public string? Name { get; set; }
|
|
||||||
[XmlAttribute]
|
|
||||||
public string? Value { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,448 +1,396 @@
|
|||||||
|
using System.Text;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using System.Xml.Schema;
|
using System.Xml.Schema;
|
||||||
using System.Text;
|
|
||||||
using XSDVisualiser.Models;
|
|
||||||
|
|
||||||
namespace XSDVisualiser.Core
|
namespace XSDVisualiser.Core;
|
||||||
|
|
||||||
|
public class XsdSchemaParser
|
||||||
{
|
{
|
||||||
public class XsdSchemaParser
|
private readonly XmlSchemaSet _set = new();
|
||||||
|
|
||||||
|
public SchemaModel Parse(string xsdPath)
|
||||||
{
|
{
|
||||||
private readonly XmlSchemaSet _set = new();
|
_set.XmlResolver = new XmlUrlResolver();
|
||||||
|
using var reader = XmlReader.Create(xsdPath, new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore });
|
||||||
|
var schema = XmlSchema.Read(reader, ValidationCallback);
|
||||||
|
_set.Add(schema!);
|
||||||
|
_set.CompilationSettings = new XmlSchemaCompilationSettings { EnableUpaCheck = true };
|
||||||
|
_set.Compile();
|
||||||
|
|
||||||
public SchemaModel Parse(string xsdPath)
|
var model = new SchemaModel
|
||||||
{
|
{
|
||||||
_set.XmlResolver = new XmlUrlResolver();
|
TargetNamespace = schema!.TargetNamespace
|
||||||
using var reader = XmlReader.Create(xsdPath, new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore });
|
};
|
||||||
var schema = XmlSchema.Read(reader, ValidationCallback);
|
|
||||||
_set.Add(schema!);
|
|
||||||
_set.CompilationSettings = new XmlSchemaCompilationSettings { EnableUpaCheck = true };
|
|
||||||
_set.Compile();
|
|
||||||
|
|
||||||
var model = new SchemaModel
|
foreach (var globalEl in _set.Schemas().Cast<XmlSchema>()
|
||||||
{
|
.SelectMany(s => s.Elements.Values.Cast<XmlSchemaElement>()))
|
||||||
TargetNamespace = schema!.TargetNamespace
|
{
|
||||||
};
|
var node = BuildNodeForElement(globalEl, null);
|
||||||
|
model.RootElements.Add(node);
|
||||||
foreach (var globalEl in _set.Schemas().Cast<XmlSchema>()
|
|
||||||
.SelectMany(s => s.Elements.Values.Cast<XmlSchemaElement>()))
|
|
||||||
{
|
|
||||||
var node = BuildNodeForElement(globalEl, parentContentModel: null);
|
|
||||||
model.RootElements.Add(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
return model;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ValidationCallback(object? sender, ValidationEventArgs e)
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ValidationCallback(object? sender, ValidationEventArgs e)
|
||||||
|
{
|
||||||
|
// For now, we do not throw; we capture compiled info best-effort.
|
||||||
|
// Console.Error.WriteLine($"[XSD Validation {e.Severity}] {e.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private SchemaNode BuildNodeForElement(XmlSchemaElement element, string? parentContentModel)
|
||||||
|
{
|
||||||
|
var node = new SchemaNode
|
||||||
{
|
{
|
||||||
// For now, we do not throw; we capture compiled info best-effort.
|
Name = element.Name ?? element.RefName.Name,
|
||||||
// Console.Error.WriteLine($"[XSD Validation {e.Severity}] {e.Message}");
|
Namespace = (element.QualifiedName.IsEmpty ? element.RefName : element.QualifiedName).Namespace,
|
||||||
}
|
IsNillable = element.IsNillable,
|
||||||
|
Cardinality = new Occurs
|
||||||
private SchemaNode BuildNodeForElement(XmlSchemaElement element, string? parentContentModel)
|
|
||||||
{
|
|
||||||
var node = new SchemaNode
|
|
||||||
{
|
{
|
||||||
Name = element.Name ?? element.RefName.Name,
|
Min = element.MinOccurs,
|
||||||
Namespace = (element.QualifiedName.IsEmpty ? element.RefName : element.QualifiedName).Namespace,
|
Max = element.MaxOccurs,
|
||||||
IsNillable = element.IsNillable,
|
MaxIsUnbounded = element.MaxOccursString == "unbounded"
|
||||||
Cardinality = new Occurs
|
},
|
||||||
{
|
ContentModel = parentContentModel
|
||||||
Min = element.MinOccurs,
|
};
|
||||||
Max = element.MaxOccurs,
|
|
||||||
MaxIsUnbounded = element.MaxOccursString == "unbounded"
|
|
||||||
},
|
|
||||||
ContentModel = parentContentModel
|
|
||||||
};
|
|
||||||
|
|
||||||
// Prefer element-level documentation
|
// Prefer element-level documentation
|
||||||
node.Documentation = ExtractDocumentation(element);
|
node.Documentation = ExtractDocumentation(element);
|
||||||
|
|
||||||
var type = ResolveElementType(element);
|
var type = ResolveElementType(element);
|
||||||
if (type == null) return node;
|
if (type == null) return node;
|
||||||
node.TypeName = GetQualifiedTypeName(type);
|
node.TypeName = GetQualifiedTypeName(type);
|
||||||
if (type.Datatype != null)
|
if (type.Datatype != null) node.BuiltInType = type.Datatype.TypeCode.ToString();
|
||||||
{
|
|
||||||
node.BuiltInType = type.Datatype.TypeCode.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback to type-level documentation if none on element
|
|
||||||
if (string.IsNullOrWhiteSpace(node.Documentation))
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case XmlSchemaComplexType ctDoc:
|
|
||||||
node.Documentation = ExtractDocumentation(ctDoc);
|
|
||||||
break;
|
|
||||||
case XmlSchemaSimpleType stDoc:
|
|
||||||
node.Documentation = ExtractDocumentation(stDoc);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Fallback to type-level documentation if none on element
|
||||||
|
if (string.IsNullOrWhiteSpace(node.Documentation))
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case XmlSchemaComplexType ct:
|
case XmlSchemaComplexType ctDoc:
|
||||||
HandleComplexType(node, ct);
|
node.Documentation = ExtractDocumentation(ctDoc);
|
||||||
break;
|
break;
|
||||||
case XmlSchemaSimpleType st:
|
case XmlSchemaSimpleType stDoc:
|
||||||
node.ContentModel = "simple";
|
node.Documentation = ExtractDocumentation(stDoc);
|
||||||
node.Constraints = ExtractConstraints(st);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return node;
|
switch (type)
|
||||||
}
|
|
||||||
|
|
||||||
private static string? GetQualifiedTypeName(XmlSchemaType type)
|
|
||||||
{
|
{
|
||||||
if (!type.QualifiedName.IsEmpty) return type.QualifiedName.ToString();
|
case XmlSchemaComplexType ct:
|
||||||
return type.BaseXmlSchemaType is { QualifiedName.IsEmpty: false }
|
HandleComplexType(node, ct);
|
||||||
? type.BaseXmlSchemaType.QualifiedName.ToString()
|
break;
|
||||||
: type.Name;
|
case XmlSchemaSimpleType st:
|
||||||
}
|
|
||||||
|
|
||||||
private XmlSchemaType? ResolveElementType(XmlSchemaElement el)
|
|
||||||
{
|
|
||||||
if (el.ElementSchemaType != null) return el.ElementSchemaType;
|
|
||||||
if (!el.SchemaTypeName.IsEmpty)
|
|
||||||
{
|
|
||||||
return _set.GlobalTypes[el.SchemaTypeName] as XmlSchemaType;
|
|
||||||
}
|
|
||||||
|
|
||||||
return el.SchemaType;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleComplexType(SchemaNode node, XmlSchemaComplexType ct)
|
|
||||||
{
|
|
||||||
// Collect attributes (ensure uniqueness)
|
|
||||||
var seenAttrKeys = new HashSet<string>(StringComparer.Ordinal);
|
|
||||||
|
|
||||||
// 1) Compiled attribute uses (includes inherited/group attributes after Compile)
|
|
||||||
foreach (var attr in ct.AttributeUses.Values.OfType<XmlSchemaAttribute>())
|
|
||||||
{
|
|
||||||
var qn = attr.QualifiedName.IsEmpty ? attr.RefName : attr.QualifiedName;
|
|
||||||
var key = qn.ToString();
|
|
||||||
if (seenAttrKeys.Add(key))
|
|
||||||
{
|
|
||||||
node.Attributes.Add(ExtractAttribute(attr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2) Uncompiled attributes directly on the type (fallback)
|
|
||||||
foreach (var a in ct.Attributes.OfType<XmlSchemaAttribute>())
|
|
||||||
{
|
|
||||||
var qn = a.QualifiedName.IsEmpty ? a.RefName : a.QualifiedName;
|
|
||||||
var key = qn.ToString();
|
|
||||||
if (seenAttrKeys.Add(key))
|
|
||||||
{
|
|
||||||
node.Attributes.Add(ExtractAttribute(a));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3) Attributes from complexContent extension/restriction
|
|
||||||
if (ct.ContentModel is XmlSchemaComplexContent cc)
|
|
||||||
{
|
|
||||||
switch (cc.Content)
|
|
||||||
{
|
|
||||||
case XmlSchemaComplexContentExtension cext:
|
|
||||||
{
|
|
||||||
foreach (var a in cext.Attributes.OfType<XmlSchemaAttribute>())
|
|
||||||
{
|
|
||||||
var qn = a.QualifiedName.IsEmpty ? a.RefName : a.QualifiedName;
|
|
||||||
var key = qn.ToString();
|
|
||||||
if (seenAttrKeys.Add(key))
|
|
||||||
{
|
|
||||||
node.Attributes.Add(ExtractAttribute(a));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case XmlSchemaComplexContentRestriction cres:
|
|
||||||
{
|
|
||||||
foreach (var a in cres.Attributes.OfType<XmlSchemaAttribute>())
|
|
||||||
{
|
|
||||||
var qn = a.QualifiedName.IsEmpty ? a.RefName : a.QualifiedName;
|
|
||||||
var key = qn.ToString();
|
|
||||||
if (seenAttrKeys.Add(key))
|
|
||||||
{
|
|
||||||
node.Attributes.Add(ExtractAttribute(a));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Content model
|
|
||||||
if (ct.ContentTypeParticle is XmlSchemaGroupBase group)
|
|
||||||
{
|
|
||||||
var content = group switch
|
|
||||||
{
|
|
||||||
XmlSchemaSequence => "sequence",
|
|
||||||
XmlSchemaChoice => "choice",
|
|
||||||
XmlSchemaAll => "all",
|
|
||||||
_ => "group"
|
|
||||||
};
|
|
||||||
node.ContentModel = content;
|
|
||||||
|
|
||||||
foreach (var item in group.Items)
|
|
||||||
{
|
|
||||||
switch (item)
|
|
||||||
{
|
|
||||||
case XmlSchemaElement childEl:
|
|
||||||
node.Children.Add(BuildNodeForElement(childEl, content));
|
|
||||||
break;
|
|
||||||
case XmlSchemaGroupBase nestedGroup:
|
|
||||||
// Flatten nested groups by introducing synthetic nodes
|
|
||||||
var synthetic = new SchemaNode
|
|
||||||
{
|
|
||||||
Name = "(group)",
|
|
||||||
Namespace = node.Namespace,
|
|
||||||
ContentModel = nestedGroup switch
|
|
||||||
{
|
|
||||||
XmlSchemaSequence => "sequence",
|
|
||||||
XmlSchemaChoice => "choice",
|
|
||||||
XmlSchemaAll => "all",
|
|
||||||
_ => "group"
|
|
||||||
},
|
|
||||||
Cardinality = new Occurs { Min = nestedGroup.MinOccurs, Max = nestedGroup.MaxOccurs, MaxIsUnbounded = nestedGroup.MaxOccursString == "unbounded" }
|
|
||||||
};
|
|
||||||
foreach (var nestedItem in nestedGroup.Items)
|
|
||||||
{
|
|
||||||
if (nestedItem is XmlSchemaElement ngChild)
|
|
||||||
{
|
|
||||||
synthetic.Children.Add(BuildNodeForElement(ngChild, synthetic.ContentModel));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
node.Children.Add(synthetic);
|
|
||||||
break;
|
|
||||||
// Skip other particles for now
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (ct.ContentType == XmlSchemaContentType.TextOnly && ct.ContentModel is XmlSchemaSimpleContent simpleContent)
|
|
||||||
{
|
|
||||||
node.ContentModel = "simple";
|
node.ContentModel = "simple";
|
||||||
switch (simpleContent.Content)
|
node.Constraints = ExtractConstraints(st);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? GetQualifiedTypeName(XmlSchemaType type)
|
||||||
|
{
|
||||||
|
if (!type.QualifiedName.IsEmpty) return type.QualifiedName.ToString();
|
||||||
|
return type.BaseXmlSchemaType is { QualifiedName.IsEmpty: false }
|
||||||
|
? type.BaseXmlSchemaType.QualifiedName.ToString()
|
||||||
|
: type.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private XmlSchemaType? ResolveElementType(XmlSchemaElement el)
|
||||||
|
{
|
||||||
|
if (el.ElementSchemaType != null) return el.ElementSchemaType;
|
||||||
|
if (!el.SchemaTypeName.IsEmpty) return _set.GlobalTypes[el.SchemaTypeName] as XmlSchemaType;
|
||||||
|
|
||||||
|
return el.SchemaType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleComplexType(SchemaNode node, XmlSchemaComplexType ct)
|
||||||
|
{
|
||||||
|
// Collect attributes (ensure uniqueness)
|
||||||
|
var seenAttrKeys = new HashSet<string>(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
// 1) Compiled attribute uses (includes inherited/group attributes after Compile)
|
||||||
|
foreach (var attr in ct.AttributeUses.Values.OfType<XmlSchemaAttribute>())
|
||||||
|
{
|
||||||
|
var qn = attr.QualifiedName.IsEmpty ? attr.RefName : attr.QualifiedName;
|
||||||
|
var key = qn.ToString();
|
||||||
|
if (seenAttrKeys.Add(key)) node.Attributes.Add(ExtractAttribute(attr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Uncompiled attributes directly on the type (fallback)
|
||||||
|
foreach (var a in ct.Attributes.OfType<XmlSchemaAttribute>())
|
||||||
|
{
|
||||||
|
var qn = a.QualifiedName.IsEmpty ? a.RefName : a.QualifiedName;
|
||||||
|
var key = qn.ToString();
|
||||||
|
if (seenAttrKeys.Add(key)) node.Attributes.Add(ExtractAttribute(a));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) Attributes from complexContent extension/restriction
|
||||||
|
if (ct.ContentModel is XmlSchemaComplexContent cc)
|
||||||
|
switch (cc.Content)
|
||||||
|
{
|
||||||
|
case XmlSchemaComplexContentExtension cext:
|
||||||
{
|
{
|
||||||
case XmlSchemaSimpleContentExtension ext:
|
foreach (var a in cext.Attributes.OfType<XmlSchemaAttribute>())
|
||||||
{
|
{
|
||||||
var baseType = ResolveType(ext.BaseTypeName);
|
var qn = a.QualifiedName.IsEmpty ? a.RefName : a.QualifiedName;
|
||||||
if (baseType is XmlSchemaSimpleType st)
|
var key = qn.ToString();
|
||||||
{
|
if (seenAttrKeys.Add(key)) node.Attributes.Add(ExtractAttribute(a));
|
||||||
node.Constraints = ExtractConstraints(st);
|
|
||||||
node.TypeName ??= GetQualifiedTypeName(st);
|
|
||||||
node.BuiltInType ??= st.Datatype?.TypeCode.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var attr in ext.Attributes.OfType<XmlSchemaAttribute>())
|
|
||||||
{
|
|
||||||
var qn = attr.QualifiedName.IsEmpty ? attr.RefName : attr.QualifiedName;
|
|
||||||
var key = qn.ToString();
|
|
||||||
if (seenAttrKeys.Add(key))
|
|
||||||
{
|
|
||||||
node.Attributes.Add(ExtractAttribute(attr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case XmlSchemaSimpleContentRestriction res:
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case XmlSchemaComplexContentRestriction cres:
|
||||||
|
{
|
||||||
|
foreach (var a in cres.Attributes.OfType<XmlSchemaAttribute>())
|
||||||
{
|
{
|
||||||
var baseType = ResolveType(res.BaseTypeName);
|
var qn = a.QualifiedName.IsEmpty ? a.RefName : a.QualifiedName;
|
||||||
if (baseType is XmlSchemaSimpleType st)
|
var key = qn.ToString();
|
||||||
{
|
if (seenAttrKeys.Add(key)) node.Attributes.Add(ExtractAttribute(a));
|
||||||
var cons = ExtractConstraints(st);
|
|
||||||
MergeFacets(cons, res.Facets);
|
|
||||||
node.Constraints = cons;
|
|
||||||
node.TypeName ??= GetQualifiedTypeName(st);
|
|
||||||
node.BuiltInType ??= st.Datatype?.TypeCode.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private XmlSchemaType? ResolveType(XmlQualifiedName qname)
|
// Content model
|
||||||
|
if (ct.ContentTypeParticle is XmlSchemaGroupBase group)
|
||||||
{
|
{
|
||||||
if (qname.IsEmpty) return null;
|
var content = group switch
|
||||||
return _set.GlobalTypes[qname] as XmlSchemaType;
|
|
||||||
}
|
|
||||||
|
|
||||||
private AttributeInfo ExtractAttribute(XmlSchemaAttribute attr)
|
|
||||||
{
|
|
||||||
var info = new AttributeInfo
|
|
||||||
{
|
{
|
||||||
Name = attr.Name ?? attr.RefName.Name,
|
XmlSchemaSequence => "sequence",
|
||||||
Namespace = (attr.QualifiedName.IsEmpty ? attr.RefName : attr.QualifiedName).Namespace,
|
XmlSchemaChoice => "choice",
|
||||||
Use = attr.Use.ToString()
|
XmlSchemaAll => "all",
|
||||||
|
_ => "group"
|
||||||
};
|
};
|
||||||
|
node.ContentModel = content;
|
||||||
|
|
||||||
XmlSchemaSimpleType? st = null;
|
foreach (var item in group.Items)
|
||||||
if (attr.AttributeSchemaType != null) st = attr.AttributeSchemaType as XmlSchemaSimpleType;
|
switch (item)
|
||||||
else if (!attr.SchemaTypeName.IsEmpty) st = ResolveType(attr.SchemaTypeName) as XmlSchemaSimpleType;
|
|
||||||
else if (attr.SchemaType != null) st = attr.SchemaType;
|
|
||||||
|
|
||||||
if (st == null) return info;
|
|
||||||
info.TypeName = GetQualifiedTypeName(st);
|
|
||||||
info.BuiltInType = st.Datatype?.TypeCode.ToString();
|
|
||||||
info.Constraints = ExtractConstraints(st);
|
|
||||||
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ConstraintSet? ExtractConstraints(XmlSchemaSimpleType st)
|
|
||||||
{
|
|
||||||
var cons = new ConstraintSet
|
|
||||||
{
|
|
||||||
BaseTypeName = GetQualifiedTypeName(st.BaseXmlSchemaType)
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (st.Content)
|
|
||||||
{
|
|
||||||
case XmlSchemaSimpleTypeRestriction restr:
|
|
||||||
MergeFacets(cons, restr.Facets);
|
|
||||||
break;
|
|
||||||
case XmlSchemaSimpleTypeList list:
|
|
||||||
{
|
{
|
||||||
if (!list.ItemTypeName.IsEmpty)
|
case XmlSchemaElement childEl:
|
||||||
{
|
node.Children.Add(BuildNodeForElement(childEl, content));
|
||||||
var baseType = ResolveType(list.ItemTypeName);
|
break;
|
||||||
if (baseType is XmlSchemaSimpleType itemSt)
|
case XmlSchemaGroupBase nestedGroup:
|
||||||
|
// Flatten nested groups by introducing synthetic nodes
|
||||||
|
var synthetic = new SchemaNode
|
||||||
{
|
{
|
||||||
var sub = ExtractConstraints(itemSt);
|
Name = "(group)",
|
||||||
Merge(cons, sub);
|
Namespace = node.Namespace,
|
||||||
}
|
ContentModel = nestedGroup switch
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case XmlSchemaSimpleTypeUnion union:
|
|
||||||
{
|
|
||||||
foreach (var memberType in union.BaseMemberTypes)
|
|
||||||
{
|
|
||||||
if (memberType is { } mst)
|
|
||||||
{
|
|
||||||
var sub = ExtractConstraints(mst);
|
|
||||||
Merge(cons, sub);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cons;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Merge(ConstraintSet target, ConstraintSet? source)
|
|
||||||
{
|
|
||||||
if (source == null) return;
|
|
||||||
|
|
||||||
// Merge generic constraints (name + value de-duplication)
|
|
||||||
if (source.Constraints != null)
|
|
||||||
{
|
|
||||||
foreach (var sc in source.Constraints)
|
|
||||||
{
|
|
||||||
var exists = false;
|
|
||||||
foreach (var tc in target.Constraints)
|
|
||||||
{
|
|
||||||
if (string.Equals(tc.Name, sc.Name, StringComparison.Ordinal) && string.Equals(tc.Value, sc.Value, StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
exists = true; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!exists)
|
|
||||||
{
|
|
||||||
target.Constraints.Add(new ConstraintEntry { Name = sc.Name, Value = sc.Value });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void MergeFacets(ConstraintSet cons, XmlSchemaObjectCollection facets)
|
|
||||||
{
|
|
||||||
foreach (var f in facets)
|
|
||||||
{
|
|
||||||
// Capture all constraints generically for dynamic display
|
|
||||||
if (f is XmlSchemaFacet baseFacet)
|
|
||||||
{
|
|
||||||
var name = GetFacetName(f);
|
|
||||||
var value = baseFacet.Value;
|
|
||||||
if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(value))
|
|
||||||
{
|
|
||||||
var exists = false;
|
|
||||||
foreach (var entry in cons.Constraints)
|
|
||||||
{
|
|
||||||
if (string.Equals(entry.Name, name, StringComparison.Ordinal) && string.Equals(entry.Value, value, StringComparison.Ordinal))
|
|
||||||
{
|
{
|
||||||
exists = true; break;
|
XmlSchemaSequence => "sequence",
|
||||||
}
|
XmlSchemaChoice => "choice",
|
||||||
}
|
XmlSchemaAll => "all",
|
||||||
if (!exists)
|
_ => "group"
|
||||||
{
|
},
|
||||||
cons.Constraints.Add(new ConstraintEntry { Name = name, Value = value });
|
Cardinality = new Occurs
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetFacetName(XmlSchemaObject facet)
|
|
||||||
{
|
|
||||||
var typeName = facet.GetType().Name; // e.g., XmlSchemaMinInclusiveFacet
|
|
||||||
if (typeName.StartsWith("XmlSchema", StringComparison.Ordinal))
|
|
||||||
typeName = typeName.Substring("XmlSchema".Length);
|
|
||||||
if (typeName.EndsWith("Facet", StringComparison.Ordinal))
|
|
||||||
typeName = typeName.Substring(0, typeName.Length - "Facet".Length);
|
|
||||||
if (typeName.Length == 0) return typeName;
|
|
||||||
return char.ToLowerInvariant(typeName[0]) + typeName.Substring(1);
|
|
||||||
}
|
|
||||||
private static string? ExtractDocumentation(XmlSchemaAnnotated? annotated)
|
|
||||||
{
|
|
||||||
if (annotated?.Annotation == null) return null;
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
foreach (var item in annotated.Annotation.Items)
|
|
||||||
{
|
|
||||||
if (item is not XmlSchemaDocumentation doc) continue;
|
|
||||||
|
|
||||||
if (doc.Markup is { Length: > 0 })
|
|
||||||
{
|
|
||||||
var pieceBuilder = new StringBuilder();
|
|
||||||
foreach (var node in doc.Markup)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var text = node?.InnerText;
|
|
||||||
if (!string.IsNullOrWhiteSpace(text))
|
|
||||||
{
|
{
|
||||||
pieceBuilder.Append(text);
|
Min = nestedGroup.MinOccurs, Max = nestedGroup.MaxOccurs,
|
||||||
|
MaxIsUnbounded = nestedGroup.MaxOccursString == "unbounded"
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
catch
|
foreach (var nestedItem in nestedGroup.Items)
|
||||||
{
|
if (nestedItem is XmlSchemaElement ngChild)
|
||||||
// ignore malformed nodes
|
synthetic.Children.Add(BuildNodeForElement(ngChild, synthetic.ContentModel));
|
||||||
}
|
|
||||||
|
node.Children.Add(synthetic);
|
||||||
|
break;
|
||||||
|
// Skip other particles for now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ct is
|
||||||
|
{
|
||||||
|
ContentType: XmlSchemaContentType.TextOnly, ContentModel: XmlSchemaSimpleContent simpleContent
|
||||||
|
})
|
||||||
|
{
|
||||||
|
node.ContentModel = "simple";
|
||||||
|
switch (simpleContent.Content)
|
||||||
|
{
|
||||||
|
case XmlSchemaSimpleContentExtension ext:
|
||||||
|
{
|
||||||
|
var baseType = ResolveType(ext.BaseTypeName);
|
||||||
|
if (baseType is XmlSchemaSimpleType st)
|
||||||
|
{
|
||||||
|
node.Constraints = ExtractConstraints(st);
|
||||||
|
node.TypeName ??= GetQualifiedTypeName(st);
|
||||||
|
node.BuiltInType ??= st.Datatype?.TypeCode.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
var piece = pieceBuilder.ToString().Trim();
|
foreach (var attr in ext.Attributes.OfType<XmlSchemaAttribute>())
|
||||||
if (string.IsNullOrWhiteSpace(piece)) continue;
|
{
|
||||||
|
var qn = attr.QualifiedName.IsEmpty ? attr.RefName : attr.QualifiedName;
|
||||||
|
var key = qn.ToString();
|
||||||
|
if (seenAttrKeys.Add(key)) node.Attributes.Add(ExtractAttribute(attr));
|
||||||
|
}
|
||||||
|
|
||||||
if (sb.Length > 0) sb.AppendLine().AppendLine();
|
break;
|
||||||
sb.Append(piece);
|
|
||||||
}
|
}
|
||||||
else if (!string.IsNullOrWhiteSpace(doc.Source))
|
case XmlSchemaSimpleContentRestriction res:
|
||||||
{
|
{
|
||||||
// If there is a source but no markup, skip; we only render text.
|
var baseType = ResolveType(res.BaseTypeName);
|
||||||
|
if (baseType is XmlSchemaSimpleType st)
|
||||||
|
{
|
||||||
|
var cons = ExtractConstraints(st);
|
||||||
|
MergeFacets(cons, res.Facets);
|
||||||
|
node.Constraints = cons;
|
||||||
|
node.TypeName ??= GetQualifiedTypeName(st);
|
||||||
|
node.BuiltInType ??= st.Datatype?.TypeCode.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.Length == 0 ? null : sb.ToString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private XmlSchemaType? ResolveType(XmlQualifiedName qname)
|
||||||
|
{
|
||||||
|
if (qname.IsEmpty) return null;
|
||||||
|
return _set.GlobalTypes[qname] as XmlSchemaType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AttributeInfo ExtractAttribute(XmlSchemaAttribute attr)
|
||||||
|
{
|
||||||
|
var info = new AttributeInfo
|
||||||
|
{
|
||||||
|
Name = attr.Name ?? attr.RefName.Name,
|
||||||
|
Namespace = (attr.QualifiedName.IsEmpty ? attr.RefName : attr.QualifiedName).Namespace,
|
||||||
|
Use = attr.Use.ToString()
|
||||||
|
};
|
||||||
|
|
||||||
|
XmlSchemaSimpleType? st = null;
|
||||||
|
if (attr.AttributeSchemaType != null) st = attr.AttributeSchemaType;
|
||||||
|
else if (!attr.SchemaTypeName.IsEmpty) st = ResolveType(attr.SchemaTypeName) as XmlSchemaSimpleType;
|
||||||
|
else if (attr.SchemaType != null) st = attr.SchemaType;
|
||||||
|
|
||||||
|
if (st == null) return info;
|
||||||
|
info.TypeName = GetQualifiedTypeName(st);
|
||||||
|
info.BuiltInType = st.Datatype?.TypeCode.ToString();
|
||||||
|
info.Constraints = ExtractConstraints(st);
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConstraintSet? ExtractConstraints(XmlSchemaSimpleType st)
|
||||||
|
{
|
||||||
|
var cons = new ConstraintSet
|
||||||
|
{
|
||||||
|
BaseTypeName = GetQualifiedTypeName(st.BaseXmlSchemaType)
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (st.Content)
|
||||||
|
{
|
||||||
|
case XmlSchemaSimpleTypeRestriction restr:
|
||||||
|
MergeFacets(cons, restr.Facets);
|
||||||
|
break;
|
||||||
|
case XmlSchemaSimpleTypeList list:
|
||||||
|
{
|
||||||
|
if (!list.ItemTypeName.IsEmpty)
|
||||||
|
{
|
||||||
|
var baseType = ResolveType(list.ItemTypeName);
|
||||||
|
if (baseType is XmlSchemaSimpleType itemSt)
|
||||||
|
{
|
||||||
|
var sub = ExtractConstraints(itemSt);
|
||||||
|
Merge(cons, sub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case XmlSchemaSimpleTypeUnion union:
|
||||||
|
{
|
||||||
|
foreach (var memberType in union.BaseMemberTypes)
|
||||||
|
{
|
||||||
|
if (memberType is not { } mst) continue;
|
||||||
|
var sub = ExtractConstraints(mst);
|
||||||
|
Merge(cons, sub);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cons;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Merge(ConstraintSet target, ConstraintSet? source)
|
||||||
|
{
|
||||||
|
// Merge generic constraints (name + value de-duplication)
|
||||||
|
if (source?.Constraints == null) return;
|
||||||
|
|
||||||
|
foreach (var sc in source.Constraints)
|
||||||
|
{
|
||||||
|
var exists = target.Constraints.Any(tc =>
|
||||||
|
string.Equals(tc.Name, sc.Name, StringComparison.Ordinal) &&
|
||||||
|
string.Equals(tc.Value, sc.Value, StringComparison.Ordinal));
|
||||||
|
if (!exists) target.Constraints.Add(new ConstraintEntry { Name = sc.Name, Value = sc.Value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void MergeFacets(ConstraintSet cons, XmlSchemaObjectCollection facets)
|
||||||
|
{
|
||||||
|
foreach (var f in facets)
|
||||||
|
{
|
||||||
|
// Capture all constraints generically for dynamic display
|
||||||
|
if (f is not XmlSchemaFacet baseFacet) continue;
|
||||||
|
|
||||||
|
var name = GetFacetName(f);
|
||||||
|
var value = baseFacet.Value;
|
||||||
|
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(value)) continue;
|
||||||
|
|
||||||
|
var exists = cons.Constraints.Any(entry =>
|
||||||
|
string.Equals(entry.Name, name, StringComparison.Ordinal) &&
|
||||||
|
string.Equals(entry.Value, value, StringComparison.Ordinal));
|
||||||
|
if (!exists) cons.Constraints.Add(new ConstraintEntry { Name = name, Value = value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetFacetName(XmlSchemaObject facet)
|
||||||
|
{
|
||||||
|
var typeName = facet.GetType().Name; // e.g., XmlSchemaMinInclusiveFacet
|
||||||
|
if (typeName.StartsWith("XmlSchema", StringComparison.Ordinal))
|
||||||
|
typeName = typeName.Substring("XmlSchema".Length);
|
||||||
|
if (typeName.EndsWith("Facet", StringComparison.Ordinal))
|
||||||
|
typeName = typeName.Substring(0, typeName.Length - "Facet".Length);
|
||||||
|
if (typeName.Length == 0) return typeName;
|
||||||
|
return char.ToLowerInvariant(typeName[0]) + typeName.Substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? ExtractDocumentation(XmlSchemaAnnotated? annotated)
|
||||||
|
{
|
||||||
|
if (annotated?.Annotation == null) return null;
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
foreach (var item in annotated.Annotation.Items)
|
||||||
|
{
|
||||||
|
if (item is not XmlSchemaDocumentation doc) continue;
|
||||||
|
|
||||||
|
if (doc.Markup is { Length: > 0 })
|
||||||
|
{
|
||||||
|
var pieceBuilder = new StringBuilder();
|
||||||
|
foreach (var node in doc.Markup)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var text = node?.InnerText;
|
||||||
|
if (!string.IsNullOrWhiteSpace(text)) pieceBuilder.Append(text);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore malformed nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
var piece = pieceBuilder.ToString().Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(piece)) continue;
|
||||||
|
|
||||||
|
if (sb.Length > 0) sb.AppendLine().AppendLine();
|
||||||
|
sb.Append(piece);
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrWhiteSpace(doc.Source))
|
||||||
|
{
|
||||||
|
// If there is a source but no markup, skip; we only render text.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.Length == 0 ? null : sb.ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -10,7 +10,8 @@ if (args.Length == 0 || args[0] is "-h" or "--help")
|
|||||||
var inputPath = args[0];
|
var inputPath = args[0];
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const string inputPath = "/home/frederik/Work/XSDVisualiser/XSDVisualiser/Rescources/SF2900/SF2900_EP_MS1-2/DistributionServiceMsgV2.xsd";
|
const string inputPath =
|
||||||
|
"/home/frederik/Work/XSDVisualiser/XSDVisualiser/Rescources/SF2900/SF2900_EP_MS1-2/DistributionServiceMsgV2.xsd";
|
||||||
|
|
||||||
if (!File.Exists(inputPath))
|
if (!File.Exists(inputPath))
|
||||||
{
|
{
|
||||||
@ -20,8 +21,7 @@ if (!File.Exists(inputPath))
|
|||||||
|
|
||||||
var format = "json";
|
var format = "json";
|
||||||
string? outPath = null;
|
string? outPath = null;
|
||||||
for (int i = 1; i < args.Length; i++)
|
for (var i = 1; i < args.Length; i++)
|
||||||
{
|
|
||||||
switch (args[i])
|
switch (args[i])
|
||||||
{
|
{
|
||||||
case "--format":
|
case "--format":
|
||||||
@ -34,6 +34,7 @@ for (int i = 1; i < args.Length; i++)
|
|||||||
Console.Error.WriteLine("--format requires a value: xml or json");
|
Console.Error.WriteLine("--format requires a value: xml or json");
|
||||||
Environment.Exit(2);
|
Environment.Exit(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "--out":
|
case "--out":
|
||||||
if (i + 1 < args.Length)
|
if (i + 1 < args.Length)
|
||||||
@ -45,16 +46,16 @@ for (int i = 1; i < args.Length; i++)
|
|||||||
Console.Error.WriteLine("--out requires a file path");
|
Console.Error.WriteLine("--out requires a file path");
|
||||||
Environment.Exit(2);
|
Environment.Exit(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var parser = new XsdSchemaParser();
|
var parser = new XsdSchemaParser();
|
||||||
var model = parser.Parse(inputPath);
|
var model = parser.Parse(inputPath);
|
||||||
|
|
||||||
string output = format switch
|
var output = format switch
|
||||||
{
|
{
|
||||||
"json" => Serialization.ToJson(model),
|
"json" => Serialization.ToJson(model),
|
||||||
_ => Serialization.ToXml(model)
|
_ => Serialization.ToXml(model)
|
||||||
@ -72,11 +73,8 @@ try
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.Error.WriteLine("Error: " + ex.ToString());
|
Console.Error.WriteLine("Error: " + ex);
|
||||||
if (ex.InnerException != null)
|
if (ex.InnerException != null) Console.Error.WriteLine("Inner: " + ex.InnerException);
|
||||||
{
|
|
||||||
Console.Error.WriteLine("Inner: " + ex.InnerException.ToString());
|
|
||||||
}
|
|
||||||
Environment.Exit(3);
|
Environment.Exit(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +82,8 @@ return;
|
|||||||
|
|
||||||
static void PrintHelp()
|
static void PrintHelp()
|
||||||
{
|
{
|
||||||
Console.WriteLine("XSDVisualiser - Parse an XSD and emit a structural model with types, constraints, and cardinality");
|
Console.WriteLine(
|
||||||
|
"XSDVisualiser - Parse an XSD and emit a structural model with types, constraints, and cardinality");
|
||||||
Console.WriteLine("Usage: XSDVisualiser <schema.xsd> [--format xml|json] [--out outputFile]");
|
Console.WriteLine("Usage: XSDVisualiser <schema.xsd> [--format xml|json] [--out outputFile]");
|
||||||
Console.WriteLine("Default format is xml; if --out is omitted the output is printed to stdout.");
|
Console.WriteLine("Default format is xml; if --out is omitted the output is printed to stdout.");
|
||||||
}
|
}
|
||||||
@ -1,10 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<xsd:schema xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types"
|
<xsd:schema xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types"
|
||||||
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types"
|
|
||||||
xmlns:callctx="http://serviceplatformen.dk/xml/schemas/CallContext/1/"
|
xmlns:callctx="http://serviceplatformen.dk/xml/schemas/CallContext/1/"
|
||||||
xmlns:authctx="http://serviceplatformen.dk/xml/schemas/AuthorityContext/1/"
|
xmlns:authctx="http://serviceplatformen.dk/xml/schemas/AuthorityContext/1/"
|
||||||
xmlns:dsv2="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types"
|
xmlns:dsv2="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types"
|
||||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types"
|
||||||
elementFormDefault="qualified"
|
elementFormDefault="qualified"
|
||||||
version="1.0">
|
version="1.0">
|
||||||
|
|
||||||
@ -39,7 +39,8 @@
|
|||||||
|
|
||||||
<xsd:complexType name="FordelingsobjektAfsendResponseType">
|
<xsd:complexType name="FordelingsobjektAfsendResponseType">
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="ForretningsKvittering" type="dsv2:ForretningskvitteringType" minOccurs="1" maxOccurs="1"/>
|
<xsd:element name="ForretningsKvittering" type="dsv2:ForretningskvitteringType" minOccurs="1"
|
||||||
|
maxOccurs="1"/>
|
||||||
<xsd:element name="DistributionContext" type="dsv2:DistributionContextType" minOccurs="1" maxOccurs="1"/>
|
<xsd:element name="DistributionContext" type="dsv2:DistributionContextType" minOccurs="1" maxOccurs="1"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
@ -48,7 +49,8 @@
|
|||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element ref="callctx:CallContext" minOccurs="0" maxOccurs="1"/>
|
<xsd:element ref="callctx:CallContext" minOccurs="0" maxOccurs="1"/>
|
||||||
<xsd:element ref="authctx:AuthorityContext" minOccurs="0" maxOccurs="1"/>
|
<xsd:element ref="authctx:AuthorityContext" minOccurs="0" maxOccurs="1"/>
|
||||||
<xsd:element name="Forretningskvittering" type="dsv2:ForretningskvitteringType" minOccurs="1" maxOccurs="1"/>
|
<xsd:element name="Forretningskvittering" type="dsv2:ForretningskvitteringType" minOccurs="1"
|
||||||
|
maxOccurs="1"/>
|
||||||
<xsd:element name="DistributionContext" type="dsv2:DistributionContextType" minOccurs="1" maxOccurs="1"/>
|
<xsd:element name="DistributionContext" type="dsv2:DistributionContextType" minOccurs="1" maxOccurs="1"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
|
<definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
|
||||||
xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/port"
|
xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/port"
|
||||||
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/port"
|
|
||||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns:types="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types"
|
||||||
|
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/port"
|
||||||
xmlns="http://schemas.xmlsoap.org/wsdl/"
|
xmlns="http://schemas.xmlsoap.org/wsdl/"
|
||||||
name="DistributionServiceAnvender"
|
name="DistributionServiceAnvender">
|
||||||
xmlns:types="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types">
|
|
||||||
|
|
||||||
<types>
|
<types>
|
||||||
<xsd:schema targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types">
|
<xsd:schema targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types">
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
|
<definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
|
||||||
xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/port"
|
xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/port"
|
||||||
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/port"
|
|
||||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns:types="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types"
|
||||||
|
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/port"
|
||||||
xmlns="http://schemas.xmlsoap.org/wsdl/"
|
xmlns="http://schemas.xmlsoap.org/wsdl/"
|
||||||
name="DistributionServiceModtager"
|
name="DistributionServiceModtager">
|
||||||
xmlns:types="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types">
|
|
||||||
|
|
||||||
<types>
|
<types>
|
||||||
<xsd:schema targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types">
|
<xsd:schema targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types">
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<xsd:schema xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types"
|
<xsd:schema xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types"
|
||||||
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types"
|
|
||||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types"
|
||||||
elementFormDefault="qualified"
|
elementFormDefault="qualified"
|
||||||
version="1.0">
|
version="1.0">
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,16 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<schema xmlns="http://www.w3.org/2001/XMLSchema"
|
<schema xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types"
|
||||||
xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types"
|
xmlns:oio="urn:oio:definitions:1.0.0"
|
||||||
|
xmlns="http://www.w3.org/2001/XMLSchema"
|
||||||
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types"
|
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/2/types"
|
||||||
elementFormDefault="qualified"
|
elementFormDefault="qualified"
|
||||||
xmlns:oio="urn:oio:definitions:1.0.0"
|
|
||||||
oio:mapping="urn:oio:sagdok:MPD:3.0.0"
|
oio:mapping="urn:oio:sagdok:MPD:3.0.0"
|
||||||
attributeFormDefault="unqualified">
|
attributeFormDefault="unqualified">
|
||||||
|
|
||||||
<simpleType name="URN">
|
<simpleType name="URN">
|
||||||
<restriction base="string">
|
<restriction base="string">
|
||||||
<pattern value="[uU][rR][nN]:[a-zA-Z0-9][a-zA-Z0-9-]{0,30}[a-zA-Z0-9]:[a-zA-Z0-9\(\)+,\\\-.:=@;$_!*'%/?#]+"/>
|
<pattern
|
||||||
|
value="[uU][rR][nN]:[a-zA-Z0-9][a-zA-Z0-9-]{0,30}[a-zA-Z0-9]:[a-zA-Z0-9\(\)+,\\\-.:=@;$_!*'%/?#]+"/>
|
||||||
</restriction>
|
</restriction>
|
||||||
</simpleType>
|
</simpleType>
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<xsd:schema targetNamespace="http://serviceplatformen.dk/xml/schemas/kvittering/1/"
|
<xsd:schema xmlns:tns="http://serviceplatformen.dk/xml/schemas/kvittering/1/"
|
||||||
xmlns:tns="http://serviceplatformen.dk/xml/schemas/kvittering/1/"
|
|
||||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
targetNamespace="http://serviceplatformen.dk/xml/schemas/kvittering/1/"
|
||||||
elementFormDefault="qualified"
|
elementFormDefault="qualified"
|
||||||
version="1.0">
|
version="1.0">
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<wsdl:definitions name="ServiceplatformFaultMessage"
|
<wsdl:definitions xmlns:tns="http://serviceplatformen.dk/xml/schemas/ServiceplatformFault/1/"
|
||||||
targetNamespace="http://serviceplatformen.dk/xml/schemas/ServiceplatformFault/1/"
|
|
||||||
xmlns:tns="http://serviceplatformen.dk/xml/schemas/ServiceplatformFault/1/"
|
|
||||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
|
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
|
||||||
|
name="ServiceplatformFaultMessage"
|
||||||
|
targetNamespace="http://serviceplatformen.dk/xml/schemas/ServiceplatformFault/1/">
|
||||||
|
|
||||||
<wsdl:types>
|
<wsdl:types>
|
||||||
<xsd:schema targetNamespace="http://serviceplatformen.dk/xml/schemas/ServiceplatformFault/1/">
|
<xsd:schema targetNamespace="http://serviceplatformen.dk/xml/schemas/ServiceplatformFault/1/">
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<xsd:schema xmlns:tns="http://serviceplatformen.dk/xml/schemas/ServiceplatformFault/1/"
|
<xsd:schema xmlns:tns="http://serviceplatformen.dk/xml/schemas/ServiceplatformFault/1/"
|
||||||
targetNamespace="http://serviceplatformen.dk/xml/schemas/ServiceplatformFault/1/"
|
|
||||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
targetNamespace="http://serviceplatformen.dk/xml/schemas/ServiceplatformFault/1/"
|
||||||
elementFormDefault="qualified"
|
elementFormDefault="qualified"
|
||||||
version="1.0">
|
version="1.0">
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<wsdl:definitions name="ServiceplatformFaultMessage"
|
<wsdl:definitions xmlns:tns="http://serviceplatformen.dk/xml/schemas/kvittering/1/"
|
||||||
targetNamespace="http://serviceplatformen.dk/xml/schemas/kvittering/1/"
|
|
||||||
xmlns:tns="http://serviceplatformen.dk/xml/schemas/kvittering/1/"
|
|
||||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
|
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
|
||||||
|
name="ServiceplatformFaultMessage"
|
||||||
|
targetNamespace="http://serviceplatformen.dk/xml/schemas/kvittering/1/">
|
||||||
|
|
||||||
<wsdl:types>
|
<wsdl:types>
|
||||||
<xsd:schema targetNamespace="http://serviceplatformen.dk/xml/schemas/kvittering/1/">
|
<xsd:schema targetNamespace="http://serviceplatformen.dk/xml/schemas/kvittering/1/">
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
|
<definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
|
||||||
xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port"
|
xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port"
|
||||||
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port"
|
|
||||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
xmlns="http://schemas.xmlsoap.org/wsdl/"
|
|
||||||
name="DistributionService"
|
|
||||||
xmlns:types="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types"
|
xmlns:types="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types"
|
||||||
xmlns:wsp="http://www.w3.org/ns/ws-policy">
|
xmlns:wsp="http://www.w3.org/ns/ws-policy"
|
||||||
|
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port"
|
||||||
|
xmlns="http://schemas.xmlsoap.org/wsdl/"
|
||||||
|
name="DistributionService">
|
||||||
|
|
||||||
<import namespace="http://serviceplatformen.dk/xml/wsdl/soap11/Security/Policy" location="policies.wsdl"/>
|
<import namespace="http://serviceplatformen.dk/xml/wsdl/soap11/Security/Policy" location="policies.wsdl"/>
|
||||||
|
|
||||||
@ -73,7 +73,8 @@
|
|||||||
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
|
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
|
||||||
|
|
||||||
<operation name="FordelingsobjektAfsend">
|
<operation name="FordelingsobjektAfsend">
|
||||||
<soap:operation soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingsobjektAfsend"/>
|
<soap:operation
|
||||||
|
soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingsobjektAfsend"/>
|
||||||
<input>
|
<input>
|
||||||
<soap:body use="literal"/>
|
<soap:body use="literal"/>
|
||||||
</input>
|
</input>
|
||||||
@ -86,7 +87,8 @@
|
|||||||
</operation>
|
</operation>
|
||||||
|
|
||||||
<operation name="FordelingskvitteringModtag">
|
<operation name="FordelingskvitteringModtag">
|
||||||
<soap:operation soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingskvitteringModtag"/>
|
<soap:operation
|
||||||
|
soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingskvitteringModtag"/>
|
||||||
<input>
|
<input>
|
||||||
<soap:body use="literal"/>
|
<soap:body use="literal"/>
|
||||||
</input>
|
</input>
|
||||||
@ -114,7 +116,8 @@
|
|||||||
</operation>
|
</operation>
|
||||||
|
|
||||||
<operation name="FordelingsmodtagerValider">
|
<operation name="FordelingsmodtagerValider">
|
||||||
<soap:operation soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingsmodtagerValider"/>
|
<soap:operation
|
||||||
|
soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingsmodtagerValider"/>
|
||||||
<input>
|
<input>
|
||||||
<soap:body use="literal"/>
|
<soap:body use="literal"/>
|
||||||
</input>
|
</input>
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<wsdl:definitions targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/Security/Policy"
|
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
|
||||||
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
|
|
||||||
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
|
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
|
||||||
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
|
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
|
||||||
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
|
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"
|
||||||
|
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/Security/Policy">
|
||||||
|
|
||||||
<wsp:Policy Name="policies.wsdl#ServiceplatformBindingPolicy">
|
<wsp:Policy Name="policies.wsdl#ServiceplatformBindingPolicy">
|
||||||
<wsp:ExactlyOne>
|
<wsp:ExactlyOne>
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
|
<definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
|
||||||
xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port"
|
xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port"
|
||||||
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port"
|
|
||||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
xmlns="http://schemas.xmlsoap.org/wsdl/"
|
|
||||||
name="DistributionService"
|
|
||||||
xmlns:types="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types"
|
xmlns:types="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types"
|
||||||
xmlns:wsp="http://www.w3.org/ns/ws-policy">
|
xmlns:wsp="http://www.w3.org/ns/ws-policy"
|
||||||
|
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port"
|
||||||
|
xmlns="http://schemas.xmlsoap.org/wsdl/"
|
||||||
|
name="DistributionService">
|
||||||
|
|
||||||
<import namespace="http://serviceplatformen.dk/xml/wsdl/soap11/Security/Policy" location="policies.wsdl"/>
|
<import namespace="http://serviceplatformen.dk/xml/wsdl/soap11/Security/Policy" location="policies.wsdl"/>
|
||||||
|
|
||||||
@ -73,7 +73,8 @@
|
|||||||
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
|
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
|
||||||
|
|
||||||
<operation name="FordelingsobjektAfsend">
|
<operation name="FordelingsobjektAfsend">
|
||||||
<soap:operation soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingsobjektAfsend"/>
|
<soap:operation
|
||||||
|
soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingsobjektAfsend"/>
|
||||||
<input>
|
<input>
|
||||||
<soap:body use="literal"/>
|
<soap:body use="literal"/>
|
||||||
</input>
|
</input>
|
||||||
@ -86,7 +87,8 @@
|
|||||||
</operation>
|
</operation>
|
||||||
|
|
||||||
<operation name="FordelingskvitteringModtag">
|
<operation name="FordelingskvitteringModtag">
|
||||||
<soap:operation soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingskvitteringModtag"/>
|
<soap:operation
|
||||||
|
soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingskvitteringModtag"/>
|
||||||
<input>
|
<input>
|
||||||
<soap:body use="literal"/>
|
<soap:body use="literal"/>
|
||||||
</input>
|
</input>
|
||||||
@ -114,7 +116,8 @@
|
|||||||
</operation>
|
</operation>
|
||||||
|
|
||||||
<operation name="FordelingsmodtagerValider">
|
<operation name="FordelingsmodtagerValider">
|
||||||
<soap:operation soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingsmodtagerValider"/>
|
<soap:operation
|
||||||
|
soapAction="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/port/FordelingsmodtagerValider"/>
|
||||||
<input>
|
<input>
|
||||||
<soap:body use="literal"/>
|
<soap:body use="literal"/>
|
||||||
</input>
|
</input>
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<wsdl:definitions targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/Security/Policy"
|
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
|
||||||
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
|
|
||||||
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
|
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
|
||||||
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
|
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
|
||||||
xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"
|
xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"
|
||||||
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
|
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"
|
||||||
|
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/Security/Policy">
|
||||||
|
|
||||||
<wsp:Policy Name="policies.wsdl#ServiceplatformBindingPolicy">
|
<wsp:Policy Name="policies.wsdl#ServiceplatformBindingPolicy">
|
||||||
<wsp:ExactlyOne>
|
<wsp:ExactlyOne>
|
||||||
@ -37,7 +37,8 @@
|
|||||||
<wsp:Policy>
|
<wsp:Policy>
|
||||||
<sp:InitiatorToken>
|
<sp:InitiatorToken>
|
||||||
<wsp:Policy>
|
<wsp:Policy>
|
||||||
<sp:SamlToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">
|
<sp:SamlToken
|
||||||
|
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">
|
||||||
<wsp:Policy>
|
<wsp:Policy>
|
||||||
<sp:WssSamlV20Token11/>
|
<sp:WssSamlV20Token11/>
|
||||||
</wsp:Policy>
|
</wsp:Policy>
|
||||||
@ -46,7 +47,8 @@
|
|||||||
</sp:InitiatorToken>
|
</sp:InitiatorToken>
|
||||||
<sp:RecipientToken>
|
<sp:RecipientToken>
|
||||||
<wsp:Policy>
|
<wsp:Policy>
|
||||||
<sp:X509Token sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToInitiator">
|
<sp:X509Token
|
||||||
|
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToInitiator">
|
||||||
<wsp:Policy>
|
<wsp:Policy>
|
||||||
<sp:WssX509V3Token10/>
|
<sp:WssX509V3Token10/>
|
||||||
</wsp:Policy>
|
</wsp:Policy>
|
||||||
@ -70,7 +72,8 @@
|
|||||||
</sp:AsymmetricBinding>
|
</sp:AsymmetricBinding>
|
||||||
<sp:SignedSupportingTokens>
|
<sp:SignedSupportingTokens>
|
||||||
<wsp:Policy>
|
<wsp:Policy>
|
||||||
<sp:IssuedToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
|
<sp:IssuedToken
|
||||||
|
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
|
||||||
<sp:RequestSecurityTokenTemplate/>
|
<sp:RequestSecurityTokenTemplate/>
|
||||||
<wsp:Policy>
|
<wsp:Policy>
|
||||||
<sp:WssSamlV20Token11/>
|
<sp:WssSamlV20Token11/>
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<xsd:schema xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types"
|
<xsd:schema xmlns:tns="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types"
|
||||||
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types"
|
|
||||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
targetNamespace="http://serviceplatformen.dk/xml/wsdl/soap11/DistributionService/3/types"
|
||||||
elementFormDefault="qualified"
|
elementFormDefault="qualified"
|
||||||
version="1.0">
|
version="1.0">
|
||||||
|
|
||||||
<xsd:element name="TransportKvittering" type="tns:TransportkvitteringType" />
|
<xsd:element name="TransportKvittering" type="tns:TransportkvitteringType"/>
|
||||||
<xsd:simpleType name="TransportValideringsKodeType">
|
<xsd:simpleType name="TransportValideringsKodeType">
|
||||||
<xsd:restriction base="xsd:string">
|
<xsd:restriction base="xsd:string">
|
||||||
<xsd:enumeration value="OK"/>
|
<xsd:enumeration value="OK"/>
|
||||||
@ -16,7 +16,8 @@
|
|||||||
|
|
||||||
<xsd:complexType name="TransportkvitteringType">
|
<xsd:complexType name="TransportkvitteringType">
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="TransportValideringsKode" type="tns:TransportValideringsKodeType" minOccurs="1" maxOccurs="1"/>
|
<xsd:element name="TransportValideringsKode" type="tns:TransportValideringsKodeType" minOccurs="1"
|
||||||
|
maxOccurs="1"/>
|
||||||
<xsd:element name="Begrundelse" type="xsd:string" minOccurs="0" maxOccurs="1"/>
|
<xsd:element name="Begrundelse" type="xsd:string" minOccurs="0" maxOccurs="1"/>
|
||||||
<xsd:element name="FejlListe" type="tns:FejlListeType" minOccurs="0" maxOccurs="1"/>
|
<xsd:element name="FejlListe" type="tns:FejlListeType" minOccurs="0" maxOccurs="1"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
@ -55,7 +56,8 @@
|
|||||||
|
|
||||||
<xsd:complexType name="ForretningskvitteringType">
|
<xsd:complexType name="ForretningskvitteringType">
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="ForretningsValideringsKode" type="tns:ForretningsValideringsKodeType" minOccurs="1" maxOccurs="1"/>
|
<xsd:element name="ForretningsValideringsKode" type="tns:ForretningsValideringsKodeType" minOccurs="1"
|
||||||
|
maxOccurs="1"/>
|
||||||
<xsd:element name="Kvitteringstype" type="tns:KvitteringstypeType" minOccurs="1" maxOccurs="1"/>
|
<xsd:element name="Kvitteringstype" type="tns:KvitteringstypeType" minOccurs="1" maxOccurs="1"/>
|
||||||
<xsd:element name="Begrundelse" type="xsd:string" minOccurs="0" maxOccurs="1"/>
|
<xsd:element name="Begrundelse" type="xsd:string" minOccurs="0" maxOccurs="1"/>
|
||||||
<xsd:element name="FejlListe" type="tns:FejlListeType" minOccurs="0" maxOccurs="1"/>
|
<xsd:element name="FejlListe" type="tns:FejlListeType" minOccurs="0" maxOccurs="1"/>
|
||||||
|
|||||||
@ -1,37 +1,35 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using System.Xml.Serialization;
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
namespace XSDVisualiser.Utils
|
namespace XSDVisualiser.Utils;
|
||||||
|
|
||||||
|
public static class Serialization
|
||||||
{
|
{
|
||||||
public static class Serialization
|
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||||
{
|
{
|
||||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
WriteIndented = true,
|
||||||
{
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||||
WriteIndented = true,
|
};
|
||||||
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
|
|
||||||
};
|
|
||||||
|
|
||||||
public static string ToXml<T>(T obj)
|
public static string ToXml<T>(T obj)
|
||||||
{
|
{
|
||||||
var serializer = new XmlSerializer(typeof(T));
|
var serializer = new XmlSerializer(typeof(T));
|
||||||
var ns = new XmlSerializerNamespaces();
|
var ns = new XmlSerializerNamespaces();
|
||||||
ns.Add(string.Empty, string.Empty);
|
ns.Add(string.Empty, string.Empty);
|
||||||
using var sw = new Utf8StringWriter();
|
using var sw = new Utf8StringWriter();
|
||||||
serializer.Serialize(sw, obj, ns);
|
serializer.Serialize(sw, obj, ns);
|
||||||
return sw.ToString();
|
return sw.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ToJson<T>(T obj)
|
public static string ToJson<T>(T obj)
|
||||||
{
|
{
|
||||||
return JsonSerializer.Serialize(obj, JsonOptions);
|
return JsonSerializer.Serialize(obj, JsonOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class Utf8StringWriter : StringWriter
|
private sealed class Utf8StringWriter : StringWriter
|
||||||
{
|
{
|
||||||
public override Encoding Encoding => Encoding.UTF8;
|
public override Encoding Encoding => Encoding.UTF8;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,16 +1,16 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Remove="Models\**\*.cs" />
|
<Compile Remove="Models\**\*.cs"/>
|
||||||
<Compile Remove="Parsing\**\*.cs" />
|
<Compile Remove="Parsing\**\*.cs"/>
|
||||||
<Compile Remove="Utils\**\*.cs" />
|
<Compile Remove="Utils\**\*.cs"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\XSDVisualiser.Core\XSDVisualiser.Core.csproj" />
|
<ProjectReference Include="..\XSDVisualiser.Core\XSDVisualiser.Core.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user