459ac1761b0219ea03bce1228b5d919213b3b9ca
[mono.git] / mcs / tools / mdoc / Mono.Documentation / msitomsx.cs
1 //
2 // msitomsx.cs: Microsoft Internal XML to Microsoft XML Documentation
3 //
4 // Arguably this doesn't belong in mdoc, but I'd rather not do some
5 // stand-alone tool either, especially since the primary reason it exists is
6 // to facilitate generating ECMA documentation via mdoc-update and
7 // mdoc-update-ecma-xml...
8 //
9 // Author:
10 //   Jonathan Pryor  <jpryor@novell.com>
11 //
12 // Copyright (c) 2010 Novell, Inc. (http://www.novell.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 //
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 //
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System;
35 using System.Collections.Generic;
36 using System.IO;
37 using System.Reflection;
38 using System.Text;
39 using System.Xml;
40 using System.Xml.Linq;
41 using System.Xml.Xsl;
42
43 using Mono.Options;
44
45 namespace Mono.Documentation {
46
47         class MsidocToMsxdocConverter : MDocCommand {
48
49                 XslCompiledTransform msiToMsxTransform = new XslCompiledTransform ();
50
51                 public MsidocToMsxdocConverter ()
52                 {
53                         using (var r = XmlReader.Create (
54                                                 Assembly.GetExecutingAssembly ().GetManifestResourceStream ("msitomsx.xsl")))
55                                 msiToMsxTransform.Load (r);
56                 }
57
58                 public override void Run (IEnumerable<string> args)
59                 {
60                         var types = new List<string> ();
61                         string outdir = null;
62
63                         var options = new OptionSet () {
64                                 { "o|out=", 
65                                         "{DIRECTORY} to create Microsoft XML assembly.xml documentation files.",
66                                         v => outdir = v },
67                                 { "library=",
68                                         "Ignored for compatibility with update-ecma-xml.",
69                                         v => {} },
70                                 { "type=",
71                                         "The full {TYPE} name of a type to copy into the output file.",
72                                         v => types.Add (v) },
73                         };
74                         var sources = Parse (options, args, "export-ecma-xml", 
75                                         "[OPTIONS]+ DIRECTORIES",
76                                         "Convert Microsoft internal XML documentation within DIRECTORIES into\n" +
77                                         "Microsoft XML documentation.\n" +
78                                         "\n" +
79                                         "Microsoft internal XML documentation is a custom XML dialect (yay) with\n" +
80                                         "a specific directory structure:\n" +
81                                         "\n" +
82                                         "\tROOT/dotted.namespace/asset.xml\n" +
83                                         "\n" +
84                                         "where ROOT is a directory that can be passed as one of the DIRECTORIES\n" +
85                                         "arguments to x-msitomsx, dotted.namespace is the lowercased namespace\n" +
86                                         "in dotted form, e.g. 'system.threading', and asset.xml is the name of \n" +
87                                         "the \"asset\" being documented.  The asset.xml basename is a GUID, and\n" +
88                                         "the file contains type/namespace/etc. documentation, one file per asset.\n" +
89                                         "\n" +
90                                         "There is no separation of assemblies in this format, only namespaces.\n" +
91                                         "Types from all assemblies are intermixed with each other.\n" +
92                                         "\n" +
93                                         "The output of x-msitomsx will be a set of files in the --out directory,\n" +
94                                         "one file per assembly (normal 'csc /doc' convention).  For example,\n" +
95                                         "\n" +
96                                         "\tmdoc x-msitomsx -o docs import-root --type System.String\n" +
97                                         "\n" +
98                                         "will create the file 'docs/mscorlib.dll' which will contain the converted\n" +
99                                         "documentation for the System.String type.");
100                         if (sources == null)
101                                 return;
102                         if (sources.Count == 0)
103                                 Error ("No directories specified.");
104                         if (outdir == null)
105                                 Error ("No output directory specified.  Please use --out=DIRECTORY.");
106
107                         types.Sort ();
108
109                         Dictionary<string, XDocument> docs = Convert (sources, types);
110                         foreach (KeyValuePair<string, XDocument> e in docs) {
111                                 using (var o = CreateWriter (Path.Combine (outdir, e.Key + ".xml")))
112                                         e.Value.WriteTo (o);
113                         }
114                 }
115
116                 private Dictionary<string, XDocument> Convert (List<string> sources, List<string> types)
117                 {
118                         var docs = new Dictionary<string, XDocument> ();
119
120                         foreach (var source in sources) {
121                                 foreach (var dir in Directory.GetDirectories (source)) {
122                                         foreach (var file in Directory.GetFiles (dir, "*.xml")) {
123                                                 ConvertDocs (docs, types, file);
124                                         }
125                                 }
126                         }
127
128                         return docs;
129                 }
130
131                 private void ConvertDocs (Dictionary<string, XDocument> docs, List<string> types, string file)
132                 {
133                         var doc = LoadFile (file);
134                         var type = doc.Root.Element ("members").Element ("member").Attribute ("name").Value;
135
136                         if (type.StartsWith ("N:"))
137                                 return;
138
139                         if (!type.StartsWith ("T:"))
140                                 throw new InvalidOperationException ("File '" + file + "' doesn't contain type documentation, it contains docs for: " + type);
141
142                         type = type.Substring (2);
143                         if (types.Count > 0 && types.BinarySearch (type) < 0)
144                                 return;
145
146                         var assembly = doc.Root.Element ("assembly").Element ("name").Value;
147                         XDocument asmdocs;
148                         if (!docs.TryGetValue (assembly, out asmdocs)) {
149                                 docs.Add (assembly, 
150                                                 asmdocs = new XDocument (
151                                                         new XElement ("doc", 
152                                                                 new XElement ("assembly",
153                                                                         new XElement ("name", assembly)),
154                                                                 new XElement ("members"))));
155                         }
156
157                         var import = new XDocument ();
158                         msiToMsxTransform.Transform (doc.CreateReader (), import.CreateWriter ());
159
160                         asmdocs.Root.Element ("members").Add (import.Root.Element ("members").Elements ("member"));
161                 }
162
163                 static XDocument LoadFile (string file)
164                 {
165                         using (XmlReader r = XmlReader.Create (file))
166                                 return XDocument.Load (r);
167                 }
168
169                 static XmlWriter CreateWriter (string file)
170                 {
171                         var settings = new XmlWriterSettings {
172                                 Encoding            = new UTF8Encoding (false),
173                                 Indent              = true,
174                                 IndentChars         = "    ",
175                                 NewLineChars        = "\r\n",
176                                 OmitXmlDeclaration  = true,
177                         };
178
179                         return XmlWriter.Create (file, settings);
180                 }
181         }
182 }
183