//=====================================================================
        /// <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);
        }
Esempio n. 2
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);
        }