229 lines
8.3 KiB
C#
Raw Normal View History

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<string> args)
{
string file = null;
bool quiet = false;
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 },
{ "q|quiet",
"Reduce logging to the console.",
v => quiet = v != null },
};
List<string> 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, quiet);
}
public static void Run (string file, IEnumerable<string> dirs, bool quiet)
{
Dictionary<string, XmlElement> outputfiles = new Dictionary<string, XmlElement> ();
XmlDocument nsSummaries = new XmlDocument();
nsSummaries.LoadXml("<namespaces/>");
foreach (string dir in dirs)
Process (dir, outputfiles, nsSummaries, file == null);
if (outputfiles.Count > 0 && file != null) {
List<string> files = new List<string> (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];
if (!quiet)
Console.WriteLine(assemblyName + ".xml");
using(StreamWriter sw = new StreamWriter(assemblyName + ".xml")) {
WriteXml(members.OwnerDocument.DocumentElement, sw);
}
}
// Write out a namespace summaries file.
if (!quiet)
Console.WriteLine("NamespaceSummaries.xml");
using(StreamWriter writer = new StreamWriter("NamespaceSummaries.xml")) {
WriteXml(nsSummaries.DocumentElement, writer);
}
}
private static void Process (string basepath, Dictionary<string, XmlElement> 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));
AppendRefSymbol(args, parameters[0]);
for (int i = 1; i < parameters.Count; ++i) {
args.Append (",");
args.Append (XmlDocUtils.ToTypeName (parameters [i].Attributes ["Type"].Value, member));
AppendRefSymbol(args, parameters[i]);
}
args.Append (")");
return args.ToString ();
}
private static void AppendRefSymbol(StringBuilder args, XmlNode parameter)
{
if (parameter.Attributes["RefType"]?.Value == "out" || parameter.Attributes["RefType"]?.Value == "ref")
args.Append("@");
}
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();
}
}
}