[monkeydoc] When encountering an old tree, we force nodes to be re-sorted as they...
[mono.git] / mcs / tools / monkeydoc / MonkeyDoc / Tree.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 MonkeyDoc
9 {
10         /// <summary>
11         ///    This tree is populated by the documentation providers, or populated
12         ///    from a binary encoding of the tree.  The format of the tree is designed
13         ///    to minimize the need to load it in full.
14         /// </summary>
15
16         /* Ideally this class should also be abstracted to let user have something
17          * else than a file as a backing store, a database for instance
18          */
19         public class Tree
20         {
21                 const long CurrentVersionNumber = 1;
22                 public readonly HelpSource HelpSource;
23         
24                 FileStream InputStream;
25                 BinaryReader InputReader;
26
27                 // This is the node which contains all the other node of the tree
28                 Node rootNode;
29
30                 /// <summary>
31                 ///   Load from file constructor
32                 /// </summary>
33                 public Tree (HelpSource hs, string filename)
34                 {
35                         HelpSource = hs;
36                         Encoding utf8 = new UTF8Encoding (false, true);
37
38                         if (!File.Exists (filename)){
39                                 throw new FileNotFoundException ();
40                         }
41                 
42                         InputStream = File.OpenRead (filename);
43                         InputReader = new BinaryReader (InputStream, utf8);
44                         byte [] sig = InputReader.ReadBytes (4);
45                 
46                         if (!GoodSig (sig))
47                                 throw new Exception ("Invalid file format");
48                 
49                         InputStream.Position = 4;
50                         // Try to read version information
51                         if (InputReader.ReadInt32 () == -(int)'v')
52                                 VersionNumber = InputReader.ReadInt64 ();
53                         else
54                                 InputStream.Position -= 4;
55
56                         var position = InputReader.ReadInt32 ();
57                         rootNode = new Node (this, position);
58                         InflateNode (rootNode);
59                 }
60
61                 /// <summary>
62                 ///    Tree creation and merged tree constructor
63                 /// </summary>
64                 public Tree (HelpSource hs, string caption, string url) : this (hs, null, caption, url)
65                 {
66                 }
67
68                 public Tree (HelpSource hs, Node parent, string caption, string element)
69                 {
70                         HelpSource = hs;
71                         rootNode = parent == null ? new Node (this, caption, element) : new Node (parent, caption, element);
72                 }
73
74                 /// <summary>
75                 ///    Saves the tree into the specified file using the help file format.
76                 /// </summary>
77                 public void Save (string file)
78                 {
79                         Encoding utf8 = new UTF8Encoding (false, true);
80                         using (FileStream output = File.OpenWrite (file)){
81                                 // Skip over the pointer to the first node.
82                                 output.Position = 4 + 4 + 8 + 4;
83                         
84                                 using (BinaryWriter writer = new BinaryWriter (output, utf8)) {
85                                         // Recursively dump
86                                         rootNode.Serialize (output, writer);
87
88                                         output.Position = 0;
89                                         writer.Write (new byte [] { (byte) 'M', (byte) 'o', (byte) 'H', (byte) 'P' });
90                                         writer.Write (-(int)'v');
91                                         writer.Write (CurrentVersionNumber);
92                                         writer.Write (rootNode.Address);
93                                 }
94                         }
95                 }
96
97                 public Node RootNode {
98                         get {
99                                 return rootNode;
100                         }
101                 }
102
103                 public long VersionNumber {
104                         get;
105                         private set;
106                 }
107
108                 static bool GoodSig (byte [] sig)
109                 {
110                         if (sig.Length != 4)
111                                 return false;
112                         return sig [0] == (byte) 'M'
113                                 && sig [1] == (byte) 'o'
114                             && sig [2] == (byte) 'H'
115                                 && sig [3] == (byte) 'P';
116                 }
117
118                 public void InflateNode (Node baseNode)
119                 {
120                         var address = baseNode.Address;
121                         if (address < 0)
122                                 address = -address;
123
124                         InputStream.Position = address;
125                         baseNode.Deserialize (InputReader);
126                 }
127
128                 // Nodes use this value to know if they should manually re-sort their child
129                 // if they come from an older generator version
130                 internal bool ForceResort {
131                         get {
132                                 return VersionNumber == 0;
133                         }
134                 }
135         }
136
137         public static class TreeDumper
138         {
139                 static int indent;
140
141                 static void Indent ()
142                 {
143                         for (int i = 0; i < indent; i++)
144                                 Console.Write ("   ");
145                 }
146         
147                 public static void PrintTree (Node node)
148                 {
149                         Indent ();
150                         Console.WriteLine ("{0},{1}\t[PublicUrl: {2}]", node.Element, node.Caption, node.PublicUrl);
151                         if (node.Nodes.Count == 0)
152                                 return;
153
154                         indent++;
155                         foreach (Node n in node.Nodes)
156                                 PrintTree (n);
157                         indent--;
158                 }
159
160                 public static string ExportToTocXml (Node root, string title, string desc)
161                 {
162                         if (root == null)
163                                 throw new ArgumentNullException ("root");
164                         // Return a toc index of sub-nodes
165                         StringBuilder buf = new StringBuilder ();
166                         var writer = XmlWriter.Create (buf);
167                         writer.WriteStartElement ("toc");
168                         writer.WriteAttributeString ("title", title ?? string.Empty);
169                         writer.WriteElementString ("description", desc ?? string.Empty);
170                         writer.WriteStartElement ("list");
171                         foreach (Node n in root.Nodes) {
172                                 writer.WriteStartElement ("item");
173                                 writer.WriteAttributeString ("url", n.Element);
174                                 writer.WriteValue (n.Caption);
175                                 writer.WriteEndElement ();
176                         }
177                         writer.WriteEndElement ();
178                         writer.WriteEndElement ();
179                         writer.Flush ();
180                         writer.Close ();
181
182                         return buf.ToString ();
183                 }
184         }
185 }