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;
129 static ArrayList empty = ArrayList.ReadOnly(new ArrayList(0));
132 /// Creates a node, called by the Tree.
134 public Node (string caption, string element)
136 this.tree = (Tree) this;
137 this.caption = caption;
138 this.element = element;
142 public Node (Node parent, string caption, string element)
144 this.parent = parent;
145 this.tree = parent.tree;
146 this.caption = caption;
147 this.element = element;
151 /// Creates a node from an on-disk representation
153 Node (Node parent, int address)
155 this.parent = parent;
157 this.tree = parent.tree;
162 public void AddNode (Node n)
169 public void DelNode (Node n)
174 public ArrayList Nodes {
178 return nodes != null ? nodes : empty;
182 public string Element {
194 public string Caption {
208 public void LoadNode ()
211 position = -position;
213 tree.InputStream.Position = position;
214 BinaryReader reader = tree.InputReader;
215 int count = DecodeInt (reader);
216 element = reader.ReadString ();
217 caption = reader.ReadString ();
221 nodes = new ArrayList (count);
222 for (int i = 0; i < count; i++){
223 int child_address = DecodeInt (reader);
225 Node t = new Node (this, -child_address);
231 /// Creates a new node, in the locator entry point, and with
232 /// a user visible caption of @caption
234 public Node CreateNode (string c_caption, string c_element)
237 nodes = new ArrayList ();
239 Node t = new Node (this, c_caption, c_element);
245 /// Looks up or creates a new node, in the locator entry point, and with
246 /// a user visible caption of @caption. This is different from
247 /// CreateNode in that it will look up an existing node for the given @locator.
249 public Node LookupNode (string c_caption, string c_element)
252 return CreateNode (c_caption, c_element);
254 foreach (Node n in nodes){
255 if (n.element == c_element)
258 return CreateNode (c_caption, c_element);
261 public void EnsureNodes ()
264 nodes = new ArrayList ();
269 return nodes == null;
273 void EncodeInt (BinaryWriter writer, int value)
276 int high = (value >> 7) & 0x01ffffff;
277 byte b = (byte)(value & 0x7f);
280 b = (byte)(b | 0x80);
288 int DecodeInt (BinaryReader reader)
295 b = reader.ReadByte();
297 ret = ret | ((b & 0x7f) << shift);
299 } while ((b & 0x80) == 0x80);
304 internal void Dump (FileStream output, BinaryWriter writer)
307 foreach (Node child in nodes){
308 child.Dump (output, writer);
311 position = (int) output.Position;
312 EncodeInt (writer, nodes == null ? 0 : (int) nodes.Count);
313 writer.Write (element);
314 writer.Write (caption);
317 foreach (Node child in nodes){
318 EncodeInt (writer, child.position);
325 static void Indent ()
327 for (int i = 0; i < indent; i++)
331 public static void PrintTree (Node node)
334 Console.WriteLine ("{0},{1}", node.Element, node.Caption);
335 if (node.Nodes == null)
339 foreach (Node n in node.nodes)
355 if (element.IndexOf (":") >= 0)
359 string url = parent.URL;
361 if (url.EndsWith ("/"))
362 return url + element;
364 return parent.URL + "/" + element;
370 int IComparable.CompareTo (object obj)
372 Node other = obj as Node;
378 if (other.position < 0)
381 Regex digits = new Regex (@"([\d]+)|([^\d]+)");
382 MatchEvaluator eval = delegate (Match m) {
383 return (m.Value.Length > 0 && char.IsDigit (m.Value [0]))
384 ? m.Value.PadLeft (System.Math.Max (caption.Length, other.caption.Length))
387 return digits.Replace (caption, eval).CompareTo (digits.Replace (other.caption, eval));
392 // The HelpSource class keeps track of the archived data, and its
395 public class HelpSource {
397 public static bool use_css = false;
398 public static string css_code;
399 public static string CssCode {
401 if (css_code != null)
404 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetCallingAssembly ();
405 Stream str_css = assembly.GetManifestResourceStream ("base.css");
406 StringBuilder sb = new StringBuilder ((new StreamReader (str_css)).ReadToEnd());
407 sb.Replace ("@@FONT_FAMILY@@", SettingsHandler.Settings.preferred_font_family);
408 sb.Replace ("@@FONT_SIZE@@", SettingsHandler.Settings.preferred_font_size.ToString());
409 css_code = sb.ToString ();
412 set { css_code = value; }
415 public virtual string InlineCss {
416 get { return CssCode; }
419 public virtual string InlineJavaScript {
423 public static bool FullHtml = true;
426 // The unique ID for this HelpSource.
429 DateTime zipFileWriteTime;
431 TraceLevel trace_level = TraceLevel.Warning;
433 public HelpSource (string base_filename, bool create)
435 this.name = Path.GetFileName (base_filename);
436 tree_filename = base_filename + ".tree";
437 zip_filename = base_filename + ".zip";
442 Tree = new Tree (this, tree_filename);
446 FileInfo fi = new FileInfo (zip_filename);
447 zipFileWriteTime = fi.LastWriteTime;
449 zipFileWriteTime = DateTime.Now;
453 public HelpSource() {
454 Tree = new Tree (this, "Blah", "Blah");
458 public DateTime ZipFileWriteTime {
460 return zipFileWriteTime;
464 public int SourceID {
476 public TraceLevel TraceLevel {
477 get { return trace_level; }
478 set { trace_level = value; }
484 /// Returns a stream from the packaged help source archive
486 public Stream GetHelpStream (string id)
488 if (zip_file == null)
489 zip_file = new ZipFile (zip_filename);
491 ZipEntry entry = zip_file.GetEntry (id);
493 return zip_file.GetInputStream (entry);
497 public string GetRealPath (string file)
499 if (zip_file == null)
500 zip_file = new ZipFile (zip_filename);
502 ZipEntry entry = zip_file.GetEntry (file);
503 if (entry != null && entry.ExtraData != null)
504 return ConvertToString (entry.ExtraData);
508 public XmlReader GetHelpXml (string id)
510 if (zip_file == null)
511 zip_file = new ZipFile (zip_filename);
513 ZipEntry entry = zip_file.GetEntry (id);
515 Stream s = zip_file.GetInputStream (entry);
516 string url = "monodoc:///" + SourceID + "@" + System.Web.HttpUtility.UrlEncode (id) + "@";
517 return new XmlTextReader (url, s);
522 public XmlDocument GetHelpXmlWithChanges (string id)
524 if (zip_file == null)
525 zip_file = new ZipFile (zip_filename);
527 ZipEntry entry = zip_file.GetEntry (id);
529 Stream s = zip_file.GetInputStream (entry);
530 string url = "monodoc:///" + SourceID + "@" + System.Web.HttpUtility.UrlEncode (id) + "@";
531 XmlReader r = new XmlTextReader (url, s);
532 XmlDocument ret = new XmlDocument ();
535 if (entry.ExtraData != null)
536 EditingUtils.AccountForChanges (ret, Name, ConvertToString (entry.ExtraData));
544 /// Get a nice, unique expression for any XPath node that you get.
545 /// This function is used by editing to get the expression to put
546 /// on to the file. The idea is to create an expression that is resistant
547 /// to changes in the structure of the XML.
549 public virtual string GetNodeXPath (XPathNavigator n)
551 return EditingUtils.GetXPath (n.Clone ());
554 public string GetEditUri (XPathNavigator n)
556 return EditingUtils.FormatEditUri (n.BaseURI, GetNodeXPath (n));
559 static string ConvertToString (byte[] data)
561 return Encoding.UTF8.GetString(data);
564 static byte[] ConvertToArray (string str)
566 return Encoding.UTF8.GetBytes(str);
570 /// The tree that is being populated
573 public RootTree RootTree;
575 // Base filename used by this HelpSource.
576 string tree_filename, zip_filename;
579 const int buffer_size = 65536;
580 ZipOutputStream zip_output;
583 HelpSource (string base_filename)
587 void SetupForOutput ()
589 Tree = new Tree (this, "", "");
591 FileStream stream = File.Create (zip_filename);
593 zip_output = new ZipOutputStream (stream);
594 zip_output.SetLevel (9);
596 buffer = new byte [buffer_size];
600 /// Saves the tree and the archive
604 Tree.Save (tree_filename);
605 zip_output.Finish ();
613 return String.Format ("{0}", code++);
617 /// Providers call this to store a file they will need, and the return value
618 /// is the name that was assigned to it
620 public string PackFile (string file)
622 string entry_name = GetNewCode ();
623 return PackFile (file, entry_name);
626 public string PackFile (string file, string entry_name)
628 using (FileStream input = File.OpenRead (file)) {
629 PackStream (input, entry_name, file);
635 public void PackStream (Stream s, string entry_name)
637 PackStream (s, entry_name, null);
640 void PackStream (Stream s, string entry_name, string realPath)
642 ZipEntry entry = new ZipEntry (entry_name);
644 if (realPath != null)
645 entry.ExtraData = ConvertToArray (realPath);
647 zip_output.PutNextEntry (entry);
650 while ((n = s.Read (buffer, 0, buffer_size)) > 0){
651 zip_output.Write (buffer, 0, n);
655 public void PackXml (string fname, XmlDocument doc, string real_path)
657 ZipEntry entry = new ZipEntry (fname);
658 if (real_path != null)
659 entry.ExtraData = ConvertToArray(real_path);
661 zip_output.PutNextEntry (entry);
662 XmlTextWriter xmlWriter = new XmlTextWriter (zip_output, Encoding.UTF8);
663 doc.WriteContentTo (xmlWriter);
667 public virtual void RenderPreviewDocs (XmlNode newNode, XmlWriter writer)
669 throw new NotImplementedException ();
672 public virtual string GetText (string url, out Node n)
678 public virtual Stream GetImage (string url)
684 // Default method implementation does not satisfy the request
686 public virtual string RenderTypeLookup (string prefix, string ns, string type, string member, out Node n)
692 public virtual string RenderNamespaceLookup (string nsurl, out Node n)
699 // Populates the index.
701 public virtual void PopulateIndex (IndexMaker index_maker)
706 // Build an html document
708 public static string BuildHtml (string css, string html_code)
710 return BuildHtml (css, null, html_code);
713 internal static string BuildHtml (string css, string js, string html_code) {
717 StringWriter output = new StringWriter ();
718 output.Write ("<html><head>");
719 output.Write ("<style type=\"text/css\">");
720 output.Write (CssCode);
722 output.Write ("</style>");
724 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetCallingAssembly ();
725 Stream str_js = assembly.GetManifestResourceStream ("helper.js");
726 StringBuilder sb = new StringBuilder ((new StreamReader (str_js)).ReadToEnd());
727 output.Write ("<script type=\"text/JavaScript\">\n");
728 output.Write (sb.ToString ());
729 output.Write ("</script>\n");
732 output.Write ("<script type=\"text/JavaScript\">\n");
734 output.Write ("\n</script>");
737 output.Write ("</head><body>");
738 output.Write (html_code);
739 output.Write ("</body></html>");
740 return output.ToString ();
744 // Create different Documents for adding to Lucene search index
745 // The default action is do nothing. Subclasses should add the docs
747 public virtual void PopulateSearchableIndex (IndexWriter writer) {
751 public void Message (TraceLevel level, string format, params object[] args)
753 if ((int) level <= (int) trace_level)
754 Console.WriteLine (format, args);
757 public void Error (string format, params object[] args)
759 Console.Error.WriteLine (format, args);
763 public abstract class Provider {
765 // This code is used to "tag" all the different sources
776 public abstract void PopulateTree (Tree tree);
779 // Called at shutdown time after the tree has been populated to perform
780 // any fixups or final tasks.
782 public abstract void CloseTree (HelpSource hs, Tree tree);
785 public class RootTree : Tree {
788 public static ArrayList UncompiledHelpSources = new ArrayList();
790 public const int MonodocVersion = 1;
792 public static RootTree LoadTree ()
795 string myPath = System.Reflection.Assembly.GetExecutingAssembly ().Location;
796 string cfgFile = myPath + ".config";
797 if (!File.Exists (cfgFile)) {
799 return LoadTree (basedir);
802 XmlDocument d = new XmlDocument ();
804 basedir = d.SelectSingleNode ("config/path").Attributes ["docsPath"].Value;
806 return LoadTree (basedir);
810 // Loads the tree layout
812 public static RootTree LoadTree (string basedir)
814 XmlDocument doc = new XmlDocument ();
816 RootTree root = new RootTree ();
817 root.basedir = basedir;
822 string layout = Path.Combine (basedir, "monodoc.xml");
824 XmlNodeList nodes = doc.SelectNodes ("/node/node");
826 root.name_to_node ["root"] = root;
827 root.name_to_node ["libraries"] = root;
828 root.Populate (root, nodes);
830 Node third_party = root.LookupEntryPoint ("various");
831 if (third_party == null) {
832 Console.Error.WriteLine ("No 'various' doc node! Check monodoc.xml!");
839 string sources_dir = Path.Combine (basedir, "sources");
841 string [] files = Directory.GetFiles (sources_dir);
842 foreach (string file in files){
843 if (!file.EndsWith (".source"))
846 doc = new XmlDocument ();
850 Console.Error.WriteLine ("Error: Could not load source file {0}", file);
854 XmlNodeList extra_nodes = doc.SelectNodes ("/monodoc/node");
855 if (extra_nodes.Count > 0)
856 root.Populate (third_party, extra_nodes);
858 XmlNodeList sources = doc.SelectNodes ("/monodoc/source");
859 if (sources == null){
860 Console.Error.WriteLine ("Error: No <source> section found in the {0} file", file);
863 foreach (XmlNode source in sources){
864 XmlAttribute a = source.Attributes ["provider"];
866 Console.Error.WriteLine ("Error: no provider in <source>");
869 string provider = a.InnerText;
870 a = source.Attributes ["basefile"];
872 Console.Error.WriteLine ("Error: no basefile in <source>");
875 string basefile = a.InnerText;
876 a = source.Attributes ["path"];
878 Console.Error.WriteLine ("Error: no path in <source>");
881 string path = a.InnerText;
883 string basefilepath = Path.Combine (sources_dir, basefile);
884 HelpSource hs = GetHelpSource (provider, basefilepath);
888 root.help_sources.Add (hs);
889 root.name_to_hs [path] = hs;
891 Node parent = root.LookupEntryPoint (path);
893 Console.Error.WriteLine ("node `{0}' is not defined on the documentation map", path);
894 parent = third_party;
897 foreach (Node n in hs.Tree.Nodes){
904 foreach (string path in UncompiledHelpSources) {
905 EcmaUncompiledHelpSource hs = new EcmaUncompiledHelpSource(path);
906 root.help_sources.Add (hs);
907 string epath = "extra-help-source-" + hs.Name;
908 Node hsn = root.CreateNode (hs.Name, "root:/" + epath);
909 root.name_to_hs [epath] = hs;
911 foreach (Node n in hs.Tree.Nodes){
924 // Delete nodes which does not have documentaiton (source)
925 static bool PurgeNode(Node node)
929 if (!node.Documented)
931 ArrayList del_child = new ArrayList();
932 //Delete node unless any child has documentation
933 bool purged_child = false;
934 foreach (Node child in node.Nodes)
936 purged_child = PurgeNode(child);
939 del_child.Add(child);
943 // delete the node if all its children are to be deleted
944 purge = (node.Nodes.Count == del_child.Count);
947 foreach (Node child in del_child)
957 static HelpSource GetHelpSource (string provider, string basefilepath)
962 return new EcmaHelpSource (basefilepath, false);
963 case "ecma-uncompiled":
964 return new EcmaUncompiledHelpSource (basefilepath);
966 return new MonoHBHelpSource(basefilepath, false);
968 return new XhtmlHelpSource (basefilepath, false);
970 return new ManHelpSource (basefilepath, false);
972 return new SimpleHelpSource (basefilepath, false);
974 return new ErrorHelpSource (basefilepath, false);
976 return new EcmaSpecHelpSource (basefilepath, false);
978 return new AddinsHelpSource (basefilepath, false);
980 Console.Error.WriteLine ("Error: Unknown provider specified: {0}", provider);
985 catch (FileNotFoundException) {
986 Console.Error.WriteLine ("Error: did not find one of the files in sources/"+basefilepath);
993 // Maintains the name to node mapping
995 Hashtable name_to_node = new Hashtable ();
996 Hashtable name_to_hs = new Hashtable ();
998 void Populate (Node parent, XmlNodeList xml_node_list)
1000 foreach (XmlNode xml_node in xml_node_list){
1001 XmlAttribute e = xml_node.Attributes ["parent"];
1002 if (e != null && name_to_node.ContainsKey (e.InnerText)) {
1003 Node p = (Node) name_to_node [e.InnerText];
1004 xml_node.Attributes.Remove (e);
1005 Populate (p, xml_node.SelectNodes ("."));
1008 e = xml_node.Attributes ["label"];
1010 Console.Error.WriteLine ("`label' attribute missing in <node>");
1013 string label = e.InnerText;
1014 e = xml_node.Attributes ["name"];
1016 Console.Error.WriteLine ("`name' attribute missing in <node>");
1019 string name = e.InnerText;
1021 Node n = parent.LookupNode (label, "root:/" + name);
1023 name_to_node [name] = n;
1024 XmlNodeList children = xml_node.SelectNodes ("./node");
1025 if (children != null)
1026 Populate (n, children);
1030 public Node LookupEntryPoint (string name)
1032 return (Node) name_to_node [name];
1035 ArrayList help_sources;
1036 DateTime lastHelpSourceTime;
1038 RootTree () : base (null, "Mono Documentation", "root:")
1040 nodes = new ArrayList ();
1041 help_sources = new ArrayList ();
1042 lastHelpSourceTime = DateTime.MinValue;
1045 public DateTime LastHelpSourceTime {
1047 return lastHelpSourceTime;
1051 public static bool GetNamespaceAndType (string url, out string ns, out string type)
1055 for (int i = 0; i < url.Length; ++i) {
1074 Console.Error.WriteLine ("Did not find dot in: " + url);
1079 ns = url.Substring (0, nsidx);
1080 type = url.Substring (nsidx + 1);
1082 //Console.Error.WriteLine ("GetNameSpaceAndType (ns={0}, type={1}", ns, type);
1086 public XmlDocument GetHelpXml (string url)
1088 string rest = url.Substring (2);
1091 if (!GetNamespaceAndType (rest, out ns, out type))
1094 foreach (HelpSource hs in help_sources) {
1095 EcmaHelpSource ehs = hs as EcmaHelpSource;
1098 string id = ehs.GetIdFromUrl ("T:", ns, type);
1101 XmlDocument doc = hs.GetHelpXmlWithChanges (id);
1108 public string TypeLookup (string url, out Node match_node)
1110 string rest = Regex.Replace (url, @"^T:\s*", "");
1113 if (!GetNamespaceAndType (rest, out ns, out type)){
1118 foreach (HelpSource hs in help_sources){
1119 string s = hs.RenderTypeLookup ("T:", ns, type, null, out match_node);
1122 lastHelpSourceTime = hs.ZipFileWriteTime;
1130 public string MemberLookup (string prefix, string url, out Node match_node)
1132 string rest = Regex.Replace (url, @"^.:\s*", "");
1134 // Dots in the arg list (for methods) confuse this.
1135 // Chop off the arg list for now and put it back later.
1136 string arglist = "";
1137 int argliststart = rest.IndexOf("(");
1138 if (argliststart >= 0) {
1139 arglist = rest.Substring(argliststart);
1140 rest = rest.Substring(0, argliststart);
1143 string ns_type, member;
1145 if (prefix != "C:") {
1146 int member_idx = rest.LastIndexOf (".");
1148 // The dot in .ctor (if it's a M: link) would confuse this.
1149 if (rest.EndsWith("..ctor")) member_idx--;
1151 ns_type = rest.Substring (0, member_idx);
1152 member = rest.Substring (member_idx + 1);
1154 // C: links don't have the .ctor member part as it would in a M: link
1155 // Even though externally C: links are different from M: links,
1156 // C: links get transformed into M:-style links (with .ctor) here.
1162 //Console.WriteLine ("NS_TYPE: {0} MEMBER: {1}", ns_type, member);
1165 if (!GetNamespaceAndType (ns_type, out ns, out type)){
1170 foreach (HelpSource hs in help_sources){
1171 string s = hs.RenderTypeLookup (prefix, ns, type, member + arglist, out match_node);
1174 lastHelpSourceTime = hs.ZipFileWriteTime;
1182 public Stream GetImage (string url)
1184 if (url.StartsWith ("source-id:")){
1185 string rest = url.Substring (10);
1186 int p = rest.IndexOf (":");
1187 string str_idx = rest.Substring (0, p);
1191 idx = Int32.Parse (str_idx);
1193 Console.Error.WriteLine ("Failed to parse source-id url: {0} `{1}'", url, str_idx);
1197 HelpSource hs = GetHelpSourceFromId (idx);
1198 lastHelpSourceTime = hs.ZipFileWriteTime;
1199 return hs.GetImage (rest.Substring (p + 1));
1201 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly (typeof(RootTree));
1202 return assembly.GetManifestResourceStream (url);
1204 lastHelpSourceTime = DateTime.MinValue;
1208 public HelpSource GetHelpSourceFromId (int id)
1210 return (HelpSource) help_sources [id];
1215 /// Allows every HelpSource to try to provide the content for this
1218 public string RenderUrl (string url, out Node match_node)
1220 lastHelpSourceTime = DateTime.MinValue;
1221 if (url == "root:") {
1224 // look whether there are contribs
1225 GlobalChangeset chgs = EditingUtils.changes;
1226 StringBuilder con = new StringBuilder ();
1228 //add links to the contrib
1229 int oldContrib = 0, contribs = 0;
1230 con.Append ("<ul>");
1231 foreach (DocSetChangeset dscs in chgs.DocSetChangesets)
1232 foreach (FileChangeset fcs in dscs.FileChangesets)
1233 foreach (Change c in fcs.Changes) {
1234 if (c.NodeUrl == null) {
1235 if (c.Serial == SettingsHandler.Settings.SerialNumber)
1237 } else if (c.Serial == SettingsHandler.Settings.SerialNumber) {
1239 con.Append (String.Format ("<li><a href=\"{0}\">{0}</a></li>", c.NodeUrl));
1243 string contrib = (oldContrib + contribs) == 1?"There is {0} contribution":"There are {0} contributions";
1244 con.Insert (0, String.Format (contrib, oldContrib + contribs) + " pending upload <i>(Contributing--> Upload)</i>", 1);
1245 con.Append ("</ul>");
1246 if (oldContrib == 1)
1247 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>");
1248 else if (oldContrib > 1)
1249 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>");
1251 //start the rendering
1252 if (!HelpSource.use_css) {
1253 StringBuilder sb = new StringBuilder ("<table bgcolor=\"#b0c4de\" width=\"100%\" cellpadding=\"5\"><tr><td><h3>Mono Documentation Library</h3></td></tr></table>");
1255 foreach (Node n in Nodes)
1256 sb.AppendFormat ("<a href='{0}'>{1}</a><br/>", n.Element, n.Caption);
1259 sb.Append ("<br><table bgcolor=\"#fff3f3\" width=\"100%\" cellpadding=\"5\"><tr><td>");
1260 sb.Append ("<h5>Contributions</h5><br>");
1261 if ((oldContrib + contribs) == 0) {
1262 sb.Append ("<p><b>You have not made any contributions yet.</b></p>");
1263 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>");
1264 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>");
1266 sb.Append (con.ToString ());
1268 sb.Append ("</td></tr></table>");
1269 return sb.ToString ();
1271 if (home_cache == null) {
1272 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly (typeof (HelpSource));
1273 Stream hp_stream = assembly.GetManifestResourceStream ("home.html");
1274 home_cache = (new StreamReader (hp_stream)).ReadToEnd ();
1276 StringBuilder sb = new StringBuilder (home_cache);
1278 sb.Replace ("@@FONT_FAMILY@@", SettingsHandler.Settings.preferred_font_family);
1279 sb.Replace ("@@FONT_SIZE@@", SettingsHandler.Settings.preferred_font_size.ToString());
1281 if ((oldContrib + contribs) == 0) {
1282 sb.Replace ("@@CONTRIB_DISP@@", "display: none;");
1284 sb.Replace ("@@NO_CONTRIB_DISP@@", "display: none;");
1285 sb.Replace ("@@CONTRIBS@@", con.ToString ());
1288 // load the url of nodes
1290 StringBuilder urls = new StringBuilder ();
1291 foreach (Node n in Nodes) {
1292 add_str = String.Format ("<li><a href=\"{0}\">{1}</a></li>", n.Element, n.Caption);
1293 urls.Append (add_str);
1295 sb.Replace ("@@API_DOCS@@", urls.ToString ());
1297 return sb.ToString ();
1301 if (url.StartsWith ("root:")) {
1302 match_node = ((Node)name_to_node [url.Substring (6)]);
1303 HelpSource hs = ((HelpSource)name_to_hs [url.Substring (6)]);
1306 return GenerateNodeIndex(match_node);
1310 lastHelpSourceTime = hs.ZipFileWriteTime;
1311 return hs.GetText ("root:", out dummy);
1315 if (url.StartsWith ("source-id:")){
1316 string rest = url.Substring (10);
1317 int p = rest.IndexOf (":");
1318 string str_idx = rest.Substring (0, p);
1322 idx = Int32.Parse (str_idx);
1324 Console.Error.WriteLine ("Failed to parse source-id url: {0} `{1}'", url, str_idx);
1328 HelpSource hs = (HelpSource) help_sources [idx];
1329 // Console.WriteLine ("Attempting to get docs from: " + rest.Substring (p + 1));
1330 lastHelpSourceTime = hs.ZipFileWriteTime;
1331 return hs.GetText (rest.Substring (p + 1), out match_node);
1334 if (url.Length < 2){
1339 string prefix = url.Substring (0, 2);
1341 switch (prefix.ToUpper ()){
1343 foreach (HelpSource hs in help_sources){
1344 string s = hs.RenderNamespaceLookup (url, out match_node);
1346 lastHelpSourceTime = hs.ZipFileWriteTime;
1354 return TypeLookup (url, out match_node);
1362 return MemberLookup (prefix, url, out match_node);
1365 foreach (HelpSource hs in help_sources){
1366 string s = hs.GetText (url, out match_node);
1369 lastHelpSourceTime = hs.ZipFileWriteTime;
1378 public string GenerateNodeIndex (Node node)
1380 StringBuilder buf = new StringBuilder();
1381 buf.AppendFormat("<H3>{0}</H3>", node.Caption);
1383 foreach (Node child in node.Nodes)
1385 buf.AppendFormat("<li><a href=\"{0}\">{1}</a>", child.URL, child.Caption);
1387 buf.Append("</ul>");
1388 return buf.ToString();
1391 public IndexReader GetIndex ()
1393 //try to load from basedir
1394 string index_file = Path.Combine (basedir, "monodoc.index");
1395 if (File.Exists (index_file))
1396 return IndexReader.Load (index_file);
1397 //then, try to load from config dir
1398 index_file = Path.Combine (SettingsHandler.Path, "monodoc.index");
1399 return IndexReader.Load (index_file);
1403 public static void MakeIndex ()
1405 RootTree root = LoadTree ();
1409 IndexMaker index_maker = new IndexMaker ();
1411 foreach (HelpSource hs in root.help_sources){
1412 hs.PopulateIndex (index_maker);
1415 // if the user has no write permissions use config dir
1416 string path = Path.Combine (root.basedir, "monodoc.index");
1418 index_maker.Save (path);
1419 } catch (System.UnauthorizedAccessException) {
1420 path = Path.Combine (SettingsHandler.Path, "monodoc.index");
1422 index_maker.Save (path);
1423 } catch (System.UnauthorizedAccessException) {
1424 Console.WriteLine ("Unable to write index file in {0}", Path.Combine (SettingsHandler.Path, "monodoc.index"));
1430 // No octal in C#, how lame is that
1431 chmod (path, 0x1a4);
1433 Console.WriteLine ("Documentation index updated");
1436 static bool IsUnix {
1438 int p = (int) Environment.OSVersion.Platform;
1439 return ((p == 4) || (p == 128) || (p == 6));
1444 public SearchableIndex GetSearchIndex ()
1446 //try to load from basedir
1447 string index_file = Path.Combine (basedir, "search_index");
1448 if (Directory.Exists (index_file))
1449 return SearchableIndex.Load (index_file);
1450 //then, try to load from config dir
1451 index_file = Path.Combine (SettingsHandler.Path, "search_index");
1452 return SearchableIndex.Load (index_file);
1455 public static void MakeSearchIndex ()
1457 // Loads the RootTree
1458 Console.WriteLine ("Loading the monodoc tree...");
1459 RootTree root = LoadTree ();
1463 string dir = Path.Combine (root.basedir, "search_index");
1465 //try to create the dir to store the index
1467 if (!Directory.Exists (dir))
1468 Directory.CreateDirectory (dir);
1470 writer = new IndexWriter(Lucene.Net.Store.FSDirectory.GetDirectory(dir, true), new StandardAnalyzer(), true);
1471 } catch (UnauthorizedAccessException) {
1472 //try in the .config directory
1474 dir = Path.Combine (SettingsHandler.Path, "search_index");
1475 if (!Directory.Exists (dir))
1476 Directory.CreateDirectory (dir);
1478 writer = new IndexWriter(Lucene.Net.Store.FSDirectory.GetDirectory(dir, true), new StandardAnalyzer(), true);
1479 } catch (UnauthorizedAccessException) {
1480 Console.WriteLine ("You don't have permissions to write on " + dir);
1485 //Collect all the documents
1486 Console.WriteLine ("Collecting and adding documents...");
1487 foreach (HelpSource hs in root.HelpSources)
1488 hs.PopulateSearchableIndex (writer);
1490 //Optimize and close
1491 Console.WriteLine ("Closing...");
1497 public ICollection HelpSources { get { return new ArrayList(help_sources); } }
1499 [System.Runtime.InteropServices.DllImport ("libc")]
1500 static extern int chmod (string filename, int mode);