1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
|
using System;
using System.Collections;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Xml;
namespace Mono.Documentation {
public delegate XmlDocument DocLoader (string escapedTypeName);
public static class XmlDocUtils
{
public static XmlNodeList GetMemberGenericParameters (XmlNode member)
{
return member.SelectNodes ("Docs/typeparam");
}
public static XmlNodeList GetTypeGenericParameters (XmlNode member)
{
return member.SelectNodes ("/Type/TypeParameters/TypeParameter");
}
public static string ToTypeName (string type, XmlNode member)
{
return ToTypeName (type, GetTypeGenericParameters (member),
GetMemberGenericParameters (member));
}
public static string ToTypeName (string type, XmlNodeList typeGenParams, XmlNodeList memberGenParams)
{
type = type.Replace ("&", "@").Replace ("<", "{").Replace (">", "}");
for (int i = 0; i < typeGenParams.Count; ++i) {
string name = typeGenParams [i].InnerText;
type = Regex.Replace (type, @"\b" + name + @"\b", "`" + i);
}
for (int i = 0; i < memberGenParams.Count; ++i) {
string name = memberGenParams [i].Attributes ["name"].Value;
type = Regex.Replace (type, @"\b" + name + @"\b", "``" + i);
}
return type;
}
public static string ToEscapedTypeName (string name)
{
return GetCountedName (name, "`");
}
private static string GetCountedName (string name, string escape)
{
int lt = name.IndexOf ("<");
if (lt == -1)
return name;
StringBuilder type = new StringBuilder (name.Length);
int start = 0;
do {
type.Append (name.Substring (start, lt - start));
type.Append (escape);
type.Append (GetGenericCount (name, lt, out start));
} while ((lt = name.IndexOf ('<', start)) >= 0);
if (start < name.Length)
type.Append (name.Substring (start));
return type.ToString ().Replace ("+", ".");
}
private static int GetGenericCount (string name, int start, out int end)
{
int n = 1;
bool r = true;
int i = start;
int depth = 1;
for ( ++i; r && i < name.Length; ++i) {
switch (name [i]) {
case ',': if (depth == 1) ++n; break;
case '<': ++depth; break;
case '>': --depth; if (depth == 0) r = false; break;
}
}
end = i;
return n;
}
public static string ToEscapedMemberName (string member)
{
// Explicitly implemented interface members contain '.'s in the member
// name, e.g. System.Collections.Generic.IEnumerable<A>.GetEnumerator.
// CSC does a s/\./#/g for these.
member = member.Replace (".", "#");
if (member [member.Length-1] == '>') {
int i = member.LastIndexOf ("<");
int ignore;
return member.Substring (0, i).Replace ("<", "{").Replace (">", "}") +
"``" + GetGenericCount (member, i, out ignore);
}
return member.Replace ("<", "{").Replace (">", "}");
}
public static void AddExtensionMethods (XmlDocument typexml, ArrayList/*<XmlNode>*/ extensions, DocLoader loader)
{
// if no members (enum, delegate) don't add extensions
XmlNode m = typexml.SelectSingleNode ("/Type/Members");
if (m == null)
return;
// static classes can't be targets:
if (typexml.SelectSingleNode (
"/Type/TypeSignature[@Language='C#']/@Value")
.Value.IndexOf (" static ") >= 0)
return;
foreach (string s in GetSupportedTypes (typexml, loader)) {
foreach (XmlNode extension in extensions) {
bool add = false;
foreach (XmlNode target in extension.SelectNodes ("Targets/Target")) {
if (target.Attributes ["Type"].Value == s) {
add = true;
break;
}
}
if (!add) {
continue;
}
foreach (XmlNode c in extension.SelectNodes ("Member")) {
XmlNode cm = typexml.ImportNode (c, true);
m.AppendChild (cm);
}
}
}
}
private static IEnumerable GetSupportedTypes (XmlDocument type, DocLoader loader)
{
yield return "System.Object";
yield return GetEscapedPath (type, "Type/@FullName");
Hashtable h = new Hashtable ();
GetInterfaces (h, type, loader);
string s = GetEscapedPath (type, "Type/Base/BaseTypeName");
if (s != null) {
yield return s;
XmlDocument d;
string p = s;
while (s != null && (d = loader (s)) != null) {
GetInterfaces (h, d, loader);
s = GetEscapedPath (d, "Type/Base/BaseTypeName");
if (p == s)
break;
yield return s;
}
}
foreach (object o in h.Keys)
yield return o.ToString ();
}
private static string GetEscapedPath (XmlDocument d, string path)
{
XmlNode n = d.SelectSingleNode (path);
if (n == null)
return null;
return "T:" + ToEscapedTypeName (n.InnerText);
}
private static void GetInterfaces (Hashtable ifaces, XmlDocument doc, DocLoader loader)
{
foreach (XmlNode n in doc.SelectNodes ("Type/Interfaces/Interface/InterfaceName")) {
string t = ToEscapedTypeName (n.InnerText);
string tk = "T:" + t;
if (!ifaces.ContainsKey (tk)) {
ifaces.Add (tk, null);
try {
XmlDocument d = loader (t);
if (d != null)
GetInterfaces (ifaces, d, loader);
}
catch (FileNotFoundException e) {
// ignore; interface documentation couldn't be found.
}
}
}
}
// Turns e.g. sources/netdocs into sources/cache/netdocs
public static string GetCacheDirectory (string assembledBase)
{
return Path.Combine (
Path.Combine (Path.GetDirectoryName (assembledBase), "cache"),
Path.GetFileName (assembledBase));
}
public static string GetCachedFileName (string cacheDir, string url)
{
return Path.Combine (cacheDir,
Uri.EscapeUriString (url).Replace ('/', '+').Replace ("*", "%2a"));
}
}
}
|