PageRenderTime 52ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/eLMM/VirusCount/Escience/Parse/ParsableAttribute.cs

#
C# | 235 lines | 144 code | 26 blank | 65 comment | 34 complexity | 41598c46d11cbce8b0b99848bc7a2c9b MD5 | raw file
  1. //*********************************************************
  2. //
  3. // Copyright (c) Microsoft. All rights reserved.
  4. // This code is licensed under the Apache License, Version 2.0.
  5. // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
  6. // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
  7. // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
  8. // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
  9. //
  10. //*********************************************************
  11. using System;
  12. using System.Reflection;
  13. using Bio.Util;
  14. using System.Collections.Generic;
  15. using System.Threading;
  16. using System.Linq;
  17. using System.Collections.Concurrent;
  18. namespace MBT.Escience.Parse
  19. {
  20. //[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
  21. //public class ParsableAttribute : Attribute { }
  22. public enum ParseAction
  23. {
  24. /// <summary>
  25. /// Specifies that a field is required when parsing.
  26. /// </summary>
  27. Required,
  28. /// <summary>
  29. /// Specifies that an element is optional. Note that all public fields are optional by default. This allows you to mark private or protected fields as parsable
  30. /// </summary>
  31. Optional,
  32. /// <summary>
  33. /// Specifies that a field should not be parsed. This only is useful for public fields that would otherwise be automatically parsed.
  34. /// </summary>
  35. Ignore,
  36. /// <summary>
  37. /// Specifies that the string used to construct this argument should be stored here. Note that this MUST be of type string.
  38. /// </summary>
  39. ArgumentString,
  40. /// <summary>
  41. /// Behaves like the params keyword for methods: sucks up all the final arguments and constructs a list out of them. They must all be the same type, as
  42. /// specified by the type of the list that this attribute is attached to. This can only be placed on a member of type List. This is considered an optional
  43. /// argument, in the sense that if there are no arguments left, an empty list will be returned. It's up to the parsable type to decide if it wants to check
  44. /// that the list is non-empty in its FinalizeParse method.
  45. /// </summary>
  46. Params
  47. };
  48. [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
  49. public class ParseAttribute : Attribute
  50. {
  51. public ParseAttribute(ParseAction action) : this(action, null) { }
  52. public ParseAttribute(ParseAction action, Type parseType)
  53. {
  54. Action = action;
  55. ParseTypeOrNull = parseType;
  56. }
  57. public ParseAction Action { get; private set; }
  58. public Type ParseTypeOrNull { get; private set; }
  59. /// <summary>
  60. /// Use ConstructorArguments syntax to hard code settings.
  61. /// </summary>
  62. public string DefaultParameters { get; set; }
  63. }
  64. /// <summary>
  65. /// Labels a Collection type as not being parsed as a collection.
  66. /// </summary>
  67. [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
  68. public class ParseAsNonCollectionAttribute : Attribute { }
  69. /// <summary>
  70. /// Marks a class so that only fields and properties that have explicit Parse attributes SET IN THE CURRENT CLASS OR A DERIVED CLASS will be parsed.
  71. /// </summary>
  72. [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
  73. public class DoNotParseInheritedAttribute : Attribute { }
  74. public static class ParseExtensions
  75. {
  76. private static ParseAttribute DefaultOptionalAttribute = new ParseAttribute(ParseAction.Optional);
  77. private static ParseAttribute DefaultIgnoreAttribute = new ParseAttribute(ParseAction.Ignore);
  78. /// <summary>
  79. /// Determines if the ParseExplicit attribute has been set.
  80. /// </summary>
  81. /// <param name="type"></param>
  82. /// <returns></returns>
  83. public static bool DoNotParseInherited(this Type type)
  84. {
  85. return Attribute.IsDefined(type, typeof(DoNotParseInheritedAttribute));
  86. }
  87. public static Type GetParseTypeOrNull(this MemberInfo member)
  88. {
  89. ParseAttribute parseAttribute = (ParseAttribute)Attribute.GetCustomAttribute(member, typeof(ParseAttribute));
  90. return parseAttribute == null ? null : parseAttribute.ParseTypeOrNull;
  91. }
  92. public static string GetDefaultParametersOrNull(this MemberInfo member)
  93. {
  94. ParseAttribute parseAttribute = (ParseAttribute)Attribute.GetCustomAttribute(member, typeof(ParseAttribute));
  95. return parseAttribute == null ? null : parseAttribute.DefaultParameters;
  96. }
  97. //static ThreadLocal<Cache<MemberInfo, ParseAttribute>> _parseAttributeCache =
  98. // new ThreadLocal<Cache<MemberInfo, ParseAttribute>>(
  99. // () => new Cache<MemberInfo, ParseAttribute>(maxSize: 10000, recoverySize: 100));
  100. static ConcurrentDictionary<MemberInfo, ParseAttribute> _parseAttributeCache = new ConcurrentDictionary<MemberInfo, ParseAttribute>();
  101. public static ParseAttribute GetParseAttribute(this MemberInfo member, Type[] actualTypeInheritanceHierarchy)
  102. {
  103. ParseAttribute pa = _parseAttributeCache.GetOrAdd(member, (m) => GetParseAttributeInternal(m, actualTypeInheritanceHierarchy));
  104. //ParseAttribute pa;
  105. //if (!_parseAttributeCache.Value.TryGetValue(member, out pa))
  106. //{
  107. // pa = GetParseAttributeInternal(member, actualTypeInheritanceHierarchy);
  108. // _parseAttributeCache.Value.Add(member, pa);
  109. //}
  110. return pa;
  111. }
  112. /// <summary>
  113. /// Gets the default or declared parse attribute for the specified member.
  114. /// </summary>
  115. /// <param name="member"></param>
  116. /// <returns></returns>
  117. private static ParseAttribute GetParseAttributeInternal(this MemberInfo member, Type[] actualTypeInheritanceHierarchy)
  118. {
  119. // march up the stack, starting at the actual type's base. If we find the member before we find a ParseExplicit, we're ok to parse this.
  120. // If we find a parseExplicity first, then we know we can't parse this member. If a member is declared in the first class that we see ParseExplicit,
  121. // keep it, because we define ParseExplicit as "keep for this and all derived types"
  122. for (int i = actualTypeInheritanceHierarchy.Length - 1; i >= 0; i--)
  123. {
  124. if (member.DeclaringType.Equals(actualTypeInheritanceHierarchy[i]))
  125. break;
  126. if (actualTypeInheritanceHierarchy[i].DoNotParseInherited())
  127. return DefaultIgnoreAttribute;
  128. }
  129. ParseAttribute parseAttribute = (ParseAttribute)Attribute.GetCustomAttribute(member, typeof(ParseAttribute));
  130. if (IsIndexer(member))
  131. {
  132. Helper.CheckCondition<ParseException>(parseAttribute == null || parseAttribute.Action == ParseAction.Ignore, "Can't parse an Indexer.");
  133. return DefaultIgnoreAttribute;
  134. }
  135. PropertyInfo property = member as PropertyInfo;
  136. if (parseAttribute == null)
  137. {
  138. FieldInfo field = member as FieldInfo;
  139. if (field != null)
  140. {
  141. return field.IsPublic ? DefaultOptionalAttribute : DefaultIgnoreAttribute;
  142. }
  143. else if (property != null)
  144. {
  145. parseAttribute = property.GetGetMethod() != null && property.GetSetMethod() != null && property.GetGetMethod().IsPublic && property.GetSetMethod().IsPublic ? // either will be null if don't exist or non-public
  146. DefaultOptionalAttribute : DefaultIgnoreAttribute;
  147. }
  148. else
  149. {
  150. parseAttribute = DefaultIgnoreAttribute;
  151. }
  152. }
  153. return parseAttribute;
  154. }
  155. private static bool IsIndexer(MemberInfo member)
  156. {
  157. return member is PropertyInfo && ((PropertyInfo)member).GetIndexParameters().Length > 0;
  158. }
  159. /// <summary>
  160. /// Returns true if and only if the type has a public default constuctor, or is an interface or abstract class, in which case a derived type may be parsed.
  161. /// </summary>
  162. public static bool IsConstructable(this Type t)
  163. {
  164. if ((t.IsInterface || t.IsAbstract))
  165. return true;
  166. return t.HasPublicDefaultConstructor();
  167. //return Attribute.IsDefined(t, typeof(ParsableAttribute));
  168. }
  169. public static bool HasPublicDefaultConstructor(this Type t)
  170. {
  171. var constructor = t.GetConstructor(Type.EmptyTypes);
  172. return constructor != null && constructor.IsPublic;
  173. }
  174. public static bool ParseAsCollection(this Type parseType)
  175. {
  176. bool result = !Attribute.IsDefined(parseType, typeof(ParseAsNonCollectionAttribute)) &&
  177. #if !SILVERLIGHT
  178. parseType.FindInterfaces(Module.FilterTypeNameIgnoreCase, "ICollection*").Length > 0
  179. #else
  180. parseType.GetInterfaces().Any(interface1 => interface1.ToString().StartsWith("ICollection"))
  181. #endif
  182. && !parseType.HasParseMethod();
  183. return result;
  184. }
  185. public static Type[] GetInheritanceHierarchy(this Type type)
  186. {
  187. var result = new Stack<Type>();
  188. while (type != null)
  189. {
  190. result.Push(type);
  191. type = type.BaseType;
  192. }
  193. return result.ToArray();
  194. }
  195. public static string ToParseString(this object o, Type parseTypeOrNull = null, bool suppressDefaults = false)
  196. {
  197. Type t = o.GetType();
  198. if (t.HasParseMethod() || !t.IsConstructable())
  199. return o.ToString();
  200. else if (parseTypeOrNull == null)
  201. return ConstructorArguments.ToString(o); // can only get here if t is constructable.
  202. else
  203. {
  204. object valueAsParseType = ArgumentCollection.ImplicitlyCastValueToType(o, parseTypeOrNull);
  205. return ConstructorArguments.ToString(valueAsParseType, suppressDefaults);
  206. }
  207. }
  208. }
  209. }