/// <summary>
        /// Writes the apply.
        /// </summary>
        /// <param name="writer">The writer.</param>
        /// <param name="xacmlApply">The xacml apply.</param>
        protected override void WriteApply(XmlWriter writer, XacmlApply xacmlApply) {
            writer.WriteStartElement(XacmlConstants.Prefixes.Policy, XacmlConstants.ElementNames.Apply, this.version.NamespacePolicy);
            writer.WriteAttributeString(XacmlConstants.AttributeNames.FunctionId, xacmlApply.FunctionId.OriginalString);

            if (xacmlApply.Description != null) {
                writer.WriteElementString(XacmlConstants.Prefixes.Policy, XacmlConstants.ElementNames.Description, this.version.NamespacePolicy, xacmlApply.Description);
            }

            foreach (IXacmlApply applyElem in xacmlApply.Parameters) {
                this.WriteExpressionType(writer, new XacmlExpression() { Property = applyElem });
            }
        }
        /// <summary>
        /// Reads the apply.
        /// </summary>
        /// <param name="reader">The reader.</param>
        /// <returns></returns>
        protected override XacmlApply ReadApply(XmlReader reader) {
            Contract.Requires<ArgumentNullException>(reader != null, "reader");
            Contract.Requires<XmlException>(reader.IsStartElement(XacmlConstants.ElementNames.Apply, this.version.NamespacePolicy));

            Uri functionId = this.ReadAttribute<Uri>(reader, XacmlConstants.AttributeNames.FunctionId);

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

            XacmlApply apply = new XacmlApply(functionId);

            if (reader.IsStartElement(XacmlConstants.ElementNames.Description, this.version.NamespacePolicy)) {
                apply.Description = reader.ReadElementContentAsString(XacmlConstants.ElementNames.Description, this.version.NamespacePolicy);
            }

            IDictionary<Tuple<string, string>, Action> dicts = new Dictionary<Tuple<string, string>, Action>()
            {
                { new Tuple<string, string>(XacmlConstants.ElementNames.Apply, this.version.NamespacePolicy), () => apply.Parameters.Add(this.ReadApply(reader)) },
                { new Tuple<string, string>(XacmlConstants.ElementNames.Function, this.version.NamespacePolicy), () => apply.Parameters.Add(this.ReadFunction(reader)) },
                { new Tuple<string, string>(XacmlConstants.ElementNames.AttributeValue, this.version.NamespacePolicy), () => apply.Parameters.Add(this.ReadAttributeValue(reader)) },
                { new Tuple<string, string>(XacmlConstants.ElementNames.AttributeDesignator, this.version.NamespacePolicy), () => apply.Parameters.Add(this.ReadAttributeDesignator(reader)) },
                { new Tuple<string, string>(XacmlConstants.ElementNames.AttributeSelector, this.version.NamespacePolicy), () => apply.Parameters.Add(this.ReadAttributeSelector(reader)) },
                { new Tuple<string, string>(XacmlConstants.ElementNames.VariableReference, this.version.NamespacePolicy), () => apply.Parameters.Add(this.ReadOptional(XacmlConstants.ElementNames.VariableReference, this.version.NamespacePolicy,
                            new ReadElement<XacmlVariableReference>(
                                o => 
                                    {
                                        string res = this.ReadAttribute<string>(reader, XacmlConstants.AttributeNames.VariableId);
                                        reader.Read();
                                        return new XacmlVariableReference(res);
                                    }
                            ), reader)) },
            };

            this.ReadChoiceMultiply(reader, dicts);

            reader.ReadEndElement();

            return apply;
        }
        protected object ApplyEvaluate(XacmlApply apply) {
            Contract.Requires<ArgumentNullException>(apply != null);

            // get function
            DelegateWrapper matchFunction = this.functions[apply.FunctionId.ToString()];
            object[] parameters = apply.Parameters.Select(o => this.ExpressionEvaluate(o)).ToArray();
            object result = matchFunction.DynamicInvoke(new XPathContext(this.xpathVersion, this.requestDoc, this.namespaces), parameters);
            return result;
        }
        /// <summary>
        /// Reads the apply.
        /// </summary>
        /// <param name="reader">The reader.</param>
        /// <returns></returns>
        /// <exception cref="System.Xml.XmlException">Apply NotStartElement</exception>
        protected virtual XacmlApply ReadApply(XmlReader reader) {
            Contract.Requires<ArgumentNullException>(reader != null, "reader");

            if (!reader.IsStartElement(XacmlConstants.ElementNames.Apply, this.version.NamespacePolicy)) {
                throw Diagnostic.DiagnosticTools.ExceptionUtil.ThrowHelperError(new XmlException("Apply NotStartElement"));
            }

            Uri functionId = this.ReadAttribute<Uri>(reader, XacmlConstants.AttributeNames.FunctionId);

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

            XacmlApply apply = new XacmlApply(functionId);

            IDictionary<Tuple<string, string>, Action> dicts = new Dictionary<Tuple<string, string>, Action>()
            {
                { new Tuple<string, string>(XacmlConstants.ElementNames.Apply, this.version.NamespacePolicy), () => apply.Parameters.Add(this.ReadApply(reader)) },
                { new Tuple<string, string>(XacmlConstants.ElementNames.Function, this.version.NamespacePolicy), () => apply.Parameters.Add(this.ReadFunction(reader)) },
                { new Tuple<string, string>(XacmlConstants.ElementNames.AttributeValue, this.version.NamespacePolicy), () => apply.Parameters.Add(this.ReadAttributeValue(reader)) },
                { new Tuple<string, string>(XacmlConstants.ElementNames.SubjectAttributeDesignator, this.version.NamespacePolicy), () => apply.Parameters.Add(this.ReadAttributeDesignator(reader)) },
                { new Tuple<string, string>(XacmlConstants.ElementNames.ResourceAttributeDesignator, this.version.NamespacePolicy), () => apply.Parameters.Add(this.ReadAttributeDesignator(reader)) },
                { new Tuple<string, string>(XacmlConstants.ElementNames.ActionAttributeDesignator, this.version.NamespacePolicy), () => apply.Parameters.Add(this.ReadAttributeDesignator(reader)) },
                { new Tuple<string, string>(XacmlConstants.ElementNames.EnvironmentAttributeDesignator, this.version.NamespacePolicy), () => apply.Parameters.Add(this.ReadAttributeDesignator(reader)) },
                { new Tuple<string, string>(XacmlConstants.ElementNames.AttributeSelector, this.version.NamespacePolicy), () => apply.Parameters.Add(this.ReadAttributeSelector(reader)) },
            };

            this.ReadChoiceMultiply(reader, dicts);

            reader.ReadEndElement();

            return apply;
        }
        /// <summary>
        /// Write ApplyType (Not element!)
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="apply"></param>
        private void WriteApplyType(XmlWriter writer, XacmlApply apply) {
            Contract.Requires<ArgumentNullException>(writer != null);
            Contract.Requires<ArgumentNullException>(apply != null);

            writer.WriteAttributeString(XacmlConstants.AttributeNames.FunctionId, apply.FunctionId.OriginalString);

            foreach (IXacmlApply applyElem in apply.Parameters) {
                Type applyElemType = applyElem.GetType();
                if (applyElemType == typeof(XacmlAttributeSelector)) {
                    XacmlAttributeSelector elem = applyElem as XacmlAttributeSelector;
                    writer.WriteStartElement(XacmlConstants.Prefixes.Policy, XacmlConstants.ElementNames.AttributeSelector, this.version.NamespacePolicy);
                    writer.WriteAttributeString(XacmlConstants.AttributeNames.RequestContextPath, elem.Path);
                    writer.WriteAttributeString(XacmlConstants.AttributeNames.DataType, elem.DataType.OriginalString);

                    if (elem.MustBePresent.HasValue) {
                        writer.WriteAttributeString(XacmlConstants.AttributeNames.DataType, XmlConvert.ToString(elem.MustBePresent.Value));
                    }

                    writer.WriteEndElement();
                }
                else if (applyElemType == typeof(XacmlResourceAttributeDesignator)) {
                    this.WriteAttributeDesignator(writer, applyElem as XacmlResourceAttributeDesignator);
                }
                else if (applyElemType == typeof(XacmlActionAttributeDesignator)) {
                    this.WriteAttributeDesignator(writer, applyElem as XacmlActionAttributeDesignator);
                }
                else if (applyElemType == typeof(XacmlEnvironmentAttributeDesignator)) {
                    this.WriteAttributeDesignator(writer, applyElem as XacmlEnvironmentAttributeDesignator);
                }
                else if (applyElemType == typeof(XacmlSubjectAttributeDesignator)) {
                    this.WriteAttributeDesignator(writer, applyElem as XacmlSubjectAttributeDesignator);
                }
                else if (applyElemType == typeof(XacmlAttributeValue)) {
                    this.WriteAttributeValue(writer, applyElem as XacmlAttributeValue);
                }
                else if (applyElemType == typeof(XacmlFunction)) {
                    writer.WriteStartElement(XacmlConstants.Prefixes.Policy, XacmlConstants.ElementNames.Function, this.version.NamespacePolicy);
                    writer.WriteAttributeString(XacmlConstants.AttributeNames.FunctionId, (applyElem as XacmlFunction).FunctionId.OriginalString);
                    writer.WriteEndElement();
                }
                else if (applyElemType == typeof(XacmlApply)) {
                    this.WriteApply(writer, applyElem as XacmlApply);
                }
            }
        }
        /// <summary>
        /// Writes the apply.
        /// </summary>
        /// <param name="writer">The writer.</param>
        /// <param name="apply">The apply.</param>
        protected virtual void WriteApply(XmlWriter writer, XacmlApply apply) {
            Contract.Requires<ArgumentNullException>(writer != null);
            Contract.Requires<ArgumentNullException>(apply != null);

            writer.WriteStartElement(XacmlConstants.Prefixes.Policy, XacmlConstants.ElementNames.Apply, this.version.NamespacePolicy);
            this.WriteApplyType(writer, apply);
            writer.WriteEndElement();
        }
        /// <summary>
        /// Writes the apply.
        /// </summary>
        /// <param name="writer">The writer.</param>
        /// <param name="xacmlApply">The xacml apply.</param>
        protected override void WriteApply(XmlWriter writer, XacmlApply xacmlApply) {
            writer.WriteStartElement(XacmlConstants.Prefixes.Policy, XacmlConstants.ElementNames.Apply, this.version.NamespacePolicy);
            writer.WriteAttributeString(XacmlConstants.AttributeNames.FunctionId, xacmlApply.FunctionId.OriginalString);

            foreach (IXacmlApply applyElem in xacmlApply.Parameters) {
                Type applyElemType = applyElem.GetType();
                if (applyElemType == typeof(XacmlVariableReference)) {
                    XacmlVariableReference elem = applyElem as XacmlVariableReference;
                    writer.WriteStartElement(XacmlConstants.Prefixes.Policy, XacmlConstants.ElementNames.VariableReference, this.version.NamespacePolicy);
                    writer.WriteAttributeString(XacmlConstants.AttributeNames.VariableId, elem.VariableReference);
                    writer.WriteEndElement();
                }
                else if (applyElemType == typeof(XacmlAttributeSelector)) {
                    XacmlAttributeSelector elem = applyElem as XacmlAttributeSelector;
                    writer.WriteStartElement(XacmlConstants.Prefixes.Policy, XacmlConstants.ElementNames.AttributeSelector, this.version.NamespacePolicy);
                    writer.WriteAttributeString(XacmlConstants.AttributeNames.RequestContextPath, elem.Path);
                    writer.WriteAttributeString(XacmlConstants.AttributeNames.DataType, elem.DataType.OriginalString);

                    if (elem.MustBePresent.HasValue) {
                        writer.WriteAttributeString(XacmlConstants.AttributeNames.DataType, XmlConvert.ToString(elem.MustBePresent.Value));
                    }

                    writer.WriteEndElement();
                }
                else if (applyElemType == typeof(XacmlResourceAttributeDesignator)) {
                    this.WriteAttributeDesignator(writer, applyElem as XacmlResourceAttributeDesignator);
                }
                else if (applyElemType == typeof(XacmlActionAttributeDesignator)) {
                    this.WriteAttributeDesignator(writer, applyElem as XacmlActionAttributeDesignator);
                }
                else if (applyElemType == typeof(XacmlEnvironmentAttributeDesignator)) {
                    this.WriteAttributeDesignator(writer, applyElem as XacmlEnvironmentAttributeDesignator);
                }
                else if (applyElemType == typeof(XacmlSubjectAttributeDesignator)) {
                    this.WriteAttributeDesignator(writer, applyElem as XacmlSubjectAttributeDesignator);
                }
                else if (applyElemType == typeof(XacmlAttributeValue)) {
                    this.WriteAttributeValue(writer, applyElem as XacmlAttributeValue);
                }
                else if (applyElemType == typeof(XacmlFunction)) {
                    writer.WriteStartElement(XacmlConstants.Prefixes.Policy, XacmlConstants.ElementNames.Function, this.version.NamespacePolicy);
                    writer.WriteAttributeString(XacmlConstants.AttributeNames.FunctionId, (applyElem as XacmlFunction).FunctionId.OriginalString);
                    writer.WriteEndElement();
                }
                else if (applyElemType == typeof(XacmlApply)) {
                    this.WriteApply(writer, applyElem as XacmlApply);
                }
            }

            writer.WriteEndElement();
        }