Able
Askowl Base Library Enabler
Trees.cs
1 // Copyright 2018 (C) paul@marrington.net http://www.askowl.net/unity-packages
2 
3 using System;
4 using System.Collections.Generic;
5 
6 namespace Askowl {
7  /// <a href="http://bit.ly/2Oq9Axs">Tree Container</a>
8  // ReSharper disable once ClassNeverInstantiated.Global
9  public class Trees : IDisposable {
10  #region Private Functionality
11  // ReSharper disable once ClassNeverInstantiated.Global
12  internal class Node : IDisposable {
13  private Node() { }
14 
15  internal struct LeafNode : IDisposable {
16  public object Value;
17  public void Dispose() => (Value as IDisposable)?.Dispose();
18  }
19 
20  internal struct BranchNode : IDisposable {
21  public Map Value;
22  public void Dispose() => Value?.Dispose();
23  }
24 
25  internal string Name;
26  internal Node Parent;
27  private LeafNode leafNode;
28  private BranchNode branchNode;
29 
30  internal object Leaf { get => leafNode.Value; set => leafNode.Value = value; }
31  internal Map Branch => branchNode.Value ?? (branchNode.Value = new Map());
32 
33  public static Node New(object name, Node parent) {
34  var node = Cache<Node>.Instance;
35  node.Name = name.ToString();
36  node.Parent = parent;
37  return node;
38  }
39 
40  public int Count => branchNode.Value == null ? 0 : Branch.Count;
41 
42  public void Dispose() {
43  leafNode.Dispose();
44  branchNode.Dispose();
45  leafNode = new LeafNode();
46  branchNode = new BranchNode();
47  Parent = null;
48  Name = null;
49  }
50 
51  public override string ToString() => Name;
52  }
53 
54  private readonly Node root;
55  private Node here;
56  private long integer;
57  private double floatingPoint;
58  private int isNumber;
59 
60  private struct Anchors : IDisposable {
61  public LinkedList<Node> Stack;
62  public Trees Tree;
63 
64  public void Dispose() => Tree.here = Stack.Pop();
65 
66  static Anchors() => LinkedList<Node>.DeactivateItemStatic = (node) => { };
67  }
68 
69  private readonly Anchors anchors;
70 
71  private Trees() {
72  root = here = Node.New("~ROOT~", null);
73  anchors = new Anchors {Stack = new LinkedList<Node>("Trees Anchor Stack"), Tree = this};
74  }
75 
76  private Trees Walk(bool create, string path) {
77  Failed = false;
78  isNumber = -1;
79  var split = path.Split('.');
80 
81  for (var i = 0; i < split.Length; i++) {
82  string key = split[i];
83  if (here == null) return Failure();
84 
85  if (here.Branch[key].Found) {
86  here = here.Branch.Value as Node;
87  } else {
88  if (string.IsNullOrWhiteSpace(key)) {
89  here = i == 0 ? here : here.Parent;
90  } else if (create) {
91  here = (Node) here.Branch.Add(key, Node.New(key, here)).Value;
92  } else {
93  return Failure();
94  }
95  }
96  }
97 
98  return this;
99  }
100 
101  private Trees Failure() {
102  Failed = true;
103  return this;
104  }
105  #endregion
106 
107  #region Public Interface
108  /// <a href="http://bit.ly/2Rj0Qbc">Fetch a cached Trees instance</a>
109  public static Trees Instance => Cache<Trees>.Instance;
110 
111  /// <a href="http://bit.ly/2Oq9u94">Set if last search did not succeed</a>
112  public bool Failed { get; private set; }
113 
114  /// <a href=""></a> //#TBD#//
115  public bool Found => !Failed;
116 
117  /// <a href="http://bit.ly/2RezGSC">Set if here is root</a>
118  public bool IsRoot => here == root;
119 
120  /// <a href="http://bit.ly/2RezGSC">Set here to root</a>
121  public Trees Root() {
122  here = root;
123  return this;
124  }
125 
126  /// <a href="http://bit.ly/2RezJxM">Walk the path, failing if a branch or leaf is unavailable</a>
127  public Trees To(string path) => Root().Walk(create: false, path: path);
128 
129  /// <a href="http://bit.ly/2OuLhhP">See `To`</a>
130  // ReSharper disable once UnusedMethodReturnValue.Global
131  public Trees Next(string path) => Walk(create: false, path: path);
132 
133  /// <a href="http://bit.ly/2Oq9Axs">Walk the path creating new segments as needed</a>
134  // ReSharper disable once UnusedMethodReturnValue.Global
135  public Trees Add(string path) => Walk(create: true, path: path);
136 
137  /// <a href="http://bit.ly/2Oq9u94">Name of node here</a>
138  public string Name => here?.Name;
139 
140  /// <a href="http://bit.ly/2NTfgR0"></a>
141  public object Leaf {
142  get => here.Leaf;
143  set {
144  here.Leaf = value;
145  isNumber = -1;
146  }
147  }
148  /// <a href="http://bit.ly/2NTfgR0">Leaf value here (null if last walk failed)</a>
149  public string Value { get => here?.Leaf?.ToString(); set => here.Leaf = value; }
150 
151  /// <a href="http://bit.ly/2NTfgR0"></a>
152  public bool IsNumber {
153  get {
154  if (isNumber != -1) return isNumber != 0;
155 
156  string word = Value;
157 
158  if (long.TryParse(word, out integer)) { floatingPoint = integer; } else if (double.TryParse(
159  word, out floatingPoint)) { integer = (long) floatingPoint; } else {
160  Failed = true;
161  return (isNumber = 0) == 1;
162  }
163 
164  Failed = false;
165  return (isNumber = 1) == 1;
166  }
167  }
168 
169  /// <a href="http://bit.ly/2NTfgR0">Long value of the leaf here</a>
170  public long Long => IsNumber ? integer : 0;
171 
172  /// <a href="http://bit.ly/2NTfgR0">Double value of the leaf here</a>
173  public double Double => IsNumber ? floatingPoint : 0;
174 
175  /// <a href="http://bit.ly/2NTfgR0">Boolean value of the leaf here</a>
176  public bool Boolean => Value.ToLower()[0] == 't';
177 
178  /// <a href="http://bit.ly/2NTfgR0">The leaf here is a null reference</a>
179  public bool IsNull => (Value == null) || (Value.ToLower() == "null");
180 
181  /// <a href="http://bit.ly/2NTfgR0">Get or set the leaf value of a named branch under here</a>
182  public object this[object key] {
183  get => (here.Branch[key].Value as Node)?.Leaf;
184  set => ((Node) here.Branch[key].Value).Leaf = value;
185  }
186 
187  /// <a href="http://bit.ly/2Oq9u94">List of strings being the names of keys for branches under here</a>
188  public object[] Children => here.Branch.Keys;
189 
190  /// <a href="http://bit.ly/2Oq9u94">First branch under here</a>
191  public object FirstChild => here.Branch.First;
192 
193  /// <a href="http://bit.ly/2Oq9u94">Second branch under here</a>
194  public object NextChild => here.Branch.Next;
195 
196  /// <a href="http://bit.ly/2RezGlA">Dispose of the branch node here</a>
197  public void Dispose() {
198  var parent = here.Parent;
199  string key = here.Name;
200  here.Dispose();
201  here = parent ?? root;
202  here.Branch.Remove(key);
203  }
204 
205  /// <a href="http://bit.ly/2NYOE0W">Mark here so wec an return to it</a>
206  public IDisposable Anchor(string path = "") {
207  anchors.Stack.Push(here);
208  Next(path);
209  return anchors;
210  }
211 
212  /// <inheritdoc />
213  public override string ToString() => Leaf.ToString();
214 
215  /// <a href="http://bit.ly/2Rj0Vf0">The path to here as a string</a>
216  public string Key {
217  get {
218  if (here == null) return "No Path";
219 
220  tsPath.Clear();
221  for (var there = here; there.Parent != null; there = there.Parent) tsPath.Add(there.Name);
222  tsPath.Reverse();
223  return string.Join(separator: ".", values: tsPath);
224  }
225  }
226 
227  private readonly List<string> tsPath = new List<string>();
228  #endregion
229  }
230 }
Definition: Clock.cs:3
Instance caching
Definition: Cache.cs:9
bool IsNumber
Definition: Trees.cs:152
Trees Root()
Set here to root
Definition: Trees.cs:121
bool IsNull
The leaf here is a null reference
Definition: Trees.cs:179
int Count
Number of entries in the map
Definition: Map.cs:77
T Pop()
Retrieve the first list item - Node.Recycle
Definition: LinkedList.cs:324
long Long
Long value of the leaf here
Definition: Trees.cs:170
static Action< Node > DeactivateItemStatic
Called before an item is returned to recycling
Definition: LinkedList.cs:151
A dictionary wrapper
Definition: Map.cs:10
double Double
Double value of the leaf here
Definition: Trees.cs:173
Trees To(string path)
Walk the path, failing if a branch or leaf is unavailable
string Value
Leaf value here (null if last walk failed)
Definition: Trees.cs:149
bool Boolean
Boolean value of the leaf here
Definition: Trees.cs:176
object FirstChild
First branch under here
Definition: Trees.cs:191
object [] Children
List of strings being the names of keys for branches under here
Definition: Trees.cs:188
string Name
Name of node here
Definition: Trees.cs:138
bool IsRoot
Set if here is root
Definition: Trees.cs:118
void Dispose()
Dispose of the branch node here
Definition: Trees.cs:197
Tree Container
Definition: Trees.cs:9
object Leaf
Definition: Trees.cs:141
static Trees Instance
Fetch a cached Trees instance
Definition: Trees.cs:109
string Key
The path to here as a string
Definition: Trees.cs:216
override string ToString()
object NextChild
Second branch under here
Definition: Trees.cs:194
IDisposable Anchor(string path="")
Mark here so wec an return to it
Definition: Trees.cs:206
bool Found
//#TBD#//
Definition: Trees.cs:115
Trees Next(string path)
See To
bool Failed
Set if last search did not succeed
Definition: Trees.cs:112
Trees Add(string path)
Walk the path creating new segments as needed