adjust version
[mcs.git] / tools / monodoc / Mono.Documentation / XmlDocUtils.cs
blob8e890e50a2a1b8ae64a32cd892ff848a19b5061d
1 using System;
2 using System.Collections;
3 using System.IO;
4 using System.Text;
5 using System.Text.RegularExpressions;
6 using System.Web;
7 using System.Xml;
9 namespace Mono.Documentation {
11 public delegate XmlDocument DocLoader (string escapedTypeName);
13 public static class XmlDocUtils
15 public static XmlNodeList GetMemberGenericParameters (XmlNode member)
17 return member.SelectNodes ("Docs/typeparam");
20 public static XmlNodeList GetTypeGenericParameters (XmlNode member)
22 return member.SelectNodes ("/Type/TypeParameters/TypeParameter");
25 public static string ToTypeName (string type, XmlNode member)
27 return ToTypeName (type, GetTypeGenericParameters (member),
28 GetMemberGenericParameters (member));
31 public static string ToTypeName (string type, XmlNodeList typeGenParams, XmlNodeList memberGenParams)
33 type = type.Replace ("&", "@").Replace ("<", "{").Replace (">", "}");
34 for (int i = 0; i < typeGenParams.Count; ++i) {
35 string name = typeGenParams [i].InnerText;
36 type = Regex.Replace (type, @"\b" + name + @"\b", "`" + i);
38 for (int i = 0; i < memberGenParams.Count; ++i) {
39 string name = memberGenParams [i].Attributes ["name"].Value;
40 type = Regex.Replace (type, @"\b" + name + @"\b", "``" + i);
42 return type;
45 public static string ToEscapedTypeName (string name)
47 return GetCountedName (name, "`");
50 private static string GetCountedName (string name, string escape)
52 int lt = name.IndexOf ("<");
53 if (lt == -1)
54 return name;
55 StringBuilder type = new StringBuilder (name.Length);
56 int start = 0;
57 do {
58 type.Append (name.Substring (start, lt - start));
59 type.Append (escape);
60 type.Append (GetGenericCount (name, lt, out start));
61 } while ((lt = name.IndexOf ('<', start)) >= 0);
62 if (start < name.Length)
63 type.Append (name.Substring (start));
64 return type.ToString ().Replace ("+", ".");
67 private static int GetGenericCount (string name, int start, out int end)
69 int n = 1;
70 bool r = true;
71 int i = start;
72 int depth = 1;
73 for ( ++i; r && i < name.Length; ++i) {
74 switch (name [i]) {
75 case ',': if (depth == 1) ++n; break;
76 case '<': ++depth; break;
77 case '>': --depth; if (depth == 0) r = false; break;
80 end = i;
81 return n;
84 public static string ToEscapedMemberName (string member)
86 // Explicitly implemented interface members contain '.'s in the member
87 // name, e.g. System.Collections.Generic.IEnumerable<A>.GetEnumerator.
88 // CSC does a s/\./#/g for these.
89 member = member.Replace (".", "#");
90 if (member [member.Length-1] == '>') {
91 int i = member.LastIndexOf ("<");
92 int ignore;
93 return member.Substring (0, i).Replace ("<", "{").Replace (">", "}") +
94 "``" + GetGenericCount (member, i, out ignore);
96 return member.Replace ("<", "{").Replace (">", "}");
99 public static void AddExtensionMethods (XmlDocument typexml, ArrayList/*<XmlNode>*/ extensions, DocLoader loader)
101 // if no members (enum, delegate) don't add extensions
102 XmlNode m = typexml.SelectSingleNode ("/Type/Members");
103 if (m == null)
104 return;
106 // static classes can't be targets:
107 if (typexml.SelectSingleNode (
108 "/Type/TypeSignature[@Language='C#']/@Value")
109 .Value.IndexOf (" static ") >= 0)
110 return;
112 foreach (string s in GetSupportedTypes (typexml, loader)) {
113 foreach (XmlNode extension in extensions) {
114 bool add = false;
115 foreach (XmlNode target in extension.SelectNodes ("Targets/Target")) {
116 if (target.Attributes ["Type"].Value == s) {
117 add = true;
118 break;
121 if (!add) {
122 continue;
124 foreach (XmlNode c in extension.SelectNodes ("Member")) {
125 XmlNode cm = typexml.ImportNode (c, true);
126 m.AppendChild (cm);
132 private static IEnumerable GetSupportedTypes (XmlDocument type, DocLoader loader)
134 yield return "System.Object";
135 yield return GetEscapedPath (type, "Type/@FullName");
137 Hashtable h = new Hashtable ();
138 GetInterfaces (h, type, loader);
140 string s = GetEscapedPath (type, "Type/Base/BaseTypeName");
141 if (s != null) {
142 yield return s;
143 XmlDocument d;
144 string p = s;
145 while (s != null && (d = loader (s)) != null) {
146 GetInterfaces (h, d, loader);
147 s = GetEscapedPath (d, "Type/Base/BaseTypeName");
148 if (p == s)
149 break;
150 yield return s;
154 foreach (object o in h.Keys)
155 yield return o.ToString ();
158 private static string GetEscapedPath (XmlDocument d, string path)
160 XmlNode n = d.SelectSingleNode (path);
161 if (n == null)
162 return null;
163 return "T:" + ToEscapedTypeName (n.InnerText);
166 private static void GetInterfaces (Hashtable ifaces, XmlDocument doc, DocLoader loader)
168 foreach (XmlNode n in doc.SelectNodes ("Type/Interfaces/Interface/InterfaceName")) {
169 string t = ToEscapedTypeName (n.InnerText);
170 string tk = "T:" + t;
171 if (!ifaces.ContainsKey (tk)) {
172 ifaces.Add (tk, null);
173 try {
174 XmlDocument d = loader (t);
175 if (d != null)
176 GetInterfaces (ifaces, d, loader);
178 catch (FileNotFoundException e) {
179 // ignore; interface documentation couldn't be found.
185 // Turns e.g. sources/netdocs into sources/cache/netdocs
186 public static string GetCacheDirectory (string assembledBase)
188 return Path.Combine (
189 Path.Combine (Path.GetDirectoryName (assembledBase), "cache"),
190 Path.GetFileName (assembledBase));
193 public static string GetCachedFileName (string cacheDir, string url)
195 return Path.Combine (cacheDir,
196 HttpUtility.UrlEncode (url).Replace ('/', '+').Replace ("*", "%2a"));