I am writing an application that works along side word and makes a custom menu system for it.
I want the user to be able to essentially add an infinite sub menus.
I am reading in from a text file where a tab represents indenting.
Example text file:
GROUP
MENU1
SUBMENU11
SUBSUBMENU111
SUBSUBMENU1111
SUBMENU12
MENU2
SUBMENU21
SUBSUBMENU211
Example XML output:
<group id="GROUP" label="GROUP">
<menu id="MENU1" label="MENU1" size="normal">
<menu id="SUBMENU11" label="SUBMENU11" size="normal" >
<menu id="SUBMENU111" label="SUBMENU111" size="normal" >
<menu id="SUBMENU1111" label="SUBMENU1111" size="normal" />
</menu>
</menu>
<menu id="SUBMENU12" label="SUBMENU12" size="normal" />
</menu>
<menu id="MENU2" label="MENU2" size="normal" >
<menu id="SUBMENU21" label="SUBMENU21" size="normal" >
<menu id="SUBMENU211" label="SUBMENU211" size="normal" />
</menu>
</menu>
</group>
Currently to add at certain points ie at 5 tabs down:
path.Descendants(ns + "menu").ElementAt(currentMenuIndex)
.Descendants(ns + "menu").ElementAt(currentMenu1Index)
.Descendants(ns + "menu").ElementAt(currentMenu2Index)
.Descendants(ns + "menu").LastOrDefault()
.Add(//add node info);
I am currently creating each of these descendant trees for each tab case, this makes the code bulky and if i can i would like to be able to do it programmatically for an infinite amount of tabs.
Any light you can shed on this would be wonderful, I've been going round in circles for days.
There is a much simpler syntax to create XElement
and XAttribute
s in an Xml Document
new XElement("group",
new XAttribute("id", "GROUP"),
new XElement("menu",
new XAttribute("id", "MENU1"),
... etc
Edit How about a recursive strategy here, with some Linq:
const string menu = // I've hardcoded tabs and newlines as SO formats these out.
"GROUP\r\n" +
"MENU1\r\n" +
"\tSUBMENU11\r\n" +
"\t\tSUBSUBMENU111\r\n"+
"\t\t\tSUBSUBMENU1111\r\n" +
"\tSUBMENU12\r\n"+
"MENU2\r\n" +
"\tSUBMENU21\r\n"+
"\t\tSUBSUBMENU211";
var sr = new StringReader(menu); // Obviously use a TextReader
var lines = sr.ReadToEnd()
.Split(new[] { Environment.NewLine },
StringSplitOptions.RemoveEmptyEntries);
var indentedLines =
lines
.Skip(1) // Group isn't part of the menu
.Select(
_ => new Tuple<string, int>( // Remove whitespace
Regex.Replace(_, @"\s+", ""),
_.Split(new[] { '\t' }).Count())) // Count the tabs
.ToList();
var doc = new XDocument();
doc.Add(new XElement("group", // Add the root group
new XAttribute("id", lines[0].Trim()),
new XAttribute("label", lines[0].Trim()),
BuildTree(indentedLines, 1, String.Empty))); // Add the top lvl nodes
With this recursive helper:
IEnumerable<XElement> BuildTree(
IList<Tuple<string, int>> indentedLines,
int indentLevel, String parentNode)
{
Func<string, string> getNodeParent =
node =>
{
var line = indentedLines.First(_ => _.Item1 == node);
for (var index = indentedLines.IndexOf(line); index >= 0; index --)
{
if (indentedLines[index].Item2 < line.Item2)
{
return indentedLines[index].Item1;
}
}
return String.Empty; // has no parent
};
foreach (var line in indentedLines.Where(_ => _.Item2 == indentLevel
&& getNodeParent(_.Item1) == parentNode))
{
yield return new XElement("menu",
new XAttribute("id", line.Item1),
new XAttribute("label", line.Item1),
new XAttribute("size", "normal"),
BuildTree(indentedLines, indentLevel + 1, line.Item1));
}
}
Produces this Xml, which I think is what you are after:
<group id="GROUP" label="GROUP">
<menu id="MENU1" label="MENU1" size="normal">
<menu id="SUBMENU11" label="SUBMENU11" size="normal">
<menu id="SUBSUBMENU111" label="SUBSUBMENU111" size="normal">
<menu id="SUBSUBMENU1111" label="SUBSUBMENU1111" size="normal" />
</menu>
</menu>
<menu id="SUBMENU12" label="SUBMENU12" size="normal" />
</menu>
<menu id="MENU2" label="MENU2" size="normal">
<menu id="SUBMENU21" label="SUBMENU21" size="normal">
<menu id="SUBSUBMENU211" label="SUBSUBMENU211" size="normal" />
</menu>
</menu>
</group>