using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; using System.Text.RegularExpressions; using System.Xml; using Monodoc; using Mono.Options; namespace Mono.Documentation { public class MDocToMSXDocConverter : MDocCommand { public override void Run (IEnumerable args) { string file = null; var p = new OptionSet () { { "o|out=", "The XML {FILE} to generate.\n" + "If not specified, will create a set of files in the curent directory " + "based on the //AssemblyInfo/AssemblyName values within the documentation.\n" + "Use '-' to write to standard output.", v => file = v }, }; List directories = Parse (p, args, "export-slashdoc", "[OPTIONS]+ DIRECTORIES", "Export mdoc(5) documentation within DIRECTORIES into \n" + "Microsoft XML Documentation format files."); if (directories == null) return; Run (file, directories); } public static void Run (string file, IEnumerable dirs) { Dictionary outputfiles = new Dictionary (); XmlDocument nsSummaries = new XmlDocument(); nsSummaries.LoadXml(""); foreach (string dir in dirs) Process (dir, outputfiles, nsSummaries, file == null); if (outputfiles.Count > 0 && file != null) { List files = new List (outputfiles.Keys); files.Sort (); XmlDocument d = new XmlDocument (); d.AppendChild (d.CreateElement ("doc")); d.FirstChild.AppendChild ( d.ImportNode (outputfiles [files [0]].SelectSingleNode ("/doc/assembly"), true)); XmlElement members = d.CreateElement ("members"); d.FirstChild.AppendChild (members); foreach (string f in files) { XmlElement from = (XmlElement) outputfiles [f]; foreach (XmlNode n in from.SelectNodes ("/doc/members/*")) members.AppendChild (d.ImportNode (n, true)); } using (TextWriter tw = file == "-" ? Console.Out : new StreamWriter (file)) WriteXml (d.DocumentElement, tw); return; } // Write out each of the assembly documents foreach (string assemblyName in outputfiles.Keys) { XmlElement members = (XmlElement)outputfiles[assemblyName]; Console.WriteLine(assemblyName + ".xml"); using(StreamWriter sw = new StreamWriter(assemblyName + ".xml")) { WriteXml(members.OwnerDocument.DocumentElement, sw); } } // Write out a namespace summaries file. Console.WriteLine("NamespaceSummaries.xml"); using(StreamWriter writer = new StreamWriter("NamespaceSummaries.xml")) { WriteXml(nsSummaries.DocumentElement, writer); } } private static void Process (string basepath, Dictionary outputfiles, XmlDocument nsSummaries, bool implicitFiles) { if (System.Environment.CurrentDirectory == System.IO.Path.GetFullPath(basepath) && implicitFiles) { Console.WriteLine("Don't run this tool from your documentation directory, since some files could be accidentally overwritten."); return; } XmlDocument index_doc = new XmlDocument(); index_doc.Load(Path.Combine(basepath, "index.xml")); XmlElement index = index_doc.DocumentElement; foreach (XmlElement assmbly in index.SelectNodes("Assemblies/Assembly")) { string assemblyName = assmbly.GetAttribute("Name"); if (outputfiles.ContainsKey (assemblyName)) continue; XmlDocument output = new XmlDocument(); XmlElement output_root = output.CreateElement("doc"); output.AppendChild(output_root); XmlElement output_assembly = output.CreateElement("assembly"); output_root.AppendChild(output_assembly); XmlElement output_assembly_name = output.CreateElement("name"); output_assembly.AppendChild(output_assembly_name); output_assembly_name.InnerText = assemblyName; XmlElement members = output.CreateElement("members"); output_root.AppendChild(members); outputfiles.Add (assemblyName, members); } foreach (XmlElement nsnode in index.SelectNodes("Types/Namespace")) { string ns = nsnode.GetAttribute("Name"); foreach (XmlElement typedoc in nsnode.SelectNodes("Type")) { string typename = typedoc.GetAttribute("Name"); XmlDocument type = new XmlDocument(); type.Load(Path.Combine(Path.Combine(basepath, ns), typename) + ".xml"); string assemblyname = type.SelectSingleNode("Type/AssemblyInfo/AssemblyName").InnerText; XmlElement members = outputfiles [assemblyname]; if (members == null) continue; // assembly is strangely not listed in the index CreateMember (GetCref (type.DocumentElement), type.DocumentElement, members); foreach (XmlElement memberdoc in type.SelectNodes("Type/Members/Member")) { string name = GetCref (memberdoc); CreateMember(name, memberdoc, members); } } } foreach (XmlElement nsnode in index.SelectNodes("Types/Namespace")) { AddNamespaceSummary(nsSummaries, basepath, nsnode.GetAttribute("Name")); } } static string GetCref (XmlElement member) { string typeName = XmlDocUtils.ToEscapedTypeName (member.SelectSingleNode("/Type/@FullName").InnerText); if (member.Name == "Type") return "T:" + typeName; string memberType = member.SelectSingleNode("MemberType").InnerText; switch (memberType) { case "Constructor": return "C:" + typeName + MakeArgs(member); case "Event": return "E:" + typeName + "." + XmlDocUtils.ToEscapedMemberName (member.GetAttribute("MemberName")); case "Field": return "F:" + typeName + "." + XmlDocUtils.ToEscapedMemberName (member.GetAttribute("MemberName")); case "Method": { string name = "M:" + typeName + "." + XmlDocUtils.ToEscapedMemberName (member.GetAttribute("MemberName")) + MakeArgs(member); if (member.GetAttribute("MemberName") == "op_Implicit" || member.GetAttribute("MemberName") == "op_Explicit") name += "~" + XmlDocUtils.ToTypeName (member.SelectSingleNode("ReturnValue/ReturnType").InnerText, member); return name; } case "Property": return "P:" + typeName + "." + XmlDocUtils.ToEscapedMemberName (member.GetAttribute("MemberName")) + MakeArgs(member); default: throw new NotSupportedException ("MemberType '" + memberType + "' is not supported."); } } static string MakeArgs (XmlElement member) { XmlNodeList parameters = member.SelectNodes ("Parameters/Parameter"); if (parameters.Count == 0) return ""; StringBuilder args = new StringBuilder (); args.Append ("("); args.Append (XmlDocUtils.ToTypeName (parameters [0].Attributes ["Type"].Value, member)); for (int i = 1; i < parameters.Count; ++i) { args.Append (","); args.Append (XmlDocUtils.ToTypeName (parameters [i].Attributes ["Type"].Value, member)); } args.Append (")"); return args.ToString (); } private static void AddNamespaceSummary(XmlDocument nsSummaries, string basepath, string currentNs) { foreach (var filename in new [] { Path.Combine(basepath, currentNs + ".xml"), Path.Combine(basepath, "ns-" + currentNs + ".xml")}) { if (File.Exists(filename)) { XmlDocument nsSummary = new XmlDocument(); nsSummary.Load(filename); XmlElement ns = nsSummaries.CreateElement("namespace"); nsSummaries.DocumentElement.AppendChild(ns); ns.SetAttribute("name", currentNs); ns.InnerText = nsSummary.SelectSingleNode("/Namespace/Docs/summary").InnerText; } } } private static void CreateMember(string name, XmlElement input, XmlElement output) { XmlElement member = output.OwnerDocument.CreateElement("member"); output.AppendChild(member); member.SetAttribute("name", name); foreach (XmlNode docnode in input.SelectSingleNode("Docs")) member.AppendChild(output.OwnerDocument.ImportNode(docnode, true)); } private static void WriteXml(XmlElement element, System.IO.TextWriter output) { XmlTextWriter writer = new XmlTextWriter(output); writer.Formatting = Formatting.Indented; writer.Indentation = 4; writer.IndentChar = ' '; element.WriteTo(writer); output.WriteLine(); } } }