/* Copyright (c) Eric Ledoux. All rights reserved. */ /* See http://www.dwell.net/terms for code sharing information. */ // DocumentationSet.cs // // Implements the DocumentationSet class and related functionality. // using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Xml; using DwellNet.CodeDoc; using CodeDocApi.Properties; namespace DwellNet.CodeDoc {
DocumentationSet Class
Represents a CodeDoc documentation set, which is a collection of source files that generates a single linked set of documentation. |
public class DocumentationSet { ////////////////////////////////////////////////////////////////////////// // Private Static Fields //
readonly static string[] s_supportedExtensions = new string[] { ".cs" };
DocumentationSet.s_aliasPrefixRegex Field
Matches a namespace alias qualifier prefix, e.g. "RE." in "RE.Regex". 25.3 of the C# Language Specification 2.0. spaces. |
readonly static Regex s_aliasPrefixRegex = new Regex(@"^([a-zA-Z_][a-zA-Z0-9_]*)\.(.*)");
DocumentationSet.s_namespaceAliasQualifierRegex Field
Matches a namespace alias qualifier, e.g. "SIO::" in "SIO::Stream". See section 25.3 of the C# Language Specification 2.0. |
readonly static Regex s_namespaceAliasQualifierRegex = new Regex(@"^([a-zA-Z_][a-zA-Z0-9_]*)::(.*)"); ////////////////////////////////////////////////////////////////////////// // Private Fields That Persist After ClearSourceFiles() //
DocumentationSet.m_lastGeneratedTopicId Field
The last TopicId that was automatically generated by DocumentationSet.
See Also
|
int m_lastGeneratedTopicId = 1000000;
List<string> m_definedSymbols = new List<string>(20);
DocumentationSet.m_allowedXmlElements Field
Contains (within the dictionary keys) XML element names (e.g. "b" for "<b>") that are allowed within XML comment sections; elements not listed are still allowed but a warning is generated, since such an element may be a mistake (e.g. "<r>" mistyped). The values in this dictionary are all true.
Remarks
The <AllowedXmlElements> section of the CodeDoc XML project file contains XML element names that are used to populate this field. |
Dictionary<string, bool> m_allowedXmlElements = new Dictionary<string,bool>(100); ////////////////////////////////////////////////////////////////////////// // Private Fields That Are Cleared By ClearSourceFiles() //
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
bool m_externalOnly;
bool m_begunUsingSources;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
List<SourceFile> m_sourceFiles;
DocumentationSet.m_topics Field
Contains all topics in the documentation set. The key is a Topic.CanonicalName; the value is the Topic that that canonical name refers to. |
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
Dictionary<string, Topic> m_topics;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
Dictionary<string, NamespaceTopic> m_namespaceTopics;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
Dictionary<string, OverloadListTopic> m_overloadListTopics;
//////////////////////////////////////////////////////////////////////////
// Public Properties
//
DocumentationSet.SupportedExtensions Property
Gets the file name extensions (for example, ".cs") of each type of source file supported by this class. |
public static string[] SupportedExtensions { [DebuggerStepThrough] get { return s_supportedExtensions; } }
DocumentationSet.SourceFiles Property
The collection of source files in this documentation set. |
public ICollection<SourceFile> SourceFiles { [DebuggerStepThrough] get { return new ReadOnlyCollection<SourceFile>(m_sourceFiles); } }
public ICollection<NamespaceTopic> NamespaceTopics { [DebuggerStepThrough] get { return m_namespaceTopics.Values; } }
DocumentationSet.OverloadListTopics Property
The collection of OverloadListTopic topics. These are topics that represent a collection of other topics that all have the same Topic.BaseName property value. |
public ICollection<OverloadListTopic> OverloadListTopics { [DebuggerStepThrough] get { return m_overloadListTopics.Values; } }
DocumentationSet.ExternalOnly Property
Gets or sets a value indicating if this is an external-only documentation build, meaning that only types and members accessible outside this program or library are being documented.
Remarks
In addition to setting ExternalOnly, the application must set IsDocumented on individual topics. |
public bool ExternalOnly { [DebuggerStepThrough] get { return m_externalOnly; } [DebuggerStepThrough] set { m_externalOnly = value; } }
DocumentationSet.DefinedSymbols Property
Gets the initial set of defined conditional compilation symbols that are defined at the beginning of subsequently-loaded source file; for example, "DEBUG". Additional symbols may be defined using
Remarks
The <DefinedSymbols> section of the CodeDoc XML project file contains the symbols used to populate this field. The application is required to initialize DefinedSymbols by calling DefineSymbol once per symbol. ClearSymbols can be used to empty the list of defined symbols so that the list can be changed for subsequently-loaded source files. |
public ICollection<string> DefinedSymbols { [DebuggerStepThrough] get { return new ReadOnlyCollection<string>(m_definedSymbols); } }
DocumentationSet.AllowedXmlElements Property
Enumerates through the list of XML element names (e.g. "b" for "<b>") that are allowed within XML comment sections; elements not listed are still allowed but a warning is generated, since such an element may be a mistake (e.g. "<r>" mistyped).
Remarks
The <AllowedXmlElements> section of the CodeDoc XML project file contains XML element names that are used to populate this property.
See Also
|
public IEnumerable<string> AllowedXmlElements { get { return m_allowedXmlElements.Keys; } }
DocumentationSet.GetSortedNamespaceTopics Method
Returns the collection of TopicKind.NamespaceTopic topics, sorted by namespace name. The order of NamespaceTopics is not affected. |
public NamespaceTopic[] GetSortedNamespaceTopics() { Dictionary<string, NamespaceTopic>.ValueCollection values = m_namespaceTopics.Values; NamespaceTopic[] array = new NamespaceTopic[values.Count]; values.CopyTo(array, 0); Array.Sort<Topic>(array); return array; }
DocumentationSet.Topics Property
Enumerates all topics in the documentation set. |
public IEnumerable<Topic> Topics { get { // loop once per namespace, including the global namespace foreach (NamespaceTopic namespaceTopic in NamespaceTopics) { #if true // yield this namespace topic yield return namespaceTopic; #else // yield this namespace topic, unless it's the global // namespace if (namespaceTopic.MemberName.Length > 0) yield return namespaceTopic; #endif // loop once per type in this namespace, including nested types foreach (Topic typeTopic in namespaceTopic.TypeTopics) { // yield this type topic yield return typeTopic; // yield members of this type topic, but skip type topics // since we're already enumerating through all type topics foreach (Topic childTopic in Topic.EnumerateDescendentNonTypes(typeTopic)) yield return childTopic; } } } } ////////////////////////////////////////////////////////////////////////// // Public Events //
DocumentationSet.AddingSourceFile Event
Fired before a source file is added to the documentation set.
Remarks
The application may want to use this event to provide feedback to the user indicating that a source file is being loaded. |
public event AddingSourceFileEventDelegate AddingSourceFile;
DocumentationSet.AddedSourceFile Event
Fired after a source file is successfully added to the documentation set (with or without warnings).
Remarks
The application may want to use this event to check for warnings in the SourceFile object and display them to the user. |
public event AddedSourceFileEventDelegate AddedSourceFile;
DocumentationSet.ParsingError Event
Fired to indicate that an error occurred while trying to parse a source file. |
public event ParsingErrorEventDelegate ParsingError;
DocumentationSet.DuplicateTopicTitle Event
Fired to indicate that two topics have the same Topic.LongTitle. This is typically a warning, displayed to the user -- it may indicate that the same source file was added twice to the documentation set. |
public event DuplicateTopicTitleEventDelegate DuplicateTopicTitle;
DocumentationSet.DuplicateCanonicalName Event
Fired to indicate that two topics have the same Topic.CanonicalName. This is purely informational -- it does not indicate an error. (Typially it indicates that one or more overload list topics are being generated.) |
public event DuplicateCanonicalNameEventDelegate DuplicateCanonicalName;
DocumentationSet.ReferenceNotFound Event
Fired to indicate that a topic made a potential reference to another topic which wasn't found. This is typically an informational message, displayed to the user after sorting all such messages and removing duplicates. This event does not necessarily indicate an error. |
public event ReferenceNotFoundEventDelegate ReferenceNotFound; ////////////////////////////////////////////////////////////////////////// // Public Methods //
DocumentationSet Constructor
Initializes an instance of this class. |
public DocumentationSet() { // initialize ClearSourceFiles(); }
DocumentationSet.CopyCoreState Method
Copies the "core" state of this DocumentationSet to another DocumentationSet. "Core" state includes the list of source files, defined symbols, allowed XML elements, and the state of GenerateTopicId, but does not include which topics are documented.
Parameters
other The DocumentationSet to copy to. |
public void CopyCoreState(DocumentationSet other) { // copy state used by GenerateTopicId() other.m_lastGeneratedTopicId = m_lastGeneratedTopicId; // copy source files foreach (SourceFile sourceFile in SourceFiles) { other.AddSourceFile(sourceFile.SourceFileAbsolutePath, sourceFile.SourceFileRelativePath, sourceFile.AssemblyName); } // copy the collection of defined symbols foreach (string symbol in DefinedSymbols) other.DefineSymbol(symbol); // copy the collection of allowed XML elements foreach (string elementName in AllowedXmlElements) other.AllowXmlElement(elementName); }
DocumentationSet.AddSourceFile Method
Adds a source code file to this documentation set. Creates an instance of the correct subclass of SourceFile based on the extension of a given source file name.
Parameters
sourceFileAbsolutePath The value to initialize the SourceFile.SourceFileAbsolutePath property to. sourceFileRelativePath The value to initialize the SourceFile.SourceFileRelativePath property to. assemblyName Specifies the name of the assembly that contains the code compiled from these source files, or null to specify none. If specified, the assembly name is included in topic HTML.
Return Value
The subclass of SourceFile that was created to represent the specified source file. The returned object is added to this documentation set.
Exceptions
Remarks
The programming language of the source file is determined based on the file name extension of sourceFileAbsolutePath and sourceFileRelativePath (which must be the same for both files). Currently, only C# source files, with an extension of ".cs", are supported. |
public SourceFile AddSourceFile(string sourceFileAbsolutePath, string sourceFileRelativePath, string assemblyName) { // don't allow source files to be added if operations have begun that // make use of those source files if (m_begunUsingSources) throw new BegunUsingSourcesException(Resources.BegunUsingSources); // notify the application that a source file is about to be added to // the documentation set if (AddingSourceFile != null) AddingSourceFile(sourceFileAbsolutePath, sourceFileRelativePath); // set <extension> to the file name extension of the source file, // converted to lowercase string extension = Path.GetExtension(sourceFileAbsolutePath) .ToLowerInvariant(); if ((sourceFileRelativePath != null) && (extension != Path.GetExtension(sourceFileRelativePath) .ToLowerInvariant())) { throw new ArgumentException(Resources.InconsistentExtension, "sourceFileAbsolutePath"); } // create an instance, <sourceFile>, of the appropriate subclass based // on <extension> SourceFile sourceFile; if (extension == ".cs") { sourceFile = new CSharpSourceFile(this, sourceFileAbsolutePath, sourceFileRelativePath); } else { throw new ArgumentException( String.Format(Resources.SourceFileExtensionNotSupported, extension, sourceFileAbsolutePath), "sourceFileAbsolutePath"); } // associate <assemblyName> (if any) with <sourceFile> sourceFile.AssemblyName = assemblyName; // give <sourceFile> a unique numeric ID sourceFile.SourceFileTopicId = GenerateTopicId(); // add <sourceFile> to the documentation set m_sourceFiles.Add(sourceFile); // load and parse <sourceFile> sourceFile.ExceptionOnInvalidXmlComment = false; sourceFile.InvalidXmlComment += delegate(ParsingException exception) { if (ParsingError != null) ParsingError(exception); }; sourceFile.Load(DefinedSymbols); // notify the application that a source file has been successfully // added to the documentation set if (AddedSourceFile != null) AddedSourceFile(sourceFile); // done return sourceFile; }
DocumentationSet.AddSourceFilesInDirectoryTree Method
Adds to this documentation set each source file with a supported file name extension (for example, ".cs") within a given directory tree.
Parameters
treePath The full path to the root of the directory tree. relativePath For each source file in the tree, its path relative to treePath, e.g. "Def\Ghi.cs", is appended to relativePath, e.g. "Abc", resulting in a compound relative path, e.g. "Abc\Def\Ghi.cs", that's used as the SourceFile.SourceFileRelativePath property. Use "" for no prefix. excludeFilePaths For each source file in the tree, if its path is matched by any regular expression in this list, the source file is ignored. This parameter is ignored if null. assemblyName Specifies the name of the assembly that contains the code compiled from these source files, or null to specify none. If specified, the assembly name is included in topic HTML. |
public void AddSourceFilesInDirectoryTree(string treePath, string relativePath, List<Regex> excludeFilePaths, string assemblyName) { // don't allow source files to be added if operations have begun that // make use of those source files if (m_begunUsingSources) throw new BegunUsingSourcesException(Resources.BegunUsingSources); // enumerate all source files in this directory foreach (string extension in SupportedExtensions) { foreach (string sourceFilePath in Directory.GetFiles(treePath, "*" + extension)) { // skip this source file if it matches any regular expression // in <excludeFilePaths> if ((excludeFilePaths != null) && (excludeFilePaths.Find( delegate(Regex regex) { return regex.Match(sourceFilePath).Success; }) != null)) { continue; } // add the source file string sourceFileName = Path.GetFileName(sourceFilePath); AddSourceFile(sourceFilePath, Path.Combine(relativePath, sourceFileName), assemblyName); } } // recurse into subdirectories foreach (string subdirPath in Directory.GetDirectories(treePath)) { string dirName = Path.GetFileName(subdirPath); AddSourceFilesInDirectoryTree(subdirPath, Path.Combine(relativePath, dirName), excludeFilePaths, assemblyName); } }
DocumentationSet.ClearSourceFiles Method
Removes all source files from this documentation set. |
public void ClearSourceFiles() { m_externalOnly = false; m_begunUsingSources = false; m_sourceFiles = new List<SourceFile>(50); m_topics = new Dictionary<string, Topic>(500); m_namespaceTopics = new Dictionary<string, NamespaceTopic>(50); m_overloadListTopics = new Dictionary<string, OverloadListTopic>(50); }
DocumentationSet.DefineSymbol Method
Adds a conditional compilation symbol to DefinedSymbols.
Parameters
symbol The symbol to define; for example, "DEBUG".
See Also
|
public void DefineSymbol(string symbol) { m_definedSymbols.Add(symbol); }
DocumentationSet.ClearSymbols Method
Empties the list of conditional complilation symbols contained in DefinedSymbols so that the list can be changed for subsequently-loaded source files (using DefineSymbol).
See Also
|
public void ClearSymbols() { m_definedSymbols.Clear(); }
DocumentationSet.AllowXmlElement Method
Adds an XML element names (e.g. "b" for "<b>") that is allowed within XML comment sections; elements not listed are still allowed but a warning is generated, since such an element may be a mistake (e.g. "<r>" mistyped).
Parameters
elementName The XML element name; for example "b".
Remarks
The <AllowedXmlElements> section of the CodeDoc XML project file contains allowed XML element names.
See Also
|
public void AllowXmlElement(string elementName) { m_allowedXmlElements[elementName] = true; }
DocumentationSet.ClearAllowedXmlElements Method
Empties the list of XML element names that are allowed within XML comment sections, so that the list can be changed for subsequently-loaded source files (using AllowXmlElement).
See Also
|
public void ClearAllowedXmlElements() { m_allowedXmlElements.Clear(); }
DocumentationSet.IsAllowedXmlElement Method
Returns true if a given element name is allowed within XML comment sections.
Parameters
elementName The XML element name; for example "b".
Remarks
See AllowXmlElement for more information. |
public bool IsAllowedXmlElement(string elementName) { bool unused; return m_allowedXmlElements.TryGetValue(elementName, out unused); }
DocumentationSet.FindNamespaceTopic Method
Returns the NamespaceTopic corresponding to a given namespace name (e.g. "Foo.Bar"), or null if that namespace isn't currently defined in NamespaceTopics.
Parameters
namespaceName The namespace name; for example, "Foo.Bar". |
public NamespaceTopic FindNamespaceTopic(string namespaceName) { NamespaceTopic topic; if (m_namespaceTopics.TryGetValue(namespaceName, out topic)) return topic; else return null; }
DocumentationSet.FindDocumentedTopicByCanonicalName Method (string)
Finds a topic that has Topic.IsDocumented equal to true given a topic's canonical name. Returns null if the topic isn't found.
Parameters
canonicalName The canonical name of the topic; for example, "Foo.Bar.Abc".
Remarks
If a topic with the given canonical name is found but has Topic.IsDocumented equal to |
public Topic FindDocumentedTopicByCanonicalName(string canonicalName) { Topic topic; if (m_topics.TryGetValue(canonicalName, out topic)) { if (topic.IsDocumented) return topic; else if (topic.CanLinkTo && (topic.ParentTopic != null)) return topic.ParentTopic; else return null; } else return null; }
DocumentationSet.FindDocumentedTopicByCanonicalName Method (string, FindTopicSettings)
Finds a topic that has Topic.IsDocumented equal to true given a topic's canonical name, with specific search conditions. Returns null if the topic isn't found.
Parameters
canonicalName The canonical name of the topic; for example, "Foo.Bar.Abc". settings Settings that control the method. |
public Topic FindDocumentedTopicByCanonicalName(string canonicalName, FindTopicSettings settings) { // look for the topic specified by <canonicalName>; return null if not // found Topic targetTopic = FindDocumentedTopicByCanonicalName(canonicalName); if (targetTopic == null) return null; // if the caller only wants a type topic, return null if <targetTopic> // isn't one if (((settings & FindTopicSettings.TypeOnly) != 0) && !targetTopic.IsTypeTopic) return null; return targetTopic; }
DocumentationSet.FindDocumentedTopic Method
Finds a topic that has Topic.IsDocumented equal to true given a member name of the sort that might be found in XML comments. Returns null if the topic isn't found.
Parameters
memberName The qualified or unqualified member name. Examples: "Bar", "Foo.Bar", "Foo<T>.Bar". contextTopic The topic containing the member name reference. For example, if contextTopic is "Foo.Abc", i.e. method "Abc" in class "Foo", then if memberName is "Bar" and "Bar" is another method in "Foo" then the return value will refer to topic "Bar". lineNumber The line number of the reference, within the source file containing contextTopic. settings Settings that control the method. |
public Topic FindDocumentedTopic(string memberName, Topic contextTopic, int lineNumber, FindTopicSettings settings) { // set <canonicalName> to the canonicalized version of <memberName> string canonicalName; if ((settings & FindTopicSettings.Precanonicalized) != 0) canonicalName = memberName; // <memberName> already canonicalized else { canonicalName = Topic.CanonicalizeMemberName(memberName, false); } // keep track of whether we failed due to the referenced topic being // the same as <contextTopic> bool isSame = false; // fail without firing the ReferenceNotFound event if <canonicalName> // is a type parameter if (contextTopic.TypeParameters != null) { if (Array.Find(contextTopic.TypeParameters, delegate(TokenList t) { return (t.ToString() == canonicalName); }) != null) return null; } // if specified by the caller, try looking up <canonicalName> within // all ancestors (base types, base types of base types, etc.) of T, // where T is either <contextTopic>, if <contextTopic> is a type topic, // or the type topic containing <contextTopic> Topic targetTopic; if (((settings & FindTopicSettings.SearchAncestors) != 0) || ((settings & FindTopicSettings.SearchOnlyAncestors) != 0)) { // set <typeTopic> to T (see above) Topic typeTopic = contextTopic; while ((typeTopic != null) && !typeTopic.IsTypeTopic) typeTopic = typeTopic.ParentTopic; // if we found T, search ancestors of T if (typeTopic != null) { if (((targetTopic = FindDocumentedTopicInAncestors( canonicalName, contextTopic, lineNumber, typeTopic)) != null) && !IsSame(targetTopic, contextTopic, ref isSame)) return targetTopic; } // if SearchOnlyAncestors is specified, don't look any further if ((settings & FindTopicSettings.SearchOnlyAncestors) != 0) { if ((ReferenceNotFound != null) && !isSame) { ReferenceNotFound(canonicalName, contextTopic, lineNumber, settings); } return null; } } // note: the following is based loosely on section 3.8 of the C# // Language Specification 1.2... // try looking up <canonicalName> within the enclosing type(s) (if // any), i.e. types that <contextTopic> is nested within Topic otherTopic = contextTopic; TokenList otherName; while (true) { #if false if ((otherTopic = otherTopic.ParentTopic) == null) break; #endif if (otherTopic.TopicKind == TopicKind.NamespaceTopic) break; if (otherTopic.TypeParameters != null) { if (Array.Find(otherTopic.TypeParameters, delegate(TokenList t) { // check if <canonicalName> is a type parameter return (t.ToString() == canonicalName); }) != null) return null; } if ((otherName = otherTopic.CanonicalName) != null) { if (((targetTopic = FindDocumentedTopicByCanonicalName( otherName + "." + canonicalName, settings)) != null) && !IsSame(targetTopic, contextTopic, ref isSame)) return targetTopic; } #if true if ((otherTopic = otherTopic.ParentTopic) == null) break; #endif } // try looking up <canonicalName> within the namespace of // <contextTopic> (if any), and any ancestor namespaces (see section // 3.7 of "C# Language Specification 1.2") if (((otherTopic = contextTopic.NamespaceTopic) != null) && ((otherName = otherTopic.CanonicalName) != null)) { string namespaceName = otherName.ToString(); while (true) { // look up <canonicalName> within <namespaceName> if (((targetTopic = FindDocumentedTopicByCanonicalName( namespaceName + "." + canonicalName, settings)) != null) && !IsSame(targetTopic, contextTopic, ref isSame)) return targetTopic; // set <namespaceName> to be its own parent int ich = namespaceName.LastIndexOf('.'); if (ich < 0) break; namespaceName = namespaceName.Substring(0, ich); } } // try looking up <canonicalName> within aliased namespaces Match match, match2 = null; if (!(match = match2 = s_namespaceAliasQualifierRegex.Match(canonicalName)).Success) match = s_aliasPrefixRegex.Match(canonicalName); if (match.Success) { string alias = match.Groups[1].Value; string remainder = match.Groups[2].Value; if ((alias == "global") && (match2 != null)) { // e.g. "global::MyType" if (((targetTopic = FindDocumentedTopicByCanonicalName( remainder, settings)) != null) && !IsSame(targetTopic, contextTopic, ref isSame)) return targetTopic; } else { // e.g. "RE.RegularExpression" TokenList namespaceOrTypeName = contextTopic.UsingContext.LookupAlias(alias); if (namespaceOrTypeName != null) { if (((targetTopic = FindDocumentedTopicByCanonicalName( namespaceOrTypeName + "." + remainder, settings)) != null) && !IsSame(targetTopic, contextTopic, ref isSame)) return targetTopic; } } } // see if <canonicalName> is a class alias TokenList typeName = contextTopic.UsingContext.LookupAlias( canonicalName); if (typeName != null) { if (((targetTopic = FindDocumentedTopicByCanonicalName( Topic.CanonicalizeMemberName(typeName, false) .ToString(), settings)) != null) && !IsSame(targetTopic, contextTopic, ref isSame)) return targetTopic; } // try looking up <canonicalName> within unaliased namespaces foreach (TokenList namespaceName in contextTopic.UsingContext.Unaliased) { if (((targetTopic = FindDocumentedTopicByCanonicalName( namespaceName + "." + canonicalName, settings)) != null) && !IsSame(targetTopic, contextTopic, ref isSame)) return targetTopic; } // try looking up <canonicalName> in the global namespace if (((targetTopic = FindDocumentedTopicByCanonicalName(canonicalName, settings)) != null) && !IsSame(targetTopic, contextTopic, ref isSame)) return targetTopic; // <canonicalName> not found -- tell the application, unless this is // due to the referenced topic being the same as the context topic, // which happens normally in cases like "class Foo : IComparable<Foo>" if ((ReferenceNotFound != null) && !isSame) { ReferenceNotFound(canonicalName, contextTopic, lineNumber, settings); } return null; }
DocumentationSet.FindDocumentedTopicInAncestors Method
Search the base types and ancestors (i.e. base types of base types, and so on) of a given type for a given member. Returns null if the topic isn't found.
Parameters
memberName The qualified or unqualified member name. Examples: "Bar", "Foo.Bar", "Foo<T>.Bar". contextTopic The topic containing the member name reference. lineNumber The line number of the reference, within the source file containing contextTopic. typeTopic All ancestors of typeTopic are searched for memberName. |
Topic FindDocumentedTopicInAncestors(string memberName, Topic contextTopic, int lineNumber, Topic typeTopic) { // do nothing if <typeTpic> has no base types if (typeTopic.BaseTypes == null) return null; // loop once for each base type of <typeTopic> foreach (TokenList baseType in typeTopic.BaseTypes) { // check if <memberName> is a member of <baseType> Topic baseTypeTopic = FindDocumentedTopic(baseType.ToString(), typeTopic, lineNumber, FindTopicSettings.TypeOnly); Topic targetTopic; if (baseTypeTopic != null) { targetTopic = FindDocumentedTopicByCanonicalName( baseTypeTopic + "." + memberName); if (targetTopic != null) return targetTopic; } // recurse if (baseTypeTopic != null) { if ((targetTopic = FindDocumentedTopicInAncestors(memberName, contextTopic, lineNumber, baseTypeTopic)) != null) return targetTopic; } } // not found return null; }
DocumentationSet.BeginUsingSources Method
Indicates that an operation that uses the list of source files is about to begin. At this point, any processing of the list of source files that needs to be done should be done. If that processing was previously performed, nothing is done. Any attempt to add source files after this point generates an exception.
Remarks
Code that renders documentation should call this method before starting the rendering process. Among other things, this method ensures that all topics are given unique HTML file names. |
public void BeginUsingSources() { // do nothing if this method was previously called if (m_begunUsingSources) return; else m_begunUsingSources = true; // perform processing on the set of source files that's required before // generating documentation etc... // create a unique Topic.Name property for each topic; see // MakeUniqueTopicName() for more information about the former Dictionary<string, List<Topic>> topicsPerBaseName = new Dictionary<string, List<Topic>>(1000); foreach (Topic topic in Topics) MakeUniqueTopicName(topic, topicsPerBaseName); // add each topic in <OverloadListTopics> to various lists foreach (OverloadListTopic topic in OverloadListTopics) { if (topic.ParentTopic != null) topic.ParentTopic.AddChildTopic(topic); if (topic.IsTypeTopic) topic.NamespaceTopic.AddTypeTopic(topic); } // add all topics to <m_topics>, and give each a unique integer Id // property; also, see if any two topics have the same <LongTitle> Dictionary<string, Topic> duplicateTitles = new Dictionary<string,Topic>(500); foreach (Topic topic in Topics) { // add <topic> to <m_topics> Topic otherTopic; string canonicalName = topic.CanonicalName.ToString(); if (m_topics.TryGetValue(canonicalName, out otherTopic)) { // fire DuplicateTopicTitle event if (DuplicateCanonicalName != null) DuplicateCanonicalName(topic, otherTopic); } else m_topics.Add(canonicalName, topic); // give <topic> a unique Id property topic.Id = GenerateTopicId(); // see if <topic> has the same <LongTitle> as another topic if (topic.SourceFile == null) // e.g. overload list topic continue; string longTitle = topic.LongTitle; if (duplicateTitles.TryGetValue(longTitle, out otherTopic)) { // fire DuplicateTopicTitle event if (DuplicateTopicTitle != null) DuplicateTopicTitle(topic, otherTopic); } else duplicateTitles.Add(longTitle, topic); } }
DocumentationSet.AfterSettingIsDocumented Method
Performs general processing on the list of topics, such as determining the base class of each documented topic, that must occur after the application has set Topic.IsDocumented to true for all topics the applicatin wants documented. |
public void AfterSettingIsDocumented() { // determine the base class of each documented class topic foreach (Topic topic in Topics) { // skip undocumented topics if (!topic.IsDocumented) continue; // skip topics that don't document classes if (topic.TopicKind != TopicKind.ClassTopic) continue; // skip overload list topics if (topic is OverloadListTopic) continue; // skip topics without base types if (topic.BaseTypes == null) continue; // attempt to find the base class among the base types -- there // should only be one type among the base types that's a class Topic baseClassTopic = null; foreach (TokenList baseType in topic.BaseTypes) { Topic targetTopic = FindDocumentedTopic(baseType.ToString(), topic, 0, FindTopicSettings.TypeOnly); if ((targetTopic != null) && (targetTopic.TopicKind == TopicKind.ClassTopic)) { if (baseClassTopic != null) { ParsingError(topic.NewParsingException( Resources.CannotDetermineBaseClass, topic.QualifiedMemberName)); break; } else baseClassTopic = targetTopic; } } if (baseClassTopic == null) continue; // set the base class of this topic topic.BaseClassTopic = baseClassTopic; // for debugging purposes #if false && DEBUG Console.WriteLine("Base class of {0} is {1}", topic, baseClassTopic); #endif } }
DocumentationSet.MakeUniqueTopicName Method
Creates a unique Topic.Name for a given topic. Creates an overload list topic if necessary.
Parameters
topic The topic to set the Topic.Name property of. topicsPerBaseName A dictionary that maps a given Topic.BaseName property to a set of topics that have that same base name. |
void MakeUniqueTopicName(Topic topic, Dictionary<string, List<Topic>> topicsPerBaseName) { string baseName = topic.BaseName; List<Topic> topicsForThisBaseName; if (!topicsPerBaseName.TryGetValue(baseName, out topicsForThisBaseName)) { // <topic> is the first topic with <baseName> -- create a // new list for this base name and add it to the dictionary topicsForThisBaseName = new List<Topic>(); topic.Name = baseName; topicsForThisBaseName.Add(topic); topicsPerBaseName.Add(baseName, topicsForThisBaseName); } else { OverloadListTopic overloadListTopic; if (topicsForThisBaseName.Count == 1) { // <topic> is the second topic with <baseName> -- change // the name of the first topic with <baseName> to have a ".1" // suffix Topic firstTopic = topicsForThisBaseName[0]; firstTopic.Name = baseName + ".1"; // create an overload list topic for <baseName> overloadListTopic = new OverloadListTopic(); overloadListTopic.DocumentationSet = this; overloadListTopic.NamespaceTopic = firstTopic.NamespaceTopic; overloadListTopic.ParentTopic = firstTopic.ParentTopic; overloadListTopic.IsTypeTopic = firstTopic.IsTypeTopic; overloadListTopic.TopicKind = firstTopic.TopicKind; overloadListTopic.NamespaceName = firstTopic.NamespaceName; overloadListTopic.MemberName = firstTopic.MemberName; overloadListTopic.MethodKind = firstTopic.MethodKind; overloadListTopic.PropertyKind = firstTopic.PropertyKind; overloadListTopic.Name = baseName; // add <firstTopic> to <overloadListTopic> overloadListTopic.AddOverloadTopic(firstTopic); firstTopic.OverloadListTopic = overloadListTopic; m_overloadListTopics.Add(baseName, overloadListTopic); } else overloadListTopic = m_overloadListTopics[baseName]; // add this topic to <overloadListTopic> overloadListTopic.AddOverloadTopic(topic); topic.OverloadListTopic = overloadListTopic; // this topic gets the next available numeric suffix topicsForThisBaseName.Add(topic); topic.Name = String.Format("{0}.{1}", baseName, topicsForThisBaseName.Count); } }
DocumentationSet.GenerateTopicId Method
Returns an automatically generated topic ID. |
public int GenerateTopicId() { return ++m_lastGeneratedTopicId; }
DocumentationSet.CreateExternalObject Method
Loads an assembly DLL, locates a type within it, and creates and returns an instance of that type.
Type Parameters
T The type to cast the returned object to.
Parameters
assemblyName The name of the assembly; for example, "MyAssembly". This DLL should be located in the same directory as the application .exe file. typeName The fully-qualified name of the type to locate; for example, "MyNamespace.MyType".
Exceptions
|
public static T CreateExternalObject<T>(string assemblyName, string typeName) where T : class { // load the assembly; set <assembly> to refer to it Assembly assembly; try { assembly = Assembly.Load(assemblyName); } catch (FileNotFoundException ex) { throw new CreateExternalObjectException(ex, Resources.CannotLoadAssembly, assemblyName, ex.Message); } // load the type; set <type> to refer to it Type type = assembly.GetType(typeName); if (type == null) { throw new CreateExternalObjectException(null, Resources.TypeNotFoundInAssembly, assemblyName, typeName); } // set <constructorInfo> to information about the default constructor // of <type> ConstructorInfo constructorInfo = type.GetConstructor(Type.EmptyTypes); if (constructorInfo == null) { throw new CreateExternalObjectException(null, Resources.ConstructorNotFoundInAssembly, assemblyName, typeName); } // set <obj> to a new instance of <type> object obj; try { obj = constructorInfo.Invoke(null); } catch (TargetInvocationException ex) { throw new CreateExternalObjectException(ex, Resources.ConstructorException, assemblyName, typeName); } // cast <obj> to type T T result = obj as T; if (result == null) { throw new CreateExternalObjectException(null, Resources.ExternalObjectWrongType, assemblyName, typeName, obj.GetType(), typeof(T)); } // done return result; } ////////////////////////////////////////////////////////////////////////// // Internal Methods //
DocumentationSet.FindOrAddNamespaceTopic Method
Returns the NamespaceTopic corresponding to a given namespace name (e.g. "Foo.Bar"). If none is found, one is created and added to NamespaceTopics.
Parameters
namespaceName The namespace name; for example, "Foo.Bar". |
internal NamespaceTopic FindOrAddNamespaceTopic(string namespaceName) { NamespaceTopic topic = FindNamespaceTopic(namespaceName); if (topic == null) { topic = new NamespaceTopic(null); topic.DocumentationSet = this; topic.TopicKind = TopicKind.NamespaceTopic; topic.MemberName = namespaceName; m_namespaceTopics.Add(namespaceName, topic); } return topic; } ////////////////////////////////////////////////////////////////////////// // Internal Methods //
DocumentationSet.IsSame Method
Checks if two Topic objects refer to the same topic. to true
Parameters
topic1 The first topic. topic2 The second topic. isSame If the two topics are the same, isSame is set to true. Otherwise, the value of isSame is unchanged by this method. |
bool IsSame(Topic topic1, Topic topic2, ref bool isSame) { if (topic1 == topic2) { isSame |= true; return true; } else return false; } }
AddingSourceFileEventDelegate Delegate
The type of an event handler for an event which is fired to indicate that a source file is about to be added to the documentation set.
Parameters
sourceFileAbsolutePath The value of the SourceFile.SourceFileAbsolutePath property of the new SourceFile object. sourceFileRelativePath The value of the SourceFile.SourceFileRelativePath property of the new SourceFile object.
Remarks
See the DocumentationSet.AddingSourceFile event. |
public delegate void AddingSourceFileEventDelegate( string sourceFileAbsolutePath, string sourceFileRelativePath);
AddedSourceFileEventDelegate Delegate
The type of an event handler for an event which is fired to indicate that a source file has been successfully added to the documentation set.
Parameters
sourceFile The newly-added SourceFile object.
Remarks
See the DocumentationSet.AddingSourceFile event. |
public delegate void AddedSourceFileEventDelegate(SourceFile sourceFile);
ParsingErrorEventDelegate Delegate
The type of an event handler for an event which is fired to indicate that an error occurred while trying to parse a source file.
Parameters
exception Information about the error.
Remarks
See the DocumentationSet.ParsingError event. |
public delegate void ParsingErrorEventDelegate(ParsingException exception);
DuplicateTopicTitleEventDelegate Delegate
The type of an event handler for an event which is fired to indicate that two topics have the same Topic.LongTitle. This is typically a warning, displayed to the user -- it may indicate that the same source file was added twice to the documentation set.
Parameters
topic1 The first topic. topic2 The second topic.
Remarks
See the DocumentationSet.DuplicateTopicTitle event. |
public delegate void DuplicateTopicTitleEventDelegate(Topic topic1, Topic topic2);
DuplicateCanonicalNameEventDelegate Delegate
The type of an event handler for an event which is fired to indicate that two topics have the same Topic.CanonicalName. This is purely informational -- it does not indicate an error. (Typially it indicates that one or more overload list topics are being generated.)
Parameters
topic1 The first topic. topic2 The second topic.
Remarks
See the DocumentationSet.DuplicateCanonicalName event. |
public delegate void DuplicateCanonicalNameEventDelegate(Topic topic1, Topic topic2);
ReferenceNotFoundEventDelegate Delegate
The type of an event handler for an event which is fired to indicate that a topic made a potential reference to another topic which wasn't found. This is typically an informational message, displayed to the user after sorting all such messages and removing duplicates. This event does not necessarily indicate an error.
Parameters
reference The presumed (but failed) qualified or unqualified member name of the topic being referenced. contextTopic The topic containing the reference. lineNumber The line number of the reference, within the source file containing contextTopic. settings Settings used to resolve the reference. |
public delegate void ReferenceNotFoundEventDelegate(string reference, Topic contextTopic, int lineNumber, FindTopicSettings settings);
BegunUsingSourcesException Class
Indicates that the application attempt to add a source file to the documention set after an operation that used the collection of source files. |
public class BegunUsingSourcesException : Exception {
BegunUsingSourcesException Constructor
Initializes an instance of this class.
Parameters
message The error message. |
public BegunUsingSourcesException(string message) : base(message) { } }
CreateExternalObjectException Class
Indicates an error that occurred while attempting to create an instance of an object in another assembly.
Remarks
|
public class CreateExternalObjectException : Exception {
CreateExternalObjectException Constructor
Initializes an instance of this class.
Parameters
innerException The inner exception, or null if none. format A formatting string for an error message to include with the exception. args Formatting arguments for the error message. |
public CreateExternalObjectException(Exception innerException, string format, params object[] args) : base(String.Format(format, args), innerException) { } }
FindTopicSettings Enumeration
Settings used by DocumentationSet.FindDocumentedTopic.
Members
|
[Flags] public enum FindTopicSettings {
FindTopicSettings.None Enumeration Value
No options specified. |
None = 0x0,
FindTopicSettings.TypeOnly Enumeration Value
Only find topics that document types. |
TypeOnly = 0x1,
FindTopicSettings.SearchAncestors Enumeration Value
Search ancestor types of the current topic (i.e. base types, base types of base types, etc.) This can be significantly slower. |
SearchAncestors = 0x2,
FindTopicSettings.SearchOnlyAncestors Enumeration Value
Search only ancestor types of the current topic (i.e. base types, base types of base types, etc.), i.e. don't search anywhere else. |
SearchOnlyAncestors = 0x4,
FindTopicSettings.Precanonicalized Enumeration Value
Specifies that the input member name is already canonicalized (i.e. no type parameter names, and any trailing set of type parameters is removed), so DocumentationSet.FindDocumentedTopic doesn't need to perform canonicalization. |
Precanonicalized = 0x8, } }