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
18 using System.Text.RegularExpressions;
19 using System.Collections;
20 using System.Configuration;
22 using System.Xml.XPath;
23 using ICSharpCode.SharpZipLib.Zip;
25 using Monodoc.Lucene.Net.Index;
26 using Monodoc.Lucene.Net.Analysis.Standard;
28 /// This tree is populated by the documentation providers, or populated
29 /// from a binary encoding of the tree. The format of the tree is designed
30 /// to minimize the need to load it in full.
32 public class Tree : Node {
34 #region Loading the tree from a file
37 /// Our HelpSource container
39 public readonly HelpSource HelpSource;
41 internal FileStream InputStream;
42 internal BinaryReader InputReader;
45 /// Load from file constructor
47 public Tree (HelpSource hs, string filename) : base (null, null)
49 Encoding utf8 = new UTF8Encoding (false, true);
51 if (!File.Exists (filename)){
52 throw new FileNotFoundException ();
55 InputStream = File.OpenRead (filename);
56 InputReader = new BinaryReader (InputStream, utf8);
57 byte [] sig = InputReader.ReadBytes (4);
60 throw new Exception ("Invalid file format");
62 InputStream.Position = 4;
63 position = InputReader.ReadInt32 ();
70 /// Tree creation and merged tree constructor
72 public Tree (HelpSource hs, string caption, string url) : base (caption, url)
77 public Tree (HelpSource hs, Node parent, string caption, string element) : base (parent, caption, element)
85 /// Saves the tree into the specified file using the help file format.
87 public void Save (string file)
89 Encoding utf8 = new UTF8Encoding (false, true);
90 using (FileStream output = File.OpenWrite (file)){
91 // Skip over the pointer to the first node.
94 using (BinaryWriter writer = new BinaryWriter (output, utf8)){
96 Dump (output, writer);
99 writer.Write (new byte [] { (byte) 'M', (byte) 'o', (byte) 'H', (byte) 'P' });
100 writer.Write (position);
105 static bool GoodSig (byte [] sig)
109 if (sig [0] != (byte) 'M' ||
110 sig [1] != (byte) 'o' ||
111 sig [2] != (byte) 'H' ||
112 sig [3] != (byte) 'P')
119 public class Node : IComparable {
120 string caption, element;
121 public bool Documented;
122 public readonly Tree tree;
124 protected ArrayList nodes;
125 protected internal int position;
128 /// Creates a node, called by the Tree.
130 public Node (string caption, string element)
132 this.tree = (Tree) this;
133 this.caption = caption;
134 this.element = element;
138 public Node (Node parent, string caption, string element)
140 this.parent = parent;
141 this.tree = parent.tree;
142 this.caption = caption;
143 this.element = element;
147 /// Creates a node from an on-disk representation
149 Node (Node parent, int address)
151 this.parent = parent;
153 this.tree = parent.tree;
158 public void AddNode (Node n)
165 public void DelNode (Node n)
170 public ArrayList Nodes {
178 public string Element {
190 public string Caption {
204 public void LoadNode ()
207 position = -position;
209 tree.InputStream.Position = position;
210 BinaryReader reader = tree.InputReader;
211 int count = DecodeInt (reader);
212 element = reader.ReadString ();
213 caption = reader.ReadString ();
217 nodes = new ArrayList (count);
218 for (int i = 0; i < count; i++){
219 int child_address = DecodeInt (reader);
221 Node t = new Node (this, -child_address);
227 /// Creates a new node, in the locator entry point, and with
228 /// a user visible caption of @caption
230 public Node CreateNode (string c_caption, string c_element)
233 nodes = new ArrayList ();
235 Node t = new Node (this, c_caption, c_element);
241 /// Looks up or creates a new node, in the locator entry point, and with
242 /// a user visible caption of @caption. This is different from
243 /// CreateNode in that it will look up an existing node for the given @locator.
245 public Node LookupNode (string c_caption, string c_element)
248 return CreateNode (c_caption, c_element);
250 foreach (Node n in nodes){
251 if (n.element == c_element)
254 return CreateNode (c_caption, c_element);
257 public void EnsureNodes ()
260 nodes = new ArrayList ();
265 return nodes == null;
269 void EncodeInt (BinaryWriter writer, int value)
272 int high = (value >> 7) & 0x01ffffff;
273 byte b = (byte)(value & 0x7f);
276 b = (byte)(b | 0x80);
284 int DecodeInt (BinaryReader reader)
291 b = reader.ReadByte();
293 ret = ret | ((b & 0x7f) << shift);
295 } while ((b & 0x80) == 0x80);
300 internal void Dump (FileStream output, BinaryWriter writer)
303 foreach (Node child in nodes){
304 child.Dump (output, writer);
307 position = (int) output.Position;
308 EncodeInt (writer, nodes == null ? 0 : (int) nodes.Count);
309 writer.Write (element);
310 writer.Write (caption);
313 foreach (Node child in nodes){
314 EncodeInt (writer, child.position);
321 static void Indent ()
323 for (int i = 0; i < indent; i++)
327 public static void PrintTree (Node node)
330 Console.WriteLine ("{0},{1}", node.Element, node.Caption);
331 if (node.Nodes == null)
335 foreach (Node n in node.nodes)
351 if (element.IndexOf (":") >= 0)
355 string url = parent.URL;
357 if (url.EndsWith ("/"))
358 return url + element;
360 return parent.URL + "/" + element;
366 int IComparable.CompareTo (object obj)
368 Node other = obj as Node;
374 if (other.position < 0)
377 return String.CompareOrdinal(caption, other.caption);
382 // The HelpSource class keeps track of the archived data, and its
385 public class HelpSource {
387 public static bool use_css = false;
388 public static string css_code;
389 public static string CssCode {
391 if (css_code != null)
394 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetCallingAssembly ();
395 Stream str_css = assembly.GetManifestResourceStream ("base.css");
396 StringBuilder sb = new StringBuilder ((new StreamReader (str_css)).ReadToEnd());
397 sb.Replace ("@@FONT_FAMILY@@", SettingsHandler.Settings.preferred_font_family);
398 sb.Replace ("@@FONT_SIZE@@", SettingsHandler.Settings.preferred_font_size.ToString());
399 css_code = sb.ToString ();
402 set { css_code = value; }
405 public static bool FullHtml = true;
408 // The unique ID for this HelpSource.
411 DateTime zipFileWriteTime;
414 public HelpSource (string base_filename, bool create)
416 this.name = Path.GetFileName (base_filename);
417 tree_filename = base_filename + ".tree";
418 zip_filename = base_filename + ".zip";
423 Tree = new Tree (this, tree_filename);
427 FileInfo fi = new FileInfo (zip_filename);
428 zipFileWriteTime = fi.LastWriteTime;
430 zipFileWriteTime = DateTime.Now;
434 public HelpSource() {
435 Tree = new Tree (this, "Blah", "Blah");
439 public DateTime ZipFileWriteTime {
441 return zipFileWriteTime;
445 public int SourceID {
460 /// Returns a stream from the packaged help source archive
462 public Stream GetHelpStream (string id)
464 if (zip_file == null)
465 zip_file = new ZipFile (zip_filename);
467 ZipEntry entry = zip_file.GetEntry (id);
469 return zip_file.GetInputStream (entry);
473 public string GetRealPath (string file)
475 if (zip_file == null)
476 zip_file = new ZipFile (zip_filename);
478 ZipEntry entry = zip_file.GetEntry (file);
479 if (entry != null && entry.ExtraData != null)
480 return ConvertToString (entry.ExtraData);
484 public XmlReader GetHelpXml (string id)
486 if (zip_file == null)
487 zip_file = new ZipFile (zip_filename);
489 ZipEntry entry = zip_file.GetEntry (id);
491 Stream s = zip_file.GetInputStream (entry);
492 string url = "monodoc:///" + SourceID + "@" + System.Web.HttpUtility.UrlEncode (id) + "@";
493 return new XmlTextReader (url, s);
498 public XmlDocument GetHelpXmlWithChanges (string id)
500 if (zip_file == null)
501 zip_file = new ZipFile (zip_filename);
503 ZipEntry entry = zip_file.GetEntry (id);
505 Stream s = zip_file.GetInputStream (entry);
506 string url = "monodoc:///" + SourceID + "@" + System.Web.HttpUtility.UrlEncode (id) + "@";
507 XmlReader r = new XmlTextReader (url, s);
508 XmlDocument ret = new XmlDocument ();
511 if (entry.ExtraData != null)
512 EditingUtils.AccountForChanges (ret, Name, ConvertToString (entry.ExtraData));
520 /// Get a nice, unique expression for any XPath node that you get.
521 /// This function is used by editing to get the expression to put
522 /// on to the file. The idea is to create an expression that is resistant
523 /// to changes in the structure of the XML.
525 public virtual string GetNodeXPath (XPathNavigator n)
527 return EditingUtils.GetXPath (n.Clone ());
530 public string GetEditUri (XPathNavigator n)
532 return EditingUtils.FormatEditUri (n.BaseURI, GetNodeXPath (n));
535 static string ConvertToString (byte[] data)
537 return Encoding.UTF8.GetString(data);
540 static byte[] ConvertToArray (string str)
542 return Encoding.UTF8.GetBytes(str);
546 /// The tree that is being populated
549 public RootTree RootTree;
551 // Base filename used by this HelpSource.
552 string tree_filename, zip_filename;
555 const int buffer_size = 65536;
556 ZipOutputStream zip_output;
559 HelpSource (string base_filename)
563 void SetupForOutput ()
565 Tree = new Tree (this, "", "");
567 FileStream stream = File.Create (zip_filename);
569 zip_output = new ZipOutputStream (stream);
570 zip_output.SetLevel (9);
572 buffer = new byte [buffer_size];
576 /// Saves the tree and the archive
580 Tree.Save (tree_filename);
581 zip_output.Finish ();
589 return String.Format ("{0}", code++);
593 /// Providers call this to store a file they will need, and the return value
594 /// is the name that was assigned to it
596 public string PackFile (string file)
598 string entry_name = GetNewCode ();
599 return PackFile (file, entry_name);
602 public string PackFile (string file, string entry_name)
604 using (FileStream input = File.OpenRead (file)) {
605 PackStream (input, entry_name, file);
611 public void PackStream (Stream s, string entry_name)
613 PackStream (s, entry_name, null);
616 void PackStream (Stream s, string entry_name, string realPath)
618 ZipEntry entry = new ZipEntry (entry_name);
620 if (realPath != null)
621 entry.ExtraData = ConvertToArray (realPath);
623 zip_output.PutNextEntry (entry);
626 while ((n = s.Read (buffer, 0, buffer_size)) > 0){
627 zip_output.Write (buffer, 0, n);
631 public void PackXml (string fname, XmlDocument doc, string real_path)
633 ZipEntry entry = new ZipEntry (fname);
634 if (real_path != null)
635 entry.ExtraData = ConvertToArray(real_path);
637 zip_output.PutNextEntry (entry);
638 XmlTextWriter xmlWriter = new XmlTextWriter (zip_output, Encoding.UTF8);
639 doc.WriteContentTo (xmlWriter);
643 public virtual void RenderPreviewDocs (XmlNode newNode, XmlWriter writer)
645 throw new NotImplementedException ();
648 public virtual string GetText (string url, out Node n)
654 public virtual Stream GetImage (string url)
660 // Default method implementation does not satisfy the request
662 public virtual string RenderTypeLookup (string prefix, string ns, string type, string member, out Node n)
668 public virtual string RenderNamespaceLookup (string nsurl, out Node n)
675 // Populates the index.
677 public virtual void PopulateIndex (IndexMaker index_maker)
682 // Build an html document
684 public static string BuildHtml (string css, string html_code)
686 return BuildHtml (css, null, html_code);
689 internal static string BuildHtml (string css, string js, string html_code) {
693 StringWriter output = new StringWriter ();
694 output.Write ("<html><head>");
695 output.Write ("<style type=\"text/css\">");
696 output.Write (CssCode);
698 output.Write ("</style>");
700 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetCallingAssembly ();
701 Stream str_js = assembly.GetManifestResourceStream ("helper.js");
702 StringBuilder sb = new StringBuilder ((new StreamReader (str_js)).ReadToEnd());
703 output.Write (sb.ToString ());
706 output.Write ("<script type=\"text/JavaScript\">\n");
708 output.Write ("\n</script>");
711 output.Write ("</head><body>");
712 output.Write (html_code);
713 output.Write ("</body></html>");
714 return output.ToString ();
718 // Create different Documents for adding to Lucene search index
719 // The default action is do nothing. Subclasses should add the docs
721 public virtual void PopulateSearchableIndex (IndexWriter writer) {
727 public abstract class Provider {
729 // This code is used to "tag" all the different sources
740 public abstract void PopulateTree (Tree tree);
743 // Called at shutdown time after the tree has been populated to perform
744 // any fixups or final tasks.
746 public abstract void CloseTree (HelpSource hs, Tree tree);
749 public class RootTree : Tree {
752 public static ArrayList UncompiledHelpSources = new ArrayList();
754 public const int MonodocVersion = 1;
756 public static RootTree LoadTree ()
759 string myPath = System.Reflection.Assembly.GetExecutingAssembly ().Location;
760 string cfgFile = myPath + ".config";
761 if (!File.Exists (cfgFile)) {
763 return LoadTree (basedir);
766 XmlDocument d = new XmlDocument ();
768 basedir = d.SelectSingleNode ("config/path").Attributes ["docsPath"].Value;
770 return LoadTree (basedir);
774 // Loads the tree layout
776 public static RootTree LoadTree (string basedir)
778 XmlDocument doc = new XmlDocument ();
780 RootTree root = new RootTree ();
781 root.basedir = basedir;
786 string layout = Path.Combine (basedir, "monodoc.xml");
788 XmlNodeList nodes = doc.SelectNodes ("/node/node");
790 root.name_to_node ["root"] = root;
791 root.name_to_node ["libraries"] = root;
792 root.Populate (root, nodes);
794 Node third_party = root.LookupEntryPoint ("various");
795 if (third_party == null) {
796 Console.Error.WriteLine ("No 'various' doc node! Check monodoc.xml!");
803 string sources_dir = Path.Combine (basedir, "sources");
805 string [] files = Directory.GetFiles (sources_dir);
806 foreach (string file in files){
807 if (!file.EndsWith (".source"))
810 doc = new XmlDocument ();
814 Console.Error.WriteLine ("Error: Could not load source file {0}", file);
818 XmlNodeList extra_nodes = doc.SelectNodes ("/monodoc/node");
819 if (extra_nodes.Count > 0)
820 root.Populate (third_party, extra_nodes);
822 XmlNodeList sources = doc.SelectNodes ("/monodoc/source");
823 if (sources == null){
824 Console.Error.WriteLine ("Error: No <source> section found in the {0} file", file);
827 foreach (XmlNode source in sources){
828 XmlAttribute a = source.Attributes ["provider"];
830 Console.Error.WriteLine ("Error: no provider in <source>");
833 string provider = a.InnerText;
834 a = source.Attributes ["basefile"];
836 Console.Error.WriteLine ("Error: no basefile in <source>");
839 string basefile = a.InnerText;
840 a = source.Attributes ["path"];
842 Console.Error.WriteLine ("Error: no path in <source>");
845 string path = a.InnerText;
847 string basefilepath = Path.Combine (sources_dir, basefile);
848 HelpSource hs = GetHelpSource (provider, basefilepath);
852 root.help_sources.Add (hs);
853 root.name_to_hs [path] = hs;
855 Node parent = root.LookupEntryPoint (path);
857 Console.Error.WriteLine ("node `{0}' is not defined on the documentation map", path);
858 parent = third_party;
861 foreach (Node n in hs.Tree.Nodes){
868 foreach (string path in UncompiledHelpSources) {
869 EcmaUncompiledHelpSource hs = new EcmaUncompiledHelpSource(path);
870 root.help_sources.Add (hs);
871 string epath = "extra-help-source-" + hs.Name;
872 Node hsn = root.CreateNode (hs.Name, "root:/" + epath);
873 root.name_to_hs [epath] = hs;
875 foreach (Node n in hs.Tree.Nodes){
888 // Delete nodes which does not have documentaiton (source)
889 static bool PurgeNode(Node node)
893 if (!node.Documented)
895 ArrayList del_child = new ArrayList();
896 //Delete node unless any child has documentation
897 bool purged_child = false;
898 foreach (Node child in node.Nodes)
900 purged_child = PurgeNode(child);
903 del_child.Add(child);
907 // delete the node if all its children are to be deleted
908 purge = (node.Nodes.Count == del_child.Count);
911 foreach (Node child in del_child)
921 static HelpSource GetHelpSource (string provider, string basefilepath)
926 return new EcmaHelpSource (basefilepath, false);
927 case "ecma-uncompiled":
928 return new EcmaUncompiledHelpSource (basefilepath);
930 return new MonoHBHelpSource(basefilepath, false);
932 return new XhtmlHelpSource (basefilepath, false);
934 return new ManHelpSource (basefilepath, false);
936 return new SimpleHelpSource (basefilepath, false);
938 return new ErrorHelpSource (basefilepath, false);
940 return new EcmaSpecHelpSource (basefilepath, false);
942 return new AddinsHelpSource (basefilepath, false);
944 Console.Error.WriteLine ("Error: Unknown provider specified: {0}", provider);
949 catch (FileNotFoundException) {
950 Console.Error.WriteLine ("Error: did not find one of the files in sources/"+basefilepath);
957 // Maintains the name to node mapping
959 Hashtable name_to_node = new Hashtable ();
960 Hashtable name_to_hs = new Hashtable ();
962 void Populate (Node parent, XmlNodeList xml_node_list)
964 foreach (XmlNode xml_node in xml_node_list){
965 XmlAttribute e = xml_node.Attributes ["parent"];
966 if (e != null && name_to_node.ContainsKey (e.InnerText)) {
967 Node p = (Node) name_to_node [e.InnerText];
968 xml_node.Attributes.Remove (e);
969 Populate (p, xml_node.SelectNodes ("."));
972 e = xml_node.Attributes ["label"];
974 Console.Error.WriteLine ("`label' attribute missing in <node>");
977 string label = e.InnerText;
978 e = xml_node.Attributes ["name"];
980 Console.Error.WriteLine ("`name' attribute missing in <node>");
983 string name = e.InnerText;
985 Node n = parent.LookupNode (label, "root:/" + name);
987 name_to_node [name] = n;
988 XmlNodeList children = xml_node.SelectNodes ("./node");
989 if (children != null)
990 Populate (n, children);
994 public Node LookupEntryPoint (string name)
996 return (Node) name_to_node [name];
999 ArrayList help_sources;
1000 DateTime lastHelpSourceTime;
1002 RootTree () : base (null, "Mono Documentation", "root:")
1004 nodes = new ArrayList ();
1005 help_sources = new ArrayList ();
1006 lastHelpSourceTime = DateTime.MinValue;
1009 public DateTime LastHelpSourceTime {
1011 return lastHelpSourceTime;
1015 public static bool GetNamespaceAndType (string url, out string ns, out string type)
1019 for (int i = 0; i < url.Length; ++i) {
1038 Console.Error.WriteLine ("Did not find dot in: " + url);
1043 ns = url.Substring (0, nsidx);
1044 type = url.Substring (nsidx + 1);
1046 //Console.Error.WriteLine ("GetNameSpaceAndType (ns={0}, type={1}", ns, type);
1050 public XmlDocument GetHelpXml (string url)
1052 string rest = url.Substring (2);
1055 if (!GetNamespaceAndType (rest, out ns, out type))
1058 foreach (HelpSource hs in help_sources) {
1059 EcmaHelpSource ehs = hs as EcmaHelpSource;
1062 string id = ehs.GetIdFromUrl ("T:", ns, type);
1065 XmlDocument doc = hs.GetHelpXmlWithChanges (id);
1072 public string TypeLookup (string url, out Node match_node)
1074 string rest = Regex.Replace (url, @"^T:\s*", "");
1077 if (!GetNamespaceAndType (rest, out ns, out type)){
1082 foreach (HelpSource hs in help_sources){
1083 string s = hs.RenderTypeLookup ("T:", ns, type, null, out match_node);
1086 lastHelpSourceTime = hs.ZipFileWriteTime;
1094 public string MemberLookup (string prefix, string url, out Node match_node)
1096 string rest = Regex.Replace (url, @"^.:\s*", "");
1098 // Dots in the arg list (for methods) confuse this.
1099 // Chop off the arg list for now and put it back later.
1100 string arglist = "";
1101 int argliststart = rest.IndexOf("(");
1102 if (argliststart >= 0) {
1103 arglist = rest.Substring(argliststart);
1104 rest = rest.Substring(0, argliststart);
1107 string ns_type, member;
1109 if (prefix != "C:") {
1110 int member_idx = rest.LastIndexOf (".");
1112 // The dot in .ctor (if it's a M: link) would confuse this.
1113 if (rest.EndsWith("..ctor")) member_idx--;
1115 ns_type = rest.Substring (0, member_idx);
1116 member = rest.Substring (member_idx + 1);
1118 // C: links don't have the .ctor member part as it would in a M: link
1119 // Even though externally C: links are different from M: links,
1120 // C: links get transformed into M:-style links (with .ctor) here.
1126 //Console.WriteLine ("NS_TYPE: {0} MEMBER: {1}", ns_type, member);
1129 if (!GetNamespaceAndType (ns_type, out ns, out type)){
1134 foreach (HelpSource hs in help_sources){
1135 string s = hs.RenderTypeLookup (prefix, ns, type, member + arglist, out match_node);
1138 lastHelpSourceTime = hs.ZipFileWriteTime;
1146 public Stream GetImage (string url)
1148 if (url.StartsWith ("source-id:")){
1149 string rest = url.Substring (10);
1150 int p = rest.IndexOf (":");
1151 string str_idx = rest.Substring (0, p);
1155 idx = Int32.Parse (str_idx);
1157 Console.Error.WriteLine ("Failed to parse source-id url: {0} `{1}'", url, str_idx);
1161 HelpSource hs = GetHelpSourceFromId (idx);
1162 lastHelpSourceTime = hs.ZipFileWriteTime;
1163 return hs.GetImage (rest.Substring (p + 1));
1165 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly (typeof(RootTree));
1166 return assembly.GetManifestResourceStream (url);
1168 lastHelpSourceTime = DateTime.MinValue;
1172 public HelpSource GetHelpSourceFromId (int id)
1174 return (HelpSource) help_sources [id];
1179 /// Allows every HelpSource to try to provide the content for this
1182 public string RenderUrl (string url, out Node match_node)
1184 lastHelpSourceTime = DateTime.MinValue;
1185 if (url == "root:") {
1188 // look whether there are contribs
1189 GlobalChangeset chgs = EditingUtils.changes;
1190 StringBuilder con = new StringBuilder ();
1192 //add links to the contrib
1193 int oldContrib = 0, contribs = 0;
1194 con.Append ("<ul>");
1195 foreach (DocSetChangeset dscs in chgs.DocSetChangesets)
1196 foreach (FileChangeset fcs in dscs.FileChangesets)
1197 foreach (Change c in fcs.Changes) {
1198 if (c.NodeUrl == null) {
1199 if (c.Serial == SettingsHandler.Settings.SerialNumber)
1201 } else if (c.Serial == SettingsHandler.Settings.SerialNumber) {
1203 con.Append (String.Format ("<li><a href=\"{0}\">{0}</a></li>", c.NodeUrl));
1207 string contrib = (oldContrib + contribs) == 1?"There is {0} contribution":"There are {0} contributions";
1208 con.Insert (0, String.Format (contrib, oldContrib + contribs) + " pending upload <i>(Contributing--> Upload)</i>", 1);
1209 con.Append ("</ul>");
1210 if (oldContrib == 1)
1211 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>");
1212 else if (oldContrib > 1)
1213 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>");
1215 //start the rendering
1216 if (!HelpSource.use_css) {
1217 StringBuilder sb = new StringBuilder ("<table bgcolor=\"#b0c4de\" width=\"100%\" cellpadding=\"5\"><tr><td><h3>Mono Documentation Library</h3></td></tr></table>");
1219 foreach (Node n in Nodes)
1220 sb.AppendFormat ("<a href='{0}'>{1}</a><br/>", n.Element, n.Caption);
1223 sb.Append ("<br><table bgcolor=\"#fff3f3\" width=\"100%\" cellpadding=\"5\"><tr><td>");
1224 sb.Append ("<h5>Contributions</h5><br>");
1225 if ((oldContrib + contribs) == 0) {
1226 sb.Append ("<p><b>You have not made any contributions yet.</b></p>");
1227 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>");
1228 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>");
1230 sb.Append (con.ToString ());
1232 sb.Append ("</td></tr></table>");
1233 return sb.ToString ();
1235 if (home_cache == null) {
1236 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly (typeof (HelpSource));
1237 Stream hp_stream = assembly.GetManifestResourceStream ("home.html");
1238 home_cache = (new StreamReader (hp_stream)).ReadToEnd ();
1240 StringBuilder sb = new StringBuilder (home_cache);
1242 sb.Replace ("@@FONT_FAMILY@@", SettingsHandler.Settings.preferred_font_family);
1243 sb.Replace ("@@FONT_SIZE@@", SettingsHandler.Settings.preferred_font_size.ToString());
1245 if ((oldContrib + contribs) == 0) {
1246 sb.Replace ("@@CONTRIB_DISP@@", "display: none;");
1248 sb.Replace ("@@NO_CONTRIB_DISP@@", "display: none;");
1249 sb.Replace ("@@CONTRIBS@@", con.ToString ());
1252 // load the url of nodes
1254 StringBuilder urls = new StringBuilder ();
1255 foreach (Node n in Nodes) {
1256 add_str = String.Format ("<li><a href=\"{0}\">{1}</a></li>", n.Element, n.Caption);
1257 urls.Append (add_str);
1259 sb.Replace ("@@API_DOCS@@", urls.ToString ());
1261 return sb.ToString ();
1265 if (url.StartsWith ("root:")) {
1266 match_node = ((Node)name_to_node [url.Substring (6)]);
1267 HelpSource hs = ((HelpSource)name_to_hs [url.Substring (6)]);
1270 return GenerateNodeIndex(match_node);
1274 lastHelpSourceTime = hs.ZipFileWriteTime;
1275 return hs.GetText ("root:", out dummy);
1279 if (url.StartsWith ("source-id:")){
1280 string rest = url.Substring (10);
1281 int p = rest.IndexOf (":");
1282 string str_idx = rest.Substring (0, p);
1286 idx = Int32.Parse (str_idx);
1288 Console.Error.WriteLine ("Failed to parse source-id url: {0} `{1}'", url, str_idx);
1292 HelpSource hs = (HelpSource) help_sources [idx];
1293 // Console.WriteLine ("Attempting to get docs from: " + rest.Substring (p + 1));
1294 lastHelpSourceTime = hs.ZipFileWriteTime;
1295 return hs.GetText (rest.Substring (p + 1), out match_node);
1298 if (url.Length < 2){
1303 string prefix = url.Substring (0, 2);
1305 switch (prefix.ToUpper ()){
1307 foreach (HelpSource hs in help_sources){
1308 string s = hs.RenderNamespaceLookup (url, out match_node);
1310 lastHelpSourceTime = hs.ZipFileWriteTime;
1318 return TypeLookup (url, out match_node);
1326 return MemberLookup (prefix, url, out match_node);
1329 foreach (HelpSource hs in help_sources){
1330 string s = hs.GetText (url, out match_node);
1333 lastHelpSourceTime = hs.ZipFileWriteTime;
1342 public string GenerateNodeIndex (Node node)
1344 StringBuilder buf = new StringBuilder();
1345 buf.AppendFormat("<H3>{0}</H3>", node.Caption);
1347 foreach (Node child in node.Nodes)
1349 buf.AppendFormat("<li><a href=\"{0}\">{1}</a>", child.URL, child.Caption);
1351 buf.Append("</ul>");
1352 return buf.ToString();
1355 public IndexReader GetIndex ()
1357 //try to load from basedir
1358 string index_file = Path.Combine (basedir, "monodoc.index");
1359 if (File.Exists (index_file))
1360 return IndexReader.Load (index_file);
1361 //then, try to load from config dir
1362 index_file = Path.Combine (SettingsHandler.Path, "monodoc.index");
1363 return IndexReader.Load (index_file);
1367 public static void MakeIndex ()
1369 RootTree root = LoadTree ();
1373 IndexMaker index_maker = new IndexMaker ();
1375 foreach (HelpSource hs in root.help_sources){
1376 hs.PopulateIndex (index_maker);
1379 // if the user has no write permissions use config dir
1380 string path = Path.Combine (root.basedir, "monodoc.index");
1382 index_maker.Save (path);
1383 } catch (System.UnauthorizedAccessException) {
1384 path = Path.Combine (SettingsHandler.Path, "monodoc.index");
1386 index_maker.Save (path);
1387 } catch (System.UnauthorizedAccessException) {
1388 Console.WriteLine ("Unable to write index file in {0}", Path.Combine (SettingsHandler.Path, "monodoc.index"));
1394 // No octal in C#, how lame is that
1395 chmod (path, 0x1a4);
1397 Console.WriteLine ("Documentation index updated");
1400 static bool IsUnix {
1402 int p = (int) Environment.OSVersion.Platform;
1403 return ((p == 4) || (p == 128));
1408 public SearchableIndex GetSearchIndex ()
1410 //try to load from basedir
1411 string index_file = Path.Combine (basedir, "search_index");
1412 if (Directory.Exists (index_file))
1413 return SearchableIndex.Load (index_file);
1414 //then, try to load from config dir
1415 index_file = Path.Combine (SettingsHandler.Path, "search_index");
1416 return SearchableIndex.Load (index_file);
1419 public static void MakeSearchIndex ()
1421 // Loads the RootTree
1422 Console.WriteLine ("Loading the monodoc tree...");
1423 RootTree root = LoadTree ();
1427 string dir = Path.Combine (root.basedir, "search_index");
1429 //try to create the dir to store the index
1431 if (!Directory.Exists (dir))
1432 Directory.CreateDirectory (dir);
1434 writer = new IndexWriter(Lucene.Net.Store.FSDirectory.GetDirectory(dir, true), new StandardAnalyzer(), true);
1435 } catch (UnauthorizedAccessException) {
1436 //try in the .config directory
1438 dir = Path.Combine (SettingsHandler.Path, "search_index");
1439 if (!Directory.Exists (dir))
1440 Directory.CreateDirectory (dir);
1442 writer = new IndexWriter(Lucene.Net.Store.FSDirectory.GetDirectory(dir, true), new StandardAnalyzer(), true);
1443 } catch (UnauthorizedAccessException) {
1444 Console.WriteLine ("You don't have permissions to write on " + dir);
1449 //Collect all the documents
1450 Console.WriteLine ("Collecting and adding documents...");
1451 foreach (HelpSource hs in root.HelpSources)
1452 hs.PopulateSearchableIndex (writer);
1454 //Optimize and close
1455 Console.WriteLine ("Closing...");
1461 public ICollection HelpSources { get { return new ArrayList(help_sources); } }
1463 [System.Runtime.InteropServices.DllImport ("libc")]
1464 static extern int chmod (string filename, int mode);