[monkeydoc] Change namespace base from MonkeyDoc to Monodoc.
[mono.git] / mcs / tools / monkeydoc / MonkeyDoc / Node.cs
1 using System;
2 using System.IO;
3 using System.Text;
4 using System.Linq;
5 using System.Xml;
6 using System.Collections.Generic;
7
8 namespace Monodoc
9 {
10         public
11 #if LEGACY_MODE
12         partial
13 #endif
14         class Node : IComparable<Node>, IComparable
15         {
16                 readonly Tree parentTree;
17                 string caption, element, pubUrl;
18                 public bool Documented;
19                 bool loaded;
20                 Node parent;
21                 List<Node> nodes;
22                 Dictionary<string, Node> childrenLookup;
23                 bool elementSort;
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
28                  */
29                 int address;
30
31                 public Node (Node parent, string caption, string element) : this (parent.Tree, caption, element)
32                 {
33                         this.parent = parent;
34                 }
35
36                 internal Node (Tree tree, string caption, string element)
37                 {
38                         this.parentTree = tree;
39                         this.caption = caption;
40                         this.element = element;
41                         this.elementSort = parentTree.HelpSource != null && parentTree.HelpSource.SortType == SortType.Element;
42                 }
43         
44                 /// <summary>
45                 ///    Creates a node from an on-disk representation
46                 /// </summary>
47                 internal Node (Node parent, int address) : this (parent.parentTree, address)
48                 {
49                         this.parent = parent;
50                 }
51
52                 internal Node (Tree tree, int address)
53                 {
54                         this.address = address;
55                         this.parentTree = tree;
56                         this.elementSort = parentTree.HelpSource != null && parentTree.HelpSource.SortType == SortType.Element;
57                         if (address > 0)
58                                 LoadNode ();
59                 }
60
61                 /* This is solely used for MatchNode to check for equality */
62                 internal Node ()
63                 {
64                 }
65
66                 void LoadNode ()
67                 {
68                         parentTree.InflateNode (this);
69                         if (parent != null)
70                                 parent.RegisterFullNode (this);
71                 }
72
73                 public void AddNode (Node n)
74                 {
75                         nodes.Add (n);
76                         n.parent = this;
77                         n.Documented = true;
78                         RegisterFullNode (n);
79                 }
80
81                 public void DeleteNode (Node n)
82                 {
83                         nodes.Remove (n);
84                         if (!string.IsNullOrEmpty (n.element))
85                                 childrenLookup.Remove (n.element);
86                 }
87
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)
91                 {
92                         if (childrenLookup == null)
93                                 childrenLookup = new Dictionary<string, Node> ();
94                         if (!string.IsNullOrEmpty (child.element))
95                                 childrenLookup[child.element] = child;
96                 }
97
98                 public List<Node> Nodes {
99                         get {
100                                 EnsureLoaded ();
101                                 return nodes != null ? nodes : new List<Node> ();
102                         }
103                 }
104
105                 public string Element {
106                         get {
107                                 EnsureLoaded ();
108                                 return element;
109                         }
110                         set {
111                                 element = value;
112                         }
113                 }
114
115                 public string Caption {
116                         get {
117                                 EnsureLoaded ();
118                                 return caption;
119                         }
120                         internal set {
121                                 caption = value;
122                         }
123                 }
124         
125                 public Node Parent {
126                         get {
127                                 return parent;
128                         }
129                 }
130
131                 public Tree Tree {
132                         get {
133                                 return parentTree;
134                         }
135                 }
136
137                 internal int Address {
138                         get {
139                                 return address;
140                         }
141                 }
142         
143                 /// <summary>
144                 ///   Creates a new node, in the locator entry point, and with
145                 ///   a user visible caption of @caption
146                 /// </summary>
147                 public Node CreateNode (string c_caption, string c_element)
148                 {
149                         EnsureNodes ();
150                         if (string.IsNullOrEmpty (c_caption))
151                                 throw new ArgumentNullException ("c_caption");
152                         if (string.IsNullOrEmpty (c_element))
153                                 throw new ArgumentNullException ("c_element");
154
155                         Node t = new Node (this, c_caption, c_element);
156                         nodes.Add (t);
157                         childrenLookup[c_element] = t;
158
159                         return t;
160                 }
161
162                 public Node GetOrCreateNode (string c_caption, string c_element)
163                 {
164                         if (nodes == null)
165                                 return CreateNode (c_caption, c_element);
166                         if (childrenLookup.Count != nodes.Count || (nodes.Count == 0 && childrenLookup.Count != nodes.Capacity))
167                                 UpdateLookup ();
168
169                         Node result;
170                         if (!childrenLookup.TryGetValue (c_element, out result))
171                                 result = CreateNode (c_caption, c_element);
172                         return result;
173                 }
174
175                 public void EnsureNodes ()
176                 {
177                         if (nodes == null) {
178                                 nodes = new List<Node> ();
179                                 childrenLookup = new Dictionary<string, Node> ();
180                         }
181                 }
182
183                 public void EnsureLoaded ()
184                 {
185                         if (address < 0 && !loaded) {
186                                 LoadNode ();
187                                 loaded = true;
188                         }
189                 }
190
191                 void UpdateLookup ()
192                 {
193                         foreach (var node in nodes)
194                                 childrenLookup[node.Element] = node;
195                 }
196         
197                 public bool IsLeaf {
198                         get {
199                                 return nodes == null || nodes.Count == 0;
200                         }
201                 }
202
203                 void EncodeInt (BinaryWriter writer, int value)
204                 {
205                         do {
206                                 int high = (value >> 7) & 0x01ffffff;
207                                 byte b = (byte)(value & 0x7f);
208
209                                 if (high != 0) {
210                                         b = (byte)(b | 0x80);
211                                 }
212                         
213                                 writer.Write(b);
214                                 value = high;
215                         } while(value != 0);
216                 }
217
218                 int DecodeInt (BinaryReader reader)
219                 {
220                         int ret = 0;
221                         int shift = 0;
222                         byte b;
223                 
224                         do {
225                                 b = reader.ReadByte();
226
227                                 ret = ret | ((b & 0x7f) << shift);
228                                 shift += 7;
229                         } while ((b & 0x80) == 0x80);
230                         
231                         return ret;
232                 }
233
234                 internal void Deserialize (BinaryReader reader)
235                 {
236                         int count = DecodeInt (reader);
237                         element = reader.ReadString ();
238                         caption = reader.ReadString ();
239
240                         if (count == 0)
241                                 return;
242                 
243                         nodes = new List<Node> (count);
244                         for (int i = 0; i < count; i++) {
245                                 int child_address = DecodeInt (reader);
246
247                                 Node t = new Node (this, -child_address);
248                                 nodes.Add (t);
249                         }
250
251                         if (parentTree.ForceResort)
252                                 nodes.Sort ();
253                 }
254
255                 internal void Serialize (FileStream output, BinaryWriter writer)
256                 {
257                         if (nodes != null)
258                                 foreach (Node child in nodes)
259                                         child.Serialize (output, writer);
260
261                         address = (int) output.Position;
262                         EncodeInt (writer, nodes == null ? 0 : (int) nodes.Count);
263                         writer.Write (element);
264                         writer.Write (caption);
265
266                         if (nodes != null)
267                                 foreach (Node child in nodes)
268                                         EncodeInt (writer, child.address);
269                 }
270
271                 public void Sort ()
272                 {
273                         if (nodes != null)
274                                 nodes.Sort ();
275                 }
276
277                 internal string GetInternalUrl ()
278                 {
279                         EnsureLoaded ();
280                         if (element.IndexOf (":") != -1 || parent == null)
281                                 return element;
282
283                         var parentUrl = parent.GetInternalUrl ();
284                         return parentUrl.EndsWith ("/") ? parentUrl + element : parentUrl + "/" + element;
285                 }
286                 
287                 public string PublicUrl {
288                         get {
289                                 if (pubUrl != null)
290                                         return pubUrl;
291                                 return pubUrl = parentTree.HelpSource != null ? parentTree.HelpSource.GetPublicUrl (this) : GetInternalUrl ();
292                         }
293                 }
294
295                 int IComparable.CompareTo (object obj)
296                 {
297                         Node other = obj as Node;
298                         if (other == null)
299                                 return -1;
300                         return CompareToInternal (other);
301                 }
302
303                 int IComparable<Node>.CompareTo (Node obj)
304                 {
305                         return CompareToInternal (obj);
306                 }
307
308                 int CompareToInternal (Node other)
309                 {
310                         EnsureLoaded ();
311                         other.EnsureLoaded ();
312
313                         var cap1 = elementSort ? element : caption;
314                         var cap2 = elementSort ? other.element : other.caption;
315
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
318                          * of digits
319                          */
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 ();
323                                 
324                                 if (c1 != c2) {
325                                         cap1 = cap1.PadLeft (cap1.Length + Math.Max (0, c2 - c1), '0');
326                                         cap2 = cap2.PadLeft (cap2.Length + Math.Max (0, c1 - c2), '0');
327                                 }
328                         }
329
330                         return string.Compare (cap1, cap2, StringComparison.Ordinal);
331                 }
332         }
333 }