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