Started working on documentation
This commit is contained in:
parent
e8c30e8a05
commit
aacaaee493
@ -4,6 +4,10 @@ using XSDVisualiser.Core;
|
|||||||
|
|
||||||
namespace XSDVisualiser.Desktop.ViewModels;
|
namespace XSDVisualiser.Desktop.ViewModels;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// View model exposing the parsed schema for binding in the main window.
|
||||||
|
/// Provides selection state and information about the currently loaded XSD file.
|
||||||
|
/// </summary>
|
||||||
public class MainWindowViewModel(SchemaModel model) : INotifyPropertyChanged
|
public class MainWindowViewModel(SchemaModel model) : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
private string? _currentFilePath;
|
private string? _currentFilePath;
|
||||||
@ -11,6 +15,10 @@ public class MainWindowViewModel(SchemaModel model) : INotifyPropertyChanged
|
|||||||
private SchemaNode? _selectedNode;
|
private SchemaNode? _selectedNode;
|
||||||
private SchemaNode? _selectedRootElement;
|
private SchemaNode? _selectedRootElement;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parsed schema model currently displayed by the UI.
|
||||||
|
/// Setting this property updates dependent properties and notifies bindings.
|
||||||
|
/// </summary>
|
||||||
public SchemaModel Model
|
public SchemaModel Model
|
||||||
{
|
{
|
||||||
get => _model;
|
get => _model;
|
||||||
@ -24,8 +32,14 @@ public class MainWindowViewModel(SchemaModel model) : INotifyPropertyChanged
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All global elements defined in the loaded schema.
|
||||||
|
/// </summary>
|
||||||
public IEnumerable<SchemaNode> RootElements => _model?.RootElements ?? Enumerable.Empty<SchemaNode>();
|
public IEnumerable<SchemaNode> RootElements => _model?.RootElements ?? Enumerable.Empty<SchemaNode>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If set, filters the view to show only this root element and its subtree.
|
||||||
|
/// </summary>
|
||||||
public SchemaNode? SelectedRootElement
|
public SchemaNode? SelectedRootElement
|
||||||
{
|
{
|
||||||
get => _selectedRootElement;
|
get => _selectedRootElement;
|
||||||
|
|||||||
@ -15,6 +15,10 @@ using XSDVisualiser.Desktop.ViewModels;
|
|||||||
|
|
||||||
namespace XSDVisualiser.Desktop.Views;
|
namespace XSDVisualiser.Desktop.Views;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Left-side tree view control that displays parsed XSD schema nodes and provides actions
|
||||||
|
/// such as copy-as-TSV, validate against XML, and export sample XML.
|
||||||
|
/// </summary>
|
||||||
public partial class LeftTreeView : UserControl
|
public partial class LeftTreeView : UserControl
|
||||||
{
|
{
|
||||||
public LeftTreeView()
|
public LeftTreeView()
|
||||||
@ -24,7 +28,6 @@ public partial class LeftTreeView : UserControl
|
|||||||
|
|
||||||
private async void OnCopyAsTsvClick(object? sender, RoutedEventArgs e)
|
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;
|
var node = (sender as MenuItem)?.DataContext as SchemaNode;
|
||||||
if (node == null)
|
if (node == null)
|
||||||
node =
|
node =
|
||||||
@ -40,7 +43,6 @@ public partial class LeftTreeView : UserControl
|
|||||||
|
|
||||||
private async void OnValidateAgainstXmlClick(object? sender, RoutedEventArgs e)
|
private async void OnValidateAgainstXmlClick(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
// Resolve node from context
|
|
||||||
var node = (sender as MenuItem)?.DataContext as SchemaNode;
|
var node = (sender as MenuItem)?.DataContext as SchemaNode;
|
||||||
if (node == null)
|
if (node == null)
|
||||||
node = (sender as Control)?.GetVisualAncestors().OfType<Control>().FirstOrDefault()?.DataContext as SchemaNode;
|
node = (sender as Control)?.GetVisualAncestors().OfType<Control>().FirstOrDefault()?.DataContext as SchemaNode;
|
||||||
@ -96,7 +98,6 @@ public partial class LeftTreeView : UserControl
|
|||||||
private static string BuildTsv(SchemaNode root)
|
private static string BuildTsv(SchemaNode root)
|
||||||
{
|
{
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
// Header
|
|
||||||
sb.AppendLine(
|
sb.AppendLine(
|
||||||
"Depth\tPath\tName\tNamespace\tTypeName\tBuiltInType\tMinOccurs\tMaxOccurs\tContentModel\tConstraints\tIsNillable");
|
"Depth\tPath\tName\tNamespace\tTypeName\tBuiltInType\tMinOccurs\tMaxOccurs\tContentModel\tConstraints\tIsNillable");
|
||||||
var initialPath = root.Name ?? string.Empty;
|
var initialPath = root.Name ?? string.Empty;
|
||||||
@ -146,7 +147,6 @@ public partial class LeftTreeView : UserControl
|
|||||||
|
|
||||||
private static async Task<string?> TryGetLocalPathAsync(IStorageFile file)
|
private static async Task<string?> TryGetLocalPathAsync(IStorageFile file)
|
||||||
{
|
{
|
||||||
// Avalonia provides TryGetLocalPath extension for local files; otherwise, we can't access it via System.IO API.
|
|
||||||
return file.TryGetLocalPath();
|
return file.TryGetLocalPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,10 +183,8 @@ public partial class LeftTreeView : UserControl
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Compose content
|
|
||||||
if (dialog.Content is Grid grid)
|
if (dialog.Content is Grid grid)
|
||||||
{
|
{
|
||||||
// Use a read-only TextBox so users can select text easily
|
|
||||||
var textBox = new TextBox
|
var textBox = new TextBox
|
||||||
{
|
{
|
||||||
Text = text,
|
Text = text,
|
||||||
@ -198,7 +196,6 @@ public partial class LeftTreeView : UserControl
|
|||||||
|
|
||||||
var scroller = new ScrollViewer { Content = textBox };
|
var scroller = new ScrollViewer { Content = textBox };
|
||||||
|
|
||||||
// Copy button
|
|
||||||
var copyButton = new Button
|
var copyButton = new Button
|
||||||
{
|
{
|
||||||
Content = "Copy",
|
Content = "Copy",
|
||||||
@ -212,7 +209,6 @@ public partial class LeftTreeView : UserControl
|
|||||||
if (topLevel?.Clipboard != null)
|
if (topLevel?.Clipboard != null)
|
||||||
{
|
{
|
||||||
await topLevel.Clipboard.SetTextAsync(text);
|
await topLevel.Clipboard.SetTextAsync(text);
|
||||||
// Provide lightweight feedback by changing content briefly
|
|
||||||
if (copyButton.Content is string)
|
if (copyButton.Content is string)
|
||||||
copyButton.Content = "Copied";
|
copyButton.Content = "Copied";
|
||||||
await Task.Delay(1000);
|
await Task.Delay(1000);
|
||||||
|
|||||||
@ -7,15 +7,33 @@ namespace XSDVisualiser.Core;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class AttributeInfo
|
public class AttributeInfo
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Local name of the attribute.
|
||||||
|
/// </summary>
|
||||||
[XmlAttribute] public string? Name { get; set; }
|
[XmlAttribute] public string? Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Namespace URI of the attribute, if any.
|
||||||
|
/// </summary>
|
||||||
[XmlAttribute] public string? Namespace { get; set; }
|
[XmlAttribute] public string? Namespace { get; set; }
|
||||||
|
|
||||||
[XmlAttribute] public string? Use { get; set; } // optional | required | prohibited
|
/// <summary>
|
||||||
|
/// Usage of the attribute: optional | required | prohibited.
|
||||||
|
/// </summary>
|
||||||
|
[XmlAttribute] public string? Use { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The qualified type name if the attribute has a named type.
|
||||||
|
/// </summary>
|
||||||
[XmlAttribute] public string? TypeName { get; set; }
|
[XmlAttribute] public string? TypeName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Built-in XML Schema type code name when applicable.
|
||||||
|
/// </summary>
|
||||||
[XmlAttribute] public string? BuiltInType { get; set; }
|
[XmlAttribute] public string? BuiltInType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Optional constraints applied to the attribute's simple type.
|
||||||
|
/// </summary>
|
||||||
[XmlElement] public ConstraintSet? Constraints { get; set; }
|
[XmlElement] public ConstraintSet? Constraints { get; set; }
|
||||||
}
|
}
|
||||||
@ -7,9 +7,14 @@ namespace XSDVisualiser.Core;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ConstraintSet
|
public class ConstraintSet
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The qualified name of the base type this constraint set applies to, if any.
|
||||||
|
/// </summary>
|
||||||
[XmlAttribute] public string? BaseTypeName { get; set; }
|
[XmlAttribute] public string? BaseTypeName { get; set; }
|
||||||
|
|
||||||
// Generic catch-all list of constraints for dynamic display and tooling
|
/// <summary>
|
||||||
|
/// A catch-all list of constraint entries for dynamic display and tooling.
|
||||||
|
/// </summary>
|
||||||
[XmlArray("Constraints")]
|
[XmlArray("Constraints")]
|
||||||
[XmlArrayItem("Constraint")]
|
[XmlArrayItem("Constraint")]
|
||||||
public List<ConstraintEntry> Constraints { get; set; } = new();
|
public List<ConstraintEntry> Constraints { get; set; } = new();
|
||||||
|
|||||||
@ -29,7 +29,10 @@ public class SchemaNode
|
|||||||
[XmlArrayItem("Element")]
|
[XmlArrayItem("Element")]
|
||||||
public List<SchemaNode> Children { get; set; } = new();
|
public List<SchemaNode> Children { get; set; } = new();
|
||||||
|
|
||||||
[XmlAttribute] public string? ContentModel { get; set; } // sequence | choice | all | simple
|
/// <summary>
|
||||||
|
/// Content model for this node: sequence | choice | all | simple. For synthetic group nodes, may be "group".
|
||||||
|
/// </summary>
|
||||||
|
[XmlAttribute] public string? ContentModel { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Human-readable documentation extracted from xsd:annotation/xsd:documentation.
|
/// Human-readable documentation extracted from xsd:annotation/xsd:documentation.
|
||||||
@ -38,14 +41,13 @@ public class SchemaNode
|
|||||||
[XmlElement]
|
[XmlElement]
|
||||||
public string? Documentation { get; set; }
|
public string? Documentation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a human-readable name for UI elements (prefers Name, then TypeName).
|
||||||
|
/// </summary>
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
// Used by AutoCompleteBox to get text for filtering and matching
|
if (!string.IsNullOrEmpty(Name)) return Name!;
|
||||||
// Prefer Name, then TypeName, and fall back to base implementation
|
if (!string.IsNullOrEmpty(TypeName)) return TypeName!;
|
||||||
if (!string.IsNullOrEmpty(Name))
|
|
||||||
return Name!;
|
|
||||||
if (!string.IsNullOrEmpty(TypeName))
|
|
||||||
return TypeName!;
|
|
||||||
return base.ToString();
|
return base.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4,10 +4,18 @@ using System.Xml.Schema;
|
|||||||
|
|
||||||
namespace XSDVisualiser.Core;
|
namespace XSDVisualiser.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses XML Schema (XSD) into a simplified model for visualization and tooling.
|
||||||
|
/// </summary>
|
||||||
public class XsdSchemaParser
|
public class XsdSchemaParser
|
||||||
{
|
{
|
||||||
private readonly XmlSchemaSet _set = new();
|
private readonly XmlSchemaSet _set = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads and compiles the provided XSD file into a schema set, then produces a simplified schema model.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="xsdPath">Path to the XSD file.</param>
|
||||||
|
/// <returns>A populated <see cref="SchemaModel"/> representing global elements and their structures.</returns>
|
||||||
public SchemaModel Parse(string xsdPath)
|
public SchemaModel Parse(string xsdPath)
|
||||||
{
|
{
|
||||||
_set.XmlResolver = new XmlUrlResolver();
|
_set.XmlResolver = new XmlUrlResolver();
|
||||||
@ -34,8 +42,7 @@ public class XsdSchemaParser
|
|||||||
|
|
||||||
private void ValidationCallback(object? sender, ValidationEventArgs e)
|
private void ValidationCallback(object? sender, ValidationEventArgs e)
|
||||||
{
|
{
|
||||||
// For now, we do not throw; we capture compiled info best-effort.
|
// Intentionally no-op: schema is parsed best-effort without throwing on compile warnings/errors.
|
||||||
// Console.Error.WriteLine($"[XSD Validation {e.Severity}] {e.Message}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SchemaNode BuildNodeForElement(XmlSchemaElement element, string? parentContentModel)
|
private SchemaNode BuildNodeForElement(XmlSchemaElement element, string? parentContentModel)
|
||||||
|
|||||||
@ -11,12 +11,29 @@ namespace XSDVisualiser.Core;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class XmlValidator
|
public static class XmlValidator
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Validates an XML document against the global element specified by name and optional namespace
|
||||||
|
/// from the XSD located at <paramref name="xsdPath"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="xsdPath">Path to the XSD file containing the target element/type definitions.</param>
|
||||||
|
/// <param name="elementName">The local name of the global element to validate against.</param>
|
||||||
|
/// <param name="elementNamespace">The namespace URI of the element; may be null to auto-detect.</param>
|
||||||
|
/// <param name="xmlPath">Path to the XML file to validate.</param>
|
||||||
|
/// <returns>Aggregated validation result with errors/warnings and diagnostics.</returns>
|
||||||
public static XmlValidationResult ValidateAgainstElement(string xsdPath, string elementName, string? elementNamespace, string xmlPath)
|
public static XmlValidationResult ValidateAgainstElement(string xsdPath, string elementName, string? elementNamespace, string xmlPath)
|
||||||
{
|
{
|
||||||
var set = BuildSchemaSet(xsdPath);
|
var set = BuildSchemaSet(xsdPath);
|
||||||
return ValidateAgainstElement(set, elementName, elementNamespace, xmlPath);
|
return ValidateAgainstElement(set, elementName, elementNamespace, xmlPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validates an XML document against a specific global element within an already built <see cref="XmlSchemaSet"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="schemas">Compiled schema set to use for validation.</param>
|
||||||
|
/// <param name="elementName">The local name of the global element to validate against.</param>
|
||||||
|
/// <param name="elementNamespace">The namespace URI of the element; may be null to auto-detect.</param>
|
||||||
|
/// <param name="xmlPath">Path to the XML file to validate.</param>
|
||||||
|
/// <returns>Aggregated validation result with errors/warnings and diagnostics.</returns>
|
||||||
public static XmlValidationResult ValidateAgainstElement(XmlSchemaSet schemas, string elementName, string? elementNamespace, string xmlPath)
|
public static XmlValidationResult ValidateAgainstElement(XmlSchemaSet schemas, string elementName, string? elementNamespace, string xmlPath)
|
||||||
{
|
{
|
||||||
var result = new XmlValidationResult();
|
var result = new XmlValidationResult();
|
||||||
@ -428,15 +445,31 @@ public static class XmlValidator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Aggregates XML validation outcomes, including errors, warnings, and overall validity.
|
||||||
|
/// </summary>
|
||||||
public sealed class XmlValidationResult
|
public sealed class XmlValidationResult
|
||||||
{
|
{
|
||||||
private readonly List<XmlValidationIssue> _issues = new();
|
private readonly List<XmlValidationIssue> _issues = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True if no validation errors have been recorded.
|
||||||
|
/// </summary>
|
||||||
public bool IsValid => _issues.TrueForAll(i => i.Severity != XmlSeverityType.Error);
|
public bool IsValid => _issues.TrueForAll(i => i.Severity != XmlSeverityType.Error);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All recorded validation issues (errors and warnings) in chronological order.
|
||||||
|
/// </summary>
|
||||||
public IReadOnlyList<XmlValidationIssue> Issues => _issues;
|
public IReadOnlyList<XmlValidationIssue> Issues => _issues;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All recorded validation errors.
|
||||||
|
/// </summary>
|
||||||
public IEnumerable<XmlValidationIssue> Errors => _issues.Where(i => i.Severity == XmlSeverityType.Error);
|
public IEnumerable<XmlValidationIssue> Errors => _issues.Where(i => i.Severity == XmlSeverityType.Error);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All recorded validation warnings.
|
||||||
|
/// </summary>
|
||||||
public IEnumerable<XmlValidationIssue> Warnings => _issues.Where(i => i.Severity == XmlSeverityType.Warning);
|
public IEnumerable<XmlValidationIssue> Warnings => _issues.Where(i => i.Severity == XmlSeverityType.Warning);
|
||||||
|
|
||||||
internal void AddError(string message, int? line = null, int? position = null) =>
|
internal void AddError(string message, int? line = null, int? position = null) =>
|
||||||
@ -446,6 +479,13 @@ public sealed class XmlValidationResult
|
|||||||
_issues.Add(new XmlValidationIssue(XmlSeverityType.Warning, message, line ?? 0, position ?? 0));
|
_issues.Add(new XmlValidationIssue(XmlSeverityType.Warning, message, line ?? 0, position ?? 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a single validation issue (error or warning) with optional location information.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Severity">Issue severity (Error or Warning).</param>
|
||||||
|
/// <param name="Message">Human-readable description of the issue.</param>
|
||||||
|
/// <param name="LineNumber">Line number in the XML where the issue occurred, if available.</param>
|
||||||
|
/// <param name="LinePosition">Column position in the XML where the issue occurred, if available.</param>
|
||||||
public sealed record XmlValidationIssue(XmlSeverityType Severity, string Message, int LineNumber, int LinePosition);
|
public sealed record XmlValidationIssue(XmlSeverityType Severity, string Message, int LineNumber, int LinePosition);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user