Prepping for monodoc move from monodoc/engine to mcs/class/monodoc...
[mono.git] / mcs / class / monodoc / wiki2ecma.cs
1 using System;
2 using System.Globalization;
3 using System.IO;
4 using System.Text;
5 using System.Xml;
6 using System.Xml.XPath;
7 using System.Xml.Xsl;
8
9 namespace Monodoc
10 {
11         public class WikiStyleDocConverter
12         {
13                 public static void Main (string [] args)
14                 {
15                         if (args.Length < 1) {
16                                 Console.Error.WriteLine ("usage: wiki2ecma sourcefile [--full]");
17                                 return;
18                         }
19
20                         bool full = args.Length > 1 && args [1] == "--full";
21
22                         bool isXml = false;
23                         using (Stream s = File.OpenRead (args [0])) {
24                                 isXml = (s.ReadByte () == '<');
25                         }
26
27                         string text;
28                         if (isXml) {
29                                 XmlDocument doc = new XmlDocument ();
30                                 doc.Load (args [0]);
31                                 XmlNode node = doc.SelectSingleNode ("//text");
32                                 text = node.InnerText;
33                         } else {
34                                 StreamReader sr = new StreamReader (args [0], 
35                                         Encoding.UTF8);
36                                 text = sr.ReadToEnd ();
37                         }
38
39                         // Pass the input Wiki-like content as the .ctor()
40                         // parameter.
41                         WikiStyleDocConverter p = new WikiStyleDocConverter (text);
42                         XmlNode result;
43                         if (full)
44                                 result = p.ParseEntireDoc ();
45                         else
46                                 result = p.ParseContent ();
47
48                         XmlTextWriter xw = new XmlTextWriter (Console.Out);
49                         xw.Formatting = Formatting.Indented;
50                         result.WriteTo (xw);
51                         xw.Close ();
52                 }
53
54                 string [] lines;
55                 int lineno = 0;
56                 XmlDocument doc;
57                 string current_member;
58
59                 public WikiStyleDocConverter (string source)
60                 {
61                         source = TransformElements (source);
62
63                         lines = source.Split ('\n');
64                         doc = new XmlDocument ();
65                         doc.AppendChild (doc.CreateElement ("root"));
66                 }
67
68                 private string TransformElements (string source)
69                 {
70                         XPathDocument doc = new XPathDocument (
71                                 new StringReader ("<root>" + source + "</root>"));
72                         XslTransform tr = new XslTransform ();
73                         tr.Load ("wiki2ecmahelper.xsl");
74                         XmlReader reader = tr.Transform (doc, null, (XmlResolver) null);
75                         reader.Read (); // should consume <root> start tag
76
77                         return reader.ReadInnerXml ();
78                 }
79
80                 public XmlNode ParseContent ()
81                 {
82                         ProcessContent (doc.DocumentElement);
83                         return doc.DocumentElement;
84                 }
85
86                 public XmlNode ParseEntireDoc ()
87                 {
88                         XmlElement el = doc.DocumentElement;
89
90                         while (lineno < lines.Length) {
91                                 string line = lines [lineno].Trim ();
92                                 if (line.Length == 0) {
93                                         lineno++;
94                                         continue;
95                                 }
96                                 XmlNode node = null;
97                                 switch (line) {
98                                 case "=== Summary ===":
99                                         node = ProcessTaggedContent (EditTarget.Summary);
100                                         el.AppendChild (node);
101                                         break;
102                                 case "=== Remarks ===":
103                                         node = ProcessTaggedContent (EditTarget.Remarks);
104                                         el.AppendChild (node);
105                                         break;
106                                 case "=== Parameters ===":
107                                         ProcessList (el, "param", "name");
108                                         break;
109                                 case "=== Exceptions ===":
110                                         ProcessList (el, "exception", "type");
111                                         break;
112                                 default:
113                                         if (StrUtil.StartsWith (line, "==")) {
114                                                 current_member = line.Substring (
115                                                         3, line.Length - 6).Trim ();
116                                                 el = doc.CreateElement ("Member");
117                                                 el.SetAttribute ("MemberName", current_member);
118                                                 doc.DocumentElement.AppendChild (el);
119                                                 lineno++;
120                                                 break;
121                                         }
122                                         throw MarkupError ("Unexpected line format: " + line);
123                                 }
124                         }
125                         return doc.DocumentElement;
126                 }
127
128                 void ProcessList (XmlNode parent, string elemName, string defAttr)
129                 {
130                         lineno++;
131                         for (; lineno < lines.Length; lineno++) {
132                                 string line = lines [lineno];
133                                 if (line.Length == 0)
134                                         continue;
135                                 if (line [0] != ';')
136                                         break;
137                                 int idx = line.IndexOf (':');
138                                 XmlElement el = doc.CreateElement (elemName);
139                                 parent.AppendChild (el);
140                                 el.SetAttribute (defAttr, line.Substring (1, idx - 1));
141                                 ProcessSimpleLine (el, line, idx + 1);
142                         }
143                 }
144
145                 XmlNode ProcessTaggedContent (string target)
146                 {
147                         XmlElement el = doc.CreateElement (target);
148                         lineno++;
149                         ProcessContent (el);
150                         return el;
151                 }
152
153                 void ProcessContent (XmlNode container)
154                 {
155                         while (lineno < lines.Length) {
156                                 string line = lines [lineno];
157                                 if (line.Length == 0) {
158                                         lineno++;
159                                         continue;
160                                 }
161
162                                 switch (line [0]) {
163                                 case '=':
164                                         return;
165                                 case '{':
166                                         ProcessTable (container);
167                                         break;
168                                 case ':':
169                                         XmlElement el = doc.CreateElement ("block");
170                                         el.SetAttribute ("subset", "none");
171                                         el.SetAttribute ("type", "note");
172                                         container.AppendChild (el);
173                                         ProcessSimple (el, true);
174                                         break;
175                                 default:
176                                         el = doc.CreateElement ("para");
177                                         container.AppendChild (el);
178                                         ProcessSimple (el, false);
179                                         break;
180                                 }
181                         }
182                 }
183
184                 void ProcessTable (XmlNode container)
185                 {
186                         lineno++;
187                         XmlElement list = doc.CreateElement ("list");
188                         container.AppendChild (list);
189                         list.SetAttribute ("type", "table");
190                         XmlElement tline = null;
191                         for (; lineno < lines.Length; lineno++) {
192                                 string line = lines [lineno];
193                                 if (line == "|}") {
194                                         lineno++;
195                                         return;
196                                 }
197
198                                 if (line.Length == 0)
199                                         continue;
200                                 if (line == "|-") {
201                                         tline = doc.CreateElement ("item");
202                                         continue;
203                                 }
204                                 switch (line [0]) {
205                                 case '!':
206                                         tline = doc.CreateElement ("listheader");
207                                         int endTerm = line.IndexOf ('!', 1);
208                                         int beginDesc = endTerm < 0 ? -1 : line.IndexOf ('!', endTerm + 1);
209                                         if (beginDesc < 0)
210                                                 throw MarkupError ("list table header has incorrect markup : " + line);
211                                         XmlElement term = doc.CreateElement ("term");
212                                         term.InnerText = line.Substring (1, endTerm - 1);
213                                         tline.AppendChild (term);
214                                         XmlElement desc = doc.CreateElement ("description");
215                                         desc.InnerText = line.Substring (beginDesc + 1);
216                                         tline.AppendChild (desc);
217                                         list.AppendChild (tline);
218                                         break;
219                                 case '|':
220                                         if (tline == null)
221                                                 throw MarkupError ("Specify '|-' to begin new table line");
222                                         endTerm = line.IndexOf ('|', 1);
223                                         if (endTerm < 0)
224                                                 throw MarkupError ("Missing list table separator '|'");
225                                         beginDesc = endTerm < 0 ? -1 : line.IndexOf ('|', endTerm + 1);
226                                         term = doc.CreateElement ("term");
227                                         term.InnerText = line.Substring (1, endTerm - 1);
228                                         tline.AppendChild (term);
229                                         desc = doc.CreateElement ("description");
230                                         ProcessSimpleLine (desc, line, beginDesc + 1);
231                                         tline.AppendChild (desc);
232                                         list.AppendChild (tline);
233                                         break;
234                                 }
235                                 tline = null;
236                         }
237                         // there is already "return" statement above.
238                         throw MarkupError ("End of list table is missing");
239                 }
240
241                 void ProcessSimple (XmlNode container, bool allowColon)
242                 {
243                         for (;lineno < lines.Length; lineno++) {
244                                 string line = lines [lineno];
245                                 if (line.Length == 0) {
246                                         if (lineno + 1 < lines.Length &&
247                                             lines [lineno + 1] == String.Empty) {
248                                                 lineno++;
249                                                 return;
250                                         }
251                                         continue;
252                                 }
253                                 switch (line [0]) {
254                                 case '=':
255                                 case '{':
256                                         return;
257                                 case ':':
258                                         if (!allowColon)
259                                                 return;
260                                         ProcessSimpleLine (container, line, 1);
261                                         break;
262                                 default:
263                                         ProcessSimpleLine (container, line, 0);
264                                         break;
265                                 }
266                         }
267                 }
268
269                 void ProcessSimpleLine (XmlNode container, string line, int from)
270                 {
271                         int idx;
272                         while ((idx = line.IndexOf ('[', from)) >= 0) {
273                                 if (idx + 1 < line.Length && line [idx + 1] == '[')
274                                         from = ProcessSee (
275                                                 container, line, idx, from);
276                                 else
277                                         from = ProcessParamRef (
278                                                 container, line, idx, from);
279                         }
280                         if (from != line.Length)
281                                 container.AppendChild (doc.CreateTextNode (line.Substring (from) + '\n'));
282                 }
283
284                 int ProcessSee (XmlNode container, string line, int idx, int from)
285                 {
286                         int end = line.IndexOf ("]]", idx);
287                         int sep = end < idx ? - 1: line.IndexOf ('|', idx, end - idx);
288                         if (sep < 0)
289                                 throw MarkupError (String.Format ("There is no matching '|' and ']' to close link at position {1} : {0}", line, idx));
290                         if (idx > from) {
291                                 XmlText text = doc.CreateTextNode (
292                                         line.Substring (from, idx - from));
293                                 container.AppendChild (text);
294                         }
295
296                         XmlElement el = doc.CreateElement ("see");
297                         el.SetAttribute ("cref", line.Substring (
298                                 idx + 2, sep - idx - 2).Trim ());
299                         container.AppendChild (el);
300
301                         end += 2;
302                         return end;
303                 }
304
305                 int ProcessParamRef (XmlNode container, string line, int idx, int from)
306                 {
307                         int end = line.IndexOf (']', idx);
308                         if (end < idx)
309                                 throw MarkupError (String.Format ("There is no matching ']' to close link at position {1} : {0}", line, idx));
310                         if (idx > from) {
311                                 XmlText text = doc.CreateTextNode (
312                                         line.Substring (from, idx - from));
313                                 container.AppendChild (text);
314                         }
315
316                         XmlElement el = doc.CreateElement ("paramref");
317                         el.SetAttribute ("name", line.Substring (
318                                 idx + 1, end - idx - 1));
319                         container.AppendChild (el);
320                         end += 1;
321                         return end;
322                 }
323
324                 Exception MarkupError (string message)
325                 {
326                         throw new Exception (String.Format (
327                                 "At line {1} : {0}", message, lineno));
328                 }
329         }
330
331         class EditTarget
332         {
333                 public const string Summary = "summary";
334                 public const string Remarks = "remarks";
335         }
336
337         class StrUtil
338         {
339                 static CompareInfo ci = CultureInfo.CurrentCulture.CompareInfo;
340
341                 public static bool StartsWith (string s, string target)
342                 {
343                         return ci.IsPrefix (s, target, CompareOptions.Ordinal);
344                 }
345         }
346 }