c# - How to create a tree representing namespaces from their string representations -
how can create tree-like data structure namespaces.
for example, these namespaces:
enums.newenums.new1 enums.newenums.new2 enums.newenums.new3 enums.oldenums enums.test.sub enums.test.sub.ok
and load treeview shown below:
i tried split namespaces, life of me can't think of logic generate correctly.
also tried generate way generate directory structure, can't head around since namespaces need splitting.
1. parsing namespace
here class represents namespace. represents namespace dictionary of directly nested namespaces. generate namespace
s strings provides static methods use recursive calls , linq:
public class namespace : idictionary<string, namespace> { #region static public static ienumerable<namespace> fromstrings(ienumerable<string> namespacestrings) { // split strings var splitsubnamespaces = namespacestrings .select(fullnamespace => fullnamespace.split('.')); return fromsplitstrings(null, splitsubnamespaces); } public static ienumerable<namespace> fromsplitstrings(namespace root, ienumerable<ienumerable<string>> splitsubnamespaces) { if (splitsubnamespaces == null) throw new argumentnullexception("splitsubnamespaces"); return splitsubnamespaces // remove split sequences have no elements .where(splitsubnamespace => splitsubnamespace.any()) // group outermost namespace .groupby(splitnamespace => splitnamespace.first()) // create namespace each group , prepare sequences represent nested namespaces .select(group => new { root = new namespace(group.key, root), splitsubnamespaces = group .select(splitnamespace => splitnamespace.skip(1)) }) // select nested namespaces recursive split call .select(obj => new { root = obj.root, subnamespaces = fromsplitstrings(obj.root, obj.splitsubnamespaces) }) // select uppermost level namespaces return .select(obj => obj.root) // avoid deferred execution problems when recursive function may not able create nested namespaces .toarray(); } #endregion #region fields private idictionary<string, namespace> subnamespaces; #endregion #region constructors private namespace(string nameonlevel, namespace parent) { if (string.isnullorwhitespace(nameonlevel)) throw new argumentexception("nameoflevel"); this.parent = parent; this.nameonlevel = nameonlevel; this.subnamespaces = new dictionary<string, namespace>(); if (this.parent != null) { this.parent.add(this.nameonlevel, this); } } private namespace(string nameoflevel) : this(nameoflevel, null) { } #endregion #region properties public string nameonlevel { get; private set; } public string fullname { { if (this.parent == null) return this.nameonlevel; return string.format("{0}.{1}", this.parent.fullname, this.nameonlevel); } } private namespace _parent; public namespace parent { { return this._parent; } private set { if (this.parent != null) this.parent.remove(this.nameonlevel); this._parent = value; } } #endregion #region idictionary implementation public void add(string key, namespace value) { if (this.containskey(key)) throw new invalidoperationexception("namespace contains namespace such name on level"); this.subnamespaces.add(key, value); } public bool containskey(string key) { return this.subnamespaces.containskey(key); } public icollection<string> keys { { return this.subnamespaces.keys; } } public bool remove(string key) { if (!this.containskey(key)) throw new keynotfoundexception(); this[key]._parent = null; return this.subnamespaces.remove(key); } public bool trygetvalue(string key, out namespace value) { return this.subnamespaces.trygetvalue(key, out value); } public icollection<namespace> values { { return this.subnamespaces.values; } } public icollection<namespace> subnamespaces { { return this.subnamespaces.values; } } public namespace this[string nameonlevel] { { return this.subnamespaces[nameonlevel]; } set { if (value == null) throw new argumentexception("value"); namespace toreplace; if (this.trygetvalue(nameonlevel, out toreplace)) { toreplace.parent = null; } value.parent = this; } } public void add(keyvaluepair<string, namespace> item) { this.add(item.key, item.value); } public void clear() { foreach (var subnamespace in this.subnamespaces.select(kv => kv.value)) { subnamespace._parent = null; } this.subnamespaces.clear(); } public bool contains(keyvaluepair<string, namespace> item) { return this.subnamespaces.contains(item); } public void copyto(keyvaluepair<string, namespace>[] array, int arrayindex) { this.subnamespaces.copyto(array, arrayindex); } public int count { { return this.subnamespaces.count; } } public bool isreadonly { { return false; } } public bool remove(keyvaluepair<string, namespace> item) { return this.subnamespaces.remove(item); } public ienumerator<keyvaluepair<string, namespace>> getenumerator() { return this.subnamespaces.getenumerator(); } system.collections.ienumerator system.collections.ienumerable.getenumerator() { return this.getenumerator(); } #endregion #region overrides public override string tostring() { return this.fullname; } #endregion }
p.s: class may have few incorrectly implemented methods.
p.s.1: parsing methods can rewritten without linq. linq solution not idiomatic or example of how , when use linq. short , simple.
2. adding namespaces treeview
you haven't mentioned ui framework use, have defaulted windows forms. assuming have added treeview named treeview_namespaces
form:
public form1() { initializecomponent(); var namespacestrings = new string[] { "enums.newenums.new1", "enums.newenums.new2", "enums.newenums.new3", "enums.oldenums", "enums.test.sub", "enums.test.sub.ok" }; var namespaces = namespace.fromstrings(namespacestrings); addnamespaces(this.treeview_namespaces.nodes, namespaces); } void addnamespaces(treenodecollection nodecollection, ienumerable<namespace> namespaces) { foreach (var anamespace in namespaces) { treenode node = new treenode(anamespace.nameonlevel); nodecollection.add(node); addnamespaces(node.nodes, anamespace.subnamespaces); node.expand(); } }
3. if need generate such tree real namespaces
to have walk through types in assembly , namespaces:
for example, code types in executing assembly:
var namespacestrings = assembly .getexecutingassembly() .gettypes() .select(type => type.namespace) .where(@namespace => @namespace != null);
Comments
Post a Comment