CSharp · Console
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var tree = new NamespaceDirectoryTree
{
"Root.Sub11.Sub11.Sub3",
"Root.Sub11.Sub11",
"Root.Sub11.Sub21",
"Root.Sub11.Sub21.Sub12",
"Root.Sub11.Sub21.Sub13.Sub14",
"Root.Sub11.Sub21.Sub13.Sub14",
"Root.Sub11.Sub21.Sub13.Sub14",
"Root.Sub11.Sub21.Sub13.Sub14.Sub47"
};
foreach(var directory in tree)
{
System.Console.WriteLine(directory + ", ");
}
}
public class NamespaceDirectoryTree : ICollection<string>
{
private readonly NamespaceNode _root = new NamespaceNode(null);
public int Count => this._root.Count(n => n.Ends);
bool ICollection<string>.IsReadOnly => false;
public NamespaceDirectoryTree() { }
public NamespaceDirectoryTree(IEnumerable<string> namespaces)
{
foreach (string ns in namespaces)
{
this.AddPath(this._root, ns);
}
}
private void AddPath(NamespaceNode node, string ns)
{
if (ns.Length == 0)
{
throw new ArgumentNullException(nameof(ns));
}
NamespaceNode parent = node;
string parentNamespace = ns;
int index;
do
{
index = parentNamespace.IndexOf('.');
if (index == -1)
{
NamespaceNode child = parent.GetOrAddChild(parentNamespace);
child.Ends = true;
}
else
{
NamespaceNode child = parent.GetOrAddChild(parentNamespace.Substring(0, index));
parent = child;
parentNamespace = parentNamespace.Substring(index + 1);
}
} while (index != -1);
}
public void Add(string item) => this.AddPath(this._root, item);
public void Clear() => this._root.Children.Clear();
public bool Contains(string item) => this._root.EnumerateDirectories().Any(n => n == item);
bool ICollection<string>.Remove(string item) => throw new NotSupportedException();
public void CopyTo(string[] array, int arrayIndex)
{
foreach (string path in this._root.EnumerateDirectories())
{
array[arrayIndex++] = path;
}
}
public IEnumerator<string> GetEnumerator()
{
return this._root.EnumerateDirectories().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
private sealed class NamespaceNode : IEnumerable<NamespaceNode>
{
public NamespaceNode(string? valueString)
{
this.Value = valueString;
this.Children = new Dictionary<string, NamespaceNode>(BucketStringComparer.Instance);
}
public bool Ends { get; set; }
public string? Value { get; }
public IDictionary<string, NamespaceNode> Children { get; }
public NamespaceNode GetOrAddChild(string path)
{
if (path is null || path.Length == 0)
{
throw new ArgumentNullException(nameof(path));
}
if (this.Children.TryGetValue(path, out NamespaceNode? child))
{
return child;
}
child = new NamespaceNode(path);
this.Children.Add(child.Value!, child);
return child;
}
public IEnumerable<string> EnumerateDirectories()
{
if (this.Ends)
{
if (this.Value is null)
{
yield break;
}
yield return this.Value;
}
string separator = this.Children.Count == 1 && !this.Ends ? "." : "\\";
foreach (NamespaceNode child in this.Children.Values)
{
foreach (string path in child.EnumerateDirectories())
{
if (this.Value is null)
{
yield return path!;
}
else
{
yield return String.Concat(this.Value, separator, path)!;
}
}
}
}
public IEnumerator<NamespaceNode> GetEnumerator()
{
var nodeBacklog = new Queue<NamespaceNode>(this.Children.Count);
nodeBacklog.Enqueue(this);
while (nodeBacklog.Count != 0)
{
NamespaceNode temp = nodeBacklog.Dequeue();
foreach (NamespaceNode child in temp.Children.Values)
{
nodeBacklog.Enqueue(child);
}
yield return temp;
}
}
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
}
}
internal sealed class BucketStringComparer : IComparer<string>, IEqualityComparer<string>
{
private static readonly Lazy<BucketStringComparer> s_instance = new Lazy<BucketStringComparer>(() => new BucketStringComparer());
public static BucketStringComparer Instance => s_instance.Value;
public int Compare( string? x, string? y) => string.Compare(x, y);
public bool Equals(string? x, string? y) => string.Equals(x, y);
public int GetHashCode(string obj) => string.IsNullOrEmpty(obj) ? 0 : obj[0] + obj.Length;
}
}