MultipartIdentifier.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. //------------------------------------------------------------------------------
  2. // <copyright file="CommandBuilder.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">Microsoft</owner>
  6. // <owner current="true" primary="false">Microsoft</owner>
  7. //------------------------------------------------------------------------------
  8. namespace System.Data.Common {
  9. using System.Diagnostics;
  10. using System.Text;
  11. using System.Globalization;
  12. internal class MultipartIdentifier {
  13. private const int MaxParts = 4;
  14. internal const int ServerIndex = 0;
  15. internal const int CatalogIndex = 1;
  16. internal const int SchemaIndex = 2;
  17. internal const int TableIndex = 3;
  18. /*
  19. Left quote strings need to correspond 1 to 1 with the right quote strings
  20. example: "ab" "cd", passed in for the left and the right quote
  21. would set a or b as a starting quote character.
  22. If a is the starting quote char then c would be the ending quote char
  23. otherwise if b is the starting quote char then d would be the ending quote character.
  24. */
  25. internal static string[] ParseMultipartIdentifier(string name, string leftQuote, string rightQuote, string property, bool ThrowOnEmptyMultipartName) {
  26. return ParseMultipartIdentifier(name, leftQuote, rightQuote,'.', MaxParts, true, property, ThrowOnEmptyMultipartName);
  27. }
  28. private enum MPIState {
  29. MPI_Value,
  30. MPI_ParseNonQuote,
  31. MPI_LookForSeparator,
  32. MPI_LookForNextCharOrSeparator,
  33. MPI_ParseQuote,
  34. MPI_RightQuote,
  35. }
  36. /* Core function for parsing the multipart identifer string.
  37. * paramaters: name - string to parse
  38. * leftquote: set of characters which are valid quoteing characters to initiate a quote
  39. * rightquote: set of characters which are valid to stop a quote, array index's correspond to the the leftquote array.
  40. * separator: separator to use
  41. * limit: number of names to parse out
  42. * removequote:to remove the quotes on the returned string
  43. */
  44. private static void IncrementStringCount (string name, string[] ary, ref int position, string property) {
  45. ++position;
  46. int limit = ary.Length;
  47. if (position >= limit) {
  48. throw ADP.InvalidMultipartNameToManyParts (property, name, limit);
  49. }
  50. ary[position] = string.Empty;
  51. }
  52. private static bool IsWhitespace (char ch) {
  53. return Char.IsWhiteSpace (ch);
  54. }
  55. internal static string[] ParseMultipartIdentifier(string name, string leftQuote, string rightQuote, char separator, int limit, bool removequotes, string property, bool ThrowOnEmptyMultipartName) {
  56. if (limit <= 0) {
  57. throw ADP.InvalidMultipartNameToManyParts (property, name, limit);
  58. }
  59. if (-1 != leftQuote.IndexOf(separator) || -1 != rightQuote.IndexOf(separator) || leftQuote.Length != rightQuote.Length) {
  60. throw ADP.InvalidMultipartNameIncorrectUsageOfQuotes (property, name);
  61. }
  62. string[] parsedNames = new string[limit]; // return string array
  63. int stringCount = 0; // index of current string in the buffer
  64. MPIState state = MPIState.MPI_Value; // Initalize the starting state
  65. StringBuilder sb = new StringBuilder (name.Length); // String buffer to hold the string being currently built, init the string builder so it will never be resized
  66. StringBuilder whitespaceSB = null; // String buffer to hold white space used when parsing nonquoted strings 'a b . c d' = 'a b' and 'c d'
  67. char rightQuoteChar = ' '; // Right quote character to use given the left quote character found.
  68. for (int index = 0; index < name.Length; ++index) {
  69. char testchar = name[index];
  70. switch (state) {
  71. case MPIState.MPI_Value : {
  72. int quoteIndex;
  73. if (IsWhitespace (testchar)) { // Is White Space then skip the whitespace
  74. continue;
  75. }
  76. else
  77. if (testchar == separator) { // If we found a separator, no string was found, initalize the string we are parsing to Empty and the next one to Empty.
  78. // This is NOT a redundent setting of string.Empty it solves the case where we are parsing ".foo" and we should be returning null, null, empty, foo
  79. parsedNames[stringCount] = string.Empty;
  80. IncrementStringCount (name, parsedNames, ref stringCount, property);
  81. }
  82. else
  83. if (-1 != (quoteIndex = leftQuote.IndexOf(testchar))) { // If we are a left quote
  84. rightQuoteChar = rightQuote[quoteIndex]; // record the corresponding right quote for the left quote
  85. sb.Length = 0;
  86. if (!removequotes) {
  87. sb.Append (testchar);
  88. }
  89. state = MPIState.MPI_ParseQuote;
  90. }
  91. else
  92. if (-1 != rightQuote.IndexOf(testchar)) { // If we shouldn't see a right quote
  93. throw ADP.InvalidMultipartNameIncorrectUsageOfQuotes (property, name);
  94. }
  95. else {
  96. sb.Length = 0;
  97. sb.Append (testchar);
  98. state = MPIState.MPI_ParseNonQuote;
  99. }
  100. break;
  101. }
  102. case MPIState.MPI_ParseNonQuote: {
  103. if (testchar == separator) {
  104. parsedNames[stringCount] = sb.ToString (); // set the currently parsed string
  105. IncrementStringCount (name, parsedNames, ref stringCount, property);
  106. state = MPIState.MPI_Value;
  107. }
  108. else // Quotes are not valid inside a non-quoted name
  109. if (-1 != rightQuote.IndexOf (testchar)) {
  110. throw ADP.InvalidMultipartNameIncorrectUsageOfQuotes (property, name);
  111. }
  112. else
  113. if (-1 != leftQuote.IndexOf(testchar)) {
  114. throw ADP.InvalidMultipartNameIncorrectUsageOfQuotes (property, name);
  115. }
  116. else
  117. if (IsWhitespace (testchar)) { // If it is Whitespace
  118. parsedNames[stringCount] = sb.ToString (); // Set the currently parsed string
  119. if (null == whitespaceSB) {
  120. whitespaceSB = new StringBuilder ();
  121. }
  122. whitespaceSB.Length = 0;
  123. whitespaceSB.Append (testchar); // start to record the white space, if we are parsing a name like "foo bar" we should return "foo bar"
  124. state = MPIState.MPI_LookForNextCharOrSeparator;
  125. }
  126. else {
  127. sb.Append (testchar);
  128. }
  129. break;
  130. }
  131. case MPIState.MPI_LookForNextCharOrSeparator : {
  132. if (!IsWhitespace (testchar)) { // If it is not whitespace
  133. if (testchar == separator) {
  134. IncrementStringCount (name, parsedNames, ref stringCount, property);
  135. state = MPIState.MPI_Value;
  136. }
  137. else { // If its not a separator and not whitespace
  138. sb.Append (whitespaceSB);
  139. sb.Append (testchar);
  140. parsedNames[stringCount] = sb.ToString (); // Need to set the name here in case the string ends here.
  141. state = MPIState.MPI_ParseNonQuote;
  142. }
  143. }
  144. else {
  145. whitespaceSB.Append (testchar);
  146. }
  147. break;
  148. }
  149. case MPIState.MPI_ParseQuote: {
  150. if (testchar == rightQuoteChar) { // if se are on a right quote see if we are escapeing the right quote or ending the quoted string
  151. if (!removequotes) {
  152. sb.Append (testchar);
  153. }
  154. state = MPIState.MPI_RightQuote;
  155. }
  156. else {
  157. sb.Append (testchar); // Append what we are currently parsing
  158. }
  159. break;
  160. }
  161. case MPIState.MPI_RightQuote : {
  162. if (testchar == rightQuoteChar) { // If the next char is a another right quote then we were escapeing the right quote
  163. sb.Append (testchar);
  164. state = MPIState.MPI_ParseQuote;
  165. }
  166. else
  167. if (testchar == separator) { // If its a separator then record what we've parsed
  168. parsedNames[stringCount] = sb.ToString ();
  169. IncrementStringCount (name, parsedNames, ref stringCount, property);
  170. state = MPIState.MPI_Value;
  171. }
  172. else
  173. if (!IsWhitespace (testchar)) { // If it is not white space we got problems
  174. throw ADP.InvalidMultipartNameIncorrectUsageOfQuotes (property, name);
  175. }
  176. else { // It is a whitespace character so the following char should be whitespace, separator, or end of string anything else is bad
  177. parsedNames[stringCount] = sb.ToString ();
  178. state = MPIState.MPI_LookForSeparator;
  179. }
  180. break;
  181. }
  182. case MPIState.MPI_LookForSeparator : {
  183. if (!IsWhitespace (testchar)) { // If it is not whitespace
  184. if (testchar == separator) { // If it is a separator
  185. IncrementStringCount (name, parsedNames, ref stringCount, property);
  186. state = MPIState.MPI_Value;
  187. }
  188. else { // Othewise not a separator
  189. throw ADP.InvalidMultipartNameIncorrectUsageOfQuotes (property, name);
  190. }
  191. }
  192. break;
  193. }
  194. }
  195. }
  196. // Resolve final states after parsing the string
  197. switch (state) {
  198. case MPIState.MPI_Value : // These states require no extra action
  199. case MPIState.MPI_LookForSeparator :
  200. case MPIState.MPI_LookForNextCharOrSeparator :
  201. break;
  202. case MPIState.MPI_ParseNonQuote: // Dump what ever was parsed
  203. case MPIState.MPI_RightQuote :
  204. parsedNames[stringCount] = sb.ToString ();
  205. break;
  206. case MPIState.MPI_ParseQuote: // Invalid Ending States
  207. default:
  208. throw ADP.InvalidMultipartNameIncorrectUsageOfQuotes (property, name);
  209. }
  210. if (parsedNames[0] == null) {
  211. if (ThrowOnEmptyMultipartName) {
  212. throw ADP.InvalidMultipartName (property, name); // Name is entirely made up of whitespace
  213. }
  214. }
  215. else {
  216. // Shuffle the parsed name, from left justification to right justification, ie [a][b][null][null] goes to [null][null][a][b]
  217. int offset = limit - stringCount - 1;
  218. if (offset > 0) {
  219. for (int x = limit - 1; x >= offset; --x) {
  220. parsedNames[x] = parsedNames[x - offset];
  221. parsedNames[x - offset] = null;
  222. }
  223. }
  224. }
  225. return parsedNames;
  226. }
  227. }
  228. }