2 // The provider for a tree of ECMA documents
5 // Miguel de Icaza (miguel@ximian.com)
6 // Joshua Tauberer (tauberer@for.net)
8 // (C) 2002, 2003 Ximian, Inc.
9 // (C) 2003 Joshua Tauberer.
10 // Copyright 2003-2011 Novell
11 // Copyright 2011 Xamarin Inc
14 // Should cluster together constructors
17 // Should render attributes on the signature.
18 // Include examples as well.
22 using System.Diagnostics;
23 using System.Reflection;
26 using System.Xml.XPath;
29 using System.Collections;
30 using Mono.Lucene.Net.Index;
31 using Mono.Lucene.Net.Documents;
33 using Mono.Documentation;
35 using BF = System.Reflection.BindingFlags;
38 // Helper routines to extract information from an Ecma XML document
40 public static class EcmaDoc {
41 public static string GetFullClassName (XmlDocument doc)
43 return doc.SelectSingleNode ("/Type").Attributes ["FullName"].InnerText;
46 public static string GetClassName (XmlDocument doc)
48 return GetDisplayName (doc.SelectSingleNode ("/Type")).Replace ("+", ".");
51 public static string GetDisplayName (XmlNode type)
53 if (type.Attributes ["DisplayName"] != null) {
54 return type.Attributes ["DisplayName"].Value;
56 return type.Attributes ["Name"].Value;
59 public static string GetClassAssembly (XmlDocument doc)
61 return doc.SelectSingleNode ("/Type/AssemblyInfo/AssemblyName").InnerText;
64 public static string GetClassNamespace (XmlDocument doc)
66 string s = doc.SelectSingleNode ("/Type").Attributes ["FullName"].InnerText;
68 return s.Substring (0, s.LastIndexOf ("."));
71 public static string GetTypeKind (XmlDocument doc)
73 XmlNode node = doc.SelectSingleNode ("/Type/Base/BaseTypeName");
76 if (GetFullClassName (doc) == "System.Object")
81 switch (node.InnerText){
83 case "System.Delegate":
84 case "System.MulticastDelegate":
86 case "System.ValueType":
96 // Utility function: converts a fully .NET qualified type name into a C#-looking one
98 public static string ConvertCTSName (string type)
100 if (!type.StartsWith ("System."))
103 if (type.EndsWith ("*"))
104 return ConvertCTSName(type.Substring(0, type.Length - 1)) + "*";
105 if (type.EndsWith ("&"))
106 return ConvertCTSName(type.Substring(0, type.Length - 1)) + "&";
107 if (type.EndsWith ("[]"))
108 return ConvertCTSName(type.Substring(0, type.Length - 2)) + "[]";
111 case "System.Byte": return "byte";
112 case "System.SByte": return "sbyte";
113 case "System.Int16": return "short";
114 case "System.Int32": return "int";
115 case "System.Int64": return "long";
117 case "System.UInt16": return "ushort";
118 case "System.UInt32": return "uint";
119 case "System.UInt64": return "ulong";
121 case "System.Single": return "float";
122 case "System.Double": return "double";
123 case "System.Decimal": return "decimal";
124 case "System.Boolean": return "bool";
125 case "System.Char": return "char";
126 case "System.String": return "string";
128 case "System.Object": return "object";
129 case "System.Void": return "void";
132 if (type.LastIndexOf(".") == 6)
133 return type.Substring(7);
138 internal static string GetNamespaceFile (string dir, string ns)
140 string nsxml = Path.Combine (dir, "ns-" + ns + ".xml");
141 if (!File.Exists (nsxml))
142 nsxml = Path.Combine (dir, ns + ".xml");
146 internal static string GetImageFile (string dir, string img)
148 string path = Path.Combine (dir, Path.Combine ("_images", img));
149 return File.Exists (path) ? path : null;
152 public static string GetCref (XmlElement member)
154 string typeName = XmlDocUtils.ToEscapedTypeName (member.SelectSingleNode("/Type/@FullName").InnerText);
155 if (member.Name == "Type")
156 return "T:" + typeName;
157 string memberType = member.SelectSingleNode("MemberType").InnerText;
158 switch (memberType) {
160 return "C:" + typeName + MakeArgs(member);
162 return "E:" + typeName + "." + XmlDocUtils.ToEscapedMemberName (member.GetAttribute("MemberName"));
164 return "F:" + typeName + "." + XmlDocUtils.ToEscapedMemberName (member.GetAttribute("MemberName"));
166 string name = "M:" + typeName + "." + XmlDocUtils.ToEscapedMemberName (member.GetAttribute("MemberName")) + MakeArgs(member);
167 if (member.GetAttribute("MemberName") == "op_Implicit" || member.GetAttribute("MemberName") == "op_Explicit")
168 name += "~" + XmlDocUtils.ToTypeName (member.SelectSingleNode("ReturnValue/ReturnType").InnerText, member);
172 return "P:" + typeName + "." + XmlDocUtils.ToEscapedMemberName (member.GetAttribute("MemberName")) + MakeArgs(member);
174 throw new NotSupportedException ("MemberType '" + memberType + "' is not supported.");
178 private static string MakeArgs (XmlElement member)
180 XmlNodeList parameters = member.SelectNodes ("Parameters/Parameter");
181 if (parameters.Count == 0)
183 StringBuilder args = new StringBuilder ();
185 args.Append (XmlDocUtils.ToTypeName (parameters [0].Attributes ["Type"].Value, member));
186 for (int i = 1; i < parameters.Count; ++i) {
188 args.Append (XmlDocUtils.ToTypeName (parameters [i].Attributes ["Type"].Value, member));
191 return args.ToString ();
196 // The Ecma documentation provider:
198 // It populates a tree with contents during the help assembly
200 public class EcmaProvider : Provider {
201 ArrayList/*<string>*/ asm_dirs = new ArrayList ();
203 public EcmaProvider ()
207 public EcmaProvider (string base_directory)
209 AddDirectory (base_directory);
212 public void AddDirectory (string directory)
214 if (!Directory.Exists (directory))
215 throw new FileNotFoundException (String.Format ("The directory `{0}' does not exist", directory));
216 asm_dirs.Add (directory);
219 public override void PopulateTree (Tree tree)
221 ArrayList ns_dirs = new ArrayList ();
222 foreach (string asm in asm_dirs) {
223 ns_dirs.AddRange (Directory.GetDirectories (asm));
226 foreach (string ns in ns_dirs){
227 string basedir = Directory.GetParent (ns).FullName;
228 string [] files = Directory.GetFiles (ns);
232 Hashtable nsnodes = new Hashtable();
234 foreach (string file in files){
235 if (!file.EndsWith (".xml"))
238 if (ns_node == null) {
239 tn = Path.GetFileName (ns);
240 tree.HelpSource.Message (TraceLevel.Info, "Processing namespace {0}", tn);
241 ns_node = tree.LookupNode (tn, "N:" + tn);
242 string ns_summary_file = EcmaDoc.GetNamespaceFile (basedir, tn);
244 nsnodes[ns_node] = nsnodes;
246 if (File.Exists (ns_summary_file)) {
247 XmlDocument nsSummaryFile = new XmlDocument ();
248 nsSummaryFile.Load (ns_summary_file);
249 namespace_realpath [tn] = ns_summary_file;
251 XmlNode ns_summary = nsSummaryFile.SelectSingleNode ("Namespace/Docs/summary");
252 if (ns_summary != null && ns_summary.InnerText.Trim () != "To be added." && ns_summary.InnerText != "") {
253 namespace_summaries [tn] = detached.ImportNode (ns_summary, true);
254 namespace_remarks [tn] = detached.ImportNode (nsSummaryFile.SelectSingleNode ("Namespace/Docs/remarks"), true);
257 } else if (!namespace_summaries.ContainsKey (tn)) {
258 namespace_summaries [tn] = null;
259 namespace_remarks [tn] = null;
262 tree.HelpSource.Message (TraceLevel.Verbose, " Processing input file {0}", Path.GetFileName (file));
264 PopulateClass (tree, tn, ns_node, file);
267 // Sort the list of types in each namespace
268 foreach (Node ns_node2 in nsnodes.Keys)
274 class TypeInfo : IComparable {
275 public string type_assembly;
276 public string type_name;
277 public string type_full;
278 public string type_kind;
279 public XmlNode type_doc;
281 public TypeInfo (string k, string a, string f, string s, XmlNode n)
290 public int CompareTo (object b)
293 TypeInfo nb = (TypeInfo) b;
295 return String.Compare (na.type_full, nb.type_full);
300 // Packs a file with the summary data
302 public override void CloseTree (HelpSource hs, Tree tree)
304 foreach (DictionaryEntry de in class_summaries){
305 XmlDocument doc = new XmlDocument ();
306 string ns = (string) de.Key;
308 ArrayList list = (ArrayList) de.Value;
311 XmlElement elements = doc.CreateElement ("elements");
312 doc.AppendChild (elements);
314 if (namespace_summaries [ns] != null)
315 elements.AppendChild (doc.ImportNode ((XmlNode)namespace_summaries [ns],true));
317 elements.AppendChild (doc.CreateElement("summary"));
319 if (namespace_remarks [ns] != null)
320 elements.AppendChild (doc.ImportNode ((XmlNode)namespace_remarks [ns],true));
322 elements.AppendChild (doc.CreateElement("remarks"));
324 hs.Message (TraceLevel.Info, "Have {0} elements in the {1}", list.Count, ns);
325 foreach (TypeInfo p in list){
328 switch (p.type_kind){
330 e = doc.CreateElement ("class");
334 e = doc.CreateElement ("enum");
338 e = doc.CreateElement ("struct");
342 e = doc.CreateElement ("delegate");
346 e = doc.CreateElement ("interface");
350 e.SetAttribute ("name", p.type_name);
351 e.SetAttribute ("fullname", p.type_full);
352 e.SetAttribute ("assembly", p.type_assembly);
353 XmlNode copy = doc.ImportNode (p.type_doc, true);
354 e.AppendChild (copy);
355 elements.AppendChild (e);
357 hs.PackXml ("xml.summary." + ns, doc,(string) namespace_realpath[ns]);
361 XmlDocument nsSummary = new XmlDocument ();
362 XmlElement root = nsSummary.CreateElement ("elements");
363 nsSummary.AppendChild (root);
365 foreach (DictionaryEntry de in namespace_summaries) {
366 XmlNode n = (XmlNode)de.Value;
367 XmlElement summary = nsSummary.CreateElement ("namespace");
368 summary.SetAttribute ("ns", (string)de.Key);
369 root.AppendChild (summary);
371 summary.AppendChild (nsSummary.ImportNode (n,true));
373 summary.AppendChild (nsSummary.CreateElement("summary"));
375 tree.HelpSource.PackXml ("mastersummary.xml", nsSummary, null);
376 AddExtensionMethods (tree);
377 AddImageFiles (hs, tree);
380 void AddExtensionMethods (Tree tree)
382 XmlDocument extensions = null;
385 foreach (string asm in asm_dirs) {
386 string overview_file = Path.Combine (asm, "index.xml");
387 if (File.Exists (overview_file)) {
388 XmlDocument overview = new XmlDocument ();
389 overview.Load (overview_file);
390 XmlNodeList e = overview.SelectNodes ("/Overview/ExtensionMethods/*");
392 if (extensions == null) {
393 extensions = new XmlDocument ();
394 root = extensions.CreateElement ("ExtensionMethods");
395 extensions.AppendChild (root);
397 foreach (XmlNode n in e) {
399 root.AppendChild (extensions.ImportNode (n, true));
404 if (extensions != null) {
405 tree.HelpSource.Message (TraceLevel.Info, "Have {0} extension methods", numMethods);
406 tree.HelpSource.PackXml ("ExtensionMethods.xml", extensions, "ExtensionMethods.xml");
410 void AddImageFiles (HelpSource hs, Tree tree)
412 foreach (string asm in asm_dirs) {
413 string path = Path.Combine (asm, "_images");
414 if (!Directory.Exists (path))
418 foreach (var img in Directory.GetFiles (path))
420 foreach (var img in Directory.EnumerateFiles (path))
422 hs.PackFile (img, Path.GetFileName (img));
426 Hashtable/*<string, List<TypeInfo>>*/ class_summaries = new Hashtable ();
427 Hashtable/*<string, XmlNode>*/ namespace_summaries = new Hashtable ();
428 Hashtable/*<string, XmlNode>*/ namespace_remarks = new Hashtable ();
429 Hashtable/*<string, string -- path>*/ namespace_realpath = new Hashtable ();
431 XmlDocument detached = new XmlDocument ();
433 void PopulateClass (Tree tree, string ns, Node ns_node, string file)
435 XmlDocument doc = new XmlDocument ();
438 string name = EcmaDoc.GetClassName (doc);
439 string assembly = EcmaDoc.GetClassAssembly (doc);
440 string kind = EcmaDoc.GetTypeKind (doc);
441 string full = EcmaDoc.GetFullClassName (doc);
444 string file_code = ns_node.tree.HelpSource.PackFile (file);
446 XmlNode class_summary = detached.ImportNode (doc.SelectSingleNode ("/Type/Docs/summary"), true);
447 ArrayList l = (ArrayList) class_summaries [ns];
449 l = new ArrayList ();
450 class_summaries [ns] = (object) l;
452 l.Add (new TypeInfo (kind, assembly, full, name, class_summary));
454 class_node = ns_node.LookupNode (String.Format ("{0} {1}", name, kind), "ecma:" + file_code + "#" + name + "/");
456 if (kind == "Delegate") {
457 if (doc.SelectSingleNode("/Type/ReturnValue") == null)
458 tree.HelpSource.Message (TraceLevel.Error, "Delegate " + name + " does not have a ReturnValue node. See the ECMA-style updates.");
461 if (kind == "Enumeration")
464 if (kind == "Delegate")
468 // Always add the Members node
470 class_node.CreateNode ("Members", "*");
472 PopulateMember (doc, name, class_node, "Constructor", "Constructors");
473 PopulateMember (doc, name, class_node, "Method", "Methods");
474 PopulateMember (doc, name, class_node, "Property", "Properties");
475 PopulateMember (doc, name, class_node, "Field", "Fields");
476 PopulateMember (doc, name, class_node, "Event", "Events");
477 PopulateMember (doc, name, class_node, "Operator", "Operators");
484 public NodeIndex (XmlNode node, int index)
491 struct NodeCompare : IComparer {
492 public int Compare (object a, object b)
494 NodeIndex na = (NodeIndex) a;
495 NodeIndex nb = (NodeIndex) b;
497 return String.Compare (na.node.Attributes ["MemberName"].InnerText,
498 nb.node.Attributes ["MemberName"].InnerText);
502 string GetMemberName (XmlNode node)
504 return node.Attributes ["MemberName"].InnerText;
508 // Performs an XPath query on the document to extract the nodes for the various members
509 // we also use some extra text to pluralize the caption
511 void PopulateMember (XmlDocument doc, string typename, Node node, string type, string caption)
513 string select = type;
514 if (select == "Operator") select = "Method";
516 XmlNodeList list1 = doc.SelectNodes (String.Format ("/Type/Members/Member[MemberType=\"{0}\"]", select));
517 ArrayList list = new ArrayList();
519 foreach (XmlElement n in list1) {
520 n.SetAttribute("assembler_index", (i++).ToString());
521 if (type == "Method" && GetMemberName(n).StartsWith("op_")) continue;
522 if (type == "Operator" && !GetMemberName(n).StartsWith("op_")) continue;
526 int count = list.Count;
532 string key = type.Substring (0, 1);
533 nodes_node = node.CreateNode (caption, key);
538 foreach (XmlElement n in list)
539 nodes_node.CreateNode (GetMemberName (n), n.GetAttribute("assembler_index"));
543 foreach (XmlElement n in list)
544 nodes_node.CreateNode (EcmaHelpSource.MakeSignature(n, typename), n.GetAttribute("assembler_index"));
547 case "Property": // properties with indexers can be overloaded too
550 foreach (XmlElement n in list) {
551 bool multiple = false;
552 foreach (XmlNode nn in list) {
553 if (n != nn && GetMemberName(n) == nn.Attributes ["MemberName"].InnerText) {
559 string group, name, sig;
560 if (type != "Operator") {
561 name = GetMemberName(n);
562 sig = EcmaHelpSource.MakeSignature(n, null);
565 EcmaHelpSource.MakeOperatorSignature(n, out name, out sig);
570 nodes_node.LookupNode (group, group)
571 .CreateNode (sig, n.GetAttribute("assembler_index"));
573 nodes_node.CreateNode (name, n.GetAttribute("assembler_index"));
577 foreach (Node n in nodes_node.Nodes) {
585 throw new InvalidOperationException();
594 // The Ecma HelpSource provider
596 public class EcmaHelpSource : HelpSource {
598 public EcmaHelpSource (string base_file, bool create) : base (base_file, create)
600 ExtObject = new ExtensionObject (this);
603 public EcmaHelpSource ()
605 ExtObject = new ExtensionObject (this);
608 static string css_ecma;
609 public static string css_ecma_code {
611 if (css_ecma != null)
614 System.Reflection.Assembly assembly = typeof(EcmaHelpSource).Assembly;
615 Stream str_css = assembly.GetManifestResourceStream ("mono-ecma.css");
616 css_ecma = (new StreamReader (str_css)).ReadToEnd();
618 css_ecma = String.Empty;
624 public override string InlineCss {
625 get {return base.InlineCss + css_ecma_code;}
629 public static string js_code {
634 System.Reflection.Assembly assembly = typeof(EcmaHelpSource).Assembly;
635 Stream str_js = assembly.GetManifestResourceStream ("helper.js");
636 js = (new StreamReader (str_js)).ReadToEnd();
644 public override string InlineJavaScript {
645 get {return js_code + base.InlineJavaScript;}
648 public override string GetPublicUrl (string url)
650 if (url == null || url.Length == 0)
654 XmlDocument d = GetXmlFromUrl (url, out rest);
656 return EcmaDoc.GetCref (d.DocumentElement);
657 XmlElement e = GetDocElement (d, rest);
659 return EcmaDoc.GetCref (d.DocumentElement) + "/" + rest;
660 return EcmaDoc.GetCref (e);
662 catch (Exception e) {
667 private static XmlElement GetDocElement (XmlDocument d, string rest)
669 string memberType = null;
670 string memberIndex = null;
672 string [] nodes = rest.Split (new char [] {'/'});
674 switch (nodes.Length) {
675 // e.g. C; not supported.
678 // e.g. C/0 or M/MethodName; the latter isn't supported.
681 // XPath wants 1-based indexes, while the url uses 0-based values.
682 memberIndex = (int.Parse (nodes [1]) + 1).ToString ();
683 memberType = GetMemberType (nodes [0]);
688 // e.g. M/MethodName/0
690 memberIndex = (int.Parse (nodes [2]) + 1).ToString ();
691 memberType = GetMemberType (nodes [0]);
697 string xpath = "/Type/Members/Member[MemberType=\"" + memberType + "\"]" +
698 "[position()=" + memberIndex + "]";
699 return (XmlElement) d.SelectSingleNode (xpath);
702 private static string GetMemberType (string type)
705 case "C": return "Constructor";
706 case "E": return "Event";
707 case "F": return "Field";
708 case "M": return "Method";
709 case "P": return "Property";
711 throw new NotSupportedException ("Member Type: '" + type + "'.");
715 public override string GetText (string url, out Node match_node)
719 string cached = GetCachedText (url);
725 XmlReader summary = GetHelpXml ("mastersummary.xml");
729 XsltArgumentList args = new XsltArgumentList();
730 args.AddExtensionObject("monodoc:///extensions", ExtObject);
731 args.AddParam("show", "", "masteroverview");
732 string s = Htmlize(summary, args);
733 return BuildHtml (css_ecma_code, js_code, s);
736 if (url.StartsWith ("ecma:")) {
737 string s = GetTextFromUrl (url);
738 return BuildHtml (css_ecma_code, js_code, s);
745 string RenderMemberLookup (string typename, string member, ref Node type_node)
747 if (type_node.Nodes == null)
750 string membername = member;
751 string[] argtypes = null;
752 if (member.IndexOf("(") > 0) {
753 membername = membername.Substring(0, member.IndexOf("("));
754 member = member.Replace("@", "&");
756 // reform the member signature with CTS names
758 string x = member.Substring(member.IndexOf("(")+1);
759 argtypes = x.Substring(0, x.Length-1).Split(',', ':'); // operator signatures have colons
761 if (membername == ".ctor")
762 membername = typename;
764 member = membername + "(";
765 for (int i = 0; i < argtypes.Length; i++) {
766 argtypes[i] = EcmaDoc.ConvertCTSName(argtypes[i]);
767 if (i > 0) member += ",";
768 member += argtypes[i];
773 // Check if a node caption matches exactly
775 bool isoperator = false;
777 if ((membername == "op_Implicit" || membername == "op_Explicit") && argtypes.Length == 2) {
779 membername = "Conversion";
780 member = argtypes[0] + " to " + argtypes[1];
781 } else if (membername.StartsWith("op_")) {
783 membername = membername.Substring(3);
786 foreach (Node x in type_node.Nodes){
789 if (isoperator && x.Caption != "Operators")
792 foreach (Node m in x.Nodes) {
793 string caption = m.Caption;
794 string ecaption = ToEscapedMemberName (caption);
796 // No overloading (usually), is just the member name. The whole thing for constructors.
797 if (caption == membername || caption == member ||
798 ecaption == membername || ecaption == member) {
800 return GetTextFromUrl (m.URL);
802 } else if (caption == member || ecaption == member) {
803 // Though there are overloads, no arguments are in the url, so use this base node
805 return GetTextFromUrl (m.URL);
807 // Check subnodes which are the overloads -- must match signature
808 foreach (Node mm in m.Nodes) {
809 ecaption = ToEscapedTypeName (mm.Caption);
810 if (mm.Caption == member || ecaption == member) {
812 return GetTextFromUrl (mm.URL);
822 public static string MakeSignature (XmlNode n, string cstyleclass)
826 if (cstyleclass == null)
827 sig = n.Attributes["MemberName"].InnerText;
833 /*if (n.SelectSingleNode("MemberType").InnerText == "Method" || n.SelectSingleNode("MemberType").InnerText == "Constructor") {*/ // properties with indexers too
834 XmlNodeList paramnodes = n.SelectNodes("Parameters/Parameter");
837 foreach (XmlNode p in paramnodes) {
838 if (!first) sig += ",";
839 string type = p.Attributes["Type"].InnerText;
840 type = EcmaDoc.ConvertCTSName(type);
850 public static void MakeOperatorSignature (XmlNode n, out string nicename, out string sig)
854 name = n.Attributes["MemberName"].InnerText;
855 nicename = name.Substring(3);
858 // unary operators: no overloading possible [ECMA-335 §10.3.1]
859 case "op_UnaryPlus": // static R operator+ (T)
860 case "op_UnaryNegation": // static R operator- (T)
861 case "op_LogicalNot": // static R operator! (T)
862 case "op_OnesComplement": // static R operator~ (T)
863 case "op_Increment": // static R operator++ (T)
864 case "op_Decrement": // static R operator-- (T)
865 case "op_True": // static bool operator true (T)
866 case "op_False": // static bool operator false (T)
867 case "op_AddressOf": // static R operator& (T)
868 case "op_PointerDereference": // static R operator* (T)
872 // binary operators: overloading is possible [ECMA-335 §10.3.2]
873 case "op_Addition": // static R operator+ (T1, T2)
874 case "op_Subtraction": // static R operator- (T1, T2)
875 case "op_Multiply": // static R operator* (T1, T2)
876 case "op_Division": // static R operator/ (T1, T2)
877 case "op_Modulus": // static R operator% (T1, T2)
878 case "op_ExclusiveOr": // static R operator^ (T1, T2)
879 case "op_BitwiseAnd": // static R operator& (T1, T2)
880 case "op_BitwiseOr": // static R operator| (T1, T2)
881 case "op_LogicalAnd": // static R operator&& (T1, T2)
882 case "op_LogicalOr": // static R operator|| (T1, T2)
883 case "op_Assign": // static R operator= (T1, T2)
884 case "op_LeftShift": // static R operator<< (T1, T2)
885 case "op_RightShift": // static R operator>> (T1, T2)
886 case "op_SignedRightShift": // static R operator>> (T1, T2)
887 case "op_UnsignedRightShift": // static R operator>>> (T1, T2)
888 case "op_Equality": // static bool operator== (T1, T2)
889 case "op_GreaterThan": // static bool operator> (T1, T2)
890 case "op_LessThan": // static bool operator< (T1, T2)
891 case "op_Inequality": // static bool operator!= (T1, T2)
892 case "op_GreaterThanOrEqual": // static bool operator>= (T1, T2)
893 case "op_LessThanOrEqual": // static bool operator<= (T1, T2)
894 case "op_UnsignedRightShiftAssignment": // static R operator>>>= (T1, T2)
895 case "op_MemberSelection": // static R operator-> (T1, T2)
896 case "op_RightShiftAssignment": // static R operator>>= (T1, T2)
897 case "op_MultiplicationAssignment": // static R operator*= (T1, T2)
898 case "op_PointerToMemberSelection": // static R operator->* (T1, T2)
899 case "op_SubtractionAssignment": // static R operator-= (T1, T2)
900 case "op_ExclusiveOrAssignment": // static R operator^= (T1, T2)
901 case "op_LeftShiftAssignment": // static R operator<<= (T1, T2)
902 case "op_ModulusAssignment": // static R operator%= (T1, T2)
903 case "op_AdditionAssignment": // static R operator+= (T1, T2)
904 case "op_BitwiseAndAssignment": // static R operator&= (T1, T2)
905 case "op_BitwiseOrAssignment": // static R operator|= (T1, T2)
906 case "op_Comma": // static R operator, (T1, T2)
907 case "op_DivisionAssignment": // static R operator/= (T1, T2)
908 default: // If all else fails, assume it can be overridden...whatever it is.
909 XmlNodeList paramnodes = n.SelectNodes("Parameters/Parameter");
910 sig = nicename + "(";
912 foreach (XmlNode p in paramnodes) {
913 if (!first) sig += ",";
914 string type = p.Attributes["Type"].InnerText;
915 type = EcmaDoc.ConvertCTSName(type);
922 // conversion operators: overloading based on parameter and return type [ECMA-335 §10.3.3]
923 case "op_Implicit": // static implicit operator R (T)
924 case "op_Explicit": // static explicit operator R (T)
925 nicename = "Conversion";
926 string arg = n.SelectSingleNode("Parameters/Parameter/@Type").InnerText;
927 string ret = n.SelectSingleNode("ReturnValue/ReturnType").InnerText;
928 sig = EcmaDoc.ConvertCTSName(arg) + " to " + EcmaDoc.ConvertCTSName(ret);
934 // This routine has to perform a lookup on a type.
936 // Example: T:System.Text.StringBuilder
938 // The prefix is the kind of opereation being requested (T:, E:, M: etc)
939 // ns is the namespace being looked up
940 // type is the type being requested
942 // This has to walk our toplevel (which is always a namespace)
943 // And then the type space, and then walk further down depending on the request
945 public override string RenderTypeLookup (string prefix, string ns, string type, string member, out Node match_node)
947 string url = GetUrlForType (prefix, ns, type, member, out match_node);
948 if (url == null) return null;
949 return GetTextFromUrl (url);
952 public virtual string GetIdFromUrl (string prefix, string ns, string type)
954 Node tmp_node = new Node (Tree, "", "");
955 string url = GetUrlForType (prefix, ns, type, null, out tmp_node);
956 if (url == null) return null;
957 return GetFile (url.Substring (5), out url);
960 public string GetUrlForType (string prefix, string ns, string type, string member, out Node match_node)
962 if (!prefix.EndsWith(":"))
963 throw new ArgumentException("prefix");
966 member = member.Replace ("#", ".").Replace ("{", "<").Replace ("}", ">");
968 // If a nested type, compare only inner type name to node list.
969 // This should be removed when the node list doesn't lose the containing type name.
970 type = ToEscapedTypeName (type.Replace("+", "."));
971 MatchAttempt[] attempts = GetAttempts (ns, type);
973 foreach (Node ns_node in Tree.Nodes){
974 string ns_node_namespace = ns_node.Element.Substring (2);
976 if (!MatchesNamespace (attempts, ns_node_namespace, out type))
979 foreach (Node type_node in ns_node.Nodes){
980 string element = type_node.Element;
983 if (element.StartsWith("T:")) {
984 cname = element.Substring(2);
986 RootTree.GetNamespaceAndType (cname, out _ns, out cname);
987 cname = ToEscapedTypeName (cname);
988 int pidx = cname.LastIndexOf (".");
989 cname = cname.Substring(pidx+1);
990 pidx = cname.LastIndexOf ("/");
992 cname = cname.Substring(0, pidx);
993 cname = cname.Replace("+", ".");
995 int pidx = element.IndexOf ("#");
996 int sidx = element.IndexOf ("/");
997 cname = element.Substring (pidx + 1, sidx-pidx-1);
998 cname = ToEscapedTypeName (cname);
1001 //Console.WriteLine ("t:{0} cn:{1} p:{2}", type, cname, prefix);
1003 if (type == cname && prefix == "T:") {
1004 match_node = type_node;
1005 return type_node.URL;
1006 } else if (type.StartsWith (cname)){
1007 int p = cname.Length;
1009 match_node = type_node;
1011 string ret = RenderMemberLookup (type, member, ref match_node);
1013 return type_node.URL;
1014 return match_node.URL;
1016 } else if (type [p] == '/'){
1018 // This handles summaries
1021 foreach (Node nd in type_node.Nodes) {
1022 if (nd.Element [nd.Element.Length - 1] == type [p + 1]) {
1028 string ret = type_node.URL;
1029 if (!ret.EndsWith("/")) ret += "/";
1030 return ret + type.Substring (p + 1);
1040 struct MatchAttempt {
1041 public string Namespace;
1044 public MatchAttempt (string ns, string t)
1051 private static MatchAttempt[] GetAttempts (string ns, string type)
1053 MatchAttempt[] attempts = new MatchAttempt [Count (ns, '.') + 1];
1055 for (int i = 0; i < ns.Length; ++i) {
1057 attempts [wns++] = new MatchAttempt (ns.Substring (0, i),
1058 ns.Substring (i+1) + "." + type);
1060 attempts [wns++] = new MatchAttempt (ns, type);
1064 private static int Count (string s, char c)
1067 for (int i = 0; i < s.Length; ++i)
1073 private static bool MatchesNamespace (MatchAttempt[] attempts, string ns, out string type)
1075 for (int i = 0; i < attempts.Length; ++i)
1076 if (ns == attempts [i].Namespace) {
1077 type = attempts [i].Type;
1084 public static string ToEscapedTypeName (string typename)
1086 return ToEscapedName (typename, "`");
1089 static string ToEscapedName (string name, string escape)
1091 StringBuilder filename = new StringBuilder (name.Length);
1096 for (int i = 0; i < name.Length; ++i) {
1108 filename.Append (escape).Append ((numArgs+1).ToString());
1119 filename.Append (c);
1123 return filename.ToString ();
1126 static string ToEscapedMemberName (string membername)
1128 return ToEscapedName (membername, "``");
1131 public override string GetNodeXPath (XPathNavigator n)
1133 if (n.Matches ("/Type/Docs/param")) {
1134 string type_name = (string) n.Evaluate ("string (ancestor::Type/@FullName)");
1135 string param_name = (string) n.Evaluate ("string (@name)");
1137 return String.Format ("/Type [@FullName = '{0}']/Docs/param[@name='{1}']", type_name, param_name);
1140 if (n.Matches ("/Type/Docs/*")) {
1141 string type_name = (string) n.Evaluate ("string (ancestor::Type/@FullName)");
1143 return String.Format ("/Type [@FullName = '{0}']/Docs/{1}", type_name, n.Name);
1146 if (n.Matches ("/Type/Members/Member/Docs/*")) {
1147 string type_name = (string) n.Evaluate ("string (ancestor::Type/@FullName)");
1148 string member_name = (string) n.Evaluate ("string (ancestor::Member/@MemberName)");
1149 string member_sig = (string) n.Evaluate ("string (ancestor::Member/MemberSignature [@Language='C#']/@Value)");
1150 string param_name = (string) n.Evaluate ("string (@name)");
1152 if (param_name == null || param_name == "") {
1153 return String.Format (
1154 "/Type [@FullName = '{0}']/Members/Member [@MemberName = '{1}'][MemberSignature [@Language='C#']/@Value = '{2}']/Docs/{3}",
1155 type_name, member_name, member_sig, n.Name);
1157 return String.Format (
1158 "/Type [@FullName = '{0}']/Members/Member [@MemberName = '{1}'][MemberSignature [@Language='C#']/@Value = '{2}']/Docs/param [@name = '{3}']",
1159 type_name, member_name, member_sig, param_name);
1163 Message (TraceLevel.Warning, "WARNING: Was not able to get clean XPath expression for node {0}", EditingUtils.GetXPath (n));
1164 return base.GetNodeXPath (n);
1167 protected virtual XmlDocument GetNamespaceDocument (string ns)
1169 return GetHelpXmlWithChanges ("xml.summary." + ns);
1172 public override string RenderNamespaceLookup (string nsurl, out Node match_node)
1174 foreach (Node ns_node in Tree.Nodes){
1175 if (ns_node.Element != nsurl)
1178 match_node = ns_node;
1179 string ns_name = nsurl.Substring (2);
1181 XmlDocument doc = GetNamespaceDocument (ns_name);
1185 XsltArgumentList args = new XsltArgumentList();
1186 args.AddExtensionObject("monodoc:///extensions", ExtObject);
1187 args.AddParam("show", "", "namespace");
1188 args.AddParam("namespace", "", ns_name);
1189 string s = Htmlize(new XmlNodeReader (doc), args);
1190 return BuildHtml (css_ecma_code, js_code, s);
1197 private string SelectString(XmlNode node, string xpath) {
1198 XmlNode ret = node.SelectSingleNode(xpath);
1199 if (ret == null) return "";
1200 return ret.InnerText;
1202 private int SelectCount(XmlNode node, string xpath) {
1203 return node.SelectNodes(xpath).Count;
1207 // Returns the XmlDocument from the given url, and fills in `rest'
1209 protected virtual XmlDocument GetXmlFromUrl(string url, out string rest) {
1211 url = url.Substring (5);
1212 string file = GetFile (url, out rest);
1214 // Console.WriteLine ("Got [{0}] and [{1}]", file, rest);
1215 return GetHelpXmlWithChanges (file);
1218 string GetTextFromUrl (string url)
1221 string path = XmlDocUtils.GetCachedFileName (base_dir, url);
1222 if (File.Exists (path))
1223 return File.OpenText (path).ReadToEnd ();
1230 XmlDocument doc = GetXmlFromUrl (url, out rest);
1232 // Load base-type information so the stylesheet can draw the inheritance
1233 // tree and (optionally) include inherited members in the members list.
1234 XmlElement basenode = (XmlElement)doc.SelectSingleNode("Type/Base");
1235 XmlElement membersnode = (XmlElement)doc.SelectSingleNode("Type/Members");
1236 XmlNode basetype = doc.SelectSingleNode("Type/Base/BaseTypeName");
1238 while (basetype != null) {
1239 // Add the ParentType node to Type/Base
1240 XmlElement inheritancenode = doc.CreateElement("ParentType");
1241 inheritancenode.SetAttribute("Type", basetype.InnerText);
1242 inheritancenode.SetAttribute("Order", (baseindex++).ToString());
1243 basenode.AppendChild(inheritancenode);
1245 // Load the base type XML data
1246 int dot = basetype.InnerText.LastIndexOf('.');
1247 string ns = basetype.InnerText.Substring(0, dot == -1 ? 0 : dot);
1248 string n = basetype.InnerText.Substring(dot == -1 ? 0 : dot+1);
1249 string basetypeurl = GetUrlForType("T:", ns, n, null, out node);
1250 XmlDocument basetypedoc = null;
1251 if (basetypeurl != null)
1252 basetypedoc = GetXmlFromUrl (basetypeurl, out rest2);
1253 if (basetypedoc == null) {
1254 inheritancenode.SetAttribute("Incomplete", "1");
1258 if (SettingsHandler.Settings.ShowInheritedMembers) {
1259 // Add inherited members
1260 foreach (XmlElement member in basetypedoc.SelectNodes("Type/Members/Member[not(MemberType='Constructor')]")) {
1261 string sig = SelectString(member, "MemberSignature[@Language='C#']/@Value");
1262 if (sig.IndexOf(" static ") >= 0) continue;
1264 // If the signature of member matches the signature of a member already in the XML,
1266 string xpath = "@MemberName='" + SelectString(member, "@MemberName") + "'";
1267 xpath += " and ReturnValue/ReturnType='" + SelectString(member, "ReturnValue/ReturnType") + "'";
1268 xpath += " and count(Parameters/Parameter)=" + SelectCount(member, "Parameters/Parameter") + "";
1270 foreach (XmlElement p in member.SelectNodes("Parameters/Parameter")) {
1271 xpath += " and Parameters/Parameter[position()=" + pi + "]/@Type = '" + p.GetAttribute("Type") + "'";
1275 // If a signature match is found, don't add.
1276 XmlNode match = membersnode.SelectSingleNode("Member[" + xpath + "]");
1280 member.SetAttribute("DeclaredIn", basetype.InnerText);
1281 membersnode.AppendChild(membersnode.OwnerDocument.ImportNode(member, true));
1285 basetype = basetypedoc.SelectSingleNode("Type/Base/BaseTypeName");
1287 ArrayList extensions = new ArrayList ();
1288 AddExtensionMethodsFromHelpSource (extensions, this);
1289 foreach (HelpSource hs in RootTree.HelpSources) {
1290 EcmaHelpSource es = hs as EcmaHelpSource;
1295 AddExtensionMethodsFromHelpSource (extensions, es);
1297 XmlDocUtils.AddExtensionMethods (doc, extensions, delegate (string s) {
1298 s = s.StartsWith ("T:") ? s : "T:" + s;
1299 return RootTree.GetHelpXml (s);
1302 XsltArgumentList args = new XsltArgumentList();
1304 args.AddExtensionObject("monodoc:///extensions", ExtObject);
1306 args.AddParam ("source-id", "", SourceID.ToString ());
1309 args.AddParam("show", "", "typeoverview");
1310 string s = Htmlize(new XmlNodeReader (doc), args);
1311 return BuildHtml (css_ecma_code, js_code, s);
1314 string [] nodes = rest.Split (new char [] {'/'});
1316 switch (nodes.Length) {
1318 args.AddParam("show", "", "members");
1319 args.AddParam("index", "", "all");
1322 // Could either be a single member, or an overload thingy
1324 int.Parse (nodes [1]); // is it an int
1326 args.AddParam("show", "", "member");
1327 args.AddParam("index", "", nodes [1]);
1329 args.AddParam("show", "", "overloads");
1330 args.AddParam("index", "", nodes [1]);
1334 args.AddParam("show", "", "member");
1335 args.AddParam("index", "", nodes [2]);
1338 return "What the hell is this URL " + url;
1343 args.AddParam("membertype", "", "Constructor");
1346 args.AddParam("membertype", "", "Method");
1349 args.AddParam("membertype", "", "Property");
1352 args.AddParam("membertype", "", "Field");
1355 args.AddParam("membertype", "", "Event");
1358 args.AddParam("membertype", "", "Operator");
1361 args.AddParam("membertype", "", "ExtensionMethod");
1364 args.AddParam("membertype", "", "All");
1367 return "Unknown url: " + url;
1370 string html = Htmlize(new XmlNodeReader (doc), args);
1371 return BuildHtml (css_ecma_code, js_code, html);
1374 void AddExtensionMethodsFromHelpSource (ArrayList extensions, EcmaHelpSource es)
1376 Stream s = es.GetHelpStream ("ExtensionMethods.xml");
1378 XmlDocument d = new XmlDocument ();
1380 foreach (XmlNode n in d.SelectNodes ("/ExtensionMethods/*")) {
1387 public override void RenderPreviewDocs (XmlNode newNode, XmlWriter writer)
1389 XsltArgumentList args = new XsltArgumentList ();
1390 args.AddExtensionObject ("monodoc:///extensions", ExtObject);
1392 Htmlize (new XmlNodeReader (newNode), args, writer);
1395 static XslCompiledTransform ecma_transform;
1397 public string Htmlize (XmlReader ecma_xml)
1399 return Htmlize(ecma_xml, null);
1402 public string Htmlize (XmlReader ecma_xml, XsltArgumentList args)
1406 var output = new StringBuilder ();
1407 ecma_transform.Transform (ecma_xml,
1409 XmlWriter.Create (output, ecma_transform.OutputSettings),
1410 CreateDocumentResolver ());
1411 return output.ToString ();
1414 protected virtual XmlResolver CreateDocumentResolver ()
1416 // results in using XmlUrlResolver
1420 static void Htmlize (XmlReader ecma_xml, XsltArgumentList args, XmlWriter w)
1424 if (ecma_xml == null)
1427 ecma_transform.Transform (ecma_xml, args, w, null);
1430 static XslCompiledTransform ecma_transform_css, ecma_transform_no_css;
1431 static void EnsureTransform ()
1433 if (ecma_transform == null) {
1434 ecma_transform_css = new XslCompiledTransform ();
1435 ecma_transform_no_css = new XslCompiledTransform ();
1436 Assembly assembly = System.Reflection.Assembly.GetCallingAssembly ();
1438 Stream stream = assembly.GetManifestResourceStream ("mono-ecma-css.xsl");
1439 XmlReader xml_reader = new XmlTextReader (stream);
1440 XmlResolver r = new ManifestResourceResolver (".");
1441 ecma_transform_css.Load (xml_reader, XsltSettings.TrustedXslt, r);
1443 stream = assembly.GetManifestResourceStream ("mono-ecma.xsl");
1444 xml_reader = new XmlTextReader (stream);
1445 ecma_transform_no_css.Load (xml_reader, XsltSettings.TrustedXslt, r);
1448 ecma_transform = ecma_transform_css;
1450 ecma_transform = ecma_transform_no_css;
1453 // This ExtensionObject stuff is used to check at run time whether
1454 // types and members have been implemented and whether there are any
1455 // MonoTODO attributes left on them.
1457 public readonly ExtensionObject ExtObject;
1458 public class ExtensionObject {
1459 readonly EcmaHelpSource hs;
1462 // We are setting this to quiet now, as we need to transition
1463 // monodoc to run with the 2.x runtime and provide accurate
1464 // information in those cases.
1468 public ExtensionObject (EcmaHelpSource hs)
1473 public string Colorize(string code, string lang) {
1474 return(Mono.Utilities.Colorizer.Colorize(code,lang));
1476 // Used by stylesheet to nicely reformat the <see cref=> tags.
1477 public string MakeNiceSignature(string sig, string contexttype)
1485 sig = sig.Substring(2);
1488 case 'N': return sig;
1489 case 'T': return ShortTypeName(sig, contexttype);
1491 case 'C': case 'M': case 'P': case 'F': case 'E':
1492 string type, mem, arg;
1496 if (s == 'C' || s == 'M')
1497 paren = sig.IndexOf("(");
1499 paren = sig.IndexOf("[");
1503 if (paren > 0 && paren < sig.Length-1) {
1504 string[] args = sig.Substring(paren+1, sig.Length-paren-2).Split(',');
1505 for (int i = 0; i < args.Length; i++)
1506 args[i] = ShortTypeName(args[i], contexttype);
1507 arg = "(" + String.Join(", ", args) + ")";
1508 sig = sig.Substring(0, paren);
1513 // Get type and member names
1514 int dot = sig.LastIndexOf(".");
1515 if (s == 'C' || dot <= 0 || dot == sig.Length-1) {
1519 type = sig.Substring(0, dot);
1520 mem = sig.Substring(dot);
1523 type = ShortTypeName(type, contexttype);
1525 return type + mem + arg;
1532 public string EditUrl (XPathNodeIterator itr)
1534 if (itr.MoveNext ())
1535 return hs.GetEditUri (itr.Current);
1540 public string EditUrlNamespace (XPathNodeIterator itr, string ns, string section)
1542 if (hs is EcmaUncompiledHelpSource)
1543 return "edit:file:" + Path.Combine(((EcmaUncompiledHelpSource)hs).BasePath, ns + ".xml") + "@/Namespace/Docs/" + section;
1544 else if (itr.MoveNext ())
1545 return EditingUtils.FormatEditUri(itr.Current.BaseURI, "/elements/" + section);
1549 private static string ShortTypeName(string name, string contexttype)
1551 int dot = contexttype.LastIndexOf(".");
1552 if (dot < 0) return name;
1553 string contextns = contexttype.Substring(0, dot+1);
1555 if (name == contexttype)
1556 return name.Substring(dot+1);
1558 if (name.StartsWith(contextns))
1559 return name.Substring(contextns.Length);
1561 return name.Replace("+", ".");
1564 public string MonoImpInfo(string assemblyname, string typename, string membername, string arglist, bool strlong)
1569 ArrayList a = new ArrayList();
1570 if (arglist != "") a.Add(arglist);
1571 return MonoImpInfo(assemblyname, typename, membername, a, strlong);
1574 public string MonoImpInfo(string assemblyname, string typename, string membername, XPathNodeIterator itr, bool strlong)
1579 ArrayList rgs = new ArrayList ();
1580 while (itr.MoveNext ())
1581 rgs.Add (itr.Current.Value);
1583 return MonoImpInfo (assemblyname, typename, membername, rgs, strlong);
1586 public string MonoImpInfo(string assemblyname, string typename, string membername, ArrayList arglist, bool strlong)
1589 Assembly assembly = null;
1592 assembly = Assembly.LoadWithPartialName(assemblyname);
1593 } catch (Exception) {
1597 if (assembly == null) {
1598 /*if (strlong) return "The assembly " + assemblyname + " is not available to MonoDoc.";
1600 return ""; // silently ignore
1603 Type t = assembly.GetType(typename, false);
1606 return typename + " has not been implemented.";
1608 return "Not implemented.";
1611 // The following code is flakey and fails to find existing members
1614 MemberInfo[] mis = t.GetMember(membername, BF.Static | BF.Instance | BF.Public | BF.NonPublic);
1616 if (mis.Length == 0)
1617 return "This member has not been implemented.";
1618 if (mis.Length == 1)
1619 return MonoImpInfo(mis[0], "member", strlong);
1621 // Scan for the appropriate member
1622 foreach (MemberInfo mi in mis) {
1623 System.Reflection.ParameterInfo[] pis;
1625 if (mi is MethodInfo || mi is ConstructorInfo)
1626 pis = ((MethodBase)mi).GetParameters();
1627 else if (mi is PropertyInfo)
1628 pis = ((PropertyInfo)mi).GetIndexParameters();
1634 if (pis.Length != arglist.Count) continue;
1635 for (int i = 0; i < pis.Length; i++) {
1636 if (pis[i].ParameterType.FullName != (string)arglist[i]) { good = false; break; }
1638 if (!good) continue;
1641 return MonoImpInfo(mi, "member", strlong);
1644 } catch (Exception) {
1649 public string MonoImpInfo(System.Reflection.MemberInfo mi, string itemtype, bool strlong)
1656 object[] atts = mi.GetCustomAttributes(true);
1658 foreach (object att in atts) if (att.GetType().Name == "MonoTODOAttribute") todoctr++;
1662 s = "This " + itemtype + " is marked as being unfinished.<BR/>\n";
1670 public string MonoImpInfo(string assemblyname, string typename, bool strlong)
1676 if (assemblyname == "")
1679 Assembly assembly = Assembly.LoadWithPartialName(assemblyname);
1680 if (assembly == null)
1683 Type t = assembly.GetType(typename, false);
1686 return typename + " has not been implemented.";
1688 return "Not implemented.";
1691 string s = MonoImpInfo(t, "type", strlong);
1694 MemberInfo[] mis = t.GetMembers(BF.Static | BF.Instance | BF.Public | BF.NonPublic);
1696 // Scan members for MonoTODO attributes
1698 foreach (MemberInfo mi in mis) {
1699 string mii = MonoImpInfo(mi, null, false);
1700 if (mii != "") mctr++;
1703 s += "This type has " + mctr + " members that are marked as unfinished.<BR/>";
1709 } catch (Exception) {
1714 public bool MonoEditing ()
1716 return SettingsHandler.Settings.EnableEditing;
1719 public bool IsToBeAdded(string text) {
1720 return text.StartsWith("To be added");
1725 // This takes one of the ecma urls, which look like this:
1726 // ecma:NUMERIC_ID#OPAQUE/REST
1728 // NUMERIC_ID is the numeric ID assigned by the compressor
1729 // OPAQUE is opaque for node rendering (it typically contains T:System.Byte for example)
1730 // REST is the rest of the argument used to decode information
1732 static string GetFile (string url, out string rest)
1734 int pound = url.IndexOf ("#");
1735 int slash = url.IndexOf ("/");
1737 string fname = url.Substring (0, pound);
1738 rest = url.Substring (slash+1);
1744 // This should have a little cache or something.
1745 static XmlDocument GetDocument (HelpSource hs, string fname)
1747 Stream s = hs.GetHelpStream (fname);
1749 Error ("Could not fetch document {0}", fname);
1753 XmlDocument doc = new XmlDocument ();
1761 string GetKindFromCaption (string s)
1763 int p = s.LastIndexOf (' ');
1765 return s.Substring (p + 1);
1770 // Obtain an URL of the type T:System.Object from the node
1772 public static string GetNiceUrl (Node node) {
1773 if (node.Element.StartsWith("N:"))
1774 return node.Element;
1776 int bk_pos = node.Caption.IndexOf (' ');
1777 // node from an overview
1779 name = node.Caption.Substring (0, bk_pos);
1780 full = node.Parent.Caption + "." + name.Replace ('.', '+');
1783 // node that lists constructors, methods, fields, ...
1784 if ((node.Caption == "Constructors") || (node.Caption == "Fields") || (node.Caption == "Events")
1785 || (node.Caption == "Members") || (node.Caption == "Properties") || (node.Caption == "Methods")
1786 || (node.Caption == "Operators")) {
1787 bk_pos = node.Parent.Caption.IndexOf (' ');
1788 name = node.Parent.Caption.Substring (0, bk_pos);
1789 full = node.Parent.Parent.Caption + "." + name.Replace ('.', '+');
1790 return "T:" + full + "/" + node.Element;
1792 int pr_pos = node.Caption.IndexOf ('(');
1793 // node from a constructor
1794 if (node.Parent.Element == "C") {
1795 name = node.Parent.Parent.Parent.Caption;
1796 int idx = node.URL.IndexOf ('/');
1797 return node.URL[idx+1] + ":" + name + "." + node.Caption.Replace ('.', '+');
1798 // node from a method with one signature, field, property, operator
1799 } else if (pr_pos == -1) {
1800 bk_pos = node.Parent.Parent.Caption.IndexOf (' ');
1801 name = node.Parent.Parent.Caption.Substring (0, bk_pos);
1802 full = node.Parent.Parent.Parent.Caption + "." + name.Replace ('.', '+');
1803 int idx = node.URL.IndexOf ('/');
1804 return node.URL[idx+1] + ":" + full + "." + node.Caption;
1805 // node from a method with several signatures
1807 bk_pos = node.Parent.Parent.Parent.Caption.IndexOf (' ');
1808 name = node.Parent.Parent.Parent.Caption.Substring (0, bk_pos);
1809 full = node.Parent.Parent.Parent.Parent.Caption + "." + name.Replace ('.', '+');
1810 int idx = node.URL.IndexOf ('/');
1811 return node.URL[idx+1] + ":" + full + "." + node.Caption;
1815 public override Stream GetImage (string url)
1817 if (url.Contains ("/"))
1818 url = url.Substring (url.LastIndexOf ('/') + 1);
1819 return GetHelpStream (url);
1823 // Populates the index.
1825 public override void PopulateIndex (IndexMaker index_maker)
1827 foreach (Node ns_node in Tree.Nodes){
1828 foreach (Node type_node in ns_node.Nodes){
1829 string typename = type_node.Caption.Substring (0, type_node.Caption.IndexOf (' '));
1830 string full = ns_node.Caption + "." + typename;
1832 string doc_tag = GetKindFromCaption (type_node.Caption);
1833 string url = "T:" + full;
1835 if (doc_tag == "Class" || doc_tag == "Structure" || doc_tag == "Interface"){
1837 index_maker.Add (type_node.Caption, typename, url);
1838 index_maker.Add (full + " " + doc_tag, full, url);
1840 foreach (Node c in type_node.Nodes){
1842 case "Constructors":
1843 index_maker.Add (" constructors", typename+"0", url + "/C");
1846 index_maker.Add (" fields", typename+"1", url + "/F");
1849 index_maker.Add (" events", typename+"2", url + "/E");
1852 index_maker.Add (" properties", typename+"3", url + "/P");
1855 index_maker.Add (" methods", typename+"4", url + "/M");
1858 index_maker.Add (" operators", typename+"5", url + "/O");
1864 // Now repeat, but use a different sort key, to make sure we come after
1865 // the summary data above, start the counter at 6
1867 string keybase = typename + "6.";
1869 foreach (Node c in type_node.Nodes){
1871 case "Constructors":
1874 foreach (Node nc in c.Nodes){
1875 string res = nc.Caption;
1877 string nurl = String.Format ("F:{0}.{1}", full, res);
1878 index_maker.Add (String.Format ("{0}.{1} field", typename, res),
1879 keybase + res, nurl);
1880 index_maker.Add (String.Format ("{0} field", res), res, nurl);
1885 foreach (Node nc in c.Nodes){
1886 string res = nc.Caption;
1887 string nurl = String.Format ("E:{0}.{1}", full, res);
1889 index_maker.Add (String.Format ("{0}.{1} event", typename, res),
1890 keybase + res, nurl);
1891 index_maker.Add (String.Format ("{0} event", res), res, nurl);
1895 foreach (Node nc in c.Nodes){
1896 string res = nc.Caption;
1897 string nurl = String.Format ("P:{0}.{1}", full, res);
1898 index_maker.Add (String.Format ("{0}.{1} property", typename, res),
1899 keybase + res, nurl);
1900 index_maker.Add (String.Format ("{0} property", res), res, nurl);
1904 foreach (Node nc in c.Nodes){
1905 string res = nc.Caption;
1906 int p = res.IndexOf ("(");
1908 res = res.Substring (0, p);
1909 string nurl = String.Format ("M:{0}.{1}", full, res);
1910 index_maker.Add (String.Format ("{0}.{1} method", typename, res),
1911 keybase + res, nurl);
1912 index_maker.Add (String.Format ("{0} method", res), res, nurl);
1917 foreach (Node nc in c.Nodes){
1918 string res = nc.Caption;
1919 string nurl = String.Format ("O:{0}.{1}", full, res);
1920 index_maker.Add (String.Format ("{0}.{1} operator", typename, res),
1921 keybase + res, nurl);
1926 } else if (doc_tag == "Enumeration"){
1928 // Enumerations: add the enumeration values
1930 index_maker.Add (type_node.Caption, typename, url);
1931 index_maker.Add (full + " " + doc_tag, full, url);
1933 // Now, pull the values.
1935 XmlDocument x = GetXmlFromUrl (type_node.URL, out rest);
1939 XmlNodeList members = x.SelectNodes ("/Type/Members/Member");
1941 if (members == null)
1944 foreach (XmlNode member_node in members){
1945 string enum_value = member_node.Attributes ["MemberName"].InnerText;
1946 string caption = enum_value + " value";
1947 index_maker.Add (caption, caption, url);
1949 } else if (doc_tag == "Delegate"){
1950 index_maker.Add (type_node.Caption, typename, url);
1951 index_maker.Add (full + " " + doc_tag, full, url);
1957 // Create list of documents for searching
1959 public override void PopulateSearchableIndex (IndexWriter writer)
1962 foreach (Node ns_node in Tree.Nodes) {
1963 Message (TraceLevel.Info, "\tNamespace: {0} ({1})", ns_node.Caption, ns_node.Nodes.Count);
1964 foreach (Node type_node in ns_node.Nodes) {
1965 string typename = type_node.Caption.Substring (0, type_node.Caption.IndexOf (' '));
1966 string full = ns_node.Caption + "." + typename;
1967 string doc_tag = GetKindFromCaption (type_node.Caption);
1968 string url = "T:" + full;
1970 XmlDocument xdoc = GetXmlFromUrl (type_node.URL, out rest);
1975 // For classes, structures or interfaces add a doc for the overview and
1976 // add a doc for every constructor, method, event, ...
1978 if (doc_tag == "Class" || doc_tag == "Structure" || doc_tag == "Interface"){
1980 // Adds a doc for every overview of every type
1981 SearchableDocument doc = new SearchableDocument ();
1982 doc.title = type_node.Caption;
1983 doc.hottext = typename;
1985 doc.fulltitle = full;
1987 XmlNode node_sel = xdoc.SelectSingleNode ("/Type/Docs");
1988 text = new StringBuilder ();
1989 GetTextFromNode (node_sel, text);
1990 doc.text = text.ToString ();
1992 text = new StringBuilder ();
1993 GetExamples (node_sel, text);
1994 doc.examples = text.ToString ();
1996 writer.AddDocument (doc.LuceneDoc);
1998 //Add docs for contructors, methods, etc.
1999 foreach (Node c in type_node.Nodes) { // c = Constructors || Fields || Events || Properties || Methods || Operators
2001 if (c.Element == "*")
2004 const float innerTypeBoost = 0.2f;
2006 foreach (Node nc in c.Nodes) {
2007 // Disable constructors indexing as it's often "polluting" search queries
2008 // because it has the same hottext than standard types
2009 if (c.Caption == "Constructors")
2011 //xpath to the docs xml node
2013 if (c.Caption == "Constructors")
2014 xpath = String.Format ("/Type/Members/Member[{0}]/Docs", i++);
2015 else if (c.Caption == "Operators")
2016 xpath = String.Format ("/Type/Members/Member[@MemberName='op_{0}']/Docs", nc.Caption);
2018 xpath = String.Format ("/Type/Members/Member[@MemberName='{0}']/Docs", nc.Caption);
2019 //construct url of the form M:Array.Sort
2021 if (c.Caption == "Constructors")
2022 urlnc = String.Format ("{0}:{1}.{2}", c.Caption[0], ns_node.Caption, nc.Caption);
2024 urlnc = String.Format ("{0}:{1}.{2}.{3}", c.Caption[0], ns_node.Caption, typename, nc.Caption);
2027 SearchableDocument doc_nod = new SearchableDocument ();
2028 doc_nod.title = LargeName (nc);
2029 switch (c.Caption[0]) {
2031 doc_nod.title += " Method";
2034 doc_nod.title += " Property";
2037 doc_nod.title += " Event";
2040 doc_nod.title += " Operator";
2045 doc_nod.fulltitle = string.Format ("{0}.{1}::{2}", ns_node.Caption, typename, nc.Caption);
2046 //dont add the parameters to the hottext
2047 int ppos = nc.Caption.IndexOf ('(');
2049 doc_nod.hottext = nc.Caption.Substring (0, ppos);
2051 doc_nod.hottext = nc.Caption;
2053 doc_nod.url = urlnc;
2055 XmlNode xmln = xdoc.SelectSingleNode (xpath);
2057 Error ("Problem: {0}, with xpath: {1}", urlnc, xpath);
2061 text = new StringBuilder ();
2062 GetTextFromNode (xmln, text);
2063 doc_nod.text = text.ToString ();
2065 text = new StringBuilder ();
2066 GetExamples (xmln, text);
2067 doc_nod.examples = text.ToString ();
2069 Document lucene_doc = doc_nod.LuceneDoc;
2070 lucene_doc.SetBoost (innerTypeBoost);
2071 writer.AddDocument (lucene_doc);
2075 // Enumerations: add the enumeration values
2077 } else if (doc_tag == "Enumeration"){
2079 XmlNodeList members = xdoc.SelectNodes ("/Type/Members/Member");
2080 if (members == null)
2083 text = new StringBuilder ();
2084 foreach (XmlNode member_node in members) {
2085 string enum_value = member_node.Attributes ["MemberName"].InnerText;
2086 text.Append (enum_value);
2088 GetTextFromNode (member_node["Docs"], text);
2091 SearchableDocument doc = new SearchableDocument ();
2093 text = new StringBuilder ();
2094 GetExamples (xdoc.SelectSingleNode ("/Type/Docs"), text);
2095 doc.examples = text.ToString ();
2097 doc.title = type_node.Caption;
2098 doc.hottext = xdoc.DocumentElement.Attributes["Name"].Value;
2100 doc.text = text.ToString();
2101 writer.AddDocument (doc.LuceneDoc);
2105 } else if (doc_tag == "Delegate"){
2106 SearchableDocument doc = new SearchableDocument ();
2107 doc.title = type_node.Caption;
2108 doc.hottext = xdoc.DocumentElement.Attributes["Name"].Value;
2111 XmlNode node_sel = xdoc.SelectSingleNode ("/Type/Docs");
2113 text = new StringBuilder ();
2114 GetTextFromNode (node_sel, text);
2115 doc.text = text.ToString();
2117 text = new StringBuilder ();
2118 GetExamples (node_sel, text);
2119 doc.examples = text.ToString();
2121 writer.AddDocument (doc.LuceneDoc);
2128 // Extract the interesting text from the docs node
2130 void GetTextFromNode (XmlNode n, StringBuilder sb)
2132 //don't include example code
2133 if (n.Name == "code")
2136 //include the url to which points the see tag
2137 if (n.Name == "see" && n.Attributes.Count > 0)
2138 sb.Append (n.Attributes [0].Value);
2140 //include the name of the parameter
2141 if (n.Name == "paramref" && n.Attributes.Count > 0)
2142 sb.Append (n.Attributes [0].Value);
2144 //include the contents for the node that contains text
2145 if (n.NodeType == XmlNodeType.Text)
2146 sb.Append (n.Value);
2148 //add the rest of xml tags recursively
2149 if (n.HasChildNodes)
2150 foreach (XmlNode n_child in n.ChildNodes)
2151 GetTextFromNode (n_child, sb);
2154 // Extract the code nodes from the docs
2156 void GetExamples (XmlNode n, StringBuilder sb)
2158 if (n.Name == "code") {
2159 sb.Append (n.InnerText);
2161 if (n.HasChildNodes)
2162 foreach (XmlNode n_child in n.ChildNodes)
2163 GetExamples (n_child, sb);
2167 // Extract a large name for the Node
2168 // (copied from mono-tools/docbrowser/browser.Render()
2169 static string LargeName (Node matched_node)
2171 string[] parts = matched_node.URL.Split('/', '#');
2172 if(parts.Length == 3 && parts[2] != String.Empty) { //List of Members, properties, events, ...
2173 return parts[1] + ": " + matched_node.Caption;
2174 } else if(parts.Length >= 4) { //Showing a concrete Member, property, ...
2175 return parts[1] + "." + matched_node.Caption;
2177 return matched_node.Caption;
2183 public class EcmaUncompiledHelpSource : EcmaHelpSource {
2184 readonly DirectoryInfo basedir;
2185 readonly XmlDocument basedoc;
2187 public new readonly string Name;
2188 public readonly string BasePath;
2190 public EcmaUncompiledHelpSource (string base_file) : base ()
2192 Message (TraceLevel.Info, "Loading uncompiled help from " + base_file);
2194 basedir = new DirectoryInfo(base_file);
2195 BasePath = basedir.FullName;
2197 basedoc = new XmlDocument();
2198 basedoc.Load(Path.Combine(basedir.FullName, "index.xml"));
2200 Name = basedoc.SelectSingleNode("Overview/Title").InnerText;
2202 bool has_content = false;
2204 foreach (XmlElement ns in basedoc.SelectNodes("Overview/Types/Namespace")) {
2206 Node nsnode = Tree.CreateNode(ns.GetAttribute("Name"), "N:" + ns.GetAttribute("Name"));
2208 bool has_types = false;
2209 foreach (XmlElement t in ns.SelectNodes("Type")) {
2211 string typename = EcmaDoc.GetDisplayName (t).Replace("+", ".");
2213 // Must load in each document to get the list of members...
2214 XmlDocument typedoc = new XmlDocument();
2215 typedoc.Load(Path.Combine(Path.Combine(basedir.FullName, ns.GetAttribute("Name")), t.GetAttribute("Name") + ".xml"));
2216 string kind = EcmaDoc.GetTypeKind (typedoc);
2218 string url = ns.GetAttribute("Name") + "." + t.GetAttribute("Name");
2219 Node typenode = nsnode.CreateNode(typename + " " + kind, "T:" + url);
2220 //Node typemembers = typenode.CreateNode("Members", "T:" + url + "/*");
2222 Hashtable groups = new Hashtable();
2223 Hashtable groups_count = new Hashtable();
2224 foreach (XmlElement member in typedoc.SelectNodes("Type/Members/Member")) {
2225 string membername = member.GetAttribute("MemberName");
2226 string membertype = member.SelectSingleNode("MemberType").InnerText;
2228 if (membertype == "Constructor")
2229 membername = t.GetAttribute("Name");
2230 if (membername.StartsWith("op_"))
2231 membertype = "Operator";
2234 if (groups.ContainsKey(membertype)) {
2235 group = (Node)groups[membertype];
2237 string membertypeplural = membertype + "s";
2238 if (membertypeplural == "Propertys") membertypeplural = "Properties";
2240 group = typenode.CreateNode(membertypeplural, "T:" + url + "/" + membertype[0]);
2241 groups[membertype] = group;
2242 groups_count[membertype] = 0;
2245 if (membertype == "Constructor" || membertype == "Method" ||
2246 (membertype == "Property" && member.SelectNodes("Parameters/Parameter").Count > 0)) {
2247 membername = EcmaHelpSource.MakeSignature(member, membertype == "Constructor" ? membername : null);
2248 } else if (membertype == "Operator") {
2250 EcmaHelpSource.MakeOperatorSignature(member, out dummy, out membername);
2253 int index = (int)groups_count[membertype];
2254 groups_count[membertype] = index + 1;
2256 group.CreateNode(membername, index.ToString());
2259 foreach (Node group in groups.Values)
2271 public override string GetIdFromUrl (string prefix, string ns, string type)
2274 throw new NotImplementedException();
2275 return Path.Combine(Path.Combine(basedir.FullName, ns), type + ".xml");
2278 protected override XmlDocument GetXmlFromUrl(string url, out string rest) {
2280 url = url.Substring(2);
2282 int sidx = url.IndexOf("/");
2286 rest = url.Substring(sidx+1);
2287 url = url.Substring(0, sidx);
2291 if (!RootTree.GetNamespaceAndType (url, out ns, out type)) {
2292 Message (TraceLevel.Error, "Could not determine namespace/type for {0}", url);
2296 string file = Path.Combine(Path.Combine(basedir.FullName, ns),
2297 ToEscapedTypeName (type).Replace ('.', '+') + ".xml");
2298 if (!new FileInfo(file).Exists) return null;
2300 XmlDocument typedoc = new XmlDocument();
2305 public override string GetText (string url, out Node match_node) {
2306 if (url == "root:") {
2310 XmlDocument index = new XmlDocument ();
2311 index.Load (Path.Combine (basedir.FullName, "index.xml"));
2312 XmlNodeList nodes = index.SelectNodes ("/Overview/Types/Namespace");
2314 //recreate masteroverview.xml
2315 XmlDocument summary = new XmlDocument ();
2316 XmlElement elements = summary.CreateElement ("elements");
2317 foreach (XmlNode node in nodes) {
2318 XmlElement ns = summary.CreateElement ("namespace");
2319 XmlAttribute attr = summary.CreateAttribute ("ns");
2320 attr.Value = EcmaDoc.GetDisplayName (node);
2321 ns.Attributes.Append (attr);
2322 elements.AppendChild (ns);
2324 summary.AppendChild (elements);
2326 XmlReader reader = new XmlTextReader (new StringReader (summary.OuterXml));
2328 //transform the recently created masteroverview.xml
2329 XsltArgumentList args = new XsltArgumentList();
2330 args.AddExtensionObject("monodoc:///extensions", ExtObject);
2331 args.AddParam("show", "", "masteroverview");
2332 string s = Htmlize(reader, args);
2333 return BuildHtml (css_ecma_code, js_code, s);
2335 return base.GetText(url, out match_node);
2338 protected override XmlDocument GetNamespaceDocument (string ns)
2340 XmlDocument nsdoc = new XmlDocument();
2341 nsdoc.Load (EcmaDoc.GetNamespaceFile (basedir.FullName, ns));
2343 XmlDocument elements = new XmlDocument();
2344 XmlElement docnode = elements.CreateElement("elements");
2345 elements.AppendChild (docnode);
2347 foreach (XmlElement doc in nsdoc.SelectNodes("Namespace/Docs/*")) {
2348 docnode.AppendChild(elements.ImportNode(doc, true));
2351 foreach (XmlElement t in basedoc.SelectNodes("Overview/Types/Namespace[@Name='" + ns + "']/Type")) {
2352 XmlDocument typedoc = new XmlDocument();
2353 typedoc.Load(Path.Combine(Path.Combine(basedir.FullName, ns), t.GetAttribute("Name") + ".xml"));
2356 switch (EcmaDoc.GetTypeKind(typedoc)) {
2357 case "Class": typekind = "class"; break;
2358 case "Enumeration": typekind = "enum"; break;
2359 case "Structure": typekind = "struct"; break;
2360 case "Delegate": typekind = "delegate"; break;
2361 case "Interface": typekind = "interface"; break;
2362 default: throw new InvalidOperationException();
2365 XmlElement typenode = elements.CreateElement(typekind);
2366 typenode.SetAttribute("name", EcmaDoc.GetDisplayName (t).Replace ('+', '.'));
2367 typenode.SetAttribute("fullname", ns + "." + t.GetAttribute("Name"));
2368 typenode.AppendChild(elements.ImportNode(typedoc.SelectSingleNode("Type/Docs/summary"), true));
2370 docnode.AppendChild(typenode);
2376 public override Stream GetHelpStream (string id)
2378 if (id == "ExtensionMethods.xml") {
2379 // TODO: generate ExtensionMethods.xml based on index.xml contents.
2384 public override XmlDocument GetHelpXmlWithChanges (string id)
2386 XmlDocument doc = new XmlDocument ();
2391 public virtual Stream GetImage (string url)
2393 string path = EcmaDoc.GetImageFile (basedir.FullName, url);
2396 return File.OpenRead (path);
2399 class UncompiledResolver : XmlResolver {
2400 public override Uri ResolveUri (Uri baseUri, string relativeUri)
2405 public override object GetEntity (Uri absoluteUri, string role, Type ofObjectToReturn)
2410 public override System.Net.ICredentials Credentials {
2415 protected override XmlResolver CreateDocumentResolver ()
2417 return new UncompiledResolver ();