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 string osxExternalDir = "/Library/Frameworks/Mono.framework/External/monodoc";
885 string[] osxExternalSources = Directory.Exists (osxExternalDir)
886 ? Directory.GetFiles (osxExternalDir, "*.source")
889 return LoadTree (basedir, doc,
890 Directory.GetFiles (Path.Combine (basedir, "sources"), "*.source")
891 .Concat (osxExternalSources));
895 // Compatibility shim w/ Mono 2.6
896 public static RootTree LoadTree (string indexDir, XmlDocument docTree, IEnumerable sourceFiles)
898 return LoadTree (indexDir, docTree, sourceFiles.Cast<string>());
901 public static RootTree LoadTree (string indexDir, XmlDocument docTree, IEnumerable<string> sourceFiles)
903 if (docTree == null) {
904 docTree = new XmlDocument ();
905 using (var defTree = typeof(RootTree).Assembly.GetManifestResourceStream ("monodoc.xml"))
906 docTree.Load (defTree);
909 sourceFiles = sourceFiles ?? new string [0];
915 RootTree root = new RootTree ();
916 root.basedir = indexDir;
918 XmlNodeList nodes = docTree.SelectNodes ("/node/node");
920 root.name_to_node ["root"] = root;
921 root.name_to_node ["libraries"] = root;
922 root.Populate (root, nodes);
924 Node third_party = root.LookupEntryPoint ("various");
925 if (third_party == null) {
926 Console.Error.WriteLine ("No 'various' doc node! Check monodoc.xml!");
933 foreach (var sourceFile in sourceFiles)
934 root.AddSourceFile (sourceFile);
936 foreach (string path in UncompiledHelpSources) {
937 EcmaUncompiledHelpSource hs = new EcmaUncompiledHelpSource(path);
939 root.help_sources.Add (hs);
940 string epath = "extra-help-source-" + hs.Name;
941 Node hsn = root.CreateNode (hs.Name, "root:/" + epath);
942 root.name_to_hs [epath] = hs;
944 foreach (Node n in hs.Tree.Nodes){
957 public void AddSource (string sources_dir)
959 string [] files = Directory.GetFiles (sources_dir);
961 foreach (string file in files){
962 if (!file.EndsWith (".source"))
964 AddSourceFile (file);
968 public void AddSourceFile (string sourceFile)
970 Node third_party = LookupEntryPoint ("various") ?? this;
972 XmlDocument doc = new XmlDocument ();
974 doc.Load (sourceFile);
977 Console.Error.WriteLine ("Error: Could not load source file {0}", sourceFile);
981 XmlNodeList extra_nodes = doc.SelectNodes ("/monodoc/node");
982 if (extra_nodes.Count > 0)
983 Populate (third_party, extra_nodes);
985 XmlNodeList sources = doc.SelectNodes ("/monodoc/source");
986 if (sources == null){
987 Console.Error.WriteLine ("Error: No <source> section found in the {0} file", sourceFile);
990 foreach (XmlNode source in sources){
991 XmlAttribute a = source.Attributes ["provider"];
993 Console.Error.WriteLine ("Error: no provider in <source>");
996 string provider = a.InnerText;
997 a = source.Attributes ["basefile"];
999 Console.Error.WriteLine ("Error: no basefile in <source>");
1002 string basefile = a.InnerText;
1003 a = source.Attributes ["path"];
1005 Console.Error.WriteLine ("Error: no path in <source>");
1008 string path = a.InnerText;
1010 string basefilepath = Path.Combine (Path.GetDirectoryName (sourceFile), basefile);
1011 HelpSource hs = GetHelpSource (provider, basefilepath);
1015 help_sources.Add (hs);
1016 name_to_hs [path] = hs;
1018 Node parent = LookupEntryPoint (path);
1019 if (parent == null){
1020 Console.Error.WriteLine ("node `{0}' is not defined on the documentation map", path);
1021 parent = third_party;
1024 foreach (Node n in hs.Tree.Nodes){
1031 // Delete nodes which does not have documentaiton (source)
1032 static bool PurgeNode(Node node)
1036 if (!node.Documented)
1038 ArrayList del_child = new ArrayList();
1039 //Delete node unless any child has documentation
1040 bool purged_child = false;
1041 foreach (Node child in node.Nodes)
1043 purged_child = PurgeNode(child);
1046 del_child.Add(child);
1050 // delete the node if all its children are to be deleted
1051 purge = (node.Nodes.Count == del_child.Count);
1054 foreach (Node child in del_child)
1056 node.DelNode(child);
1063 public static string[] GetSupportedFormats ()
1065 return new string[]{
1076 public static HelpSource GetHelpSource (string provider, string basefilepath)
1081 return new EcmaHelpSource (basefilepath, false);
1082 case "ecma-uncompiled":
1083 return new EcmaUncompiledHelpSource (basefilepath);
1085 return new MonoHBHelpSource(basefilepath, false);
1086 case "xhtml": case "hb":
1087 return new XhtmlHelpSource (basefilepath, false);
1089 return new ManHelpSource (basefilepath, false);
1091 return new SimpleHelpSource (basefilepath, false);
1093 return new ErrorHelpSource (basefilepath, false);
1095 return new EcmaSpecHelpSource (basefilepath, false);
1097 return new AddinsHelpSource (basefilepath, false);
1099 Console.Error.WriteLine ("Error: Unknown provider specified: {0}", provider);
1104 catch (FileNotFoundException) {
1105 Console.Error.WriteLine ("Error: did not find one of the files in sources/"+basefilepath);
1110 public static Provider GetProvider (string provider, params string[] basefilepaths)
1114 return new AddinsProvider (basefilepaths [0]);
1116 EcmaProvider p = new EcmaProvider ();
1117 foreach (string d in basefilepaths)
1122 return new EcmaSpecProvider (basefilepaths [0]);
1124 return new ErrorProvider (basefilepaths [0]);
1126 return new ManProvider (basefilepaths);
1128 return new SimpleProvider (basefilepaths [0]);
1131 return new XhtmlProvider (basefilepaths [0]);
1133 throw new NotSupportedException (provider);
1138 // Maintains the name to node mapping
1140 Hashtable name_to_node = new Hashtable ();
1141 Hashtable name_to_hs = new Hashtable ();
1143 void Populate (Node parent, XmlNodeList xml_node_list)
1145 foreach (XmlNode xml_node in xml_node_list){
1146 XmlAttribute e = xml_node.Attributes ["parent"];
1147 if (e != null && name_to_node.ContainsKey (e.InnerText)) {
1148 Node p = (Node) name_to_node [e.InnerText];
1149 xml_node.Attributes.Remove (e);
1150 Populate (p, xml_node.SelectNodes ("."));
1153 e = xml_node.Attributes ["label"];
1155 Console.Error.WriteLine ("`label' attribute missing in <node>");
1158 string label = e.InnerText;
1159 e = xml_node.Attributes ["name"];
1161 Console.Error.WriteLine ("`name' attribute missing in <node>");
1164 string name = e.InnerText;
1166 Node n = parent.LookupNode (label, "root:/" + name);
1168 name_to_node [name] = n;
1169 XmlNodeList children = xml_node.SelectNodes ("./node");
1170 if (children != null)
1171 Populate (n, children);
1175 public Node LookupEntryPoint (string name)
1177 return (Node) name_to_node [name];
1180 ArrayList help_sources;
1181 DateTime lastHelpSourceTime;
1183 RootTree () : base (null, "Mono Documentation", "root:")
1185 nodes = new ArrayList ();
1186 help_sources = new ArrayList ();
1187 lastHelpSourceTime = DateTime.MinValue;
1190 public DateTime LastHelpSourceTime {
1192 return lastHelpSourceTime;
1196 public static bool GetNamespaceAndType (string url, out string ns, out string type)
1200 for (int i = 0; i < url.Length; ++i) {
1219 Console.Error.WriteLine ("Did not find dot in: " + url);
1224 ns = url.Substring (0, nsidx);
1225 type = url.Substring (nsidx + 1);
1227 //Console.Error.WriteLine ("GetNameSpaceAndType (ns={0}, type={1}", ns, type);
1231 public XmlDocument GetHelpXml (string url)
1233 string rest = url.Substring (2);
1236 if (!GetNamespaceAndType (rest, out ns, out type))
1239 foreach (HelpSource hs in help_sources) {
1240 EcmaHelpSource ehs = hs as EcmaHelpSource;
1243 string id = ehs.GetIdFromUrl ("T:", ns, type);
1246 XmlDocument doc = hs.GetHelpXmlWithChanges (id);
1253 public string TypeLookup (string url, out Node match_node)
1255 string rest = Regex.Replace (url, @"^T:\s*", "");
1258 if (!GetNamespaceAndType (rest, out ns, out type)){
1263 foreach (HelpSource hs in help_sources){
1264 string s = hs.RenderTypeLookup ("T:", ns, type, null, out match_node);
1267 lastHelpSourceTime = hs.ZipFileWriteTime;
1275 public string MemberLookup (string prefix, string url, out Node match_node)
1277 string rest = Regex.Replace (url, @"^.:\s*", "");
1279 // Dots in the arg list (for methods) confuse this.
1280 // Chop off the arg list for now and put it back later.
1281 string arglist = "";
1282 int argliststart = rest.IndexOf("(");
1283 if (argliststart >= 0) {
1284 arglist = rest.Substring(argliststart);
1285 rest = rest.Substring(0, argliststart);
1288 string ns_type, member;
1290 if (prefix != "C:") {
1291 int member_idx = rest.LastIndexOf (".");
1293 // The dot in .ctor (if it's a M: link) would confuse this.
1294 if (rest.EndsWith("..ctor")) member_idx--;
1296 ns_type = rest.Substring (0, member_idx);
1297 member = rest.Substring (member_idx + 1);
1299 // C: links don't have the .ctor member part as it would in a M: link
1300 // Even though externally C: links are different from M: links,
1301 // C: links get transformed into M:-style links (with .ctor) here.
1307 //Console.WriteLine ("NS_TYPE: {0} MEMBER: {1}", ns_type, member);
1310 if (!GetNamespaceAndType (ns_type, out ns, out type)){
1315 foreach (HelpSource hs in help_sources){
1316 string s = hs.RenderTypeLookup (prefix, ns, type, member + arglist, out match_node);
1319 lastHelpSourceTime = hs.ZipFileWriteTime;
1327 public Stream GetImage (string url)
1329 if (url.StartsWith ("source-id:")){
1330 string rest = url.Substring (10);
1331 int p = rest.IndexOf (":");
1332 string str_idx = rest.Substring (0, p);
1336 idx = Int32.Parse (str_idx);
1338 Console.Error.WriteLine ("Failed to parse source-id url: {0} `{1}'", url, str_idx);
1342 HelpSource hs = GetHelpSourceFromId (idx);
1343 lastHelpSourceTime = hs.ZipFileWriteTime;
1344 return hs.GetImage (rest.Substring (p + 1));
1346 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly (typeof(RootTree));
1347 return assembly.GetManifestResourceStream (url);
1349 lastHelpSourceTime = DateTime.MinValue;
1353 public HelpSource GetHelpSourceFromId (int id)
1355 return (HelpSource) help_sources [id];
1359 // Fetches the node title
1361 public string GetTitle (string url)
1365 if (url == null || url.StartsWith ("root:"))
1366 return "Mono Documentation";
1368 if (url.Length > 2 && url [1] == ':'){
1371 return url.Substring (2) + " Namespace";
1374 string s = TypeLookup (url, out match_node);
1375 if (match_node != null)
1376 return match_node.Caption;
1377 return url.Substring (2) + " type";
1385 MemberLookup (url.Substring (0,2), url, out match_node);
1386 if (match_node != null)
1387 return match_node.Caption;
1392 return "Mono Documentation";
1397 /// Allows every HelpSource to try to provide the content for this
1400 public string RenderUrl (string url, out Node match_node)
1402 lastHelpSourceTime = DateTime.MinValue;
1403 if (url == "root:") {
1406 // look whether there are contribs
1407 GlobalChangeset chgs = EditingUtils.changes;
1408 StringBuilder con = new StringBuilder ();
1410 //add links to the contrib
1411 int oldContrib = 0, contribs = 0;
1412 con.Append ("<ul>");
1413 foreach (DocSetChangeset dscs in chgs.DocSetChangesets)
1414 foreach (FileChangeset fcs in dscs.FileChangesets)
1415 foreach (Change c in fcs.Changes) {
1416 if (c.NodeUrl == null) {
1417 if (c.Serial == SettingsHandler.Settings.SerialNumber)
1419 } else if (c.Serial == SettingsHandler.Settings.SerialNumber) {
1421 con.Append (String.Format ("<li><a href=\"{0}\">{0}</a></li>", c.NodeUrl));
1425 string contrib = (oldContrib + contribs) == 1?"There is {0} contribution":"There are {0} contributions";
1426 con.Insert (0, String.Format (contrib, oldContrib + contribs) + " pending upload <i>(Contributing--> Upload)</i>", 1);
1427 con.Append ("</ul>");
1428 if (oldContrib == 1)
1429 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>");
1430 else if (oldContrib > 1)
1431 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>");
1433 //start the rendering
1434 if (!HelpSource.use_css) {
1435 StringBuilder sb = new StringBuilder ("<table bgcolor=\"#b0c4de\" width=\"100%\" cellpadding=\"5\"><tr><td><h3>Mono Documentation Library</h3></td></tr></table>");
1437 foreach (Node n in Nodes)
1438 sb.AppendFormat ("<a href='{0}'>{1}</a><br/>", n.Element, n.Caption);
1441 sb.Append ("<br><table bgcolor=\"#fff3f3\" width=\"100%\" cellpadding=\"5\"><tr><td>");
1442 sb.Append ("<h5>Contributions</h5><br>");
1443 if ((oldContrib + contribs) == 0) {
1444 sb.Append ("<p><b>You have not made any contributions yet.</b></p>");
1445 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>");
1446 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>");
1448 sb.Append (con.ToString ());
1450 sb.Append ("</td></tr></table>");
1451 return sb.ToString ();
1453 if (home_cache == null) {
1454 System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly (typeof (HelpSource));
1455 Stream hp_stream = assembly.GetManifestResourceStream ("home.html");
1456 home_cache = (new StreamReader (hp_stream)).ReadToEnd ();
1458 StringBuilder sb = new StringBuilder (home_cache);
1460 sb.Replace ("@@FONT_FAMILY@@", SettingsHandler.Settings.preferred_font_family);
1461 sb.Replace ("@@FONT_SIZE@@", SettingsHandler.Settings.preferred_font_size.ToString());
1463 var visible = SettingsHandler.Settings.EnableEditing ? "block;" : "none;";
1464 if ((oldContrib + contribs) == 0) {
1465 sb.Replace ("@@CONTRIB_DISP@@", "display: none;");
1466 sb.Replace ("@@NO_CONTRIB_DISP@@", "display: " + visible);
1468 sb.Replace ("@@CONTRIB_DISP@@", "display: " + visible);
1469 sb.Replace ("@@NO_CONTRIB_DISP@@", "display: none;");
1470 sb.Replace ("@@CONTRIBS@@", con.ToString ());
1472 sb.Replace ("@@EDITING_ENABLED@@", "display: " + visible);
1474 // load the url of nodes
1476 StringBuilder urls = new StringBuilder ();
1477 foreach (Node n in Nodes) {
1478 add_str = String.Format ("<li><a href=\"{0}\">{1}</a></li>", n.Element, n.Caption);
1479 urls.Append (add_str);
1481 sb.Replace ("@@API_DOCS@@", urls.ToString ());
1483 return sb.ToString ();
1487 if (url.StartsWith ("root:")) {
1488 match_node = ((Node)name_to_node [url.Substring (6)]);
1489 HelpSource hs = ((HelpSource)name_to_hs [url.Substring (6)]);
1492 return GenerateNodeIndex(match_node);
1496 lastHelpSourceTime = hs.ZipFileWriteTime;
1497 return hs.GetText ("root:", out dummy);
1501 if (url.StartsWith ("source-id:")){
1502 string rest = url.Substring (10);
1503 int p = rest.IndexOf (":");
1504 string str_idx = rest.Substring (0, p);
1508 idx = Int32.Parse (str_idx);
1510 Console.Error.WriteLine ("Failed to parse source-id url: {0} `{1}'", url, str_idx);
1514 HelpSource hs = (HelpSource) help_sources [idx];
1515 // Console.WriteLine ("Attempting to get docs from: " + rest.Substring (p + 1));
1516 lastHelpSourceTime = hs.ZipFileWriteTime;
1517 return hs.GetText (rest.Substring (p + 1), out match_node);
1520 if (url.Length < 2){
1525 string prefix = url.Substring (0, 2);
1527 switch (prefix.ToUpper ()){
1529 foreach (HelpSource hs in help_sources){
1530 string s = hs.RenderNamespaceLookup (url, out match_node);
1532 lastHelpSourceTime = hs.ZipFileWriteTime;
1540 return TypeLookup (url, out match_node);
1548 return MemberLookup (prefix, url, out match_node);
1551 foreach (HelpSource hs in help_sources){
1552 string s = hs.GetText (url, out match_node);
1555 lastHelpSourceTime = hs.ZipFileWriteTime;
1564 public string GenerateNodeIndex (Node node)
1566 StringBuilder buf = new StringBuilder();
1567 buf.AppendFormat("<H3>{0}</H3>", node.Caption);
1569 foreach (Node child in node.Nodes)
1571 buf.AppendFormat("<li><a href=\"{0}\">{1}</a>", child.URL, child.Caption);
1573 buf.Append("</ul>");
1574 return buf.ToString();
1577 public IndexReader GetIndex ()
1579 //try to load from basedir
1580 string index_file = Path.Combine (basedir, "monodoc.index");
1581 if (File.Exists (index_file))
1582 return IndexReader.Load (index_file);
1583 //then, try to load from config dir
1584 index_file = Path.Combine (SettingsHandler.Path, "monodoc.index");
1585 return IndexReader.Load (index_file);
1589 public static void MakeIndex ()
1591 RootTree root = LoadTree ();
1595 IndexMaker index_maker = new IndexMaker ();
1597 foreach (HelpSource hs in root.help_sources){
1598 hs.PopulateIndex (index_maker);
1601 // if the user has no write permissions use config dir
1602 string path = Path.Combine (root.basedir, "monodoc.index");
1604 index_maker.Save (path);
1605 } catch (System.UnauthorizedAccessException) {
1606 path = Path.Combine (SettingsHandler.Path, "monodoc.index");
1608 index_maker.Save (path);
1609 } catch (System.UnauthorizedAccessException) {
1610 Console.WriteLine ("Unable to write index file in {0}", Path.Combine (SettingsHandler.Path, "monodoc.index"));
1616 // No octal in C#, how lame is that
1617 chmod (path, 0x1a4);
1619 Console.WriteLine ("Documentation index updated");
1622 static bool IsUnix {
1624 int p = (int) Environment.OSVersion.Platform;
1625 return ((p == 4) || (p == 128) || (p == 6));
1630 public SearchableIndex GetSearchIndex ()
1632 //try to load from basedir
1633 string index_file = Path.Combine (basedir, "search_index");
1634 if (Directory.Exists (index_file))
1635 return SearchableIndex.Load (index_file);
1636 //then, try to load from config dir
1637 index_file = Path.Combine (SettingsHandler.Path, "search_index");
1638 return SearchableIndex.Load (index_file);
1641 public static void MakeSearchIndex ()
1643 // Loads the RootTree
1644 Console.WriteLine ("Loading the monodoc tree...");
1645 RootTree root = LoadTree ();
1649 string dir = Path.Combine (root.basedir, "search_index");
1651 //try to create the dir to store the index
1653 if (!Directory.Exists (dir))
1654 Directory.CreateDirectory (dir);
1656 writer = new IndexWriter(Lucene.Net.Store.FSDirectory.GetDirectory(dir, true), new StandardAnalyzer(), true);
1657 } catch (UnauthorizedAccessException) {
1658 //try in the .config directory
1660 dir = Path.Combine (SettingsHandler.Path, "search_index");
1661 if (!Directory.Exists (dir))
1662 Directory.CreateDirectory (dir);
1664 writer = new IndexWriter(Lucene.Net.Store.FSDirectory.GetDirectory(dir, true), new StandardAnalyzer(), true);
1665 } catch (UnauthorizedAccessException) {
1666 Console.WriteLine ("You don't have permissions to write on " + dir);
1671 //Collect all the documents
1672 Console.WriteLine ("Collecting and adding documents...");
1673 foreach (HelpSource hs in root.HelpSources)
1674 hs.PopulateSearchableIndex (writer);
1676 //Optimize and close
1677 Console.WriteLine ("Closing...");
1683 public ICollection HelpSources { get { return new ArrayList(help_sources); } }
1685 [System.Runtime.InteropServices.DllImport ("libc")]
1686 static extern int chmod (string filename, int mode);