CodeIdentifier.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. //------------------------------------------------------------------------------
  2. // <copyright file="CodeIdentifier.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">Microsoft</owner>
  6. //------------------------------------------------------------------------------
  7. namespace System.Xml.Serialization {
  8. using System;
  9. using System.Text;
  10. using System.Collections;
  11. using System.IO;
  12. using System.Globalization;
  13. using System.Diagnostics;
  14. using System.CodeDom;
  15. using System.CodeDom.Compiler;
  16. using Microsoft.CSharp;
  17. /// <include file='doc\CodeIdentifier.uex' path='docs/doc[@for="CodeIdentifier"]/*' />
  18. ///<internalonly/>
  19. /// <devdoc>
  20. /// <para>[To be supplied.]</para>
  21. /// </devdoc>
  22. public class CodeIdentifier {
  23. internal static CodeDomProvider csharp = new CSharpCodeProvider();
  24. internal const int MaxIdentifierLength = 511;
  25. [Obsolete("This class should never get constructed as it contains only static methods.")]
  26. public CodeIdentifier() {
  27. }
  28. /// <include file='doc\CodeIdentifier.uex' path='docs/doc[@for="CodeIdentifier.MakePascal"]/*' />
  29. /// <devdoc>
  30. /// <para>[To be supplied.]</para>
  31. /// </devdoc>
  32. public static string MakePascal(string identifier) {
  33. identifier = MakeValid(identifier);
  34. if (identifier.Length <= 2)
  35. return identifier.ToUpper(CultureInfo.InvariantCulture);
  36. else if (char.IsLower(identifier[0]))
  37. return char.ToUpper(identifier[0], CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture) + identifier.Substring(1);
  38. else
  39. return identifier;
  40. }
  41. /// <include file='doc\CodeIdentifier.uex' path='docs/doc[@for="CodeIdentifier.MakeCamel"]/*' />
  42. /// <devdoc>
  43. /// <para>[To be supplied.]</para>
  44. /// </devdoc>
  45. public static string MakeCamel(string identifier) {
  46. identifier = MakeValid(identifier);
  47. if (identifier.Length <= 2)
  48. return identifier.ToLower(CultureInfo.InvariantCulture);
  49. else if (char.IsUpper(identifier[0]))
  50. return char.ToLower(identifier[0], CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture) + identifier.Substring(1);
  51. else
  52. return identifier;
  53. }
  54. /// <include file='doc\CodeIdentifier.uex' path='docs/doc[@for="CodeIdentifier.MakeValid"]/*' />
  55. /// <devdoc>
  56. /// <para>[To be supplied.]</para>
  57. /// </devdoc>
  58. public static string MakeValid(string identifier) {
  59. StringBuilder builder = new StringBuilder();
  60. for (int i = 0; i < identifier.Length && builder.Length < MaxIdentifierLength; i++) {
  61. char c = identifier[i];
  62. if (IsValid(c)) {
  63. if (builder.Length == 0 && !IsValidStart(c)) {
  64. builder.Append("Item");
  65. }
  66. builder.Append(c);
  67. }
  68. }
  69. if (builder.Length == 0) return "Item";
  70. return builder.ToString();
  71. }
  72. internal static string MakeValidInternal(string identifier) {
  73. if (identifier.Length > 30) {
  74. return "Item";
  75. }
  76. return MakeValid(identifier);
  77. }
  78. static bool IsValidStart(char c) {
  79. // the given char is already a valid name character
  80. #if DEBUG
  81. // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
  82. if (!IsValid(c)) throw new ArgumentException(Res.GetString(Res.XmlInternalErrorDetails, "Invalid identifier character " + ((Int16)c).ToString(CultureInfo.InvariantCulture)), "c");
  83. #endif
  84. // First char cannot be a number
  85. if (Char.GetUnicodeCategory(c) == UnicodeCategory.DecimalDigitNumber)
  86. return false;
  87. return true;
  88. }
  89. static bool IsValid(char c) {
  90. UnicodeCategory uc = Char.GetUnicodeCategory(c);
  91. // each char must be Lu, Ll, Lt, Lm, Lo, Nd, Mn, Mc, Pc
  92. //
  93. switch (uc) {
  94. case UnicodeCategory.UppercaseLetter: // Lu
  95. case UnicodeCategory.LowercaseLetter: // Ll
  96. case UnicodeCategory.TitlecaseLetter: // Lt
  97. case UnicodeCategory.ModifierLetter: // Lm
  98. case UnicodeCategory.OtherLetter: // Lo
  99. case UnicodeCategory.DecimalDigitNumber: // Nd
  100. case UnicodeCategory.NonSpacingMark: // Mn
  101. case UnicodeCategory.SpacingCombiningMark: // Mc
  102. case UnicodeCategory.ConnectorPunctuation: // Pc
  103. break;
  104. case UnicodeCategory.LetterNumber:
  105. case UnicodeCategory.OtherNumber:
  106. case UnicodeCategory.EnclosingMark:
  107. case UnicodeCategory.SpaceSeparator:
  108. case UnicodeCategory.LineSeparator:
  109. case UnicodeCategory.ParagraphSeparator:
  110. case UnicodeCategory.Control:
  111. case UnicodeCategory.Format:
  112. case UnicodeCategory.Surrogate:
  113. case UnicodeCategory.PrivateUse:
  114. case UnicodeCategory.DashPunctuation:
  115. case UnicodeCategory.OpenPunctuation:
  116. case UnicodeCategory.ClosePunctuation:
  117. case UnicodeCategory.InitialQuotePunctuation:
  118. case UnicodeCategory.FinalQuotePunctuation:
  119. case UnicodeCategory.OtherPunctuation:
  120. case UnicodeCategory.MathSymbol:
  121. case UnicodeCategory.CurrencySymbol:
  122. case UnicodeCategory.ModifierSymbol:
  123. case UnicodeCategory.OtherSymbol:
  124. case UnicodeCategory.OtherNotAssigned:
  125. return false;
  126. default:
  127. #if DEBUG
  128. // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
  129. throw new ArgumentException(Res.GetString(Res.XmlInternalErrorDetails, "Unhandled category " + uc), "c");
  130. #else
  131. return false;
  132. #endif
  133. }
  134. return true;
  135. }
  136. internal static void CheckValidIdentifier(string ident) {
  137. if (!CodeGenerator.IsValidLanguageIndependentIdentifier(ident))
  138. throw new ArgumentException(Res.GetString(Res.XmlInvalidIdentifier, ident), "ident");
  139. }
  140. internal static string GetCSharpName(string name) {
  141. //
  142. return EscapeKeywords(name.Replace('+', '.'), csharp);
  143. }
  144. static int GetCSharpName(Type t, Type[] parameters, int index, StringBuilder sb) {
  145. if (t.DeclaringType != null && t.DeclaringType != t) {
  146. index = GetCSharpName(t.DeclaringType, parameters, index, sb);
  147. sb.Append(".");
  148. }
  149. string name = t.Name;
  150. int nameEnd = name.IndexOf('`');
  151. if (nameEnd < 0) {
  152. nameEnd = name.IndexOf('!');
  153. }
  154. if (nameEnd > 0) {
  155. EscapeKeywords(name.Substring(0, nameEnd), csharp, sb);
  156. sb.Append("<");
  157. int arguments = Int32.Parse(name.Substring(nameEnd+1), CultureInfo.InvariantCulture) + index;
  158. for (; index < arguments; index++) {
  159. sb.Append(GetCSharpName(parameters[index]));
  160. if (index < arguments -1) {
  161. sb.Append(",");
  162. }
  163. }
  164. sb.Append(">");
  165. }
  166. else {
  167. EscapeKeywords(name, csharp, sb);
  168. }
  169. return index;
  170. }
  171. internal static string GetCSharpName(Type t) {
  172. int rank = 0;
  173. while (t.IsArray) {
  174. t = t.GetElementType();
  175. rank++;
  176. }
  177. StringBuilder sb = new StringBuilder();
  178. sb.Append("global::");
  179. string ns = t.Namespace;
  180. if (ns != null && ns.Length > 0) {
  181. string[] parts = ns.Split(new char[] {'.'});
  182. for (int i = 0; i < parts.Length; i++) {
  183. EscapeKeywords(parts[i], csharp, sb);
  184. sb.Append(".");
  185. }
  186. }
  187. Type[] arguments = t.IsGenericType || t.ContainsGenericParameters ? t.GetGenericArguments() : new Type[0];
  188. GetCSharpName(t, arguments, 0, sb);
  189. for (int i = 0; i < rank; i++) {
  190. sb.Append("[]");
  191. }
  192. return sb.ToString();
  193. }
  194. //
  195. /*
  196. internal static string GetTypeName(string name, CodeDomProvider codeProvider) {
  197. return codeProvider.GetTypeOutput(new CodeTypeReference(name));
  198. }
  199. */
  200. static void EscapeKeywords(string identifier, CodeDomProvider codeProvider, StringBuilder sb) {
  201. if (identifier == null || identifier.Length == 0)
  202. return;
  203. string originalIdentifier = identifier;
  204. int arrayCount = 0;
  205. while (identifier.EndsWith("[]", StringComparison.Ordinal)) {
  206. arrayCount++;
  207. identifier = identifier.Substring(0, identifier.Length - 2);
  208. }
  209. if (identifier.Length > 0) {
  210. CheckValidIdentifier(identifier);
  211. identifier = codeProvider.CreateEscapedIdentifier(identifier);
  212. sb.Append(identifier);
  213. }
  214. for (int i = 0; i < arrayCount; i++) {
  215. sb.Append("[]");
  216. }
  217. }
  218. static string EscapeKeywords(string identifier, CodeDomProvider codeProvider) {
  219. if (identifier == null || identifier.Length == 0) return identifier;
  220. string originalIdentifier = identifier;
  221. string[] names = identifier.Split(new char[] {'.', ',', '<', '>'});
  222. StringBuilder sb = new StringBuilder();
  223. int separator = -1;
  224. for (int i = 0; i < names.Length; i++) {
  225. if (separator >= 0) {
  226. sb.Append(originalIdentifier.Substring(separator, 1));
  227. }
  228. separator++;
  229. separator += names[i].Length;
  230. string escapedName = names[i].Trim();
  231. EscapeKeywords(escapedName, codeProvider, sb);
  232. }
  233. if (sb.Length != originalIdentifier.Length)
  234. return sb.ToString();
  235. return originalIdentifier;
  236. }
  237. }
  238. }