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 using Mono.Documentation;
33 /// This tree is populated by the documentation providers, or populated
34 /// from a binary encoding of the tree. The format of the tree is designed
35 /// to minimize the need to load it in full.
37 public class Tree : Node {
39 #region Loading the tree from a file
42 /// Our HelpSource container
44 public readonly HelpSource HelpSource;
46 internal FileStream InputStream;
47 internal BinaryReader InputReader;
50 /// Load from file constructor
52 public Tree (HelpSource hs, string filename) : base (null, null)
54 Encoding utf8 = new UTF8Encoding (false, true);
56 if (!File.Exists (filename)){
57 throw new FileNotFoundException ();
60 InputStream = File.OpenRead (filename);
61 InputReader = new BinaryReader (InputStream, utf8);
62 byte [] sig = InputReader.ReadBytes (4);
65 throw new Exception ("Invalid file format");
67 InputStream.Position = 4;
68 position = InputReader.ReadInt32 ();
75 /// Tree creation and merged tree constructor
77 public Tree (HelpSource hs, string caption, string url) : base (caption, url)
82 public Tree (HelpSource hs, Node parent, string caption, string element) : base (parent, caption, element)
90 /// Saves the tree into the specified file using the help file format.
92 public void Save (string file)
94 Encoding utf8 = new UTF8Encoding (false, true);
95 using (FileStream output = File.OpenWrite (file)){
96 // Skip over the pointer to the first node.
99 using (BinaryWriter writer = new BinaryWriter (output, utf8)){
101 Dump (output, writer);
104 writer.Write (new byte [] { (byte) 'M', (byte) 'o', (byte) 'H', (byte) 'P' });
105 writer.Write (position);
110 static bool GoodSig (byte [] sig)
114 if (sig [0] != (byte) 'M' ||
115 sig [1] != (byte) 'o' ||
116 sig [2] != (byte) 'H' ||
117 sig [3] != (byte) 'P')
124 public class Node : IComparable {
125 string caption, element;
126 public bool Documented;
127 public readonly Tree tree;
129 protected ArrayList nodes;
130 protected internal int position;
132 static ArrayList empty = ArrayList.ReadOnly(new ArrayList(0));
135 /// Creates a node, called by the Tree.
137 public Node (string caption, string element)
139 this.tree = (Tree) this;
140 this.caption = caption;
141 this.element = element;
145 public Node (Node parent, string caption, string element)
147 this.parent = parent;
148 this.tree = parent.tree;
149 this.caption = caption;
150 this.element = element;
154 /// Creates a node from an on-disk representation
156 Node (Node parent, int address)
158 this.parent = parent;
160 this.tree = parent.tree;
165 public void AddNode (Node n)
172 public void DelNode (Node n)
177 public ArrayList Nodes {
181 return nodes != null ? nodes : empty;
185 public string Element {
197 public string Caption {
211 public void LoadNode ()
214 position = -position;
216 tree.InputStream.Position = position;
217 BinaryReader reader = tree.InputReader;
218 int count = DecodeInt (reader);
219 element = reader.ReadString ();
220 caption = reader.ReadString ();
224 nodes = new ArrayList (count);
225 for (int i = 0; i < count; i++){
226 int child_address = DecodeInt (reader);
228 Node t = new Node (this, -child_address);
234 /// Creates a new node, in the locator entry point, and with
235 /// a user visible caption of @caption
237 public Node CreateNode (string c_caption, string c_element)
240 nodes = new ArrayList ();
242 Node t = new Node (this, c_caption, c_element);
248 /// Looks up or creates a new node, in the locator entry point, and with
249 /// a user visible caption of @caption. This is different from
250 /// CreateNode in that it will look up an existing node for the given @locator.
252 public Node LookupNode (string c_caption, string c_element)
255 return CreateNode (c_caption, c_element);
257 foreach (Node n in nodes){
258 if (n.element == c_element)
261 return CreateNode (c_caption, c_element);
264 public void EnsureNodes ()
267 nodes = new ArrayList ();
272 return nodes == null;
276 void EncodeInt (BinaryWriter writer, int value)
279 int high = (value >> 7) & 0x01ffffff;
280 byte b = (byte)(value & 0x7f);
283 b = (byte)(b | 0x80);
291 int DecodeInt (BinaryReader reader)
298 b = reader.ReadByte();
300 ret = ret | ((b & 0x7f) << shift);
302 } while ((b & 0x80) == 0x80);
307 internal void Dump (FileStream output, BinaryWriter writer)
310 foreach (Node child in nodes){
311 child.Dump (output, writer);
314 position = (int) output.Position;
315 EncodeInt (writer, nodes == null ? 0 : (int) nodes.Count);
316 writer.Write (element);
317 writer.Write (caption);
320 foreach (Node child in nodes){
321 EncodeInt (writer, child.position);
328 static void Indent ()
330 for (int i = 0; i < indent; i++)
334 public static void PrintTree (Node node)
337 Console.WriteLine ("{0},{1}\t[PublicUrl: {2}]", node.Element, node.Caption, node.PublicUrl);
338 if (node.Nodes.Count == 0)
342 foreach (Node n in node.Nodes)
353 [Obsolete("Use PublicUrl")]
359 if (element.IndexOf (":") >= 0)
363 string url = parent.URL;
365 if (url.EndsWith ("/"))
366 return url + element;
368 return parent.URL + "/" + element;
374 public string PublicUrl {
376 return tree.HelpSource != null
377 ? tree.HelpSource.GetPublicUrl (URL)
382 int IComparable.CompareTo (object obj)
384 Node other = obj as Node;
390 if (other.position < 0)
393 Regex digits = new Regex (@"([\d]+)|([^\d]+)");
394 MatchEvaluator eval = delegate (Match m) {
395 return (m.Value.Length > 0 && char.IsDigit (m.Value [0]))
396 ? m.Value.PadLeft (System.Math.Max (caption.Length, other.caption.Length))
399 return digits.Replace (caption, eval).CompareTo (digits.Replace (other.caption, eval));
404 // The HelpSource class keeps track of the archived data, and its
407 public class HelpSource {
409 public static bool use_css = false;
410 public static string css_code;
411 public static string CssCode {
413 if (css_code != null)
416 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetCallingAssembly ();
417 Stream str_css = assembly.GetManifestResourceStream ("base.css");
418 StringBuilder sb = new StringBuilder ((new StreamReader (str_css)).ReadToEnd());
419 sb.Replace ("@@FONT_FAMILY@@", SettingsHandler.Settings.preferred_font_family);
420 sb.Replace ("@@FONT_SIZE@@", SettingsHandler.Settings.preferred_font_size.ToString());
421 css_code = sb.ToString ();
424 set { css_code = value; }
427 public virtual string InlineCss {
428 get { return CssCode; }
431 public virtual string InlineJavaScript {
435 public static bool FullHtml = true;
437 // should only be enabled by ASP.NET webdoc
438 public static bool UseWebdocCache;
441 // The unique ID for this HelpSource.
444 DateTime zipFileWriteTime;
446 TraceLevel trace_level = TraceLevel.Warning;
447 protected bool nozip;
448 protected string base_dir;
450 public HelpSource (string base_filename, bool create)
452 this.name = Path.GetFileName (base_filename);
453 tree_filename = base_filename + ".tree";
454 zip_filename = base_filename + ".zip";
455 base_dir = XmlDocUtils.GetCacheDirectory (base_filename);
456 if (UseWebdocCache && !create && Directory.Exists (base_dir)) {
463 Tree = new Tree (this, tree_filename);
467 FileInfo fi = new FileInfo (zip_filename);
468 zipFileWriteTime = fi.LastWriteTime;
470 zipFileWriteTime = DateTime.Now;
474 public HelpSource() {
475 Tree = new Tree (this, "Blah", "Blah");
479 public DateTime ZipFileWriteTime {
481 return zipFileWriteTime;
485 public int SourceID {
497 public TraceLevel TraceLevel {
498 get { return trace_level; }
499 set { trace_level = value; }
505 /// Returns a stream from the packaged help source archive
507 public virtual Stream GetHelpStream (string id)
510 string path = XmlDocUtils.GetCachedFileName (base_dir, id);
511 if (File.Exists (path))
512 return File.OpenRead (path);
516 if (zip_file == null)
517 zip_file = new ZipFile (zip_filename);
519 ZipEntry entry = zip_file.GetEntry (id);
521 return zip_file.GetInputStream (entry);
525 public string GetRealPath (string file)
527 if (zip_file == null)
528 zip_file = new ZipFile (zip_filename);
530 ZipEntry entry = zip_file.GetEntry (file);
531 if (entry != null && entry.ExtraData != null)
532 return ConvertToString (entry.ExtraData);
536 public XmlReader GetHelpXml (string id)
539 Stream s = File.OpenRead (XmlDocUtils.GetCachedFileName (base_dir, id));
540 string url = "monodoc:///" + SourceID + "@" + System.Web.HttpUtility.UrlEncode (id) + "@";
541 return new XmlTextReader (url, s);
544 if (zip_file == null)
545 zip_file = new ZipFile (zip_filename);
547 ZipEntry entry = zip_file.GetEntry (id);
549 Stream s = zip_file.GetInputStream (entry);
550 string url = "monodoc:///" + SourceID + "@" + System.Web.HttpUtility.UrlEncode (id) + "@";
551 return new XmlTextReader (url, s);
556 public virtual XmlDocument GetHelpXmlWithChanges (string id)
559 Stream s = File.OpenRead (XmlDocUtils.GetCachedFileName (base_dir, id));
560 string url = "monodoc:///" + SourceID + "@" + System.Web.HttpUtility.UrlEncode (id) + "@";
561 XmlReader r = new XmlTextReader (url, s);
562 XmlDocument ret = new XmlDocument ();
567 if (zip_file == null)
568 zip_file = new ZipFile (zip_filename);
570 ZipEntry entry = zip_file.GetEntry (id);
572 Stream s = zip_file.GetInputStream (entry);
573 string url = "monodoc:///" + SourceID + "@" + System.Web.HttpUtility.UrlEncode (id) + "@";
574 XmlReader r = new XmlTextReader (url, s);
575 XmlDocument ret = new XmlDocument ();
578 if (entry.ExtraData != null)
579 EditingUtils.AccountForChanges (ret, Name, ConvertToString (entry.ExtraData));
587 /// Get a nice, unique expression for any XPath node that you get.
588 /// This function is used by editing to get the expression to put
589 /// on to the file. The idea is to create an expression that is resistant
590 /// to changes in the structure of the XML.
592 public virtual string GetNodeXPath (XPathNavigator n)
594 return EditingUtils.GetXPath (n.Clone ());
597 public string GetEditUri (XPathNavigator n)
599 return EditingUtils.FormatEditUri (n.BaseURI, GetNodeXPath (n));
602 static string ConvertToString (byte[] data)
604 return Encoding.UTF8.GetString(data);
607 static byte[] ConvertToArray (string str)
609 return Encoding.UTF8.GetBytes(str);
613 /// The tree that is being populated
616 public RootTree RootTree;
618 // Base filename used by this HelpSource.
619 string tree_filename, zip_filename;
622 const int buffer_size = 65536;
623 ZipOutputStream zip_output;
626 HelpSource (string base_filename)
630 void SetupForOutput ()
632 Tree = new Tree (this, "", "");
634 FileStream stream = File.Create (zip_filename);
636 zip_output = new ZipOutputStream (stream);
637 zip_output.SetLevel (9);
639 buffer = new byte [buffer_size];
643 /// Saves the tree and the archive
647 Tree.Save (tree_filename);
648 zip_output.Finish ();
656 return String.Format ("{0}", code++);
660 /// Providers call this to store a file they will need, and the return value
661 /// is the name that was assigned to it
663 public string PackFile (string file)
665 string entry_name = GetNewCode ();
666 return PackFile (file, entry_name);
669 public string PackFile (string file, string entry_name)
671 using (FileStream input = File.OpenRead (file)) {
672 PackStream (input, entry_name, file);
678 public void PackStream (Stream s, string entry_name)
680 PackStream (s, entry_name, null);
683 void PackStream (Stream s, string entry_name, string realPath)
685 ZipEntry entry = new ZipEntry (entry_name);
687 if (realPath != null)
688 entry.ExtraData = ConvertToArray (realPath);
690 zip_output.PutNextEntry (entry);
693 while ((n = s.Read (buffer, 0, buffer_size)) > 0){
694 zip_output.Write (buffer, 0, n);
698 public void PackXml (string fname, XmlDocument doc, string real_path)
700 ZipEntry entry = new ZipEntry (fname);
701 if (real_path != null)
702 entry.ExtraData = ConvertToArray(real_path);
704 zip_output.PutNextEntry (entry);
705 XmlTextWriter xmlWriter = new XmlTextWriter (zip_output, Encoding.UTF8);
706 doc.WriteContentTo (xmlWriter);
710 public virtual void RenderPreviewDocs (XmlNode newNode, XmlWriter writer)
712 throw new NotImplementedException ();
715 public virtual string GetPublicUrl (string id)
720 public virtual string GetText (string url, out Node n)
726 protected string GetCachedText (string url)
730 string file = XmlDocUtils.GetCachedFileName (base_dir, url);
731 if (!File.Exists (file))
733 return File.OpenText (file).ReadToEnd ();
736 public virtual Stream GetImage (string url)
742 // Default method implementation does not satisfy the request
744 public virtual string RenderTypeLookup (string prefix, string ns, string type, string member, out Node n)
750 public virtual string RenderNamespaceLookup (string nsurl, out Node n)
757 // Populates the index.
759 public virtual void PopulateIndex (IndexMaker index_maker)
764 // Build an html document
766 public static string BuildHtml (string css, string html_code)
768 return BuildHtml (css, null, html_code);
771 internal static string BuildHtml (string css, string js, string html_code) {
775 StringWriter output = new StringWriter ();
776 output.Write ("<html><head>");
777 output.Write ("<style type=\"text/css\">");
778 output.Write (CssCode);
780 output.Write ("</style>");
782 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetCallingAssembly ();
783 Stream str_js = assembly.GetManifestResourceStream ("helper.js");
784 StringBuilder sb = new StringBuilder ((new StreamReader (str_js)).ReadToEnd());
785 output.Write ("<script type=\"text/JavaScript\">\n");
786 output.Write (sb.ToString ());
787 output.Write ("</script>\n");
790 output.Write ("<script type=\"text/JavaScript\">\n");
792 output.Write ("\n</script>");
795 output.Write ("</head><body>");
796 output.Write (html_code);
797 output.Write ("</body></html>");
798 return output.ToString ();
802 // Create different Documents for adding to Lucene search index
803 // The default action is do nothing. Subclasses should add the docs
805 public virtual void PopulateSearchableIndex (IndexWriter writer) {
809 public void Message (TraceLevel level, string format, params object[] args)
811 if ((int) level <= (int) trace_level)
812 Console.WriteLine (format, args);
815 public void Error (string format, params object[] args)
817 Console.Error.WriteLine (format, args);
821 public abstract class Provider {
823 // This code is used to "tag" all the different sources
834 public abstract void PopulateTree (Tree tree);
837 // Called at shutdown time after the tree has been populated to perform
838 // any fixups or final tasks.
840 public abstract void CloseTree (HelpSource hs, Tree tree);
843 public class RootTree : Tree {
846 public static ArrayList UncompiledHelpSources = new ArrayList();
848 public const int MonodocVersion = 1;
850 public static RootTree LoadTree ()
852 return LoadTree (null);
856 // Loads the tree layout
858 public static RootTree LoadTree (string basedir)
860 if (basedir == null) {
861 string myPath = System.Reflection.Assembly.GetExecutingAssembly ().Location;
862 string cfgFile = myPath + ".config";
863 if (!File.Exists (cfgFile)) {
867 XmlDocument d = new XmlDocument ();
869 basedir = d.SelectSingleNode ("config/path").Attributes ["docsPath"].Value;
872 XmlDocument doc = new XmlDocument ();
874 RootTree root = new RootTree ();
875 root.basedir = basedir;
880 string layout = Path.Combine (basedir, "monodoc.xml");
882 XmlNodeList nodes = doc.SelectNodes ("/node/node");
884 root.name_to_node ["root"] = root;
885 root.name_to_node ["libraries"] = root;
886 root.Populate (root, nodes);
888 Node third_party = root.LookupEntryPoint ("various");
889 if (third_party == null) {
890 Console.Error.WriteLine ("No 'various' doc node! Check monodoc.xml!");
897 root.AddSource (Path.Combine (basedir, "sources"));
899 foreach (string path in UncompiledHelpSources) {
900 EcmaUncompiledHelpSource hs = new EcmaUncompiledHelpSource(path);
902 root.help_sources.Add (hs);
903 string epath = "extra-help-source-" + hs.Name;
904 Node hsn = root.CreateNode (hs.Name, "root:/" + epath);
905 root.name_to_hs [epath] = hs;
907 foreach (Node n in hs.Tree.Nodes){
920 public void AddSource (string sources_dir)
922 Node third_party = LookupEntryPoint ("various") ?? this;
924 string [] files = Directory.GetFiles (sources_dir);
926 foreach (string file in files){
927 if (!file.EndsWith (".source"))
930 XmlDocument doc = new XmlDocument ();
934 Console.Error.WriteLine ("Error: Could not load source file {0}", file);
938 XmlNodeList extra_nodes = doc.SelectNodes ("/monodoc/node");
939 if (extra_nodes.Count > 0)
940 Populate (third_party, extra_nodes);
942 XmlNodeList sources = doc.SelectNodes ("/monodoc/source");
943 if (sources == null){
944 Console.Error.WriteLine ("Error: No <source> section found in the {0} file", file);
947 foreach (XmlNode source in sources){
948 XmlAttribute a = source.Attributes ["provider"];
950 Console.Error.WriteLine ("Error: no provider in <source>");
953 string provider = a.InnerText;
954 a = source.Attributes ["basefile"];
956 Console.Error.WriteLine ("Error: no basefile in <source>");
959 string basefile = a.InnerText;
960 a = source.Attributes ["path"];
962 Console.Error.WriteLine ("Error: no path in <source>");
965 string path = a.InnerText;
967 string basefilepath = Path.Combine (sources_dir, basefile);
968 HelpSource hs = GetHelpSource (provider, basefilepath);
972 help_sources.Add (hs);
973 name_to_hs [path] = hs;
975 Node parent = LookupEntryPoint (path);
977 Console.Error.WriteLine ("node `{0}' is not defined on the documentation map", path);
978 parent = third_party;
981 foreach (Node n in hs.Tree.Nodes){
989 // Delete nodes which does not have documentaiton (source)
990 static bool PurgeNode(Node node)
994 if (!node.Documented)
996 ArrayList del_child = new ArrayList();
997 //Delete node unless any child has documentation
998 bool purged_child = false;
999 foreach (Node child in node.Nodes)
1001 purged_child = PurgeNode(child);
1004 del_child.Add(child);
1008 // delete the node if all its children are to be deleted
1009 purge = (node.Nodes.Count == del_child.Count);
1012 foreach (Node child in del_child)
1014 node.DelNode(child);
1021 public static string[] GetSupportedFormats ()
1023 return new string[]{
1034 public static HelpSource GetHelpSource (string provider, string basefilepath)
1039 return new EcmaHelpSource (basefilepath, false);
1040 case "ecma-uncompiled":
1041 return new EcmaUncompiledHelpSource (basefilepath);
1043 return new MonoHBHelpSource(basefilepath, false);
1044 case "xhtml": case "hb":
1045 return new XhtmlHelpSource (basefilepath, false);
1047 return new ManHelpSource (basefilepath, false);
1049 return new SimpleHelpSource (basefilepath, false);
1051 return new ErrorHelpSource (basefilepath, false);
1053 return new EcmaSpecHelpSource (basefilepath, false);
1055 return new AddinsHelpSource (basefilepath, false);
1057 Console.Error.WriteLine ("Error: Unknown provider specified: {0}", provider);
1062 catch (FileNotFoundException) {
1063 Console.Error.WriteLine ("Error: did not find one of the files in sources/"+basefilepath);
1068 public static Provider GetProvider (string provider, params string[] basefilepaths)
1072 return new AddinsProvider (basefilepaths [0]);
1074 EcmaProvider p = new EcmaProvider ();
1075 foreach (string d in basefilepaths)
1080 return new EcmaSpecProvider (basefilepaths [0]);
1082 return new ErrorProvider (basefilepaths [0]);
1084 return new ManProvider (basefilepaths);
1086 return new SimpleProvider (basefilepaths [0]);
1089 return new XhtmlProvider (basefilepaths [0]);
1091 throw new NotSupportedException (provider);
1096 // Maintains the name to node mapping
1098 Hashtable name_to_node = new Hashtable ();
1099 Hashtable name_to_hs = new Hashtable ();
1101 void Populate (Node parent, XmlNodeList xml_node_list)
1103 foreach (XmlNode xml_node in xml_node_list){
1104 XmlAttribute e = xml_node.Attributes ["parent"];
1105 if (e != null && name_to_node.ContainsKey (e.InnerText)) {
1106 Node p = (Node) name_to_node [e.InnerText];
1107 xml_node.Attributes.Remove (e);
1108 Populate (p, xml_node.SelectNodes ("."));
1111 e = xml_node.Attributes ["label"];
1113 Console.Error.WriteLine ("`label' attribute missing in <node>");
1116 string label = e.InnerText;
1117 e = xml_node.Attributes ["name"];
1119 Console.Error.WriteLine ("`name' attribute missing in <node>");
1122 string name = e.InnerText;
1124 Node n = parent.LookupNode (label, "root:/" + name);
1126 name_to_node [name] = n;
1127 XmlNodeList children = xml_node.SelectNodes ("./node");
1128 if (children != null)
1129 Populate (n, children);
1133 public Node LookupEntryPoint (string name)
1135 return (Node) name_to_node [name];
1138 ArrayList help_sources;
1139 DateTime lastHelpSourceTime;
1141 RootTree () : base (null, "Mono Documentation", "root:")
1143 nodes = new ArrayList ();
1144 help_sources = new ArrayList ();
1145 lastHelpSourceTime = DateTime.MinValue;
1148 public DateTime LastHelpSourceTime {
1150 return lastHelpSourceTime;
1154 public static bool GetNamespaceAndType (string url, out string ns, out string type)
1158 for (int i = 0; i < url.Length; ++i) {
1177 Console.Error.WriteLine ("Did not find dot in: " + url);
1182 ns = url.Substring (0, nsidx);
1183 type = url.Substring (nsidx + 1);
1185 //Console.Error.WriteLine ("GetNameSpaceAndType (ns={0}, type={1}", ns, type);
1189 public XmlDocument GetHelpXml (string url)
1191 string rest = url.Substring (2);
1194 if (!GetNamespaceAndType (rest, out ns, out type))
1197 foreach (HelpSource hs in help_sources) {
1198 EcmaHelpSource ehs = hs as EcmaHelpSource;
1201 string id = ehs.GetIdFromUrl ("T:", ns, type);
1204 XmlDocument doc = hs.GetHelpXmlWithChanges (id);
1211 public string TypeLookup (string url, out Node match_node)
1213 string rest = Regex.Replace (url, @"^T:\s*", "");
1216 if (!GetNamespaceAndType (rest, out ns, out type)){
1221 foreach (HelpSource hs in help_sources){
1222 string s = hs.RenderTypeLookup ("T:", ns, type, null, out match_node);
1225 lastHelpSourceTime = hs.ZipFileWriteTime;
1233 public string MemberLookup (string prefix, string url, out Node match_node)
1235 string rest = Regex.Replace (url, @"^.:\s*", "");
1237 // Dots in the arg list (for methods) confuse this.
1238 // Chop off the arg list for now and put it back later.
1239 string arglist = "";
1240 int argliststart = rest.IndexOf("(");
1241 if (argliststart >= 0) {
1242 arglist = rest.Substring(argliststart);
1243 rest = rest.Substring(0, argliststart);
1246 string ns_type, member;
1248 if (prefix != "C:") {
1249 int member_idx = rest.LastIndexOf (".");
1251 // The dot in .ctor (if it's a M: link) would confuse this.
1252 if (rest.EndsWith("..ctor")) member_idx--;
1254 ns_type = rest.Substring (0, member_idx);
1255 member = rest.Substring (member_idx + 1);
1257 // C: links don't have the .ctor member part as it would in a M: link
1258 // Even though externally C: links are different from M: links,
1259 // C: links get transformed into M:-style links (with .ctor) here.
1265 //Console.WriteLine ("NS_TYPE: {0} MEMBER: {1}", ns_type, member);
1268 if (!GetNamespaceAndType (ns_type, out ns, out type)){
1273 foreach (HelpSource hs in help_sources){
1274 string s = hs.RenderTypeLookup (prefix, ns, type, member + arglist, out match_node);
1277 lastHelpSourceTime = hs.ZipFileWriteTime;
1285 public Stream GetImage (string url)
1287 if (url.StartsWith ("source-id:")){
1288 string rest = url.Substring (10);
1289 int p = rest.IndexOf (":");
1290 string str_idx = rest.Substring (0, p);
1294 idx = Int32.Parse (str_idx);
1296 Console.Error.WriteLine ("Failed to parse source-id url: {0} `{1}'", url, str_idx);
1300 HelpSource hs = GetHelpSourceFromId (idx);
1301 lastHelpSourceTime = hs.ZipFileWriteTime;
1302 return hs.GetImage (rest.Substring (p + 1));
1304 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly (typeof(RootTree));
1305 return assembly.GetManifestResourceStream (url);
1307 lastHelpSourceTime = DateTime.MinValue;
1311 public HelpSource GetHelpSourceFromId (int id)
1313 return (HelpSource) help_sources [id];
1317 // Fetches the node title
1319 public string GetTitle (string url)
1323 if (url == null || url.StartsWith ("root:"))
1324 return "Mono Documentation";
1326 if (url.Length > 2 && url [1] == ':'){
1329 return url.Substring (2) + " Namespace";
1332 string s = TypeLookup (url, out match_node);
1333 if (match_node != null)
1334 return match_node.Caption;
1335 return url.Substring (2) + " type";
1343 MemberLookup (url.Substring (0,2), url, out match_node);
1344 if (match_node != null)
1345 return match_node.Caption;
1350 return "Mono Documentation";
1355 /// Allows every HelpSource to try to provide the content for this
1358 public string RenderUrl (string url, out Node match_node)
1360 lastHelpSourceTime = DateTime.MinValue;
1361 if (url == "root:") {
1364 // look whether there are contribs
1365 GlobalChangeset chgs = EditingUtils.changes;
1366 StringBuilder con = new StringBuilder ();
1368 //add links to the contrib
1369 int oldContrib = 0, contribs = 0;
1370 con.Append ("<ul>");
1371 foreach (DocSetChangeset dscs in chgs.DocSetChangesets)
1372 foreach (FileChangeset fcs in dscs.FileChangesets)
1373 foreach (Change c in fcs.Changes) {
1374 if (c.NodeUrl == null) {
1375 if (c.Serial == SettingsHandler.Settings.SerialNumber)
1377 } else if (c.Serial == SettingsHandler.Settings.SerialNumber) {
1379 con.Append (String.Format ("<li><a href=\"{0}\">{0}</a></li>", c.NodeUrl));
1383 string contrib = (oldContrib + contribs) == 1?"There is {0} contribution":"There are {0} contributions";
1384 con.Insert (0, String.Format (contrib, oldContrib + contribs) + " pending upload <i>(Contributing--> Upload)</i>", 1);
1385 con.Append ("</ul>");
1386 if (oldContrib == 1)
1387 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>");
1388 else if (oldContrib > 1)
1389 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>");
1391 //start the rendering
1392 if (!HelpSource.use_css) {
1393 StringBuilder sb = new StringBuilder ("<table bgcolor=\"#b0c4de\" width=\"100%\" cellpadding=\"5\"><tr><td><h3>Mono Documentation Library</h3></td></tr></table>");
1395 foreach (Node n in Nodes)
1396 sb.AppendFormat ("<a href='{0}'>{1}</a><br/>", n.Element, n.Caption);
1399 sb.Append ("<br><table bgcolor=\"#fff3f3\" width=\"100%\" cellpadding=\"5\"><tr><td>");
1400 sb.Append ("<h5>Contributions</h5><br>");
1401 if ((oldContrib + contribs) == 0) {
1402 sb.Append ("<p><b>You have not made any contributions yet.</b></p>");
1403 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>");
1404 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>");
1406 sb.Append (con.ToString ());
1408 sb.Append ("</td></tr></table>");
1409 return sb.ToString ();
1411 if (home_cache == null) {
1412 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly (typeof (HelpSource));
1413 Stream hp_stream = assembly.GetManifestResourceStream ("home.html");
1414 home_cache = (new StreamReader (hp_stream)).ReadToEnd ();
1416 StringBuilder sb = new StringBuilder (home_cache);
1418 sb.Replace ("@@FONT_FAMILY@@", SettingsHandler.Settings.preferred_font_family);
1419 sb.Replace ("@@FONT_SIZE@@", SettingsHandler.Settings.preferred_font_size.ToString());
1421 if ((oldContrib + contribs) == 0) {
1422 sb.Replace ("@@CONTRIB_DISP@@", "display: none;");
1424 sb.Replace ("@@NO_CONTRIB_DISP@@", "display: none;");
1425 sb.Replace ("@@CONTRIBS@@", con.ToString ());
1428 // load the url of nodes
1430 StringBuilder urls = new StringBuilder ();
1431 foreach (Node n in Nodes) {
1432 add_str = String.Format ("<li><a href=\"{0}\">{1}</a></li>", n.Element, n.Caption);
1433 urls.Append (add_str);
1435 sb.Replace ("@@API_DOCS@@", urls.ToString ());
1437 return sb.ToString ();
1441 if (url.StartsWith ("root:")) {
1442 match_node = ((Node)name_to_node [url.Substring (6)]);
1443 HelpSource hs = ((HelpSource)name_to_hs [url.Substring (6)]);
1446 return GenerateNodeIndex(match_node);
1450 lastHelpSourceTime = hs.ZipFileWriteTime;
1451 return hs.GetText ("root:", out dummy);
1455 if (url.StartsWith ("source-id:")){
1456 string rest = url.Substring (10);
1457 int p = rest.IndexOf (":");
1458 string str_idx = rest.Substring (0, p);
1462 idx = Int32.Parse (str_idx);
1464 Console.Error.WriteLine ("Failed to parse source-id url: {0} `{1}'", url, str_idx);
1468 HelpSource hs = (HelpSource) help_sources [idx];
1469 // Console.WriteLine ("Attempting to get docs from: " + rest.Substring (p + 1));
1470 lastHelpSourceTime = hs.ZipFileWriteTime;
1471 return hs.GetText (rest.Substring (p + 1), out match_node);
1474 if (url.Length < 2){
1479 string prefix = url.Substring (0, 2);
1481 switch (prefix.ToUpper ()){
1483 foreach (HelpSource hs in help_sources){
1484 string s = hs.RenderNamespaceLookup (url, out match_node);
1486 lastHelpSourceTime = hs.ZipFileWriteTime;
1494 return TypeLookup (url, out match_node);
1502 return MemberLookup (prefix, url, out match_node);
1505 foreach (HelpSource hs in help_sources){
1506 string s = hs.GetText (url, out match_node);
1509 lastHelpSourceTime = hs.ZipFileWriteTime;
1518 public string GenerateNodeIndex (Node node)
1520 StringBuilder buf = new StringBuilder();
1521 buf.AppendFormat("<H3>{0}</H3>", node.Caption);
1523 foreach (Node child in node.Nodes)
1525 buf.AppendFormat("<li><a href=\"{0}\">{1}</a>", child.URL, child.Caption);
1527 buf.Append("</ul>");
1528 return buf.ToString();
1531 public IndexReader GetIndex ()
1533 //try to load from basedir
1534 string index_file = Path.Combine (basedir, "monodoc.index");
1535 if (File.Exists (index_file))
1536 return IndexReader.Load (index_file);
1537 //then, try to load from config dir
1538 index_file = Path.Combine (SettingsHandler.Path, "monodoc.index");
1539 return IndexReader.Load (index_file);
1543 public static void MakeIndex ()
1545 RootTree root = LoadTree ();
1549 IndexMaker index_maker = new IndexMaker ();
1551 foreach (HelpSource hs in root.help_sources){
1552 hs.PopulateIndex (index_maker);
1555 // if the user has no write permissions use config dir
1556 string path = Path.Combine (root.basedir, "monodoc.index");
1558 index_maker.Save (path);
1559 } catch (System.UnauthorizedAccessException) {
1560 path = Path.Combine (SettingsHandler.Path, "monodoc.index");
1562 index_maker.Save (path);
1563 } catch (System.UnauthorizedAccessException) {
1564 Console.WriteLine ("Unable to write index file in {0}", Path.Combine (SettingsHandler.Path, "monodoc.index"));
1570 // No octal in C#, how lame is that
1571 chmod (path, 0x1a4);
1573 Console.WriteLine ("Documentation index updated");
1576 static bool IsUnix {
1578 int p = (int) Environment.OSVersion.Platform;
1579 return ((p == 4) || (p == 128) || (p == 6));
1584 public SearchableIndex GetSearchIndex ()
1586 //try to load from basedir
1587 string index_file = Path.Combine (basedir, "search_index");
1588 if (Directory.Exists (index_file))
1589 return SearchableIndex.Load (index_file);
1590 //then, try to load from config dir
1591 index_file = Path.Combine (SettingsHandler.Path, "search_index");
1592 return SearchableIndex.Load (index_file);
1595 public static void MakeSearchIndex ()
1597 // Loads the RootTree
1598 Console.WriteLine ("Loading the monodoc tree...");
1599 RootTree root = LoadTree ();
1603 string dir = Path.Combine (root.basedir, "search_index");
1605 //try to create the dir to store the index
1607 if (!Directory.Exists (dir))
1608 Directory.CreateDirectory (dir);
1610 writer = new IndexWriter(Lucene.Net.Store.FSDirectory.GetDirectory(dir, true), new StandardAnalyzer(), true);
1611 } catch (UnauthorizedAccessException) {
1612 //try in the .config directory
1614 dir = Path.Combine (SettingsHandler.Path, "search_index");
1615 if (!Directory.Exists (dir))
1616 Directory.CreateDirectory (dir);
1618 writer = new IndexWriter(Lucene.Net.Store.FSDirectory.GetDirectory(dir, true), new StandardAnalyzer(), true);
1619 } catch (UnauthorizedAccessException) {
1620 Console.WriteLine ("You don't have permissions to write on " + dir);
1625 //Collect all the documents
1626 Console.WriteLine ("Collecting and adding documents...");
1627 foreach (HelpSource hs in root.HelpSources)
1628 hs.PopulateSearchableIndex (writer);
1630 //Optimize and close
1631 Console.WriteLine ("Closing...");
1637 public ICollection HelpSources { get { return new ArrayList(help_sources); } }
1639 [System.Runtime.InteropServices.DllImport ("libc")]
1640 static extern int chmod (string filename, int mode);