Prepping for mdoc et. al. move from monodoc/tools to mcs/tools/mdoc...
[mono.git] / mcs / tools / mdoc / monodocs2slashdoc.cs
1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using System.IO;
5 using System.Text;
6 using System.Text.RegularExpressions;
7 using System.Xml;
8
9 using NDesk.Options;
10
11 namespace Mono.Documentation {
12 public class Monodocs2SlashDoc {
13         
14         public static void Main(string[] args) {
15                 string file = null;
16                 bool showHelp = false;
17                 OptionSet p = new OptionSet () {
18                         { "o|out=", 
19                                 "The XML {FILE} to generate.\n" + 
20                                 "If not specified, will create a set of files in the curent directory " +
21                                 "based on the //AssemblyInfo/AssemblyName values within the documentation.\n" +
22                                 "Use '-' to write to standard output.",
23                                 v => file = v },
24                         { "h|?|help",
25                                 "Show this message and exit.",
26                                 v => showHelp = v != null },
27                 };
28                 List<string> extra = p.Parse (args);
29                 if (extra.Count == 0 || showHelp) {
30                         Console.WriteLine ("usage: monodocs2slashdoc [OPTION]* DIRECTORIES");
31                         Console.WriteLine ();
32                         Console.WriteLine ("Converts mdoc(5)-style XML documentation into Microsoft XML Documentation.");
33                         Console.WriteLine ();
34                         Console.WriteLine ("Available Options:");
35                         p.WriteOptionDescriptions (Console.Out);
36                         return;
37                 }
38
39                 Process (extra, file);
40         }
41
42         private static void Process (List<string> dirs, string file)
43         {
44                 Dictionary<string, XmlElement> outputfiles = new Dictionary<string, XmlElement> ();
45
46                 XmlDocument nsSummaries = new XmlDocument();
47                 nsSummaries.LoadXml("<namespaces/>");
48
49                 foreach (string dir in dirs)
50                         Process (dir, outputfiles, nsSummaries, file == null);
51
52                 if (outputfiles.Count > 0 && file != null) {
53                         List<string> files = new List<string> (outputfiles.Keys);
54                         files.Sort ();
55                         XmlDocument d = new XmlDocument ();
56                         d.AppendChild (d.CreateElement ("doc"));
57                         d.FirstChild.AppendChild (
58                                         d.ImportNode (outputfiles [files [0]].SelectSingleNode ("/doc/assembly"), true));
59                         XmlElement members = d.CreateElement ("members");
60                         d.FirstChild.AppendChild (members);
61                         foreach (string f in files) {
62                                 XmlElement from = (XmlElement) outputfiles [f];
63                                 foreach (XmlNode n in from.SelectNodes ("/doc/members/*"))
64                                         members.AppendChild (d.ImportNode (n, true));
65                         }
66                         using (TextWriter tw = file == "-" ? Console.Out : new StreamWriter (file))
67                                 WriteXml (d.DocumentElement, tw);
68                         return;
69                 }
70
71                 // Write out each of the assembly documents
72                 foreach (string assemblyName in outputfiles.Keys) {
73                         XmlElement members = (XmlElement)outputfiles[assemblyName];
74                         Console.WriteLine(assemblyName + ".xml");
75                         using(StreamWriter sw = new StreamWriter(assemblyName + ".xml")) {
76                                 WriteXml(members.OwnerDocument.DocumentElement, sw);
77                         }
78                 }
79         
80                 // Write out a namespace summaries file.
81                 Console.WriteLine("NamespaceSummaries.xml");
82                 using(StreamWriter writer = new StreamWriter("NamespaceSummaries.xml")) {
83                         WriteXml(nsSummaries.DocumentElement, writer);
84                 }
85         }
86
87         private static void Process (string basepath, Dictionary<string, XmlElement> outputfiles, XmlDocument nsSummaries, bool implicitFiles)
88         {
89                 if (System.Environment.CurrentDirectory == System.IO.Path.GetFullPath(basepath) && implicitFiles) {
90                         Console.WriteLine("Don't run this tool from your documentation directory, since some files could be accidentally overwritten.");
91                         return;
92                 }
93
94                 XmlDocument index_doc = new XmlDocument();
95                 index_doc.Load(Path.Combine(basepath, "index.xml"));
96                 XmlElement index = index_doc.DocumentElement;
97                 
98                 foreach (XmlElement assmbly in index.SelectNodes("Assemblies/Assembly")) {
99                         string assemblyName = assmbly.GetAttribute("Name");
100                         if (outputfiles.ContainsKey (assemblyName))
101                                 continue;
102                         XmlDocument output = new XmlDocument();
103                         XmlElement output_root = output.CreateElement("doc");
104                         output.AppendChild(output_root);
105
106                         XmlElement output_assembly = output.CreateElement("assembly");
107                         output_root.AppendChild(output_assembly);
108                         XmlElement output_assembly_name = output.CreateElement("name");
109                         output_assembly.AppendChild(output_assembly_name);
110                         output_assembly_name.InnerText = assemblyName;
111                 
112                         XmlElement members = output.CreateElement("members");
113                         output_root.AppendChild(members);
114                         
115                         outputfiles.Add (assemblyName, members);
116                 }
117                         
118                 foreach (XmlElement nsnode in index.SelectNodes("Types/Namespace")) {
119                         string ns = nsnode.GetAttribute("Name");
120                         foreach (XmlElement typedoc in nsnode.SelectNodes("Type")) {
121                                 string typename = typedoc.GetAttribute("Name");
122                                 XmlDocument type = new XmlDocument();
123                                 type.Load(Path.Combine(Path.Combine(basepath, ns), typename) + ".xml");
124                                 
125                                 string assemblyname = type.SelectSingleNode("Type/AssemblyInfo/AssemblyName").InnerText;
126                                 XmlElement members = outputfiles [assemblyname];
127                                 if (members == null) continue; // assembly is strangely not listed in the index
128                                 
129                                 string typeName = XmlDocUtils.ToEscapedTypeName (type.SelectSingleNode("Type/@FullName").InnerText);
130                                 CreateMember("T:" + typeName, type.DocumentElement, members);
131                                         
132                                 foreach (XmlElement memberdoc in type.SelectNodes("Type/Members/Member")) {
133                                         string name = typeName;
134                                         switch (memberdoc.SelectSingleNode("MemberType").InnerText) {
135                                                 case "Constructor":
136                                                         name = "C:" + name + MakeArgs(memberdoc);
137                                                         break;
138                                                 case "Method":
139                                                         name = "M:" + name + "." + XmlDocUtils.ToEscapedMemberName (memberdoc.GetAttribute("MemberName")) + MakeArgs(memberdoc);
140                                                         if (memberdoc.GetAttribute("MemberName") == "op_Implicit" || memberdoc.GetAttribute("MemberName") == "op_Explicit")
141                                                                 name += "~" + XmlDocUtils.ToTypeName (memberdoc.SelectSingleNode("ReturnValue/ReturnType").InnerText, memberdoc);
142                                                         break;
143                                                 case "Property":
144                                                         name = "P:" + name + "." + XmlDocUtils.ToEscapedMemberName (memberdoc.GetAttribute("MemberName")) + MakeArgs(memberdoc);
145                                                         break;
146                                                 case "Field":
147                                                         name = "F:" + name + "." + XmlDocUtils.ToEscapedMemberName (memberdoc.GetAttribute("MemberName"));
148                                                         break;
149                                                 case "Event":
150                                                         name = "E:" + name + "." + XmlDocUtils.ToEscapedMemberName (memberdoc.GetAttribute("MemberName"));
151                                                         break;
152                                         }
153                                         
154                                         CreateMember(name, memberdoc, members);
155                                 }
156                         }
157                 }
158                 foreach (XmlElement nsnode in index.SelectNodes("Types/Namespace")) {
159                         AddNamespaceSummary(nsSummaries, basepath, nsnode.GetAttribute("Name"));
160                 }
161         }
162         
163         private static void AddNamespaceSummary(XmlDocument nsSummaries, string basepath, string currentNs) {
164                 string filename = Path.Combine(basepath, currentNs + ".xml");
165                 if (File.Exists(filename))      {
166                         XmlDocument nsSummary = new XmlDocument();
167                         nsSummary.Load(filename);
168                         XmlElement ns = nsSummaries.CreateElement("namespace");
169                         nsSummaries.DocumentElement.AppendChild(ns);
170                         ns.SetAttribute("name", currentNs);
171                         ns.InnerText = nsSummary.SelectSingleNode("/Namespace/Docs/summary").InnerText;
172                 }
173         }
174         
175         private static void CreateMember(string name, XmlElement input, XmlElement output) {
176                 XmlElement member = output.OwnerDocument.CreateElement("member");
177                 output.AppendChild(member);
178                 
179                 member.SetAttribute("name", name);
180                 
181                 foreach (XmlNode docnode in input.SelectSingleNode("Docs"))
182                         member.AppendChild(output.OwnerDocument.ImportNode(docnode, true));
183         }
184         
185         private static string MakeArgs (XmlElement member)
186         {
187                 XmlNodeList parameters = member.SelectNodes ("Parameters/Parameter");
188                 if (parameters.Count == 0)
189                         return "";
190                 StringBuilder args = new StringBuilder ();
191                 args.Append ("(");
192                 args.Append (XmlDocUtils.ToTypeName (parameters [0].Attributes ["Type"].Value, member));
193                 for (int i = 1; i < parameters.Count; ++i) {
194                         args.Append (",");
195                         args.Append (XmlDocUtils.ToTypeName (parameters [i].Attributes ["Type"].Value, member));
196                 }
197                 args.Append (")");
198                 return args.ToString ();
199         }
200
201         private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
202                 XmlTextWriter writer = new XmlTextWriter(output);
203                 writer.Formatting = Formatting.Indented;
204                 writer.Indentation = 4;
205                 writer.IndentChar = ' ';
206                 element.WriteTo(writer);
207                 output.WriteLine();     
208         }
209 }
210
211 }