/// <summary>
        /// This method is used to execute the plug-in during the build process
        /// </summary>
        /// <param name="context">The current execution context</param>
        public void Execute(ExecutionContext context)
        {
            XmlDocument toc;
            XPathNavigator root, navToc, tocEntry, tocParent;
            bool hasParent;

            // Scan the XML comments files.  The files aren't available soon
            // after this step and by now everything that other plug-ins may
            // have added to the collection should be there.
            if(context.BuildStep == BuildStep.CopyStandardContent)
            {
                builder.ReportProgress("Searching for comment members containing <tocexclude />...");

                foreach(XmlCommentsFile f in builder.CommentsFiles)
                    foreach(XmlNode member in f.Members.SelectNodes("member[count(.//tocexclude) > 0]/@name"))
                        exclusionList.Add(member.Value);

                builder.ReportProgress("Found {0} members to exclude from the TOC", exclusionList.Count);
                return;
            }

            if(exclusionList.Count == 0)
            {
                builder.ReportProgress("No members found to exclude");
                return;
            }

            builder.ReportProgress("Removing members from the TOC");

            toc = new XmlDocument();
            toc.Load(builder.WorkingFolder + "toc.xml");
            navToc = toc.CreateNavigator();

            // If a root namespace container node is present, we need to look
            // in it rather than the document root node.
            root = navToc.SelectSingleNode("topics/topic[starts-with(@id, 'R:')]");

            if(root == null)
                root = navToc.SelectSingleNode("topics");

            foreach(string id in exclusionList)
            {
                tocEntry = root.SelectSingleNode("//topic[@id='" + id + "']");

                // Ignore if null, it was probably excluded by the API filter
                if(tocEntry != null)
                {
                    // Remove the entry.  If this results in the parent
                    // being an empty node, remove it too.
                    do
                    {
                        tocParent = tocEntry.Clone();
                        hasParent = tocParent.MoveToParent();

                        builder.ReportProgress("    Removing '{0}'", tocEntry.GetAttribute("id", String.Empty));
                        tocEntry.DeleteSelf();

                    } while(hasParent && !tocParent.HasChildren);
                }
            }

            toc.Save(builder.WorkingFolder + "toc.xml");
        }
        /// <summary>
        /// Execute all plug-ins that need to execute in the given build step
        /// that have the given execution behavior.
        /// </summary>
        /// <param name="behavior">The execution behavior</param>
        /// <returns>True if at least one plug-in was executed or false if
        /// no plug-ins were executed.</returns>
        /// <remarks>Plug-ins will execute based on their execution priority.
        /// Those with a higher priority value will execute before those with
        /// a lower value.  Plug-ins with identical priority values may execute
        /// in any order within their group.</remarks>
        private bool ExecutePlugIns(ExecutionBehaviors behavior)
        {
            List<IPlugIn> executeList;
            ExecutionContext context;
            BuildStep step;
            int numberExecuted = 0;

            if(loadedPlugIns == null)
                return false;

            executeList = new List<IPlugIn>();
            step = progressArgs.BuildStep;

            // Find plug-ins that need to be executed
            foreach(IPlugIn plugIn in loadedPlugIns.Values)
                if(plugIn.ExecutionPoints.RunsAt(step, behavior))
                    executeList.Add(plugIn);

            if(executeList.Count == 0)
                return false;

            // Sort by execution priority in descending order
            executeList.Sort(
                delegate(IPlugIn x, IPlugIn y)
                {
                    return y.ExecutionPoints.PriorityFor(step, behavior) -
                        x.ExecutionPoints.PriorityFor(step, behavior);
                });

            context = new ExecutionContext(step, behavior);

            foreach(IPlugIn plugIn in executeList)
            {
                try
                {
                    // Wrap plug-in output in an element so that it
                    // can be formatted differently.
                    swLog.WriteLine("<plugIn name=\"{0}\" " +
                        "behavior=\"{1}\" priority=\"{2}\">", plugIn.Name,
                        behavior, plugIn.ExecutionPoints.PriorityFor(step,
                        behavior));

                    context.Executed = true;
                    plugIn.Execute(context);

                    swLog.Write("</plugIn>");
                }
                catch(Exception ex)
                {
                    swLog.WriteLine("</plugIn>");

                    throw new BuilderException("BE0029", "Unexpected " +
                        "error while executing plug-in '" +
                        plugIn.Name + "': " + ex.ToString(), ex);
                }

                if(context.Executed)
                    numberExecuted++;
            }

            return (numberExecuted != 0);
        }
        /// <summary>
        /// This method is used to execute the plug-in during the build process
        /// </summary>
        /// <param name="context">The current execution context</param>
        public void Execute(ExecutionContext context)
        {
            XmlDocument refInfo = new XmlDocument();
            XmlNodeList nodes;

            refInfo.Load(builder.ReflectionInfoFilename);

            foreach(string expression in expressions)
            {
                builder.ReportProgress("Removing items matching '{0}'",
                    expression);

                nodes = refInfo.SelectNodes(expression);

                foreach(XmlNode node in nodes)
                    node.ParentNode.RemoveChild(node);

                builder.ReportProgress("    Removed {0} items", nodes.Count);
            }

            refInfo.Save(builder.ReflectionInfoFilename);
        }
        /// <summary>
        /// This method is used to execute the plug-in during the build process
        /// </summary>
        /// <param name="context">The current execution context</param>
        /// <remarks>Since this runs after completion of the build and the
        /// log file is closed, any progress messages reported here will not
        /// appear in it, just in the output window on the main form.</remarks>
        public void Execute(ExecutionContext context)
        {
            MailMessage msg = null;
            string logFilename = null;

            // There is nothing to do on completion if there is no success
            // e-mail address.
            if(context.BuildStep == BuildStep.Completed &&
              successEMailAddress.Length == 0)
            {
                context.Executed = false;
                return;
            }

            try
            {
                logFilename = builder.LogFilename;

                // Run the log file through an XSL transform first?
                if(!String.IsNullOrEmpty(xslTransformFile) &&
                  File.Exists(logFilename))
                    logFilename = this.TransformLogFile();

                msg = new MailMessage();

                msg.IsBodyHtml = false;
                msg.Subject = String.Format(CultureInfo.InvariantCulture,
                    "Build {0}: {1}", context.BuildStep,
                    builder.ProjectFilename);

                if(fromEMailAddress.Length != 0)
                    msg.From = new MailAddress(fromEMailAddress);
                else
                    msg.From = new MailAddress("*****@*****.**");

                if(context.BuildStep == BuildStep.Completed)
                {
                    msg.To.Add(successEMailAddress);

                    if(attachLogOnSuccess && File.Exists(logFilename))
                        msg.Attachments.Add(new Attachment(logFilename));
                }
                else
                {
                    msg.To.Add(failureEMailAddress);

                    if(attachLogOnFailure && File.Exists(logFilename))
                        msg.Attachments.Add(new Attachment(logFilename));
                }

                msg.Body = String.Format(CultureInfo.InvariantCulture,
                    "Build {0}: {1}{2}\r\nBuild output is located at {3}\r\n",
                    context.BuildStep, builder.ProjectFolder,
                    builder.ProjectFilename, builder.OutputFolder);

                if(context.BuildStep != BuildStep.Completed ||
                  builder.CurrentProject.KeepLogFile)
                    msg.Body += "Build details can be found in the log file " +
                        builder.LogFilename + "\r\n";

                SmtpClient smtp = new SmtpClient();

                if(smtpServer.Length != 0)
                {
                    smtp.Host = smtpServer;
                    smtp.Port = smtpPort;
                }

                if(!credentials.UseDefaultCredentials)
                    smtp.Credentials = new NetworkCredential(
                        credentials.UserName, credentials.Password);

                smtp.Send(msg);

                builder.ReportProgress("The build notification e-mail was " +
                    "sent successfully to {0}", msg.To[0].Address);
            }
            catch(FormatException)
            {
                builder.ReportProgress("Failed to send build notification " +
                    "e-mail!  The e-mail addresses '{0}' appears to be " +
                    "invalid.", msg.To[0]);
            }
            catch(SmtpFailedRecipientException recipEx)
            {
                builder.ReportProgress("Failed to send build notification " +
                    "e-mail!  A problem occurred trying to send the e-mail " +
                    "to the recipient '{0}': {1}", recipEx.FailedRecipient,
                    recipEx.Message);
            }
            catch(SmtpException smtpEx)
            {
                System.Diagnostics.Debug.WriteLine(smtpEx.ToString());

                builder.ReportProgress("Failed to send build notification " +
                    "e-mail!  A problem occurred trying to connect to the " +
                    "e-mail server.  Details:\r\n{0}\r\n", smtpEx.ToString());
            }
            finally
            {
                if(msg != null)
                    msg.Dispose();

                // Delete the transformed log file if it exists
                if(!String.IsNullOrEmpty(logFilename) &&
                  logFilename.EndsWith(".html", StringComparison.OrdinalIgnoreCase))
                    File.Delete(logFilename);
            }
        }
        /// <summary>
        /// This method is used to execute the plug-in during the build process
        /// </summary>
        /// <param name="context">The current execution context</param>
        public void Execute(ExecutionContext context)
        {
            builder.ReportProgress("Multilanguage Documentation Support ...");

            if (targetLanguage == null || targetLanguage.Length == 0)
            {
                builder.ReportProgress("No target language was specified.");
                goto DONE;
            }

            builder.ReportProgress("Target language = {0}", targetLanguage);

            foreach (XmlCommentsFile commentsFile in builder.CommentsFiles)
            {
                // Collect elements which have language tags.
                List<XmlNode> docNodeList = new List<XmlNode>();
                foreach (XmlNode docNode in commentsFile.Comments.SelectNodes("doc/members/member//*[en|ja|" + targetLanguage + "]"))
                {
                    docNodeList.Add(docNode);
                }

                // Process each element.
                bool modified = false;
                foreach (XmlNode docNode in docNodeList)
                {
                    // Collect language tags.
                    List<XmlNode> langNodes = new List<XmlNode>();
                    bool hasEnglishDoc = false;
                    bool hasTargetDoc = false;
                    foreach (XmlNode langNode in docNode.ChildNodes)
                    {
                        if (IsLangTag(langNode))
                        {
                            langNodes.Add(langNode);

                            if (langNode.LocalName == ENGLISH_TAG_NAME)
                                hasEnglishDoc = true;

                            if (langNode.LocalName == targetLanguage)
                                hasTargetDoc = true;
                        }
                    }

                    // Determine which language to leave.
                    string languageToLeave;
                    if (hasTargetDoc)
                    {
                        languageToLeave = targetLanguage;
                    }
                    else if (hasEnglishDoc)
                    {
                        languageToLeave = ENGLISH_TAG_NAME;
                        builder.ReportProgress("Missing target language ... use {0} : {1}", languageToLeave, GetMemberName(docNode));
                    }
                    else if (langNodes.Count > 0)
                    {
                        languageToLeave = langNodes[0].LocalName;
                    }
                    else
                    {
                        languageToLeave = null;
                        builder.ReportProgress("No language tag : {0}", GetMemberName(docNode));
                    }

                    // Strip targeted language tag, and remove other language tags.
                    if (languageToLeave != null)
                    {
                        foreach (XmlNode langNode in langNodes)
                        {
                            if (langNode.LocalName == languageToLeave)
                            {
                                StripLangTag(docNode, langNode);
                                modified = true;
                            }
                            else
                            {
                                docNode.RemoveChild(langNode);
                                modified = true;
                            }
                        }
                    }
                }

                if (modified)
                {
                    commentsFile.Save();
                }
            }

            DONE:
            builder.ReportProgress("Multilanguage Documentation Support Done.");
        }
        /// <summary>
        /// This method is used to execute the plug-in during the build process
        /// </summary>
        /// <param name="context">The current execution context</param>
        public void Execute(ExecutionContext context)
        {
            builder.ReportProgress("Creating Features documentation...");

            var contentGenerator = new ContentGenerator(builder, gherkinFeaturesPath, new CultureInfo(gherkinFeaturesLanguage));
            contentGenerator.Generate();

            AddLinkedItem(BuildAction.ContentLayout, contentGenerator.ContentFile);

            foreach (var topic in contentGenerator.Topics)
            {
                AddTopicItemToProject(BuildAction.None, topic);
            }

            var componentConfig = GetComponentConfiguration(contentGenerator.TopicIndexPath);
            builder.CurrentProject.ComponentConfigurations.Add(GetComponentId(), true, componentConfig);

            builder.CurrentProject.MSBuildProject.ReevaluateIfNecessary();
        }
        /// <summary>
        /// This method is used to execute the plug-in during the build process
        /// </summary>
        /// <param name="context">The current execution context</param>
        public void Execute(ExecutionContext context)
        {
            List<string> namespaceList = new List<string>();
            Dictionary<string, XmlNode> namespaceNodes = new Dictionary<string, XmlNode>();
            XmlDocument toc;
            XPathNavigator root, navToc;
            XmlAttribute attr;
            XmlNode tocEntry, tocParent;
            string[] parts;
            string name, parent, topicTitle;
            int parentIdx, childIdx, entriesAdded;

            builder.ReportProgress("Retrieving namespace topic title from shared content...");

            toc = new XmlDocument();
            toc.Load(builder.PresentationStyleFolder + @"content\reference_content.xml");
            tocEntry = toc.SelectSingleNode("content/item[@id='namespaceTopicTitle']");

            if(tocEntry != null)
                topicTitle = tocEntry.InnerText;
            else
            {
                builder.ReportWarning("HTP0001", "Unable to locate namespace " +
                    "topic title in reference content file.  Using default.");
                topicTitle = "{0} Namespace";
            }

            builder.ReportProgress("Creating namespace hierarchy...");

            toc = new XmlDocument();
            toc.Load(builder.WorkingFolder + "toc.xml");
            navToc = toc.CreateNavigator();

            // Get a list of the namespaces.  If a root namespace container
            // node is present, we need to look in it rather than the document
            // root node.
            root = navToc.SelectSingleNode("topics/topic[starts-with(@id, 'R:')]");

            if(root == null)
                root = navToc.SelectSingleNode("topics");

            foreach(XPathNavigator ns in root.Select("topic[starts-with(@id, 'N:')]"))
            {
                name = ns.GetAttribute("id", String.Empty);
                namespaceList.Add(name);
                namespaceNodes.Add(name, ((IHasXmlNode)ns).GetNode());
            }

            // See if any container nodes need to be created for namespaces
            // with a common root name.
            for(parentIdx = 0; parentIdx < namespaceList.Count; parentIdx++)
            {
                parts = namespaceList[parentIdx].Split('.');

                // Only do it for namespaces with a minimum number of parts
                if(parts.Length > minParts)
                {
                    for(childIdx = minParts; childIdx < parts.Length; childIdx++)
                    {
                        name = String.Join(".", parts, 0, childIdx);

                        if(!namespaceList.Contains(name))
                        {
                            if(namespaceList.FindAll(
                              ns => ns.StartsWith(name + ".", StringComparison.Ordinal)).Count > 0)
                            {
                                // The nodes will be created later once
                                // we know where to insert them.
                                namespaceList.Add(name);
                                namespaceNodes.Add(name, null);
                            }
                        }
                    }
                }
            }

            // Sort them in reverse order
            namespaceList.Sort((n1, n2) => String.Compare(n2, n1, StringComparison.Ordinal));

            // If any container namespaces were added, create nodes for them
            // and insert them before the namespace ahead of them in the list.
            foreach(string key in namespaceList)
                if(namespaceNodes[key] == null)
                {
                    tocEntry = toc.CreateElement("topic");
                    attr = toc.CreateAttribute("id");

                    attr.Value = String.Format(CultureInfo.InvariantCulture,
                        topicTitle, (key.Length > 2) ? key.Substring(2) : "Global");
                    tocEntry.Attributes.Append(attr);

                    parentIdx = namespaceList.IndexOf(key);
                    tocParent = namespaceNodes[namespaceList[parentIdx - 1]];
                    tocParent.ParentNode.InsertBefore(tocEntry, tocParent);
                    namespaceNodes[key] = tocEntry;
                }

            for(parentIdx = 1; parentIdx < namespaceList.Count; parentIdx++)
            {
                parent = namespaceList[parentIdx];
                entriesAdded = 0;

                // Check each preceding namespace.  If it starts with the
                // parent's name, insert it as a child of that one.
                for(childIdx = 0; childIdx < parentIdx; childIdx++)
                {
                    name = namespaceList[childIdx];

                    if(name.StartsWith(parent + ".", StringComparison.Ordinal))
                    {
                        tocEntry = namespaceNodes[name];
                        tocParent = namespaceNodes[parent];

                        if(insertBelow && entriesAdded < tocParent.ChildNodes.Count)
                            tocParent.InsertAfter(tocEntry, tocParent.ChildNodes[
                                tocParent.ChildNodes.Count - entriesAdded - 1]);
                        else
                            tocParent.InsertBefore(tocEntry, tocParent.ChildNodes[0]);

                        namespaceList.RemoveAt(childIdx);
                        entriesAdded++;
                        parentIdx--;
                        childIdx--;
                    }
                }
            }

            toc.Save(builder.WorkingFolder + "toc.xml");
        }
        /// <summary>
        /// This method is used to execute the plug-in during the build process
        /// </summary>
        /// <param name="context">The current execution context</param>
        public void Execute(ExecutionContext context)
        {
            XmlDocument config = new XmlDocument();
            XmlAttribute attr;
            XmlNode resolver, ddue;

            config.Load(builder.WorkingFolder + "MRefBuilder.config");
            resolver = config.SelectSingleNode("configuration/dduetools/resolver");

            if(resolver == null)
            {
                ddue = config.SelectSingleNode("configuration/dduetools");

                if(ddue == null)
                    throw new BuilderException("ABR0002", "Unable to locate " +
                        "configuration/dduetools or its child resolver " +
                        "element in MRefBuilder.config");

                builder.ReportProgress("Default resolver element not found, " +
                    "adding new element");
                resolver = config.CreateNode(XmlNodeType.Element, "resolver", null);
                ddue.AppendChild(resolver);

                attr = config.CreateAttribute("type");
                resolver.Attributes.Append(attr);
                attr = config.CreateAttribute("assembly");
                resolver.Attributes.Append(attr);
                attr = config.CreateAttribute("use-gac");
                attr.Value = "false";
                resolver.Attributes.Append(attr);
            }

            resolver.Attributes["type"].Value =
                "SandcastleBuilder.Components.BindingRedirectResolver";
            resolver.Attributes["assembly"].Value = builder.TransformText(
                "{@SHFBFolder}SandcastleBuilder.MRefBuilder.dll");

            builder.ReportProgress("Default resolver replaced with '{0}' in '{1}'",
                resolver.Attributes["type"].Value,
                resolver.Attributes["assembly"].Value);

            builder.ReportProgress("Adding redirects:");

            foreach(BindingRedirectSettings brs in redirects)
                builder.ReportProgress("    {0}", brs);

            redirects.ToXml(config, resolver);

            config.Save(builder.WorkingFolder + "MRefBuilder.config");
        }
 /// <summary>
 /// This method is used to execute the plug-in during the build process
 /// </summary>
 /// <param name="context">The current execution context</param>
 public void Execute(ExecutionContext context)
 {
     // TODO: Add your execution code here
 }
        /// <summary>
        /// This method is used to execute the plug-in during the build process
        /// </summary>
        /// <param name="context">The current execution context</param>
        public void Execute(ExecutionContext context)
        {
            XmlDocument config;
            XPathNavigator navConfig;
            List<XPathNavigator> deleteTargets = new List<XPathNavigator>();

            // Create a dummy reflection.org and reflection.xml file
            if(context.BuildStep == BuildStep.GenerateReflectionInfo)
            {
                // Allow Before step plug-ins to run
                builder.ExecuteBeforeStepPlugIns();

                using(StreamWriter sw = new StreamWriter(builder.ReflectionInfoFilename, false, Encoding.UTF8))
                {
                    sw.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
                    sw.WriteLine("<reflection>");
                    sw.WriteLine("  <assemblies/>");
                    sw.WriteLine("  <apis>");
                    sw.WriteLine("    <api id=\"N:\"/>");
                    sw.WriteLine("  </apis>");
                    sw.WriteLine("</reflection>");
                }

                File.Copy(builder.ReflectionInfoFilename,
                    Path.ChangeExtension(builder.ReflectionInfoFilename, ".xml"), true);

                // Allow After step plug-ins to run
                builder.ExecuteAfterStepPlugIns();
            }
            else
                if(context.BuildStep == BuildStep.MergeCustomConfigs &&
                  builder.CurrentProject.HasItems(BuildAction.ContentLayout))
                {
                    // Remove the reflection.xml file from the conceptual
                    // configuration file since it isn't valid.
                    config = new XmlDocument();
                    config.Load(builder.WorkingFolder + "conceptual.config");
                    navConfig = config.CreateNavigator();

                    XPathNodeIterator allTargets = navConfig.Select("//targets[@files='reflection.xml']");

                    foreach(XPathNavigator target in allTargets)
                        deleteTargets.Add(target);

                    // Get rid of the framework targets too in preview build.
                    // This can knock about 20 seconds off the build time.
                    if(isPreviewBuild)
                    {
                        allTargets = navConfig.Select("//targets[contains(@base, 'Data\\Reflection')]");

                        foreach(XPathNavigator target in allTargets)
                            deleteTargets.Add(target);
                    }

                    foreach(var t in deleteTargets)
                        t.DeleteSelf();

                    config.Save(builder.WorkingFolder + "conceptual.config");
                }

            // Ignore all other the steps
        }
        /// <summary>
        /// This method is used to execute the plug-in during the build process
        /// </summary>
        /// <param name="context">The current execution context</param>
        public void Execute(ExecutionContext context)
        {
            // Deploy each of the selected help file formats
            if(builder.CurrentFormat == HelpFileFormat.HtmlHelp1)
            {
                builder.ReportProgress("Deploying HTML Help 1 file");
                this.DeployOutput(builder.Help1Files, deployHelp1);
            }

            if(builder.CurrentFormat == HelpFileFormat.MSHelp2)
            {
                builder.ReportProgress("Deploying MS Help 2 files");
                this.DeployOutput(builder.Help2Files, deployHelp2);
            }

            if(builder.CurrentFormat == HelpFileFormat.MSHelpViewer)
            {
                builder.ReportProgress("Deploying MS Help Viewer files");
                this.DeployOutput(builder.HelpViewerFiles, deployHelpViewer);
            }

            if(builder.CurrentFormat == HelpFileFormat.Website)
            {
                builder.ReportProgress("Deploying website files");
                this.DeployOutput(builder.WebsiteFiles, deployWebsite);
            }
        }
        /// <summary>
        /// This method is used to execute the plug-in during the build process
        /// </summary>
        /// <param name="context">The current execution context</param>
        public void Execute(ExecutionContext context)
        {
            string configFilename;

            // Merge the reflection file info into conceptual.config
            configFilename = builder.WorkingFolder + "conceptual.config";

            if(File.Exists(configFilename))
                this.AddBibliographyParameter(configFilename);

            // Merge the reflection file info into sancastle.config
            configFilename = builder.WorkingFolder + "sandcastle.config";

            if(File.Exists(configFilename))
                this.AddBibliographyParameter(configFilename);
        }