98f81db13e6bf45c17bfb7b9d19e61e383152acd
[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                         string current_library = "";
61
62                         var types = new List<string> ();
63                         string outdir = null;
64
65                         var options = new OptionSet () {
66                                 { "o|out=", 
67                                         "{DIRECTORY} to create Microsoft XML assembly.xml documentation files.",
68                                         v => outdir = v },
69                                 { "library=",
70                                         "Ignored for compatibility with update-ecma-xml.",
71                                         v => {} },
72                                 { "type=",
73                                         "The full {TYPE} name of a type to copy into the output file.",
74                                         v => types.Add (v) },
75                         };
76                         var sources = Parse (options, args, "export-ecma-xml", 
77                                         "[OPTIONS]+ DIRECTORIES",
78                                         "Convert Microsoft internal XML documentation within DIRECTORIES into\n" +
79                                         "Microsoft XML documentation.\n" +
80                                         "\n" +
81                                         "Microsoft internal XML documentation is a custom XML dialect (yay) with\n" +
82                                         "a specific directory structure:\n" +
83                                         "\n" +
84                                         "\tROOT/dotted.namespace/asset.xml\n" +
85                                         "\n" +
86                                         "where ROOT is a directory that can be passed as one of the DIRECTORIES\n" +
87                                         "arguments to x-msitomsx, dotted.namespace is the lowercased namespace\n" +
88                                         "in dotted form, e.g. 'system.threading', and asset.xml is the name of \n" +
89                                         "the \"asset\" being documented.  The asset.xml basename is a GUID, and\n" +
90                                         "the file contains type/namespace/etc. documentation, one file per asset.\n" +
91                                         "\n" +
92                                         "There is no separation of assemblies in this format, only namespaces.\n" +
93                                         "Types from all assemblies are intermixed with each other.\n" +
94                                         "\n" +
95                                         "The output of x-msitomsx will be a set of files in the --out directory,\n" +
96                                         "one file per assembly (normal 'csc /doc' convention).  For example,\n" +
97                                         "\n" +
98                                         "\tmdoc x-msitomsx -o docs import-root --type System.String\n" +
99                                         "\n" +
100                                         "will create the file 'docs/mscorlib.dll' which will contain the converted\n" +
101                                         "documentation for the System.String type.");
102                         if (sources == null)
103                                 return;
104                         if (sources.Count == 0)
105                                 Error ("No directories specified.");
106                         if (outdir == null)
107                                 Error ("No output directory specified.  Please use --out=DIRECTORY.");
108
109                         types.Sort ();
110
111                         Dictionary<string, XDocument> docs = Convert (sources, types);
112                         foreach (KeyValuePair<string, XDocument> e in docs) {
113                                 using (var o = CreateWriter (Path.Combine (outdir, e.Key + ".xml")))
114                                         e.Value.WriteTo (o);
115                         }
116                 }
117
118                 private Dictionary<string, XDocument> Convert (List<string> sources, List<string> types)
119                 {
120                         var docs = new Dictionary<string, XDocument> ();
121
122                         foreach (var source in sources) {
123                                 foreach (var dir in Directory.GetDirectories (source)) {
124                                         foreach (var file in Directory.GetFiles (dir, "*.xml")) {
125                                                 ConvertDocs (docs, types, file);
126                                         }
127                                 }
128                         }
129
130                         return docs;
131                 }
132
133                 private void ConvertDocs (Dictionary<string, XDocument> docs, List<string> types, string file)
134                 {
135                         var doc = LoadFile (file);
136                         var type = doc.Root.Element ("members").Element ("member").Attribute ("name").Value;
137
138                         if (type.StartsWith ("N:"))
139                                 return;
140
141                         if (!type.StartsWith ("T:"))
142                                 throw new InvalidOperationException ("File '" + file + "' doesn't contain type documentation, it contains docs for: " + type);
143
144                         type = type.Substring (2);
145                         if (types.Count > 0 && types.BinarySearch (type) < 0)
146                                 return;
147
148                         var assembly = doc.Root.Element ("assembly").Element ("name").Value;
149                         XDocument asmdocs;
150                         if (!docs.TryGetValue (assembly, out asmdocs)) {
151                                 docs.Add (assembly, 
152                                                 asmdocs = new XDocument (
153                                                         new XElement ("doc", 
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