File: System\Xml\Xsl\XmlQueryCardinality.cs
Web Access
Project: src\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
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 readonly struct XmlQueryCardinality : IEquatable<XmlQueryCardinality>
    {
        private readonly int _value;
 
        #region ctor
        /// <summary>
        /// Private constructor
        /// </summary>
        private XmlQueryCardinality(int value)
        {
            Debug.Assert(0x00 <= value && value <= 0x07);
            _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 _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([NotNullWhen(true)] 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 this product other
        /// </summary>
        public static XmlQueryCardinality operator *(XmlQueryCardinality left, XmlQueryCardinality right)
        {
            return s_cardinalityProduct[left._value, right._value];
        }
 
        /// <summary>
        /// Return sum with other
        /// </summary>
        public static XmlQueryCardinality operator +(XmlQueryCardinality left, XmlQueryCardinality right)
        {
            return s_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>
        /// <returns>the cardinality of a subset</returns>
        public XmlQueryCardinality AtMost()
        {
            //  Fill downward to zero
            return new XmlQueryCardinality(_value | (_value >> 1) | (_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 _value != 0 && (_value & other._value) == 0;
        }
 
        /// <summary>
        /// Table of cardinality products.
        /// </summary>
        private static readonly XmlQueryCardinality[,] s_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[,] s_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[] s_toString = {
            /* None       */ "",
            /* Zero       */ "?",
            /* One        */ "",
            /* ZeroOrOne  */ "?",
            /* More       */ "+",
            /* NotOne     */ "*",
            /* OneOrMore  */ "+",
            /* ZeroOrMore */ "*"
        };
 
        /// <summary>
        /// Serialization
        /// </summary>
        private static readonly string[] s_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 s_serialized[_value];
            }
            else
            {
                return ToString();
            }
        }
 
        /// <summary>
        /// Return the string representation of a cardinality, normalized to either ?, +, *, or "" (card 1).
        /// </summary>
        public override string ToString()
        {
            return s_toString[_value];
        }
 
        /// <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
    }
}