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