importing messaging-2008 branch to trunk [continued]
[mono.git] / mcs / tools / mdoc / Mono.Documentation / monodocs2html.cs
1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using System.IO;
5 using System.Reflection;
6 using System.Xml;
7 using System.Xml.Xsl;
8 using System.Xml.XPath;
9
10 using Mono.Documentation;
11 using Mono.Options;
12
13 [assembly: AssemblyTitle("Monodocs-to-HTML")]
14 [assembly: AssemblyCopyright("Copyright (c) 2004 Joshua Tauberer <tauberer@for.net>, released under the GPL.")]
15 [assembly: AssemblyDescription("Convert Monodoc XML documentation to static HTML.")]
16
17 namespace Mono.Documentation {
18
19 class MDocToHtmlConverterOptions {
20         public string source;
21         public string dest;
22         public string ext = "html";
23         public string onlytype;
24         public string template;
25         public bool   dumptemplate;
26 }
27
28 class MDocToHtmlConverter : MDocCommand {
29
30         public override void Run (IEnumerable<string> args)
31         {
32                 opts = new MDocToHtmlConverterOptions ();
33                 var p = new OptionSet () {
34                         { "default-template",
35                                 "Writes the default XSLT to stdout.",
36                                 v => opts.dumptemplate = v != null },
37                         { "ext=",
38                                 "The file {EXTENSION} to use for created files.  "+
39                                         "This defaults to \"html\".",
40                                 v => opts.ext = v },
41                         { "o|out=",
42                                 "The {DIRECTORY} to place the generated files and directories.",
43                                 v => opts.dest = v },
44                         { "template=",
45                                 "An XSLT {FILE} to use to generate the created " + 
46                                         "files.  If not specified, uses the template generated by " + 
47                                         "--default-template.",
48                                 v => opts.template = v },
49                 };
50                 List<string> extra = Parse (p, args, "export-html", 
51                                 "[OPTIONS]+ DIRECTORIES",
52                                 "Export mdoc documentation within DIRECTORIES to HTML.");
53                 if (extra == null)
54                         return;
55                 if (extra.Count == 0)
56                         Main2 ();
57                 foreach (var source in extra) {
58                         opts.source = source;
59                         Main2 ();
60                 }
61                 opts.onlytype = "ignore"; // remove warning about unused member
62         }
63
64         static MDocToHtmlConverterOptions opts;
65
66         private static void Main2()
67         {
68                 if (opts.dumptemplate) {
69                         DumpTemplate();
70                         return;
71                 }
72                 
73                 if (opts.source == null || opts.source == "" || opts.dest == null || opts.dest == "")
74                         throw new ApplicationException("The source and dest options must be specified.");
75                 
76                 Directory.CreateDirectory(opts.dest);
77                 
78                 // Load the stylesheets, overview.xml, and resolver
79                 
80                 XslTransform overviewxsl = LoadTransform("overview.xsl");
81                 XslTransform stylesheet = LoadTransform("stylesheet.xsl");
82                 XslTransform template;
83                 if (opts.template == null) {
84                         template = LoadTransform("defaulttemplate.xsl");
85                 } else {
86                         try {
87                                 XmlDocument templatexsl = new XmlDocument();
88                                 templatexsl.Load(opts.template);
89                                 template = new XslTransform();
90                                 template.Load(templatexsl);
91                         } catch (Exception e) {
92                                 throw new ApplicationException("There was an error loading " + opts.template, e);
93                         }
94                 }
95                 
96                 XmlDocument overview = new XmlDocument();
97                 overview.Load(opts.source + "/index.xml");
98
99                 ArrayList extensions = GetExtensionMethods (overview);
100                 
101                 // Create the master page
102                 XsltArgumentList overviewargs = new XsltArgumentList();
103                 overviewargs.AddParam("ext", "", opts.ext);
104                 overviewargs.AddParam("basepath", "", "./");
105                 Generate(overview, overviewxsl, overviewargs, opts.dest + "/index." + opts.ext, template);
106                 overviewargs.RemoveParam("basepath", "");
107                 overviewargs.AddParam("basepath", "", "../");
108                 
109                 // Create the namespace & type pages
110                 
111                 XsltArgumentList typeargs = new XsltArgumentList();
112                 typeargs.AddParam("ext", "", opts.ext);
113                 typeargs.AddParam("basepath", "", "../");
114                 
115                 foreach (XmlElement ns in overview.SelectNodes("Overview/Types/Namespace")) {
116                         string nsname = ns.GetAttribute("Name");
117
118                         if (opts.onlytype != null && !opts.onlytype.StartsWith(nsname + "."))
119                                 continue;
120                                 
121                         System.IO.DirectoryInfo d = new System.IO.DirectoryInfo(opts.dest + "/" + nsname);
122                         if (!d.Exists) d.Create();
123                         
124                         // Create the NS page
125                         overviewargs.AddParam("namespace", "", nsname);
126                         Generate(overview, overviewxsl, overviewargs, opts.dest + "/" + nsname + "/index." + opts.ext, template);
127                         overviewargs.RemoveParam("namespace", "");
128                         
129                         foreach (XmlElement ty in ns.SelectNodes("Type")) {
130                                 string typefilebase = ty.GetAttribute("Name");
131                                 string typename = ty.GetAttribute("DisplayName");
132                                 if (typename.Length == 0)
133                                         typename = typefilebase;
134                                 
135                                 if (opts.onlytype != null && !(nsname + "." + typename).StartsWith(opts.onlytype))
136                                         continue;
137
138                                 string typefile = opts.source + "/" + nsname + "/" + typefilebase + ".xml";
139                                 if (!File.Exists(typefile)) continue;
140
141                                 XmlDocument typexml = new XmlDocument();
142                                 typexml.Load(typefile);
143                                 if (extensions != null) {
144                                         DocLoader loader = CreateDocLoader (overview);
145                                         XmlDocUtils.AddExtensionMethods (typexml, extensions, loader);
146                                 }
147                                 
148                                 Console.WriteLine(nsname + "." + typename);
149                                 
150                                 Generate(typexml, stylesheet, typeargs, opts.dest + "/" + nsname + "/" + typefilebase + "." + opts.ext, template);
151                         }
152                 }
153         }
154
155         private static ArrayList GetExtensionMethods (XmlDocument doc)
156         {
157                 XmlNodeList extensions = doc.SelectNodes ("/Overview/ExtensionMethods/*");
158                 if (extensions.Count == 0)
159                         return null;
160                 ArrayList r = new ArrayList (extensions.Count);
161                 foreach (XmlNode n in extensions)
162                         r.Add (n);
163                 return r;
164         }
165         
166         private static void DumpTemplate() {
167                 Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream("defaulttemplate.xsl");
168                 Stream o = Console.OpenStandardOutput ();
169                 byte[] buf = new byte[1024];
170                 int r;
171                 while ((r = s.Read (buf, 0, buf.Length)) > 0) {
172                         o.Write (buf, 0, r);
173                 }
174         }
175         
176         private static void Generate(XmlDocument source, XslTransform transform, XsltArgumentList args, string output, XslTransform template) {
177                 using (TextWriter textwriter = new StreamWriter(new FileStream(output, FileMode.Create))) {
178                         XmlTextWriter writer = new XmlTextWriter(textwriter);
179                         writer.Formatting = Formatting.Indented;
180                         writer.Indentation = 2;
181                         writer.IndentChar = ' ';
182                         
183                         try {
184                                 XmlDocument intermediate = new XmlDocument();
185                                 intermediate.PreserveWhitespace = true;
186                                 intermediate.Load(transform.Transform(source, args, new ManifestResourceResolver(opts.source)));
187                                 template.Transform(intermediate, new XsltArgumentList(), new XhtmlWriter (writer), null);
188                         } catch (Exception e) {
189                                 throw new ApplicationException("An error occured while generating " + output, e);
190                         }
191                 }
192         }
193         
194         private static XslTransform LoadTransform(string name) {
195                 try {
196                         XmlDocument xsl = new XmlDocument();
197                         xsl.Load(Assembly.GetExecutingAssembly().GetManifestResourceStream(name));
198                         
199                         if (name == "overview.xsl") {
200                                 // bit of a hack.  overview needs the templates in stylesheet
201                                 // for doc formatting, and rather than write a resolver, I'll
202                                 // just do the import for it.
203                                 
204                                 XmlNode importnode = xsl.DocumentElement.SelectSingleNode("*[name()='xsl:include']");
205                                 xsl.DocumentElement.RemoveChild(importnode);
206                                 
207                                 XmlDocument xsl2 = new XmlDocument();
208                                 xsl2.Load(Assembly.GetExecutingAssembly().GetManifestResourceStream("stylesheet.xsl"));
209                                 foreach (XmlNode node in xsl2.DocumentElement.ChildNodes)
210                                         xsl.DocumentElement.AppendChild(xsl.ImportNode(node, true));
211                         }
212                         
213                         XslTransform t = new XslTransform();
214                         t.Load (xsl, new ManifestResourceResolver (opts.source));
215                         
216                         return t;
217                 } catch (Exception e) {
218                         throw new ApplicationException("Error loading " + name + " from internal resource", e);
219                 }
220         }
221
222         private static DocLoader CreateDocLoader (XmlDocument overview)
223         {
224                 Hashtable docs = new Hashtable ();
225                 DocLoader loader = delegate (string s) {
226                         XmlDocument d = null;
227                         if (!docs.ContainsKey (s)) {
228                                 foreach (XmlNode n in overview.SelectNodes ("//Type")) {
229                                         string ns = n.ParentNode.Attributes ["Name"].Value;
230                                         string t  = n.Attributes ["Name"].Value;
231                                         if (s == ns + "." + t.Replace ("+", ".")) {
232                                                 string f = opts.source + "/" + ns + "/" + t + ".xml";
233                                                 if (File.Exists (f)) {
234                                                         d = new XmlDocument ();
235                                                         d.Load (f);
236                                                 }
237                                                 docs.Add (s, d);
238                                                 break;
239                                         }
240                                 }
241                         }
242                         else
243                                 d = (XmlDocument) docs [s];
244                         return d;
245                 };
246                 return loader;
247         }
248 }
249
250 }