//=====================================================================

            /// <summary>
            /// Constructor
            /// </summary>
            /// <param name="cache">The cache with which this indexed document is associated</param>
            /// <param name="filename">The name of the XML comments file to index</param>
            public IndexedCommentsFile(IndexedCommentsCache cache, string filename)
            {
                foreach (var kv in cache.GetValues(filename))
                {
                    index[kv.Key] = kv.Value;
                }
            }
        /// <summary>
        /// This is called if indexing completes successfully
        /// </summary>
        /// <param name="cache">The index cache</param>
        private void IndexCompleted(IndexedCommentsCache cache)
        {
            this.DisposeOfTask();

            spIndexingPanel.Visibility = Visibility.Collapsed;

            codeEntities = new List<string>(cache.AllKeys);

            if(cboEntityType.SelectedIndex == (int)EntityType.CodeEntity)
            {
                txtFindName.IsReadOnly = false;
                txtFindName.Text = "<Enter text or reg ex to find here, then click Go>";
                txtFindName.SelectAll();
                tvEntities.IsEnabled = btnGo.IsEnabled = true;
            }
        }
        /// <summary>
        /// This is the method that indexes the comments files
        /// </summary>
        /// <remarks>Rather than a partial build, we'll just index the comments files.</remarks>
        private IndexedCommentsCache IndexComments()
        {
            HashSet<string> projectDictionary = new HashSet<string>();
            IndexedCommentsCache cache = new IndexedCommentsCache(100);
            MSBuildProject projRef;
            string lastSolution = null;

            // Index the framework comments based on the framework version in the project
            FrameworkSettings frameworkSettings = FrameworkDictionary.AllFrameworks.GetFrameworkWithRedirect(
                currentProject.FrameworkVersion);

            if(frameworkSettings == null)
                throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
                    "Unable to locate information for a framework version or its redirect: {0}",
                    currentProject.FrameworkVersion));

            foreach(var location in frameworkSettings.CommentsFileLocations(currentProject.Language))
            {
                indexTokenSource.Token.ThrowIfCancellationRequested();

                cache.IndexCommentsFiles(Path.GetDirectoryName(location), Path.GetFileName(location),
                    false, null);
            }

            // Index the comments file documentation sources
            foreach(string file in currentProject.DocumentationSources.SelectMany(ds => ds.CommentsFiles))
            {
                indexTokenSource.Token.ThrowIfCancellationRequested();

                cache.IndexCommentsFiles(Path.GetDirectoryName(file), Path.GetFileName(file), false, null);
            }

            // Also, index the comments files in project documentation sources
            foreach(DocumentationSource ds in currentProject.DocumentationSources)
                foreach(var sourceProject in ds.Projects(
                  !String.IsNullOrEmpty(ds.Configuration) ? ds.Configuration : currentProject.Configuration,
                  !String.IsNullOrEmpty(ds.Platform) ? ds.Platform : currentProject.Platform))
                {
                    indexTokenSource.Token.ThrowIfCancellationRequested();

                    // NOTE: This code should be similar to the code in BuildProcess.ValidateDocumentationSources!

                    // Solutions are followed by the projects that they contain
                    if(sourceProject.ProjectFileName.EndsWith(".sln", StringComparison.OrdinalIgnoreCase))
                    {
                        lastSolution = sourceProject.ProjectFileName;
                        continue;
                    }

                    // Ignore projects that we've already seen
                    if(projectDictionary.Add(sourceProject.ProjectFileName))
                    {
                        using(projRef = new MSBuildProject(sourceProject.ProjectFileName))
                        {
                            // Use the project file configuration and platform properties if they are set.  If
                            // not, use the documentation source values.  If they are not set, use the SHFB
                            // project settings.
                            projRef.SetConfiguration(
                                !String.IsNullOrEmpty(sourceProject.Configuration) ? sourceProject.Configuration :
                                    !String.IsNullOrEmpty(ds.Configuration) ? ds.Configuration : currentProject.Configuration,
                                !String.IsNullOrEmpty(sourceProject.Platform) ? sourceProject.Platform :
                                    !String.IsNullOrEmpty(ds.Platform) ? ds.Platform : currentProject.Platform,
                                currentProject.MSBuildOutDir, false);

                            // Add Visual Studio solution macros if necessary
                            if(lastSolution != null)
                                projRef.SetSolutionMacros(lastSolution);

                            if(!String.IsNullOrEmpty(projRef.XmlCommentsFile))
                                cache.IndexCommentsFiles(Path.GetDirectoryName(projRef.XmlCommentsFile),
                                    Path.GetFileName(projRef.XmlCommentsFile), false, null);
                        }
                    }
                }

            return cache;
        }
        /// <summary>
        /// This is called if indexing completes successfully
        /// </summary>
        /// <param name="cache">The index cache</param>
        private void IndexCompleted(IndexedCommentsCache cache)
        {
            this.DisposeOfTask();

            spIndexingPanel.Visibility = Visibility.Collapsed;

            codeEntities = new List<string>(cache.AllKeys);

            // Add an entry for the root namespace container
            codeEntities.Add("R:Project_" + currentProject.HtmlHelpName.Replace(" ", "_"));

            if(cboEntityType.SelectedIndex == (int)EntityType.CodeEntity)
            {
                txtFindName.IsReadOnly = false;
                txtFindName.Text = "<Enter text or reg ex to find here, then click Go>";
                txtFindName.SelectAll();
                tvEntities.IsEnabled = btnGo.IsEnabled = true;
            }
        }
            //=====================================================================

            /// <summary>
            /// Constructor
            /// </summary>
            /// <param name="cache">The cache with which this indexed document is associated</param>
            /// <param name="filename">The name of the XML comments file to index</param>
            public IndexedCommentsFile(IndexedCommentsCache cache, string filename)
            {
                foreach(var kv in cache.GetValues(filename))
                    index[kv.Key] = kv.Value;
            }
        //=====================================================================
        /// <summary>
        /// Load the configuration file
        /// </summary>
        /// <param name="configFile">The configuration file to load</param>
        /// <exception cref="ArgumentException">This is thrown if the
        /// configuration file does not exist.</exception>
        /// <exception cref="ConfigurationErrorsException">This is thrown if
        /// the configuration file is not valid.</exception>
        private static void LoadConfiguration(string configFile)
        {
            XPathDocument config, xpathDoc;
            XPathNavigator navConfig, navComments, element, refInfo;
            string path, wildcard, attrValue;
            bool recurse;
            int cacheSize = 100;

            if(!File.Exists(configFile))
                throw new ArgumentException("Configuration file not found: " + configFile, "configFile");

            config = new XPathDocument(configFile);
            navConfig = config.CreateNavigator();

            // Show duplicate key warnings?
            showDupWarning = (navConfig.SelectSingleNode("configuration/showDuplicateWarning") != null);

            // Get the reflection information file
            element = navConfig.SelectSingleNode("configuration/reflectionInfo/@file");

            if(element == null || !File.Exists(element.Value))
                throw new ConfigurationErrorsException("The reflectionFile " +
                    "element is missing or the specified file does not exist");

            Console.WriteLine("Reflection information will be retrieved from '{0}'", element.Value);
            xpathDoc = new XPathDocument(element.Value);
            refInfo = xpathDoc.CreateNavigator();
            apisNode = refInfo.SelectSingleNode("reflection/apis");

            // Get the inherited documentation filename
            element = navConfig.SelectSingleNode("configuration/inheritedDocs/@file");

            if(element == null)
                throw new ConfigurationErrorsException("The inheritedDocs " +
                    "element does not exist or is not valid");

            inheritedDocsFilename = element.Value;
            Console.WriteLine("Inherited documentation will be written to '{0}'", inheritedDocsFilename);

            // Load the comments file information
            navComments = navConfig.SelectSingleNode("configuration/commentsFiles");

            attrValue = navComments.GetAttribute("cacheSize", String.Empty);

            if(attrValue.Length != 0)
                cacheSize = Convert.ToInt32(attrValue, CultureInfo.InvariantCulture);

            commentsCache = new IndexedCommentsCache(cacheSize);
            commentsCache.ReportWarning += commentsCache_ReportWarning;

            foreach(XPathNavigator nav in navComments.Select("*"))
            {
                path = nav.GetAttribute("path", String.Empty);
                wildcard = nav.GetAttribute("file", String.Empty);
                attrValue = nav.GetAttribute("recurse", String.Empty);

                if(path.Length == 0)
                {
                    path = Path.GetDirectoryName(wildcard);
                    wildcard = Path.GetFileName(wildcard);
                }

                path = Environment.ExpandEnvironmentVariables(path);

                if(wildcard.Length != 0)
                    wildcard = Environment.ExpandEnvironmentVariables(wildcard);
                else
                    wildcard = "*.xml";

                if(attrValue.Length == 0)
                    recurse = false;
                else
                    recurse = Convert.ToBoolean(attrValue, CultureInfo.InvariantCulture);

                Console.WriteLine("Indexing {0}\\{1}", path, wildcard);
                commentsCache.IndexCommentsFiles(path, wildcard, recurse, (nav.Name == "scan") ? commentsFiles : null);
            }

            if(commentsCache.FilesIndexed == 0 || commentsCache.IndexCount == 0)
                throw new ConfigurationErrorsException("No comments files were specified or they did not " +
                    "contain valid information to index");

            if(commentsFiles.Count == 0)
                throw new ConfigurationErrorsException("No comments files were specified to scan for " +
                    "<inheritdoc /> tags.");

            Console.WriteLine("Indexed {0} members in {1} file(s).  {2} file(s) to scan for <inheritdoc /> tags.",
                commentsCache.IndexCount, commentsCache.FilesIndexed, commentsFiles.Count);
        }
Beispiel #7
0
        //=====================================================================

        /// <summary>
        /// Load the configuration file
        /// </summary>
        /// <param name="configFile">The configuration file to load</param>
        /// <exception cref="ArgumentException">This is thrown if the configuration file does not exist or is not valid</exception>
        private void LoadConfiguration(string configFile)
        {
            XPathDocument  config;
            XPathNavigator navConfig, navComments, element;
            string         path, wildcard, attrValue;
            bool           recurse;
            int            cacheSize = 100;

            if (!File.Exists(configFile))
            {
                throw new ArgumentException("Configuration file not found: " + configFile, nameof(configFile));
            }

            using (var reader = XmlReader.Create(configFile, new XmlReaderSettings {
                CloseInput = true
            }))
            {
                config    = new XPathDocument(reader);
                navConfig = config.CreateNavigator();

                // Show duplicate key warnings?
                showDupWarning = (navConfig.SelectSingleNode("configuration/showDuplicateWarning") != null);

                // Get the reflection information files
                reflectionFiles = new ReflectionFiles();

                foreach (XPathNavigator refFile in navConfig.Select("configuration/reflectionInfo/@file"))
                {
                    if (!File.Exists(refFile.Value))
                    {
                        throw new ArgumentException("The reflectionFile element's target file '" +
                                                    refFile.Value + "' does not exist", nameof(configFile));
                    }

                    this.Log.LogMessage("Reflection information will be retrieved from '{0}'", refFile.Value);
                    reflectionFiles.AddReflectionFile(refFile.Value);
                }

                if (reflectionFiles.Count == 0)
                {
                    throw new ArgumentException("At least one reflectionFile element is required", nameof(configFile));
                }

                // Get the inherited documentation filename
                element = navConfig.SelectSingleNode("configuration/inheritedDocs/@file");

                if (element == null)
                {
                    throw new ArgumentException("The inheritedDocs element does not exist or is not valid", nameof(configFile));
                }

                inheritedDocsFilename = element.Value;
                this.Log.LogMessage("Inherited documentation will be written to '{0}'", inheritedDocsFilename);

                // Load the comments file information
                navComments = navConfig.SelectSingleNode("configuration/commentsFiles");

                attrValue = navComments.GetAttribute("cacheSize", String.Empty);

                if (attrValue.Length != 0)
                {
                    cacheSize = Convert.ToInt32(attrValue, CultureInfo.InvariantCulture);
                }

                commentsCache = new IndexedCommentsCache(cacheSize)
                {
                    ShowDuplicatesWarning = showDupWarning
                };
                commentsCache.ReportWarning += (s, e) => this.Log.LogMessage(e.Message);

                foreach (XPathNavigator nav in navComments.Select("*"))
                {
                    path      = nav.GetAttribute("path", String.Empty);
                    wildcard  = nav.GetAttribute("file", String.Empty);
                    attrValue = nav.GetAttribute("recurse", String.Empty);

                    if (path.Length == 0)
                    {
                        path     = Path.GetDirectoryName(wildcard);
                        wildcard = Path.GetFileName(wildcard);
                    }

                    path = Environment.ExpandEnvironmentVariables(path);

                    if (wildcard.Length != 0)
                    {
                        wildcard = Environment.ExpandEnvironmentVariables(wildcard);
                    }
                    else
                    {
                        wildcard = "*.xml";
                    }

                    if (attrValue.Length == 0)
                    {
                        recurse = false;
                    }
                    else
                    {
                        recurse = Convert.ToBoolean(attrValue, CultureInfo.InvariantCulture);
                    }

                    this.Log.LogMessage("Indexing {0}\\{1}", path, wildcard);
                    commentsCache.IndexCommentsFiles(path, wildcard, recurse, (nav.Name == "scan") ? commentsFiles : null);
                }
            }

            if (commentsCache.FilesIndexed == 0 || commentsCache.IndexCount == 0)
            {
                throw new ArgumentException("No comments files were specified or they did not " +
                                            "contain valid information to index", nameof(configFile));
            }

            if (commentsFiles.IsEmpty)
            {
                throw new ArgumentException("No comments files were specified to scan for " +
                                            "<inheritdoc /> tags.", nameof(configFile));
            }

            this.Log.LogMessage("Indexed {0} members in {1} file(s).  {2} file(s) to scan for <inheritdoc /> tags.",
                                commentsCache.IndexCount, commentsCache.FilesIndexed, commentsFiles.Count);
        }
        /// <summary>
        /// This is the thread method that indexes the comments files
        /// </summary>
        /// <remarks>Rather than a partial build, we'll just index the
        /// comments files.</remarks>
        private void IndexComments()
        {
            HashSet<string> projectDictionary = new HashSet<string>();
            Collection<string> frameworkLocations = new Collection<string>();
            Dictionary<string, string> cacheName = new Dictionary<string,string>();
            IndexedCommentsCache cache = new IndexedCommentsCache(100);
            MSBuildProject projRef;
            string path, lastSolution = null;

            try
            {
                BuildProcess.GetFrameworkCommentsFiles(frameworkLocations,
                    cacheName, currentProject.Language,
                    currentProject.FrameworkVersion);

                // Index the framework comments
                foreach(string location in frameworkLocations)
                {
                    path = Environment.ExpandEnvironmentVariables(location);
                    cache.IndexCommentsFiles(path, null, true, null);
                }

                // Index the comments file documentation sources
                foreach(string file in currentProject.DocumentationSources.CommentsFiles)
                    cache.IndexCommentsFiles(Path.GetDirectoryName(file),
                        Path.GetFileName(file), false, null);

                // Also, index the comments files in project documentation sources
                foreach(DocumentationSource ds in currentProject.DocumentationSources)
                    foreach(var sourceProject in DocumentationSource.Projects(ds.SourceFile, ds.IncludeSubFolders,
                      !String.IsNullOrEmpty(ds.Configuration) ? ds.Configuration : currentProject.Configuration,
                      !String.IsNullOrEmpty(ds.Platform) ? ds.Platform : currentProject.Platform))
                    {
                        // NOTE: This code should be similar to the code in BuildProcess.ValidateDocumentationSources!

                        // Solutions are followed by the projects that they contain
                        if(sourceProject.ProjectFileName.EndsWith(".sln", StringComparison.OrdinalIgnoreCase))
                        {
                            lastSolution = sourceProject.ProjectFileName;
                            continue;
                        }

                        // Ignore projects that we've already seen
                        if(projectDictionary.Add(sourceProject.ProjectFileName))
                        {
                            projRef = new MSBuildProject(sourceProject.ProjectFileName);

                            // Use the project file configuration and platform properties if they are set.  If not,
                            // use the documentation source values.  If they are not set, use the SHFB project settings.
                            projRef.SetConfiguration(
                                !String.IsNullOrEmpty(sourceProject.Configuration) ? sourceProject.Configuration :
                                    !String.IsNullOrEmpty(ds.Configuration) ? ds.Configuration : currentProject.Configuration,
                                !String.IsNullOrEmpty(sourceProject.Platform) ? sourceProject.Platform :
                                    !String.IsNullOrEmpty(ds.Platform) ? ds.Platform : currentProject.Platform,
                                currentProject.MSBuildOutDir);

                            // Add Visual Studio solution macros if necessary
                            if(lastSolution != null)
                                projRef.SetSolutionMacros(lastSolution);

                            if(!String.IsNullOrEmpty(projRef.XmlCommentsFile))
                                cache.IndexCommentsFiles(Path.GetDirectoryName(projRef.XmlCommentsFile),
                                    Path.GetFileName(projRef.XmlCommentsFile), false, null);
                        }
                    }

                this.Invoke(new IndexingCompleted(this.Completed), new object[] { cache });
            }
            catch(Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.ToString());
                this.Invoke(new IndexingFailed(this.Failed), new object[] { ex.Message });
            }
        }
        /// <summary>
        /// This is called if indexing completes successfully
        /// </summary>
        private void Completed(IndexedCommentsCache cache)
        {
            indexThread = null;
            pbWait.Visible = lblLoading.Visible = false;
            tvEntities.Enabled = true;
            codeEntities = cache.GetKeys();

            if(cboContentType.SelectedIndex == (int)EntityType.CodeEntities)
            {
                txtFindName.Enabled = true;
                txtFindName.ReadOnly = false;
            }
        }