Last active
August 11, 2023 19:46
-
-
Save ChaunceyHoover/3ac18e33445afb2535c4f6afa3af8ac7 to your computer and use it in GitHub Desktop.
Period separated, string-based nodes to a C# parent-child / tree-like node list
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class PermissionNode : List<PermissionNode> { | |
/// <summary> | |
/// Represents each text-based node inside of a permission string | |
/// </summary> | |
public string Key { get; set; } | |
/// <summary> | |
/// The original, non-split permission string for this node | |
/// </summary> | |
public string Original { get; set; } | |
/// <summary> | |
/// The nodes below this node or "module", if any | |
/// </summary> | |
public List<PermissionNode> Children { get; set; } = new List<PermissionNode>(); | |
/// <summary> | |
/// Converts a list of permission strings (ex: "App.Category.Sub Category.Action", "App.X.Y.Z.Action", etc.) into a parent-child node list | |
/// </summary> | |
/// <remarks> | |
/// NOTE: If all permissions start with the same node (ex: "App.%"), you can safely get the first element of the list and not iterate | |
/// through it. However, if it has multiple (ex: "App1.%" and "App2.%") or you aren't sure if it'll have multiple, you will have to iterate | |
/// through this list to get all node trees. | |
/// </remarks> | |
/// <param name="nodes">The list of permissions to convert</param> | |
/// <returns>A parent-child node list</returns> | |
public static List<PermissionNode> Create(params string[] nodes) { | |
// "parent" will act as a container to split up the given nodes | |
var parent = new PermissionNode { Key = "Root", Original = "" }; // arbitrary names - have no meaning | |
foreach (var node in nodes) { | |
Process(parent, node, node); | |
} | |
return parent.Children; | |
} | |
/// <summary> | |
/// Processes the given permission string recursively to break it down from a string to a series of linked nodes | |
/// </summary> | |
/// <param name="parent">The root node of the list</param> | |
/// <param name="permission">The permission string to be broken down and parented accordingly in the parent</param> | |
/// <param name="original">The original, non-split permission</param> | |
private static void Process(PermissionNode parent, string permission, string original) { | |
var split = permission.Split('.'); | |
if (split.Count() == 1) { | |
// End of the recursive line - add to parent with no children | |
parent.Children.Add(new PermissionNode { Key = permission, Original = original, Children = null }); | |
} else { | |
// Check root of this node has already been processed or not | |
var node = parent.Children.FirstOrDefault(x => x.Key == split[0]); | |
// If it hasn't been, add it to list with empty list of children | |
if (node == null) { | |
node = new PermissionNode { Key = split[0] }; | |
parent.Children.Add(node); | |
} | |
// Populate children | |
Process(node, String.Join(".", split.Skip(1).Take(split.Count() - 1)), original); | |
} | |
} | |
/// <summary> | |
/// Converts the given parent-child PermissionNode into a formatted HTML list (ex: unordered or ordered) based off the given `listType` | |
/// </summary> | |
/// <remarks>NOTE: This is to be used inside an existing list (ex: <ul> or <ol>) - it will NOT generate the opening tags</remarks> | |
/// <param name="pm">The parent node, populated with the children</param> | |
/// <param name="listType">The type of HTML list to use, ex: "ul" or "ol"</param> | |
/// <param name="tabCount">Used to specify starting amount of tabs for the list for formatting (defaults to 0)</param> | |
public static string ToHtmlList(PermissionNode pm, string listType, int tabCount = 0) { | |
// Write the initial <li> element with checkbox and proper tabs. If this is the final child, make the ID of the input the original string value (ex: 'App.X.Y.Z.Action', | |
// but make the label 'Action') | |
string listContent = $"{new string('\t', tabCount)}<li><input{(pm.Children == null ? $" id=\"{pm.Original}\"" : "")} type=\"checkbox\">{pm.Key}"; | |
if (pm.Children != null) { | |
listContent += $"{Environment.NewLine}{new string('\t', tabCount + 1)}<{listType}>{Environment.NewLine}"; | |
foreach (var node in pm.Children) | |
listContent += ToHtmlList(node, listType, tabCount + 1); | |
listContent += $"{new string('\t', tabCount + 1)}</{listType}>{Environment.NewLine}{new string('\t', tabCount)}</li>{Environment.NewLine}"; | |
} else { | |
listContent += $"</li>{Environment.NewLine}"; | |
} | |
return listContent; | |
} | |
/// <summary> | |
/// Prints out the parent-child node showing the relationship for the given PermissionNode | |
/// </summary> | |
/// <param name="pm">The PermissionNode to explore</param> | |
/// <param name="depth">The amount of '+' symbols to be used for this iteration of the recursive list</param> | |
public static void Print(PermissionNode pm, int depth = 0) { | |
// Print out the parent node | |
Console.WriteLine($"{new string('+', depth)}{pm.Key}"); | |
// If it has children, increase the depth by 1 and print out each child under the parent | |
if (pm.Children != null) | |
foreach (var node in pm.Children) | |
Print(node, depth + 1); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
////////////// | |
// EXAMPLES // | |
////////////// | |
// Convert the following list: | |
// ["App1.Category.Sub Category.Action 1", "App1.Category.Sub Category.Action 2", "App2.Category.Sub Category.Action 1", "App2.Category.Sub Category.Action 2"] | |
// into an unordered HTML list: | |
var list = PermissionNode.Create("App1.Category.Sub Category.Action 1", | |
"App1.Category.Sub Category.Action 2", | |
"App2.Category.Sub Category.Action 1", | |
"App2.Category.Sub Category.Action 2"); | |
foreach (var rootNode in list) | |
Console.WriteLine(PermissionNode.ToHtmlList(rootNode, "ul")); | |
/** | |
* Outputs the following: | |
<li><input type="checkbox">App1 | |
<ul> | |
<li><input type="checkbox">Category | |
<ul> | |
<li><input type="checkbox">Sub Category | |
<ul> | |
<li><input id="App1.Category.Sub Category.Action 1" type="checkbox">Action 1</li> | |
<li><input id="App1.Category.Sub Category.Action 2" type="checkbox">Action 2</li> | |
</ul> | |
</li> | |
</ul> | |
</li> | |
</ul> | |
</li> | |
<li><input type="checkbox">App2 | |
<ul> | |
<li><input type="checkbox">Category | |
<ul> | |
<li><input type="checkbox">Sub Category | |
<ul> | |
<li><input id="App2.Category.Sub Category.Action 1" type="checkbox">Action 1</li> | |
<li><input id="App2.Category.Sub Category.Action 2" type="checkbox">Action 2</li> | |
</ul> | |
</li> | |
</ul> | |
</li> | |
</ul> | |
</li> | |
*/ | |
//////////////////////////////////////// | |
// Debug the same list above in the terminal | |
var list = PermissionNode.Create("App1.Category.Sub Category.Action 1", | |
"App1.Category.Sub Category.Action 2", | |
"App2.Category.Sub Category.Action 1", | |
"App2.Category.Sub Category.Action 2"); | |
foreach (var rootNode in list) | |
PermissionNode.Print(rootNode); | |
/** | |
* Outputs: | |
App1 | |
+Category | |
++Sub Category | |
+++Action 1 | |
+++Action 2 | |
App2 | |
+Category | |
++Sub Category | |
+++Action 1 | |
+++Action 2 | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment