File: System\Xml\Xsl\XmlQueryCardinality.cs
Project: ndp\fx\src\XmlUtils\System.Data.SqlXml.csproj (System.Data.SqlXml)
//------------------------------------------------------------------------------
// <copyright file="XmlQueryCardinality.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
//------------------------------------------------------------------------------
 
using System.Diagnostics;
using System.IO;
 
namespace System.Xml.Xsl {
 
    /// <summary>
    /// Cardinality of part of XmlQueryType
    /// struct is being used because enum doesn't allow members
    /// </summary>
    internal struct XmlQueryCardinality  {
        private int value;
 
        #region ctor
        /// <summary>
        /// Private constructor
        /// </summary>
        private XmlQueryCardinality(int value) {
            Debug.Assert(0x00 <= value && value <= 0x07);
            this.value = value;
        }
        #endregion
 
        #region enum
        /// <summary>
        /// exactly zero (empty)
        /// </summary>
        public static XmlQueryCardinality None {
            get { return new XmlQueryCardinality(0x00); }
        }
 
        /// <summary>
        /// exactly zero (empty)
        /// </summary>
        public static XmlQueryCardinality Zero {
            get { return new XmlQueryCardinality(0x01); }
        }
 
        /// <summary>
        /// exactly one
        /// </summary>
        public static XmlQueryCardinality One {
            get { return new XmlQueryCardinality(0x02); }
        }
 
        /// <summary>
        /// zero or one (not more)
        /// </summary>
        public static XmlQueryCardinality ZeroOrOne {
            get { return new XmlQueryCardinality(0x03); }
        }
 
        /// <summary>
        /// strictly more than one
        /// </summary>
        public static XmlQueryCardinality More {
            get { return new XmlQueryCardinality(0x04); }
        }
 
        /// <summary>
        /// not one (strictly zero or strictly more)
        /// </summary>
        public static XmlQueryCardinality NotOne {
            get { return new XmlQueryCardinality(0x05); }
        }
 
        /// <summary>
        /// one or more (not empty)
        /// </summary>
        public static XmlQueryCardinality OneOrMore {
            get { return new XmlQueryCardinality(0x06); }
        }
 
        /// <summary>
        /// zero or more (any cardinality)
        /// </summary>
        public static XmlQueryCardinality ZeroOrMore {
            get { return new XmlQueryCardinality(0x07); }
        }
        #endregion
 
        #region ==
        /// <summary>
        /// Strongly-typed Equals that returns true if this type and "other" type are equivalent.
        /// </summary>
        public bool Equals(XmlQueryCardinality other) {
            return this.value == other.value;
        }
 
        /// <summary>
        /// Overload == operator to call Equals rather than do reference equality.
        /// </summary>
        public static bool operator ==(XmlQueryCardinality left, XmlQueryCardinality right) {
            return left.value == right.value;
        }
 
        /// <summary>
        /// Overload != operator to call Equals rather than do reference inequality.
        /// </summary>
        public static bool operator !=(XmlQueryCardinality left, XmlQueryCardinality right) {
            return left.value != right.value;
        }
 
        /// <summary>
        /// True if "other" is an XmlQueryCardinality, and this type is the exact same static type.
        /// </summary>
        public override bool Equals(object other) {
            if (other is XmlQueryCardinality) {
                return Equals((XmlQueryCardinality)other);
            }
            return false;
        }
 
        /// <summary>
        /// Return hash code of this instance.
        /// </summary>
        public override int GetHashCode() {
            return value;
        }
        #endregion
 
        #region algebra
        /// <summary>
        /// Return union with other
        /// </summary>
        public static XmlQueryCardinality operator |(XmlQueryCardinality left, XmlQueryCardinality right) {
            return new XmlQueryCardinality(left.value | right.value);
        }
 
        /// <summary>
        /// Return intersection with other
        /// </summary>
        public static XmlQueryCardinality operator &(XmlQueryCardinality left, XmlQueryCardinality right) {
            return new XmlQueryCardinality(left.value & right.value);
        }
 
        /// <summary>
        /// Return this product other
        /// </summary>
        public static XmlQueryCardinality operator *(XmlQueryCardinality left, XmlQueryCardinality right) {
            return cardinalityProduct[left.value, right.value];
        }
 
        /// <summary>
        /// Return sum with other
        /// </summary>
        public static XmlQueryCardinality operator +(XmlQueryCardinality left, XmlQueryCardinality right) {
            return cardinalitySum[left.value, right.value];
        }
 
    #if NEVER
        /// <summary>
        /// Returns true if this cardinality is guaranteed to be a subset of "other".
        /// </summary>
        private bool IsSubset(XmlQueryCardinality other) {
            return (this.value & ~other.value) == 0;
        }
    #endif
 
        /// <summary>
        /// Returns true is left is subset of right.
        /// </summary>
        public static bool operator <=(XmlQueryCardinality left, XmlQueryCardinality right) {
            return (left.value & ~right.value) == 0;
        }
 
        /// <summary>
        /// Returns true is right is subset of left.
        /// </summary>
        public static bool operator >=(XmlQueryCardinality left, XmlQueryCardinality right) {
            return (right.value & ~left.value) == 0;
        }
 
        /// <summary>
        /// Compute the cardinality of a subset of a set of the given cardinality.
        /// </summary>
        /// <param name="c">the cardinality of a set</param>
        /// <returns>the cardinality of a subset</returns>
        public XmlQueryCardinality AtMost() {
            //  Fill downward to zero
            return new XmlQueryCardinality(this.value | (this.value >> 1) | (this.value >> 2));
        }
 
        /// <summary>
        /// Returns true if every non-None subset of this cardinality is disjoint with "other" cardinality.
        /// Here is the behavior for None, which is the inverse of the None behavior for IsSubset:
        ///   None op  None = false
        ///   None op ~None = false
        ///  ~None op  None = true
        /// </summary>
        public bool NeverSubset(XmlQueryCardinality other) {
            return this.value != 0 && (this.value & other.value) == 0;
        }
 
        /// <summary>
        /// Table of cardinality products.
        /// </summary>
        private static readonly XmlQueryCardinality[,] cardinalityProduct = {
                          //   None  Zero  One         ZeroOrOne   More    NotOne  OneOrMore   ZeroOrMore
            /* None       */ { None, Zero, None      , Zero      , None  , Zero  , None      , Zero       },
            /* Zero       */ { Zero, Zero, Zero      , Zero      , Zero  , Zero  , Zero      , Zero       },
            /* One        */ { None, Zero, One       , ZeroOrOne , More  , NotOne, OneOrMore , ZeroOrMore },
            /* ZeroOrOne  */ { Zero, Zero, ZeroOrOne , ZeroOrOne , NotOne, NotOne, ZeroOrMore, ZeroOrMore },
            /* More       */ { None, Zero, More      , NotOne    , More  , NotOne, More      , NotOne     },
            /* NotOne     */ { Zero, Zero, NotOne    , NotOne    , NotOne, NotOne, NotOne    , NotOne     },
            /* OneOrMore  */ { None, Zero, OneOrMore , ZeroOrMore, More  , NotOne, OneOrMore , ZeroOrMore },
            /* ZeroOrMore */ { Zero, Zero, ZeroOrMore, ZeroOrMore, NotOne, NotOne, ZeroOrMore, ZeroOrMore }
        };
 
        /// <summary>
        /// Table of cardinality sums.
        /// </summary>
        private static readonly XmlQueryCardinality[,] cardinalitySum = {
                          //   None        Zero        One        ZeroOrOne   More  NotOne      OneOrMore  ZeroOrMore
            /* None       */ { None      , Zero      , One      , ZeroOrOne , More, NotOne    , OneOrMore, ZeroOrMore},
            /* Zero       */ { Zero      , Zero      , One      , ZeroOrOne , More, NotOne    , OneOrMore, ZeroOrMore},
            /* One        */ { One       , One       , More     , OneOrMore , More, OneOrMore , More     , OneOrMore },
            /* ZeroOrOne  */ { ZeroOrOne , ZeroOrOne , OneOrMore, ZeroOrMore, More, ZeroOrMore, OneOrMore, ZeroOrMore},
            /* More       */ { More      , More      , More     , More      , More, More      , More     , More      },
            /* NotOne     */ { NotOne    , NotOne    , OneOrMore, ZeroOrMore, More, NotOne    , OneOrMore, ZeroOrMore},
            /* OneOrMore  */ { OneOrMore , OneOrMore , More     , OneOrMore , More, OneOrMore , More     , OneOrMore },
            /* ZeroOrMore */ { ZeroOrMore, ZeroOrMore, OneOrMore, ZeroOrMore, More, ZeroOrMore, OneOrMore, ZeroOrMore}
        };
        #endregion
 
        #region Serialization
        /// <summary>
        /// String representation.
        /// </summary>
        private static readonly string[] toString = {
            /* None       */ ""  ,
            /* Zero       */ "?" ,
            /* One        */ ""  ,
            /* ZeroOrOne  */ "?" ,
            /* More       */ "+" ,
            /* NotOne     */ "*" ,
            /* OneOrMore  */ "+" ,
            /* ZeroOrMore */ "*"
        };
 
        /// <summary>
        /// Serialization
        /// </summary>
        private static readonly string[] serialized = {
            /* None       */ "None",
            /* Zero       */ "Zero",
            /* One        */ "One",
            /* ZeroOrOne  */ "ZeroOrOne",
            /* More       */ "More",
            /* NotOne     */ "NotOne",
            /* OneOrMore  */ "OneOrMore",
            /* ZeroOrMore */ "ZeroOrMore"
        };
 
        /// <summary>
        /// Return the string representation of a cardinality, normalized to either ?, +, *, or "" (card 1).
        /// </summary>
        public string ToString(string format) {
            if (format == "S") {
                return serialized[this.value];
            }
            else {
                return ToString();
            }
        }
 
        /// <summary>
        /// Return the string representation of a cardinality, normalized to either ?, +, *, or "" (card 1).
        /// </summary>
        public override string ToString() {
            return toString[this.value];
        }
 
        /// <summary>
        /// Deserialization
        /// </summary>
        public XmlQueryCardinality(string s) {
            this.value = 0x00;
            for (int i = 0; i < serialized.Length; i++) {
                if (s == serialized[i]) {
                    this.value = i;
                    break;
                }
            }
        }
 
        /// <summary>
        /// Serialize the object to BinaryWriter.
        /// </summary>
        public void GetObjectData(BinaryWriter writer) {
            writer.Write((byte)value);
        }
 
        /// <summary>
        /// Deserialize the object from BinaryReader.
        /// </summary>
        public XmlQueryCardinality(BinaryReader reader) : this(reader.ReadByte()) {
        }
        #endregion
    }
}