Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataWeb / Server / System / Data / Services / KeyInstance.cs / 1305376 / KeyInstance.cs
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Provides a class used to represent a key value for a resource.
//
//
// @owner [....]
//---------------------------------------------------------------------
namespace System.Data.Services
{
using System.Collections.Generic;
using System.Data.Services.Parsing;
using System.Data.Services.Providers;
using System.Diagnostics;
/// Provides a class used to represent a key for a resource.
///
/// Internally, every key instance has a collection of values. These values
/// can be named or positional, depending on how they were specified
/// if parsed from a URI.
///
internal class KeyInstance
{
/// Empty key singleton.
private static readonly KeyInstance Empty = new KeyInstance();
/// Named values.
private readonly Dictionary namedValues;
/// Positional values.
private readonly List positionalValues;
/// Initializes a new empty instance.
private KeyInstance()
{
}
/// Initializes a new instance.
/// Named values.
/// Positional values for this instance.
///
/// One of namedValues or positionalValues should be non-null, but not both.
///
private KeyInstance(Dictionary namedValues, List positionalValues)
{
Debug.Assert(
(namedValues == null) != (positionalValues == null),
"namedValues == null != positionalValues == null -- one or the other should be assigned, but not both");
this.namedValues = namedValues;
this.positionalValues = positionalValues;
}
/// Whether the values have a name.
internal bool AreValuesNamed
{
get { return this.namedValues != null; }
}
/// Checks whether this key has any values.
internal bool IsEmpty
{
get { return this == Empty; }
}
/// Returns a dictionary of named values when they AreValuesNamed is true.
internal IDictionary NamedValues
{
get { return this.namedValues; }
}
/// Returns a list of values when they AreValuesNamed is false.
internal IList PositionalValues
{
get { return this.positionalValues; }
}
/// Number of values in the key.
internal int ValueCount
{
get
{
if (this == Empty)
{
return 0;
}
else if (this.namedValues != null)
{
return this.namedValues.Count;
}
else
{
Debug.Assert(this.positionalValues != null, "this.positionalValues != null");
return this.positionalValues.Count;
}
}
}
/// Attempts to parse key values from the specified text.
/// Text to parse (not null).
/// After invocation, the parsed key instance.
///
/// true if the key instance was parsed; false if there was a
/// syntactic error.
///
///
/// The returned instance contains only string values. To get typed values, a call to
/// is necessary.
///
internal static bool TryParseKeysFromUri(string text, out KeyInstance instance)
{
return TryParseFromUri(text, true /*allowNamedValues*/, false /*allowNull*/, out instance);
}
/// Attempts to parse nullable values (only positional values, no name-value pairs) from the specified text.
/// Text to parse (not null).
/// After invocation, the parsed key instance.
///
/// true if the given values were parsed; false if there was a
/// syntactic error.
///
///
/// The returned instance contains only string values. To get typed values, a call to
/// is necessary.
///
internal static bool TryParseNullableTokens(string text, out KeyInstance instance)
{
return TryParseFromUri(text, false /*allowNamedValues*/, true /*allowNull*/, out instance);
}
/// Tries to convert values to the keys of the specified type.
/// Type with key information for conversion.
/// true if all values were converted; false otherwise.
internal bool TryConvertValues(ResourceType type)
{
Debug.Assert(type != null, "type != null");
Debug.Assert(!this.IsEmpty, "!this.IsEmpty -- caller should check");
Debug.Assert(type.KeyProperties.Count == this.ValueCount, "type.KeyProperties.Count == this.ValueCount -- will change with containment");
if (this.namedValues != null)
{
for (int i = 0; i < type.KeyProperties.Count; i++)
{
ResourceProperty property = type.KeyProperties[i];
object unconvertedValue;
if (!this.namedValues.TryGetValue(property.Name, out unconvertedValue))
{
return false;
}
string valueText = (string)unconvertedValue;
object convertedValue;
if (!WebConvert.TryKeyStringToPrimitive(valueText, property.Type, out convertedValue))
{
return false;
}
this.namedValues[property.Name] = convertedValue;
}
}
else
{
Debug.Assert(this.positionalValues != null, "positionalValues != null -- otherwise this is Empty");
for (int i = 0; i < type.KeyProperties.Count; i++)
{
string valueText = (string)this.positionalValues[i];
object convertedValue;
if (!WebConvert.TryKeyStringToPrimitive(valueText, type.KeyProperties[i].Type, out convertedValue))
{
return false;
}
this.positionalValues[i] = convertedValue;
}
}
return true;
}
/// Attempts to parse key values from the specified text.
/// Text to parse (not null).
/// Set to true if the parser should accept named values
/// so syntax like Name='value'. If this is false, the parsing will fail on such constructs.
/// Set to true if the parser should accept null values.
/// If set to false, the parser will fail on null values.
/// After invocation, the parsed key instance.
///
/// true if the key instance was parsed; false if there was a
/// syntactic error.
///
///
/// The returned instance contains only string values. To get typed values, a call to
/// is necessary.
///
private static bool TryParseFromUri(string text, bool allowNamedValues, bool allowNull, out KeyInstance instance)
{
Debug.Assert(text != null, "text != null");
Dictionary namedValues = null;
List positionalValues = null;
ExpressionLexer lexer = new ExpressionLexer(text);
Token currentToken = lexer.CurrentToken;
if (currentToken.Id == TokenId.End)
{
instance = Empty;
return true;
}
instance = null;
do
{
if (currentToken.Id == TokenId.Identifier && allowNamedValues)
{
// Name-value pair.
if (positionalValues != null)
{
// We cannot mix named and non-named values.
return false;
}
string identifier = lexer.CurrentToken.GetIdentifier();
lexer.NextToken();
if (lexer.CurrentToken.Id != TokenId.Equal)
{
return false;
}
lexer.NextToken();
if (!lexer.CurrentToken.IsKeyValueToken)
{
return false;
}
string namedValue = lexer.CurrentToken.Text;
WebUtil.CreateIfNull(ref namedValues);
if (namedValues.ContainsKey(identifier))
{
// Duplicate name.
return false;
}
namedValues.Add(identifier, namedValue);
}
else if (currentToken.IsKeyValueToken || (allowNull && currentToken.Id == TokenId.NullLiteral))
{
// Positional value.
if (namedValues != null)
{
// We cannot mix named and non-named values.
return false;
}
WebUtil.CreateIfNull(ref positionalValues);
positionalValues.Add(lexer.CurrentToken.Text);
}
else
{
return false;
}
// Read the next token. We should be at the end, or find
// we have a comma followed by something.
lexer.NextToken();
currentToken = lexer.CurrentToken;
if (currentToken.Id == TokenId.Comma)
{
lexer.NextToken();
currentToken = lexer.CurrentToken;
if (currentToken.Id == TokenId.End)
{
// Trailing comma.
return false;
}
}
}
while (currentToken.Id != TokenId.End);
instance = new KeyInstance(namedValues, positionalValues);
return true;
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Provides a class used to represent a key value for a resource.
//
//
// @owner [....]
//---------------------------------------------------------------------
namespace System.Data.Services
{
using System.Collections.Generic;
using System.Data.Services.Parsing;
using System.Data.Services.Providers;
using System.Diagnostics;
/// Provides a class used to represent a key for a resource.
///
/// Internally, every key instance has a collection of values. These values
/// can be named or positional, depending on how they were specified
/// if parsed from a URI.
///
internal class KeyInstance
{
/// Empty key singleton.
private static readonly KeyInstance Empty = new KeyInstance();
/// Named values.
private readonly Dictionary namedValues;
/// Positional values.
private readonly List positionalValues;
/// Initializes a new empty instance.
private KeyInstance()
{
}
/// Initializes a new instance.
/// Named values.
/// Positional values for this instance.
///
/// One of namedValues or positionalValues should be non-null, but not both.
///
private KeyInstance(Dictionary namedValues, List positionalValues)
{
Debug.Assert(
(namedValues == null) != (positionalValues == null),
"namedValues == null != positionalValues == null -- one or the other should be assigned, but not both");
this.namedValues = namedValues;
this.positionalValues = positionalValues;
}
/// Whether the values have a name.
internal bool AreValuesNamed
{
get { return this.namedValues != null; }
}
/// Checks whether this key has any values.
internal bool IsEmpty
{
get { return this == Empty; }
}
/// Returns a dictionary of named values when they AreValuesNamed is true.
internal IDictionary NamedValues
{
get { return this.namedValues; }
}
/// Returns a list of values when they AreValuesNamed is false.
internal IList PositionalValues
{
get { return this.positionalValues; }
}
/// Number of values in the key.
internal int ValueCount
{
get
{
if (this == Empty)
{
return 0;
}
else if (this.namedValues != null)
{
return this.namedValues.Count;
}
else
{
Debug.Assert(this.positionalValues != null, "this.positionalValues != null");
return this.positionalValues.Count;
}
}
}
/// Attempts to parse key values from the specified text.
/// Text to parse (not null).
/// After invocation, the parsed key instance.
///
/// true if the key instance was parsed; false if there was a
/// syntactic error.
///
///
/// The returned instance contains only string values. To get typed values, a call to
/// is necessary.
///
internal static bool TryParseKeysFromUri(string text, out KeyInstance instance)
{
return TryParseFromUri(text, true /*allowNamedValues*/, false /*allowNull*/, out instance);
}
/// Attempts to parse nullable values (only positional values, no name-value pairs) from the specified text.
/// Text to parse (not null).
/// After invocation, the parsed key instance.
///
/// true if the given values were parsed; false if there was a
/// syntactic error.
///
///
/// The returned instance contains only string values. To get typed values, a call to
/// is necessary.
///
internal static bool TryParseNullableTokens(string text, out KeyInstance instance)
{
return TryParseFromUri(text, false /*allowNamedValues*/, true /*allowNull*/, out instance);
}
/// Tries to convert values to the keys of the specified type.
/// Type with key information for conversion.
/// true if all values were converted; false otherwise.
internal bool TryConvertValues(ResourceType type)
{
Debug.Assert(type != null, "type != null");
Debug.Assert(!this.IsEmpty, "!this.IsEmpty -- caller should check");
Debug.Assert(type.KeyProperties.Count == this.ValueCount, "type.KeyProperties.Count == this.ValueCount -- will change with containment");
if (this.namedValues != null)
{
for (int i = 0; i < type.KeyProperties.Count; i++)
{
ResourceProperty property = type.KeyProperties[i];
object unconvertedValue;
if (!this.namedValues.TryGetValue(property.Name, out unconvertedValue))
{
return false;
}
string valueText = (string)unconvertedValue;
object convertedValue;
if (!WebConvert.TryKeyStringToPrimitive(valueText, property.Type, out convertedValue))
{
return false;
}
this.namedValues[property.Name] = convertedValue;
}
}
else
{
Debug.Assert(this.positionalValues != null, "positionalValues != null -- otherwise this is Empty");
for (int i = 0; i < type.KeyProperties.Count; i++)
{
string valueText = (string)this.positionalValues[i];
object convertedValue;
if (!WebConvert.TryKeyStringToPrimitive(valueText, type.KeyProperties[i].Type, out convertedValue))
{
return false;
}
this.positionalValues[i] = convertedValue;
}
}
return true;
}
/// Attempts to parse key values from the specified text.
/// Text to parse (not null).
/// Set to true if the parser should accept named values
/// so syntax like Name='value'. If this is false, the parsing will fail on such constructs.
/// Set to true if the parser should accept null values.
/// If set to false, the parser will fail on null values.
/// After invocation, the parsed key instance.
///
/// true if the key instance was parsed; false if there was a
/// syntactic error.
///
///
/// The returned instance contains only string values. To get typed values, a call to
/// is necessary.
///
private static bool TryParseFromUri(string text, bool allowNamedValues, bool allowNull, out KeyInstance instance)
{
Debug.Assert(text != null, "text != null");
Dictionary namedValues = null;
List positionalValues = null;
ExpressionLexer lexer = new ExpressionLexer(text);
Token currentToken = lexer.CurrentToken;
if (currentToken.Id == TokenId.End)
{
instance = Empty;
return true;
}
instance = null;
do
{
if (currentToken.Id == TokenId.Identifier && allowNamedValues)
{
// Name-value pair.
if (positionalValues != null)
{
// We cannot mix named and non-named values.
return false;
}
string identifier = lexer.CurrentToken.GetIdentifier();
lexer.NextToken();
if (lexer.CurrentToken.Id != TokenId.Equal)
{
return false;
}
lexer.NextToken();
if (!lexer.CurrentToken.IsKeyValueToken)
{
return false;
}
string namedValue = lexer.CurrentToken.Text;
WebUtil.CreateIfNull(ref namedValues);
if (namedValues.ContainsKey(identifier))
{
// Duplicate name.
return false;
}
namedValues.Add(identifier, namedValue);
}
else if (currentToken.IsKeyValueToken || (allowNull && currentToken.Id == TokenId.NullLiteral))
{
// Positional value.
if (namedValues != null)
{
// We cannot mix named and non-named values.
return false;
}
WebUtil.CreateIfNull(ref positionalValues);
positionalValues.Add(lexer.CurrentToken.Text);
}
else
{
return false;
}
// Read the next token. We should be at the end, or find
// we have a comma followed by something.
lexer.NextToken();
currentToken = lexer.CurrentToken;
if (currentToken.Id == TokenId.Comma)
{
lexer.NextToken();
currentToken = lexer.CurrentToken;
if (currentToken.Id == TokenId.End)
{
// Trailing comma.
return false;
}
}
}
while (currentToken.Id != TokenId.End);
instance = new KeyInstance(namedValues, positionalValues);
return true;
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.