/// <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)
        {
            XDocument config;
            XElement  currentFilter;

            if (context.Behavior == ExecutionBehaviors.Before)
            {
                // Having an API filter in the SHFB project is counterproductive.  Either use the API filter or
                // rely on the XML comments.  It's too complicated to try and use both since you can't reliably
                // resolve conflicts between what is included/excluded between the two.  Using the presence or
                // absence of the XML comments alone produces consistent results and avoids unexpected results.
                builder.ReportProgress("Removing any API filter defined in the project...");

                config = XDocument.Load(builder.WorkingFolder + "MRefBuilder.config");

                currentFilter = config.Root.Descendants("apiFilter").FirstOrDefault();

                if (currentFilter != null)
                {
                    currentFilter.RemoveNodes();
                }

                config.Save(builder.WorkingFolder + "MRefBuilder.config");

                return;
            }

            Dictionary <string, DocumentationState> memberDocState = new Dictionary <string, DocumentationState>();
            DocumentationState docState;
            string             id;
            bool isAutoDocumented, originalTypeDocState, autoDocConstructors = builder.CurrentProject.AutoDocumentConstructors,
                 autoDocDispose = builder.CurrentProject.AutoDocumentDisposeMethods, isDocumented;

            builder.ReportProgress("Determining the documentation state of members in the documented assemblies...");

            // To get everything documented correctly, we need to get a list of all assembly member IDs and then
            // match them up to documentation member IDs.
            var reflectionInfo = new XPathDocument(builder.ReflectionInfoFilename);
            var fileNav        = reflectionInfo.CreateNavigator();

            foreach (XPathNavigator member in fileNav.Select("reflection/apis/api"))
            {
                docState = DocumentationState.FromApiMember(member);

                if (docState.NamespaceName != null)
                {
                    memberDocState[docState.MemberId] = docState;
                }
            }

            foreach (var file in builder.CommentsFiles)
            {
                foreach (XmlNode member in file.Members)
                {
                    if (member.LocalName != "member")
                    {
                        continue;
                    }

                    id = member.Attributes["name"].Value;

                    // Convert NamespaceDoc types to namespace comments element IDs
                    if (id.Length > 2 && id[0] == 'T' && id.EndsWith("NamespaceDoc", StringComparison.Ordinal))
                    {
                        id = "N:" + id.Substring(2, id.Length - 15);
                    }

                    if (memberDocState.TryGetValue(id, out docState))
                    {
                        // Note whether or not the member is explicitly excluded.  For namespaces and types, this
                        // will govern whether or not any of the children are documented even if the children
                        // have documentation.
                        if (member.SelectSingleNode("exclude") != null)
                        {
                            docState.IsExplicitlyExcluded = true;
                            docState.IsDocumented         = false;
                        }
                        else
                        {
                            docState.IsExplicitlyExcluded = false;
                            docState.IsDocumented         = true;
                        }

                        // Add or update entries for attached properties and events
                        if (id[0] == 'F')
                        {
                            if (member.SelectSingleNode("AttachedPropertyComments") != null &&
                                id.EndsWith("Property", StringComparison.Ordinal))
                            {
                                id = "P" + id.Substring(1, id.Length - 9);

                                docState = new DocumentationState
                                {
                                    MemberId      = id,
                                    NamespaceName = docState.NamespaceName,
                                    FullTypeName  = docState.FullTypeName,
                                    TypeName      = docState.TypeName,
                                    MemberName    = id.Substring(id.LastIndexOf('.') + 1),
                                    IsDocumented  = docState.IsDocumented
                                };

                                memberDocState[id] = docState;
                            }
                            else
                            if (member.SelectSingleNode("AttachedEventComments") != null &&
                                id.EndsWith("Event", StringComparison.Ordinal))
                            {
                                id = "E" + id.Substring(1, id.Length - 6);

                                docState = new DocumentationState
                                {
                                    MemberId      = id,
                                    NamespaceName = docState.NamespaceName,
                                    FullTypeName  = docState.FullTypeName,
                                    TypeName      = docState.TypeName,
                                    MemberName    = id.Substring(id.LastIndexOf('.') + 1),
                                    IsDocumented  = docState.IsDocumented
                                };

                                memberDocState[id] = docState;
                            }
                        }
                    }
                }
            }

            var allNamespaces = memberDocState.Values.Where(m => m.IdType == 'N').ToList();
            var allTypes      = memberDocState.Values.Where(m => m.IdType == 'T');
            var allMembers    = memberDocState.Values.Where(m => m.IdType != 'N' && m.IdType != 'T');

            foreach (var ns in allNamespaces)
            {
                foreach (var t in allTypes.Where(t => t.NamespaceName == ns.NamespaceName))
                {
                    ns.Members.Add(t);

                    foreach (var m in allMembers.Where(am => am.FullTypeName == t.FullTypeName))
                    {
                        t.Members.Add(m);
                    }
                }
            }

            builder.ReportProgress("Building API filter based on the XML comments members...");

            // First, see if types and namespaces need to be excluded
            foreach (var namespaceDoc in allNamespaces)
            {
                if (namespaceDoc.IsExplicitlyExcluded)
                {
                    namespaceDoc.IsDocumented = false;
                }
                else
                {
                    foreach (var typeDoc in namespaceDoc.Members)
                    {
                        if (typeDoc.IsExplicitlyExcluded)
                        {
                            typeDoc.IsDocumented = false;
                        }
                        else
                        {
                            foreach (var memberGroup in typeDoc.Members.GroupBy(m => m.MemberName))
                            {
                                isAutoDocumented = false;

                                // Auto-document static constructors if wanted.  Only auto-document parameterless
                                // constructors if wanted and if there are no other constructors or if all other
                                // constructors are documented as well.  Only auto-document the standard dispose methods
                                // if wanted.  For all others, base it on whether or not any single method is excluded
                                // which is how the API filter works.  If one overload is excluded, they are all excluded.
                                if (memberGroup.Key == ".cctor" && autoDocConstructors)
                                {
                                    isAutoDocumented = true;
                                    isDocumented     = !memberGroup.First().IsExplicitlyExcluded;
                                }
                                else
                                if (memberGroup.Key == ".ctor" && autoDocConstructors)
                                {
                                    isAutoDocumented = true;
                                    isDocumented     = (memberGroup.All(m => !m.IsExplicitlyExcluded) &&
                                                        memberGroup.Where(m => m.MemberId.IndexOf('(') != -1).All(m => m.IsDocumented));
                                }
                                else
                                if (memberGroup.Key == "Dispose" && autoDocDispose)
                                {
                                    isAutoDocumented = true;
                                    isDocumented     = memberGroup.All(m => !m.IsExplicitlyExcluded &&
                                                                       (m.MemberId.EndsWith(".Dispose", StringComparison.Ordinal) ||
                                                                        m.MemberId.EndsWith(".Dispose(System.Boolean)", StringComparison.Ordinal)));
                                }
                                else
                                {
                                    isDocumented = memberGroup.All(m => m.IsDocumented);
                                }

                                foreach (var m in memberGroup)
                                {
                                    m.IsAutoDocumented = isAutoDocumented;
                                    m.IsDocumented     = isDocumented;
                                }
                            }

                            // Document the type if any members are documented or if it is documented but has no members
                            originalTypeDocState = typeDoc.IsDocumented;

                            typeDoc.IsDocumented = (typeDoc.Members.Any(m => m.IsDocumented) ||
                                                    (originalTypeDocState && typeDoc.Members.Count == 0));

                            // One exception is if the only members documented are the auto-documented
                            // constructors and dispose methods.  In that case, exclude the type unless the
                            // type has documentation as indicated by the original state.
                            if (!originalTypeDocState && typeDoc.IsDocumented &&
                                (typeDoc.Members.Count(m => !m.IsAutoDocumented) != 0 ||
                                 typeDoc.Members.All(m => m.IsAutoDocumented)) &&
                                !typeDoc.Members.Any(m => !m.IsAutoDocumented && m.IsDocumented))
                            {
                                typeDoc.IsDocumented = false;
                            }
                        }
                    }

                    namespaceDoc.IsDocumented = namespaceDoc.Members.Any(m => m.IsDocumented);
                }
            }

            // The root filter element must set the expose attribute to true or it rips all inherited member
            // information which prevents inherited documentation from working among other things.
            //
            // The namespace elements set the expose attribute to false if they contain no documented types thus
            // excluding everything.  If they contain documented types, it will be set to true.
            //
            // The type elements set the expose attribute to false if they contain no documented members thus
            // excluding everything.  If they contain documented members, it will be set to true.
            //
            // The member elements are always set to false as we only add the undocumented members to exclude
            // from the type.  Documented members are included by default since the type member is exposed.
            var apiFilter = new XElement("apiFilter", new XAttribute("expose", true));

            foreach (var namespaceDoc in allNamespaces.OrderBy(n => n.NamespaceName))
            {
                var ns = new XElement("namespace", new XAttribute("name", namespaceDoc.NamespaceName),
                                      new XAttribute("expose", namespaceDoc.IsDocumented));
                apiFilter.Add(ns);

                // Only add documented types if the namespace is documented
                if (namespaceDoc.IsDocumented)
                {
                    foreach (var typeDoc in namespaceDoc.Members.OrderBy(m => m.TypeName))
                    {
                        // Only add types if not documented or one or more members are not documented
                        if (!typeDoc.IsDocumented || !typeDoc.Members.All(m => m.IsDocumented))
                        {
                            var t = new XElement("type", new XAttribute("name", typeDoc.TypeName),
                                                 new XAttribute("expose", typeDoc.IsDocumented));
                            ns.Add(t);

                            // Only add undocumented members if the type is documented.  Note that we group by
                            // member name.  The way the API filter works is by name alone.  If one overload of a
                            // set is excluded, all of them are.
                            if (typeDoc.IsDocumented)
                            {
                                foreach (var memberDoc in typeDoc.Members.OrderBy(m => m.MemberName).GroupBy(m => m.MemberName))
                                {
                                    if (!memberDoc.First().IsDocumented)
                                    {
                                        t.Add(new XElement("member", new XAttribute("name", memberDoc.Key),
                                                           new XAttribute("expose", false)));
                                    }
                                }
                            }
                        }
                    }
                }
            }

            config = XDocument.Load(builder.WorkingFolder + "MRefBuilder.config");

            currentFilter = config.Root.Descendants("apiFilter").FirstOrDefault();

            if (currentFilter != null)
            {
                currentFilter.ReplaceWith(apiFilter);
            }
            else
            {
                config.Root.Element("dduetools").Add(apiFilter);
            }

            config.Save(builder.WorkingFolder + "MRefBuilder.config");

            builder.ReportProgress("Regenerating reflection information based on the documented members API filter...");

            // Silverlight build targets are only available for 32-bit builds regardless of the framework
            // version and require the 32-bit version of MSBuild in order to load the target file correctly.
            if (builder.CurrentProject.FrameworkVersion.StartsWith("Silverlight", StringComparison.OrdinalIgnoreCase))
            {
                builder.TaskRunner.Run32BitProject("GenerateRefInfo.proj", false);
            }
            else
            {
                builder.TaskRunner.RunProject("GenerateRefInfo.proj", false);
            }
        }
            /// <summary>
            /// This is used to get the member information from a reflection information file node
            /// </summary>
            /// <param name="member">The reflection information file node from which to obtain the details</param>
            public static DocumentationState FromApiMember(XPathNavigator member)
            {
                var docState = new DocumentationState();

                docState.MemberId = member.SelectSingleNode("@id").Value;

                if (String.IsNullOrWhiteSpace(docState.MemberId) || docState.MemberId.Length < 2 ||
                    docState.IdType == 'G' || docState.MemberId[1] != ':')
                {
                    return(docState);
                }

                if (docState.IdType == 'N')
                {
                    docState.NamespaceName = docState.MemberId.Substring(2);
                }
                else
                {
                    docState.NamespaceName = member.SelectSingleNode("containers/namespace/@api").Value.Substring(2);

                    if (docState.IdType == 'T')
                    {
                        docState.FullTypeName = docState.MemberId.Substring(2);
                    }
                    else
                    {
                        docState.FullTypeName = member.SelectSingleNode("containers/type/@api").Value.Substring(2);
                    }

                    if (docState.NamespaceName.Length != 0)
                    {
                        docState.TypeName = docState.FullTypeName.Substring(docState.NamespaceName.Length + 1);
                    }
                    else
                    {
                        docState.TypeName = docState.FullTypeName;
                    }

                    if (docState.IdType != 'T')
                    {
                        string memberName = docState.MemberId.Substring(docState.FullTypeName.Length + 3);

                        // Strip off parameters and generic type counts from type members
                        int pos = memberName.IndexOf('(');

                        if (pos != -1)
                        {
                            memberName = memberName.Substring(0, pos);
                        }

                        pos = memberName.IndexOf("``");

                        if (pos != -1)
                        {
                            memberName = memberName.Substring(0, pos);
                        }

                        docState.MemberName = memberName.Replace('#', '.');
                    }
                }

                return(docState);
            }