6 using System.Collections.Generic;
14 class Node : IComparable<Node>, IComparable
16 readonly Tree parentTree;
17 string caption, element, pubUrl;
18 public bool Documented;
22 Dictionary<string, Node> childrenLookup;
24 /* Address has three types of value,
25 * _ 0 is for no on-disk representation
26 * _ >0 is a valid address that is loaded immediately
27 * _ <0 is a valid negated address to indicate lazy loading
31 public Node (Node parent, string caption, string element) : this (parent.Tree, caption, element)
36 internal Node (Tree tree, string caption, string element)
38 this.parentTree = tree;
39 this.caption = caption;
40 this.element = element;
41 this.elementSort = parentTree.HelpSource != null && parentTree.HelpSource.SortType == SortType.Element;
45 /// Creates a node from an on-disk representation
47 internal Node (Node parent, int address) : this (parent.parentTree, address)
52 internal Node (Tree tree, int address)
54 this.address = address;
55 this.parentTree = tree;
56 this.elementSort = parentTree.HelpSource != null && parentTree.HelpSource.SortType == SortType.Element;
61 /* This is solely used for MatchNode to check for equality */
68 parentTree.InflateNode (this);
70 parent.RegisterFullNode (this);
73 public void AddNode (Node n)
81 public void DeleteNode (Node n)
84 if (!string.IsNullOrEmpty (n.element))
85 childrenLookup.Remove (n.element);
88 // When a child node is inflated, it calls this method
89 // so that we can add it to our lookup for quick search
90 void RegisterFullNode (Node child)
92 if (childrenLookup == null)
93 childrenLookup = new Dictionary<string, Node> ();
94 if (!string.IsNullOrEmpty (child.element))
95 childrenLookup[child.element] = child;
98 public List<Node> Nodes {
101 return nodes != null ? nodes : new List<Node> ();
105 public string Element {
115 public string Caption {
137 internal int Address {
144 /// Creates a new node, in the locator entry point, and with
145 /// a user visible caption of @caption
147 public Node CreateNode (string c_caption, string c_element)
150 if (string.IsNullOrEmpty (c_caption))
151 throw new ArgumentNullException ("c_caption");
152 if (string.IsNullOrEmpty (c_element))
153 throw new ArgumentNullException ("c_element");
155 Node t = new Node (this, c_caption, c_element);
157 childrenLookup[c_element] = t;
162 public Node GetOrCreateNode (string c_caption, string c_element)
165 return CreateNode (c_caption, c_element);
166 if (childrenLookup.Count != nodes.Count || (nodes.Count == 0 && childrenLookup.Count != nodes.Capacity))
170 if (!childrenLookup.TryGetValue (c_element, out result))
171 result = CreateNode (c_caption, c_element);
175 public void EnsureNodes ()
178 nodes = new List<Node> ();
179 childrenLookup = new Dictionary<string, Node> ();
183 public void EnsureLoaded ()
185 if (address < 0 && !loaded) {
193 foreach (var node in nodes)
194 childrenLookup[node.Element] = node;
199 return nodes == null || nodes.Count == 0;
203 void EncodeInt (BinaryWriter writer, int value)
206 int high = (value >> 7) & 0x01ffffff;
207 byte b = (byte)(value & 0x7f);
210 b = (byte)(b | 0x80);
218 int DecodeInt (BinaryReader reader)
225 b = reader.ReadByte();
227 ret = ret | ((b & 0x7f) << shift);
229 } while ((b & 0x80) == 0x80);
234 internal void Deserialize (BinaryReader reader)
236 int count = DecodeInt (reader);
237 element = reader.ReadString ();
238 caption = reader.ReadString ();
243 nodes = new List<Node> (count);
244 for (int i = 0; i < count; i++) {
245 int child_address = DecodeInt (reader);
247 Node t = new Node (this, -child_address);
251 if (parentTree.ForceResort)
255 internal void Serialize (FileStream output, BinaryWriter writer)
258 foreach (Node child in nodes)
259 child.Serialize (output, writer);
261 address = (int) output.Position;
262 EncodeInt (writer, nodes == null ? 0 : (int) nodes.Count);
263 writer.Write (element);
264 writer.Write (caption);
267 foreach (Node child in nodes)
268 EncodeInt (writer, child.address);
277 internal string GetInternalUrl ()
280 if (element.IndexOf (":") != -1 || parent == null)
283 var parentUrl = parent.GetInternalUrl ();
284 return parentUrl.EndsWith ("/") ? parentUrl + element : parentUrl + "/" + element;
287 public string PublicUrl {
291 return pubUrl = parentTree.HelpSource != null ? parentTree.HelpSource.GetPublicUrl (this) : GetInternalUrl ();
295 int IComparable.CompareTo (object obj)
297 Node other = obj as Node;
300 return CompareToInternal (other);
303 int IComparable<Node>.CompareTo (Node obj)
305 return CompareToInternal (obj);
308 int CompareToInternal (Node other)
311 other.EnsureLoaded ();
313 var cap1 = elementSort ? element : caption;
314 var cap2 = elementSort ? other.element : other.caption;
316 /* Some node (notably from ecmaspec) have number prepended to them
317 * which we need to sort better by padding them to the same number
320 if (char.IsDigit (cap1[0]) && char.IsDigit (cap2[0])) {
321 int c1 = cap1.TakeWhile (char.IsDigit).Count ();
322 int c2 = cap2.TakeWhile (char.IsDigit).Count ();
325 cap1 = cap1.PadLeft (cap1.Length + Math.Max (0, c2 - c1), '0');
326 cap2 = cap2.PadLeft (cap2.Length + Math.Max (0, c1 - c2), '0');
330 return string.Compare (cap1, cap2, StringComparison.Ordinal);