2 // Provider: shared code and interfaces for providers
5 // Miguel de Icaza (miguel@ximian.com)
7 // (C) 2002, Ximian, Inc.
10 // Each node should have a provider link
12 // Should encode numbers using a runlength encoding to save space
16 using System.Collections.Generic;
20 using System.Text.RegularExpressions;
21 using System.Collections;
22 using System.Diagnostics;
23 using System.Configuration;
24 using System.Reflection;
25 using System.Text.RegularExpressions;
27 using System.Xml.XPath;
28 using ICSharpCode.SharpZipLib.Zip;
30 using Monodoc.Lucene.Net.Index;
31 using Monodoc.Lucene.Net.Analysis.Standard;
33 using Mono.Documentation;
36 /// This tree is populated by the documentation providers, or populated
37 /// from a binary encoding of the tree. The format of the tree is designed
38 /// to minimize the need to load it in full.
40 public class Tree : Node {
42 #region Loading the tree from a file
45 /// Our HelpSource container
47 public readonly HelpSource HelpSource;
49 internal FileStream InputStream;
50 internal BinaryReader InputReader;
53 /// Load from file constructor
55 public Tree (HelpSource hs, string filename) : base (null, null)
57 Encoding utf8 = new UTF8Encoding (false, true);
59 if (!File.Exists (filename)){
60 throw new FileNotFoundException ();
63 InputStream = File.OpenRead (filename);
64 InputReader = new BinaryReader (InputStream, utf8);
65 byte [] sig = InputReader.ReadBytes (4);
68 throw new Exception ("Invalid file format");
70 InputStream.Position = 4;
71 position = InputReader.ReadInt32 ();
78 /// Tree creation and merged tree constructor
80 public Tree (HelpSource hs, string caption, string url) : base (caption, url)
85 public Tree (HelpSource hs, Node parent, string caption, string element) : base (parent, caption, element)
93 /// Saves the tree into the specified file using the help file format.
95 public void Save (string file)
97 Encoding utf8 = new UTF8Encoding (false, true);
98 using (FileStream output = File.OpenWrite (file)){
99 // Skip over the pointer to the first node.
102 using (BinaryWriter writer = new BinaryWriter (output, utf8)){
104 Dump (output, writer);
107 writer.Write (new byte [] { (byte) 'M', (byte) 'o', (byte) 'H', (byte) 'P' });
108 writer.Write (position);
113 static bool GoodSig (byte [] sig)
117 if (sig [0] != (byte) 'M' ||
118 sig [1] != (byte) 'o' ||
119 sig [2] != (byte) 'H' ||
120 sig [3] != (byte) 'P')
127 public class Node : IComparable {
128 string caption, element;
129 public bool Documented;
130 public readonly Tree tree;
132 protected ArrayList nodes;
133 protected internal int position;
135 static ArrayList empty = ArrayList.ReadOnly(new ArrayList(0));
138 /// Creates a node, called by the Tree.
140 public Node (string caption, string element)
142 this.tree = (Tree) this;
143 this.caption = caption;
144 this.element = element;
148 public Node (Node parent, string caption, string element)
150 this.parent = parent;
151 this.tree = parent.tree;
152 this.caption = caption;
153 this.element = element;
157 /// Creates a node from an on-disk representation
159 Node (Node parent, int address)
161 this.parent = parent;
163 this.tree = parent.tree;
168 public void AddNode (Node n)
175 public void DelNode (Node n)
180 public ArrayList Nodes {
184 return nodes != null ? nodes : empty;
188 public string Element {
200 public string Caption {
214 public void LoadNode ()
217 position = -position;
219 tree.InputStream.Position = position;
220 BinaryReader reader = tree.InputReader;
221 int count = DecodeInt (reader);
222 element = reader.ReadString ();
223 caption = reader.ReadString ();
227 nodes = new ArrayList (count);
228 for (int i = 0; i < count; i++){
229 int child_address = DecodeInt (reader);
231 Node t = new Node (this, -child_address);
237 /// Creates a new node, in the locator entry point, and with
238 /// a user visible caption of @caption
240 public Node CreateNode (string c_caption, string c_element)
243 nodes = new ArrayList ();
245 Node t = new Node (this, c_caption, c_element);
251 /// Looks up or creates a new node, in the locator entry point, and with
252 /// a user visible caption of @caption. This is different from
253 /// CreateNode in that it will look up an existing node for the given @locator.
255 public Node LookupNode (string c_caption, string c_element)
258 return CreateNode (c_caption, c_element);
260 foreach (Node n in nodes){
261 if (n.element == c_element)
264 return CreateNode (c_caption, c_element);
267 public void EnsureNodes ()
270 nodes = new ArrayList ();
275 return nodes == null;
279 void EncodeInt (BinaryWriter writer, int value)
282 int high = (value >> 7) & 0x01ffffff;
283 byte b = (byte)(value & 0x7f);
286 b = (byte)(b | 0x80);
294 int DecodeInt (BinaryReader reader)
301 b = reader.ReadByte();
303 ret = ret | ((b & 0x7f) << shift);
305 } while ((b & 0x80) == 0x80);
310 internal void Dump (FileStream output, BinaryWriter writer)
313 foreach (Node child in nodes){
314 child.Dump (output, writer);
317 position = (int) output.Position;
318 EncodeInt (writer, nodes == null ? 0 : (int) nodes.Count);
319 writer.Write (element);
320 writer.Write (caption);
323 foreach (Node child in nodes){
324 EncodeInt (writer, child.position);
331 static void Indent ()
333 for (int i = 0; i < indent; i++)
337 public static void PrintTree (Node node)
340 Console.WriteLine ("{0},{1}\t[PublicUrl: {2}]", node.Element, node.Caption, node.PublicUrl);
341 if (node.Nodes.Count == 0)
345 foreach (Node n in node.Nodes)
356 [Obsolete("Use PublicUrl")]
362 if (element.IndexOf (":") >= 0)
366 string url = parent.URL;
368 if (url.EndsWith ("/"))
369 return url + element;
371 return parent.URL + "/" + element;
377 public string PublicUrl {
379 return tree.HelpSource != null
380 ? tree.HelpSource.GetPublicUrl (URL)
385 int IComparable.CompareTo (object obj)
387 Node other = obj as Node;
393 if (other.position < 0)
396 Regex digits = new Regex (@"([\d]+)|([^\d]+)");
397 MatchEvaluator eval = delegate (Match m) {
398 return (m.Value.Length > 0 && char.IsDigit (m.Value [0]))
399 ? m.Value.PadLeft (System.Math.Max (caption.Length, other.caption.Length))
402 return digits.Replace (caption, eval).CompareTo (digits.Replace (other.caption, eval));
407 // The HelpSource class keeps track of the archived data, and its
410 public class HelpSource {
412 public static bool use_css = false;
413 public static string css_code;
414 public static string CssCode {
416 if (css_code != null)
419 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetCallingAssembly ();
420 Stream str_css = assembly.GetManifestResourceStream ("base.css");
421 StringBuilder sb = new StringBuilder ((new StreamReader (str_css)).ReadToEnd());
422 sb.Replace ("@@FONT_FAMILY@@", SettingsHandler.Settings.preferred_font_family);
423 sb.Replace ("@@FONT_SIZE@@", SettingsHandler.Settings.preferred_font_size.ToString());
424 css_code = sb.ToString ();
427 set { css_code = value; }
430 public virtual string InlineCss {
431 get { return CssCode; }
434 public virtual string InlineJavaScript {
438 public static bool FullHtml = true;
440 // should only be enabled by ASP.NET webdoc
441 public static bool UseWebdocCache;
444 // The unique ID for this HelpSource.
447 DateTime zipFileWriteTime;
449 TraceLevel trace_level = TraceLevel.Warning;
450 protected bool nozip;
451 protected string base_dir;
453 public HelpSource (string base_filename, bool create)
455 this.name = Path.GetFileName (base_filename);
456 tree_filename = base_filename + ".tree";
457 zip_filename = base_filename + ".zip";
458 base_dir = XmlDocUtils.GetCacheDirectory (base_filename);
459 if (UseWebdocCache && !create && Directory.Exists (base_dir)) {
466 Tree = new Tree (this, tree_filename);
470 FileInfo fi = new FileInfo (zip_filename);
471 zipFileWriteTime = fi.LastWriteTime;
473 zipFileWriteTime = DateTime.Now;
477 public HelpSource() {
478 Tree = new Tree (this, "Blah", "Blah");
482 public DateTime ZipFileWriteTime {
484 return zipFileWriteTime;
488 public int SourceID {
500 public TraceLevel TraceLevel {
501 get { return trace_level; }
502 set { trace_level = value; }
508 /// Returns a stream from the packaged help source archive
510 public virtual Stream GetHelpStream (string id)
513 string path = XmlDocUtils.GetCachedFileName (base_dir, id);
514 if (File.Exists (path))
515 return File.OpenRead (path);
519 if (zip_file == null)
520 zip_file = new ZipFile (zip_filename);
522 ZipEntry entry = zip_file.GetEntry (id);
524 return zip_file.GetInputStream (entry);
528 public string GetRealPath (string file)
530 if (zip_file == null)
531 zip_file = new ZipFile (zip_filename);
533 ZipEntry entry = zip_file.GetEntry (file);
534 if (entry != null && entry.ExtraData != null)
535 return ConvertToString (entry.ExtraData);
539 public XmlReader GetHelpXml (string id)
542 Stream s = File.OpenRead (XmlDocUtils.GetCachedFileName (base_dir, id));
543 string url = "monodoc:///" + SourceID + "@" + System.Web.HttpUtility.UrlEncode (id) + "@";
544 return new XmlTextReader (url, s);
547 if (zip_file == null)
548 zip_file = new ZipFile (zip_filename);
550 ZipEntry entry = zip_file.GetEntry (id);
552 Stream s = zip_file.GetInputStream (entry);
553 string url = "monodoc:///" + SourceID + "@" + System.Web.HttpUtility.UrlEncode (id) + "@";
554 return new XmlTextReader (url, s);
559 public virtual XmlDocument GetHelpXmlWithChanges (string id)
562 Stream s = File.OpenRead (XmlDocUtils.GetCachedFileName (base_dir, id));
563 string url = "monodoc:///" + SourceID + "@" + System.Web.HttpUtility.UrlEncode (id) + "@";
564 XmlReader r = new XmlTextReader (url, s);
565 XmlDocument ret = new XmlDocument ();
570 if (zip_file == null)
571 zip_file = new ZipFile (zip_filename);
573 ZipEntry entry = zip_file.GetEntry (id);
575 Stream s = zip_file.GetInputStream (entry);
576 string url = "monodoc:///" + SourceID + "@" + System.Web.HttpUtility.UrlEncode (id) + "@";
577 XmlReader r = new XmlTextReader (url, s);
578 XmlDocument ret = new XmlDocument ();
581 if (entry.ExtraData != null)
582 EditingUtils.AccountForChanges (ret, Name, ConvertToString (entry.ExtraData));
590 /// Get a nice, unique expression for any XPath node that you get.
591 /// This function is used by editing to get the expression to put
592 /// on to the file. The idea is to create an expression that is resistant
593 /// to changes in the structure of the XML.
595 public virtual string GetNodeXPath (XPathNavigator n)
597 return EditingUtils.GetXPath (n.Clone ());
600 public string GetEditUri (XPathNavigator n)
602 return EditingUtils.FormatEditUri (n.BaseURI, GetNodeXPath (n));
605 static string ConvertToString (byte[] data)
607 return Encoding.UTF8.GetString(data);
610 static byte[] ConvertToArray (string str)
612 return Encoding.UTF8.GetBytes(str);
616 /// The tree that is being populated
619 public RootTree RootTree;
621 // Base filename used by this HelpSource.
622 string tree_filename, zip_filename;
625 const int buffer_size = 65536;
626 ZipOutputStream zip_output;
629 HelpSource (string base_filename)
633 void SetupForOutput ()
635 Tree = new Tree (this, "", "");
637 FileStream stream = File.Create (zip_filename);
639 zip_output = new ZipOutputStream (stream);
640 zip_output.SetLevel (9);
642 buffer = new byte [buffer_size];
646 /// Saves the tree and the archive
650 Tree.Save (tree_filename);
651 zip_output.Finish ();
659 return String.Format ("{0}", code++);
663 /// Providers call this to store a file they will need, and the return value
664 /// is the name that was assigned to it
666 public string PackFile (string file)
668 string entry_name = GetNewCode ();
669 return PackFile (file, entry_name);
672 public string PackFile (string file, string entry_name)
674 using (FileStream input = File.OpenRead (file)) {
675 PackStream (input, entry_name, file);
681 public void PackStream (Stream s, string entry_name)
683 PackStream (s, entry_name, null);
686 void PackStream (Stream s, string entry_name, string realPath)
688 ZipEntry entry = new ZipEntry (entry_name);
690 if (realPath != null)
691 entry.ExtraData = ConvertToArray (realPath);
693 zip_output.PutNextEntry (entry);
696 while ((n = s.Read (buffer, 0, buffer_size)) > 0){
697 zip_output.Write (buffer, 0, n);
701 public void PackXml (string fname, XmlDocument doc, string real_path)
703 ZipEntry entry = new ZipEntry (fname);
704 if (real_path != null)
705 entry.ExtraData = ConvertToArray(real_path);
707 zip_output.PutNextEntry (entry);
708 XmlTextWriter xmlWriter = new XmlTextWriter (zip_output, Encoding.UTF8);
709 doc.WriteContentTo (xmlWriter);
713 public virtual void RenderPreviewDocs (XmlNode newNode, XmlWriter writer)
715 throw new NotImplementedException ();
718 public virtual string GetPublicUrl (string id)
723 public virtual string GetText (string url, out Node n)
729 protected string GetCachedText (string url)
733 string file = XmlDocUtils.GetCachedFileName (base_dir, url);
734 if (!File.Exists (file))
736 return File.OpenText (file).ReadToEnd ();
739 public virtual Stream GetImage (string url)
745 // Default method implementation does not satisfy the request
747 public virtual string RenderTypeLookup (string prefix, string ns, string type, string member, out Node n)
753 public virtual string RenderNamespaceLookup (string nsurl, out Node n)
760 // Populates the index.
762 public virtual void PopulateIndex (IndexMaker index_maker)
767 // Build an html document
769 public static string BuildHtml (string css, string html_code)
771 return BuildHtml (css, null, html_code);
774 internal static string BuildHtml (string css, string js, string html_code) {
778 StringWriter output = new StringWriter ();
779 output.Write ("<html><head>");
780 output.Write ("<style type=\"text/css\">");
781 output.Write (CssCode);
783 output.Write ("</style>");
785 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetCallingAssembly ();
786 Stream str_js = assembly.GetManifestResourceStream ("helper.js");
787 StringBuilder sb = new StringBuilder ((new StreamReader (str_js)).ReadToEnd());
788 output.Write ("<script type=\"text/JavaScript\">\n");
789 output.Write (sb.ToString ());
790 output.Write ("</script>\n");
793 output.Write ("<script type=\"text/JavaScript\">\n");
795 output.Write ("\n</script>");
798 output.Write ("</head><body>");
799 output.Write (html_code);
800 output.Write ("</body></html>");
801 return output.ToString ();
805 // Create different Documents for adding to Lucene search index
806 // The default action is do nothing. Subclasses should add the docs
808 public virtual void PopulateSearchableIndex (IndexWriter writer) {
812 public void Message (TraceLevel level, string format, params object[] args)
814 if ((int) level <= (int) trace_level)
815 Console.WriteLine (format, args);
818 public void Error (string format, params object[] args)
820 Console.Error.WriteLine (format, args);
824 public abstract class Provider {
826 // This code is used to "tag" all the different sources
837 public abstract void PopulateTree (Tree tree);
840 // Called at shutdown time after the tree has been populated to perform
841 // any fixups or final tasks.
843 public abstract void CloseTree (HelpSource hs, Tree tree);
846 public class RootTree : Tree {
849 public static ArrayList UncompiledHelpSources = new ArrayList();
851 public const int MonodocVersion = 1;
853 public static RootTree LoadTree ()
855 return LoadTree (null);
859 // Loads the tree layout
861 public static RootTree LoadTree (string basedir)
863 if (basedir == null) {
864 string myPath = System.Reflection.Assembly.GetExecutingAssembly ().Location;
865 string cfgFile = myPath + ".config";
866 if (!File.Exists (cfgFile)) {
870 XmlDocument d = new XmlDocument ();
872 basedir = d.SelectSingleNode ("config/path").Attributes ["docsPath"].Value;
874 //basedir = "/Library/Frameworks/Mono.framework/Versions/Current/lib/monodoc/";
880 XmlDocument doc = new XmlDocument ();
881 string layout = Path.Combine (basedir, "monodoc.xml");
884 string osxExternalDir = "/Library/Frameworks/Mono.framework/External/monodoc";
885 string[] osxExternalSources = Directory.Exists (osxExternalDir)
886 ? Directory.GetFiles (osxExternalDir, "*.source")
889 return LoadTree (basedir, doc,
890 Directory.GetFiles (Path.Combine (basedir, "sources"), "*.source")
891 .Concat (osxExternalSources));
895 // Compatibility shim w/ Mono 2.6
896 public static RootTree LoadTree (string indexDir, XmlDocument docTree, IEnumerable sourceFiles)
898 return LoadTree (indexDir, docTree, sourceFiles.Cast<string>());
901 public static RootTree LoadTree (string indexDir, XmlDocument docTree, IEnumerable<string> sourceFiles)
903 if (docTree == null) {
904 docTree = new XmlDocument ();
905 using (var defTree = typeof(RootTree).Assembly.GetManifestResourceStream ("monodoc.xml"))
906 docTree.Load (defTree);
910 sourceFiles = sourceFiles ?? new string [0];
916 RootTree root = new RootTree ();
917 root.basedir = indexDir;
919 XmlNodeList nodes = docTree.SelectNodes ("/node/node");
921 root.name_to_node ["root"] = root;
922 root.name_to_node ["libraries"] = root;
923 root.Populate (root, nodes);
925 Node third_party = root.LookupEntryPoint ("various");
926 if (third_party == null) {
927 Console.Error.WriteLine ("No 'various' doc node! Check monodoc.xml!");
934 foreach (var sourceFile in sourceFiles){
935 root.AddSourceFile (sourceFile);
938 foreach (string path in UncompiledHelpSources) {
939 EcmaUncompiledHelpSource hs = new EcmaUncompiledHelpSource(path);
941 root.help_sources.Add (hs);
942 string epath = "extra-help-source-" + hs.Name;
943 Node hsn = root.CreateNode (hs.Name, "root:/" + epath);
944 root.name_to_hs [epath] = hs;
946 foreach (Node n in hs.Tree.Nodes){
959 public void AddSource (string sources_dir)
961 string [] files = Directory.GetFiles (sources_dir);
963 foreach (string file in files){
964 if (!file.EndsWith (".source"))
966 AddSourceFile (file);
970 Dictionary<string,string> loadedSourceFiles = new Dictionary<string,string> ();
972 public void AddSourceFile (string sourceFile)
974 if (loadedSourceFiles.ContainsKey (sourceFile))
977 Node third_party = LookupEntryPoint ("various") ?? this;
979 XmlDocument doc = new XmlDocument ();
981 doc.Load (sourceFile);
984 Console.Error.WriteLine ("Error: Could not load source file {0}", sourceFile);
988 XmlNodeList extra_nodes = doc.SelectNodes ("/monodoc/node");
989 if (extra_nodes.Count > 0)
990 Populate (third_party, extra_nodes);
992 XmlNodeList sources = doc.SelectNodes ("/monodoc/source");
993 if (sources == null){
994 Console.Error.WriteLine ("Error: No <source> section found in the {0} file", sourceFile);
997 loadedSourceFiles [sourceFile] = sourceFile;
998 foreach (XmlNode source in sources){
999 XmlAttribute a = source.Attributes ["provider"];
1001 Console.Error.WriteLine ("Error: no provider in <source>");
1004 string provider = a.InnerText;
1005 a = source.Attributes ["basefile"];
1007 Console.Error.WriteLine ("Error: no basefile in <source>");
1010 string basefile = a.InnerText;
1011 a = source.Attributes ["path"];
1013 Console.Error.WriteLine ("Error: no path in <source>");
1016 string path = a.InnerText;
1018 string basefilepath = Path.Combine (Path.GetDirectoryName (sourceFile), basefile);
1019 HelpSource hs = GetHelpSource (provider, basefilepath);
1023 help_sources.Add (hs);
1024 name_to_hs [path] = hs;
1026 Node parent = LookupEntryPoint (path);
1027 if (parent == null){
1028 Console.Error.WriteLine ("node `{0}' is not defined on the documentation map", path);
1029 parent = third_party;
1032 foreach (Node n in hs.Tree.Nodes){
1039 // Delete nodes which does not have documentaiton (source)
1040 static bool PurgeNode(Node node)
1044 if (!node.Documented)
1046 ArrayList del_child = new ArrayList();
1047 //Delete node unless any child has documentation
1048 bool purged_child = false;
1049 foreach (Node child in node.Nodes)
1051 purged_child = PurgeNode(child);
1054 del_child.Add(child);
1058 // delete the node if all its children are to be deleted
1059 purge = (node.Nodes.Count == del_child.Count);
1062 foreach (Node child in del_child)
1064 node.DelNode(child);
1071 public static string[] GetSupportedFormats ()
1073 return new string[]{
1084 public static HelpSource GetHelpSource (string provider, string basefilepath)
1089 return new EcmaHelpSource (basefilepath, false);
1090 case "ecma-uncompiled":
1091 return new EcmaUncompiledHelpSource (basefilepath);
1093 return new MonoHBHelpSource(basefilepath, false);
1094 case "xhtml": case "hb":
1095 return new XhtmlHelpSource (basefilepath, false);
1097 return new ManHelpSource (basefilepath, false);
1099 return new SimpleHelpSource (basefilepath, false);
1101 return new ErrorHelpSource (basefilepath, false);
1103 return new EcmaSpecHelpSource (basefilepath, false);
1105 return new AddinsHelpSource (basefilepath, false);
1107 Console.Error.WriteLine ("Error: Unknown provider specified: {0}", provider);
1112 catch (FileNotFoundException) {
1113 Console.Error.WriteLine ("Error: did not find one of the files in sources/"+basefilepath);
1118 public static Provider GetProvider (string provider, params string[] basefilepaths)
1122 return new AddinsProvider (basefilepaths [0]);
1124 EcmaProvider p = new EcmaProvider ();
1125 foreach (string d in basefilepaths)
1130 return new EcmaSpecProvider (basefilepaths [0]);
1132 return new ErrorProvider (basefilepaths [0]);
1134 return new ManProvider (basefilepaths);
1136 return new SimpleProvider (basefilepaths [0]);
1139 return new XhtmlProvider (basefilepaths [0]);
1141 throw new NotSupportedException (provider);
1146 // Maintains the name to node mapping
1148 Hashtable name_to_node = new Hashtable ();
1149 Hashtable name_to_hs = new Hashtable ();
1151 void Populate (Node parent, XmlNodeList xml_node_list)
1153 foreach (XmlNode xml_node in xml_node_list){
1154 XmlAttribute e = xml_node.Attributes ["parent"];
1155 if (e != null && name_to_node.ContainsKey (e.InnerText)) {
1156 Node p = (Node) name_to_node [e.InnerText];
1157 xml_node.Attributes.Remove (e);
1158 Populate (p, xml_node.SelectNodes ("."));
1161 e = xml_node.Attributes ["label"];
1163 Console.Error.WriteLine ("`label' attribute missing in <node>");
1166 string label = e.InnerText;
1167 e = xml_node.Attributes ["name"];
1169 Console.Error.WriteLine ("`name' attribute missing in <node>");
1172 string name = e.InnerText;
1174 Node n = parent.LookupNode (label, "root:/" + name);
1176 name_to_node [name] = n;
1177 XmlNodeList children = xml_node.SelectNodes ("./node");
1178 if (children != null)
1179 Populate (n, children);
1183 public Node LookupEntryPoint (string name)
1185 return (Node) name_to_node [name];
1188 ArrayList help_sources;
1189 DateTime lastHelpSourceTime;
1191 RootTree () : base (null, "Mono Documentation", "root:")
1193 nodes = new ArrayList ();
1194 help_sources = new ArrayList ();
1195 lastHelpSourceTime = DateTime.MinValue;
1198 public DateTime LastHelpSourceTime {
1200 return lastHelpSourceTime;
1204 public static bool GetNamespaceAndType (string url, out string ns, out string type)
1208 for (int i = 0; i < url.Length; ++i) {
1227 Console.Error.WriteLine ("Did not find dot in: " + url);
1232 ns = url.Substring (0, nsidx);
1233 type = url.Substring (nsidx + 1);
1235 //Console.Error.WriteLine ("GetNameSpaceAndType (ns={0}, type={1}", ns, type);
1239 public XmlDocument GetHelpXml (string url)
1241 string rest = url.Substring (2);
1244 if (!GetNamespaceAndType (rest, out ns, out type))
1247 foreach (HelpSource hs in help_sources) {
1248 EcmaHelpSource ehs = hs as EcmaHelpSource;
1251 string id = ehs.GetIdFromUrl ("T:", ns, type);
1254 XmlDocument doc = hs.GetHelpXmlWithChanges (id);
1261 public string TypeLookup (string url, out Node match_node)
1263 string rest = Regex.Replace (url, @"^T:\s*", "");
1266 if (!GetNamespaceAndType (rest, out ns, out type)){
1271 foreach (HelpSource hs in help_sources){
1272 string s = hs.RenderTypeLookup ("T:", ns, type, null, out match_node);
1275 lastHelpSourceTime = hs.ZipFileWriteTime;
1283 public string MemberLookup (string prefix, string url, out Node match_node)
1285 string rest = Regex.Replace (url, @"^.:\s*", "");
1287 // Dots in the arg list (for methods) confuse this.
1288 // Chop off the arg list for now and put it back later.
1289 string arglist = "";
1290 int argliststart = rest.IndexOf("(");
1291 if (argliststart >= 0) {
1292 arglist = rest.Substring(argliststart);
1293 rest = rest.Substring(0, argliststart);
1296 string ns_type, member;
1298 if (prefix != "C:") {
1299 int member_idx = rest.LastIndexOf (".");
1301 // The dot in .ctor (if it's a M: link) would confuse this.
1302 if (rest.EndsWith("..ctor")) member_idx--;
1304 ns_type = rest.Substring (0, member_idx);
1305 member = rest.Substring (member_idx + 1);
1307 // C: links don't have the .ctor member part as it would in a M: link
1308 // Even though externally C: links are different from M: links,
1309 // C: links get transformed into M:-style links (with .ctor) here.
1315 //Console.WriteLine ("NS_TYPE: {0} MEMBER: {1}", ns_type, member);
1318 if (!GetNamespaceAndType (ns_type, out ns, out type)){
1323 foreach (HelpSource hs in help_sources){
1324 string s = hs.RenderTypeLookup (prefix, ns, type, member + arglist, out match_node);
1327 lastHelpSourceTime = hs.ZipFileWriteTime;
1335 public Stream GetImage (string url)
1337 if (url.StartsWith ("source-id:")){
1338 string rest = url.Substring (10);
1339 int p = rest.IndexOf (":");
1340 string str_idx = rest.Substring (0, p);
1344 idx = Int32.Parse (str_idx);
1346 Console.Error.WriteLine ("Failed to parse source-id url: {0} `{1}'", url, str_idx);
1350 HelpSource hs = GetHelpSourceFromId (idx);
1351 lastHelpSourceTime = hs.ZipFileWriteTime;
1352 return hs.GetImage (rest.Substring (p + 1));
1354 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly (typeof(RootTree));
1355 return assembly.GetManifestResourceStream (url);
1357 lastHelpSourceTime = DateTime.MinValue;
1361 public HelpSource GetHelpSourceFromId (int id)
1363 return (HelpSource) help_sources [id];
1367 // Fetches the node title
1369 public string GetTitle (string url)
1373 if (url == null || url.StartsWith ("root:"))
1374 return "Mono Documentation";
1376 if (url.Length > 2 && url [1] == ':'){
1379 return url.Substring (2) + " Namespace";
1382 string s = TypeLookup (url, out match_node);
1383 if (match_node != null)
1384 return match_node.Caption;
1385 return url.Substring (2) + " type";
1393 MemberLookup (url.Substring (0,2), url, out match_node);
1394 if (match_node != null)
1395 return match_node.Caption;
1400 return "Mono Documentation";
1405 /// Allows every HelpSource to try to provide the content for this
1408 public string RenderUrl (string url, out Node match_node)
1410 lastHelpSourceTime = DateTime.MinValue;
1411 if (url == "root:") {
1414 // look whether there are contribs
1415 GlobalChangeset chgs = EditingUtils.changes;
1416 StringBuilder con = new StringBuilder ();
1418 //add links to the contrib
1419 int oldContrib = 0, contribs = 0;
1420 con.Append ("<ul>");
1421 foreach (DocSetChangeset dscs in chgs.DocSetChangesets)
1422 foreach (FileChangeset fcs in dscs.FileChangesets)
1423 foreach (Change c in fcs.Changes) {
1424 if (c.NodeUrl == null) {
1425 if (c.Serial == SettingsHandler.Settings.SerialNumber)
1427 } else if (c.Serial == SettingsHandler.Settings.SerialNumber) {
1429 con.Append (String.Format ("<li><a href=\"{0}\">{0}</a></li>", c.NodeUrl));
1433 string contrib = (oldContrib + contribs) == 1?"There is {0} contribution":"There are {0} contributions";
1434 con.Insert (0, String.Format (contrib, oldContrib + contribs) + " pending upload <i>(Contributing--> Upload)</i>", 1);
1435 con.Append ("</ul>");
1436 if (oldContrib == 1)
1437 con.Append ("<i>You have 1 contribution that is not listed below that will be sent the next time you upload contributions. Only contributions made from now on will be listed.</i>");
1438 else if (oldContrib > 1)
1439 con.Append ("<i>You have " + oldContrib + "contributions that are not listed below and will be sent the next time you upload contributions. Only contributions made from now on will be listed.</i>");
1441 //start the rendering
1442 if (!HelpSource.use_css) {
1443 StringBuilder sb = new StringBuilder ("<table bgcolor=\"#b0c4de\" width=\"100%\" cellpadding=\"5\"><tr><td><h3>Mono Documentation Library</h3></td></tr></table>");
1445 foreach (Node n in Nodes)
1446 sb.AppendFormat ("<a href='{0}'>{1}</a><br/>", n.Element, n.Caption);
1449 sb.Append ("<br><table bgcolor=\"#fff3f3\" width=\"100%\" cellpadding=\"5\"><tr><td>");
1450 sb.Append ("<h5>Contributions</h5><br>");
1451 if ((oldContrib + contribs) == 0) {
1452 sb.Append ("<p><b>You have not made any contributions yet.</b></p>");
1453 sb.Append ("<p>The Documentation of the libraries is not complete and your contributions would be greatly appreciated. The procedure is easy, browse to the part of the documentation you want to contribute to and click on the <font color=\"blue\">[Edit]</font> link to start writing the documentation.</p>");
1454 sb.Append ("<p>When you are happy with your changes, use the Contributing--> Upload Contributions menu to send your contributions to our server.</p></div>");
1456 sb.Append (con.ToString ());
1458 sb.Append ("</td></tr></table>");
1459 return sb.ToString ();
1461 if (home_cache == null) {
1462 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly (typeof (HelpSource));
1463 Stream hp_stream = assembly.GetManifestResourceStream ("home.html");
1464 home_cache = (new StreamReader (hp_stream)).ReadToEnd ();
1466 StringBuilder sb = new StringBuilder (home_cache);
1468 sb.Replace ("@@FONT_FAMILY@@", SettingsHandler.Settings.preferred_font_family);
1469 sb.Replace ("@@FONT_SIZE@@", SettingsHandler.Settings.preferred_font_size.ToString());
1471 var visible = SettingsHandler.Settings.EnableEditing ? "block;" : "none;";
1472 if ((oldContrib + contribs) == 0) {
1473 sb.Replace ("@@CONTRIB_DISP@@", "display: none;");
1474 sb.Replace ("@@NO_CONTRIB_DISP@@", "display: " + visible);
1476 sb.Replace ("@@CONTRIB_DISP@@", "display: " + visible);
1477 sb.Replace ("@@NO_CONTRIB_DISP@@", "display: none;");
1478 sb.Replace ("@@CONTRIBS@@", con.ToString ());
1480 sb.Replace ("@@EDITING_ENABLED@@", "display: " + visible);
1482 // load the url of nodes
1484 StringBuilder urls = new StringBuilder ();
1485 foreach (Node n in Nodes) {
1486 add_str = String.Format ("<li><a href=\"{0}\">{1}</a></li>", n.Element, n.Caption);
1487 urls.Append (add_str);
1489 sb.Replace ("@@API_DOCS@@", urls.ToString ());
1491 return sb.ToString ();
1495 if (url.StartsWith ("root:")) {
1496 match_node = ((Node)name_to_node [url.Substring (6)]);
1497 HelpSource hs = ((HelpSource)name_to_hs [url.Substring (6)]);
1500 return GenerateNodeIndex(match_node);
1504 lastHelpSourceTime = hs.ZipFileWriteTime;
1505 return hs.GetText ("root:", out dummy);
1509 if (url.StartsWith ("source-id:")){
1510 string rest = url.Substring (10);
1511 int p = rest.IndexOf (":");
1512 string str_idx = rest.Substring (0, p);
1516 idx = Int32.Parse (str_idx);
1518 Console.Error.WriteLine ("Failed to parse source-id url: {0} `{1}'", url, str_idx);
1522 HelpSource hs = (HelpSource) help_sources [idx];
1523 // Console.WriteLine ("Attempting to get docs from: " + rest.Substring (p + 1));
1524 lastHelpSourceTime = hs.ZipFileWriteTime;
1525 return hs.GetText (rest.Substring (p + 1), out match_node);
1528 if (url.Length < 2){
1533 string prefix = url.Substring (0, 2);
1535 switch (prefix.ToUpper ()){
1537 foreach (HelpSource hs in help_sources){
1538 string s = hs.RenderNamespaceLookup (url, out match_node);
1540 lastHelpSourceTime = hs.ZipFileWriteTime;
1548 return TypeLookup (url, out match_node);
1556 return MemberLookup (prefix, url, out match_node);
1559 foreach (HelpSource hs in help_sources){
1560 string s = hs.GetText (url, out match_node);
1563 lastHelpSourceTime = hs.ZipFileWriteTime;
1572 public string GenerateNodeIndex (Node node)
1574 StringBuilder buf = new StringBuilder();
1575 buf.AppendFormat("<H3>{0}</H3>", node.Caption);
1577 foreach (Node child in node.Nodes)
1579 buf.AppendFormat("<li><a href=\"{0}\">{1}</a>", child.URL, child.Caption);
1581 buf.Append("</ul>");
1582 return buf.ToString();
1585 public IndexReader GetIndex ()
1587 //try to load from basedir
1588 string index_file = Path.Combine (basedir, "monodoc.index");
1589 if (File.Exists (index_file))
1590 return IndexReader.Load (index_file);
1591 //then, try to load from config dir
1592 index_file = Path.Combine (SettingsHandler.Path, "monodoc.index");
1593 return IndexReader.Load (index_file);
1597 public static void MakeIndex ()
1599 RootTree root = LoadTree ();
1603 IndexMaker index_maker = new IndexMaker ();
1605 foreach (HelpSource hs in root.help_sources){
1606 hs.PopulateIndex (index_maker);
1609 // if the user has no write permissions use config dir
1610 string path = Path.Combine (root.basedir, "monodoc.index");
1612 index_maker.Save (path);
1613 } catch (System.UnauthorizedAccessException) {
1614 path = Path.Combine (SettingsHandler.Path, "monodoc.index");
1616 index_maker.Save (path);
1617 } catch (System.UnauthorizedAccessException) {
1618 Console.WriteLine ("Unable to write index file in {0}", Path.Combine (SettingsHandler.Path, "monodoc.index"));
1624 // No octal in C#, how lame is that
1625 chmod (path, 0x1a4);
1627 Console.WriteLine ("Documentation index updated");
1630 static bool IsUnix {
1632 int p = (int) Environment.OSVersion.Platform;
1633 return ((p == 4) || (p == 128) || (p == 6));
1638 public SearchableIndex GetSearchIndex ()
1640 //try to load from basedir
1641 string index_file = Path.Combine (basedir, "search_index");
1642 if (Directory.Exists (index_file))
1643 return SearchableIndex.Load (index_file);
1644 //then, try to load from config dir
1645 index_file = Path.Combine (SettingsHandler.Path, "search_index");
1646 return SearchableIndex.Load (index_file);
1649 public static void MakeSearchIndex ()
1651 // Loads the RootTree
1652 Console.WriteLine ("Loading the monodoc tree...");
1653 RootTree root = LoadTree ();
1657 string dir = Path.Combine (root.basedir, "search_index");
1659 //try to create the dir to store the index
1661 if (!Directory.Exists (dir))
1662 Directory.CreateDirectory (dir);
1664 writer = new IndexWriter(Lucene.Net.Store.FSDirectory.GetDirectory(dir, true), new StandardAnalyzer(), true);
1665 } catch (UnauthorizedAccessException) {
1666 //try in the .config directory
1668 dir = Path.Combine (SettingsHandler.Path, "search_index");
1669 if (!Directory.Exists (dir))
1670 Directory.CreateDirectory (dir);
1672 writer = new IndexWriter(Lucene.Net.Store.FSDirectory.GetDirectory(dir, true), new StandardAnalyzer(), true);
1673 } catch (UnauthorizedAccessException) {
1674 Console.WriteLine ("You don't have permissions to write on " + dir);
1679 //Collect all the documents
1680 Console.WriteLine ("Collecting and adding documents...");
1681 foreach (HelpSource hs in root.HelpSources)
1682 hs.PopulateSearchableIndex (writer);
1684 //Optimize and close
1685 Console.WriteLine ("Closing...");
1691 public ICollection HelpSources { get { return new ArrayList(help_sources); } }
1693 [System.Runtime.InteropServices.DllImport ("libc")]
1694 static extern int chmod (string filename, int mode);