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