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.Diagnostics;
21 using System.Configuration;
22 using System.Text.RegularExpressions;
24 using System.Xml.XPath;
25 using ICSharpCode.SharpZipLib.Zip;
27 using Monodoc.Lucene.Net.Index;
28 using Monodoc.Lucene.Net.Analysis.Standard;
30 /// This tree is populated by the documentation providers, or populated
31 /// from a binary encoding of the tree. The format of the tree is designed
32 /// to minimize the need to load it in full.
34 public class Tree : Node {
36 #region Loading the tree from a file
39 /// Our HelpSource container
41 public readonly HelpSource HelpSource;
43 internal FileStream InputStream;
44 internal BinaryReader InputReader;
47 /// Load from file constructor
49 public Tree (HelpSource hs, string filename) : base (null, null)
51 Encoding utf8 = new UTF8Encoding (false, true);
53 if (!File.Exists (filename)){
54 throw new FileNotFoundException ();
57 InputStream = File.OpenRead (filename);
58 InputReader = new BinaryReader (InputStream, utf8);
59 byte [] sig = InputReader.ReadBytes (4);
62 throw new Exception ("Invalid file format");
64 InputStream.Position = 4;
65 position = InputReader.ReadInt32 ();
72 /// Tree creation and merged tree constructor
74 public Tree (HelpSource hs, string caption, string url) : base (caption, url)
79 public Tree (HelpSource hs, Node parent, string caption, string element) : base (parent, caption, element)
87 /// Saves the tree into the specified file using the help file format.
89 public void Save (string file)
91 Encoding utf8 = new UTF8Encoding (false, true);
92 using (FileStream output = File.OpenWrite (file)){
93 // Skip over the pointer to the first node.
96 using (BinaryWriter writer = new BinaryWriter (output, utf8)){
98 Dump (output, writer);
101 writer.Write (new byte [] { (byte) 'M', (byte) 'o', (byte) 'H', (byte) 'P' });
102 writer.Write (position);
107 static bool GoodSig (byte [] sig)
111 if (sig [0] != (byte) 'M' ||
112 sig [1] != (byte) 'o' ||
113 sig [2] != (byte) 'H' ||
114 sig [3] != (byte) 'P')
121 public class Node : IComparable {
122 string caption, element;
123 public bool Documented;
124 public readonly Tree tree;
126 protected ArrayList nodes;
127 protected internal int position;
130 /// Creates a node, called by the Tree.
132 public Node (string caption, string element)
134 this.tree = (Tree) this;
135 this.caption = caption;
136 this.element = element;
140 public Node (Node parent, string caption, string element)
142 this.parent = parent;
143 this.tree = parent.tree;
144 this.caption = caption;
145 this.element = element;
149 /// Creates a node from an on-disk representation
151 Node (Node parent, int address)
153 this.parent = parent;
155 this.tree = parent.tree;
160 public void AddNode (Node n)
167 public void DelNode (Node n)
172 public ArrayList Nodes {
180 public string Element {
192 public string Caption {
206 public void LoadNode ()
209 position = -position;
211 tree.InputStream.Position = position;
212 BinaryReader reader = tree.InputReader;
213 int count = DecodeInt (reader);
214 element = reader.ReadString ();
215 caption = reader.ReadString ();
219 nodes = new ArrayList (count);
220 for (int i = 0; i < count; i++){
221 int child_address = DecodeInt (reader);
223 Node t = new Node (this, -child_address);
229 /// Creates a new node, in the locator entry point, and with
230 /// a user visible caption of @caption
232 public Node CreateNode (string c_caption, string c_element)
235 nodes = new ArrayList ();
237 Node t = new Node (this, c_caption, c_element);
243 /// Looks up or creates a new node, in the locator entry point, and with
244 /// a user visible caption of @caption. This is different from
245 /// CreateNode in that it will look up an existing node for the given @locator.
247 public Node LookupNode (string c_caption, string c_element)
250 return CreateNode (c_caption, c_element);
252 foreach (Node n in nodes){
253 if (n.element == c_element)
256 return CreateNode (c_caption, c_element);
259 public void EnsureNodes ()
262 nodes = new ArrayList ();
267 return nodes == null;
271 void EncodeInt (BinaryWriter writer, int value)
274 int high = (value >> 7) & 0x01ffffff;
275 byte b = (byte)(value & 0x7f);
278 b = (byte)(b | 0x80);
286 int DecodeInt (BinaryReader reader)
293 b = reader.ReadByte();
295 ret = ret | ((b & 0x7f) << shift);
297 } while ((b & 0x80) == 0x80);
302 internal void Dump (FileStream output, BinaryWriter writer)
305 foreach (Node child in nodes){
306 child.Dump (output, writer);
309 position = (int) output.Position;
310 EncodeInt (writer, nodes == null ? 0 : (int) nodes.Count);
311 writer.Write (element);
312 writer.Write (caption);
315 foreach (Node child in nodes){
316 EncodeInt (writer, child.position);
323 static void Indent ()
325 for (int i = 0; i < indent; i++)
329 public static void PrintTree (Node node)
332 Console.WriteLine ("{0},{1}", node.Element, node.Caption);
333 if (node.Nodes == null)
337 foreach (Node n in node.nodes)
353 if (element.IndexOf (":") >= 0)
357 string url = parent.URL;
359 if (url.EndsWith ("/"))
360 return url + element;
362 return parent.URL + "/" + element;
368 int IComparable.CompareTo (object obj)
370 Node other = obj as Node;
376 if (other.position < 0)
379 Regex digits = new Regex (@"([\d]+)|([^\d]+)");
380 MatchEvaluator eval = delegate (Match m) {
381 return (m.Value.Length > 0 && char.IsDigit (m.Value [0]))
382 ? m.Value.PadLeft (System.Math.Max (caption.Length, other.caption.Length))
385 return digits.Replace (caption, eval).CompareTo (digits.Replace (other.caption, eval));
390 // The HelpSource class keeps track of the archived data, and its
393 public class HelpSource {
395 public static bool use_css = false;
396 public static string css_code;
397 public static string CssCode {
399 if (css_code != null)
402 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetCallingAssembly ();
403 Stream str_css = assembly.GetManifestResourceStream ("base.css");
404 StringBuilder sb = new StringBuilder ((new StreamReader (str_css)).ReadToEnd());
405 sb.Replace ("@@FONT_FAMILY@@", SettingsHandler.Settings.preferred_font_family);
406 sb.Replace ("@@FONT_SIZE@@", SettingsHandler.Settings.preferred_font_size.ToString());
407 css_code = sb.ToString ();
410 set { css_code = value; }
413 public static bool FullHtml = true;
416 // The unique ID for this HelpSource.
419 DateTime zipFileWriteTime;
421 TraceLevel trace_level = TraceLevel.Warning;
423 public HelpSource (string base_filename, bool create)
425 this.name = Path.GetFileName (base_filename);
426 tree_filename = base_filename + ".tree";
427 zip_filename = base_filename + ".zip";
432 Tree = new Tree (this, tree_filename);
436 FileInfo fi = new FileInfo (zip_filename);
437 zipFileWriteTime = fi.LastWriteTime;
439 zipFileWriteTime = DateTime.Now;
443 public HelpSource() {
444 Tree = new Tree (this, "Blah", "Blah");
448 public DateTime ZipFileWriteTime {
450 return zipFileWriteTime;
454 public int SourceID {
466 public TraceLevel TraceLevel {
467 get { return trace_level; }
468 set { trace_level = value; }
474 /// Returns a stream from the packaged help source archive
476 public Stream GetHelpStream (string id)
478 if (zip_file == null)
479 zip_file = new ZipFile (zip_filename);
481 ZipEntry entry = zip_file.GetEntry (id);
483 return zip_file.GetInputStream (entry);
487 public string GetRealPath (string file)
489 if (zip_file == null)
490 zip_file = new ZipFile (zip_filename);
492 ZipEntry entry = zip_file.GetEntry (file);
493 if (entry != null && entry.ExtraData != null)
494 return ConvertToString (entry.ExtraData);
498 public XmlReader GetHelpXml (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 return new XmlTextReader (url, s);
512 public XmlDocument GetHelpXmlWithChanges (string id)
514 if (zip_file == null)
515 zip_file = new ZipFile (zip_filename);
517 ZipEntry entry = zip_file.GetEntry (id);
519 Stream s = zip_file.GetInputStream (entry);
520 string url = "monodoc:///" + SourceID + "@" + System.Web.HttpUtility.UrlEncode (id) + "@";
521 XmlReader r = new XmlTextReader (url, s);
522 XmlDocument ret = new XmlDocument ();
525 if (entry.ExtraData != null)
526 EditingUtils.AccountForChanges (ret, Name, ConvertToString (entry.ExtraData));
534 /// Get a nice, unique expression for any XPath node that you get.
535 /// This function is used by editing to get the expression to put
536 /// on to the file. The idea is to create an expression that is resistant
537 /// to changes in the structure of the XML.
539 public virtual string GetNodeXPath (XPathNavigator n)
541 return EditingUtils.GetXPath (n.Clone ());
544 public string GetEditUri (XPathNavigator n)
546 return EditingUtils.FormatEditUri (n.BaseURI, GetNodeXPath (n));
549 static string ConvertToString (byte[] data)
551 return Encoding.UTF8.GetString(data);
554 static byte[] ConvertToArray (string str)
556 return Encoding.UTF8.GetBytes(str);
560 /// The tree that is being populated
563 public RootTree RootTree;
565 // Base filename used by this HelpSource.
566 string tree_filename, zip_filename;
569 const int buffer_size = 65536;
570 ZipOutputStream zip_output;
573 HelpSource (string base_filename)
577 void SetupForOutput ()
579 Tree = new Tree (this, "", "");
581 FileStream stream = File.Create (zip_filename);
583 zip_output = new ZipOutputStream (stream);
584 zip_output.SetLevel (9);
586 buffer = new byte [buffer_size];
590 /// Saves the tree and the archive
594 Tree.Save (tree_filename);
595 zip_output.Finish ();
603 return String.Format ("{0}", code++);
607 /// Providers call this to store a file they will need, and the return value
608 /// is the name that was assigned to it
610 public string PackFile (string file)
612 string entry_name = GetNewCode ();
613 return PackFile (file, entry_name);
616 public string PackFile (string file, string entry_name)
618 using (FileStream input = File.OpenRead (file)) {
619 PackStream (input, entry_name, file);
625 public void PackStream (Stream s, string entry_name)
627 PackStream (s, entry_name, null);
630 void PackStream (Stream s, string entry_name, string realPath)
632 ZipEntry entry = new ZipEntry (entry_name);
634 if (realPath != null)
635 entry.ExtraData = ConvertToArray (realPath);
637 zip_output.PutNextEntry (entry);
640 while ((n = s.Read (buffer, 0, buffer_size)) > 0){
641 zip_output.Write (buffer, 0, n);
645 public void PackXml (string fname, XmlDocument doc, string real_path)
647 ZipEntry entry = new ZipEntry (fname);
648 if (real_path != null)
649 entry.ExtraData = ConvertToArray(real_path);
651 zip_output.PutNextEntry (entry);
652 XmlTextWriter xmlWriter = new XmlTextWriter (zip_output, Encoding.UTF8);
653 doc.WriteContentTo (xmlWriter);
657 public virtual void RenderPreviewDocs (XmlNode newNode, XmlWriter writer)
659 throw new NotImplementedException ();
662 public virtual string GetText (string url, out Node n)
668 public virtual Stream GetImage (string url)
674 // Default method implementation does not satisfy the request
676 public virtual string RenderTypeLookup (string prefix, string ns, string type, string member, out Node n)
682 public virtual string RenderNamespaceLookup (string nsurl, out Node n)
689 // Populates the index.
691 public virtual void PopulateIndex (IndexMaker index_maker)
696 // Build an html document
698 public static string BuildHtml (string css, string html_code)
700 return BuildHtml (css, null, html_code);
703 internal static string BuildHtml (string css, string js, string html_code) {
707 StringWriter output = new StringWriter ();
708 output.Write ("<html><head>");
709 output.Write ("<style type=\"text/css\">");
710 output.Write (CssCode);
712 output.Write ("</style>");
714 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetCallingAssembly ();
715 Stream str_js = assembly.GetManifestResourceStream ("helper.js");
716 StringBuilder sb = new StringBuilder ((new StreamReader (str_js)).ReadToEnd());
717 output.Write (sb.ToString ());
720 output.Write ("<script type=\"text/JavaScript\">\n");
722 output.Write ("\n</script>");
725 output.Write ("</head><body>");
726 output.Write (html_code);
727 output.Write ("</body></html>");
728 return output.ToString ();
732 // Create different Documents for adding to Lucene search index
733 // The default action is do nothing. Subclasses should add the docs
735 public virtual void PopulateSearchableIndex (IndexWriter writer) {
739 public void Message (TraceLevel level, string format, params object[] args)
741 if ((int) level <= (int) trace_level)
742 Console.WriteLine (format, args);
745 public void Error (string format, params object[] args)
747 Console.Error.WriteLine (format, args);
751 public abstract class Provider {
753 // This code is used to "tag" all the different sources
764 public abstract void PopulateTree (Tree tree);
767 // Called at shutdown time after the tree has been populated to perform
768 // any fixups or final tasks.
770 public abstract void CloseTree (HelpSource hs, Tree tree);
773 public class RootTree : Tree {
776 public static ArrayList UncompiledHelpSources = new ArrayList();
778 public const int MonodocVersion = 1;
780 public static RootTree LoadTree ()
783 string myPath = System.Reflection.Assembly.GetExecutingAssembly ().Location;
784 string cfgFile = myPath + ".config";
785 if (!File.Exists (cfgFile)) {
787 return LoadTree (basedir);
790 XmlDocument d = new XmlDocument ();
792 basedir = d.SelectSingleNode ("config/path").Attributes ["docsPath"].Value;
794 return LoadTree (basedir);
798 // Loads the tree layout
800 public static RootTree LoadTree (string basedir)
802 XmlDocument doc = new XmlDocument ();
804 RootTree root = new RootTree ();
805 root.basedir = basedir;
810 string layout = Path.Combine (basedir, "monodoc.xml");
812 XmlNodeList nodes = doc.SelectNodes ("/node/node");
814 root.name_to_node ["root"] = root;
815 root.name_to_node ["libraries"] = root;
816 root.Populate (root, nodes);
818 Node third_party = root.LookupEntryPoint ("various");
819 if (third_party == null) {
820 Console.Error.WriteLine ("No 'various' doc node! Check monodoc.xml!");
827 string sources_dir = Path.Combine (basedir, "sources");
829 string [] files = Directory.GetFiles (sources_dir);
830 foreach (string file in files){
831 if (!file.EndsWith (".source"))
834 doc = new XmlDocument ();
838 Console.Error.WriteLine ("Error: Could not load source file {0}", file);
842 XmlNodeList extra_nodes = doc.SelectNodes ("/monodoc/node");
843 if (extra_nodes.Count > 0)
844 root.Populate (third_party, extra_nodes);
846 XmlNodeList sources = doc.SelectNodes ("/monodoc/source");
847 if (sources == null){
848 Console.Error.WriteLine ("Error: No <source> section found in the {0} file", file);
851 foreach (XmlNode source in sources){
852 XmlAttribute a = source.Attributes ["provider"];
854 Console.Error.WriteLine ("Error: no provider in <source>");
857 string provider = a.InnerText;
858 a = source.Attributes ["basefile"];
860 Console.Error.WriteLine ("Error: no basefile in <source>");
863 string basefile = a.InnerText;
864 a = source.Attributes ["path"];
866 Console.Error.WriteLine ("Error: no path in <source>");
869 string path = a.InnerText;
871 string basefilepath = Path.Combine (sources_dir, basefile);
872 HelpSource hs = GetHelpSource (provider, basefilepath);
876 root.help_sources.Add (hs);
877 root.name_to_hs [path] = hs;
879 Node parent = root.LookupEntryPoint (path);
881 Console.Error.WriteLine ("node `{0}' is not defined on the documentation map", path);
882 parent = third_party;
885 foreach (Node n in hs.Tree.Nodes){
892 foreach (string path in UncompiledHelpSources) {
893 EcmaUncompiledHelpSource hs = new EcmaUncompiledHelpSource(path);
894 root.help_sources.Add (hs);
895 string epath = "extra-help-source-" + hs.Name;
896 Node hsn = root.CreateNode (hs.Name, "root:/" + epath);
897 root.name_to_hs [epath] = hs;
899 foreach (Node n in hs.Tree.Nodes){
912 // Delete nodes which does not have documentaiton (source)
913 static bool PurgeNode(Node node)
917 if (!node.Documented)
919 ArrayList del_child = new ArrayList();
920 //Delete node unless any child has documentation
921 bool purged_child = false;
922 foreach (Node child in node.Nodes)
924 purged_child = PurgeNode(child);
927 del_child.Add(child);
931 // delete the node if all its children are to be deleted
932 purge = (node.Nodes.Count == del_child.Count);
935 foreach (Node child in del_child)
945 static HelpSource GetHelpSource (string provider, string basefilepath)
950 return new EcmaHelpSource (basefilepath, false);
951 case "ecma-uncompiled":
952 return new EcmaUncompiledHelpSource (basefilepath);
954 return new MonoHBHelpSource(basefilepath, false);
956 return new XhtmlHelpSource (basefilepath, false);
958 return new ManHelpSource (basefilepath, false);
960 return new SimpleHelpSource (basefilepath, false);
962 return new ErrorHelpSource (basefilepath, false);
964 return new EcmaSpecHelpSource (basefilepath, false);
966 return new AddinsHelpSource (basefilepath, false);
968 Console.Error.WriteLine ("Error: Unknown provider specified: {0}", provider);
973 catch (FileNotFoundException) {
974 Console.Error.WriteLine ("Error: did not find one of the files in sources/"+basefilepath);
981 // Maintains the name to node mapping
983 Hashtable name_to_node = new Hashtable ();
984 Hashtable name_to_hs = new Hashtable ();
986 void Populate (Node parent, XmlNodeList xml_node_list)
988 foreach (XmlNode xml_node in xml_node_list){
989 XmlAttribute e = xml_node.Attributes ["parent"];
990 if (e != null && name_to_node.ContainsKey (e.InnerText)) {
991 Node p = (Node) name_to_node [e.InnerText];
992 xml_node.Attributes.Remove (e);
993 Populate (p, xml_node.SelectNodes ("."));
996 e = xml_node.Attributes ["label"];
998 Console.Error.WriteLine ("`label' attribute missing in <node>");
1001 string label = e.InnerText;
1002 e = xml_node.Attributes ["name"];
1004 Console.Error.WriteLine ("`name' attribute missing in <node>");
1007 string name = e.InnerText;
1009 Node n = parent.LookupNode (label, "root:/" + name);
1011 name_to_node [name] = n;
1012 XmlNodeList children = xml_node.SelectNodes ("./node");
1013 if (children != null)
1014 Populate (n, children);
1018 public Node LookupEntryPoint (string name)
1020 return (Node) name_to_node [name];
1023 ArrayList help_sources;
1024 DateTime lastHelpSourceTime;
1026 RootTree () : base (null, "Mono Documentation", "root:")
1028 nodes = new ArrayList ();
1029 help_sources = new ArrayList ();
1030 lastHelpSourceTime = DateTime.MinValue;
1033 public DateTime LastHelpSourceTime {
1035 return lastHelpSourceTime;
1039 public static bool GetNamespaceAndType (string url, out string ns, out string type)
1043 for (int i = 0; i < url.Length; ++i) {
1062 Console.Error.WriteLine ("Did not find dot in: " + url);
1067 ns = url.Substring (0, nsidx);
1068 type = url.Substring (nsidx + 1);
1070 //Console.Error.WriteLine ("GetNameSpaceAndType (ns={0}, type={1}", ns, type);
1074 public XmlDocument GetHelpXml (string url)
1076 string rest = url.Substring (2);
1079 if (!GetNamespaceAndType (rest, out ns, out type))
1082 foreach (HelpSource hs in help_sources) {
1083 EcmaHelpSource ehs = hs as EcmaHelpSource;
1086 string id = ehs.GetIdFromUrl ("T:", ns, type);
1089 XmlDocument doc = hs.GetHelpXmlWithChanges (id);
1096 public string TypeLookup (string url, out Node match_node)
1098 string rest = Regex.Replace (url, @"^T:\s*", "");
1101 if (!GetNamespaceAndType (rest, out ns, out type)){
1106 foreach (HelpSource hs in help_sources){
1107 string s = hs.RenderTypeLookup ("T:", ns, type, null, out match_node);
1110 lastHelpSourceTime = hs.ZipFileWriteTime;
1118 public string MemberLookup (string prefix, string url, out Node match_node)
1120 string rest = Regex.Replace (url, @"^.:\s*", "");
1122 // Dots in the arg list (for methods) confuse this.
1123 // Chop off the arg list for now and put it back later.
1124 string arglist = "";
1125 int argliststart = rest.IndexOf("(");
1126 if (argliststart >= 0) {
1127 arglist = rest.Substring(argliststart);
1128 rest = rest.Substring(0, argliststart);
1131 string ns_type, member;
1133 if (prefix != "C:") {
1134 int member_idx = rest.LastIndexOf (".");
1136 // The dot in .ctor (if it's a M: link) would confuse this.
1137 if (rest.EndsWith("..ctor")) member_idx--;
1139 ns_type = rest.Substring (0, member_idx);
1140 member = rest.Substring (member_idx + 1);
1142 // C: links don't have the .ctor member part as it would in a M: link
1143 // Even though externally C: links are different from M: links,
1144 // C: links get transformed into M:-style links (with .ctor) here.
1150 //Console.WriteLine ("NS_TYPE: {0} MEMBER: {1}", ns_type, member);
1153 if (!GetNamespaceAndType (ns_type, out ns, out type)){
1158 foreach (HelpSource hs in help_sources){
1159 string s = hs.RenderTypeLookup (prefix, ns, type, member + arglist, out match_node);
1162 lastHelpSourceTime = hs.ZipFileWriteTime;
1170 public Stream GetImage (string url)
1172 if (url.StartsWith ("source-id:")){
1173 string rest = url.Substring (10);
1174 int p = rest.IndexOf (":");
1175 string str_idx = rest.Substring (0, p);
1179 idx = Int32.Parse (str_idx);
1181 Console.Error.WriteLine ("Failed to parse source-id url: {0} `{1}'", url, str_idx);
1185 HelpSource hs = GetHelpSourceFromId (idx);
1186 lastHelpSourceTime = hs.ZipFileWriteTime;
1187 return hs.GetImage (rest.Substring (p + 1));
1189 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly (typeof(RootTree));
1190 return assembly.GetManifestResourceStream (url);
1192 lastHelpSourceTime = DateTime.MinValue;
1196 public HelpSource GetHelpSourceFromId (int id)
1198 return (HelpSource) help_sources [id];
1203 /// Allows every HelpSource to try to provide the content for this
1206 public string RenderUrl (string url, out Node match_node)
1208 lastHelpSourceTime = DateTime.MinValue;
1209 if (url == "root:") {
1212 // look whether there are contribs
1213 GlobalChangeset chgs = EditingUtils.changes;
1214 StringBuilder con = new StringBuilder ();
1216 //add links to the contrib
1217 int oldContrib = 0, contribs = 0;
1218 con.Append ("<ul>");
1219 foreach (DocSetChangeset dscs in chgs.DocSetChangesets)
1220 foreach (FileChangeset fcs in dscs.FileChangesets)
1221 foreach (Change c in fcs.Changes) {
1222 if (c.NodeUrl == null) {
1223 if (c.Serial == SettingsHandler.Settings.SerialNumber)
1225 } else if (c.Serial == SettingsHandler.Settings.SerialNumber) {
1227 con.Append (String.Format ("<li><a href=\"{0}\">{0}</a></li>", c.NodeUrl));
1231 string contrib = (oldContrib + contribs) == 1?"There is {0} contribution":"There are {0} contributions";
1232 con.Insert (0, String.Format (contrib, oldContrib + contribs) + " pending upload <i>(Contributing--> Upload)</i>", 1);
1233 con.Append ("</ul>");
1234 if (oldContrib == 1)
1235 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>");
1236 else if (oldContrib > 1)
1237 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>");
1239 //start the rendering
1240 if (!HelpSource.use_css) {
1241 StringBuilder sb = new StringBuilder ("<table bgcolor=\"#b0c4de\" width=\"100%\" cellpadding=\"5\"><tr><td><h3>Mono Documentation Library</h3></td></tr></table>");
1243 foreach (Node n in Nodes)
1244 sb.AppendFormat ("<a href='{0}'>{1}</a><br/>", n.Element, n.Caption);
1247 sb.Append ("<br><table bgcolor=\"#fff3f3\" width=\"100%\" cellpadding=\"5\"><tr><td>");
1248 sb.Append ("<h5>Contributions</h5><br>");
1249 if ((oldContrib + contribs) == 0) {
1250 sb.Append ("<p><b>You have not made any contributions yet.</b></p>");
1251 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>");
1252 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>");
1254 sb.Append (con.ToString ());
1256 sb.Append ("</td></tr></table>");
1257 return sb.ToString ();
1259 if (home_cache == null) {
1260 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly (typeof (HelpSource));
1261 Stream hp_stream = assembly.GetManifestResourceStream ("home.html");
1262 home_cache = (new StreamReader (hp_stream)).ReadToEnd ();
1264 StringBuilder sb = new StringBuilder (home_cache);
1266 sb.Replace ("@@FONT_FAMILY@@", SettingsHandler.Settings.preferred_font_family);
1267 sb.Replace ("@@FONT_SIZE@@", SettingsHandler.Settings.preferred_font_size.ToString());
1269 if ((oldContrib + contribs) == 0) {
1270 sb.Replace ("@@CONTRIB_DISP@@", "display: none;");
1272 sb.Replace ("@@NO_CONTRIB_DISP@@", "display: none;");
1273 sb.Replace ("@@CONTRIBS@@", con.ToString ());
1276 // load the url of nodes
1278 StringBuilder urls = new StringBuilder ();
1279 foreach (Node n in Nodes) {
1280 add_str = String.Format ("<li><a href=\"{0}\">{1}</a></li>", n.Element, n.Caption);
1281 urls.Append (add_str);
1283 sb.Replace ("@@API_DOCS@@", urls.ToString ());
1285 return sb.ToString ();
1289 if (url.StartsWith ("root:")) {
1290 match_node = ((Node)name_to_node [url.Substring (6)]);
1291 HelpSource hs = ((HelpSource)name_to_hs [url.Substring (6)]);
1294 return GenerateNodeIndex(match_node);
1298 lastHelpSourceTime = hs.ZipFileWriteTime;
1299 return hs.GetText ("root:", out dummy);
1303 if (url.StartsWith ("source-id:")){
1304 string rest = url.Substring (10);
1305 int p = rest.IndexOf (":");
1306 string str_idx = rest.Substring (0, p);
1310 idx = Int32.Parse (str_idx);
1312 Console.Error.WriteLine ("Failed to parse source-id url: {0} `{1}'", url, str_idx);
1316 HelpSource hs = (HelpSource) help_sources [idx];
1317 // Console.WriteLine ("Attempting to get docs from: " + rest.Substring (p + 1));
1318 lastHelpSourceTime = hs.ZipFileWriteTime;
1319 return hs.GetText (rest.Substring (p + 1), out match_node);
1322 if (url.Length < 2){
1327 string prefix = url.Substring (0, 2);
1329 switch (prefix.ToUpper ()){
1331 foreach (HelpSource hs in help_sources){
1332 string s = hs.RenderNamespaceLookup (url, out match_node);
1334 lastHelpSourceTime = hs.ZipFileWriteTime;
1342 return TypeLookup (url, out match_node);
1350 return MemberLookup (prefix, url, out match_node);
1353 foreach (HelpSource hs in help_sources){
1354 string s = hs.GetText (url, out match_node);
1357 lastHelpSourceTime = hs.ZipFileWriteTime;
1366 public string GenerateNodeIndex (Node node)
1368 StringBuilder buf = new StringBuilder();
1369 buf.AppendFormat("<H3>{0}</H3>", node.Caption);
1371 foreach (Node child in node.Nodes)
1373 buf.AppendFormat("<li><a href=\"{0}\">{1}</a>", child.URL, child.Caption);
1375 buf.Append("</ul>");
1376 return buf.ToString();
1379 public IndexReader GetIndex ()
1381 //try to load from basedir
1382 string index_file = Path.Combine (basedir, "monodoc.index");
1383 if (File.Exists (index_file))
1384 return IndexReader.Load (index_file);
1385 //then, try to load from config dir
1386 index_file = Path.Combine (SettingsHandler.Path, "monodoc.index");
1387 return IndexReader.Load (index_file);
1391 public static void MakeIndex ()
1393 RootTree root = LoadTree ();
1397 IndexMaker index_maker = new IndexMaker ();
1399 foreach (HelpSource hs in root.help_sources){
1400 hs.PopulateIndex (index_maker);
1403 // if the user has no write permissions use config dir
1404 string path = Path.Combine (root.basedir, "monodoc.index");
1406 index_maker.Save (path);
1407 } catch (System.UnauthorizedAccessException) {
1408 path = Path.Combine (SettingsHandler.Path, "monodoc.index");
1410 index_maker.Save (path);
1411 } catch (System.UnauthorizedAccessException) {
1412 Console.WriteLine ("Unable to write index file in {0}", Path.Combine (SettingsHandler.Path, "monodoc.index"));
1418 // No octal in C#, how lame is that
1419 chmod (path, 0x1a4);
1421 Console.WriteLine ("Documentation index updated");
1424 static bool IsUnix {
1426 int p = (int) Environment.OSVersion.Platform;
1427 return ((p == 4) || (p == 128));
1432 public SearchableIndex GetSearchIndex ()
1434 //try to load from basedir
1435 string index_file = Path.Combine (basedir, "search_index");
1436 if (Directory.Exists (index_file))
1437 return SearchableIndex.Load (index_file);
1438 //then, try to load from config dir
1439 index_file = Path.Combine (SettingsHandler.Path, "search_index");
1440 return SearchableIndex.Load (index_file);
1443 public static void MakeSearchIndex ()
1445 // Loads the RootTree
1446 Console.WriteLine ("Loading the monodoc tree...");
1447 RootTree root = LoadTree ();
1451 string dir = Path.Combine (root.basedir, "search_index");
1453 //try to create the dir to store the index
1455 if (!Directory.Exists (dir))
1456 Directory.CreateDirectory (dir);
1458 writer = new IndexWriter(Lucene.Net.Store.FSDirectory.GetDirectory(dir, true), new StandardAnalyzer(), true);
1459 } catch (UnauthorizedAccessException) {
1460 //try in the .config directory
1462 dir = Path.Combine (SettingsHandler.Path, "search_index");
1463 if (!Directory.Exists (dir))
1464 Directory.CreateDirectory (dir);
1466 writer = new IndexWriter(Lucene.Net.Store.FSDirectory.GetDirectory(dir, true), new StandardAnalyzer(), true);
1467 } catch (UnauthorizedAccessException) {
1468 Console.WriteLine ("You don't have permissions to write on " + dir);
1473 //Collect all the documents
1474 Console.WriteLine ("Collecting and adding documents...");
1475 foreach (HelpSource hs in root.HelpSources)
1476 hs.PopulateSearchableIndex (writer);
1478 //Optimize and close
1479 Console.WriteLine ("Closing...");
1485 public ICollection HelpSources { get { return new ArrayList(help_sources); } }
1487 [System.Runtime.InteropServices.DllImport ("libc")]
1488 static extern int chmod (string filename, int mode);