Exemple #1
0
        /// <inheritdoc />
        protected override bool BindControlValue(string propertyName)
        {
            ProjectProperty projProp;
            VisibleItems    items;

#if !STANDALONEGUI
            if (this.ProjectMgr == null)
            {
                return(false);
            }
#else
            if (this.CurrentProject == null)
            {
                return(false);
            }
#endif

            if (propertyName == "ApiFilter")
            {
                // Pass it the Sandcastle project instance as we use the designer dialog to edit the collection
                // and it obtains it from the collection to do the required partial build.
#if !STANDALONEGUI
                var filter = new ApiFilterCollection {
                    Project = ((SandcastleBuilderProjectNode)this.ProjectMgr).SandcastleProject
                };

                projProp = this.ProjectMgr.BuildProject.GetProperty("ApiFilter");
#else
                var filter = new ApiFilterCollection {
                    Project = base.CurrentProject
                };

                projProp = this.CurrentProject.MSBuildProject.GetProperty("ApiFilter");
#endif
                if (projProp != null && !String.IsNullOrEmpty(projProp.UnevaluatedValue))
                {
                    filter.FromXml(projProp.UnevaluatedValue);
                }

                ucVisibilityPropertiesPageContent.ApiFilter           = filter;
                ucVisibilityPropertiesPageContent.ApiFilterHasChanges = false;
                ucVisibilityPropertiesPageContent.UpdateApiFilterInfo();
                return(true);
            }

#if !STANDALONEGUI
            projProp = this.ProjectMgr.BuildProject.GetProperty("VisibleItems");
#else
            projProp = this.CurrentProject.MSBuildProject.GetProperty("VisibleItems");
#endif
            // If not found or not valid, we'll ignore it and use the defaults
            if (projProp == null || !Enum.TryParse <VisibleItems>(projProp.UnevaluatedValue, out items))
            {
                items = VisibleItems.InheritedFrameworkMembers | VisibleItems.InheritedMembers |
                        VisibleItems.Protected | VisibleItems.ProtectedInternalAsProtected | VisibleItems.NonBrowsable;
            }

            ucVisibilityPropertiesPageContent.VisibleItems = items;
            return(true);
        }
        //=====================================================================
        /// <summary>
        /// This is used to manually apply the specified API filter to the
        /// specified reflection information file.
        /// </summary>
        /// <param name="apiFilter">The API filter to apply</param>
        /// <param name="reflectionFilename">The reflection information file</param>
        /// <remarks>This can be used by any plug-in that does not produce a
        /// reflection information file using <b>MRefBuilder.exe</b>.  In such
        /// cases, the API filter is not applied unless the plug-in uses this
        /// method.  If the reflection information file is produced by
        /// <b>MRefBuilder.exe</b>, there is no need to use this method as it
        /// will apply the API filter automatically to the file that it
        /// produces.</remarks>
        public void ApplyManualApiFilter(ApiFilterCollection apiFilter,
          string reflectionFilename)
        {
            XmlDocument refInfo;
            XmlNode apis;
            string id;
            bool keep;

            refInfo = new XmlDocument();
            refInfo.Load(reflectionFilename);
            apis = refInfo.SelectSingleNode("reflection/apis");

            foreach(ApiFilter nsFilter in apiFilter)
                if(nsFilter.Children.Count == 0)
                    this.RemoveNamespace(apis, nsFilter.FullName);
                else
                    if(!nsFilter.IsExposed)
                    {
                        // Remove all but the indicated types
                        foreach(XmlNode typeNode in apis.SelectNodes(
                          "api[starts-with(@id, 'T:') and containers/" +
                          "namespace/@api='N:" + nsFilter.FullName + "']"))
                        {
                            id = typeNode.Attributes["id"].Value.Substring(2);
                            keep = false;

                            foreach(ApiFilter typeFilter in nsFilter.Children)
                                if(typeFilter.FullName == id)
                                {
                                    // Just keep or remove members
                                    this.ApplyMemberFilter(apis, typeFilter);
                                    keep = true;
                                    break;
                                }

                            if(!keep)
                                this.RemoveType(apis, id);
                        }
                    }
                    else
                    {
                        // Remove just the indicated types or their members
                        foreach(ApiFilter typeFilter in nsFilter.Children)
                        {
                            if(!typeFilter.IsExposed &&
                              typeFilter.Children.Count == 0)
                            {
                                this.RemoveType(apis, typeFilter.FullName);
                                continue;
                            }

                            // Just keep or remove members
                            this.ApplyMemberFilter(apis, typeFilter);
                        }
                    }

            refInfo.Save(reflectionFilename);
        }
        //=====================================================================
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="filter">The item collection to edit</param>
        internal ApiFilterEditorDlg(ApiFilterCollection filter)
        {
            InitializeComponent();

            // An italic font is used for nodes that cannot have their
            // check state changed.  A tooltip will give further details.
            tvApiList.Nodes[0].NodeFont = tvApiList.Nodes[1].NodeFont =
                italicFont = new Font(this.Font, FontStyle.Italic);

            apiFilter = filter;
        }
        /// <summary>
        /// Edit the project's API filter
        /// </summary>
        /// <param name="sender">The sender of the event</param>
        /// <param name="e">The event arguments</param>
        private void btnEditAPIFilter_Click(object sender, EventArgs e)
        {
#if !STANDALONEGUI
            if (this.ProjectMgr == null)
            {
                return;
            }

            // Apply any pending visibility changes first
            if (this.IsDirty && ((IPropertyPage)this).Apply() != VSConstants.S_OK)
            {
                return;
            }

            // Create an API filter collection that we can edit
            ApiFilterCollection filter = new ApiFilterCollection {
                Project =
                    ((SandcastleBuilderProjectNode)this.ProjectMgr).SandcastleProject
            };
#else
            if (base.CurrentProject == null)
            {
                return;
            }

            // Apply any pending visibility changes first
            if (this.IsDirty && !this.Apply())
            {
                return;
            }

            // Create an API filter collection that we can edit
            ApiFilterCollection filter = new ApiFilterCollection {
                Project = base.CurrentProject
            };
#endif
            filter.FromXml(apiFilter);

            using (ApiFilterEditorDlg dlg = new ApiFilterEditorDlg(filter))
            {
                dlg.ShowDialog();

                string newFilter = filter.ToXml();

                // If it changes, mark the page as dirty and update the local copy of the filter
                if (apiFilter != newFilter)
                {
                    apiFilter    = newFilter;
                    this.IsDirty = filterChanged = true;
                    this.UpdateApiFilterInfo();
                }
            }
        }
Exemple #5
0
        /// <inheritdoc />
        public override object ConvertTo(ITypeDescriptorContext context,
                                         CultureInfo culture, object value, Type destinationType)
        {
            ApiFilterCollection items = value as ApiFilterCollection;

            if (items == null || destinationType != typeof(string))
            {
                return(base.ConvertTo(context, culture, value, destinationType));
            }

            // Since we can't give a meaningful count, just indicate
            // whether or not the topics will be filtered.
            if (items.Count == 0)
            {
                return("(None)");
            }

            return("(Filter defined)");
        }
Exemple #6
0
        public override object EditValue(System.ComponentModel.ITypeDescriptorContext context,
                                         IServiceProvider provider, object value)
        {
            // Get the API filter collection
            ApiFilterCollection items = value as ApiFilterCollection;

            if (context == null || provider == null || context.Instance == null ||
                items == null)
            {
                return(base.EditValue(context, provider, value));
            }

            using (ApiFilterEditorDlg dlg = new ApiFilterEditorDlg(items))
            {
                dlg.ShowDialog();
            }

            return(value);
        }
        //=====================================================================

        /// <summary>
        /// This is used to convert the API filter from the build into a
        /// dictionary so that it is easier to look up the entries.
        /// </summary>
        /// <param name="filter">The filter collection to search for project
        /// level exclusions.</param>
        private void ConvertApiFilter(ApiFilterCollection filter)
        {
            foreach(ApiFilter entry in filter)
            {
#if DEBUG
                // This shouldn't happen.  If it does, there's a problem
                // in generating the API filter in the build process.
                if(buildFilterEntries.ContainsKey(entry.FullName))
                    System.Diagnostics.Debugger.Break();
#endif
                // Sometimes, it does happen for unknown reasons.  Ignore it
                // so that it doesn't prevent the filter from opening.
                if(!buildFilterEntries.ContainsKey(entry.FullName))
                    buildFilterEntries.Add(entry.FullName, entry);

                if(entry.Children.Count != 0)
                    this.ConvertApiFilter(entry.Children);
            }
        }
        /// <summary>
        /// This refreshes the project instance property values by reloading them from the underlying MSBuild
        /// project.
        /// </summary>
        public void RefreshProjectProperties()
        {
            projectPropertyCache = null;
            docSources = null;
            apiFilter = null;
            namespaceSummaries = null;
            componentConfigs = null;
            plugInConfigs = null;

            msBuildProject.ReevaluateIfNecessary();

            this.LoadProperties();
        }
        //=====================================================================

        /// <summary>
        /// This is used to manually apply the specified API filter to the specified reflection information file
        /// </summary>
        /// <param name="filterToApply">The API filter to apply</param>
        /// <param name="reflectionFilename">The reflection information file</param>
        private void ApplyManualApiFilter(ApiFilterCollection filterToApply, string reflectionFilename)
        {
            XmlDocument refInfo;
            XmlNode     apis;
            string      id;
            bool        keep;

            refInfo = new XmlDocument();
            refInfo.Load(reflectionFilename);
            apis = refInfo.SelectSingleNode("reflection/apis");

            foreach (ApiFilter nsFilter in filterToApply)
            {
                if (nsFilter.Children.Count == 0)
                {
                    this.RemoveNamespace(apis, nsFilter.FullName);
                }
                else
                if (!nsFilter.IsExposed)
                {
                    // Remove all but the indicated types
                    foreach (XmlNode typeNode in apis.SelectNodes("api[starts-with(@id, 'T:') and containers/" +
                                                                  "namespace/@api='N:" + nsFilter.FullName + "']"))
                    {
                        id   = typeNode.Attributes["id"].Value.Substring(2);
                        keep = false;

                        foreach (ApiFilter typeFilter in nsFilter.Children)
                        {
                            if (typeFilter.FullName == id)
                            {
                                // Just keep or remove members
                                this.ApplyMemberFilter(apis, typeFilter);
                                keep = true;
                                break;
                            }
                        }

                        if (!keep)
                        {
                            this.RemoveType(apis, id);
                        }
                    }
                }
                else
                {
                    // Remove just the indicated types or their members
                    foreach (ApiFilter typeFilter in nsFilter.Children)
                    {
                        if (!typeFilter.IsExposed && typeFilter.Children.Count == 0)
                        {
                            this.RemoveType(apis, typeFilter.FullName);
                            continue;
                        }

                        // Just keep or remove members
                        this.ApplyMemberFilter(apis, typeFilter);
                    }
                }
            }

            refInfo.Save(reflectionFilename);
        }
        //=====================================================================
        /// <summary>
        /// This is used to generate the API filter collection used by
        /// MRefBuilder to exclude items from the reflection information file.
        /// </summary>
        /// <remarks>Namespaces and members with an <code>&lt;exclude /&gt;</code>
        /// tag in their comments are removed using the ripping feature as it
        /// is more efficient than searching for and removing them from the
        /// reflection file after it has been generated especially on large
        /// projects.</remarks>
        private void GenerateApiFilter()
        {
            XmlNodeList excludes;
            XmlNode docMember;
            List<string> ripList;
            string nameSpace, memberName, typeName, fullName;
            int pos;

            this.ReportProgress(BuildStep.GenerateApiFilter,
                "Generating API filter for MRefBuilder...");

            if(this.ExecutePlugIns(ExecutionBehaviors.InsteadOf))
                return;

            this.ExecutePlugIns(ExecutionBehaviors.Before);

            ripList = new List<string>();

            // Add excluded namespaces
            foreach(NamespaceSummaryItem ns in project.NamespaceSummaries)
                if(!ns.IsDocumented)
                {
                    memberName = ns.Name;

                    if(memberName[0] == '(')
                        memberName = "N:";  // Global namespace
                    else
                        memberName = "N:" + memberName;

                    ripList.Add(memberName);
                }

            // If the namespace summaries don't contain an explicit entry
            // for the global namespace, exclude it by default.
            if(project.NamespaceSummaries[null] == null)
                ripList.Add("N:");

            // Add members excluded via comments
            foreach(XmlCommentsFile comments in commentsFiles)
            {
                excludes = comments.Members.SelectNodes("//exclude/..");

                foreach(XmlNode member in excludes)
                {
                    // It should appear at the same level as <summary> so that
                    // we can find the member name in the parent node.
                    if(member.Attributes["name"] == null)
                    {
                        this.ReportProgress("    Incorrect placement of " +
                            "<exclude/> tag.  Unable to locate member name.");
                        continue;
                    }

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

                    if(!ripList.Contains(memberName))
                        ripList.Add(memberName);
                }
            }

            // Sort by entry type and name so that we create the collection
            // from the namespace down to the members.
            ripList.Sort(
                delegate(string x, string y)
                {
                    ApiEntryType xType = ApiFilter.ApiEntryTypeFromLetter(x[0]),
                        yType = ApiFilter.ApiEntryTypeFromLetter(y[0]);

                    if(xType == yType)
                        return String.Compare(x, y, false,
                            CultureInfo.CurrentCulture);

                    return (int)xType - (int)yType;
                });

            // Clone the project ApiFilter and merge the members from the
            // rip list.
            apiFilter = (ApiFilterCollection)project.ApiFilter.Clone();

            // For the API filter to work, we have to nest the entries by
            // namespace, type, and member.  As such, we have to break apart
            // what we've got in the list and merge it with the stuff the user
            // may have specified using the project's API filter property.
            foreach(string member in ripList)
            {
                // Namespaces are easy
                if(member[0] == 'N')
                {
                    if(!apiFilter.MergeEntry(ApiEntryType.Namespace,
                      member.Substring(2), false, true))
                        this.ReportWarning("BE0008", "Namespace '{0}' " +
                            "excluded via namespace comments conflicted with " +
                            "API filter setting.  Exclusion ignored.", member);

                    continue;
                }

                // Types and members are a bit tricky.  Since we don't have any
                // real context, we have to assume that we can remove the last
                // part and look it up.  If a type entry isn't found, we can
                // assume it's the namespace.  Where this can fail is on a
                // nested class where the parent class is lacking XML comments.
                // Not much we can do about it in that case.
                if(member[0] == 'T')
                {
                    fullName = nameSpace = member;
                    typeName = member.Substring(2);
                    memberName = null;
                }
                else
                {
                    // Strip parameters.  The ripping feature only goes to
                    // the name level.  If one overload is ripped, they are
                    // all ripped.
                    pos = member.IndexOf('(');

                    if(pos != -1)
                        fullName = memberName = member.Substring(0, pos);
                    else
                        fullName = memberName = member;

                    // Generic method
                    pos = memberName.IndexOf("``", StringComparison.Ordinal);

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

                    pos = memberName.LastIndexOf('.');
                    memberName = memberName.Substring(pos + 1);
                    typeName = fullName.Substring(2, pos - 2);
                    nameSpace = "T:" + typeName;
                }

                for(int idx = 0; idx < commentsFiles.Count; idx++)
                {
                    docMember = commentsFiles[idx].Members.SelectSingleNode(
                        "member[@name='" + nameSpace + "']");

                    if(docMember != null)
                    {
                        pos = nameSpace.LastIndexOf('.');

                        if(pos == -1)
                        {
                            nameSpace = "N:";
                            break;
                        }
                        else
                            nameSpace = nameSpace.Substring(0, pos);

                        idx = -1;
                    }
                }

                nameSpace = nameSpace.Substring(2);

                // If the names still match, we probably didn't find comments
                // for the type so assume the namespace is the part up to
                // the last period.
                if(nameSpace == typeName)
                {
                    pos = nameSpace.LastIndexOf('.');

                    if(pos != -1)
                        nameSpace = nameSpace.Substring(0, pos);
                    else
                        nameSpace = "N:";   // Global namespace
                }

                if(apiFilter.AddNamespaceChild(fullName, nameSpace, typeName,
                  memberName))
                {
                    if(fullName.Length > 2)
                    {
                        // If it's a nested class, adjust the filter name
                        fullName = typeName;
                        typeName = typeName.Substring(nameSpace.Length + 1);

                        if(typeName.IndexOf('.') != -1)
                            foreach(ApiFilter ns in apiFilter)
                                if(ns.FullName == nameSpace)
                                {
                                    foreach(ApiFilter t in ns.Children)
                                        if(t.FullName == fullName)
                                        {
                                            t.FilterName = typeName;
                                            break;
                                        }

                                    break;
                                }
                    }
                }
                else
                    this.ReportWarning("BE0009", "'{0}' is marked with " +
                        "<exclude /> but conflicted with the API filter " +
                        "setting.  Exclusion ignored.", member);
            }

            this.ExecutePlugIns(ExecutionBehaviors.After);
        }
        /// <summary>
        /// This is used to convert the API filter from the build into a
        /// dictionary so that it is easier to look up the entries.
        /// </summary>
        /// <param name="filter">The filter collection to search for project
        /// level exclusions.</param>
        private void ConvertApiFilter(ApiFilterCollection filter)
        {
            foreach(ApiFilter entry in filter)
            {
            #if DEBUG
                // This shouldn't happen.  If it does, there's a problem
                // in generating the API filter in the build process.
                if(buildFilterEntries.ContainsKey(entry.FullName))
                    System.Diagnostics.Debugger.Break();
            #endif
                buildFilterEntries.Add(entry.FullName, entry);

                if(entry.Children.Count != 0)
                    this.ConvertApiFilter(entry.Children);
            }
        }
Exemple #12
0
        //=====================================================================

        /// <summary>
        /// This is used to generate the API filter collection used by
        /// MRefBuilder to exclude items from the reflection information file.
        /// </summary>
        /// <remarks>Namespaces and members with an <code>&lt;exclude /&gt;</code>
        /// tag in their comments are removed using the ripping feature as it
        /// is more efficient than searching for and removing them from the
        /// reflection file after it has been generated especially on large
        /// projects.</remarks>
        private void GenerateApiFilter()
        {
            XmlNodeList   excludes;
            XmlNode       docMember;
            List <string> ripList;
            string        nameSpace, memberName, typeName, fullName;
            int           pos;

            this.ReportProgress(BuildStep.GenerateApiFilter,
                                "Generating API filter for MRefBuilder...");

            if (this.ExecutePlugIns(ExecutionBehaviors.InsteadOf))
            {
                return;
            }

            this.ExecutePlugIns(ExecutionBehaviors.Before);

            ripList = new List <string>();

            // Add excluded namespaces
            foreach (NamespaceSummaryItem ns in project.NamespaceSummaries)
            {
                if (!ns.IsDocumented)
                {
                    memberName = ns.Name;

                    if (memberName[0] == '(')
                    {
                        memberName = "N:";  // Global namespace
                    }
                    else
                    {
                        memberName = "N:" + memberName;
                    }

                    ripList.Add(memberName);
                }
            }

            // If the namespace summaries don't contain an explicit entry
            // for the global namespace, exclude it by default.
            if (project.NamespaceSummaries[null] == null)
            {
                ripList.Add("N:");
            }

            // Add members excluded via comments
            foreach (XmlCommentsFile comments in commentsFiles)
            {
                excludes = comments.Members.SelectNodes("//exclude/..");

                foreach (XmlNode member in excludes)
                {
                    // It should appear at the same level as <summary> so that
                    // we can find the member name in the parent node.
                    if (member.Attributes["name"] == null)
                    {
                        this.ReportProgress("    Incorrect placement of " +
                                            "<exclude/> tag.  Unable to locate member name.");
                        continue;
                    }

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

                    if (!ripList.Contains(memberName))
                    {
                        ripList.Add(memberName);
                    }
                }
            }

            // Sort by entry type and name so that we create the collection
            // from the namespace down to the members.
            ripList.Sort(
                delegate(string x, string y)
            {
                ApiEntryType xType = ApiFilter.ApiEntryTypeFromLetter(x[0]),
                yType = ApiFilter.ApiEntryTypeFromLetter(y[0]);

                if (xType == yType)
                {
                    return(String.Compare(x, y, false,
                                          CultureInfo.CurrentCulture));
                }

                return((int)xType - (int)yType);
            });

            // Clone the project ApiFilter and merge the members from the
            // rip list.
            apiFilter = (ApiFilterCollection)project.ApiFilter.Clone();

            // For the API filter to work, we have to nest the entries by
            // namespace, type, and member.  As such, we have to break apart
            // what we've got in the list and merge it with the stuff the user
            // may have specified using the project's API filter property.
            foreach (string member in ripList)
            {
                // Namespaces are easy
                if (member[0] == 'N')
                {
                    if (!apiFilter.MergeEntry(ApiEntryType.Namespace,
                                              member.Substring(2), false, true))
                    {
                        this.ReportWarning("BE0008", "Namespace '{0}' " +
                                           "excluded via namespace comments conflicted with " +
                                           "API filter setting.  Exclusion ignored.", member);
                    }

                    continue;
                }

                // Types and members are a bit tricky.  Since we don't have any
                // real context, we have to assume that we can remove the last
                // part and look it up.  If a type entry isn't found, we can
                // assume it's the namespace.  Where this can fail is on a
                // nested class where the parent class is lacking XML comments.
                // Not much we can do about it in that case.
                if (member[0] == 'T')
                {
                    fullName   = nameSpace = member;
                    typeName   = member.Substring(2);
                    memberName = null;
                }
                else
                {
                    // Strip parameters.  The ripping feature only goes to
                    // the name level.  If one overload is ripped, they are
                    // all ripped.
                    pos = member.IndexOf('(');

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

                    // Generic method
                    pos = memberName.IndexOf("``", StringComparison.Ordinal);

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

                    pos        = memberName.LastIndexOf('.');
                    memberName = memberName.Substring(pos + 1);
                    typeName   = fullName.Substring(2, pos - 2);
                    nameSpace  = "T:" + typeName;
                }

                for (int idx = 0; idx < commentsFiles.Count; idx++)
                {
                    docMember = commentsFiles[idx].Members.SelectSingleNode(
                        "member[@name='" + nameSpace + "']");

                    if (docMember != null)
                    {
                        pos = nameSpace.LastIndexOf('.');

                        if (pos == -1)
                        {
                            nameSpace = "N:";
                            break;
                        }
                        else
                        {
                            nameSpace = nameSpace.Substring(0, pos);
                        }

                        idx = -1;
                    }
                }

                nameSpace = nameSpace.Substring(2);

                // If the names still match, we probably didn't find comments
                // for the type so assume the namespace is the part up to
                // the last period.
                if (nameSpace == typeName)
                {
                    pos = nameSpace.LastIndexOf('.');

                    if (pos != -1)
                    {
                        nameSpace = nameSpace.Substring(0, pos);
                    }
                    else
                    {
                        nameSpace = "N:";   // Global namespace
                    }
                }

                if (apiFilter.AddNamespaceChild(fullName, nameSpace, typeName,
                                                memberName))
                {
                    if (fullName.Length > 2)
                    {
                        // If it's a nested class, adjust the filter name
                        fullName = typeName;
                        typeName = typeName.Substring(nameSpace.Length + 1);

                        if (typeName.IndexOf('.') != -1)
                        {
                            foreach (ApiFilter ns in apiFilter)
                            {
                                if (ns.FullName == nameSpace)
                                {
                                    foreach (ApiFilter t in ns.Children)
                                    {
                                        if (t.FullName == fullName)
                                        {
                                            t.FilterName = typeName;
                                            break;
                                        }
                                    }

                                    break;
                                }
                            }
                        }
                    }
                }
                else
                {
                    this.ReportWarning("BE0009", "'{0}' is marked with " +
                                       "<exclude /> but conflicted with the API filter " +
                                       "setting.  Exclusion ignored.", member);
                }
            }

            this.ExecutePlugIns(ExecutionBehaviors.After);
        }
        /// <summary>
        /// Edit the project's API filter
        /// </summary>
        /// <param name="sender">The sender of the event</param>
        /// <param name="e">The event arguments</param>
        private void btnEditAPIFilter_Click(object sender, EventArgs e)
        {
#if !STANDALONEGUI
            if(this.ProjectMgr == null)
                return;

            // Apply any pending visibility changes first
            if(this.IsDirty && ((IPropertyPage)this).Apply() != VSConstants.S_OK)
                return;

            // Create an API filter collection that we can edit
            ApiFilterCollection filter = new ApiFilterCollection { Project =
                ((SandcastleBuilderProjectNode)this.ProjectMgr).SandcastleProject };
#else
            if(base.CurrentProject == null)
                return;

            // Apply any pending visibility changes first
            if(this.IsDirty && !this.Apply())
                return;

            // Create an API filter collection that we can edit
            ApiFilterCollection filter = new ApiFilterCollection { Project = base.CurrentProject };
#endif
            filter.FromXml(apiFilter);

            using(ApiFilterEditorDlg dlg = new ApiFilterEditorDlg(filter))
            {
                dlg.ShowDialog();

                string newFilter = filter.ToXml();

                // If it changes, mark the page as dirty and update the local copy of the filter
                if(apiFilter != newFilter)
                {
                    apiFilter = newFilter;
                    this.IsDirty = filterChanged = true;
                    this.UpdateApiFilterInfo();
                }
            }
        }
        //=====================================================================

        /// <summary>
        /// Constructor
        /// </summary>
        /// <overloads>There are five overloads for the constructor</overloads>
        protected SandcastleProject()
        {
            characterMatchEval = new MatchEvaluator(this.OnCharacterMatch);
            buildVarMatchEval = new MatchEvaluator(this.OnBuildVarMatch);

            docSources = new DocumentationSourceCollection(this);
            docSources.ListChanged += docSources_ListChanged;

            namespaceSummaries = new NamespaceSummaryItemCollection(this);
            namespaceSummaries.ListChanged += ItemList_ListChanged;

            references = new ReferenceItemCollection(this);
            references.ListChanged += ItemList_ListChanged;

            componentConfigs = new ComponentConfigurationDictionary(this);
            plugInConfigs = new PlugInConfigurationDictionary(this);

            apiFilter = new ApiFilterCollection(this);
            apiFilter.ListChanged += ItemList_ListChanged;

            helpAttributes = new MSHelpAttrCollection(this);
            helpAttributes.ListChanged += ItemList_ListChanged;

            try
            {
                loadingProperties = removeProjectWhenDisposed = true;

                contentPlacement = ContentPlacement.AboveNamespaces;
                cleanIntermediates = keepLogFile = binaryTOC = includeStopWordList = true;

                this.BuildLogFile = null;

                missingTags = MissingTags.Summary | MissingTags.Parameter | MissingTags.TypeParameter |
                    MissingTags.Returns | MissingTags.AutoDocumentCtors | MissingTags.Namespace |
                    MissingTags.AutoDocumentDispose;

                visibleItems = VisibleItems.InheritedFrameworkMembers | VisibleItems.InheritedMembers |
                    VisibleItems.Protected | VisibleItems.ProtectedInternalAsProtected;

                buildAssemblerVerbosity = BuildAssemblerVerbosity.OnlyWarningsAndErrors;
                helpFileFormat = HelpFileFormats.HtmlHelp1;
                htmlSdkLinkType = websiteSdkLinkType = HtmlSdkLinkType.Msdn;
                help2SdkLinkType = MSHelp2SdkLinkType.Msdn;
                helpViewerSdkLinkType = MSHelpViewerSdkLinkType.Msdn;
                sdkLinkTarget = SdkLinkTarget.Blank;
                presentationStyle = Constants.DefaultPresentationStyle;
                namingMethod = NamingMethod.Guid;
                syntaxFilters = ComponentUtilities.DefaultSyntaxFilter;
                collectionTocStyle = CollectionTocStyle.Hierarchical;
                helpFileVersion = "1.0.0.0";
                tocOrder = -1;
                maximumGroupParts = 2;

                this.OutputPath = null;
                this.HtmlHelp1xCompilerPath = this.HtmlHelp2xCompilerPath = this.WorkingPath =
                    this.ComponentPath = null;

                this.HelpTitle = this.HtmlHelpName = this.CopyrightHref = this.CopyrightText =
                    this.FeedbackEMailAddress = this.FeedbackEMailLinkText = this.HeaderText = this.FooterText =
                    this.ProjectSummary = this.RootNamespaceTitle = this.PlugInNamespaces = this.TopicVersion =
                    this.TocParentId = this.TocParentVersion = this.CatalogProductId = this.CatalogVersion =
                    this.CatalogName = null;
                this.FrameworkVersion = null;

                language = new CultureInfo("en-US");
            }
            finally
            {
                loadingProperties = false;
            }
        }