249 lines
14 KiB
C#
249 lines
14 KiB
C#
|
//------------------------------------------------------------------------------
|
||
|
// <copyright file="CommandBuilder.cs" company="Microsoft">
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// </copyright>
|
||
|
// <owner current="true" primary="true">[....]</owner>
|
||
|
// <owner current="true" primary="false">[....]</owner>
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
namespace System.Data.Common {
|
||
|
using System.Diagnostics;
|
||
|
using System.Text;
|
||
|
using System.Globalization;
|
||
|
|
||
|
internal 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;
|
||
|
|
||
|
/*
|
||
|
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.
|
||
|
*/
|
||
|
internal static string[] ParseMultipartIdentifier(string name, string leftQuote, string rightQuote, string property, bool ThrowOnEmptyMultipartName) {
|
||
|
return ParseMultipartIdentifier(name, leftQuote, rightQuote,'.', MaxParts, true, property, ThrowOnEmptyMultipartName);
|
||
|
}
|
||
|
|
||
|
private enum MPIState {
|
||
|
MPI_Value,
|
||
|
MPI_ParseNonQuote,
|
||
|
MPI_LookForSeparator,
|
||
|
MPI_LookForNextCharOrSeparator,
|
||
|
MPI_ParseQuote,
|
||
|
MPI_RightQuote,
|
||
|
}
|
||
|
|
||
|
/* Core function for parsing the multipart identifer string.
|
||
|
* paramaters: name - string to parse
|
||
|
* leftquote: set of characters which are valid quoteing characters to initiate a quote
|
||
|
* rightquote: set of characters which are valid to stop a quote, array index's correspond to the the leftquote array.
|
||
|
* separator: separator to use
|
||
|
* limit: number of names to parse out
|
||
|
* removequote:to remove the quotes on the returned string
|
||
|
*/
|
||
|
private static void IncrementStringCount (string name, string[] ary, ref int position, string property) {
|
||
|
++position;
|
||
|
int limit = ary.Length;
|
||
|
if (position >= limit) {
|
||
|
throw ADP.InvalidMultipartNameToManyParts (property, name, limit);
|
||
|
}
|
||
|
ary[position] = string.Empty;
|
||
|
}
|
||
|
|
||
|
private static bool IsWhitespace (char ch) {
|
||
|
return Char.IsWhiteSpace (ch);
|
||
|
}
|
||
|
|
||
|
internal static string[] ParseMultipartIdentifier(string name, string leftQuote, string rightQuote, char separator, int limit, bool removequotes, string property, bool ThrowOnEmptyMultipartName) {
|
||
|
|
||
|
if (limit <= 0) {
|
||
|
throw ADP.InvalidMultipartNameToManyParts (property, name, limit);
|
||
|
}
|
||
|
|
||
|
if (-1 != leftQuote.IndexOf(separator) || -1 != rightQuote.IndexOf(separator) || leftQuote.Length != rightQuote.Length) {
|
||
|
throw ADP.InvalidMultipartNameIncorrectUsageOfQuotes (property, name);
|
||
|
}
|
||
|
|
||
|
string[] parsedNames = new string[limit]; // return string array
|
||
|
int stringCount = 0; // index of current string in the buffer
|
||
|
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 (name, parsedNames, ref stringCount, property);
|
||
|
}
|
||
|
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;
|
||
|
if (!removequotes) {
|
||
|
sb.Append (testchar);
|
||
|
}
|
||
|
state = MPIState.MPI_ParseQuote;
|
||
|
}
|
||
|
else
|
||
|
if (-1 != rightQuote.IndexOf(testchar)) { // If we shouldn't see a right quote
|
||
|
throw ADP.InvalidMultipartNameIncorrectUsageOfQuotes (property, name);
|
||
|
}
|
||
|
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 (name, parsedNames, ref stringCount, property);
|
||
|
state = MPIState.MPI_Value;
|
||
|
}
|
||
|
else // Quotes are not valid inside a non-quoted name
|
||
|
if (-1 != rightQuote.IndexOf (testchar)) {
|
||
|
throw ADP.InvalidMultipartNameIncorrectUsageOfQuotes (property, name);
|
||
|
}
|
||
|
else
|
||
|
if (-1 != leftQuote.IndexOf(testchar)) {
|
||
|
throw ADP.InvalidMultipartNameIncorrectUsageOfQuotes (property, name);
|
||
|
}
|
||
|
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 "foo bar" we should return "foo bar"
|
||
|
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 (name, parsedNames, ref stringCount, property);
|
||
|
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
|
||
|
if (!removequotes) {
|
||
|
sb.Append (testchar);
|
||
|
}
|
||
|
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 (name, parsedNames, ref stringCount, property);
|
||
|
state = MPIState.MPI_Value;
|
||
|
}
|
||
|
else
|
||
|
if (!IsWhitespace (testchar)) { // If it is not white space we got problems
|
||
|
throw ADP.InvalidMultipartNameIncorrectUsageOfQuotes (property, name);
|
||
|
}
|
||
|
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 (name, parsedNames, ref stringCount, property);
|
||
|
state = MPIState.MPI_Value;
|
||
|
}
|
||
|
else { // Othewise not a separator
|
||
|
throw ADP.InvalidMultipartNameIncorrectUsageOfQuotes (property, name);
|
||
|
}
|
||
|
}
|
||
|
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 ADP.InvalidMultipartNameIncorrectUsageOfQuotes (property, name);
|
||
|
}
|
||
|
|
||
|
if (parsedNames[0] == null) {
|
||
|
if (ThrowOnEmptyMultipartName) {
|
||
|
throw ADP.InvalidMultipartName (property, name); // Name is entirely made up of whitespace
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
// Shuffle the parsed name, from left justification to right justification, ie [a][b][null][null] goes to [null][null][a][b]
|
||
|
int offset = limit - stringCount - 1;
|
||
|
if (offset > 0) {
|
||
|
for (int x = limit - 1; x >= offset; --x) {
|
||
|
parsedNames[x] = parsedNames[x - offset];
|
||
|
parsedNames[x - offset] = null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return parsedNames;
|
||
|
}
|
||
|
}
|
||
|
}
|