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 ICSharpCode.SharpZipLib.Zip;
29 using Monodoc.Lucene.Net.Index;
30 using Monodoc.Lucene.Net.Documents;
32 using Mono.Documentation;
34 using BF = System.Reflection.BindingFlags;
37 // Helper routines to extract information from an Ecma XML document
39 public static class EcmaDoc {
40 public static string GetFullClassName (XmlDocument doc)
42 return doc.SelectSingleNode ("/Type").Attributes ["FullName"].InnerText;
45 public static string GetClassName (XmlDocument doc)
47 return GetDisplayName (doc.SelectSingleNode ("/Type")).Replace ("+", ".");
50 public static string GetDisplayName (XmlNode type)
52 if (type.Attributes ["DisplayName"] != null) {
53 return type.Attributes ["DisplayName"].Value;
55 return type.Attributes ["Name"].Value;
58 public static string GetClassAssembly (XmlDocument doc)
60 return doc.SelectSingleNode ("/Type/AssemblyInfo/AssemblyName").InnerText;
63 public static string GetClassNamespace (XmlDocument doc)
65 string s = doc.SelectSingleNode ("/Type").Attributes ["FullName"].InnerText;
67 return s.Substring (0, s.LastIndexOf ("."));
70 public static string GetTypeKind (XmlDocument doc)
72 XmlNode node = doc.SelectSingleNode ("/Type/Base/BaseTypeName");
75 if (GetFullClassName (doc) == "System.Object")
80 switch (node.InnerText){
82 case "System.Delegate":
83 case "System.MulticastDelegate":
85 case "System.ValueType":
95 // Utility function: converts a fully .NET qualified type name into a C#-looking one
97 public static string ConvertCTSName (string type)
99 if (!type.StartsWith ("System."))
102 if (type.EndsWith ("*"))
103 return ConvertCTSName(type.Substring(0, type.Length - 1)) + "*";
104 if (type.EndsWith ("&"))
105 return ConvertCTSName(type.Substring(0, type.Length - 1)) + "&";
106 if (type.EndsWith ("[]"))
107 return ConvertCTSName(type.Substring(0, type.Length - 2)) + "[]";
110 case "System.Byte": return "byte";
111 case "System.SByte": return "sbyte";
112 case "System.Int16": return "short";
113 case "System.Int32": return "int";
114 case "System.Int64": return "long";
116 case "System.UInt16": return "ushort";
117 case "System.UInt32": return "uint";
118 case "System.UInt64": return "ulong";
120 case "System.Single": return "float";
121 case "System.Double": return "double";
122 case "System.Decimal": return "decimal";
123 case "System.Boolean": return "bool";
124 case "System.Char": return "char";
125 case "System.String": return "string";
127 case "System.Object": return "object";
128 case "System.Void": return "void";
131 if (type.LastIndexOf(".") == 6)
132 return type.Substring(7);
137 internal static string GetNamespaceFile (string dir, string ns)
139 string nsxml = Path.Combine (dir, "ns-" + ns + ".xml");
140 if (!File.Exists (nsxml))
141 nsxml = Path.Combine (dir, ns + ".xml");
147 // The Ecma documentation provider:
149 // It populates a tree with contents during the help assembly
151 public class EcmaProvider : Provider {
152 ArrayList/*<string>*/ asm_dirs = new ArrayList ();
154 public EcmaProvider ()
158 public EcmaProvider (string base_directory)
160 AddDirectory (base_directory);
163 public void AddDirectory (string directory)
165 if (!Directory.Exists (directory))
166 throw new FileNotFoundException (String.Format ("The directory `{0}' does not exist", directory));
167 asm_dirs.Add (directory);
170 public override void PopulateTree (Tree tree)
172 ArrayList ns_dirs = new ArrayList ();
173 foreach (string asm in asm_dirs) {
174 ns_dirs.AddRange (Directory.GetDirectories (asm));
177 foreach (string ns in ns_dirs){
178 string basedir = Directory.GetParent (ns).FullName;
179 string [] files = Directory.GetFiles (ns);
183 Hashtable nsnodes = new Hashtable();
185 foreach (string file in files){
186 if (!file.EndsWith (".xml"))
189 if (ns_node == null) {
190 tn = Path.GetFileName (ns);
191 tree.HelpSource.Message (TraceLevel.Info, "Processing namespace {0}", tn);
192 ns_node = tree.LookupNode (tn, "N:" + tn);
193 string ns_summary_file = EcmaDoc.GetNamespaceFile (basedir, tn);
195 nsnodes[ns_node] = nsnodes;
197 if (File.Exists (ns_summary_file)) {
198 XmlDocument nsSummaryFile = new XmlDocument ();
199 nsSummaryFile.Load (ns_summary_file);
200 namespace_realpath [tn] = ns_summary_file;
202 XmlNode ns_summary = nsSummaryFile.SelectSingleNode ("Namespace/Docs/summary");
203 if (ns_summary != null && ns_summary.InnerText.Trim () != "To be added." && ns_summary.InnerText != "") {
204 namespace_summaries [tn] = ns_summary;
205 namespace_remarks [tn] = nsSummaryFile.SelectSingleNode ("Namespace/Docs/remarks");
208 } else if (!namespace_summaries.ContainsKey (tn)) {
209 namespace_summaries [tn] = null;
210 namespace_remarks [tn] = null;
213 tree.HelpSource.Message (TraceLevel.Verbose, " Processing input file {0}", Path.GetFileName (file));
215 PopulateClass (tree, tn, ns_node, file);
218 // Sort the list of types in each namespace
219 foreach (Node ns_node2 in nsnodes.Keys)
225 struct TypeInfo : IComparable {
226 public string type_assembly;
227 public string type_name;
228 public string type_full;
229 public string type_kind;
230 public XmlNode type_doc;
232 public TypeInfo (string k, string a, string f, string s, XmlNode n)
241 public int CompareTo (object b)
244 TypeInfo nb = (TypeInfo) b;
246 return String.Compare (na.type_full, nb.type_full);
251 // Packs a file with the summary data
253 public override void CloseTree (HelpSource hs, Tree tree)
255 foreach (DictionaryEntry de in class_summaries){
256 XmlDocument doc = new XmlDocument ();
257 string ns = (string) de.Key;
259 ArrayList list = (ArrayList) de.Value;
262 XmlElement elements = doc.CreateElement ("elements");
263 doc.AppendChild (elements);
265 if (namespace_summaries [ns] != null)
266 elements.AppendChild (doc.ImportNode ((XmlNode)namespace_summaries [ns],true));
268 elements.AppendChild (doc.CreateElement("summary"));
270 if (namespace_remarks [ns] != null)
271 elements.AppendChild (doc.ImportNode ((XmlNode)namespace_remarks [ns],true));
273 elements.AppendChild (doc.CreateElement("remarks"));
275 hs.Message (TraceLevel.Info, "Have {0} elements in the {1}", list.Count, ns);
276 foreach (TypeInfo p in list){
279 switch (p.type_kind){
281 e = doc.CreateElement ("class");
285 e = doc.CreateElement ("enum");
289 e = doc.CreateElement ("struct");
293 e = doc.CreateElement ("delegate");
297 e = doc.CreateElement ("interface");
301 e.SetAttribute ("name", p.type_name);
302 e.SetAttribute ("fullname", p.type_full);
303 e.SetAttribute ("assembly", p.type_assembly);
304 XmlNode copy = doc.ImportNode (p.type_doc, true);
305 e.AppendChild (copy);
306 elements.AppendChild (e);
308 hs.PackXml ("xml.summary." + ns, doc,(string) namespace_realpath[ns]);
312 XmlDocument nsSummary = new XmlDocument ();
313 XmlElement root = nsSummary.CreateElement ("elements");
314 nsSummary.AppendChild (root);
316 foreach (DictionaryEntry de in namespace_summaries) {
317 XmlNode n = (XmlNode)de.Value;
318 XmlElement summary = nsSummary.CreateElement ("namespace");
319 summary.SetAttribute ("ns", (string)de.Key);
320 root.AppendChild (summary);
322 summary.AppendChild (nsSummary.ImportNode (n,true));
324 summary.AppendChild (nsSummary.CreateElement("summary"));
326 tree.HelpSource.PackXml ("mastersummary.xml", nsSummary, null);
327 AddExtensionMethods (tree);
330 void AddExtensionMethods (Tree tree)
332 XmlDocument extensions = null;
335 foreach (string asm in asm_dirs) {
336 string overview_file = Path.Combine (asm, "index.xml");
337 if (File.Exists (overview_file)) {
338 XmlDocument overview = new XmlDocument ();
339 overview.Load (overview_file);
340 XmlNodeList e = overview.SelectNodes ("/Overview/ExtensionMethods/*");
342 if (extensions == null) {
343 extensions = new XmlDocument ();
344 root = extensions.CreateElement ("ExtensionMethods");
345 extensions.AppendChild (root);
347 foreach (XmlNode n in e) {
349 root.AppendChild (extensions.ImportNode (n, true));
354 if (extensions != null) {
355 tree.HelpSource.Message (TraceLevel.Info, "Have {0} extension methods", numMethods);
356 tree.HelpSource.PackXml ("ExtensionMethods.xml", extensions, "ExtensionMethods.xml");
360 Hashtable class_summaries = new Hashtable ();
361 Hashtable namespace_summaries = new Hashtable ();
362 Hashtable namespace_remarks = new Hashtable ();
363 Hashtable namespace_realpath = new Hashtable ();
366 void PopulateClass (Tree tree, string ns, Node ns_node, string file)
368 doc = new XmlDocument ();
371 string name = EcmaDoc.GetClassName (doc);
372 string assembly = EcmaDoc.GetClassAssembly (doc);
373 string kind = EcmaDoc.GetTypeKind (doc);
374 string full = EcmaDoc.GetFullClassName (doc);
377 string file_code = ns_node.tree.HelpSource.PackFile (file);
379 XmlNode class_summary = doc.SelectSingleNode ("/Type/Docs/summary");
380 ArrayList l = (ArrayList) class_summaries [ns];
382 l = new ArrayList ();
383 class_summaries [ns] = (object) l;
385 l.Add (new TypeInfo (kind, assembly, full, name, class_summary));
387 class_node = ns_node.LookupNode (String.Format ("{0} {1}", name, kind), "ecma:" + file_code + "#" + name + "/");
389 if (kind == "Delegate") {
390 if (doc.SelectSingleNode("/Type/ReturnValue") == null)
391 tree.HelpSource.Message (TraceLevel.Error, "Delegate " + name + " does not have a ReturnValue node. See the ECMA-style updates.");
394 if (kind == "Enumeration")
397 if (kind == "Delegate")
401 // Always add the Members node
403 class_node.CreateNode ("Members", "*");
405 PopulateMember (name, class_node, "Constructor", "Constructors");
406 PopulateMember (name, class_node, "Method", "Methods");
407 PopulateMember (name, class_node, "Property", "Properties");
408 PopulateMember (name, class_node, "Field", "Fields");
409 PopulateMember (name, class_node, "Event", "Events");
410 PopulateMember (name, class_node, "Operator", "Operators");
417 public NodeIndex (XmlNode node, int index)
424 struct NodeCompare : IComparer {
425 public int Compare (object a, object b)
427 NodeIndex na = (NodeIndex) a;
428 NodeIndex nb = (NodeIndex) b;
430 return String.Compare (na.node.Attributes ["MemberName"].InnerText,
431 nb.node.Attributes ["MemberName"].InnerText);
435 string GetMemberName (XmlNode node)
437 return node.Attributes ["MemberName"].InnerText;
441 // Performs an XPath query on the document to extract the nodes for the various members
442 // we also use some extra text to pluralize the caption
444 void PopulateMember (string typename, Node node, string type, string caption)
446 string select = type;
447 if (select == "Operator") select = "Method";
449 XmlNodeList list1 = doc.SelectNodes (String.Format ("/Type/Members/Member[MemberType=\"{0}\"]", select));
450 ArrayList list = new ArrayList();
452 foreach (XmlElement n in list1) {
453 n.SetAttribute("assembler_index", (i++).ToString());
454 if (type == "Method" && GetMemberName(n).StartsWith("op_")) continue;
455 if (type == "Operator" && !GetMemberName(n).StartsWith("op_")) continue;
459 int count = list.Count;
465 string key = type.Substring (0, 1);
466 nodes_node = node.CreateNode (caption, key);
471 foreach (XmlElement n in list)
472 nodes_node.CreateNode (GetMemberName (n), n.GetAttribute("assembler_index"));
476 foreach (XmlElement n in list)
477 nodes_node.CreateNode (EcmaHelpSource.MakeSignature(n, typename), n.GetAttribute("assembler_index"));
480 case "Property": // properties with indexers can be overloaded too
483 foreach (XmlElement n in list) {
484 bool multiple = false;
485 foreach (XmlNode nn in list) {
486 if (n != nn && GetMemberName(n) == nn.Attributes ["MemberName"].InnerText) {
492 string group, name, sig;
493 if (type != "Operator") {
494 name = GetMemberName(n);
495 sig = EcmaHelpSource.MakeSignature(n, null);
498 EcmaHelpSource.MakeOperatorSignature(n, out name, out sig);
503 nodes_node.LookupNode (group, group)
504 .CreateNode (sig, n.GetAttribute("assembler_index"));
506 nodes_node.CreateNode (name, n.GetAttribute("assembler_index"));
510 foreach (Node n in nodes_node.Nodes) {
518 throw new InvalidOperationException();
527 // The Ecma HelpSource provider
529 public class EcmaHelpSource : HelpSource {
531 public EcmaHelpSource (string base_file, bool create) : base (base_file, create)
533 ExtObject = new ExtensionObject (this);
536 public EcmaHelpSource ()
538 ExtObject = new ExtensionObject (this);
541 static string css_ecma;
542 public static string css_ecma_code {
544 if (css_ecma != null)
547 System.Reflection.Assembly assembly = typeof(EcmaHelpSource).Assembly;
548 Stream str_css = assembly.GetManifestResourceStream ("mono-ecma.css");
549 css_ecma = (new StreamReader (str_css)).ReadToEnd();
551 css_ecma = String.Empty;
557 public override string InlineCss {
558 get {return base.InlineCss + css_ecma_code;}
562 public static string js_code {
567 System.Reflection.Assembly assembly = typeof(EcmaHelpSource).Assembly;
568 Stream str_js = assembly.GetManifestResourceStream ("helper.js");
569 js = (new StreamReader (str_js)).ReadToEnd();
577 public override string InlineJavaScript {
578 get {return js_code + base.InlineJavaScript;}
581 public override string GetText (string url, out Node match_node)
587 XmlReader summary = GetHelpXml ("mastersummary.xml");
591 XsltArgumentList args = new XsltArgumentList();
592 args.AddExtensionObject("monodoc:///extensions", ExtObject);
593 args.AddParam("show", "", "masteroverview");
594 string s = Htmlize(new XPathDocument (summary), args);
595 return BuildHtml (css_ecma_code, js_code, s);
598 if (url.StartsWith ("ecma:")) {
599 string s = GetTextFromUrl (url);
600 return BuildHtml (css_ecma_code, js_code, s);
607 string RenderMemberLookup (string typename, string member, ref Node type_node)
609 if (type_node.Nodes == null)
612 string membername = member;
613 string[] argtypes = null;
614 if (member.IndexOf("(") > 0) {
615 membername = membername.Substring(0, member.IndexOf("("));
616 member = member.Replace("@", "&");
618 // reform the member signature with CTS names
620 string x = member.Substring(member.IndexOf("(")+1);
621 argtypes = x.Substring(0, x.Length-1).Split(',', ':'); // operator signatures have colons
623 if (membername == ".ctor")
624 membername = typename;
626 member = membername + "(";
627 for (int i = 0; i < argtypes.Length; i++) {
628 argtypes[i] = EcmaDoc.ConvertCTSName(argtypes[i]);
629 if (i > 0) member += ",";
630 member += argtypes[i];
635 // Check if a node caption matches exactly
637 bool isoperator = false;
639 if ((membername == "op_Implicit" || membername == "op_Explicit") && argtypes.Length == 2) {
641 membername = "Conversion";
642 member = argtypes[0] + " to " + argtypes[1];
643 } else if (membername.StartsWith("op_")) {
645 membername = membername.Substring(3);
648 foreach (Node x in type_node.Nodes){
651 if (isoperator && x.Caption != "Operators")
654 foreach (Node m in x.Nodes) {
655 string caption = m.Caption;
656 string ecaption = ToEscapedMemberName (caption);
658 // No overloading (usually), is just the member name. The whole thing for constructors.
659 if (caption == membername || caption == member ||
660 ecaption == membername || ecaption == member) {
662 return GetTextFromUrl (m.URL);
664 } else if (caption == member || ecaption == member) {
665 // Though there are overloads, no arguments are in the url, so use this base node
667 return GetTextFromUrl (m.URL);
669 // Check subnodes which are the overloads -- must match signature
670 foreach (Node mm in m.Nodes) {
671 ecaption = ToEscapedTypeName (mm.Caption);
672 if (mm.Caption == member || ecaption == member) {
674 return GetTextFromUrl (mm.URL);
684 public static string MakeSignature (XmlNode n, string cstyleclass)
688 if (cstyleclass == null)
689 sig = n.Attributes["MemberName"].InnerText;
695 /*if (n.SelectSingleNode("MemberType").InnerText == "Method" || n.SelectSingleNode("MemberType").InnerText == "Constructor") {*/ // properties with indexers too
696 XmlNodeList paramnodes = n.SelectNodes("Parameters/Parameter");
699 foreach (XmlNode p in paramnodes) {
700 if (!first) sig += ",";
701 string type = p.Attributes["Type"].InnerText;
702 type = EcmaDoc.ConvertCTSName(type);
712 public static void MakeOperatorSignature (XmlNode n, out string nicename, out string sig)
716 name = n.Attributes["MemberName"].InnerText;
717 nicename = name.Substring(3);
720 // unary operators: no overloading possible
721 case "op_UnaryPlus": case "op_UnaryNegation": case "op_LogicalNot": case "op_OnesComplement":
722 case "op_Increment": case "op_Decrement":
723 case "op_True": case "op_False":
727 // binary operators: overloading is possible based on parameter types
728 case "op_Addition": case "op_Subtraction": case "op_Multiply": case "op_Division": case "op_Modulus":
729 case "op_BitwiseAnd": case "op_BitwiseOr": case "op_ExclusiveOr":
730 case "op_LeftShift": case "op_RightShift":
731 case "op_Equality": case "op_Inequality":
732 case "op_GreaterThan": case "op_LessThan": case "op_GreaterThanOrEqual": case "op_LessThanOrEqual":
733 XmlNodeList paramnodes = n.SelectNodes("Parameters/Parameter");
734 sig = nicename + "(";
736 foreach (XmlNode p in paramnodes) {
737 if (!first) sig += ",";
738 string type = p.Attributes["Type"].InnerText;
739 type = EcmaDoc.ConvertCTSName(type);
746 // overloading based on parameter and return type
747 case "op_Implicit": case "op_Explicit":
748 nicename = "Conversion";
749 string arg = n.SelectSingleNode("Parameters/Parameter/@Type").InnerText;
750 string ret = n.SelectSingleNode("ReturnValue/ReturnType").InnerText;
751 sig = EcmaDoc.ConvertCTSName(arg) + " to " + EcmaDoc.ConvertCTSName(ret);
755 throw new InvalidOperationException();
760 // This routine has to perform a lookup on a type.
762 // Example: T:System.Text.StringBuilder
764 // The prefix is the kind of opereation being requested (T:, E:, M: etc)
765 // ns is the namespace being looked up
766 // type is the type being requested
768 // This has to walk our toplevel (which is always a namespace)
769 // And then the type space, and then walk further down depending on the request
771 public override string RenderTypeLookup (string prefix, string ns, string type, string member, out Node match_node)
773 string url = GetUrlForType (prefix, ns, type, member, out match_node);
774 if (url == null) return null;
775 return GetTextFromUrl (url);
778 public virtual string GetIdFromUrl (string prefix, string ns, string type)
780 Node tmp_node = new Node (Tree, "", "");
781 string url = GetUrlForType (prefix, ns, type, null, out tmp_node);
782 if (url == null) return null;
783 return GetFile (url.Substring (5), out url);
786 public string GetUrlForType (string prefix, string ns, string type, string member, out Node match_node)
788 if (!prefix.EndsWith(":"))
789 throw new ArgumentException("prefix");
792 member = member.Replace ("#", ".").Replace ("{", "<").Replace ("}", ">");
794 // If a nested type, compare only inner type name to node list.
795 // This should be removed when the node list doesn't lose the containing type name.
796 type = ToEscapedTypeName (type.Replace("+", "."));
797 MatchAttempt[] attempts = GetAttempts (ns, type);
799 foreach (Node ns_node in Tree.Nodes){
800 string ns_node_namespace = ns_node.Element.Substring (2);
802 if (!MatchesNamespace (attempts, ns_node_namespace, out type))
805 foreach (Node type_node in ns_node.Nodes){
806 string element = type_node.Element;
809 if (element.StartsWith("T:")) {
810 cname = element.Substring(2);
812 RootTree.GetNamespaceAndType (cname, out _ns, out cname);
813 cname = ToEscapedTypeName (cname);
814 int pidx = cname.LastIndexOf (".");
815 cname = cname.Substring(pidx+1);
816 pidx = cname.LastIndexOf ("/");
818 cname = cname.Substring(0, pidx);
819 cname = cname.Replace("+", ".");
821 int pidx = element.IndexOf ("#");
822 int sidx = element.IndexOf ("/");
823 cname = element.Substring (pidx + 1, sidx-pidx-1);
824 cname = ToEscapedTypeName (cname);
827 //Console.WriteLine ("t:{0} cn:{1} p:{2}", type, cname, prefix);
829 if (type == cname && prefix == "T:") {
830 match_node = type_node;
831 return type_node.URL;
832 } else if (type.StartsWith (cname)){
833 int p = cname.Length;
835 match_node = type_node;
837 string ret = RenderMemberLookup (type, member, ref match_node);
839 return type_node.URL;
840 return match_node.URL;
842 } else if (type [p] == '/'){
844 // This handles summaries
847 foreach (Node nd in type_node.Nodes) {
848 if (nd.Element [nd.Element.Length - 1] == type [p + 1]) {
854 string ret = type_node.URL;
855 if (!ret.EndsWith("/")) ret += "/";
856 return ret + type.Substring (p + 1);
866 struct MatchAttempt {
867 public string Namespace;
870 public MatchAttempt (string ns, string t)
877 private static MatchAttempt[] GetAttempts (string ns, string type)
879 MatchAttempt[] attempts = new MatchAttempt [Count (ns, '.') + 1];
881 for (int i = 0; i < ns.Length; ++i) {
883 attempts [wns++] = new MatchAttempt (ns.Substring (0, i),
884 ns.Substring (i+1) + "." + type);
886 attempts [wns++] = new MatchAttempt (ns, type);
890 private static int Count (string s, char c)
893 for (int i = 0; i < s.Length; ++i)
899 private static bool MatchesNamespace (MatchAttempt[] attempts, string ns, out string type)
901 for (int i = 0; i < attempts.Length; ++i)
902 if (ns == attempts [i].Namespace) {
903 type = attempts [i].Type;
910 public static string ToEscapedTypeName (string typename)
912 return ToEscapedName (typename, "`");
915 static string ToEscapedName (string name, string escape)
917 StringBuilder filename = new StringBuilder (name.Length);
922 for (int i = 0; i < name.Length; ++i) {
934 filename.Append (escape).Append ((numArgs+1).ToString());
949 return filename.ToString ();
952 static string ToEscapedMemberName (string membername)
954 return ToEscapedName (membername, "``");
957 public override string GetNodeXPath (XPathNavigator n)
959 if (n.Matches ("/Type/Docs/param")) {
960 string type_name = (string) n.Evaluate ("string (ancestor::Type/@FullName)");
961 string param_name = (string) n.Evaluate ("string (@name)");
963 return String.Format ("/Type [@FullName = '{0}']/Docs/param[@name='{1}']", type_name, param_name);
966 if (n.Matches ("/Type/Docs/*")) {
967 string type_name = (string) n.Evaluate ("string (ancestor::Type/@FullName)");
969 return String.Format ("/Type [@FullName = '{0}']/Docs/{1}", type_name, n.Name);
972 if (n.Matches ("/Type/Members/Member/Docs/*")) {
973 string type_name = (string) n.Evaluate ("string (ancestor::Type/@FullName)");
974 string member_name = (string) n.Evaluate ("string (ancestor::Member/@MemberName)");
975 string member_sig = (string) n.Evaluate ("string (ancestor::Member/MemberSignature [@Language='C#']/@Value)");
976 string param_name = (string) n.Evaluate ("string (@name)");
978 if (param_name == null || param_name == "") {
979 return String.Format (
980 "/Type [@FullName = '{0}']/Members/Member [@MemberName = '{1}'][MemberSignature [@Language='C#']/@Value = '{2}']/Docs/{3}",
981 type_name, member_name, member_sig, n.Name);
983 return String.Format (
984 "/Type [@FullName = '{0}']/Members/Member [@MemberName = '{1}'][MemberSignature [@Language='C#']/@Value = '{2}']/Docs/param [@name = '{3}']",
985 type_name, member_name, member_sig, param_name);
989 Message (TraceLevel.Warning, "WARNING: Was not able to get clean XPath expression for node {0}", EditingUtils.GetXPath (n));
990 return base.GetNodeXPath (n);
993 protected virtual XmlReader GetNamespaceDocument (string ns) {
994 return GetHelpXml ("xml.summary." + ns);
997 public override string RenderNamespaceLookup (string nsurl, out Node match_node)
999 foreach (Node ns_node in Tree.Nodes){
1000 if (ns_node.Element != nsurl)
1003 match_node = ns_node;
1004 string ns_name = nsurl.Substring (2);
1006 XmlDocument doc = GetHelpXmlWithChanges("xml.summary." + ns_name);
1010 XsltArgumentList args = new XsltArgumentList();
1011 args.AddExtensionObject("monodoc:///extensions", ExtObject);
1012 args.AddParam("show", "", "namespace");
1013 args.AddParam("namespace", "", ns_name);
1014 string s = Htmlize(doc, args);
1015 return BuildHtml (css_ecma_code, js_code, s);
1022 private string SelectString(XmlNode node, string xpath) {
1023 XmlNode ret = node.SelectSingleNode(xpath);
1024 if (ret == null) return "";
1025 return ret.InnerText;
1027 private int SelectCount(XmlNode node, string xpath) {
1028 return node.SelectNodes(xpath).Count;
1032 // Returns the XmlDocument from the given url, and fills in `rest'
1034 protected virtual XmlDocument GetXmlFromUrl(string url, out string rest) {
1036 url = url.Substring (5);
1037 string file = GetFile (url, out rest);
1039 // Console.WriteLine ("Got [{0}] and [{1}]", file, rest);
1040 return GetHelpXmlWithChanges (file);
1043 string GetTextFromUrl (string url)
1048 XmlDocument doc = GetXmlFromUrl (url, out rest);
1050 // Load base-type information so the stylesheet can draw the inheritance
1051 // tree and (optionally) include inherited members in the members list.
1052 XmlElement basenode = (XmlElement)doc.SelectSingleNode("Type/Base");
1053 XmlElement membersnode = (XmlElement)doc.SelectSingleNode("Type/Members");
1054 XmlNode basetype = doc.SelectSingleNode("Type/Base/BaseTypeName");
1056 while (basetype != null) {
1057 // Add the ParentType node to Type/Base
1058 XmlElement inheritancenode = doc.CreateElement("ParentType");
1059 inheritancenode.SetAttribute("Type", basetype.InnerText);
1060 inheritancenode.SetAttribute("Order", (baseindex++).ToString());
1061 basenode.AppendChild(inheritancenode);
1063 // Load the base type XML data
1064 int dot = basetype.InnerText.LastIndexOf('.');
1065 string ns = basetype.InnerText.Substring(0, dot == -1 ? 0 : dot);
1066 string n = basetype.InnerText.Substring(dot == -1 ? 0 : dot+1);
1067 string basetypeurl = GetUrlForType("T:", ns, n, null, out node);
1068 XmlDocument basetypedoc = null;
1069 if (basetypeurl != null)
1070 basetypedoc = GetXmlFromUrl (basetypeurl, out rest2);
1071 if (basetypedoc == null) {
1072 inheritancenode.SetAttribute("Incomplete", "1");
1076 if (SettingsHandler.Settings.ShowInheritedMembers) {
1077 // Add inherited members
1078 foreach (XmlElement member in basetypedoc.SelectNodes("Type/Members/Member[not(MemberType='Constructor')]")) {
1079 string sig = SelectString(member, "MemberSignature[@Language='C#']/@Value");
1080 if (sig.IndexOf(" static ") >= 0) continue;
1082 // If the signature of member matches the signature of a member already in the XML,
1084 string xpath = "@MemberName='" + SelectString(member, "@MemberName") + "'";
1085 xpath += " and ReturnValue/ReturnType='" + SelectString(member, "ReturnValue/ReturnType") + "'";
1086 xpath += " and count(Parameters/Parameter)=" + SelectCount(member, "Parameters/Parameter") + "";
1088 foreach (XmlElement p in member.SelectNodes("Parameters/Parameter")) {
1089 xpath += " and Parameters/Parameter[position()=" + pi + "]/@Type = '" + p.GetAttribute("Type") + "'";
1093 // If a signature match is found, don't add.
1094 XmlNode match = membersnode.SelectSingleNode("Member[" + xpath + "]");
1098 member.SetAttribute("DeclaredIn", basetype.InnerText);
1099 membersnode.AppendChild(membersnode.OwnerDocument.ImportNode(member, true));
1103 basetype = basetypedoc.SelectSingleNode("Type/Base/BaseTypeName");
1105 ArrayList extensions = new ArrayList ();
1106 foreach (HelpSource hs in RootTree.HelpSources) {
1107 EcmaHelpSource es = hs as EcmaHelpSource;
1110 Stream s = es.GetHelpStream ("ExtensionMethods.xml");
1112 XmlDocument d = new XmlDocument ();
1114 foreach (XmlNode n in d.SelectNodes ("/ExtensionMethods/*")) {
1119 XmlDocUtils.AddExtensionMethods (doc, extensions, delegate (string s) {
1120 return RootTree.GetHelpXml ("T:" + s);
1123 XsltArgumentList args = new XsltArgumentList();
1125 args.AddExtensionObject("monodoc:///extensions", ExtObject);
1128 args.AddParam("show", "", "typeoverview");
1129 string s = Htmlize(doc, args);
1130 return BuildHtml (css_ecma_code, js_code, s);
1133 string [] nodes = rest.Split (new char [] {'/'});
1135 switch (nodes.Length) {
1137 args.AddParam("show", "", "members");
1138 args.AddParam("index", "", "all");
1141 // Could either be a single member, or an overload thingy
1143 int.Parse (nodes [1]); // is it an int
1145 args.AddParam("show", "", "member");
1146 args.AddParam("index", "", nodes [1]);
1148 args.AddParam("show", "", "overloads");
1149 args.AddParam("index", "", nodes [1]);
1153 args.AddParam("show", "", "member");
1154 args.AddParam("index", "", nodes [2]);
1157 return "What the hell is this URL " + url;
1162 args.AddParam("membertype", "", "Constructor");
1165 args.AddParam("membertype", "", "Method");
1168 args.AddParam("membertype", "", "Property");
1171 args.AddParam("membertype", "", "Field");
1174 args.AddParam("membertype", "", "Event");
1177 args.AddParam("membertype", "", "Operator");
1180 args.AddParam("membertype", "", "ExtensionMethod");
1183 args.AddParam("membertype", "", "All");
1186 return "Unknown url: " + url;
1189 string html = Htmlize(doc, args);
1190 return BuildHtml (css_ecma_code, js_code, html);
1194 public override void RenderPreviewDocs (XmlNode newNode, XmlWriter writer)
1196 XsltArgumentList args = new XsltArgumentList ();
1197 args.AddExtensionObject ("monodoc:///extensions", ExtObject);
1199 Htmlize (newNode, args, writer);
1202 static XslTransform ecma_transform;
1204 public static string Htmlize (IXPathNavigable ecma_xml)
1206 return Htmlize(ecma_xml, null);
1209 public static string Htmlize (IXPathNavigable ecma_xml, XsltArgumentList args)
1213 StringWriter output = new StringWriter ();
1214 ecma_transform.Transform (ecma_xml, args, output, null);
1215 return output.ToString ();
1218 static void Htmlize (IXPathNavigable ecma_xml, XsltArgumentList args, XmlWriter w)
1222 if (ecma_xml == null)
1225 ecma_transform.Transform (ecma_xml, args, w, null);
1228 static XslTransform ecma_transform_css, ecma_transform_no_css;
1229 static void EnsureTransform ()
1231 if (ecma_transform == null) {
1232 ecma_transform_css = new XslTransform ();
1233 ecma_transform_no_css = new XslTransform ();
1234 Assembly assembly = System.Reflection.Assembly.GetCallingAssembly ();
1236 Stream stream = assembly.GetManifestResourceStream ("mono-ecma-css.xsl");
1237 XmlReader xml_reader = new XmlTextReader (stream);
1238 XmlResolver r = new ManifestResourceResolver (".");
1239 ecma_transform_css.Load (xml_reader, r, null);
1241 stream = assembly.GetManifestResourceStream ("mono-ecma.xsl");
1242 xml_reader = new XmlTextReader (stream);
1243 ecma_transform_no_css.Load (xml_reader, r, null);
1246 ecma_transform = ecma_transform_css;
1248 ecma_transform = ecma_transform_no_css;
1251 // This ExtensionObject stuff is used to check at run time whether
1252 // types and members have been implemented and whether there are any
1253 // MonoTODO attributes left on them.
1255 public readonly ExtensionObject ExtObject;
1256 public class ExtensionObject {
1257 readonly EcmaHelpSource hs;
1260 // We are setting this to quiet now, as we need to transition
1261 // monodoc to run with the 2.x runtime and provide accurate
1262 // information in those cases.
1266 public ExtensionObject (EcmaHelpSource hs)
1271 public string Colorize(string code, string lang) {
1272 return(Mono.Utilities.Colorizer.Colorize(code,lang));
1274 // Used by stylesheet to nicely reformat the <see cref=> tags.
1275 public string MakeNiceSignature(string sig, string contexttype)
1283 sig = sig.Substring(2);
1286 case 'N': return sig;
1287 case 'T': return ShortTypeName(sig, contexttype);
1289 case 'C': case 'M': case 'P': case 'F': case 'E':
1290 string type, mem, arg;
1294 if (s == 'C' || s == 'M')
1295 paren = sig.IndexOf("(");
1297 paren = sig.IndexOf("[");
1301 if (paren > 0 && paren < sig.Length-1) {
1302 string[] args = sig.Substring(paren+1, sig.Length-paren-2).Split(',');
1303 for (int i = 0; i < args.Length; i++)
1304 args[i] = ShortTypeName(args[i], contexttype);
1305 arg = "(" + String.Join(", ", args) + ")";
1306 sig = sig.Substring(0, paren);
1311 // Get type and member names
1312 int dot = sig.LastIndexOf(".");
1313 if (s == 'C' || dot <= 0 || dot == sig.Length-1) {
1317 type = sig.Substring(0, dot);
1318 mem = sig.Substring(dot);
1321 type = ShortTypeName(type, contexttype);
1323 return type + mem + arg;
1330 public string EditUrl (XPathNodeIterator itr)
1332 if (itr.MoveNext ())
1333 return hs.GetEditUri (itr.Current);
1338 public string EditUrlNamespace (XPathNodeIterator itr, string ns, string section)
1340 if (hs is EcmaUncompiledHelpSource)
1341 return "edit:file:" + Path.Combine(((EcmaUncompiledHelpSource)hs).BasePath, ns + ".xml") + "@/Namespace/Docs/" + section;
1342 else if (itr.MoveNext ())
1343 return EditingUtils.FormatEditUri(itr.Current.BaseURI, "/elements/" + section);
1347 private static string ShortTypeName(string name, string contexttype)
1349 int dot = contexttype.LastIndexOf(".");
1350 if (dot < 0) return name;
1351 string contextns = contexttype.Substring(0, dot+1);
1353 if (name == contexttype)
1354 return name.Substring(dot+1);
1356 if (name.StartsWith(contextns))
1357 return name.Substring(contextns.Length);
1359 return name.Replace("+", ".");
1362 public string MonoImpInfo(string assemblyname, string typename, string membername, string arglist, bool strlong)
1367 ArrayList a = new ArrayList();
1368 if (arglist != "") a.Add(arglist);
1369 return MonoImpInfo(assemblyname, typename, membername, a, strlong);
1372 public string MonoImpInfo(string assemblyname, string typename, string membername, XPathNodeIterator itr, bool strlong)
1377 ArrayList rgs = new ArrayList ();
1378 while (itr.MoveNext ())
1379 rgs.Add (itr.Current.Value);
1381 return MonoImpInfo (assemblyname, typename, membername, rgs, strlong);
1384 public string MonoImpInfo(string assemblyname, string typename, string membername, ArrayList arglist, bool strlong)
1387 Assembly assembly = null;
1390 assembly = Assembly.LoadWithPartialName(assemblyname);
1391 } catch (Exception) {
1395 if (assembly == null) {
1396 /*if (strlong) return "The assembly " + assemblyname + " is not available to MonoDoc.";
1398 return ""; // silently ignore
1401 Type t = assembly.GetType(typename, false);
1404 return typename + " has not been implemented.";
1406 return "Not implemented.";
1409 // The following code is flakey and fails to find existing members
1412 MemberInfo[] mis = t.GetMember(membername, BF.Static | BF.Instance | BF.Public | BF.NonPublic);
1414 if (mis.Length == 0)
1415 return "This member has not been implemented.";
1416 if (mis.Length == 1)
1417 return MonoImpInfo(mis[0], "member", strlong);
1419 // Scan for the appropriate member
1420 foreach (MemberInfo mi in mis) {
1421 System.Reflection.ParameterInfo[] pis;
1423 if (mi is MethodInfo || mi is ConstructorInfo)
1424 pis = ((MethodBase)mi).GetParameters();
1425 else if (mi is PropertyInfo)
1426 pis = ((PropertyInfo)mi).GetIndexParameters();
1432 if (pis.Length != arglist.Count) continue;
1433 for (int i = 0; i < pis.Length; i++) {
1434 if (pis[i].ParameterType.FullName != (string)arglist[i]) { good = false; break; }
1436 if (!good) continue;
1439 return MonoImpInfo(mi, "member", strlong);
1442 } catch (Exception) {
1447 public string MonoImpInfo(System.Reflection.MemberInfo mi, string itemtype, bool strlong)
1454 object[] atts = mi.GetCustomAttributes(true);
1456 foreach (object att in atts) if (att.GetType().Name == "MonoTODOAttribute") todoctr++;
1460 s = "This " + itemtype + " is marked as being unfinished.<BR/>\n";
1468 public string MonoImpInfo(string assemblyname, string typename, bool strlong)
1474 if (assemblyname == "")
1477 Assembly assembly = Assembly.LoadWithPartialName(assemblyname);
1478 if (assembly == null)
1481 Type t = assembly.GetType(typename, false);
1484 return typename + " has not been implemented.";
1486 return "Not implemented.";
1489 string s = MonoImpInfo(t, "type", strlong);
1492 MemberInfo[] mis = t.GetMembers(BF.Static | BF.Instance | BF.Public | BF.NonPublic);
1494 // Scan members for MonoTODO attributes
1496 foreach (MemberInfo mi in mis) {
1497 string mii = MonoImpInfo(mi, null, false);
1498 if (mii != "") mctr++;
1501 s += "This type has " + mctr + " members that are marked as unfinished.<BR/>";
1507 } catch (Exception) {
1512 public bool MonoEditing ()
1514 return SettingsHandler.Settings.EnableEditing;
1517 public bool IsToBeAdded(string text) {
1518 return text.StartsWith("To be added");
1523 // This takes one of the ecma urls, which look like this:
1524 // ecma:NUMERIC_ID#OPAQUE/REST
1526 // NUMERIC_ID is the numeric ID assigned by the compressor
1527 // OPAQUE is opaque for node rendering (it typically contains T:System.Byte for example)
1528 // REST is the rest of the argument used to decode information
1530 static string GetFile (string url, out string rest)
1532 int pound = url.IndexOf ("#");
1533 int slash = url.IndexOf ("/");
1535 string fname = url.Substring (0, pound);
1536 rest = url.Substring (slash+1);
1542 // This should have a little cache or something.
1543 static XmlDocument GetDocument (HelpSource hs, string fname)
1545 Stream s = hs.GetHelpStream (fname);
1547 Error ("Could not fetch document {0}", fname);
1551 XmlDocument doc = new XmlDocument ();
1559 string GetKindFromCaption (string s)
1561 int p = s.LastIndexOf (' ');
1563 return s.Substring (p + 1);
1568 // Obtain an URL of the type T:System.Object from the node
1570 public static string GetNiceUrl (Node node) {
1571 if (node.Element.StartsWith("N:"))
1572 return node.Element;
1574 int bk_pos = node.Caption.IndexOf (' ');
1575 // node from an overview
1577 name = node.Caption.Substring (0, bk_pos);
1578 full = node.Parent.Caption + "." + name.Replace ('.', '+');
1581 // node that lists constructors, methods, fields, ...
1582 if ((node.Caption == "Constructors") || (node.Caption == "Fields") || (node.Caption == "Events")
1583 || (node.Caption == "Members") || (node.Caption == "Properties") || (node.Caption == "Methods")
1584 || (node.Caption == "Operators")) {
1585 bk_pos = node.Parent.Caption.IndexOf (' ');
1586 name = node.Parent.Caption.Substring (0, bk_pos);
1587 full = node.Parent.Parent.Caption + "." + name.Replace ('.', '+');
1588 return "T:" + full + "/" + node.Element;
1590 int pr_pos = node.Caption.IndexOf ('(');
1591 // node from a constructor
1592 if (node.Parent.Element == "C") {
1593 name = node.Parent.Parent.Parent.Caption;
1594 int idx = node.URL.IndexOf ('/');
1595 return node.URL[idx+1] + ":" + name + "." + node.Caption.Replace ('.', '+');
1596 // node from a method with one signature, field, property, operator
1597 } else if (pr_pos == -1) {
1598 bk_pos = node.Parent.Parent.Caption.IndexOf (' ');
1599 name = node.Parent.Parent.Caption.Substring (0, bk_pos);
1600 full = node.Parent.Parent.Parent.Caption + "." + name.Replace ('.', '+');
1601 int idx = node.URL.IndexOf ('/');
1602 return node.URL[idx+1] + ":" + full + "." + node.Caption;
1603 // node from a method with several signatures
1605 bk_pos = node.Parent.Parent.Parent.Caption.IndexOf (' ');
1606 name = node.Parent.Parent.Parent.Caption.Substring (0, bk_pos);
1607 full = node.Parent.Parent.Parent.Parent.Caption + "." + name.Replace ('.', '+');
1608 int idx = node.URL.IndexOf ('/');
1609 return node.URL[idx+1] + ":" + full + "." + node.Caption;
1614 // Populates the index.
1616 public override void PopulateIndex (IndexMaker index_maker)
1618 foreach (Node ns_node in Tree.Nodes){
1619 foreach (Node type_node in ns_node.Nodes){
1620 string typename = type_node.Caption.Substring (0, type_node.Caption.IndexOf (' '));
1621 string full = ns_node.Caption + "." + typename;
1623 string doc_tag = GetKindFromCaption (type_node.Caption);
1624 string url = "T:" + full;
1626 if (doc_tag == "Class" || doc_tag == "Structure" || doc_tag == "Interface"){
1628 index_maker.Add (type_node.Caption, typename, url);
1629 index_maker.Add (full + " " + doc_tag, full, url);
1631 foreach (Node c in type_node.Nodes){
1633 case "Constructors":
1634 index_maker.Add (" constructors", typename+"0", url + "/C");
1637 index_maker.Add (" fields", typename+"1", url + "/F");
1640 index_maker.Add (" events", typename+"2", url + "/E");
1643 index_maker.Add (" properties", typename+"3", url + "/P");
1646 index_maker.Add (" methods", typename+"4", url + "/M");
1649 index_maker.Add (" operators", typename+"5", url + "/O");
1655 // Now repeat, but use a different sort key, to make sure we come after
1656 // the summary data above, start the counter at 6
1658 string keybase = typename + "6.";
1660 foreach (Node c in type_node.Nodes){
1662 case "Constructors":
1665 foreach (Node nc in c.Nodes){
1666 string res = nc.Caption;
1668 string nurl = String.Format ("F:{0}.{1}", full, res);
1669 index_maker.Add (String.Format ("{0}.{1} field", typename, res),
1670 keybase + res, nurl);
1671 index_maker.Add (String.Format ("{0} field", res), res, nurl);
1676 foreach (Node nc in c.Nodes){
1677 string res = nc.Caption;
1678 string nurl = String.Format ("E:{0}.{1}", full, res);
1680 index_maker.Add (String.Format ("{0}.{1} event", typename, res),
1681 keybase + res, nurl);
1682 index_maker.Add (String.Format ("{0} event", res), res, nurl);
1686 foreach (Node nc in c.Nodes){
1687 string res = nc.Caption;
1688 string nurl = String.Format ("P:{0}.{1}", full, res);
1689 index_maker.Add (String.Format ("{0}.{1} property", typename, res),
1690 keybase + res, nurl);
1691 index_maker.Add (String.Format ("{0} property", res), res, nurl);
1695 foreach (Node nc in c.Nodes){
1696 string res = nc.Caption;
1697 int p = res.IndexOf ("(");
1699 res = res.Substring (0, p);
1700 string nurl = String.Format ("M:{0}.{1}", full, res);
1701 index_maker.Add (String.Format ("{0}.{1} method", typename, res),
1702 keybase + res, nurl);
1703 index_maker.Add (String.Format ("{0} method", res), res, nurl);
1708 foreach (Node nc in c.Nodes){
1709 string res = nc.Caption;
1710 string nurl = String.Format ("O:{0}.{1}", full, res);
1711 index_maker.Add (String.Format ("{0}.{1} operator", typename, res),
1712 keybase + res, nurl);
1717 } else if (doc_tag == "Enumeration"){
1719 // Enumerations: add the enumeration values
1721 index_maker.Add (type_node.Caption, typename, url);
1722 index_maker.Add (full + " " + doc_tag, full, url);
1724 // Now, pull the values.
1726 XmlDocument x = GetXmlFromUrl (type_node.URL, out rest);
1730 XmlNodeList members = x.SelectNodes ("/Type/Members/Member");
1732 if (members == null)
1735 foreach (XmlNode member_node in members){
1736 string enum_value = member_node.Attributes ["MemberName"].InnerText;
1737 string caption = enum_value + " value";
1738 index_maker.Add (caption, caption, url);
1740 } else if (doc_tag == "Delegate"){
1741 index_maker.Add (type_node.Caption, typename, url);
1742 index_maker.Add (full + " " + doc_tag, full, url);
1748 // Create list of documents for searching
1750 public override void PopulateSearchableIndex (IndexWriter writer)
1753 foreach (Node ns_node in Tree.Nodes) {
1754 Message (TraceLevel.Info, "\tNamespace: {0} ({1})", ns_node.Caption, ns_node.Nodes.Count);
1755 foreach (Node type_node in ns_node.Nodes) {
1756 string typename = type_node.Caption.Substring (0, type_node.Caption.IndexOf (' '));
1757 string full = ns_node.Caption + "." + typename;
1758 string doc_tag = GetKindFromCaption (type_node.Caption);
1759 string url = "T:" + full;
1761 XmlDocument xdoc = GetXmlFromUrl (type_node.URL, out rest);
1766 // For classes, structures or interfaces add a doc for the overview and
1767 // add a doc for every constructor, method, event, ...
1769 if (doc_tag == "Class" || doc_tag == "Structure" || doc_tag == "Interface"){
1771 // Adds a doc for every overview of every type
1772 SearchableDocument doc = new SearchableDocument ();
1773 doc.title = type_node.Caption;
1774 doc.hottext = typename;
1777 XmlNode node_sel = xdoc.SelectSingleNode ("/Type/Docs");
1778 text = new StringBuilder ();
1779 GetTextFromNode (node_sel, text);
1780 doc.text = text.ToString ();
1782 text = new StringBuilder ();
1783 GetExamples (node_sel, text);
1784 doc.examples = text.ToString ();
1786 writer.AddDocument (doc.LuceneDoc);
1788 //Add docs for contructors, methods, etc.
1789 foreach (Node c in type_node.Nodes) { // c = Constructors || Fields || Events || Properties || Methods || Operators
1791 if (c.Element == "*")
1794 foreach (Node nc in c.Nodes) {
1795 //xpath to the docs xml node
1797 if (c.Caption == "Constructors")
1798 xpath = String.Format ("/Type/Members/Member[{0}]/Docs", i++);
1799 else if (c.Caption == "Operators")
1800 xpath = String.Format ("/Type/Members/Member[@MemberName='op_{0}']/Docs", nc.Caption);
1802 xpath = String.Format ("/Type/Members/Member[@MemberName='{0}']/Docs", nc.Caption);
1803 //construct url of the form M:Array.Sort
1805 if (c.Caption == "Constructors")
1806 urlnc = String.Format ("{0}:{1}.{2}", c.Caption[0], ns_node.Caption, nc.Caption);
1808 urlnc = String.Format ("{0}:{1}.{2}.{3}", c.Caption[0], ns_node.Caption, typename, nc.Caption);
1811 SearchableDocument doc_nod = new SearchableDocument ();
1812 doc_nod.title = LargeName (nc);
1813 //dont add the parameters to the hottext
1814 int ppos = nc.Caption.IndexOf ('(');
1816 doc_nod.hottext = nc.Caption.Substring (0, ppos);
1818 doc_nod.hottext = nc.Caption;
1820 doc_nod.url = urlnc;
1822 XmlNode xmln = xdoc.SelectSingleNode (xpath);
1824 Error ("Problem: {0}, with xpath: {1}", urlnc, xpath);
1828 text = new StringBuilder ();
1829 GetTextFromNode (xmln, text);
1830 doc_nod.text = text.ToString ();
1832 text = new StringBuilder ();
1833 GetExamples (xmln, text);
1834 doc_nod.examples = text.ToString ();
1836 writer.AddDocument (doc_nod.LuceneDoc);
1840 // Enumerations: add the enumeration values
1842 } else if (doc_tag == "Enumeration"){
1844 XmlNodeList members = xdoc.SelectNodes ("/Type/Members/Member");
1845 if (members == null)
1848 text = new StringBuilder ();
1849 foreach (XmlNode member_node in members) {
1850 string enum_value = member_node.Attributes ["MemberName"].InnerText;
1851 text.Append (enum_value);
1853 GetTextFromNode (member_node["Docs"], text);
1856 SearchableDocument doc = new SearchableDocument ();
1858 text = new StringBuilder ();
1859 GetExamples (xdoc.SelectSingleNode ("/Type/Docs"), text);
1860 doc.examples = text.ToString ();
1862 doc.title = type_node.Caption;
1863 doc.hottext = xdoc.DocumentElement.Attributes["Name"].Value;
1865 doc.text = text.ToString();
1866 writer.AddDocument (doc.LuceneDoc);
1870 } else if (doc_tag == "Delegate"){
1871 SearchableDocument doc = new SearchableDocument ();
1872 doc.title = type_node.Caption;
1873 doc.hottext = xdoc.DocumentElement.Attributes["Name"].Value;
1876 XmlNode node_sel = xdoc.SelectSingleNode ("/Type/Docs");
1878 text = new StringBuilder ();
1879 GetTextFromNode (node_sel, text);
1880 doc.text = text.ToString();
1882 text = new StringBuilder ();
1883 GetExamples (node_sel, text);
1884 doc.examples = text.ToString();
1886 writer.AddDocument (doc.LuceneDoc);
1893 // Extract the interesting text from the docs node
1895 void GetTextFromNode (XmlNode n, StringBuilder sb)
1897 //don't include example code
1898 if (n.Name == "code")
1901 //include the url to which points the see tag
1902 if (n.Name == "see" && n.Attributes.Count > 0)
1903 sb.Append (n.Attributes [0].Value);
1905 //include the name of the parameter
1906 if (n.Name == "paramref" && n.Attributes.Count > 0)
1907 sb.Append (n.Attributes [0].Value);
1909 //include the contents for the node that contains text
1910 if (n.NodeType == XmlNodeType.Text)
1911 sb.Append (n.Value);
1913 //add the rest of xml tags recursively
1914 if (n.HasChildNodes)
1915 foreach (XmlNode n_child in n.ChildNodes)
1916 GetTextFromNode (n_child, sb);
1919 // Extract the code nodes from the docs
1921 void GetExamples (XmlNode n, StringBuilder sb)
1923 if (n.Name == "code") {
1924 sb.Append (n.InnerText);
1926 if (n.HasChildNodes)
1927 foreach (XmlNode n_child in n.ChildNodes)
1928 GetExamples (n_child, sb);
1932 // Extract a large name for the Node
1933 // (copied from mono-tools/docbrowser/browser.Render()
1934 static string LargeName (Node matched_node)
1936 string[] parts = matched_node.URL.Split('/', '#');
1937 if(parts.Length == 3 && parts[2] != String.Empty) { //List of Members, properties, events, ...
1938 return parts[1] + ": " + matched_node.Caption;
1939 } else if(parts.Length >= 4) { //Showing a concrete Member, property, ...
1940 return parts[1] + "." + matched_node.Caption;
1942 return matched_node.Caption;
1948 public class EcmaUncompiledHelpSource : EcmaHelpSource {
1949 readonly DirectoryInfo basedir;
1950 readonly XmlDocument basedoc;
1952 public new readonly string Name;
1953 public readonly string BasePath;
1955 public EcmaUncompiledHelpSource (string base_file) : base ()
1957 Message (TraceLevel.Info, "Loading uncompiled help from " + base_file);
1959 basedir = new DirectoryInfo(base_file);
1960 BasePath = basedir.FullName;
1962 basedoc = new XmlDocument();
1963 basedoc.Load(Path.Combine(basedir.FullName, "index.xml"));
1965 Name = basedoc.SelectSingleNode("Overview/Title").InnerText;
1967 bool has_content = false;
1969 foreach (XmlElement ns in basedoc.SelectNodes("Overview/Types/Namespace")) {
1971 Node nsnode = Tree.CreateNode(ns.GetAttribute("Name"), "N:" + ns.GetAttribute("Name"));
1973 bool has_types = false;
1974 foreach (XmlElement t in ns.SelectNodes("Type")) {
1976 string typename = EcmaDoc.GetDisplayName (t).Replace("+", ".");
1978 // Must load in each document to get the list of members...
1979 XmlDocument typedoc = new XmlDocument();
1980 typedoc.Load(Path.Combine(Path.Combine(basedir.FullName, ns.GetAttribute("Name")), t.GetAttribute("Name") + ".xml"));
1981 string kind = EcmaDoc.GetTypeKind (typedoc);
1983 string url = ns.GetAttribute("Name") + "." + t.GetAttribute("Name");
1984 Node typenode = nsnode.CreateNode(typename + " " + kind, "T:" + url);
1985 //Node typemembers = typenode.CreateNode("Members", "T:" + url + "/*");
1987 Hashtable groups = new Hashtable();
1988 Hashtable groups_count = new Hashtable();
1989 foreach (XmlElement member in typedoc.SelectNodes("Type/Members/Member")) {
1990 string membername = member.GetAttribute("MemberName");
1991 string membertype = member.SelectSingleNode("MemberType").InnerText;
1993 if (membertype == "Constructor")
1994 membername = t.GetAttribute("Name");
1995 if (membername.StartsWith("op_"))
1996 membertype = "Operator";
1999 if (groups.ContainsKey(membertype)) {
2000 group = (Node)groups[membertype];
2002 string membertypeplural = membertype + "s";
2003 if (membertypeplural == "Propertys") membertypeplural = "Properties";
2005 group = typenode.CreateNode(membertypeplural, "T:" + url + "/" + membertype[0]);
2006 groups[membertype] = group;
2007 groups_count[membertype] = 0;
2010 if (membertype == "Constructor" || membertype == "Method" ||
2011 (membertype == "Property" && member.SelectNodes("Parameters/Parameter").Count > 0)) {
2012 membername = EcmaHelpSource.MakeSignature(member, membertype == "Constructor" ? membername : null);
2013 } else if (membertype == "Operator") {
2015 EcmaHelpSource.MakeOperatorSignature(member, out dummy, out membername);
2018 int index = (int)groups_count[membertype];
2019 groups_count[membertype] = index + 1;
2021 group.CreateNode(membername, index.ToString());
2024 foreach (Node group in groups.Values)
2036 public override string GetIdFromUrl (string prefix, string ns, string type)
2039 throw new NotImplementedException();
2040 return Path.Combine(Path.Combine(basedir.FullName, ns), type + ".xml");
2043 protected override XmlDocument GetXmlFromUrl(string url, out string rest) {
2045 url = url.Substring(2);
2047 int sidx = url.IndexOf("/");
2051 rest = url.Substring(sidx+1);
2052 url = url.Substring(0, sidx);
2056 if (!RootTree.GetNamespaceAndType (url, out ns, out type)) {
2057 Message (TraceLevel.Error, "Could not determine namespace/type for {0}", url);
2061 string file = Path.Combine(Path.Combine(basedir.FullName, ns),
2062 ToEscapedTypeName (type).Replace ('.', '+') + ".xml");
2063 if (!new FileInfo(file).Exists) return null;
2065 XmlDocument typedoc = new XmlDocument();
2070 public override string GetText (string url, out Node match_node) {
2071 if (url == "root:") {
2075 XmlDocument index = new XmlDocument ();
2076 index.Load (Path.Combine (basedir.FullName, "index.xml"));
2077 XmlNodeList nodes = index.SelectNodes ("/Overview/Types/Namespace");
2079 //recreate masteroverview.xml
2080 XmlDocument summary = new XmlDocument ();
2081 XmlElement elements = summary.CreateElement ("elements");
2082 foreach (XmlNode node in nodes) {
2083 XmlElement ns = summary.CreateElement ("namespace");
2084 XmlAttribute attr = summary.CreateAttribute ("ns");
2085 attr.Value = EcmaDoc.GetDisplayName (node);
2086 ns.Attributes.Append (attr);
2087 elements.AppendChild (ns);
2089 summary.AppendChild (elements);
2091 XmlReader reader = new XmlTextReader (new StringReader (summary.OuterXml));
2093 //transform the recently created masteroverview.xml
2094 XsltArgumentList args = new XsltArgumentList();
2095 args.AddExtensionObject("monodoc:///extensions", ExtObject);
2096 args.AddParam("show", "", "masteroverview");
2097 string s = EcmaHelpSource.Htmlize(new XPathDocument (reader), args);
2098 return BuildHtml (css_ecma_code, js_code, s);
2100 return base.GetText(url, out match_node);
2103 protected override XmlReader GetNamespaceDocument (string ns) {
2104 XmlDocument nsdoc = new XmlDocument();
2105 nsdoc.Load (EcmaDoc.GetNamespaceFile (basedir.FullName, ns));
2107 XmlDocument elements = new XmlDocument();
2108 XmlElement docnode = elements.CreateElement("elements");
2110 foreach (XmlElement doc in nsdoc.SelectNodes("Namespace/Docs/*")) {
2111 docnode.AppendChild(elements.ImportNode(doc, true));
2114 foreach (XmlElement t in basedoc.SelectNodes("Overview/Types/Namespace[@Name='" + ns + "']/Type")) {
2115 XmlDocument typedoc = new XmlDocument();
2116 typedoc.Load(Path.Combine(Path.Combine(basedir.FullName, ns), t.GetAttribute("Name") + ".xml"));
2119 switch (EcmaDoc.GetTypeKind(typedoc)) {
2120 case "Class": typekind = "class"; break;
2121 case "Enumeration": typekind = "enum"; break;
2122 case "Structure": typekind = "struct"; break;
2123 case "Delegate": typekind = "delegate"; break;
2124 case "Interface": typekind = "interface"; break;
2125 default: throw new InvalidOperationException();
2128 XmlElement typenode = elements.CreateElement(typekind);
2129 typenode.SetAttribute("name", EcmaDoc.GetDisplayName (t).Replace ('+', '.'));
2130 typenode.SetAttribute("fullname", ns + "." + t.GetAttribute("Name"));
2131 typenode.AppendChild(elements.ImportNode(typedoc.SelectSingleNode("Type/Docs/summary"), true));
2133 docnode.AppendChild(typenode);
2136 return new XmlNodeReader(docnode);