/// <summary> /// Makes a table of documentation pulled from the XML C# comments in the assembly for the given type. /// </summary> /// <param name="t">The type to make the documentation table for.</param> /// <param name="summaryDescriptions">The list of all the XML comments for the members of the type.</param> /// <param name="reflectedMembers">The list of all the reflected properties and fields of the type.</param> /// <returns></returns> protected StringBuilder MakeDocumentationTableForType(Type t, List <MemberSummary> summaryDescriptions, List <MemberInfo> reflectedMembers) { if (t == null || summaryDescriptions == null || reflectedMembers == null) { return(null); } StringBuilder tableBuilder = new StringBuilder(); foreach (MemberInfo reflectedMember in reflectedMembers) { MemberSummary match = summaryDescriptions.FirstOrDefault(item => item.Name == reflectedMember.Name); if (ShouldIncludeInDocTable(match, reflectedMember) == false) { continue; } Type itemType = (reflectedMember is PropertyInfo) ? (reflectedMember as PropertyInfo).PropertyType : (reflectedMember as FieldInfo).FieldType; string summary = (match == null) ? "??" : match.Description; string jsType = GetJavaScriptTypeEquivalent(itemType); if (jsType == null) { jsType = "??"; } string tableLine = GetTableLine(reflectedMember, summary, jsType); tableBuilder.AppendLine(tableLine); } return(tableBuilder); }
/// <summary> /// Writes all the lines of documentation needed for the JSON example and properties table for an object of the given type to the internal string builder, _builder. /// </summary> /// <param name="t">The type to produce documentation for.</param> protected void MakeDocumentationForType(Type t) { if (t == null) { return; } List <MemberSummary> matches = GetMemberSummariesForType(t); //find all the summary descriptions from the XML file for the given type and its members List <MemberInfo> reflectedMembers = GetReflectedMembersForType(t); //get all the public fields and properties belonging to the type if (matches == null || reflectedMembers == null || (matches.Count == 0 && reflectedMembers.Count == 0)) { return; } string typeStringToMatch = (t.IsNested == true) ? t.FullName.Replace("+", ".") : t.FullName; MemberSummary typeSummary = matches.Where(item => item.NamespacePath + "." + item.Name == typeStringToMatch && item.MemberType == MemberType.Type).FirstOrDefault(); //get the doc summary entry for the type definition itself string header = t.Name + " - " + ((typeSummary == null) ? "??" : typeSummary.Description); _builder.Append(header); _builder.AppendLine(); _builder.Append(MakeJSONSampleForType(t, reflectedMembers)); _builder.AppendLine("\n"); _builder.Append(MakeDocumentationTableForType(t, matches, reflectedMembers)); _builder.AppendLine("---------------------------------------------------------------------------------------"); }
/// <summary> /// Whether or not to include a member in the table of summary descriptions. /// </summary> /// <param name="summary">The summary description from the XML doc file.</param> /// <param name="reflectionMemberInfo">The reflected member info of the member to include or not.</param> /// <returns></returns> protected virtual bool ShouldIncludeInDocTable(MemberSummary summary, MemberInfo reflectionMemberInfo) { if (reflectionMemberInfo.DeclaringType.IsEnum == true && reflectionMemberInfo.Name == "value__") { return(false); } return(true); }
/// <summary> /// Reads the assembly's XML documentation and parses out the names and summary descriptions of each member. /// </summary> /// <param name="xmlDocFilePath"></param> protected void ReadXMLDoc(string xmlDocFilePath) { if (xmlDocFilePath == null || File.Exists(xmlDocFilePath) == false) { return; } using (XmlReader reader = XmlTextReader.Create(xmlDocFilePath)) { while (reader.Read() == true) { if (reader.Name != "member" || reader.NodeType == XmlNodeType.EndElement) { continue; } string name = reader.GetAttribute("name"); string summary = null; while (reader.Read() == true) { if (reader.Name == "summary") { reader.Read(); summary = reader.Value.Trim(); break; } } MemberSummary info = ProcessXMLData(name, summary, reader); if (info != null) { _members.Add(info); } } } }
/// <summary> /// Walks through the parameters for a method in the xml documentation file, parses them, and adds them as MethodDefinition.MethodParameter to the methodInfo's parameter list. /// </summary> /// <param name="methodInfo">The data from the XML file that pertains to the method and its signature.</param> /// <param name="reader">The XML reader that is reading the </param> /// <returns></returns> protected MemberSummary ReadMethodParameters(MemberSummary methodInfo, XmlReader reader) { if (methodInfo == null || reader == null) { return(null); } string originalName = methodInfo.Name; string[] parameterTypes = null; if (methodInfo.Name != null) { int signatureStart = methodInfo.Name.IndexOf("("); if (signatureStart != -1) { methodInfo.Name = methodInfo.Name.Substring(0, signatureStart); string paramTypesString = originalName.Substring(signatureStart + 1, originalName.Length - signatureStart - 2); //skip over first parenthsis, last parenthsis parameterTypes = paramTypesString.Split(','); if (parameterTypes.Length == 0) { parameterTypes = null; } } if (methodInfo.Name.Contains("#ctor") == true) //its a constructor - we dont want the #ctor in the name because it is ugly and will confuse some developers/consumers of documentation { int finalSegmentStart = methodInfo.NamespacePath.LastIndexOf("."); if (finalSegmentStart != -1) { methodInfo.Name = methodInfo.NamespacePath.Substring(finalSegmentStart + 1, methodInfo.NamespacePath.Length - finalSegmentStart - 1); } } } int paramNumber = 0; while (reader.Read() == true) { if (reader.Name == "member" && reader.NodeType == XmlNodeType.EndElement) { break; //reached the end of the method defintion } if (reader.Name != "param" || reader.NodeType == XmlNodeType.EndElement) { continue; } string name = reader.GetAttribute("name"); string summary = null; reader.Read(); summary = reader.Value.Trim(); MethodDefinition.MethodParameter curParam = new MethodDefinition.MethodParameter(); curParam.ParamName = name; curParam.ParamDescription = summary; if (parameterTypes != null && paramNumber <= parameterTypes.Length) { curParam.ParamType = parameterTypes[paramNumber]; //we got the types from the method signature and the params are in the same order as the types, so we can match them up. } (methodInfo as MethodDefinition).Parameters.Add(curParam); paramNumber++; } return(methodInfo); }
/// <summary> /// Turns an entry from a XML documentation file into a MemberSummary object. /// </summary> /// <param name="name">The value of the "Name" attribute from the XML documentation.</param> /// <param name="summary">The summary string from the XML documentation.</param> /// <param name="reader">The XML reader that is reading the XML file.</param> /// <returns></returns> protected MemberSummary ProcessXMLData(string name, string summary, XmlReader reader) { if (name == null || summary == null || name.Length == 0) { return(null); } MemberSummary info = null; string firstChar = name.Substring(0, 1).ToLower(); if (firstChar == "p" || firstChar == "t" || firstChar == "f") //property, field, or type { int lastDot = name.LastIndexOf("."); string description = summary; string namespacePath = name.Substring(2, lastDot - 2); //the name of the member always starts with a type prefix and a colon, get the rest of the string after the prefix string memberName = name.Substring(lastDot + 1); info = new MemberSummary(); info.Name = memberName; info.Description = description; info.NamespacePath = namespacePath; if (firstChar == "p") { info.MemberType = MemberType.Property; } else if (firstChar == "t") { info.MemberType = MemberType.Type; } else if (firstChar == "f") { info.MemberType = MemberType.Field; } } else if (firstChar == "m") //method { int lastDot = name.LastIndexOf("."); int firstParenthesis = name.IndexOf("("); string description = summary; string namespacePath = null; string memberName = null; if (firstParenthesis == -1) //no parenthsis on the method signature in the xml documentation if it has no parameters { namespacePath = name.Substring(2, lastDot - 2); //the name of the member always starts with a type prefix and a colon, get the rest of the string after the prefix memberName = name.Substring(lastDot + 1); } else { string withoutParams = name.Substring(0, firstParenthesis); //cut off the parameter string lastDot = withoutParams.LastIndexOf("."); //get the last dot in the parameterless name (otherwise the namespaces of the parameters get used as the last dot) namespacePath = withoutParams.Substring(2, lastDot - 2); //the name of the member always starts with a type prefix and a colon, get the rest of the string after the prefix memberName = name.Substring(namespacePath.Length + 3); //2 for the prefix, 1 for the last dot } info = new MethodDefinition(); info.Name = memberName; info.Description = description; info.NamespacePath = namespacePath; info.MemberType = MemberType.Method; return(ReadMethodParameters(info, reader)); } return(info); }