115 lines
3.7 KiB
C#
115 lines
3.7 KiB
C#
using System.Globalization;
|
|
using System.Text;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Interactivity;
|
|
using Avalonia.VisualTree;
|
|
using XSDVisualiser.Core;
|
|
|
|
namespace XSDVisualiser.Desktop.Views;
|
|
|
|
public partial class LeftTreeView : UserControl
|
|
{
|
|
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)
|
|
{
|
|
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 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);
|
|
}
|
|
|
|
if (dict.Count == 0) return string.Empty;
|
|
|
|
var parts = new List<string>(dict.Count);
|
|
foreach (var kvp in dict)
|
|
{
|
|
var joinedValues = string.Join(",", kvp.Value);
|
|
parts.Add($"{kvp.Key}={joinedValues}");
|
|
}
|
|
|
|
return string.Join(";", parts);
|
|
}
|
|
} |