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 return LoadTree (basedir, doc,
885 Directory.GetFiles (Path.Combine (basedir, "sources"))
886 .Where (file => file.EndsWith (".source")));
890 // Compatibility shim w/ Mono 2.6
891 public static RootTree LoadTree (string indexDir, XmlDocument docTree, IEnumerable sourceFiles)
893 return LoadTree (indexDir, docTree, sourceFiles.Cast<string>());
896 public static RootTree LoadTree (string indexDir, XmlDocument docTree, IEnumerable<string> sourceFiles)
898 if (docTree == null) {
899 docTree = new XmlDocument ();
900 using (var defTree = typeof(RootTree).Assembly.GetManifestResourceStream ("monodoc.xml"))
901 docTree.Load (defTree);
904 sourceFiles = sourceFiles ?? new string [0];
910 RootTree root = new RootTree ();
911 root.basedir = indexDir;
913 XmlNodeList nodes = docTree.SelectNodes ("/node/node");
915 root.name_to_node ["root"] = root;
916 root.name_to_node ["libraries"] = root;
917 root.Populate (root, nodes);
919 Node third_party = root.LookupEntryPoint ("various");
920 if (third_party == null) {
921 Console.Error.WriteLine ("No 'various' doc node! Check monodoc.xml!");
928 foreach (var sourceFile in sourceFiles)
929 root.AddSourceFile (sourceFile);
931 foreach (string path in UncompiledHelpSources) {
932 EcmaUncompiledHelpSource hs = new EcmaUncompiledHelpSource(path);
934 root.help_sources.Add (hs);
935 string epath = "extra-help-source-" + hs.Name;
936 Node hsn = root.CreateNode (hs.Name, "root:/" + epath);
937 root.name_to_hs [epath] = hs;
939 foreach (Node n in hs.Tree.Nodes){
952 public void AddSource (string sources_dir)
954 string [] files = Directory.GetFiles (sources_dir);
956 foreach (string file in files){
957 if (!file.EndsWith (".source"))
959 AddSourceFile (file);
963 public void AddSourceFile (string sourceFile)
965 Node third_party = LookupEntryPoint ("various") ?? this;
967 XmlDocument doc = new XmlDocument ();
969 doc.Load (sourceFile);
972 Console.Error.WriteLine ("Error: Could not load source file {0}", sourceFile);
976 XmlNodeList extra_nodes = doc.SelectNodes ("/monodoc/node");
977 if (extra_nodes.Count > 0)
978 Populate (third_party, extra_nodes);
980 XmlNodeList sources = doc.SelectNodes ("/monodoc/source");
981 if (sources == null){
982 Console.Error.WriteLine ("Error: No <source> section found in the {0} file", sourceFile);
985 foreach (XmlNode source in sources){
986 XmlAttribute a = source.Attributes ["provider"];
988 Console.Error.WriteLine ("Error: no provider in <source>");
991 string provider = a.InnerText;
992 a = source.Attributes ["basefile"];
994 Console.Error.WriteLine ("Error: no basefile in <source>");
997 string basefile = a.InnerText;
998 a = source.Attributes ["path"];
1000 Console.Error.WriteLine ("Error: no path in <source>");
1003 string path = a.InnerText;
1005 string basefilepath = Path.Combine (Path.GetDirectoryName (sourceFile), basefile);
1006 HelpSource hs = GetHelpSource (provider, basefilepath);
1010 help_sources.Add (hs);
1011 name_to_hs [path] = hs;
1013 Node parent = LookupEntryPoint (path);
1014 if (parent == null){
1015 Console.Error.WriteLine ("node `{0}' is not defined on the documentation map", path);
1016 parent = third_party;
1019 foreach (Node n in hs.Tree.Nodes){
1026 // Delete nodes which does not have documentaiton (source)
1027 static bool PurgeNode(Node node)
1031 if (!node.Documented)
1033 ArrayList del_child = new ArrayList();
1034 //Delete node unless any child has documentation
1035 bool purged_child = false;
1036 foreach (Node child in node.Nodes)
1038 purged_child = PurgeNode(child);
1041 del_child.Add(child);
1045 // delete the node if all its children are to be deleted
1046 purge = (node.Nodes.Count == del_child.Count);
1049 foreach (Node child in del_child)
1051 node.DelNode(child);
1058 public static string[] GetSupportedFormats ()
1060 return new string[]{
1071 public static HelpSource GetHelpSource (string provider, string basefilepath)
1076 return new EcmaHelpSource (basefilepath, false);
1077 case "ecma-uncompiled":
1078 return new EcmaUncompiledHelpSource (basefilepath);
1080 return new MonoHBHelpSource(basefilepath, false);
1081 case "xhtml": case "hb":
1082 return new XhtmlHelpSource (basefilepath, false);
1084 return new ManHelpSource (basefilepath, false);
1086 return new SimpleHelpSource (basefilepath, false);
1088 return new ErrorHelpSource (basefilepath, false);
1090 return new EcmaSpecHelpSource (basefilepath, false);
1092 return new AddinsHelpSource (basefilepath, false);
1094 Console.Error.WriteLine ("Error: Unknown provider specified: {0}", provider);
1099 catch (FileNotFoundException) {
1100 Console.Error.WriteLine ("Error: did not find one of the files in sources/"+basefilepath);
1105 public static Provider GetProvider (string provider, params string[] basefilepaths)
1109 return new AddinsProvider (basefilepaths [0]);
1111 EcmaProvider p = new EcmaProvider ();
1112 foreach (string d in basefilepaths)
1117 return new EcmaSpecProvider (basefilepaths [0]);
1119 return new ErrorProvider (basefilepaths [0]);
1121 return new ManProvider (basefilepaths);
1123 return new SimpleProvider (basefilepaths [0]);
1126 return new XhtmlProvider (basefilepaths [0]);
1128 throw new NotSupportedException (provider);
1133 // Maintains the name to node mapping
1135 Hashtable name_to_node = new Hashtable ();
1136 Hashtable name_to_hs = new Hashtable ();
1138 void Populate (Node parent, XmlNodeList xml_node_list)
1140 foreach (XmlNode xml_node in xml_node_list){
1141 XmlAttribute e = xml_node.Attributes ["parent"];
1142 if (e != null && name_to_node.ContainsKey (e.InnerText)) {
1143 Node p = (Node) name_to_node [e.InnerText];
1144 xml_node.Attributes.Remove (e);
1145 Populate (p, xml_node.SelectNodes ("."));
1148 e = xml_node.Attributes ["label"];
1150 Console.Error.WriteLine ("`label' attribute missing in <node>");
1153 string label = e.InnerText;
1154 e = xml_node.Attributes ["name"];
1156 Console.Error.WriteLine ("`name' attribute missing in <node>");
1159 string name = e.InnerText;
1161 Node n = parent.LookupNode (label, "root:/" + name);
1163 name_to_node [name] = n;
1164 XmlNodeList children = xml_node.SelectNodes ("./node");
1165 if (children != null)
1166 Populate (n, children);
1170 public Node LookupEntryPoint (string name)
1172 return (Node) name_to_node [name];
1175 ArrayList help_sources;
1176 DateTime lastHelpSourceTime;
1178 RootTree () : base (null, "Mono Documentation", "root:")
1180 nodes = new ArrayList ();
1181 help_sources = new ArrayList ();
1182 lastHelpSourceTime = DateTime.MinValue;
1185 public DateTime LastHelpSourceTime {
1187 return lastHelpSourceTime;
1191 public static bool GetNamespaceAndType (string url, out string ns, out string type)
1195 for (int i = 0; i < url.Length; ++i) {
1214 Console.Error.WriteLine ("Did not find dot in: " + url);
1219 ns = url.Substring (0, nsidx);
1220 type = url.Substring (nsidx + 1);
1222 //Console.Error.WriteLine ("GetNameSpaceAndType (ns={0}, type={1}", ns, type);
1226 public XmlDocument GetHelpXml (string url)
1228 string rest = url.Substring (2);
1231 if (!GetNamespaceAndType (rest, out ns, out type))
1234 foreach (HelpSource hs in help_sources) {
1235 EcmaHelpSource ehs = hs as EcmaHelpSource;
1238 string id = ehs.GetIdFromUrl ("T:", ns, type);
1241 XmlDocument doc = hs.GetHelpXmlWithChanges (id);
1248 public string TypeLookup (string url, out Node match_node)
1250 string rest = Regex.Replace (url, @"^T:\s*", "");
1253 if (!GetNamespaceAndType (rest, out ns, out type)){
1258 foreach (HelpSource hs in help_sources){
1259 string s = hs.RenderTypeLookup ("T:", ns, type, null, out match_node);
1262 lastHelpSourceTime = hs.ZipFileWriteTime;
1270 public string MemberLookup (string prefix, string url, out Node match_node)
1272 string rest = Regex.Replace (url, @"^.:\s*", "");
1274 // Dots in the arg list (for methods) confuse this.
1275 // Chop off the arg list for now and put it back later.
1276 string arglist = "";
1277 int argliststart = rest.IndexOf("(");
1278 if (argliststart >= 0) {
1279 arglist = rest.Substring(argliststart);
1280 rest = rest.Substring(0, argliststart);
1283 string ns_type, member;
1285 if (prefix != "C:") {
1286 int member_idx = rest.LastIndexOf (".");
1288 // The dot in .ctor (if it's a M: link) would confuse this.
1289 if (rest.EndsWith("..ctor")) member_idx--;
1291 ns_type = rest.Substring (0, member_idx);
1292 member = rest.Substring (member_idx + 1);
1294 // C: links don't have the .ctor member part as it would in a M: link
1295 // Even though externally C: links are different from M: links,
1296 // C: links get transformed into M:-style links (with .ctor) here.
1302 //Console.WriteLine ("NS_TYPE: {0} MEMBER: {1}", ns_type, member);
1305 if (!GetNamespaceAndType (ns_type, out ns, out type)){
1310 foreach (HelpSource hs in help_sources){
1311 string s = hs.RenderTypeLookup (prefix, ns, type, member + arglist, out match_node);
1314 lastHelpSourceTime = hs.ZipFileWriteTime;
1322 public Stream GetImage (string url)
1324 if (url.StartsWith ("source-id:")){
1325 string rest = url.Substring (10);
1326 int p = rest.IndexOf (":");
1327 string str_idx = rest.Substring (0, p);
1331 idx = Int32.Parse (str_idx);
1333 Console.Error.WriteLine ("Failed to parse source-id url: {0} `{1}'", url, str_idx);
1337 HelpSource hs = GetHelpSourceFromId (idx);
1338 lastHelpSourceTime = hs.ZipFileWriteTime;
1339 return hs.GetImage (rest.Substring (p + 1));
1341 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly (typeof(RootTree));
1342 return assembly.GetManifestResourceStream (url);
1344 lastHelpSourceTime = DateTime.MinValue;
1348 public HelpSource GetHelpSourceFromId (int id)
1350 return (HelpSource) help_sources [id];
1354 // Fetches the node title
1356 public string GetTitle (string url)
1360 if (url == null || url.StartsWith ("root:"))
1361 return "Mono Documentation";
1363 if (url.Length > 2 && url [1] == ':'){
1366 return url.Substring (2) + " Namespace";
1369 string s = TypeLookup (url, out match_node);
1370 if (match_node != null)
1371 return match_node.Caption;
1372 return url.Substring (2) + " type";
1380 MemberLookup (url.Substring (0,2), url, out match_node);
1381 if (match_node != null)
1382 return match_node.Caption;
1387 return "Mono Documentation";
1392 /// Allows every HelpSource to try to provide the content for this
1395 public string RenderUrl (string url, out Node match_node)
1397 lastHelpSourceTime = DateTime.MinValue;
1398 if (url == "root:") {
1401 // look whether there are contribs
1402 GlobalChangeset chgs = EditingUtils.changes;
1403 StringBuilder con = new StringBuilder ();
1405 //add links to the contrib
1406 int oldContrib = 0, contribs = 0;
1407 con.Append ("<ul>");
1408 foreach (DocSetChangeset dscs in chgs.DocSetChangesets)
1409 foreach (FileChangeset fcs in dscs.FileChangesets)
1410 foreach (Change c in fcs.Changes) {
1411 if (c.NodeUrl == null) {
1412 if (c.Serial == SettingsHandler.Settings.SerialNumber)
1414 } else if (c.Serial == SettingsHandler.Settings.SerialNumber) {
1416 con.Append (String.Format ("<li><a href=\"{0}\">{0}</a></li>", c.NodeUrl));
1420 string contrib = (oldContrib + contribs) == 1?"There is {0} contribution":"There are {0} contributions";
1421 con.Insert (0, String.Format (contrib, oldContrib + contribs) + " pending upload <i>(Contributing--> Upload)</i>", 1);
1422 con.Append ("</ul>");
1423 if (oldContrib == 1)
1424 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>");
1425 else if (oldContrib > 1)
1426 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>");
1428 //start the rendering
1429 if (!HelpSource.use_css) {
1430 StringBuilder sb = new StringBuilder ("<table bgcolor=\"#b0c4de\" width=\"100%\" cellpadding=\"5\"><tr><td><h3>Mono Documentation Library</h3></td></tr></table>");
1432 foreach (Node n in Nodes)
1433 sb.AppendFormat ("<a href='{0}'>{1}</a><br/>", n.Element, n.Caption);
1436 sb.Append ("<br><table bgcolor=\"#fff3f3\" width=\"100%\" cellpadding=\"5\"><tr><td>");
1437 sb.Append ("<h5>Contributions</h5><br>");
1438 if ((oldContrib + contribs) == 0) {
1439 sb.Append ("<p><b>You have not made any contributions yet.</b></p>");
1440 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>");
1441 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>");
1443 sb.Append (con.ToString ());
1445 sb.Append ("</td></tr></table>");
1446 return sb.ToString ();
1448 if (home_cache == null) {
1449 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly (typeof (HelpSource));
1450 Stream hp_stream = assembly.GetManifestResourceStream ("home.html");
1451 home_cache = (new StreamReader (hp_stream)).ReadToEnd ();
1453 StringBuilder sb = new StringBuilder (home_cache);
1455 sb.Replace ("@@FONT_FAMILY@@", SettingsHandler.Settings.preferred_font_family);
1456 sb.Replace ("@@FONT_SIZE@@", SettingsHandler.Settings.preferred_font_size.ToString());
1458 var visible = SettingsHandler.Settings.EnableEditing ? "block;" : "none;";
1459 if ((oldContrib + contribs) == 0) {
1460 sb.Replace ("@@CONTRIB_DISP@@", "display: none;");
1461 sb.Replace ("@@NO_CONTRIB_DISP@@", "display: " + visible);
1463 sb.Replace ("@@CONTRIB_DISP@@", "display: " + visible);
1464 sb.Replace ("@@NO_CONTRIB_DISP@@", "display: none;");
1465 sb.Replace ("@@CONTRIBS@@", con.ToString ());
1467 sb.Replace ("@@EDITING_ENABLED@@", "display: " + visible);
1469 // load the url of nodes
1471 StringBuilder urls = new StringBuilder ();
1472 foreach (Node n in Nodes) {
1473 add_str = String.Format ("<li><a href=\"{0}\">{1}</a></li>", n.Element, n.Caption);
1474 urls.Append (add_str);
1476 sb.Replace ("@@API_DOCS@@", urls.ToString ());
1478 return sb.ToString ();
1482 if (url.StartsWith ("root:")) {
1483 match_node = ((Node)name_to_node [url.Substring (6)]);
1484 HelpSource hs = ((HelpSource)name_to_hs [url.Substring (6)]);
1487 return GenerateNodeIndex(match_node);
1491 lastHelpSourceTime = hs.ZipFileWriteTime;
1492 return hs.GetText ("root:", out dummy);
1496 if (url.StartsWith ("source-id:")){
1497 string rest = url.Substring (10);
1498 int p = rest.IndexOf (":");
1499 string str_idx = rest.Substring (0, p);
1503 idx = Int32.Parse (str_idx);
1505 Console.Error.WriteLine ("Failed to parse source-id url: {0} `{1}'", url, str_idx);
1509 HelpSource hs = (HelpSource) help_sources [idx];
1510 // Console.WriteLine ("Attempting to get docs from: " + rest.Substring (p + 1));
1511 lastHelpSourceTime = hs.ZipFileWriteTime;
1512 return hs.GetText (rest.Substring (p + 1), out match_node);
1515 if (url.Length < 2){
1520 string prefix = url.Substring (0, 2);
1522 switch (prefix.ToUpper ()){
1524 foreach (HelpSource hs in help_sources){
1525 string s = hs.RenderNamespaceLookup (url, out match_node);
1527 lastHelpSourceTime = hs.ZipFileWriteTime;
1535 return TypeLookup (url, out match_node);
1543 return MemberLookup (prefix, url, out match_node);
1546 foreach (HelpSource hs in help_sources){
1547 string s = hs.GetText (url, out match_node);
1550 lastHelpSourceTime = hs.ZipFileWriteTime;
1559 public string GenerateNodeIndex (Node node)
1561 StringBuilder buf = new StringBuilder();
1562 buf.AppendFormat("<H3>{0}</H3>", node.Caption);
1564 foreach (Node child in node.Nodes)
1566 buf.AppendFormat("<li><a href=\"{0}\">{1}</a>", child.URL, child.Caption);
1568 buf.Append("</ul>");
1569 return buf.ToString();
1572 public IndexReader GetIndex ()
1574 //try to load from basedir
1575 string index_file = Path.Combine (basedir, "monodoc.index");
1576 if (File.Exists (index_file))
1577 return IndexReader.Load (index_file);
1578 //then, try to load from config dir
1579 index_file = Path.Combine (SettingsHandler.Path, "monodoc.index");
1580 return IndexReader.Load (index_file);
1584 public static void MakeIndex ()
1586 RootTree root = LoadTree ();
1590 IndexMaker index_maker = new IndexMaker ();
1592 foreach (HelpSource hs in root.help_sources){
1593 hs.PopulateIndex (index_maker);
1596 // if the user has no write permissions use config dir
1597 string path = Path.Combine (root.basedir, "monodoc.index");
1599 index_maker.Save (path);
1600 } catch (System.UnauthorizedAccessException) {
1601 path = Path.Combine (SettingsHandler.Path, "monodoc.index");
1603 index_maker.Save (path);
1604 } catch (System.UnauthorizedAccessException) {
1605 Console.WriteLine ("Unable to write index file in {0}", Path.Combine (SettingsHandler.Path, "monodoc.index"));
1611 // No octal in C#, how lame is that
1612 chmod (path, 0x1a4);
1614 Console.WriteLine ("Documentation index updated");
1617 static bool IsUnix {
1619 int p = (int) Environment.OSVersion.Platform;
1620 return ((p == 4) || (p == 128) || (p == 6));
1625 public SearchableIndex GetSearchIndex ()
1627 //try to load from basedir
1628 string index_file = Path.Combine (basedir, "search_index");
1629 if (Directory.Exists (index_file))
1630 return SearchableIndex.Load (index_file);
1631 //then, try to load from config dir
1632 index_file = Path.Combine (SettingsHandler.Path, "search_index");
1633 return SearchableIndex.Load (index_file);
1636 public static void MakeSearchIndex ()
1638 // Loads the RootTree
1639 Console.WriteLine ("Loading the monodoc tree...");
1640 RootTree root = LoadTree ();
1644 string dir = Path.Combine (root.basedir, "search_index");
1646 //try to create the dir to store the index
1648 if (!Directory.Exists (dir))
1649 Directory.CreateDirectory (dir);
1651 writer = new IndexWriter(Lucene.Net.Store.FSDirectory.GetDirectory(dir, true), new StandardAnalyzer(), true);
1652 } catch (UnauthorizedAccessException) {
1653 //try in the .config directory
1655 dir = Path.Combine (SettingsHandler.Path, "search_index");
1656 if (!Directory.Exists (dir))
1657 Directory.CreateDirectory (dir);
1659 writer = new IndexWriter(Lucene.Net.Store.FSDirectory.GetDirectory(dir, true), new StandardAnalyzer(), true);
1660 } catch (UnauthorizedAccessException) {
1661 Console.WriteLine ("You don't have permissions to write on " + dir);
1666 //Collect all the documents
1667 Console.WriteLine ("Collecting and adding documents...");
1668 foreach (HelpSource hs in root.HelpSources)
1669 hs.PopulateSearchableIndex (writer);
1671 //Optimize and close
1672 Console.WriteLine ("Closing...");
1678 public ICollection HelpSources { get { return new ArrayList(help_sources); } }
1680 [System.Runtime.InteropServices.DllImport ("libc")]
1681 static extern int chmod (string filename, int mode);