/// <summary>
        /// Reads the match.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="reader">The reader.</param>
        /// <returns></returns>
        /// <exception cref="System.Xml.XmlException">MatchId IsNullOrEmpty</exception>
        protected override XacmlMatch ReadMatch(XmlReader reader) {
            Contract.Requires<ArgumentNullException>(reader != null, "reader");
            Contract.Requires<XmlException>(reader.IsStartElement(XacmlConstants.ElementNames.Match, this.version.NamespacePolicy));

            var gaMatchId = reader.GetAttribute("MatchId");
            if (string.IsNullOrEmpty(gaMatchId)) {
                throw Diagnostic.DiagnosticTools.ExceptionUtil.ThrowHelperError(new XmlException("MatchId IsNullOrEmpty"));
            }

            reader.ReadStartElement(XacmlConstants.ElementNames.Match, this.version.NamespacePolicy);

            var attributeValue = ReadAttributeValue(reader);

            XacmlMatch result;
            if (reader.IsStartElement(XacmlConstants.ElementNames.AttributeDesignator, this.version.NamespacePolicy)) {
                var attributeDesignator = this.ReadAttributeDesignator(reader) as XacmlAttributeDesignator;
                result = new XacmlMatch(new Uri(gaMatchId, UriKind.RelativeOrAbsolute), attributeValue, attributeDesignator);
            }
            else {
                XacmlAttributeSelector attributeSelector = ReadAttributeSelector(reader);
                result = new XacmlMatch(new Uri(gaMatchId, UriKind.RelativeOrAbsolute), attributeValue, attributeSelector);
            }

            reader.ReadEndElement();
            return result;
        }
        protected bool? MatchEvaluation(XacmlMatch match) {
            Contract.Requires<ArgumentNullException>(match != null);

            // get function
            DelegateWrapper matchFunction = this.functions[match.MatchId.ToString()];

            // get attribute value
            object attribute1 = this.types[match.AttributeValue.DataType.ToString()].ConvertFromString(match.AttributeValue.Value, match.AttributeValue);

            IEnumerable<string> attribute2Values;
            string dataType;
            object source;

            // get second attribute
            if (match.AttributeDesignator != null) {
                dataType = match.AttributeDesignator.DataType.ToString();
                attribute2Values = this.GetAttributeDesignator(match.AttributeDesignator as XacmlAttributeDesignator);
                source = match.AttributeDesignator;
            }
            else {
                dataType = match.AttributeSelector.DataType.ToString();
                attribute2Values = this.GetAttributeSelector(match.AttributeSelector);
                source = match.AttributeSelector;
            }

            if (attribute2Values == null) {
                // If an operational error were to occur while evaluating the <AttributeDesignator> or <AttributeSelector> element, then the result of the entire expression SHALL be "Indeterminate".
                return null;
            }
            else {
                // If the <AttributeDesignator> or <AttributeSelector> element were to evaluate to an empty bag, then the result of the expression SHALL be "False".
                if (attribute2Values.Count() == 0) {
                    return false;
                }
            }

            bool? matchResult = false;
            foreach (string designatorValue in attribute2Values) {
                object attribute2 = this.types[dataType].ConvertFromString(designatorValue, source);

                // evaluate
                bool? functionResult = (bool)matchFunction.DynamicInvoke(new XPathContext(this.xpathVersion, this.requestDoc, this.namespaces), attribute1, attribute2);

                // Otherwise, if at least one of the function applications results in "Indeterminate", then the result SHALL be "Indeterminate".
                if (!functionResult.HasValue) {
                    matchResult = null;
                }
                // If at least one of those function applications were to evaluate to "True", then the result of the entire expression SHALL be "True".
                else if (functionResult.Value) {
                    return true;
                }
            }

            return matchResult;
        }
        /// <summary>
        /// Writes the match.
        /// </summary>
        /// <param name="writer">The writer.</param>
        /// <param name="data">The data.</param>
        /// <exception cref="System.InvalidOperationException"></exception>
        protected override void WriteMatch(XmlWriter writer, XacmlMatch data) {
            Contract.Requires<ArgumentNullException>(writer != null);
            Contract.Requires<ArgumentNullException>(data != null);

            writer.WriteStartElement(XacmlConstants.Prefixes.Policy, XacmlConstants.ElementNames.SubjectMatch, this.version.NamespacePolicy);
            writer.WriteAttributeString(XacmlConstants.AttributeNames.MatchId, data.MatchId.OriginalString);

            this.WriteAttributeValue(writer, data.AttributeValue);

            if (data.AttributeSelector == null && data.AttributeDesignator == null) {
                throw Diagnostic.DiagnosticTools.ExceptionUtil.ThrowHelperError(new InvalidOperationException());
            }

            if (data.AttributeDesignator != null) {
                this.WriteAttributeDesignator(writer, data.AttributeDesignator);
            }

            if (data.AttributeSelector != null) {
                this.WriteAttributeSelector(writer, data.AttributeSelector);
            }

            writer.WriteEndElement();
        }
        /// <summary>
        /// Writes the match.
        /// </summary>
        /// <param name="writer">The writer.</param>
        /// <param name="data">The data.</param>
        protected virtual void WriteMatch(XmlWriter writer, XacmlMatch data) {
            Contract.Requires<ArgumentNullException>(writer != null);
            Contract.Requires<ArgumentNullException>(data != null);

            Action<string, dynamic> action = (matchType, match) => {
                writer.WriteStartElement(XacmlConstants.Prefixes.Policy, matchType, this.version.NamespacePolicy);
                writer.WriteAttributeString(XacmlConstants.AttributeNames.MatchId, match.MatchId.OriginalString);

                this.WriteAttributeValue(writer, match.AttributeValue);

                if (match.AttributeSelector == null && match.AttributeDesignator == null) {
                    throw Diagnostic.DiagnosticTools.ExceptionUtil.ThrowHelperError(new InvalidOperationException());
                }

                if (match.AttributeDesignator != null) {
                    this.WriteAttributeDesignator(writer, match.AttributeDesignator);
                }

                if (match.AttributeSelector != null) {
                    this.WriteAttributeSelector(writer, match.AttributeSelector);
                }

                writer.WriteEndElement();
            };

            var actionMatch = data as XacmlActionMatch;
            if (actionMatch != null) {
                action(XacmlConstants.ElementNames.ActionMatch, actionMatch);
            }

            var resourceMatch = data as XacmlResourceMatch;
            if (resourceMatch != null) {
                action(XacmlConstants.ElementNames.ResourceMatch, resourceMatch);
            }

            var subjectMatch = data as XacmlSubjectMatch;
            if (subjectMatch != null) {
                action(XacmlConstants.ElementNames.SubjectMatch, subjectMatch);
            }
        }