Merge pull request #495 from nicolas-raoul/fix-for-issue2907-with-no-formatting-changes
[mono.git] / mcs / tools / monodoc / Monodoc / provider.cs
index 4cb2727b9dba89387a59de845138b12d799f00b6..180ae0d1f4eb2c6b158cba2780a5221680099aca 100644 (file)
@@ -5,6 +5,8 @@
 //   Miguel de Icaza (miguel@ximian.com)
 //
 // (C) 2002, Ximian, Inc.
+// Copyright 2003-2011 Novell
+// Copyright 2011 Xamarin Inc
 //
 // TODO:
 //   Each node should have a provider link
 //
 namespace Monodoc {
 using System;
+using System.Collections.Generic;
 using System.IO;
+using System.Linq;
 using System.Text;
 using System.Text.RegularExpressions;
 using System.Collections;
 using System.Diagnostics;
 using System.Configuration;
-using System.Text.RegularExpressions;
+using System.Reflection;
 using System.Xml;
 using System.Xml.XPath;
 using ICSharpCode.SharpZipLib.Zip;
 
-using Monodoc.Lucene.Net.Index;
-using Monodoc.Lucene.Net.Analysis.Standard;
+using Mono.Lucene.Net.Index;
+using Mono.Lucene.Net.Analysis.Standard;
 
 using Mono.Documentation;
 
@@ -390,13 +394,24 @@ public class Node : IComparable {
                if (other.position < 0)
                        other.LoadNode ();
 
-               Regex digits = new Regex (@"([\d]+)|([^\d]+)");
-               MatchEvaluator eval = delegate (Match m) {
-                       return (m.Value.Length > 0 && char.IsDigit (m.Value [0])) 
-                               ? m.Value.PadLeft (System.Math.Max (caption.Length, other.caption.Length)) 
-                               : m.Value;
-               };
-               return digits.Replace (caption, eval).CompareTo (digits.Replace (other.caption, eval));
+               var cap1 = caption;
+               var cap2 = other.caption;
+
+               /* Some node (notably from ecmaspec) have number prepended to them
+                * which we need to sort better by padding them to the same number
+                * of digits
+                */
+               if (char.IsDigit (cap1[0]) && char.IsDigit (cap2[0])) {
+                       int c1 = cap1.TakeWhile (char.IsDigit).Count ();
+                       int c2 = cap2.TakeWhile (char.IsDigit).Count ();
+
+                       if (c1 != c2) {
+                               cap1 = cap1.PadLeft (cap1.Length + Math.Max (0, c2 - c1), '0');
+                               cap2 = cap2.PadLeft (cap2.Length + Math.Max (0, c1 - c2), '0');
+                       }
+               }
+
+               return string.Compare (cap1, cap2, StringComparison.OrdinalIgnoreCase);
        }
 }
 
@@ -443,13 +458,15 @@ public class HelpSource {
        int source_id;
        DateTime zipFileWriteTime;
        string name;
+       string basepath;
        TraceLevel trace_level = TraceLevel.Warning;
-       bool nozip;
-       string base_dir;
+       protected bool nozip;
+       protected string base_dir;
 
        public HelpSource (string base_filename, bool create)
        {
                this.name = Path.GetFileName (base_filename);
+               this.basepath = Path.GetDirectoryName (base_filename);
                tree_filename = base_filename + ".tree";
                zip_filename = base_filename + ".zip";
                base_dir = XmlDocUtils.GetCacheDirectory (base_filename);
@@ -494,10 +511,23 @@ public class HelpSource {
                }
        }
 
+       /* This gives the full path of the source/ directory */
+       public string BaseFilePath {
+               get {
+                       return basepath;
+               }
+       }
+
        public TraceLevel TraceLevel {
                get { return trace_level; }
                set { trace_level = value; }
        }
+
+       public string BaseDir {
+               get {
+                       return base_dir;
+               }
+       }
        
        ZipFile zip_file;
        
@@ -537,7 +567,7 @@ public class HelpSource {
        {
                if (nozip) {
                        Stream s = File.OpenRead (XmlDocUtils.GetCachedFileName (base_dir, id));
-                       string url = "monodoc:///" + SourceID + "@" + System.Web.HttpUtility.UrlEncode (id) + "@";
+                       string url = "monodoc:///" + SourceID + "@" + Uri.EscapeUriString (id) + "@";
                        return new XmlTextReader (url, s);
                }
 
@@ -547,7 +577,7 @@ public class HelpSource {
                ZipEntry entry = zip_file.GetEntry (id);
                if (entry != null) {
                        Stream s = zip_file.GetInputStream (entry);
-                       string url = "monodoc:///" + SourceID + "@" + System.Web.HttpUtility.UrlEncode (id) + "@";
+                       string url = "monodoc:///" + SourceID + "@" + Uri.EscapeUriString (id) + "@";
                        return new XmlTextReader (url, s);
                }
                return null;
@@ -557,7 +587,7 @@ public class HelpSource {
        {
                if (nozip) {
                        Stream s = File.OpenRead (XmlDocUtils.GetCachedFileName (base_dir, id));
-                       string url = "monodoc:///" + SourceID + "@" + System.Web.HttpUtility.UrlEncode (id) + "@";
+                       string url = "monodoc:///" + SourceID + "@" + Uri.EscapeUriString (id) + "@";
                        XmlReader r = new XmlTextReader (url, s);
                        XmlDocument ret = new XmlDocument ();
                        ret.Load (r);
@@ -570,7 +600,7 @@ public class HelpSource {
                ZipEntry entry = zip_file.GetEntry (id);
                if (entry != null) {
                        Stream s = zip_file.GetInputStream (entry);
-                       string url = "monodoc:///" + SourceID + "@" + System.Web.HttpUtility.UrlEncode (id) + "@";
+                       string url = "monodoc:///" + SourceID + "@" + Uri.EscapeUriString (id) + "@";
                        XmlReader r = new XmlTextReader (url, s);
                        XmlDocument ret = new XmlDocument ();
                        ret.Load (r);
@@ -851,6 +881,8 @@ public class RootTree : Tree {
        {
                return LoadTree (null);
        }
+
+       const string MacMonoDocDir = "/Library/Frameworks/Mono.framework/Versions/Current/lib/monodoc";
        
        //
        // Loads the tree layout
@@ -868,18 +900,57 @@ public class RootTree : Tree {
                                d.Load (cfgFile);
                                basedir = d.SelectSingleNode ("config/path").Attributes ["docsPath"].Value;
                        }
+                       // Temporary workaround for developers distributing a monodoc.dll themselves on Mac
+                       if (Directory.Exists (MacMonoDocDir)){
+                               Console.WriteLine ("MacDir exists");
+                               if (!File.Exists (Path.Combine (basedir, "monodoc.xml"))){
+                                       basedir = MacMonoDocDir;
+                               }
+                       }
                }
-               XmlDocument doc = new XmlDocument ();
 
-               RootTree root = new RootTree ();
-               root.basedir = basedir;
-               
                //
                // Load the layout
                //
+               XmlDocument doc = new XmlDocument ();
                string layout = Path.Combine (basedir, "monodoc.xml");
                doc.Load (layout);
-               XmlNodeList nodes = doc.SelectNodes ("/node/node");
+
+               string osxExternalDir = "/Library/Frameworks/Mono.framework/External/monodoc";
+               string[] osxExternalSources = Directory.Exists (osxExternalDir)
+                       ? Directory.GetFiles (osxExternalDir, "*.source")
+                       : new string[0];
+
+               return LoadTree (basedir, doc, 
+                               Directory.GetFiles (Path.Combine (basedir, "sources"), "*.source")
+                               .Concat (osxExternalSources));
+       }
+
+       // Compatibility shim w/ Mono 2.6
+       public static RootTree LoadTree (string indexDir, XmlDocument docTree, IEnumerable sourceFiles)
+       {
+               return LoadTree (indexDir, docTree, sourceFiles.Cast<string>());
+       }
+
+       public static RootTree LoadTree (string indexDir, XmlDocument docTree, IEnumerable<string> sourceFiles)
+       {
+               if (docTree == null) {
+                       docTree = new XmlDocument ();
+                       using (var defTree = typeof(RootTree).Assembly.GetManifestResourceStream ("monodoc.xml"))
+                               docTree.Load (defTree);
+               }
+
+
+               sourceFiles = sourceFiles ?? new string [0];
+
+               //
+               // Load the layout
+               //
+
+               RootTree root = new RootTree ();
+               root.basedir = indexDir;
+
+               XmlNodeList nodes = docTree.SelectNodes ("/node/node");
 
                root.name_to_node ["root"] = root;
                root.name_to_node ["libraries"] = root;
@@ -894,7 +965,8 @@ public class RootTree : Tree {
                //
                // Load the sources
                //
-               root.AddSource (Path.Combine (basedir, "sources"));
+               foreach (var sourceFile in sourceFiles)
+                       root.AddSourceFile (sourceFile);
                
                foreach (string path in UncompiledHelpSources) {
                        EcmaUncompiledHelpSource hs = new EcmaUncompiledHelpSource(path);
@@ -919,70 +991,81 @@ public class RootTree : Tree {
 
        public void AddSource (string sources_dir)
        {
-               Node third_party = LookupEntryPoint ("various") ?? this;
-
                string [] files = Directory.GetFiles (sources_dir);
 
                foreach (string file in files){
                        if (!file.EndsWith (".source"))
                                continue;
+                       AddSourceFile (file);
+               }
+       }
 
-                       XmlDocument doc = new XmlDocument ();
-                       try {
-                               doc.Load (file);
-                       } catch {
-                               Console.Error.WriteLine ("Error: Could not load source file {0}", file);
+       Dictionary<string,string> loadedSourceFiles = new Dictionary<string,string> ();
+       
+       public void AddSourceFile (string sourceFile)
+       {
+               if (loadedSourceFiles.ContainsKey (sourceFile))
+                       return;
+               
+               Node third_party = LookupEntryPoint ("various") ?? this;
+
+               XmlDocument doc = new XmlDocument ();
+               try {
+                       doc.Load (sourceFile);
+               }
+               catch {
+                       Console.Error.WriteLine ("Error: Could not load source file {0}", sourceFile);
+                       return;
+               }
+
+               XmlNodeList extra_nodes = doc.SelectNodes ("/monodoc/node");
+               if (extra_nodes.Count > 0)
+                       Populate (third_party, extra_nodes);
+
+               XmlNodeList sources = doc.SelectNodes ("/monodoc/source");
+               if (sources == null){
+                       Console.Error.WriteLine ("Error: No <source> section found in the {0} file", sourceFile);
+                       return;
+               }
+               loadedSourceFiles [sourceFile] = sourceFile;
+               foreach (XmlNode source in sources){
+                       XmlAttribute a = source.Attributes ["provider"];
+                       if (a == null){
+                               Console.Error.WriteLine ("Error: no provider in <source>");
                                continue;
                        }
+                       string provider = a.InnerText;
+                       a = source.Attributes ["basefile"];
+                       if (a == null){
+                               Console.Error.WriteLine ("Error: no basefile in <source>");
+                               continue;
+                       }
+                       string basefile = a.InnerText;
+                       a = source.Attributes ["path"];
+                       if (a == null){
+                               Console.Error.WriteLine ("Error: no path in <source>");
+                               continue;
+                       }
+                       string path = a.InnerText;
 
-                       XmlNodeList extra_nodes = doc.SelectNodes ("/monodoc/node");
-                       if (extra_nodes.Count > 0)
-                               Populate (third_party, extra_nodes);
-
-                       XmlNodeList sources = doc.SelectNodes ("/monodoc/source");
-                       if (sources == null){
-                               Console.Error.WriteLine ("Error: No <source> section found in the {0} file", file);
+                       string basefilepath = Path.Combine (Path.GetDirectoryName (sourceFile), basefile);
+                       HelpSource hs = GetHelpSource (provider, basefilepath);
+                       if (hs == null)
                                continue;
+                       hs.RootTree = this;
+                       help_sources.Add (hs);
+                       name_to_hs [path] = hs;
+
+                       Node parent = LookupEntryPoint (path);
+                       if (parent == null){
+                               Console.Error.WriteLine ("node `{0}' is not defined on the documentation map", path);
+                               parent = third_party;
                        }
-                       foreach (XmlNode source in sources){
-                               XmlAttribute a = source.Attributes ["provider"];
-                               if (a == null){
-                                       Console.Error.WriteLine ("Error: no provider in <source>");
-                                       continue;
-                               }
-                               string provider = a.InnerText;
-                               a = source.Attributes ["basefile"];
-                               if (a == null){
-                                       Console.Error.WriteLine ("Error: no basefile in <source>");
-                                       continue;
-                               }
-                               string basefile = a.InnerText;
-                               a = source.Attributes ["path"];
-                               if (a == null){
-                                       Console.Error.WriteLine ("Error: no path in <source>");
-                                       continue;
-                               }
-                               string path = a.InnerText;
-
-                               string basefilepath = Path.Combine (sources_dir, basefile);
-                               HelpSource hs = GetHelpSource (provider, basefilepath);
-                               if (hs == null)
-                                       continue;
-                               hs.RootTree = this;
-                               help_sources.Add (hs);
-                               name_to_hs [path] = hs;
-
-                               Node parent = LookupEntryPoint (path);
-                               if (parent == null){
-                                       Console.Error.WriteLine ("node `{0}' is not defined on the documentation map", path);
-                                       parent = third_party;
-                               }
 
-                               foreach (Node n in hs.Tree.Nodes){
-                                       parent.AddNode (n);
-                               }
-                               parent.Sort ();
+                       foreach (Node n in hs.Tree.Nodes){
+                               parent.AddNode (n);
                        }
+                       parent.Sort ();
                }
        }
        
@@ -1150,7 +1233,7 @@ public class RootTree : Tree {
                        return lastHelpSourceTime;
                }
        }
-       
+
        public static bool GetNamespaceAndType (string url, out string ns, out string type)
        {
                int nsidx = -1;
@@ -1174,7 +1257,6 @@ public class RootTree : Tree {
                }
 
                if (nsidx == -1) {
-                       Console.Error.WriteLine ("Did not find dot in: " + url);
                        ns = null;
                        type = null;
                        return false;
@@ -1418,12 +1500,16 @@ public class RootTree : Tree {
                                sb.Replace ("@@FONT_FAMILY@@", SettingsHandler.Settings.preferred_font_family);
                                sb.Replace ("@@FONT_SIZE@@", SettingsHandler.Settings.preferred_font_size.ToString());
                                //contributions
+                               var visible = SettingsHandler.Settings.EnableEditing ? "block;" : "none;";
                                if ((oldContrib + contribs) == 0) {
                                        sb.Replace ("@@CONTRIB_DISP@@", "display: none;");
+                                        sb.Replace ("@@NO_CONTRIB_DISP@@", "display: " + visible);
                                } else {
+                                       sb.Replace ("@@CONTRIB_DISP@@", "display: " + visible);
                                        sb.Replace ("@@NO_CONTRIB_DISP@@", "display: none;");
                                        sb.Replace ("@@CONTRIBS@@", con.ToString ());
                                }
+                               sb.Replace ("@@EDITING_ENABLED@@", "display: " + visible);
                                        
                                // load the url of nodes
                                String add_str;
@@ -1542,7 +1628,11 @@ public class RootTree : Tree {
 
        public static void MakeIndex ()
        {
-               RootTree root = LoadTree ();
+               MakeIndex (LoadTree ());
+       }
+
+       public static void MakeIndex (RootTree root)
+       {
                if (root == null)
                        return;
 
@@ -1570,7 +1660,7 @@ public class RootTree : Tree {
                        // No octal in C#, how lame is that
                        chmod (path, 0x1a4);
                }
-               Console.WriteLine ("Documentation index updated");
+               Console.WriteLine ("Documentation index at {0} updated", path);
        }
 
        static bool IsUnix {
@@ -1593,10 +1683,15 @@ public class RootTree : Tree {
        }
 
        public static void MakeSearchIndex ()
+       {
+               MakeSearchIndex (LoadTree ());
+       }
+
+       public static void MakeSearchIndex (RootTree root)
        {
                // Loads the RootTree
                Console.WriteLine ("Loading the monodoc tree...");
-               RootTree root = LoadTree ();
+
                if (root == null)
                        return;
 
@@ -1607,7 +1702,7 @@ public class RootTree : Tree {
                        if (!Directory.Exists (dir)) 
                                Directory.CreateDirectory (dir);
 
-                       writer = new IndexWriter(Lucene.Net.Store.FSDirectory.GetDirectory(dir, true), new StandardAnalyzer(), true);
+                       writer = new IndexWriter(Mono.Lucene.Net.Store.FSDirectory.GetDirectory(dir, true), new StandardAnalyzer(), true);
                } catch (UnauthorizedAccessException) {
                        //try in the .config directory
                        try {
@@ -1615,7 +1710,7 @@ public class RootTree : Tree {
                                if (!Directory.Exists (dir)) 
                                        Directory.CreateDirectory (dir);
 
-                               writer = new IndexWriter(Lucene.Net.Store.FSDirectory.GetDirectory(dir, true), new StandardAnalyzer(), true);
+                               writer = new IndexWriter(Mono.Lucene.Net.Store.FSDirectory.GetDirectory(dir, true), new StandardAnalyzer(), true);
                        } catch (UnauthorizedAccessException) {
                                Console.WriteLine ("You don't have permissions to write on " + dir);
                                return;