// --------------------------------------------------------------------------- // // 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"); } } } }