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.
12 // Should cluster together constructors
15 // Should render attributes on the signature.
16 // Include examples as well.
20 using System.Diagnostics;
21 using System.Reflection;
24 using System.Xml.XPath;
27 using System.Collections;
28 using Monodoc.Lucene.Net.Index;
29 using Monodoc.Lucene.Net.Documents;
31 using Mono.Documentation;
33 using BF = System.Reflection.BindingFlags;
36 // Helper routines to extract information from an Ecma XML document
38 public static class EcmaDoc {
39 public static string GetFullClassName (XmlDocument doc)
41 return doc.SelectSingleNode ("/Type").Attributes ["FullName"].InnerText;
44 public static string GetClassName (XmlDocument doc)
46 return GetDisplayName (doc.SelectSingleNode ("/Type")).Replace ("+", ".");
49 public static string GetDisplayName (XmlNode type)
51 if (type.Attributes ["DisplayName"] != null) {
52 return type.Attributes ["DisplayName"].Value;
54 return type.Attributes ["Name"].Value;
57 public static string GetClassAssembly (XmlDocument doc)
59 return doc.SelectSingleNode ("/Type/AssemblyInfo/AssemblyName").InnerText;
62 public static string GetClassNamespace (XmlDocument doc)
64 string s = doc.SelectSingleNode ("/Type").Attributes ["FullName"].InnerText;
66 return s.Substring (0, s.LastIndexOf ("."));
69 public static string GetTypeKind (XmlDocument doc)
71 XmlNode node = doc.SelectSingleNode ("/Type/Base/BaseTypeName");
74 if (GetFullClassName (doc) == "System.Object")
79 switch (node.InnerText){
81 case "System.Delegate":
82 case "System.MulticastDelegate":
84 case "System.ValueType":
94 // Utility function: converts a fully .NET qualified type name into a C#-looking one
96 public static string ConvertCTSName (string type)
98 if (!type.StartsWith ("System."))
101 if (type.EndsWith ("*"))
102 return ConvertCTSName(type.Substring(0, type.Length - 1)) + "*";
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 - 2)) + "[]";
109 case "System.Byte": return "byte";
110 case "System.SByte": return "sbyte";
111 case "System.Int16": return "short";
112 case "System.Int32": return "int";
113 case "System.Int64": return "long";
115 case "System.UInt16": return "ushort";
116 case "System.UInt32": return "uint";
117 case "System.UInt64": return "ulong";
119 case "System.Single": return "float";
120 case "System.Double": return "double";
121 case "System.Decimal": return "decimal";
122 case "System.Boolean": return "bool";
123 case "System.Char": return "char";
124 case "System.String": return "string";
126 case "System.Object": return "object";
127 case "System.Void": return "void";
130 if (type.LastIndexOf(".") == 6)
131 return type.Substring(7);
136 internal static string GetNamespaceFile (string dir, string ns)
138 string nsxml = Path.Combine (dir, "ns-" + ns + ".xml");
139 if (!File.Exists (nsxml))
140 nsxml = Path.Combine (dir, ns + ".xml");
146 // The Ecma documentation provider:
148 // It populates a tree with contents during the help assembly
150 public class EcmaProvider : Provider {
151 ArrayList/*<string>*/ asm_dirs = new ArrayList ();
153 public EcmaProvider ()
157 public EcmaProvider (string base_directory)
159 AddDirectory (base_directory);
162 public void AddDirectory (string directory)
164 if (!Directory.Exists (directory))
165 throw new FileNotFoundException (String.Format ("The directory `{0}' does not exist", directory));
166 asm_dirs.Add (directory);
169 public override void PopulateTree (Tree tree)
171 ArrayList ns_dirs = new ArrayList ();
172 foreach (string asm in asm_dirs) {
173 ns_dirs.AddRange (Directory.GetDirectories (asm));
176 foreach (string ns in ns_dirs){
177 string basedir = Directory.GetParent (ns).FullName;
178 string [] files = Directory.GetFiles (ns);
182 Hashtable nsnodes = new Hashtable();
184 foreach (string file in files){
185 if (!file.EndsWith (".xml"))
188 if (ns_node == null) {
189 tn = Path.GetFileName (ns);
190 tree.HelpSource.Message (TraceLevel.Info, "Processing namespace {0}", tn);
191 ns_node = tree.LookupNode (tn, "N:" + tn);
192 string ns_summary_file = EcmaDoc.GetNamespaceFile (basedir, tn);
194 nsnodes[ns_node] = nsnodes;
196 if (File.Exists (ns_summary_file)) {
197 XmlDocument nsSummaryFile = new XmlDocument ();
198 nsSummaryFile.Load (ns_summary_file);
199 namespace_realpath [tn] = ns_summary_file;
201 XmlNode ns_summary = nsSummaryFile.SelectSingleNode ("Namespace/Docs/summary");
202 if (ns_summary != null && ns_summary.InnerText.Trim () != "To be added." && ns_summary.InnerText != "") {
203 namespace_summaries [tn] = ns_summary;
204 namespace_remarks [tn] = nsSummaryFile.SelectSingleNode ("Namespace/Docs/remarks");
207 } else if (!namespace_summaries.ContainsKey (tn)) {
208 namespace_summaries [tn] = null;
209 namespace_remarks [tn] = null;
212 tree.HelpSource.Message (TraceLevel.Verbose, " Processing input file {0}", Path.GetFileName (file));
214 PopulateClass (tree, tn, ns_node, file);
217 // Sort the list of types in each namespace
218 foreach (Node ns_node2 in nsnodes.Keys)
224 struct TypeInfo : IComparable {
225 public string type_assembly;
226 public string type_name;
227 public string type_full;
228 public string type_kind;
229 public XmlNode type_doc;
231 public TypeInfo (string k, string a, string f, string s, XmlNode n)
240 public int CompareTo (object b)
243 TypeInfo nb = (TypeInfo) b;
245 return String.Compare (na.type_full, nb.type_full);
250 // Packs a file with the summary data
252 public override void CloseTree (HelpSource hs, Tree tree)
254 foreach (DictionaryEntry de in class_summaries){
255 XmlDocument doc = new XmlDocument ();
256 string ns = (string) de.Key;
258 ArrayList list = (ArrayList) de.Value;
261 XmlElement elements = doc.CreateElement ("elements");
262 doc.AppendChild (elements);
264 if (namespace_summaries [ns] != null)
265 elements.AppendChild (doc.ImportNode ((XmlNode)namespace_summaries [ns],true));
267 elements.AppendChild (doc.CreateElement("summary"));
269 if (namespace_remarks [ns] != null)
270 elements.AppendChild (doc.ImportNode ((XmlNode)namespace_remarks [ns],true));
272 elements.AppendChild (doc.CreateElement("remarks"));
274 hs.Message (TraceLevel.Info, "Have {0} elements in the {1}", list.Count, ns);
275 foreach (TypeInfo p in list){
278 switch (p.type_kind){
280 e = doc.CreateElement ("class");
284 e = doc.CreateElement ("enum");
288 e = doc.CreateElement ("struct");
292 e = doc.CreateElement ("delegate");
296 e = doc.CreateElement ("interface");
300 e.SetAttribute ("name", p.type_name);
301 e.SetAttribute ("fullname", p.type_full);
302 e.SetAttribute ("assembly", p.type_assembly);
303 XmlNode copy = doc.ImportNode (p.type_doc, true);
304 e.AppendChild (copy);
305 elements.AppendChild (e);
307 hs.PackXml ("xml.summary." + ns, doc,(string) namespace_realpath[ns]);
311 XmlDocument nsSummary = new XmlDocument ();
312 XmlElement root = nsSummary.CreateElement ("elements");
313 nsSummary.AppendChild (root);
315 foreach (DictionaryEntry de in namespace_summaries) {
316 XmlNode n = (XmlNode)de.Value;
317 XmlElement summary = nsSummary.CreateElement ("namespace");
318 summary.SetAttribute ("ns", (string)de.Key);
319 root.AppendChild (summary);
321 summary.AppendChild (nsSummary.ImportNode (n,true));
323 summary.AppendChild (nsSummary.CreateElement("summary"));
325 tree.HelpSource.PackXml ("mastersummary.xml", nsSummary, null);
326 AddExtensionMethods (tree);
329 void AddExtensionMethods (Tree tree)
331 XmlDocument extensions = null;
334 foreach (string asm in asm_dirs) {
335 string overview_file = Path.Combine (asm, "index.xml");
336 if (File.Exists (overview_file)) {
337 XmlDocument overview = new XmlDocument ();
338 overview.Load (overview_file);
339 XmlNodeList e = overview.SelectNodes ("/Overview/ExtensionMethods/*");
341 if (extensions == null) {
342 extensions = new XmlDocument ();
343 root = extensions.CreateElement ("ExtensionMethods");
344 extensions.AppendChild (root);
346 foreach (XmlNode n in e) {
348 root.AppendChild (extensions.ImportNode (n, true));
353 if (extensions != null) {
354 tree.HelpSource.Message (TraceLevel.Info, "Have {0} extension methods", numMethods);
355 tree.HelpSource.PackXml ("ExtensionMethods.xml", extensions, "ExtensionMethods.xml");
359 Hashtable class_summaries = new Hashtable ();
360 Hashtable namespace_summaries = new Hashtable ();
361 Hashtable namespace_remarks = new Hashtable ();
362 Hashtable namespace_realpath = new Hashtable ();
365 void PopulateClass (Tree tree, string ns, Node ns_node, string file)
367 doc = new XmlDocument ();
370 string name = EcmaDoc.GetClassName (doc);
371 string assembly = EcmaDoc.GetClassAssembly (doc);
372 string kind = EcmaDoc.GetTypeKind (doc);
373 string full = EcmaDoc.GetFullClassName (doc);
376 string file_code = ns_node.tree.HelpSource.PackFile (file);
378 XmlNode class_summary = doc.SelectSingleNode ("/Type/Docs/summary");
379 ArrayList l = (ArrayList) class_summaries [ns];
381 l = new ArrayList ();
382 class_summaries [ns] = (object) l;
384 l.Add (new TypeInfo (kind, assembly, full, name, class_summary));
386 class_node = ns_node.LookupNode (String.Format ("{0} {1}", name, kind), "ecma:" + file_code + "#" + name + "/");
388 if (kind == "Delegate") {
389 if (doc.SelectSingleNode("/Type/ReturnValue") == null)
390 tree.HelpSource.Message (TraceLevel.Error, "Delegate " + name + " does not have a ReturnValue node. See the ECMA-style updates.");
393 if (kind == "Enumeration")
396 if (kind == "Delegate")
400 // Always add the Members node
402 class_node.CreateNode ("Members", "*");
404 PopulateMember (name, class_node, "Constructor", "Constructors");
405 PopulateMember (name, class_node, "Method", "Methods");
406 PopulateMember (name, class_node, "Property", "Properties");
407 PopulateMember (name, class_node, "Field", "Fields");
408 PopulateMember (name, class_node, "Event", "Events");
409 PopulateMember (name, class_node, "Operator", "Operators");
416 public NodeIndex (XmlNode node, int index)
423 struct NodeCompare : IComparer {
424 public int Compare (object a, object b)
426 NodeIndex na = (NodeIndex) a;
427 NodeIndex nb = (NodeIndex) b;
429 return String.Compare (na.node.Attributes ["MemberName"].InnerText,
430 nb.node.Attributes ["MemberName"].InnerText);
434 string GetMemberName (XmlNode node)
436 return node.Attributes ["MemberName"].InnerText;
440 // Performs an XPath query on the document to extract the nodes for the various members
441 // we also use some extra text to pluralize the caption
443 void PopulateMember (string typename, Node node, string type, string caption)
445 string select = type;
446 if (select == "Operator") select = "Method";
448 XmlNodeList list1 = doc.SelectNodes (String.Format ("/Type/Members/Member[MemberType=\"{0}\"]", select));
449 ArrayList list = new ArrayList();
451 foreach (XmlElement n in list1) {
452 n.SetAttribute("assembler_index", (i++).ToString());
453 if (type == "Method" && GetMemberName(n).StartsWith("op_")) continue;
454 if (type == "Operator" && !GetMemberName(n).StartsWith("op_")) continue;
458 int count = list.Count;
464 string key = type.Substring (0, 1);
465 nodes_node = node.CreateNode (caption, key);
470 foreach (XmlElement n in list)
471 nodes_node.CreateNode (GetMemberName (n), n.GetAttribute("assembler_index"));
475 foreach (XmlElement n in list)
476 nodes_node.CreateNode (EcmaHelpSource.MakeSignature(n, typename), n.GetAttribute("assembler_index"));
479 case "Property": // properties with indexers can be overloaded too
482 foreach (XmlElement n in list) {
483 bool multiple = false;
484 foreach (XmlNode nn in list) {
485 if (n != nn && GetMemberName(n) == nn.Attributes ["MemberName"].InnerText) {
491 string group, name, sig;
492 if (type != "Operator") {
493 name = GetMemberName(n);
494 sig = EcmaHelpSource.MakeSignature(n, null);
497 EcmaHelpSource.MakeOperatorSignature(n, out name, out sig);
502 nodes_node.LookupNode (group, group)
503 .CreateNode (sig, n.GetAttribute("assembler_index"));
505 nodes_node.CreateNode (name, n.GetAttribute("assembler_index"));
509 foreach (Node n in nodes_node.Nodes) {
517 throw new InvalidOperationException();
526 // The Ecma HelpSource provider
528 public class EcmaHelpSource : HelpSource {
530 public EcmaHelpSource (string base_file, bool create) : base (base_file, create)
532 ExtObject = new ExtensionObject (this);
535 public EcmaHelpSource ()
537 ExtObject = new ExtensionObject (this);
540 static string css_ecma;
541 public static string css_ecma_code {
543 if (css_ecma != null)
546 System.Reflection.Assembly assembly = typeof(EcmaHelpSource).Assembly;
547 Stream str_css = assembly.GetManifestResourceStream ("mono-ecma.css");
548 css_ecma = (new StreamReader (str_css)).ReadToEnd();
550 css_ecma = String.Empty;
556 public override string InlineCss {
557 get {return base.InlineCss + css_ecma_code;}
561 public static string js_code {
566 System.Reflection.Assembly assembly = typeof(EcmaHelpSource).Assembly;
567 Stream str_js = assembly.GetManifestResourceStream ("helper.js");
568 js = (new StreamReader (str_js)).ReadToEnd();
576 public override string InlineJavaScript {
577 get {return js_code + base.InlineJavaScript;}
580 public override string GetText (string url, out Node match_node)
584 string c = GetCachedText (url);
588 string cached = GetCachedText (url);
594 XmlReader summary = GetHelpXml ("mastersummary.xml");
598 XsltArgumentList args = new XsltArgumentList();
599 args.AddExtensionObject("monodoc:///extensions", ExtObject);
600 args.AddParam("show", "", "masteroverview");
601 string s = Htmlize(new XPathDocument (summary), args);
602 return BuildHtml (css_ecma_code, js_code, s);
605 if (url.StartsWith ("ecma:")) {
606 string s = GetTextFromUrl (url);
607 return BuildHtml (css_ecma_code, js_code, s);
614 string RenderMemberLookup (string typename, string member, ref Node type_node)
616 if (type_node.Nodes == null)
619 string membername = member;
620 string[] argtypes = null;
621 if (member.IndexOf("(") > 0) {
622 membername = membername.Substring(0, member.IndexOf("("));
623 member = member.Replace("@", "&");
625 // reform the member signature with CTS names
627 string x = member.Substring(member.IndexOf("(")+1);
628 argtypes = x.Substring(0, x.Length-1).Split(',', ':'); // operator signatures have colons
630 if (membername == ".ctor")
631 membername = typename;
633 member = membername + "(";
634 for (int i = 0; i < argtypes.Length; i++) {
635 argtypes[i] = EcmaDoc.ConvertCTSName(argtypes[i]);
636 if (i > 0) member += ",";
637 member += argtypes[i];
642 // Check if a node caption matches exactly
644 bool isoperator = false;
646 if ((membername == "op_Implicit" || membername == "op_Explicit") && argtypes.Length == 2) {
648 membername = "Conversion";
649 member = argtypes[0] + " to " + argtypes[1];
650 } else if (membername.StartsWith("op_")) {
652 membername = membername.Substring(3);
655 foreach (Node x in type_node.Nodes){
658 if (isoperator && x.Caption != "Operators")
661 foreach (Node m in x.Nodes) {
662 string caption = m.Caption;
663 string ecaption = ToEscapedMemberName (caption);
665 // No overloading (usually), is just the member name. The whole thing for constructors.
666 if (caption == membername || caption == member ||
667 ecaption == membername || ecaption == member) {
669 return GetTextFromUrl (m.URL);
671 } else if (caption == member || ecaption == member) {
672 // Though there are overloads, no arguments are in the url, so use this base node
674 return GetTextFromUrl (m.URL);
676 // Check subnodes which are the overloads -- must match signature
677 foreach (Node mm in m.Nodes) {
678 ecaption = ToEscapedTypeName (mm.Caption);
679 if (mm.Caption == member || ecaption == member) {
681 return GetTextFromUrl (mm.URL);
691 public static string MakeSignature (XmlNode n, string cstyleclass)
695 if (cstyleclass == null)
696 sig = n.Attributes["MemberName"].InnerText;
702 /*if (n.SelectSingleNode("MemberType").InnerText == "Method" || n.SelectSingleNode("MemberType").InnerText == "Constructor") {*/ // properties with indexers too
703 XmlNodeList paramnodes = n.SelectNodes("Parameters/Parameter");
706 foreach (XmlNode p in paramnodes) {
707 if (!first) sig += ",";
708 string type = p.Attributes["Type"].InnerText;
709 type = EcmaDoc.ConvertCTSName(type);
719 public static void MakeOperatorSignature (XmlNode n, out string nicename, out string sig)
723 name = n.Attributes["MemberName"].InnerText;
724 nicename = name.Substring(3);
727 // unary operators: no overloading possible
728 case "op_UnaryPlus": case "op_UnaryNegation": case "op_LogicalNot": case "op_OnesComplement":
729 case "op_Increment": case "op_Decrement":
730 case "op_True": case "op_False":
734 // binary operators: overloading is possible based on parameter types
735 case "op_Addition": case "op_Subtraction": case "op_Multiply": case "op_Division": case "op_Modulus":
736 case "op_BitwiseAnd": case "op_BitwiseOr": case "op_ExclusiveOr":
737 case "op_LeftShift": case "op_RightShift":
738 case "op_Equality": case "op_Inequality":
739 case "op_GreaterThan": case "op_LessThan": case "op_GreaterThanOrEqual": case "op_LessThanOrEqual":
740 XmlNodeList paramnodes = n.SelectNodes("Parameters/Parameter");
741 sig = nicename + "(";
743 foreach (XmlNode p in paramnodes) {
744 if (!first) sig += ",";
745 string type = p.Attributes["Type"].InnerText;
746 type = EcmaDoc.ConvertCTSName(type);
753 // overloading based on parameter and return type
754 case "op_Implicit": case "op_Explicit":
755 nicename = "Conversion";
756 string arg = n.SelectSingleNode("Parameters/Parameter/@Type").InnerText;
757 string ret = n.SelectSingleNode("ReturnValue/ReturnType").InnerText;
758 sig = EcmaDoc.ConvertCTSName(arg) + " to " + EcmaDoc.ConvertCTSName(ret);
762 throw new InvalidOperationException();
767 // This routine has to perform a lookup on a type.
769 // Example: T:System.Text.StringBuilder
771 // The prefix is the kind of opereation being requested (T:, E:, M: etc)
772 // ns is the namespace being looked up
773 // type is the type being requested
775 // This has to walk our toplevel (which is always a namespace)
776 // And then the type space, and then walk further down depending on the request
778 public override string RenderTypeLookup (string prefix, string ns, string type, string member, out Node match_node)
780 string url = GetUrlForType (prefix, ns, type, member, out match_node);
781 if (url == null) return null;
782 return GetTextFromUrl (url);
785 public virtual string GetIdFromUrl (string prefix, string ns, string type)
787 Node tmp_node = new Node (Tree, "", "");
788 string url = GetUrlForType (prefix, ns, type, null, out tmp_node);
789 if (url == null) return null;
790 return GetFile (url.Substring (5), out url);
793 public string GetUrlForType (string prefix, string ns, string type, string member, out Node match_node)
795 if (!prefix.EndsWith(":"))
796 throw new ArgumentException("prefix");
799 member = member.Replace ("#", ".").Replace ("{", "<").Replace ("}", ">");
801 // If a nested type, compare only inner type name to node list.
802 // This should be removed when the node list doesn't lose the containing type name.
803 type = ToEscapedTypeName (type.Replace("+", "."));
804 MatchAttempt[] attempts = GetAttempts (ns, type);
806 foreach (Node ns_node in Tree.Nodes){
807 string ns_node_namespace = ns_node.Element.Substring (2);
809 if (!MatchesNamespace (attempts, ns_node_namespace, out type))
812 foreach (Node type_node in ns_node.Nodes){
813 string element = type_node.Element;
816 if (element.StartsWith("T:")) {
817 cname = element.Substring(2);
819 RootTree.GetNamespaceAndType (cname, out _ns, out cname);
820 cname = ToEscapedTypeName (cname);
821 int pidx = cname.LastIndexOf (".");
822 cname = cname.Substring(pidx+1);
823 pidx = cname.LastIndexOf ("/");
825 cname = cname.Substring(0, pidx);
826 cname = cname.Replace("+", ".");
828 int pidx = element.IndexOf ("#");
829 int sidx = element.IndexOf ("/");
830 cname = element.Substring (pidx + 1, sidx-pidx-1);
831 cname = ToEscapedTypeName (cname);
834 //Console.WriteLine ("t:{0} cn:{1} p:{2}", type, cname, prefix);
836 if (type == cname && prefix == "T:") {
837 match_node = type_node;
838 return type_node.URL;
839 } else if (type.StartsWith (cname)){
840 int p = cname.Length;
842 match_node = type_node;
844 string ret = RenderMemberLookup (type, member, ref match_node);
846 return type_node.URL;
847 return match_node.URL;
849 } else if (type [p] == '/'){
851 // This handles summaries
854 foreach (Node nd in type_node.Nodes) {
855 if (nd.Element [nd.Element.Length - 1] == type [p + 1]) {
861 string ret = type_node.URL;
862 if (!ret.EndsWith("/")) ret += "/";
863 return ret + type.Substring (p + 1);
873 struct MatchAttempt {
874 public string Namespace;
877 public MatchAttempt (string ns, string t)
884 private static MatchAttempt[] GetAttempts (string ns, string type)
886 MatchAttempt[] attempts = new MatchAttempt [Count (ns, '.') + 1];
888 for (int i = 0; i < ns.Length; ++i) {
890 attempts [wns++] = new MatchAttempt (ns.Substring (0, i),
891 ns.Substring (i+1) + "." + type);
893 attempts [wns++] = new MatchAttempt (ns, type);
897 private static int Count (string s, char c)
900 for (int i = 0; i < s.Length; ++i)
906 private static bool MatchesNamespace (MatchAttempt[] attempts, string ns, out string type)
908 for (int i = 0; i < attempts.Length; ++i)
909 if (ns == attempts [i].Namespace) {
910 type = attempts [i].Type;
917 public static string ToEscapedTypeName (string typename)
919 return ToEscapedName (typename, "`");
922 static string ToEscapedName (string name, string escape)
924 StringBuilder filename = new StringBuilder (name.Length);
929 for (int i = 0; i < name.Length; ++i) {
941 filename.Append (escape).Append ((numArgs+1).ToString());
956 return filename.ToString ();
959 static string ToEscapedMemberName (string membername)
961 return ToEscapedName (membername, "``");
964 public override string GetNodeXPath (XPathNavigator n)
966 if (n.Matches ("/Type/Docs/param")) {
967 string type_name = (string) n.Evaluate ("string (ancestor::Type/@FullName)");
968 string param_name = (string) n.Evaluate ("string (@name)");
970 return String.Format ("/Type [@FullName = '{0}']/Docs/param[@name='{1}']", type_name, param_name);
973 if (n.Matches ("/Type/Docs/*")) {
974 string type_name = (string) n.Evaluate ("string (ancestor::Type/@FullName)");
976 return String.Format ("/Type [@FullName = '{0}']/Docs/{1}", type_name, n.Name);
979 if (n.Matches ("/Type/Members/Member/Docs/*")) {
980 string type_name = (string) n.Evaluate ("string (ancestor::Type/@FullName)");
981 string member_name = (string) n.Evaluate ("string (ancestor::Member/@MemberName)");
982 string member_sig = (string) n.Evaluate ("string (ancestor::Member/MemberSignature [@Language='C#']/@Value)");
983 string param_name = (string) n.Evaluate ("string (@name)");
985 if (param_name == null || param_name == "") {
986 return String.Format (
987 "/Type [@FullName = '{0}']/Members/Member [@MemberName = '{1}'][MemberSignature [@Language='C#']/@Value = '{2}']/Docs/{3}",
988 type_name, member_name, member_sig, n.Name);
990 return String.Format (
991 "/Type [@FullName = '{0}']/Members/Member [@MemberName = '{1}'][MemberSignature [@Language='C#']/@Value = '{2}']/Docs/param [@name = '{3}']",
992 type_name, member_name, member_sig, param_name);
996 Message (TraceLevel.Warning, "WARNING: Was not able to get clean XPath expression for node {0}", EditingUtils.GetXPath (n));
997 return base.GetNodeXPath (n);
1000 protected virtual XmlDocument GetNamespaceDocument (string ns)
1002 return GetHelpXmlWithChanges ("xml.summary." + ns);
1005 public override string RenderNamespaceLookup (string nsurl, out Node match_node)
1007 foreach (Node ns_node in Tree.Nodes){
1008 if (ns_node.Element != nsurl)
1011 match_node = ns_node;
1012 string ns_name = nsurl.Substring (2);
1014 XmlDocument doc = GetNamespaceDocument (ns_name);
1018 XsltArgumentList args = new XsltArgumentList();
1019 args.AddExtensionObject("monodoc:///extensions", ExtObject);
1020 args.AddParam("show", "", "namespace");
1021 args.AddParam("namespace", "", ns_name);
1022 string s = Htmlize(doc, args);
1023 return BuildHtml (css_ecma_code, js_code, s);
1030 private string SelectString(XmlNode node, string xpath) {
1031 XmlNode ret = node.SelectSingleNode(xpath);
1032 if (ret == null) return "";
1033 return ret.InnerText;
1035 private int SelectCount(XmlNode node, string xpath) {
1036 return node.SelectNodes(xpath).Count;
1040 // Returns the XmlDocument from the given url, and fills in `rest'
1042 protected virtual XmlDocument GetXmlFromUrl(string url, out string rest) {
1044 url = url.Substring (5);
1045 string file = GetFile (url, out rest);
1047 // Console.WriteLine ("Got [{0}] and [{1}]", file, rest);
1048 return GetHelpXmlWithChanges (file);
1051 string GetTextFromUrl (string url)
1056 XmlDocument doc = GetXmlFromUrl (url, out rest);
1058 // Load base-type information so the stylesheet can draw the inheritance
1059 // tree and (optionally) include inherited members in the members list.
1060 XmlElement basenode = (XmlElement)doc.SelectSingleNode("Type/Base");
1061 XmlElement membersnode = (XmlElement)doc.SelectSingleNode("Type/Members");
1062 XmlNode basetype = doc.SelectSingleNode("Type/Base/BaseTypeName");
1064 while (basetype != null) {
1065 // Add the ParentType node to Type/Base
1066 XmlElement inheritancenode = doc.CreateElement("ParentType");
1067 inheritancenode.SetAttribute("Type", basetype.InnerText);
1068 inheritancenode.SetAttribute("Order", (baseindex++).ToString());
1069 basenode.AppendChild(inheritancenode);
1071 // Load the base type XML data
1072 int dot = basetype.InnerText.LastIndexOf('.');
1073 string ns = basetype.InnerText.Substring(0, dot == -1 ? 0 : dot);
1074 string n = basetype.InnerText.Substring(dot == -1 ? 0 : dot+1);
1075 string basetypeurl = GetUrlForType("T:", ns, n, null, out node);
1076 XmlDocument basetypedoc = null;
1077 if (basetypeurl != null)
1078 basetypedoc = GetXmlFromUrl (basetypeurl, out rest2);
1079 if (basetypedoc == null) {
1080 inheritancenode.SetAttribute("Incomplete", "1");
1084 if (SettingsHandler.Settings.ShowInheritedMembers) {
1085 // Add inherited members
1086 foreach (XmlElement member in basetypedoc.SelectNodes("Type/Members/Member[not(MemberType='Constructor')]")) {
1087 string sig = SelectString(member, "MemberSignature[@Language='C#']/@Value");
1088 if (sig.IndexOf(" static ") >= 0) continue;
1090 // If the signature of member matches the signature of a member already in the XML,
1092 string xpath = "@MemberName='" + SelectString(member, "@MemberName") + "'";
1093 xpath += " and ReturnValue/ReturnType='" + SelectString(member, "ReturnValue/ReturnType") + "'";
1094 xpath += " and count(Parameters/Parameter)=" + SelectCount(member, "Parameters/Parameter") + "";
1096 foreach (XmlElement p in member.SelectNodes("Parameters/Parameter")) {
1097 xpath += " and Parameters/Parameter[position()=" + pi + "]/@Type = '" + p.GetAttribute("Type") + "'";
1101 // If a signature match is found, don't add.
1102 XmlNode match = membersnode.SelectSingleNode("Member[" + xpath + "]");
1106 member.SetAttribute("DeclaredIn", basetype.InnerText);
1107 membersnode.AppendChild(membersnode.OwnerDocument.ImportNode(member, true));
1111 basetype = basetypedoc.SelectSingleNode("Type/Base/BaseTypeName");
1113 ArrayList extensions = new ArrayList ();
1114 foreach (HelpSource hs in RootTree.HelpSources) {
1115 EcmaHelpSource es = hs as EcmaHelpSource;
1118 Stream s = es.GetHelpStream ("ExtensionMethods.xml");
1120 XmlDocument d = new XmlDocument ();
1122 foreach (XmlNode n in d.SelectNodes ("/ExtensionMethods/*")) {
1127 XmlDocUtils.AddExtensionMethods (doc, extensions, delegate (string s) {
1128 s = s.StartsWith ("T:") ? s : "T:" + s;
1129 return RootTree.GetHelpXml (s);
1132 XsltArgumentList args = new XsltArgumentList();
1134 args.AddExtensionObject("monodoc:///extensions", ExtObject);
1137 args.AddParam("show", "", "typeoverview");
1138 string s = Htmlize(doc, args);
1139 return BuildHtml (css_ecma_code, js_code, s);
1142 string [] nodes = rest.Split (new char [] {'/'});
1144 switch (nodes.Length) {
1146 args.AddParam("show", "", "members");
1147 args.AddParam("index", "", "all");
1150 // Could either be a single member, or an overload thingy
1152 int.Parse (nodes [1]); // is it an int
1154 args.AddParam("show", "", "member");
1155 args.AddParam("index", "", nodes [1]);
1157 args.AddParam("show", "", "overloads");
1158 args.AddParam("index", "", nodes [1]);
1162 args.AddParam("show", "", "member");
1163 args.AddParam("index", "", nodes [2]);
1166 return "What the hell is this URL " + url;
1171 args.AddParam("membertype", "", "Constructor");
1174 args.AddParam("membertype", "", "Method");
1177 args.AddParam("membertype", "", "Property");
1180 args.AddParam("membertype", "", "Field");
1183 args.AddParam("membertype", "", "Event");
1186 args.AddParam("membertype", "", "Operator");
1189 args.AddParam("membertype", "", "ExtensionMethod");
1192 args.AddParam("membertype", "", "All");
1195 return "Unknown url: " + url;
1198 string html = Htmlize(doc, args);
1199 return BuildHtml (css_ecma_code, js_code, html);
1203 public override void RenderPreviewDocs (XmlNode newNode, XmlWriter writer)
1205 XsltArgumentList args = new XsltArgumentList ();
1206 args.AddExtensionObject ("monodoc:///extensions", ExtObject);
1208 Htmlize (newNode, args, writer);
1211 static XslTransform ecma_transform;
1213 public string Htmlize (IXPathNavigable ecma_xml)
1215 return Htmlize(ecma_xml, null);
1218 public string Htmlize (IXPathNavigable ecma_xml, XsltArgumentList args)
1222 StringWriter output = new StringWriter ();
1223 ecma_transform.Transform (ecma_xml, args, output, CreateDocumentResolver ());
1224 return output.ToString ();
1227 protected virtual XmlResolver CreateDocumentResolver ()
1229 // results in using XmlUrlResolver
1233 static void Htmlize (IXPathNavigable ecma_xml, XsltArgumentList args, XmlWriter w)
1237 if (ecma_xml == null)
1240 ecma_transform.Transform (ecma_xml, args, w, null);
1243 static XslTransform ecma_transform_css, ecma_transform_no_css;
1244 static void EnsureTransform ()
1246 if (ecma_transform == null) {
1247 ecma_transform_css = new XslTransform ();
1248 ecma_transform_no_css = new XslTransform ();
1249 Assembly assembly = System.Reflection.Assembly.GetCallingAssembly ();
1251 Stream stream = assembly.GetManifestResourceStream ("mono-ecma-css.xsl");
1252 XmlReader xml_reader = new XmlTextReader (stream);
1253 XmlResolver r = new ManifestResourceResolver (".");
1254 ecma_transform_css.Load (xml_reader, r, null);
1256 stream = assembly.GetManifestResourceStream ("mono-ecma.xsl");
1257 xml_reader = new XmlTextReader (stream);
1258 ecma_transform_no_css.Load (xml_reader, r, null);
1261 ecma_transform = ecma_transform_css;
1263 ecma_transform = ecma_transform_no_css;
1266 // This ExtensionObject stuff is used to check at run time whether
1267 // types and members have been implemented and whether there are any
1268 // MonoTODO attributes left on them.
1270 public readonly ExtensionObject ExtObject;
1271 public class ExtensionObject {
1272 readonly EcmaHelpSource hs;
1275 // We are setting this to quiet now, as we need to transition
1276 // monodoc to run with the 2.x runtime and provide accurate
1277 // information in those cases.
1281 public ExtensionObject (EcmaHelpSource hs)
1286 public string Colorize(string code, string lang) {
1287 return(Mono.Utilities.Colorizer.Colorize(code,lang));
1289 // Used by stylesheet to nicely reformat the <see cref=> tags.
1290 public string MakeNiceSignature(string sig, string contexttype)
1298 sig = sig.Substring(2);
1301 case 'N': return sig;
1302 case 'T': return ShortTypeName(sig, contexttype);
1304 case 'C': case 'M': case 'P': case 'F': case 'E':
1305 string type, mem, arg;
1309 if (s == 'C' || s == 'M')
1310 paren = sig.IndexOf("(");
1312 paren = sig.IndexOf("[");
1316 if (paren > 0 && paren < sig.Length-1) {
1317 string[] args = sig.Substring(paren+1, sig.Length-paren-2).Split(',');
1318 for (int i = 0; i < args.Length; i++)
1319 args[i] = ShortTypeName(args[i], contexttype);
1320 arg = "(" + String.Join(", ", args) + ")";
1321 sig = sig.Substring(0, paren);
1326 // Get type and member names
1327 int dot = sig.LastIndexOf(".");
1328 if (s == 'C' || dot <= 0 || dot == sig.Length-1) {
1332 type = sig.Substring(0, dot);
1333 mem = sig.Substring(dot);
1336 type = ShortTypeName(type, contexttype);
1338 return type + mem + arg;
1345 public string EditUrl (XPathNodeIterator itr)
1347 if (itr.MoveNext ())
1348 return hs.GetEditUri (itr.Current);
1353 public string EditUrlNamespace (XPathNodeIterator itr, string ns, string section)
1355 if (hs is EcmaUncompiledHelpSource)
1356 return "edit:file:" + Path.Combine(((EcmaUncompiledHelpSource)hs).BasePath, ns + ".xml") + "@/Namespace/Docs/" + section;
1357 else if (itr.MoveNext ())
1358 return EditingUtils.FormatEditUri(itr.Current.BaseURI, "/elements/" + section);
1362 private static string ShortTypeName(string name, string contexttype)
1364 int dot = contexttype.LastIndexOf(".");
1365 if (dot < 0) return name;
1366 string contextns = contexttype.Substring(0, dot+1);
1368 if (name == contexttype)
1369 return name.Substring(dot+1);
1371 if (name.StartsWith(contextns))
1372 return name.Substring(contextns.Length);
1374 return name.Replace("+", ".");
1377 public string MonoImpInfo(string assemblyname, string typename, string membername, string arglist, bool strlong)
1382 ArrayList a = new ArrayList();
1383 if (arglist != "") a.Add(arglist);
1384 return MonoImpInfo(assemblyname, typename, membername, a, strlong);
1387 public string MonoImpInfo(string assemblyname, string typename, string membername, XPathNodeIterator itr, bool strlong)
1392 ArrayList rgs = new ArrayList ();
1393 while (itr.MoveNext ())
1394 rgs.Add (itr.Current.Value);
1396 return MonoImpInfo (assemblyname, typename, membername, rgs, strlong);
1399 public string MonoImpInfo(string assemblyname, string typename, string membername, ArrayList arglist, bool strlong)
1402 Assembly assembly = null;
1405 assembly = Assembly.LoadWithPartialName(assemblyname);
1406 } catch (Exception) {
1410 if (assembly == null) {
1411 /*if (strlong) return "The assembly " + assemblyname + " is not available to MonoDoc.";
1413 return ""; // silently ignore
1416 Type t = assembly.GetType(typename, false);
1419 return typename + " has not been implemented.";
1421 return "Not implemented.";
1424 // The following code is flakey and fails to find existing members
1427 MemberInfo[] mis = t.GetMember(membername, BF.Static | BF.Instance | BF.Public | BF.NonPublic);
1429 if (mis.Length == 0)
1430 return "This member has not been implemented.";
1431 if (mis.Length == 1)
1432 return MonoImpInfo(mis[0], "member", strlong);
1434 // Scan for the appropriate member
1435 foreach (MemberInfo mi in mis) {
1436 System.Reflection.ParameterInfo[] pis;
1438 if (mi is MethodInfo || mi is ConstructorInfo)
1439 pis = ((MethodBase)mi).GetParameters();
1440 else if (mi is PropertyInfo)
1441 pis = ((PropertyInfo)mi).GetIndexParameters();
1447 if (pis.Length != arglist.Count) continue;
1448 for (int i = 0; i < pis.Length; i++) {
1449 if (pis[i].ParameterType.FullName != (string)arglist[i]) { good = false; break; }
1451 if (!good) continue;
1454 return MonoImpInfo(mi, "member", strlong);
1457 } catch (Exception) {
1462 public string MonoImpInfo(System.Reflection.MemberInfo mi, string itemtype, bool strlong)
1469 object[] atts = mi.GetCustomAttributes(true);
1471 foreach (object att in atts) if (att.GetType().Name == "MonoTODOAttribute") todoctr++;
1475 s = "This " + itemtype + " is marked as being unfinished.<BR/>\n";
1483 public string MonoImpInfo(string assemblyname, string typename, bool strlong)
1489 if (assemblyname == "")
1492 Assembly assembly = Assembly.LoadWithPartialName(assemblyname);
1493 if (assembly == null)
1496 Type t = assembly.GetType(typename, false);
1499 return typename + " has not been implemented.";
1501 return "Not implemented.";
1504 string s = MonoImpInfo(t, "type", strlong);
1507 MemberInfo[] mis = t.GetMembers(BF.Static | BF.Instance | BF.Public | BF.NonPublic);
1509 // Scan members for MonoTODO attributes
1511 foreach (MemberInfo mi in mis) {
1512 string mii = MonoImpInfo(mi, null, false);
1513 if (mii != "") mctr++;
1516 s += "This type has " + mctr + " members that are marked as unfinished.<BR/>";
1522 } catch (Exception) {
1527 public bool MonoEditing ()
1529 return SettingsHandler.Settings.EnableEditing;
1532 public bool IsToBeAdded(string text) {
1533 return text.StartsWith("To be added");
1538 // This takes one of the ecma urls, which look like this:
1539 // ecma:NUMERIC_ID#OPAQUE/REST
1541 // NUMERIC_ID is the numeric ID assigned by the compressor
1542 // OPAQUE is opaque for node rendering (it typically contains T:System.Byte for example)
1543 // REST is the rest of the argument used to decode information
1545 static string GetFile (string url, out string rest)
1547 int pound = url.IndexOf ("#");
1548 int slash = url.IndexOf ("/");
1550 string fname = url.Substring (0, pound);
1551 rest = url.Substring (slash+1);
1557 // This should have a little cache or something.
1558 static XmlDocument GetDocument (HelpSource hs, string fname)
1560 Stream s = hs.GetHelpStream (fname);
1562 Error ("Could not fetch document {0}", fname);
1566 XmlDocument doc = new XmlDocument ();
1574 string GetKindFromCaption (string s)
1576 int p = s.LastIndexOf (' ');
1578 return s.Substring (p + 1);
1583 // Obtain an URL of the type T:System.Object from the node
1585 public static string GetNiceUrl (Node node) {
1586 if (node.Element.StartsWith("N:"))
1587 return node.Element;
1589 int bk_pos = node.Caption.IndexOf (' ');
1590 // node from an overview
1592 name = node.Caption.Substring (0, bk_pos);
1593 full = node.Parent.Caption + "." + name.Replace ('.', '+');
1596 // node that lists constructors, methods, fields, ...
1597 if ((node.Caption == "Constructors") || (node.Caption == "Fields") || (node.Caption == "Events")
1598 || (node.Caption == "Members") || (node.Caption == "Properties") || (node.Caption == "Methods")
1599 || (node.Caption == "Operators")) {
1600 bk_pos = node.Parent.Caption.IndexOf (' ');
1601 name = node.Parent.Caption.Substring (0, bk_pos);
1602 full = node.Parent.Parent.Caption + "." + name.Replace ('.', '+');
1603 return "T:" + full + "/" + node.Element;
1605 int pr_pos = node.Caption.IndexOf ('(');
1606 // node from a constructor
1607 if (node.Parent.Element == "C") {
1608 name = node.Parent.Parent.Parent.Caption;
1609 int idx = node.URL.IndexOf ('/');
1610 return node.URL[idx+1] + ":" + name + "." + node.Caption.Replace ('.', '+');
1611 // node from a method with one signature, field, property, operator
1612 } else if (pr_pos == -1) {
1613 bk_pos = node.Parent.Parent.Caption.IndexOf (' ');
1614 name = node.Parent.Parent.Caption.Substring (0, bk_pos);
1615 full = node.Parent.Parent.Parent.Caption + "." + name.Replace ('.', '+');
1616 int idx = node.URL.IndexOf ('/');
1617 return node.URL[idx+1] + ":" + full + "." + node.Caption;
1618 // node from a method with several signatures
1620 bk_pos = node.Parent.Parent.Parent.Caption.IndexOf (' ');
1621 name = node.Parent.Parent.Parent.Caption.Substring (0, bk_pos);
1622 full = node.Parent.Parent.Parent.Parent.Caption + "." + name.Replace ('.', '+');
1623 int idx = node.URL.IndexOf ('/');
1624 return node.URL[idx+1] + ":" + full + "." + node.Caption;
1629 // Populates the index.
1631 public override void PopulateIndex (IndexMaker index_maker)
1633 foreach (Node ns_node in Tree.Nodes){
1634 foreach (Node type_node in ns_node.Nodes){
1635 string typename = type_node.Caption.Substring (0, type_node.Caption.IndexOf (' '));
1636 string full = ns_node.Caption + "." + typename;
1638 string doc_tag = GetKindFromCaption (type_node.Caption);
1639 string url = "T:" + full;
1641 if (doc_tag == "Class" || doc_tag == "Structure" || doc_tag == "Interface"){
1643 index_maker.Add (type_node.Caption, typename, url);
1644 index_maker.Add (full + " " + doc_tag, full, url);
1646 foreach (Node c in type_node.Nodes){
1648 case "Constructors":
1649 index_maker.Add (" constructors", typename+"0", url + "/C");
1652 index_maker.Add (" fields", typename+"1", url + "/F");
1655 index_maker.Add (" events", typename+"2", url + "/E");
1658 index_maker.Add (" properties", typename+"3", url + "/P");
1661 index_maker.Add (" methods", typename+"4", url + "/M");
1664 index_maker.Add (" operators", typename+"5", url + "/O");
1670 // Now repeat, but use a different sort key, to make sure we come after
1671 // the summary data above, start the counter at 6
1673 string keybase = typename + "6.";
1675 foreach (Node c in type_node.Nodes){
1677 case "Constructors":
1680 foreach (Node nc in c.Nodes){
1681 string res = nc.Caption;
1683 string nurl = String.Format ("F:{0}.{1}", full, res);
1684 index_maker.Add (String.Format ("{0}.{1} field", typename, res),
1685 keybase + res, nurl);
1686 index_maker.Add (String.Format ("{0} field", res), res, nurl);
1691 foreach (Node nc in c.Nodes){
1692 string res = nc.Caption;
1693 string nurl = String.Format ("E:{0}.{1}", full, res);
1695 index_maker.Add (String.Format ("{0}.{1} event", typename, res),
1696 keybase + res, nurl);
1697 index_maker.Add (String.Format ("{0} event", res), res, nurl);
1701 foreach (Node nc in c.Nodes){
1702 string res = nc.Caption;
1703 string nurl = String.Format ("P:{0}.{1}", full, res);
1704 index_maker.Add (String.Format ("{0}.{1} property", typename, res),
1705 keybase + res, nurl);
1706 index_maker.Add (String.Format ("{0} property", res), res, nurl);
1710 foreach (Node nc in c.Nodes){
1711 string res = nc.Caption;
1712 int p = res.IndexOf ("(");
1714 res = res.Substring (0, p);
1715 string nurl = String.Format ("M:{0}.{1}", full, res);
1716 index_maker.Add (String.Format ("{0}.{1} method", typename, res),
1717 keybase + res, nurl);
1718 index_maker.Add (String.Format ("{0} method", res), res, nurl);
1723 foreach (Node nc in c.Nodes){
1724 string res = nc.Caption;
1725 string nurl = String.Format ("O:{0}.{1}", full, res);
1726 index_maker.Add (String.Format ("{0}.{1} operator", typename, res),
1727 keybase + res, nurl);
1732 } else if (doc_tag == "Enumeration"){
1734 // Enumerations: add the enumeration values
1736 index_maker.Add (type_node.Caption, typename, url);
1737 index_maker.Add (full + " " + doc_tag, full, url);
1739 // Now, pull the values.
1741 XmlDocument x = GetXmlFromUrl (type_node.URL, out rest);
1745 XmlNodeList members = x.SelectNodes ("/Type/Members/Member");
1747 if (members == null)
1750 foreach (XmlNode member_node in members){
1751 string enum_value = member_node.Attributes ["MemberName"].InnerText;
1752 string caption = enum_value + " value";
1753 index_maker.Add (caption, caption, url);
1755 } else if (doc_tag == "Delegate"){
1756 index_maker.Add (type_node.Caption, typename, url);
1757 index_maker.Add (full + " " + doc_tag, full, url);
1763 // Create list of documents for searching
1765 public override void PopulateSearchableIndex (IndexWriter writer)
1768 foreach (Node ns_node in Tree.Nodes) {
1769 Message (TraceLevel.Info, "\tNamespace: {0} ({1})", ns_node.Caption, ns_node.Nodes.Count);
1770 foreach (Node type_node in ns_node.Nodes) {
1771 string typename = type_node.Caption.Substring (0, type_node.Caption.IndexOf (' '));
1772 string full = ns_node.Caption + "." + typename;
1773 string doc_tag = GetKindFromCaption (type_node.Caption);
1774 string url = "T:" + full;
1776 XmlDocument xdoc = GetXmlFromUrl (type_node.URL, out rest);
1781 // For classes, structures or interfaces add a doc for the overview and
1782 // add a doc for every constructor, method, event, ...
1784 if (doc_tag == "Class" || doc_tag == "Structure" || doc_tag == "Interface"){
1786 // Adds a doc for every overview of every type
1787 SearchableDocument doc = new SearchableDocument ();
1788 doc.title = type_node.Caption;
1789 doc.hottext = typename;
1792 XmlNode node_sel = xdoc.SelectSingleNode ("/Type/Docs");
1793 text = new StringBuilder ();
1794 GetTextFromNode (node_sel, text);
1795 doc.text = text.ToString ();
1797 text = new StringBuilder ();
1798 GetExamples (node_sel, text);
1799 doc.examples = text.ToString ();
1801 writer.AddDocument (doc.LuceneDoc);
1803 //Add docs for contructors, methods, etc.
1804 foreach (Node c in type_node.Nodes) { // c = Constructors || Fields || Events || Properties || Methods || Operators
1806 if (c.Element == "*")
1809 foreach (Node nc in c.Nodes) {
1810 //xpath to the docs xml node
1812 if (c.Caption == "Constructors")
1813 xpath = String.Format ("/Type/Members/Member[{0}]/Docs", i++);
1814 else if (c.Caption == "Operators")
1815 xpath = String.Format ("/Type/Members/Member[@MemberName='op_{0}']/Docs", nc.Caption);
1817 xpath = String.Format ("/Type/Members/Member[@MemberName='{0}']/Docs", nc.Caption);
1818 //construct url of the form M:Array.Sort
1820 if (c.Caption == "Constructors")
1821 urlnc = String.Format ("{0}:{1}.{2}", c.Caption[0], ns_node.Caption, nc.Caption);
1823 urlnc = String.Format ("{0}:{1}.{2}.{3}", c.Caption[0], ns_node.Caption, typename, nc.Caption);
1826 SearchableDocument doc_nod = new SearchableDocument ();
1827 doc_nod.title = LargeName (nc);
1828 //dont add the parameters to the hottext
1829 int ppos = nc.Caption.IndexOf ('(');
1831 doc_nod.hottext = nc.Caption.Substring (0, ppos);
1833 doc_nod.hottext = nc.Caption;
1835 doc_nod.url = urlnc;
1837 XmlNode xmln = xdoc.SelectSingleNode (xpath);
1839 Error ("Problem: {0}, with xpath: {1}", urlnc, xpath);
1843 text = new StringBuilder ();
1844 GetTextFromNode (xmln, text);
1845 doc_nod.text = text.ToString ();
1847 text = new StringBuilder ();
1848 GetExamples (xmln, text);
1849 doc_nod.examples = text.ToString ();
1851 writer.AddDocument (doc_nod.LuceneDoc);
1855 // Enumerations: add the enumeration values
1857 } else if (doc_tag == "Enumeration"){
1859 XmlNodeList members = xdoc.SelectNodes ("/Type/Members/Member");
1860 if (members == null)
1863 text = new StringBuilder ();
1864 foreach (XmlNode member_node in members) {
1865 string enum_value = member_node.Attributes ["MemberName"].InnerText;
1866 text.Append (enum_value);
1868 GetTextFromNode (member_node["Docs"], text);
1871 SearchableDocument doc = new SearchableDocument ();
1873 text = new StringBuilder ();
1874 GetExamples (xdoc.SelectSingleNode ("/Type/Docs"), text);
1875 doc.examples = text.ToString ();
1877 doc.title = type_node.Caption;
1878 doc.hottext = xdoc.DocumentElement.Attributes["Name"].Value;
1880 doc.text = text.ToString();
1881 writer.AddDocument (doc.LuceneDoc);
1885 } else if (doc_tag == "Delegate"){
1886 SearchableDocument doc = new SearchableDocument ();
1887 doc.title = type_node.Caption;
1888 doc.hottext = xdoc.DocumentElement.Attributes["Name"].Value;
1891 XmlNode node_sel = xdoc.SelectSingleNode ("/Type/Docs");
1893 text = new StringBuilder ();
1894 GetTextFromNode (node_sel, text);
1895 doc.text = text.ToString();
1897 text = new StringBuilder ();
1898 GetExamples (node_sel, text);
1899 doc.examples = text.ToString();
1901 writer.AddDocument (doc.LuceneDoc);
1908 // Extract the interesting text from the docs node
1910 void GetTextFromNode (XmlNode n, StringBuilder sb)
1912 //don't include example code
1913 if (n.Name == "code")
1916 //include the url to which points the see tag
1917 if (n.Name == "see" && n.Attributes.Count > 0)
1918 sb.Append (n.Attributes [0].Value);
1920 //include the name of the parameter
1921 if (n.Name == "paramref" && n.Attributes.Count > 0)
1922 sb.Append (n.Attributes [0].Value);
1924 //include the contents for the node that contains text
1925 if (n.NodeType == XmlNodeType.Text)
1926 sb.Append (n.Value);
1928 //add the rest of xml tags recursively
1929 if (n.HasChildNodes)
1930 foreach (XmlNode n_child in n.ChildNodes)
1931 GetTextFromNode (n_child, sb);
1934 // Extract the code nodes from the docs
1936 void GetExamples (XmlNode n, StringBuilder sb)
1938 if (n.Name == "code") {
1939 sb.Append (n.InnerText);
1941 if (n.HasChildNodes)
1942 foreach (XmlNode n_child in n.ChildNodes)
1943 GetExamples (n_child, sb);
1947 // Extract a large name for the Node
1948 // (copied from mono-tools/docbrowser/browser.Render()
1949 static string LargeName (Node matched_node)
1951 string[] parts = matched_node.URL.Split('/', '#');
1952 if(parts.Length == 3 && parts[2] != String.Empty) { //List of Members, properties, events, ...
1953 return parts[1] + ": " + matched_node.Caption;
1954 } else if(parts.Length >= 4) { //Showing a concrete Member, property, ...
1955 return parts[1] + "." + matched_node.Caption;
1957 return matched_node.Caption;
1963 public class EcmaUncompiledHelpSource : EcmaHelpSource {
1964 readonly DirectoryInfo basedir;
1965 readonly XmlDocument basedoc;
1967 public new readonly string Name;
1968 public readonly string BasePath;
1970 public EcmaUncompiledHelpSource (string base_file) : base ()
1972 Message (TraceLevel.Info, "Loading uncompiled help from " + base_file);
1974 basedir = new DirectoryInfo(base_file);
1975 BasePath = basedir.FullName;
1977 basedoc = new XmlDocument();
1978 basedoc.Load(Path.Combine(basedir.FullName, "index.xml"));
1980 Name = basedoc.SelectSingleNode("Overview/Title").InnerText;
1982 bool has_content = false;
1984 foreach (XmlElement ns in basedoc.SelectNodes("Overview/Types/Namespace")) {
1986 Node nsnode = Tree.CreateNode(ns.GetAttribute("Name"), "N:" + ns.GetAttribute("Name"));
1988 bool has_types = false;
1989 foreach (XmlElement t in ns.SelectNodes("Type")) {
1991 string typename = EcmaDoc.GetDisplayName (t).Replace("+", ".");
1993 // Must load in each document to get the list of members...
1994 XmlDocument typedoc = new XmlDocument();
1995 typedoc.Load(Path.Combine(Path.Combine(basedir.FullName, ns.GetAttribute("Name")), t.GetAttribute("Name") + ".xml"));
1996 string kind = EcmaDoc.GetTypeKind (typedoc);
1998 string url = ns.GetAttribute("Name") + "." + t.GetAttribute("Name");
1999 Node typenode = nsnode.CreateNode(typename + " " + kind, "T:" + url);
2000 //Node typemembers = typenode.CreateNode("Members", "T:" + url + "/*");
2002 Hashtable groups = new Hashtable();
2003 Hashtable groups_count = new Hashtable();
2004 foreach (XmlElement member in typedoc.SelectNodes("Type/Members/Member")) {
2005 string membername = member.GetAttribute("MemberName");
2006 string membertype = member.SelectSingleNode("MemberType").InnerText;
2008 if (membertype == "Constructor")
2009 membername = t.GetAttribute("Name");
2010 if (membername.StartsWith("op_"))
2011 membertype = "Operator";
2014 if (groups.ContainsKey(membertype)) {
2015 group = (Node)groups[membertype];
2017 string membertypeplural = membertype + "s";
2018 if (membertypeplural == "Propertys") membertypeplural = "Properties";
2020 group = typenode.CreateNode(membertypeplural, "T:" + url + "/" + membertype[0]);
2021 groups[membertype] = group;
2022 groups_count[membertype] = 0;
2025 if (membertype == "Constructor" || membertype == "Method" ||
2026 (membertype == "Property" && member.SelectNodes("Parameters/Parameter").Count > 0)) {
2027 membername = EcmaHelpSource.MakeSignature(member, membertype == "Constructor" ? membername : null);
2028 } else if (membertype == "Operator") {
2030 EcmaHelpSource.MakeOperatorSignature(member, out dummy, out membername);
2033 int index = (int)groups_count[membertype];
2034 groups_count[membertype] = index + 1;
2036 group.CreateNode(membername, index.ToString());
2039 foreach (Node group in groups.Values)
2051 public override string GetIdFromUrl (string prefix, string ns, string type)
2054 throw new NotImplementedException();
2055 return Path.Combine(Path.Combine(basedir.FullName, ns), type + ".xml");
2058 protected override XmlDocument GetXmlFromUrl(string url, out string rest) {
2060 url = url.Substring(2);
2062 int sidx = url.IndexOf("/");
2066 rest = url.Substring(sidx+1);
2067 url = url.Substring(0, sidx);
2071 if (!RootTree.GetNamespaceAndType (url, out ns, out type)) {
2072 Message (TraceLevel.Error, "Could not determine namespace/type for {0}", url);
2076 string file = Path.Combine(Path.Combine(basedir.FullName, ns),
2077 ToEscapedTypeName (type).Replace ('.', '+') + ".xml");
2078 if (!new FileInfo(file).Exists) return null;
2080 XmlDocument typedoc = new XmlDocument();
2085 public override string GetText (string url, out Node match_node) {
2086 if (url == "root:") {
2090 XmlDocument index = new XmlDocument ();
2091 index.Load (Path.Combine (basedir.FullName, "index.xml"));
2092 XmlNodeList nodes = index.SelectNodes ("/Overview/Types/Namespace");
2094 //recreate masteroverview.xml
2095 XmlDocument summary = new XmlDocument ();
2096 XmlElement elements = summary.CreateElement ("elements");
2097 foreach (XmlNode node in nodes) {
2098 XmlElement ns = summary.CreateElement ("namespace");
2099 XmlAttribute attr = summary.CreateAttribute ("ns");
2100 attr.Value = EcmaDoc.GetDisplayName (node);
2101 ns.Attributes.Append (attr);
2102 elements.AppendChild (ns);
2104 summary.AppendChild (elements);
2106 XmlReader reader = new XmlTextReader (new StringReader (summary.OuterXml));
2108 //transform the recently created masteroverview.xml
2109 XsltArgumentList args = new XsltArgumentList();
2110 args.AddExtensionObject("monodoc:///extensions", ExtObject);
2111 args.AddParam("show", "", "masteroverview");
2112 string s = Htmlize(new XPathDocument (reader), args);
2113 return BuildHtml (css_ecma_code, js_code, s);
2115 return base.GetText(url, out match_node);
2118 protected override XmlDocument GetNamespaceDocument (string ns)
2120 XmlDocument nsdoc = new XmlDocument();
2121 nsdoc.Load (EcmaDoc.GetNamespaceFile (basedir.FullName, ns));
2123 XmlDocument elements = new XmlDocument();
2124 XmlElement docnode = elements.CreateElement("elements");
2125 elements.AppendChild (docnode);
2127 foreach (XmlElement doc in nsdoc.SelectNodes("Namespace/Docs/*")) {
2128 docnode.AppendChild(elements.ImportNode(doc, true));
2131 foreach (XmlElement t in basedoc.SelectNodes("Overview/Types/Namespace[@Name='" + ns + "']/Type")) {
2132 XmlDocument typedoc = new XmlDocument();
2133 typedoc.Load(Path.Combine(Path.Combine(basedir.FullName, ns), t.GetAttribute("Name") + ".xml"));
2136 switch (EcmaDoc.GetTypeKind(typedoc)) {
2137 case "Class": typekind = "class"; break;
2138 case "Enumeration": typekind = "enum"; break;
2139 case "Structure": typekind = "struct"; break;
2140 case "Delegate": typekind = "delegate"; break;
2141 case "Interface": typekind = "interface"; break;
2142 default: throw new InvalidOperationException();
2145 XmlElement typenode = elements.CreateElement(typekind);
2146 typenode.SetAttribute("name", EcmaDoc.GetDisplayName (t).Replace ('+', '.'));
2147 typenode.SetAttribute("fullname", ns + "." + t.GetAttribute("Name"));
2148 typenode.AppendChild(elements.ImportNode(typedoc.SelectSingleNode("Type/Docs/summary"), true));
2150 docnode.AppendChild(typenode);
2156 public override Stream GetHelpStream (string id)
2158 if (id == "ExtensionMethods.xml") {
2159 // TODO: generate ExtensionMethods.xml based on index.xml contents.
2164 public override XmlDocument GetHelpXmlWithChanges (string id)
2166 XmlDocument doc = new XmlDocument ();
2171 class UncompiledResolver : XmlResolver {
2172 public override Uri ResolveUri (Uri baseUri, string relativeUri)
2177 public override object GetEntity (Uri absoluteUri, string role, Type ofObjectToReturn)
2182 public override System.Net.ICredentials Credentials {
2187 protected override XmlResolver CreateDocumentResolver ()
2189 return new UncompiledResolver ();