// ---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------
//
//
// ---------------------------------------------------------------------
namespace Microsoft.Database.Isam
{
using System;
using System.Collections;
///
/// This collection is used to construct keys using generic values for each
/// key segment comprising the key. Each key is NOT tied to any specific
/// schema so the same key could be used with different indices. However,
/// the key must be compatible with the index with which it is used or else
/// an argument exception will be thrown. Type coercion will be performed
/// as appropriate for each key segment when the key is used with an index
/// based on the type of each key segment in the key versus the type of each
/// key column in that index. If type coercion fails, an invalid cast
/// exception will be thrown when the key is used.
///
public class Key : CollectionBase
{
///
/// The prefix
///
private bool prefix = false;
///
/// The wildcard
///
private bool wildcard = false;
///
/// Gets a key that can be used to represent the start of an index.
///
public static Key Start
{
get
{
return new Key();
}
}
///
/// Gets a key that can be used to represent the end of an index.
///
public static Key End
{
get
{
return new Key();
}
}
///
/// Gets a value indicating whether [has prefix].
///
///
/// true if [has prefix]; otherwise, false.
///
internal bool HasPrefix
{
get
{
return this.prefix;
}
}
///
/// Gets a value indicating whether [has wildcard].
///
///
/// true if [has wildcard]; otherwise, false.
///
internal bool HasWildcard
{
get
{
return this.wildcard == true || List.Count == 0;
}
}
///
/// Returns a key containing one ordinary key segment per given value.
///
/// the values for each key column that will be used to compose the key
/// A key containing one ordinary key segment per given value.
///
///
public static Key Compose(params object[] values)
{
Key key = new Key();
foreach (object value in values)
{
key.Add(value);
}
return key;
}
///
/// Returns a key containing one ordinary key segment per given value
/// except that the last given value becomes a prefix key segment.
///
/// the values for each key column that will be used to compose the key
/// A key containing one ordinary key segment per given value
/// except that the last given value becomes a prefix key segment.
///
///
public static Key ComposePrefix(params object[] values)
{
Key key = new Key();
for (int i = 0; i < values.Length - 1; i++)
{
key.Add(values[i]);
}
key.AddPrefix(values[values.Length - 1]);
return key;
}
///
/// Returns a key containing one ordinary key segment per given value
/// and one wildcard key segment.
///
/// the values for each key column that will be used to compose the key
/// A key containing one ordinary key segment per given value
/// and one wildcard key segment.
///
///
public static Key ComposeWildcard(params object[] values)
{
Key key = new Key();
foreach (object value in values)
{
key.Add(value);
}
key.AddWildcard();
return key;
}
///
/// Sets the next key segment of the key to the given value.
///
/// the value for the next key column that will be used to compose the key
public void Add(object value)
{
List.Add(new KeySegment(value, false, false, false));
}
///
/// Sets the next key segment of the key to the given value as a
/// prefix. A prefix key segment will match any column value that
/// has a prefix that matches the given value.
///
/// the value for the next key column that will be used to compose the key
///
/// Prefix key segments only make sense in the context of a variable
/// length column. Any other use will result in an argument exception
/// when the key is used.
///
/// A key may contain only one prefix key segment. Further, a prefix
/// key segment may not be followed by an ordinary key segment.
///
///
public void AddPrefix(object value)
{
List.Add(new KeySegment(value, true, false, false));
this.prefix = true;
}
///
/// Sets the next key segment of the key to the given value as a
/// wildcard. A wildcard key segment will match any column value.
///
///
/// A key may contain any number of wildcard key segments. Further, a
/// wildcard key segment may be followed only by other wildcard key
/// segments. Finally, extra wildcard key segments will be ignored
/// when the key is used.
///
/// A key with no key segments acts as if it contains a single wildcard
/// key segment.
///
///
public void AddWildcard()
{
// flag current last key segment as saying that the next key segment
// will be a wildcard
if (List.Count > 0)
{
KeySegment segment = (KeySegment)List[List.Count - 1];
this.List[List.Count - 1] = new KeySegment(segment.Value, segment.Prefix, segment.Wildcard, true);
}
try
{
// add a wildcard key segment
List.Add(new KeySegment(null, false, true, false));
this.wildcard = true;
}
finally
{
// flag the new last key segment as saying that the next key segment
// will not be a wildcard. this will handle the case where the
// insertion of the new wildcard fails
if (List.Count > 0)
{
KeySegment segment = (KeySegment)List[List.Count - 1];
this.List[List.Count - 1] = new KeySegment(segment.Value, segment.Prefix, segment.Wildcard, false);
}
}
}
///
/// Performs additional custom processes when validating a value.
///
/// The object to validate.
///
/// value must be of type KeySegment;value
/// or
/// value cannot be a prefix if the key already contains a prefix;value
/// or
/// value cannot be a prefix if the key already contains a wildcard;value
/// or
/// value must be a wildcard if the key already contains a prefix;value
/// or
/// value must be a wildcard if the key already contains a wildcard;value
///
protected override void OnValidate(object value)
{
bool sawPrefix = false;
bool sawWildcard = false;
foreach (KeySegment segment in this)
{
sawPrefix = sawPrefix || segment.Prefix == true;
sawWildcard = sawWildcard || segment.Wildcard == true;
}
if (!(value is KeySegment))
{
throw new ArgumentException("value must be of type KeySegment", "value");
}
if (((KeySegment)value).Prefix == true && sawPrefix == true)
{
throw new ArgumentException("value cannot be a prefix if the key already contains a prefix", "value");
}
if (((KeySegment)value).Prefix == true && sawWildcard == true)
{
throw new ArgumentException("value cannot be a prefix if the key already contains a wildcard", "value");
}
if (((KeySegment)value).Prefix == false && ((KeySegment)value).Wildcard == false && sawPrefix == true)
{
throw new ArgumentException("value must be a wildcard if the key already contains a prefix", "value");
}
if (((KeySegment)value).Prefix == false && ((KeySegment)value).Wildcard == false && sawWildcard == true)
{
throw new ArgumentException("value must be a wildcard if the key already contains a wildcard", "value");
}
}
}
}