/main/contrib/ICSharpCode.NRefactory/TypeSystem/IAnnotatable.cs

https://github.com/jfcantin/monodevelop · C# · 231 lines · 145 code · 17 blank · 69 comment · 42 complexity · b372520089ae5a324790e05bc643d808 MD5 · raw file

  1. // Copyright (c) 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. using System;
  19. using System.Collections.Generic;
  20. using System.Linq;
  21. using System.Threading;
  22. namespace ICSharpCode.NRefactory
  23. {
  24. /// <summary>
  25. /// Provides an interface to handle annotations in an object.
  26. /// </summary>
  27. public interface IAnnotatable
  28. {
  29. /// <summary>
  30. /// Gets all annotations stored on this IAnnotatable.
  31. /// </summary>
  32. IEnumerable<object> Annotations {
  33. get;
  34. }
  35. /// <summary>
  36. /// Gets the first annotation of the specified type.
  37. /// Returns null if no matching annotation exists.
  38. /// </summary>
  39. /// <typeparam name='T'>
  40. /// The type of the annotation.
  41. /// </typeparam>
  42. T Annotation<T> () where T: class;
  43. /// <summary>
  44. /// Gets the first annotation of the specified type.
  45. /// Returns null if no matching annotation exists.
  46. /// </summary>
  47. /// <param name='type'>
  48. /// The type of the annotation.
  49. /// </param>
  50. object Annotation (Type type);
  51. /// <summary>
  52. /// Adds an annotation to this instance.
  53. /// </summary>
  54. /// <param name='annotation'>
  55. /// The annotation to add.
  56. /// </param>
  57. void AddAnnotation (object annotation);
  58. /// <summary>
  59. /// Removes all annotations of the specified type.
  60. /// </summary>
  61. /// <typeparam name='T'>
  62. /// The type of the annotations to remove.
  63. /// </typeparam>
  64. void RemoveAnnotations<T> () where T : class;
  65. /// <summary>
  66. /// Removes all annotations of the specified type.
  67. /// </summary>
  68. /// <param name='type'>
  69. /// The type of the annotations to remove.
  70. /// </param>
  71. void RemoveAnnotations(Type type);
  72. }
  73. public abstract class AbstractAnnotatable : IAnnotatable
  74. {
  75. // Annotations: points either null (no annotations), to the single annotation,
  76. // or to an AnnotationList.
  77. // Once it is pointed at an AnnotationList, it will never change (this allows thread-safety support by locking the list)
  78. protected object annotations;
  79. sealed class AnnotationList : List<object>, ICloneable
  80. {
  81. // There are two uses for this custom list type:
  82. // 1) it's private, and thus (unlike List<object>) cannot be confused with real annotations
  83. // 2) It allows us to simplify the cloning logic by making the list behave the same as a clonable annotation.
  84. public AnnotationList (int initialCapacity) : base(initialCapacity)
  85. {
  86. }
  87. public object Clone ()
  88. {
  89. lock (this) {
  90. AnnotationList copy = new AnnotationList (this.Count);
  91. for (int i = 0; i < this.Count; i++) {
  92. object obj = this [i];
  93. ICloneable c = obj as ICloneable;
  94. copy.Add (c != null ? c.Clone () : obj);
  95. }
  96. return copy;
  97. }
  98. }
  99. }
  100. public virtual void AddAnnotation (object annotation)
  101. {
  102. if (annotation == null)
  103. throw new ArgumentNullException ("annotation");
  104. retry: // Retry until successful
  105. object oldAnnotation = Interlocked.CompareExchange (ref this.annotations, annotation, null);
  106. if (oldAnnotation == null) {
  107. return; // we successfully added a single annotation
  108. }
  109. AnnotationList list = oldAnnotation as AnnotationList;
  110. if (list == null) {
  111. // we need to transform the old annotation into a list
  112. list = new AnnotationList (4);
  113. list.Add (oldAnnotation);
  114. list.Add (annotation);
  115. if (Interlocked.CompareExchange (ref this.annotations, list, oldAnnotation) != oldAnnotation) {
  116. // the transformation failed (some other thread wrote to this.annotations first)
  117. goto retry;
  118. }
  119. } else {
  120. // once there's a list, use simple locking
  121. lock (list) {
  122. list.Add (annotation);
  123. }
  124. }
  125. }
  126. public virtual void RemoveAnnotations<T> () where T : class
  127. {
  128. retry: // Retry until successful
  129. object oldAnnotations = this.annotations;
  130. AnnotationList list = oldAnnotations as AnnotationList;
  131. if (list != null) {
  132. lock (list)
  133. list.RemoveAll (obj => obj is T);
  134. } else if (oldAnnotations is T) {
  135. if (Interlocked.CompareExchange (ref this.annotations, null, oldAnnotations) != oldAnnotations) {
  136. // Operation failed (some other thread wrote to this.annotations first)
  137. goto retry;
  138. }
  139. }
  140. }
  141. public virtual void RemoveAnnotations (Type type)
  142. {
  143. if (type == null)
  144. throw new ArgumentNullException ("type");
  145. retry: // Retry until successful
  146. object oldAnnotations = this.annotations;
  147. AnnotationList list = oldAnnotations as AnnotationList;
  148. if (list != null) {
  149. lock (list)
  150. list.RemoveAll (obj => type.IsInstanceOfType (obj));
  151. } else if (type.IsInstanceOfType (oldAnnotations)) {
  152. if (Interlocked.CompareExchange (ref this.annotations, null, oldAnnotations) != oldAnnotations) {
  153. // Operation failed (some other thread wrote to this.annotations first)
  154. goto retry;
  155. }
  156. }
  157. }
  158. public T Annotation<T> () where T: class
  159. {
  160. object annotations = this.annotations;
  161. AnnotationList list = annotations as AnnotationList;
  162. if (list != null) {
  163. lock (list) {
  164. foreach (object obj in list) {
  165. T t = obj as T;
  166. if (t != null)
  167. return t;
  168. }
  169. return null;
  170. }
  171. } else {
  172. return annotations as T;
  173. }
  174. }
  175. public object Annotation (Type type)
  176. {
  177. if (type == null)
  178. throw new ArgumentNullException ("type");
  179. object annotations = this.annotations;
  180. AnnotationList list = annotations as AnnotationList;
  181. if (list != null) {
  182. lock (list) {
  183. foreach (object obj in list) {
  184. if (type.IsInstanceOfType (obj))
  185. return obj;
  186. }
  187. }
  188. } else {
  189. if (type.IsInstanceOfType (annotations))
  190. return annotations;
  191. }
  192. return null;
  193. }
  194. /// <summary>
  195. /// Gets all annotations stored on this AstNode.
  196. /// </summary>
  197. public IEnumerable<object> Annotations {
  198. get {
  199. object annotations = this.annotations;
  200. AnnotationList list = annotations as AnnotationList;
  201. if (list != null) {
  202. lock (list) {
  203. return list.ToArray ();
  204. }
  205. } else {
  206. if (annotations != null)
  207. return new object[] { annotations };
  208. else
  209. return Enumerable.Empty<object> ();
  210. }
  211. }
  212. }
  213. }
  214. }