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