Did some cleanup of the UI and code
This commit is contained in:
parent
8fd2b05b4d
commit
14bad15f4a
@ -9,14 +9,12 @@ namespace XSDVisualiser.Desktop.Converters
|
|||||||
{
|
{
|
||||||
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 Occurs occ)
|
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}]";
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
</DockPanel>
|
</DockPanel>
|
||||||
|
|
||||||
<!-- Body: left tree | splitter | right details -->
|
<!-- Body: left tree | splitter | right details -->
|
||||||
<Grid Grid.Row="1" ColumnDefinitions="*,Auto,1.6*" RowDefinitions="*">
|
<Grid Grid.Row="1" ColumnDefinitions="2*,Auto,*" RowDefinitions="*">
|
||||||
<!-- Left: Tree -->
|
<!-- Left: Tree -->
|
||||||
<Border Background="{DynamicResource PanelBackgroundBrush}"
|
<Border Background="{DynamicResource PanelBackgroundBrush}"
|
||||||
BorderBrush="{DynamicResource PanelBorderBrush}"
|
BorderBrush="{DynamicResource PanelBorderBrush}"
|
||||||
@ -30,11 +30,8 @@
|
|||||||
<TreeDataTemplate DataType="{x:Type m:SchemaNode}" ItemsSource="{Binding Children}">
|
<TreeDataTemplate DataType="{x:Type m:SchemaNode}" ItemsSource="{Binding Children}">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<!-- Connector line from parent to this node (hidden for roots where ContentModel is null) -->
|
<!-- Connector line from parent to this node (hidden for roots where ContentModel is null) -->
|
||||||
<Grid Margin="0,6,0,2" IsVisible="{Binding ContentModel, Converter={x:Static ObjectConverters.IsNotNull}}">
|
<Grid Margin="0,8,8,2" IsVisible="{Binding ContentModel, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||||
<Grid.ColumnDefinitions>
|
ColumnDefinitions="16,*">
|
||||||
<ColumnDefinition Width="16"/>
|
|
||||||
<ColumnDefinition Width="*"/>
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<!-- leading small elbow -->
|
<!-- leading small elbow -->
|
||||||
<Rectangle Grid.Column="0" Width="16" Height="2" Fill="{DynamicResource SeparatorBrush}" VerticalAlignment="Center"/>
|
<Rectangle Grid.Column="0" Width="16" Height="2" Fill="{DynamicResource SeparatorBrush}" VerticalAlignment="Center"/>
|
||||||
<Grid Grid.Column="1">
|
<Grid Grid.Column="1">
|
||||||
@ -50,7 +47,7 @@
|
|||||||
<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,0,6" Background="{DynamicResource PanelBackgroundBrush}">
|
Padding="8" Margin="0,0,8,6" Background="{DynamicResource PanelBackgroundBrush}">
|
||||||
<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"/>
|
||||||
@ -69,7 +66,7 @@
|
|||||||
|
|
||||||
<!-- 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 MutedTextBrush}"/>
|
Foreground="{DynamicResource SubtleTextBrush}" FontWeight="SemiBold"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
@ -95,116 +92,31 @@
|
|||||||
|
|
||||||
<!-- General info -->
|
<!-- General info -->
|
||||||
<Border BorderBrush="{DynamicResource PanelBorderBrush}" BorderThickness="1" CornerRadius="4" Padding="10">
|
<Border BorderBrush="{DynamicResource PanelBorderBrush}" BorderThickness="1" CornerRadius="4" Padding="10">
|
||||||
<Grid ColumnDefinitions="Auto,*,Auto,*" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
|
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,*">
|
||||||
<TextBlock Text="Name:" FontWeight="Bold"/>
|
<TextBlock Grid.Row="0" Grid.Column="0" Text="Name:" FontWeight="Bold"/>
|
||||||
<TextBlock Grid.Column="1" Margin="8,0,0,0" Text="{Binding Name}"/>
|
<TextBlock Grid.Row="0" Grid.Column="1" Margin="8,0,0,0" Text="{Binding Name}"/>
|
||||||
|
|
||||||
<TextBlock Grid.Row="1" Text="Type:" FontWeight="Bold"/>
|
<TextBlock Grid.Row="1" Grid.Column="0" Text="Built-in:" FontWeight="Bold"/>
|
||||||
<TextBlock Grid.Column="1" Grid.Row="1" Margin="8,0,0,0" Text="{Binding TypeName}"/>
|
<TextBlock Grid.Row="1" Grid.Column="1" Margin="8,0,0,0" Text="{Binding BuiltInType}" FontWeight="Bold"/>
|
||||||
|
|
||||||
<TextBlock Grid.Row="2" Text="Built-in:" FontWeight="Bold"/>
|
<TextBlock Grid.Row="2" Grid.Column="0" Text="Model:" FontWeight="Bold"/>
|
||||||
<TextBlock Grid.Column="1" Grid.Row="2" Margin="8,0,0,0" Text="{Binding BuiltInType}"/>
|
<TextBlock Grid.Row="2" Grid.Column="1" Margin="8,0,0,0" Text="{Binding ContentModel}"/>
|
||||||
|
|
||||||
<TextBlock Grid.Row="3" Text="Namespace:" FontWeight="Bold"/>
|
<TextBlock Grid.Row="3" Grid.Column="0" Text="Namespace:" FontWeight="Bold"/>
|
||||||
<TextBlock Grid.Column="1" Grid.Row="3" 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" Text="Cardinality:" FontWeight="Bold"/>
|
<TextBlock Grid.Row="4" Grid.Column="0" Text="Cardinality:" FontWeight="Bold"/>
|
||||||
<TextBlock Grid.Column="1" Grid.Row="4" 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.Column="2" Text="Model:" FontWeight="Bold"/>
|
<TextBlock Grid.Row="5" Grid.Column="0" Text="Nillable:" FontWeight="Bold"/>
|
||||||
<TextBlock Grid.Column="3" Margin="8,0,0,0" Text="{Binding ContentModel}"/>
|
<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="1" Margin="8,0,0,0" Text="{Binding TypeName}"/>
|
||||||
|
|
||||||
<TextBlock Grid.Row="5" Text="Nillable:" FontWeight="Bold"/>
|
|
||||||
<TextBlock Grid.Column="1" Grid.Row="5" Margin="8,0,0,0" Text="{Binding IsNillable}"/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- Attributes -->
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Text="Attributes" FontWeight="SemiBold"/>
|
|
||||||
<ItemsControl ItemsSource="{Binding Attributes}">
|
|
||||||
<ItemsControl.ItemTemplate>
|
|
||||||
<DataTemplate x:DataType="m:AttributeInfo">
|
|
||||||
<Border BorderBrush="{DynamicResource PanelBorderBrush}" BorderThickness="1" CornerRadius="4" Padding="8" Margin="0,6,0,0">
|
|
||||||
<StackPanel Spacing="8">
|
|
||||||
<Grid ColumnDefinitions="Auto,*,Auto,*" RowDefinitions="Auto,Auto,Auto">
|
|
||||||
<TextBlock Text="Name:" FontWeight="Bold"/>
|
|
||||||
<TextBlock Grid.Column="1" Margin="8,0,0,0" Text="{Binding Name}"/>
|
|
||||||
|
|
||||||
<TextBlock Grid.Row="1" Text="Use:" FontWeight="Bold"/>
|
|
||||||
<TextBlock Grid.Column="1" Grid.Row="1" Margin="8,0,0,0" Text="{Binding Use}"/>
|
|
||||||
|
|
||||||
<TextBlock Grid.Row="2" Text="Type:" FontWeight="Bold"/>
|
|
||||||
<TextBlock Grid.Column="1" Grid.Row="2" Margin="8,0,0,0" Text="{Binding TypeName}"/>
|
|
||||||
|
|
||||||
<TextBlock Grid.Column="2" Text="Built-in:" FontWeight="Bold"/>
|
|
||||||
<TextBlock Grid.Column="3" Margin="8,0,0,0" Text="{Binding BuiltInType}"/>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<StackPanel IsVisible="{Binding Constraints, Converter={x:Static ObjectConverters.IsNotNull}}">
|
|
||||||
<TextBlock Text="Constraints" FontWeight="SemiBold"/>
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Text="Enumerations" FontWeight="SemiBold"/>
|
|
||||||
<ItemsControl ItemsSource="{Binding Constraints.Enumerations}">
|
|
||||||
<ItemsControl.ItemTemplate>
|
|
||||||
<DataTemplate x:DataType="x:String">
|
|
||||||
<TextBlock Text="{Binding ., StringFormat=• {0}}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</ItemsControl.ItemTemplate>
|
|
||||||
</ItemsControl>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Text="Patterns" FontWeight="SemiBold"/>
|
|
||||||
<ItemsControl ItemsSource="{Binding Constraints.Patterns}">
|
|
||||||
<ItemsControl.ItemTemplate>
|
|
||||||
<DataTemplate x:DataType="x:String">
|
|
||||||
<TextBlock Text="{Binding ., StringFormat=• {0}}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</ItemsControl.ItemTemplate>
|
|
||||||
</ItemsControl>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel IsVisible="{Binding Constraints.Numeric, Converter={x:Static ObjectConverters.IsNotNull}}">
|
|
||||||
<TextBlock Text="Numeric bounds" FontWeight="SemiBold"/>
|
|
||||||
<StackPanel Orientation="Horizontal" IsVisible="{Binding Constraints.Numeric.MinInclusive, Converter={x:Static ObjectConverters.IsNotNull}}">
|
|
||||||
<TextBlock Text="Min inclusive:" FontWeight="Bold"/>
|
|
||||||
<TextBlock Margin="8,0,0,0" Text="{Binding Constraints.Numeric.MinInclusive}"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Orientation="Horizontal" IsVisible="{Binding Constraints.Numeric.MaxInclusive, Converter={x:Static ObjectConverters.IsNotNull}}">
|
|
||||||
<TextBlock Text="Max inclusive:" FontWeight="Bold"/>
|
|
||||||
<TextBlock Margin="8,0,0,0" Text="{Binding Constraints.Numeric.MaxInclusive}"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Orientation="Horizontal" IsVisible="{Binding Constraints.Numeric.MinExclusive, Converter={x:Static ObjectConverters.IsNotNull}}">
|
|
||||||
<TextBlock Text="Min exclusive:" FontWeight="Bold"/>
|
|
||||||
<TextBlock Margin="8,0,0,0" Text="{Binding Constraints.Numeric.MinExclusive}"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Orientation="Horizontal" IsVisible="{Binding Constraints.Numeric.MaxExclusive, Converter={x:Static ObjectConverters.IsNotNull}}">
|
|
||||||
<TextBlock Text="Max exclusive:" FontWeight="Bold"/>
|
|
||||||
<TextBlock Margin="8,0,0,0" Text="{Binding Constraints.Numeric.MaxExclusive}"/>
|
|
||||||
</StackPanel>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel IsVisible="{Binding Constraints.Length, Converter={x:Static ObjectConverters.IsNotNull}}">
|
|
||||||
<TextBlock Text="Length bounds" FontWeight="SemiBold"/>
|
|
||||||
<StackPanel Orientation="Horizontal" IsVisible="{Binding Constraints.Length.LengthSpecified}">
|
|
||||||
<TextBlock Text="Length:" FontWeight="Bold"/>
|
|
||||||
<TextBlock Margin="8,0,0,0" Text="{Binding Constraints.Length.Length}"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Orientation="Horizontal" IsVisible="{Binding Constraints.Length.MinLengthSpecified}">
|
|
||||||
<TextBlock Text="Min length:" FontWeight="Bold"/>
|
|
||||||
<TextBlock Margin="8,0,0,0" Text="{Binding Constraints.Length.MinLength}"/>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel Orientation="Horizontal" IsVisible="{Binding Constraints.Length.MaxLengthSpecified}">
|
|
||||||
<TextBlock Text="Max length:" FontWeight="Bold"/>
|
|
||||||
<TextBlock Margin="8,0,0,0" Text="{Binding Constraints.Length.MaxLength}"/>
|
|
||||||
</StackPanel>
|
|
||||||
</StackPanel>
|
|
||||||
</StackPanel>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
</DataTemplate>
|
|
||||||
</ItemsControl.ItemTemplate>
|
|
||||||
</ItemsControl>
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<!-- Constraints -->
|
<!-- Constraints -->
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Text="Constraints" FontWeight="SemiBold"/>
|
<TextBlock Text="Constraints" FontWeight="SemiBold"/>
|
||||||
|
|||||||
@ -3,8 +3,8 @@ using System.Threading.Tasks;
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using XSDVisualiser.Core;
|
||||||
using XSDVisualiser.Models;
|
using XSDVisualiser.Models;
|
||||||
using XSDVisualiser.Parsing;
|
|
||||||
|
|
||||||
namespace XSDVisualiser.Desktop
|
namespace XSDVisualiser.Desktop
|
||||||
{
|
{
|
||||||
|
|||||||
@ -12,7 +12,7 @@ namespace XSDVisualiser.Desktop
|
|||||||
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AppBuilder BuildAvaloniaApp() =>
|
private static AppBuilder BuildAvaloniaApp() =>
|
||||||
AppBuilder.Configure<App>()
|
AppBuilder.Configure<App>()
|
||||||
.UsePlatformDetect()
|
.UsePlatformDetect()
|
||||||
.LogToTrace()
|
.LogToTrace()
|
||||||
|
|||||||
@ -1,11 +1,8 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using System.Xml.Schema;
|
using System.Xml.Schema;
|
||||||
using XSDVisualiser.Models;
|
using XSDVisualiser.Models;
|
||||||
|
|
||||||
namespace XSDVisualiser.Parsing
|
namespace XSDVisualiser.Core
|
||||||
{
|
{
|
||||||
public class XsdSchemaParser
|
public class XsdSchemaParser
|
||||||
{
|
{
|
||||||
@ -16,16 +13,16 @@ namespace XSDVisualiser.Parsing
|
|||||||
_set.XmlResolver = new XmlUrlResolver();
|
_set.XmlResolver = new XmlUrlResolver();
|
||||||
using var reader = XmlReader.Create(xsdPath, new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore });
|
using var reader = XmlReader.Create(xsdPath, new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore });
|
||||||
var schema = XmlSchema.Read(reader, ValidationCallback);
|
var schema = XmlSchema.Read(reader, ValidationCallback);
|
||||||
_set.Add(schema);
|
_set.Add(schema!);
|
||||||
_set.CompilationSettings = new XmlSchemaCompilationSettings { EnableUpaCheck = true };
|
_set.CompilationSettings = new XmlSchemaCompilationSettings { EnableUpaCheck = true };
|
||||||
_set.Compile();
|
_set.Compile();
|
||||||
|
|
||||||
var model = new SchemaModel
|
var model = new SchemaModel
|
||||||
{
|
{
|
||||||
TargetNamespace = schema.TargetNamespace
|
TargetNamespace = schema!.TargetNamespace
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (XmlSchemaElement globalEl in _set.Schemas().Cast<XmlSchema>()
|
foreach (var globalEl in _set.Schemas().Cast<XmlSchema>()
|
||||||
.SelectMany(s => s.Elements.Values.Cast<XmlSchemaElement>()))
|
.SelectMany(s => s.Elements.Values.Cast<XmlSchemaElement>()))
|
||||||
{
|
{
|
||||||
var node = BuildNodeForElement(globalEl, parentContentModel: null);
|
var node = BuildNodeForElement(globalEl, parentContentModel: null);
|
||||||
@ -58,8 +55,7 @@ namespace XSDVisualiser.Parsing
|
|||||||
};
|
};
|
||||||
|
|
||||||
var type = ResolveElementType(element);
|
var type = ResolveElementType(element);
|
||||||
if (type != null)
|
if (type == null) return node;
|
||||||
{
|
|
||||||
node.TypeName = GetQualifiedTypeName(type);
|
node.TypeName = GetQualifiedTypeName(type);
|
||||||
if (type.Datatype != null)
|
if (type.Datatype != null)
|
||||||
{
|
{
|
||||||
@ -76,7 +72,6 @@ namespace XSDVisualiser.Parsing
|
|||||||
node.Constraints = ExtractConstraints(st);
|
node.Constraints = ExtractConstraints(st);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@ -84,7 +79,7 @@ namespace XSDVisualiser.Parsing
|
|||||||
private static string? GetQualifiedTypeName(XmlSchemaType type)
|
private static string? GetQualifiedTypeName(XmlSchemaType type)
|
||||||
{
|
{
|
||||||
if (!type.QualifiedName.IsEmpty) return type.QualifiedName.ToString();
|
if (!type.QualifiedName.IsEmpty) return type.QualifiedName.ToString();
|
||||||
return type.BaseXmlSchemaType != null && !type.BaseXmlSchemaType.QualifiedName.IsEmpty
|
return type.BaseXmlSchemaType is { QualifiedName.IsEmpty: false }
|
||||||
? type.BaseXmlSchemaType.QualifiedName.ToString()
|
? type.BaseXmlSchemaType.QualifiedName.ToString()
|
||||||
: type.Name;
|
: type.Name;
|
||||||
}
|
}
|
||||||
@ -96,17 +91,72 @@ namespace XSDVisualiser.Parsing
|
|||||||
{
|
{
|
||||||
return _set.GlobalTypes[el.SchemaTypeName] as XmlSchemaType;
|
return _set.GlobalTypes[el.SchemaTypeName] as XmlSchemaType;
|
||||||
}
|
}
|
||||||
if (el.SchemaType != null) return el.SchemaType;
|
|
||||||
return null;
|
return el.SchemaType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleComplexType(SchemaNode node, XmlSchemaComplexType ct)
|
private void HandleComplexType(SchemaNode node, XmlSchemaComplexType ct)
|
||||||
{
|
{
|
||||||
// Attributes
|
// Collect attributes (ensure uniqueness)
|
||||||
foreach (XmlSchemaAttribute attr in ct.AttributeUses.Values.OfType<XmlSchemaAttribute>())
|
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));
|
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
|
// Content model
|
||||||
if (ct.ContentTypeParticle is XmlSchemaGroupBase group)
|
if (ct.ContentTypeParticle is XmlSchemaGroupBase group)
|
||||||
@ -158,7 +208,9 @@ namespace XSDVisualiser.Parsing
|
|||||||
else if (ct.ContentType == XmlSchemaContentType.TextOnly && ct.ContentModel is XmlSchemaSimpleContent simpleContent)
|
else if (ct.ContentType == XmlSchemaContentType.TextOnly && ct.ContentModel is XmlSchemaSimpleContent simpleContent)
|
||||||
{
|
{
|
||||||
node.ContentModel = "simple";
|
node.ContentModel = "simple";
|
||||||
if (simpleContent.Content is XmlSchemaSimpleContentExtension ext)
|
switch (simpleContent.Content)
|
||||||
|
{
|
||||||
|
case XmlSchemaSimpleContentExtension ext:
|
||||||
{
|
{
|
||||||
var baseType = ResolveType(ext.BaseTypeName);
|
var baseType = ResolveType(ext.BaseTypeName);
|
||||||
if (baseType is XmlSchemaSimpleType st)
|
if (baseType is XmlSchemaSimpleType st)
|
||||||
@ -168,12 +220,19 @@ namespace XSDVisualiser.Parsing
|
|||||||
node.BuiltInType ??= st.Datatype?.TypeCode.ToString();
|
node.BuiltInType ??= st.Datatype?.TypeCode.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (XmlSchemaAttribute attr in ext.Attributes.OfType<XmlSchemaAttribute>())
|
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));
|
node.Attributes.Add(ExtractAttribute(attr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (simpleContent.Content is XmlSchemaSimpleContentRestriction res)
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case XmlSchemaSimpleContentRestriction res:
|
||||||
{
|
{
|
||||||
var baseType = ResolveType(res.BaseTypeName);
|
var baseType = ResolveType(res.BaseTypeName);
|
||||||
if (baseType is XmlSchemaSimpleType st)
|
if (baseType is XmlSchemaSimpleType st)
|
||||||
@ -184,6 +243,9 @@ namespace XSDVisualiser.Parsing
|
|||||||
node.TypeName ??= GetQualifiedTypeName(st);
|
node.TypeName ??= GetQualifiedTypeName(st);
|
||||||
node.BuiltInType ??= st.Datatype?.TypeCode.ToString();
|
node.BuiltInType ??= st.Datatype?.TypeCode.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -208,12 +270,10 @@ namespace XSDVisualiser.Parsing
|
|||||||
else if (!attr.SchemaTypeName.IsEmpty) st = ResolveType(attr.SchemaTypeName) as XmlSchemaSimpleType;
|
else if (!attr.SchemaTypeName.IsEmpty) st = ResolveType(attr.SchemaTypeName) as XmlSchemaSimpleType;
|
||||||
else if (attr.SchemaType != null) st = attr.SchemaType;
|
else if (attr.SchemaType != null) st = attr.SchemaType;
|
||||||
|
|
||||||
if (st != null)
|
if (st == null) return info;
|
||||||
{
|
|
||||||
info.TypeName = GetQualifiedTypeName(st);
|
info.TypeName = GetQualifiedTypeName(st);
|
||||||
info.BuiltInType = st.Datatype?.TypeCode.ToString();
|
info.BuiltInType = st.Datatype?.TypeCode.ToString();
|
||||||
info.Constraints = ExtractConstraints(st);
|
info.Constraints = ExtractConstraints(st);
|
||||||
}
|
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
@ -225,11 +285,12 @@ namespace XSDVisualiser.Parsing
|
|||||||
BaseTypeName = GetQualifiedTypeName(st.BaseXmlSchemaType)
|
BaseTypeName = GetQualifiedTypeName(st.BaseXmlSchemaType)
|
||||||
};
|
};
|
||||||
|
|
||||||
if (st.Content is XmlSchemaSimpleTypeRestriction restr)
|
switch (st.Content)
|
||||||
{
|
{
|
||||||
|
case XmlSchemaSimpleTypeRestriction restr:
|
||||||
MergeFacets(cons, restr.Facets);
|
MergeFacets(cons, restr.Facets);
|
||||||
}
|
break;
|
||||||
else if (st.Content is XmlSchemaSimpleTypeList list)
|
case XmlSchemaSimpleTypeList list:
|
||||||
{
|
{
|
||||||
cons.Patterns.Add("(list)");
|
cons.Patterns.Add("(list)");
|
||||||
if (!list.ItemTypeName.IsEmpty)
|
if (!list.ItemTypeName.IsEmpty)
|
||||||
@ -241,18 +302,23 @@ namespace XSDVisualiser.Parsing
|
|||||||
Merge(cons, sub);
|
Merge(cons, sub);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if (st.Content is XmlSchemaSimpleTypeUnion union)
|
case XmlSchemaSimpleTypeUnion union:
|
||||||
{
|
{
|
||||||
cons.Patterns.Add("(union)");
|
cons.Patterns.Add("(union)");
|
||||||
foreach (var memberType in union.BaseMemberTypes)
|
foreach (var memberType in union.BaseMemberTypes)
|
||||||
{
|
{
|
||||||
if (memberType is XmlSchemaSimpleType mst)
|
if (memberType is { } mst)
|
||||||
{
|
{
|
||||||
var sub = ExtractConstraints(mst);
|
var sub = ExtractConstraints(mst);
|
||||||
Merge(cons, sub);
|
Merge(cons, sub);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return cons;
|
return cons;
|
||||||
@ -261,8 +327,8 @@ namespace XSDVisualiser.Parsing
|
|||||||
private static void Merge(ConstraintSet target, ConstraintSet? source)
|
private static void Merge(ConstraintSet target, ConstraintSet? source)
|
||||||
{
|
{
|
||||||
if (source == null) return;
|
if (source == null) return;
|
||||||
foreach (var e in source.Enumerations) if (!target.Enumerations.Contains(e)) target.Enumerations.Add(e);
|
foreach (var e in source.Enumerations.Where(e => !target.Enumerations.Contains(e))) target.Enumerations.Add(e);
|
||||||
foreach (var p in source.Patterns) if (!target.Patterns.Contains(p)) target.Patterns.Add(p);
|
foreach (var p in source.Patterns.Where(p => !target.Patterns.Contains(p))) target.Patterns.Add(p);
|
||||||
if (source.Numeric != null)
|
if (source.Numeric != null)
|
||||||
{
|
{
|
||||||
target.Numeric ??= new NumericBounds();
|
target.Numeric ??= new NumericBounds();
|
||||||
@ -271,8 +337,9 @@ namespace XSDVisualiser.Parsing
|
|||||||
target.Numeric.MinExclusive ??= source.Numeric.MinExclusive;
|
target.Numeric.MinExclusive ??= source.Numeric.MinExclusive;
|
||||||
target.Numeric.MaxExclusive ??= source.Numeric.MaxExclusive;
|
target.Numeric.MaxExclusive ??= source.Numeric.MaxExclusive;
|
||||||
}
|
}
|
||||||
if (source.Length != null)
|
|
||||||
{
|
if (source.Length == null) return;
|
||||||
|
|
||||||
target.Length ??= new LengthBounds();
|
target.Length ??= new LengthBounds();
|
||||||
if (source.Length.LengthSpecified && !target.Length.LengthSpecified)
|
if (source.Length.LengthSpecified && !target.Length.LengthSpecified)
|
||||||
{
|
{
|
||||||
@ -284,13 +351,12 @@ namespace XSDVisualiser.Parsing
|
|||||||
target.Length.MinLength = source.Length.MinLength;
|
target.Length.MinLength = source.Length.MinLength;
|
||||||
target.Length.MinLengthSpecified = true;
|
target.Length.MinLengthSpecified = true;
|
||||||
}
|
}
|
||||||
if (source.Length.MaxLengthSpecified && !target.Length.MaxLengthSpecified)
|
|
||||||
{
|
if (!source.Length.MaxLengthSpecified || target.Length.MaxLengthSpecified) return;
|
||||||
|
|
||||||
target.Length.MaxLength = source.Length.MaxLength;
|
target.Length.MaxLength = source.Length.MaxLength;
|
||||||
target.Length.MaxLengthSpecified = true;
|
target.Length.MaxLengthSpecified = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void MergeFacets(ConstraintSet cons, XmlSchemaObjectCollection facets)
|
private static void MergeFacets(ConstraintSet cons, XmlSchemaObjectCollection facets)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
using XSDVisualiser.Parsing;
|
using XSDVisualiser.Core;
|
||||||
using XSDVisualiser.Utils;
|
using XSDVisualiser.Utils;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -80,6 +80,8 @@ catch (Exception ex)
|
|||||||
Environment.Exit(3);
|
Environment.Exit(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
|||||||
@ -8,6 +8,12 @@ namespace XSDVisualiser.Utils
|
|||||||
{
|
{
|
||||||
public static class Serialization
|
public static class Serialization
|
||||||
{
|
{
|
||||||
|
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||||
|
{
|
||||||
|
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));
|
||||||
@ -20,12 +26,7 @@ namespace XSDVisualiser.Utils
|
|||||||
|
|
||||||
public static string ToJson<T>(T obj)
|
public static string ToJson<T>(T obj)
|
||||||
{
|
{
|
||||||
var options = new JsonSerializerOptions
|
return JsonSerializer.Serialize(obj, JsonOptions);
|
||||||
{
|
|
||||||
WriteIndented = true,
|
|
||||||
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
|
|
||||||
};
|
|
||||||
return JsonSerializer.Serialize(obj, options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class Utf8StringWriter : StringWriter
|
private sealed class Utf8StringWriter : StringWriter
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user