Free cookie consent management tool by TermsFeed Policy Generator

source: branches/Async/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.Xml-5.5.0/DocumentationElement.cs @ 13329

Last change on this file since 13329 was 11804, checked in by jkarder, 10 years ago

#2077:

  • added fancy xml documentation
  • fixed configurations and plattforms
File size: 9.6 KB
Line 
1// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4// software and associated documentation files (the "Software"), to deal in the Software
5// without restriction, including without limitation the rights to use, copy, modify, merge,
6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7// to whom the Software is furnished to do so, subject to the following conditions:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17// DEALINGS IN THE SOFTWARE.
18
19using System;
20using System.Collections.Generic;
21using System.Linq;
22using System.Text;
23using System.Threading;
24using ICSharpCode.NRefactory.Documentation;
25using ICSharpCode.NRefactory.TypeSystem;
26using ICSharpCode.NRefactory.Utils;
27
28namespace ICSharpCode.NRefactory.Xml
29{
30  /// <summary>
31  /// Represents an element in the XML documentation.
32  /// Any occurrences of "&lt;inheritdoc/>" are replaced with the inherited documentation.
33  /// </summary>
34  public class XmlDocumentationElement
35  {
36    /// <summary>
37    /// Gets the XML documentation element for the specified entity.
38    /// Returns null if no documentation is found.
39    /// </summary>
40    public static XmlDocumentationElement Get(IEntity entity, bool inheritDocIfMissing = true)
41    {
42      if (entity == null)
43        return null;
44      var documentationComment = entity.Documentation;
45      if (documentationComment != null) {
46        return Create(documentationComment, entity);
47      }
48     
49      IMember member = entity as IMember;
50      if (inheritDocIfMissing && member != null) {
51        if (member.SymbolKind == SymbolKind.Constructor) {
52          // For constructors, the documentation of the base class ctor
53          // isn't really suitable as constructors are not inherited.
54          // We'll use the type's documentation instead:
55          return Get(entity.DeclaringTypeDefinition, inheritDocIfMissing);
56        }
57        foreach (IMember baseMember in InheritanceHelper.GetBaseMembers(member, includeImplementedInterfaces: true)) {
58          documentationComment = baseMember.Documentation;
59          if (documentationComment != null)
60            return Create(documentationComment, baseMember);
61        }
62      }
63      return null;
64    }
65   
66    static XmlDocumentationElement Create(DocumentationComment documentationComment, IEntity declaringEntity)
67    {
68      var doc = new AXmlParser().Parse(documentationComment.Xml);
69      return new XmlDocumentationElement(doc, declaringEntity, documentationComment.ResolveCref);
70    }
71   
72    readonly AXmlObject xmlObject;
73    readonly AXmlElement element;
74    readonly IEntity declaringEntity;
75    readonly Func<string, IEntity> crefResolver;
76    volatile string textContent;
77   
78    /// <summary>
79    /// Inheritance level; used to prevent cyclic doc inheritance.
80    /// </summary>
81    int nestingLevel;
82   
83    /// <summary>
84    /// Creates a new documentation element.
85    /// </summary>
86    public XmlDocumentationElement(AXmlElement element, IEntity declaringEntity, Func<string, IEntity> crefResolver)
87    {
88      if (element == null)
89        throw new ArgumentNullException("element");
90      this.element = element;
91      this.xmlObject = element;
92      this.declaringEntity = declaringEntity;
93      this.crefResolver = crefResolver;
94    }
95   
96    /// <summary>
97    /// Creates a new documentation element.
98    /// </summary>
99    public XmlDocumentationElement(AXmlDocument document, IEntity declaringEntity, Func<string, IEntity> crefResolver)
100    {
101      if (document == null)
102        throw new ArgumentNullException("document");
103      this.xmlObject = document;
104      this.declaringEntity = declaringEntity;
105      this.crefResolver = crefResolver;
106    }
107   
108    /// <summary>
109    /// Creates a new documentation element.
110    /// </summary>
111    public XmlDocumentationElement(string text, IEntity declaringEntity)
112    {
113      if (text == null)
114        throw new ArgumentNullException("text");
115      this.declaringEntity = declaringEntity;
116      this.textContent = text;
117    }
118   
119    /// <summary>
120    /// Gets the entity on which this documentation was originally declared.
121    /// May return null.
122    /// </summary>
123    public IEntity DeclaringEntity {
124      get { return declaringEntity; }
125    }
126   
127    IEntity referencedEntity;
128    volatile bool referencedEntityInitialized;
129   
130    /// <summary>
131    /// Gets the entity referenced by the 'cref' attribute.
132    /// May return null.
133    /// </summary>
134    public IEntity ReferencedEntity {
135      get {
136        if (!referencedEntityInitialized) {
137          string cref = GetAttribute("cref");
138          if (cref != null && crefResolver != null)
139            referencedEntity = crefResolver(cref);
140          referencedEntityInitialized = true;
141        }
142        return referencedEntity;
143      }
144    }
145   
146    /// <summary>
147    /// Gets the element name.
148    /// </summary>
149    public string Name {
150      get {
151        return element != null ? element.Name : string.Empty;
152      }
153    }
154   
155    /// <summary>
156    /// Gets the attribute value.
157    /// </summary>
158    public string GetAttribute(string name)
159    {
160      return element != null ? element.GetAttributeValue(name) : string.Empty;
161    }
162   
163    /// <summary>
164    /// Gets whether this is a pure text node.
165    /// </summary>
166    public bool IsTextNode {
167      get { return xmlObject == null; }
168    }
169   
170    /// <summary>
171    /// Gets the text content.
172    /// </summary>
173    public string TextContent {
174      get {
175        if (textContent == null) {
176          StringBuilder b = new StringBuilder();
177          foreach (var child in this.Children)
178            b.Append(child.TextContent);
179          textContent = b.ToString();
180        }
181        return textContent;
182      }
183    }
184   
185    IList<XmlDocumentationElement> children;
186   
187    /// <summary>
188    /// Gets the child elements.
189    /// </summary>
190    public IList<XmlDocumentationElement> Children {
191      get {
192        if (xmlObject == null)
193          return EmptyList<XmlDocumentationElement>.Instance;
194        return LazyInitializer.EnsureInitialized(
195          ref this.children,
196          () => CreateElements(xmlObject.Children, declaringEntity, crefResolver, nestingLevel));
197      }
198    }
199   
200    static readonly string[] doNotInheritIfAlreadyPresent = {
201      "example", "exclude", "filterpriority", "preliminary", "summary",
202      "remarks", "returns", "threadsafety", "value"
203    };
204   
205    static List<XmlDocumentationElement> CreateElements(IEnumerable<AXmlObject> childObjects, IEntity declaringEntity, Func<string, IEntity> crefResolver, int nestingLevel)
206    {
207      List<XmlDocumentationElement> list = new List<XmlDocumentationElement>();
208      foreach (var child in childObjects) {
209        var childText = child as AXmlText;
210        var childTag = child as AXmlTag;
211        var childElement = child as AXmlElement;
212        if (childText != null) {
213          list.Add(new XmlDocumentationElement(childText.Value, declaringEntity));
214        } else if (childTag != null && childTag.IsCData) {
215          foreach (var text in childTag.Children.OfType<AXmlText>())
216            list.Add(new XmlDocumentationElement(text.Value, declaringEntity));
217        } else if (childElement != null) {
218          if (nestingLevel < 5 && childElement.Name == "inheritdoc") {
219            string cref = childElement.GetAttributeValue("cref");
220            IEntity inheritedFrom = null;
221            DocumentationComment inheritedDocumentation = null;
222            if (cref != null) {
223              inheritedFrom = crefResolver(cref);
224              if (inheritedFrom != null)
225                inheritedDocumentation = inheritedFrom.Documentation;
226            } else {
227              foreach (IMember baseMember in InheritanceHelper.GetBaseMembers((IMember)declaringEntity, includeImplementedInterfaces: true)) {
228                inheritedDocumentation = baseMember.Documentation;
229                if (inheritedDocumentation != null) {
230                  inheritedFrom = baseMember;
231                  break;
232                }
233              }
234            }
235           
236            if (inheritedDocumentation != null) {
237              var doc = new AXmlParser().Parse(inheritedDocumentation.Xml);
238             
239              // XPath filter not yet implemented
240              if (childElement.Parent is AXmlDocument && childElement.GetAttributeValue("select") == null) {
241                // Inheriting documentation at the root level
242                List<string> doNotInherit = new List<string>();
243                doNotInherit.Add("overloads");
244                doNotInherit.AddRange(childObjects.OfType<AXmlElement>().Select(e => e.Name).Intersect(
245                  doNotInheritIfAlreadyPresent));
246               
247                var inheritedChildren = doc.Children.Where(
248                  inheritedObject => {
249                    AXmlElement inheritedElement = inheritedObject as AXmlElement;
250                    return !(inheritedElement != null && doNotInherit.Contains(inheritedElement.Name));
251                  });
252               
253                list.AddRange(CreateElements(inheritedChildren, inheritedFrom, inheritedDocumentation.ResolveCref, nestingLevel + 1));
254              }
255            }
256          } else {
257            list.Add(new XmlDocumentationElement(childElement, declaringEntity, crefResolver) { nestingLevel = nestingLevel });
258          }
259        }
260      }
261      if (list.Count > 0 && list[0].IsTextNode) {
262        if (string.IsNullOrWhiteSpace(list[0].textContent))
263          list.RemoveAt(0);
264        else
265          list[0].textContent = list[0].textContent.TrimStart();
266      }
267      if (list.Count > 0 && list[list.Count - 1].IsTextNode) {
268        if (string.IsNullOrWhiteSpace(list[list.Count - 1].textContent))
269          list.RemoveAt(list.Count - 1);
270        else
271          list[list.Count - 1].textContent = list[list.Count - 1].textContent.TrimEnd();
272      }
273      return list;
274    }
275   
276    /// <inheritdoc/>
277    public override string ToString()
278    {
279      if (element != null)
280        return "<" + element.Name + ">";
281      else
282        return this.TextContent;
283    }
284  }
285}
Note: See TracBrowser for help on using the repository browser.