//===================================================================== /// <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><exclude /></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 generate the API filter collection used by /// MRefBuilder to exclude items from the reflection information file. /// </summary> /// <remarks>Namespaces and members with an <code><exclude /></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); }