Esempio n. 1
0
        //=====================================================================

        /// <summary>
        /// This is used to merge an exclusion entry with the filter collection
        /// </summary>
        /// <param name="entryType">The entry type</param>
        /// <param name="fullName">The member's full name</param>
        /// <returns>True if merged without conflict or false if the merged member conflicted with an existing
        /// entry.  The existing entry will take precedence.</returns>
        public bool MergeExclusionEntry(ApiEntryType entryType, string fullName)
        {
            ApiFilter newEntry;

            foreach (ApiFilter child in this)
            {
                if (child.FullName == fullName)
                {
                    // If the exposure doesn't match, use the existing entry and ignore the merged entry
                    if (child.IsExposed)
                    {
                        return(false);
                    }

                    child.IsProjectExclude = true;
                    return(true);
                }
            }

            // It's a new one
            newEntry = new ApiFilter(entryType, fullName, false)
            {
                IsProjectExclude = true
            };

            this.Add(newEntry);

            return(true);
        }
Esempio n. 2
0
        /// <summary>
        /// Constructor.  This takes the API type and the full name
        /// </summary>
        /// <param name="apiType">The API entry type</param>
        /// <param name="name">The fully qualified name</param>
        /// <param name="exposed">True to expose it, false to hide it</param>
        internal ApiFilter(ApiEntryType apiType, string name, bool exposed) : this()
        {
            int pos;

            if (name == null)
            {
                name = String.Empty;
            }

            entryType = apiType;
            fullName  = name;
            isExposed = exposed;

            // By default, we'll use the last part as the filter name unless it's a namespace
            pos = name.LastIndexOf('.');

            if (entryType == ApiEntryType.Namespace || pos == -1)
            {
                this.FilterName = name;
            }
            else
            {
                this.FilterName = name.Substring(pos + 1);
            }
        }
Esempio n. 3
0
            /// <summary>
            /// Determine the API entry type from the ID and possible the subgroup
            /// </summary>
            /// <param name="apiType">The type character to convert</param>
            /// <param name="subgroup">The subgroup to use</param>
            /// <returns>An <see cref="ApiEntryType"/> indicating the entry type</returns>
            internal static ApiEntryType EntryTypeFromId(char apiType, string subgroup)
            {
                ApiEntryType entryType;

                switch(apiType)
                {
                    case 'N':   // Namespace
                        entryType = ApiEntryType.Namespace;
                        break;

                    case 'T':   // A type
                        switch(subgroup)
                        {
                            case "structure":
                                entryType = ApiEntryType.Structure;
                                break;

                            case "interface":
                                entryType = ApiEntryType.Interface;
                                break;

                            case "enumeration":
                                entryType = ApiEntryType.Enumeration;
                                break;

                            case "delegate":
                                entryType = ApiEntryType.Delegate;
                                break;

                            default:
                                entryType = ApiEntryType.Class;
                                break;
                        }
                        break;

                    default:    // Must be a member of some sort
                        switch(subgroup)
                        {
                            case "constructor":
                                entryType = ApiEntryType.Constructor;
                                break;

                            case "operator":
                                entryType = ApiEntryType.Operator;
                                break;

                            case "property":
                                entryType = ApiEntryType.Property;
                                break;

                            case "event":
                                entryType = ApiEntryType.Event;
                                break;

                            case "field":
                                entryType = ApiEntryType.Field;
                                break;

                            default:
                                entryType = ApiEntryType.Method;
                                break;
                        }
                        break;
                }

                return entryType;
            }
Esempio n. 4
0
            /// <summary>
            /// This will determine the API entry type and visibility based on the information in the reflection
            /// information node.
            /// </summary>
            private void DetermineApiEntryType()
            {
                XmlNode subsubgroup;
                string subgroup, entryId;

                // Is it an inherited namespace?
                if(apiNode.Name == "namespace")
                {
                    entryType = ApiEntryType.Namespace;
                    return;
                }

                // Is it an inherited type?
                if(apiNode.Name == "type")
                {
                    // Assume class, it'll be close enough
                    entryType = ApiEntryType.Class;
                    return;
                }

                // It's a documented or inherited member of some sort
                if(apiNode.Name == "element" || apiNode.Name == "type")
                    entryId = apiNode.Attributes["api"].Value;   // Inherited
                else
                {
                    entryId = apiNode.Attributes["id"].Value;    // Documented

                    // Is it a namespace?
                    if(entryId[0] == 'N')
                    {
                        entryType = ApiEntryType.Namespace;
                        return;
                    }
                }

                subsubgroup = apiNode.SelectSingleNode("apidata/@subsubgroup");

                if(subsubgroup != null)
                    subgroup = subsubgroup.Value;
                else
                    subgroup = apiNode.SelectSingleNode("apidata/@subgroup").Value;

                entryType = EntryTypeFromId(entryId[0], subgroup);
                visibility = DetermineVisibility(entryId[0], apiNode);
            }
Esempio n. 5
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 <c>&lt;exclude /&gt;</c> 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 && !ns.IsGroup)
                {
                    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((x, y) =>
            {
                ApiEntryType xType = ApiFilter.ApiEntryTypeFromLetter(x[0]),
                yType = ApiFilter.ApiEntryTypeFromLetter(y[0]);

                if (xType == yType)
                {
                    return(String.Compare(x, y, StringComparison.Ordinal));
                }

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

            // Get the project's API filter and merge the members from the rip list
            var apiFilter = project.ApiFilter;

            // 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.MergeExclusionEntry(ApiEntryType.Namespace, member.Substring(2)))
                    {
                        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);
        }