/src/NHibernateClient.Silverlight/Util/TypeNameParser.cs

https://github.com/jaundice/nhibernate-core · C# · 241 lines · 209 code · 27 blank · 5 comment · 37 complexity · d2a8f94da312c3d6d7435b098f164d0f MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Runtime.Serialization;
  5. using System.Text;
  6. using System.Text.RegularExpressions;
  7. namespace NHibernateClient.Util
  8. {
  9. public class ParserException : Exception
  10. {
  11. public ParserException(string message) : base(message)
  12. {
  13. }
  14. }
  15. [DataContract(IsReference = true)]
  16. public class TypeNameParser
  17. {
  18. [DataMember] internal string defaultNamespace;
  19. [DataMember] internal string defaultAssembly;
  20. private static readonly Regex WhiteSpaces = new Regex(@"[\t\r\n]");
  21. private static readonly Regex MultipleSpaces = new Regex(@"[ ]+");
  22. public TypeNameParser(string defaultNamespace, string defaultAssembly)
  23. {
  24. this.defaultNamespace = defaultNamespace;
  25. this.defaultAssembly = defaultAssembly;
  26. }
  27. public static AssemblyQualifiedTypeName Parse(string type)
  28. {
  29. return Parse(type, null, null);
  30. }
  31. public static AssemblyQualifiedTypeName Parse(string type, string defaultNamespace, string defaultAssembly)
  32. {
  33. return new TypeNameParser(defaultNamespace, defaultAssembly).ParseTypeName(type);
  34. }
  35. public AssemblyQualifiedTypeName ParseTypeName(string typeName)
  36. {
  37. if (typeName == null)
  38. {
  39. throw new ArgumentNullException("typeName");
  40. }
  41. var type = WhiteSpaces.Replace(typeName, " ");
  42. type = MultipleSpaces.Replace(type, " ").Replace(", [", ",[").Replace("[ [", "[[").Replace("] ]", "]]");
  43. if (type.Trim(' ', '[', ']', '\\', ',') == string.Empty)
  44. {
  45. throw new ArgumentException(string.Format("The type to parse is not a type name:{0}", typeName),
  46. "typeName");
  47. }
  48. int genericTypeArgsStartIdx = type.IndexOf('[');
  49. int genericTypeArgsEndIdx = type.LastIndexOf(']');
  50. int genericTypeCardinalityIdx = -1;
  51. if (genericTypeArgsStartIdx >= 0)
  52. {
  53. genericTypeCardinalityIdx = type.IndexOf('`', 0, genericTypeArgsStartIdx);
  54. }
  55. if (genericTypeArgsStartIdx == -1 || genericTypeCardinalityIdx == -1)
  56. {
  57. return ParseNonGenericType(type);
  58. }
  59. else
  60. {
  61. var isArrayType = type.EndsWith("[]");
  62. if (genericTypeCardinalityIdx < 0)
  63. {
  64. throw new ParserException("Invalid generic fully-qualified type name:" + type);
  65. }
  66. // the follow will fail with a generic class with more the 9 type-args (I would see that entity class)
  67. string cardinalityString = type.Substring(genericTypeCardinalityIdx + 1, 1);
  68. int genericTypeCardinality = int.Parse(cardinalityString);
  69. // get the FullName of the non-generic type
  70. string fullName = type.Substring(0, genericTypeArgsStartIdx);
  71. if (type.Length - genericTypeArgsEndIdx - 1 > 0)
  72. fullName += type.Substring(genericTypeArgsEndIdx + 1, type.Length - genericTypeArgsEndIdx - 1);
  73. // parse the type arguments
  74. var genericTypeArgs = new List<AssemblyQualifiedTypeName>();
  75. string typeArguments = type.Substring(genericTypeArgsStartIdx + 1,
  76. genericTypeArgsEndIdx - genericTypeArgsStartIdx - 1);
  77. foreach (string item in GenericTypesArguments(typeArguments, genericTypeCardinality))
  78. {
  79. var typeArgument = ParseTypeName(item);
  80. genericTypeArgs.Add(typeArgument);
  81. }
  82. // construct the generic type definition
  83. return MakeGenericType(ParseNonGenericType(fullName), isArrayType, genericTypeArgs.ToArray());
  84. }
  85. }
  86. public AssemblyQualifiedTypeName MakeGenericType(AssemblyQualifiedTypeName qualifiedName, bool isArrayType,
  87. AssemblyQualifiedTypeName[] typeArguments)
  88. {
  89. Debug.Assert(typeArguments.Length > 0);
  90. var baseType = qualifiedName.Type;
  91. var sb = new StringBuilder(typeArguments.Length*200);
  92. sb.Append(baseType);
  93. sb.Append('[');
  94. for (int i = 0; i < typeArguments.Length; i++)
  95. {
  96. if (i > 0)
  97. {
  98. sb.Append(",");
  99. }
  100. sb.Append('[').Append(typeArguments[i].ToString()).Append(']');
  101. }
  102. sb.Append(']');
  103. if (isArrayType)
  104. {
  105. sb.Append("[]");
  106. }
  107. return new AssemblyQualifiedTypeName(sb.ToString(), qualifiedName.Assembly);
  108. }
  109. private static IEnumerable<string> GenericTypesArguments(string s, int cardinality)
  110. {
  111. Debug.Assert(cardinality != 0);
  112. Debug.Assert(string.IsNullOrEmpty(s) == false);
  113. Debug.Assert(s.Length > 0);
  114. int startIndex = 0;
  115. while (cardinality > 0)
  116. {
  117. var sb = new StringBuilder(s.Length);
  118. int bracketCount = 0;
  119. for (int i = startIndex; i < s.Length; i++)
  120. {
  121. switch (s[i])
  122. {
  123. case '[':
  124. bracketCount++;
  125. continue;
  126. case ']':
  127. if (--bracketCount == 0)
  128. {
  129. string item = s.Substring(startIndex + 1, i - startIndex - 1);
  130. yield return item;
  131. sb = new StringBuilder(s.Length);
  132. startIndex = i + 2; // 2 = '[' + ']'
  133. }
  134. break;
  135. default:
  136. sb.Append(s[i]);
  137. continue;
  138. }
  139. }
  140. if (bracketCount != 0)
  141. {
  142. throw new ParserException(string.Format("The brackets are unbalanced in the type name: {0}", s));
  143. }
  144. if (sb.Length > 0)
  145. {
  146. var result = sb.ToString();
  147. startIndex += result.Length;
  148. yield return result.TrimStart(' ', ',');
  149. }
  150. cardinality--;
  151. }
  152. }
  153. private AssemblyQualifiedTypeName ParseNonGenericType(string typeName)
  154. {
  155. string typeFullName;
  156. string assembliQualifiedName;
  157. if (NeedDefaultAssembly(typeName))
  158. {
  159. assembliQualifiedName = defaultAssembly;
  160. typeFullName = typeName;
  161. }
  162. else
  163. {
  164. int assemblyFullNameIdx = FindAssemblyQualifiedNameStartIndex(typeName);
  165. if (assemblyFullNameIdx > 0)
  166. {
  167. assembliQualifiedName =
  168. typeName.Substring(assemblyFullNameIdx + 1, typeName.Length - assemblyFullNameIdx - 1).Trim();
  169. typeFullName = typeName.Substring(0, assemblyFullNameIdx).Trim();
  170. }
  171. else
  172. {
  173. assembliQualifiedName = null;
  174. typeFullName = typeName.Trim();
  175. }
  176. }
  177. if (NeedDefaultNamespace(typeFullName) && !string.IsNullOrEmpty(defaultNamespace))
  178. {
  179. typeFullName = string.Concat(defaultNamespace, ".", typeFullName);
  180. }
  181. return new AssemblyQualifiedTypeName(typeFullName, assembliQualifiedName);
  182. }
  183. private static int FindAssemblyQualifiedNameStartIndex(string typeName)
  184. {
  185. for (int i = 0; i < typeName.Length; i++)
  186. {
  187. if (typeName[i] == ',' && typeName[i - 1] != '\\')
  188. {
  189. return i;
  190. }
  191. }
  192. return -1;
  193. }
  194. private static bool NeedDefaultNamespaceOrDefaultAssembly(string typeFullName)
  195. {
  196. return !typeFullName.StartsWith("System."); // ugly
  197. }
  198. private static bool NeedDefaultNamespace(string typeFullName)
  199. {
  200. if (!NeedDefaultNamespaceOrDefaultAssembly(typeFullName))
  201. {
  202. return false;
  203. }
  204. int assemblyFullNameIndex = typeFullName.IndexOf(',');
  205. int firstDotIndex = typeFullName.IndexOf('.');
  206. // does not have a dot or the dot is part of AssemblyFullName
  207. return firstDotIndex < 0 || (firstDotIndex > assemblyFullNameIndex && assemblyFullNameIndex > 0);
  208. }
  209. private static bool NeedDefaultAssembly(string typeFullName)
  210. {
  211. return NeedDefaultNamespaceOrDefaultAssembly(typeFullName) && typeFullName.IndexOf(',') < 0;
  212. }
  213. }
  214. }