[runtime] Disable some tests in full-aot mode which cannot be AOTed because of type...
[mono.git] / mcs / class / monodoc / Monodoc / 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;
7 using System.Collections.Generic;
8
9 namespace Monodoc
10 {
11         public
12 #if LEGACY_MODE
13         partial
14 #endif
15         class Node : IComparable<Node>, IComparable
16         {
17                 readonly Tree parentTree;
18                 string caption, element, pubUrl;
19                 public bool Documented;
20                 bool loaded;
21                 Node parent;
22                 List<Node> nodes;
23 #if LEGACY_MODE
24                 ArrayList legacyNodes;
25 #endif
26                 Dictionary<string, Node> childrenLookup;
27                 bool elementSort;
28                 /* Address has three types of value, 
29                  *   _ 0 is for no on-disk representation
30                  *   _ >0 is a valid address that is loaded immediately
31                  *   _ <0 is a valid negated address to indicate lazy loading
32                  */
33                 int address;
34
35 #if LEGACY_MODE
36                 [Obsolete ("Tree inheriting Node is being phased out. Use the `Tree.RootNode' property instead")]
37                 public Node (string caption, string element)
38                 {
39                         this.parentTree = (Tree) this;
40                         this.caption = caption;
41                         this.element = element;
42                         parent = null;
43                 }
44 #endif
45
46                 public Node (Node parent, string caption, string element) : this (parent.Tree, caption, element)
47                 {
48                         this.parent = parent;
49                 }
50
51                 internal Node (Tree tree, string caption, string element)
52                 {
53                         this.parentTree = tree;
54                         this.caption = caption;
55                         this.element = element;
56                         this.elementSort = parentTree.HelpSource != null && parentTree.HelpSource.SortType == SortType.Element;
57                 }
58         
59                 /// <summary>
60                 ///    Creates a node from an on-disk representation
61                 /// </summary>
62                 internal Node (Node parent, int address) : this (parent.parentTree, address)
63                 {
64                         this.parent = parent;
65                 }
66
67                 internal Node (Tree tree, int address)
68                 {
69                         this.address = address;
70                         this.parentTree = tree;
71                         this.elementSort = parentTree.HelpSource != null && parentTree.HelpSource.SortType == SortType.Element;
72                         if (address > 0)
73                                 LoadNode ();
74                 }
75
76                 /* This is solely used for MatchNode to check for equality */
77                 internal Node ()
78                 {
79                 }
80
81                 void LoadNode ()
82                 {
83                         parentTree.InflateNode (this);
84                         if (parent != null)
85                                 parent.RegisterFullNode (this);
86                 }
87
88                 public void AddNode (Node n)
89                 {
90                         nodes.Add (n);
91                         n.parent = this;
92                         n.Documented = true;
93                         RegisterFullNode (n);
94                 }
95
96                 public void DeleteNode (Node n)
97                 {
98                         nodes.Remove (n);
99                         if (!string.IsNullOrEmpty (n.element))
100                                 childrenLookup.Remove (n.element);
101                 }
102
103                 // When a child node is inflated, it calls this method
104                 // so that we can add it to our lookup for quick search
105                 void RegisterFullNode (Node child)
106                 {
107                         if (childrenLookup == null)
108                                 childrenLookup = new Dictionary<string, Node> ();
109                         if (!string.IsNullOrEmpty (child.element))
110                                 childrenLookup[child.element] = child;
111                 }
112
113                 [Obsolete ("Use ChildNodes")]
114                 public ArrayList Nodes {
115                         get {
116                                 if (legacyNodes == null)
117                                         legacyNodes = new ArrayList (ChildNodes as ICollection);
118                                 return legacyNodes;
119                         }
120                 }
121
122                 public IList<Node> ChildNodes {
123                         get {
124                                 EnsureLoaded ();
125                                 return nodes != null ? nodes : new List<Node> ();
126                         }
127                 }
128
129                 public string Element {
130                         get {
131                                 EnsureLoaded ();
132                                 return element;
133                         }
134                         set {
135                                 element = value;
136                         }
137                 }
138
139                 public string Caption {
140                         get {
141                                 EnsureLoaded ();
142                                 return caption;
143                         }
144                         internal set {
145                                 caption = value;
146                         }
147                 }
148         
149                 public Node Parent {
150                         get {
151                                 return parent;
152                         }
153                 }
154
155                 public Tree Tree {
156                         get {
157                                 return parentTree;
158                         }
159                 }
160
161                 internal int Address {
162                         get {
163                                 return address;
164                         }
165 #if LEGACY_MODE
166                         set {
167                                 address = value;
168                         }
169 #endif
170                 }
171         
172                 /// <summary>
173                 ///   Creates a new node, in the locator entry point, and with
174                 ///   a user visible caption of @caption
175                 /// </summary>
176                 public Node CreateNode (string c_caption, string c_element)
177                 {
178                         EnsureNodes ();
179                         if (string.IsNullOrEmpty (c_caption))
180                                 throw new ArgumentNullException ("c_caption");
181                         if (string.IsNullOrEmpty (c_element))
182                                 throw new ArgumentNullException ("c_element");
183
184                         Node t = new Node (this, c_caption, c_element);
185                         nodes.Add (t);
186                         childrenLookup[c_element] = t;
187
188                         return t;
189                 }
190
191                 public Node GetOrCreateNode (string c_caption, string c_element)
192                 {
193                         if (nodes == null)
194                                 return CreateNode (c_caption, c_element);
195                         if (childrenLookup.Count != nodes.Count || (nodes.Count == 0 && childrenLookup.Count != nodes.Capacity))
196                                 UpdateLookup ();
197
198                         Node result;
199                         if (!childrenLookup.TryGetValue (c_element, out result))
200                                 result = CreateNode (c_caption, c_element);
201                         return result;
202                 }
203
204                 public void EnsureNodes ()
205                 {
206                         if (nodes == null) {
207                                 nodes = new List<Node> ();
208                                 childrenLookup = new Dictionary<string, Node> ();
209                         }
210                 }
211
212                 public void EnsureLoaded ()
213                 {
214                         if (address < 0 && !loaded) {
215                                 LoadNode ();
216                                 loaded = true;
217                         }
218                 }
219
220                 void UpdateLookup ()
221                 {
222                         foreach (var node in nodes)
223                                 childrenLookup[node.Element] = node;
224                 }
225         
226                 public bool IsLeaf {
227                         get {
228                                 return nodes == null || nodes.Count == 0;
229                         }
230                 }
231
232                 void EncodeInt (BinaryWriter writer, int value)
233                 {
234                         do {
235                                 int high = (value >> 7) & 0x01ffffff;
236                                 byte b = (byte)(value & 0x7f);
237
238                                 if (high != 0) {
239                                         b = (byte)(b | 0x80);
240                                 }
241                         
242                                 writer.Write(b);
243                                 value = high;
244                         } while(value != 0);
245                 }
246
247                 int DecodeInt (BinaryReader reader)
248                 {
249                         int ret = 0;
250                         int shift = 0;
251                         byte b;
252                 
253                         do {
254                                 b = reader.ReadByte();
255
256                                 ret = ret | ((b & 0x7f) << shift);
257                                 shift += 7;
258                         } while ((b & 0x80) == 0x80);
259                         
260                         return ret;
261                 }
262
263                 internal void Deserialize (BinaryReader reader)
264                 {
265                         int count = DecodeInt (reader);
266                         element = reader.ReadString ();
267                         caption = reader.ReadString ();
268
269                         if (count == 0)
270                                 return;
271                 
272                         nodes = new List<Node> (count);
273                         for (int i = 0; i < count; i++) {
274                                 int child_address = DecodeInt (reader);
275
276                                 Node t = new Node (this, -child_address);
277                                 nodes.Add (t);
278                         }
279
280                         if (parentTree.ForceResort)
281                                 nodes.Sort ();
282                 }
283
284                 internal void Serialize (FileStream output, BinaryWriter writer)
285                 {
286                         if (nodes != null)
287                                 foreach (Node child in nodes)
288                                         child.Serialize (output, writer);
289
290                         address = (int) output.Position;
291                         EncodeInt (writer, nodes == null ? 0 : (int) nodes.Count);
292                         writer.Write (element);
293                         writer.Write (caption);
294
295                         if (nodes != null)
296                                 foreach (Node child in nodes)
297                                         EncodeInt (writer, child.address);
298                 }
299
300                 public void Sort ()
301                 {
302                         if (nodes != null)
303                                 nodes.Sort ();
304                 }
305
306                 internal string GetInternalUrl ()
307                 {
308                         EnsureLoaded ();
309                         if (element.IndexOf (":") != -1 || parent == null)
310                                 return element;
311
312                         var parentUrl = parent.GetInternalUrl ();
313                         return parentUrl.EndsWith ("/") ? parentUrl + element : parentUrl + "/" + element;
314                 }
315                 
316                 public string PublicUrl {
317                         get {
318                                 if (pubUrl != null)
319                                         return pubUrl;
320                                 return pubUrl = parentTree.HelpSource != null ? parentTree.HelpSource.GetPublicUrl (this) : GetInternalUrl ();
321                         }
322                 }
323
324                 int IComparable.CompareTo (object obj)
325                 {
326                         Node other = obj as Node;
327                         if (other == null)
328                                 return -1;
329                         return CompareToInternal (other);
330                 }
331
332                 int IComparable<Node>.CompareTo (Node obj)
333                 {
334                         return CompareToInternal (obj);
335                 }
336
337                 int CompareToInternal (Node other)
338                 {
339                         EnsureLoaded ();
340                         other.EnsureLoaded ();
341
342                         var cap1 = elementSort ? element : caption;
343                         var cap2 = elementSort ? other.element : other.caption;
344
345                         /* Some node (notably from ecmaspec) have number prepended to them
346                          * which we need to sort better by padding them to the same number
347                          * of digits
348                          */
349                         if (char.IsDigit (cap1[0]) && char.IsDigit (cap2[0])) {
350                                 int c1 = cap1.TakeWhile (char.IsDigit).Count ();
351                                 int c2 = cap2.TakeWhile (char.IsDigit).Count ();
352                                 
353                                 if (c1 != c2) {
354                                         cap1 = cap1.PadLeft (cap1.Length + Math.Max (0, c2 - c1), '0');
355                                         cap2 = cap2.PadLeft (cap2.Length + Math.Max (0, c1 - c2), '0');
356                                 }
357                         }
358
359                         return string.Compare (cap1, cap2, StringComparison.Ordinal);
360                 }
361         }
362
363         internal static class IListExtensions
364         {
365                 // TODO: if the backing store ever change from List<T>, we need to tune these methods to have a fallback mechanism
366                 public static int BinarySearch<T> (this IList<T> ilist, T item)
367                 {
368                         var list = ilist as List<T>;
369                         if (list == null)
370                                 throw new NotSupportedException ();
371                         return list.BinarySearch (item);
372                 }
373
374                 public static int BinarySearch<T> (this IList<T> ilist, T item, IComparer<T> comparer)
375                 {
376                         var list = ilist as List<T>;
377                         if (list == null)
378                                 throw new NotSupportedException ();
379                         return list.BinarySearch (item, comparer);
380                 }
381         }
382 }