Welcome to mirror list, hosted at ThFree Co, Russian Federation.

MultipartIdentifier.cs « Internal « Common « Data « System « System.Data.Entity « referencesource « class « mcs - github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: b4f9fb47850f38da78e55b2697c621f197e6ddc3 (plain)
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;
        }
    }
}