Add [Exports] from MonoTouch/MonoMac to the documentation index
[mono.git] / mcs / tools / monodoc / Monodoc / index.cs
1 //
2 // index.cs: Handling of the index files
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@xamarin.com)
6 //
7 // (C) 2003 Ximian, Inc.
8 // Copyright 2003-2011 Novell Inc
9 // Copyright 2011 Xamarin Inc.
10 //
11 // Possible file format optimizations:
12 //   * Do not use 4 bytes for each index entry, use 3 bytes
13 //   * Find a way of compressing strings, there are plenty of duplicates
14 //     Find common roots, and use an encoding that uses a root to compress data.
15 //     "System", "System.Data", "System.Data class"
16 //     0: PLAIN: "System"
17 //     1: PLAIN: " class"
18 //     2: LINK0 PLAIN ".DATA"
19 //     3: LINK0 LINK1
20 //     
21 //     Maybe split everything at spaces and dots, and encode that:
22 //     string-1-idx "System."
23 //     string-1-idx "Data"
24 //     2-items [ string-1-idx string-2-idx]
25 //
26 //     Other variations are possible;  Like Archive "System", "System." when we
27 //     see "System.Data".
28 //
29 //
30 namespace Monodoc {
31         
32 using System;
33 using System.IO;
34 using System.Text;
35 using System.Collections;
36
37 public class Topic  {
38         public readonly string Caption;
39         public readonly string SortKey;
40         public readonly string Url;
41
42         public Topic (string caption, string sort_key, string url)
43         {
44                 Caption = caption;
45                 SortKey = sort_key;
46                 Url = url;
47         }
48 }
49
50 public class IndexEntry {
51         public int Position;
52         public object topics;
53         public int Count;
54                 
55         public void Add (Topic t)
56         {
57                 Count++;
58                 if (topics == null)
59                         topics = t;
60                 else {
61                         if (!(topics is ArrayList)){
62                                 Topic temp = (Topic) topics;
63
64                                 topics = new ArrayList ();
65                                 ((ArrayList)topics).Add (temp);
66                         }
67                         ((ArrayList)topics).Add (t);
68                 }
69         }
70
71         public Topic this [int idx] {
72                 get {
73                         if (topics is Topic){
74                                 if (idx == 0)
75                                         return (Topic) topics;
76                                 else
77                                         throw new Exception ("Out of range index");
78                         } else {
79                                 return (Topic) (((ArrayList)topics) [idx]);
80                         }
81                 }
82         }
83
84         //
85         // Constructor from a stream
86         //
87         public IndexEntry (FileStream fs, BinaryReader reader, int position)
88         {
89                 Count = reader.ReadInt32 ();
90                 int caption_offset = reader.ReadInt32 ();
91                 string caption;
92                 
93                 if (Count == 1){
94                         int url_offset = reader.ReadInt32 ();
95                         fs.Position = caption_offset;
96                         caption = reader.ReadString ();
97                         fs.Position = url_offset;
98                         string url = reader.ReadString ();
99                         topics = new Topic (caption, "", url);
100                 } else {
101                         ArrayList l = new ArrayList (Count);
102                         topics = l;
103                         int [] offsets = new int [Count];
104                         for (int i = 0; i < Count; i++){
105                                 offsets [i] = reader.ReadInt32 ();
106                         }
107                         fs.Position = caption_offset;
108                         caption = reader.ReadString ();
109                         for (int i = 0; i < Count; i++){
110                                 fs.Position = offsets [i];
111                                 string url = reader.ReadString ();
112                                 l.Add (new Topic (caption, "", url));
113                         }
114                 }
115         }
116
117 //      Topic ReadTopic (FileStream fs, BinaryReader reader, ref string caption)
118 //      {
119 //              int caption_offset = -1;
120 //              if (caption == null)
121 //                      caption_offset = reader.ReadInt32 ();
122 //              int url_offset = reader.ReadInt32 ();
123 //
124 //              if (caption == null){
125 //                      fs.Position = caption_offset;
126 //                      caption = reader.ReadString ();
127 //              }
128 //              fs.Position = url_offset;
129 //              string url = reader.ReadString ();
130 //
131 //              return new Topic (caption, "", url);
132 //      }
133         
134         //
135         // Regular constructor
136         
137         public IndexEntry ()
138         {
139         }
140
141         public void WriteTopics (IndexMaker maker, Stream stream, BinaryWriter writer)
142         {
143                 //
144                 // Convention: entries with the same SortKey should have the same Caption
145                 //
146                 Position = (int) stream.Position;
147                 writer.Write (Count);
148
149                 if (topics is ArrayList){
150                         bool first = true;
151                         foreach (Topic t in (ArrayList) topics){
152                                 if (first){
153                                         writer.Write (maker.GetCode (t.Caption));
154                                         first = false;
155                                 }
156                                 writer.Write (maker.GetCode (t.Url));
157                         }
158                 } else {
159                         Topic t = (Topic) topics;
160
161                         writer.Write (maker.GetCode (t.Caption));
162                         writer.Write (maker.GetCode (t.Url));
163                 }
164         }
165 }
166
167 public class IndexMaker {
168         Hashtable entries = new Hashtable ();
169         Hashtable all_strings = new Hashtable ();
170
171         void add_string (string s)
172         {
173                 if (all_strings.Contains (s))
174                         return;
175                 all_strings [s] = 0;
176         }
177         
178         public void AddTopic (Topic topic)
179         {
180                 IndexEntry entry = (IndexEntry) entries [topic.SortKey];
181                 if (entry == null){
182                         entry = new IndexEntry ();
183                         entries [topic.SortKey] = entry;
184                 }
185
186                 add_string (topic.SortKey);
187                 add_string (topic.Caption);
188                 add_string (topic.Url);
189                 entry.Add (topic);
190         }
191
192         public void Add (string caption, string sort_key, string url)
193         {
194                 Topic t = new Topic (caption, sort_key, url);
195                 AddTopic (t);
196         }
197         
198         void SaveStringTable (Stream stream, BinaryWriter writer)
199         {
200                 ICollection k = all_strings.Keys;
201                 string [] ks = new string [k.Count];
202                 k.CopyTo (ks, 0);
203                 
204                 foreach (string s in ks){
205                         int pos = (int) stream.Position;
206                         writer.Write (s);
207                         all_strings [s] = pos;
208                 }
209         }
210
211         public int GetCode (string s)
212         {
213                 return (int) all_strings [s];
214         }
215
216         int index_position;
217         
218         void SaveTopics (Stream stream, BinaryWriter writer)
219         {
220                 //
221                 // Convention: entries with the same SortKey should have the same Caption
222                 //
223                 foreach (IndexEntry e in entries.Values)
224                         e.WriteTopics (this, stream, writer);
225         }
226
227         void SaveIndexEntries (Stream stream, BinaryWriter writer)
228         {
229                 index_position = (int) stream.Position;
230                 writer.Write (entries.Count);
231                 ICollection keys = entries.Keys;
232                 string [] keys_name = new string [keys.Count];
233                 keys.CopyTo (keys_name, 0);
234                 Array.Sort (keys_name, new NameSort ());
235                 
236                 foreach (string s in keys_name){
237                         IndexEntry e = (IndexEntry) entries [s];
238                         writer.Write (e.Position);
239                 }
240         }
241
242         class NameSort : IComparer {
243                 public int Compare (object a, object b)
244                 {
245                         string sa = (string) a;
246                         string sb = (string) b;
247
248                         return String.Compare (sa, sb, true);
249                 }
250         }
251         
252         public void Save (string filename)
253         {
254                 Encoding utf8 = new UTF8Encoding (false, true);
255
256                         using (FileStream fs = File.OpenWrite (filename)){
257                                 BinaryWriter writer = 
258                                                 new BinaryWriter (fs, utf8);
259                                 writer.Write (new byte [] { (byte) 'M', 
260                                                 (byte) 'o', (byte) 'i', 
261                                                 (byte) 'x'});
262
263                                 // Leave room for pointer
264                                 fs.Position = 8;
265
266                                 SaveStringTable (fs, writer);
267                                 SaveTopics (fs, writer);
268
269                                 // index_position is set here
270                         
271                                 SaveIndexEntries (fs, writer);
272
273                                 fs.Position = 4;
274                                 writer.Write (index_position);
275                         }
276         }
277 }
278
279 public interface IListModel {
280         int Rows {get; }
281         string GetValue (int row);
282         string GetDescription (int row);
283 }
284
285 public class IndexReader : IListModel {
286         Encoding utf8 = new UTF8Encoding (false, true);
287         FileStream fs;
288         BinaryReader reader;
289
290         // The offset of the table of entries
291         int table_offset;
292         int entries;
293
294         static public IndexReader Load (string filename)
295         {
296                 if (!File.Exists (filename))
297                         return null;
298
299                 try {
300                         return new IndexReader (filename);
301                 } catch {
302                         return null;
303                 }
304         }
305         
306         IndexReader (string filename)
307         {
308                 fs = File.OpenRead (filename);
309                 reader = new BinaryReader (fs, utf8);
310
311                 if (fs.ReadByte () != 'M' ||
312                     fs.ReadByte () != 'o' ||
313                     fs.ReadByte () != 'i' ||
314                     fs.ReadByte () != 'x'){
315                         throw new Exception ("Corrupt index");
316                 }
317
318                 // Seek to index_entries
319                 fs.Position = reader.ReadInt32 ();
320                 
321                 entries = reader.ReadInt32 ();
322
323                 table_offset = (int) fs.Position;
324         }
325
326         public int Rows {
327                 get {
328                         return entries;
329                 }
330         }
331
332         public string GetValue (int row)
333         {
334                 fs.Position = row * 4 + table_offset;
335                 fs.Position = reader.ReadInt32 () + 4;
336                 int code = reader.ReadInt32 ();
337                 fs.Position = code;
338                 string caption = reader.ReadString ();
339
340                 return caption;
341         }
342
343         public string GetDescription (int row)
344         {
345                 return GetValue (row);
346         }
347         
348         public IndexEntry GetIndexEntry (int row)
349         {
350                 fs.Position = row * 4 + table_offset;
351                 int entry_offset = reader.ReadInt32 ();
352                 fs.Position = entry_offset;
353                 
354                 return new IndexEntry (fs, reader, entry_offset);
355         }
356 }
357
358 } // Namespace