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