/// <summary>
        /// Initializes a new instance of the <see cref="PolicyInformationPoint"/> class.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="requestDoc">The request document.</param>
        public PolicyInformationPoint(XacmlContextRequest request, XmlDocument requestDoc) {
            Contract.Requires<ArgumentNullException>(request != null);
            Contract.Requires<ArgumentNullException>(requestDoc != null);

            this.request = request;
            this.attributesProcessor = AttributesProcessor.Instance;
            this.xpathProcessor = XPathProcessor.Instance;
            this.requestDocument = requestDoc;
        }
        public void WriteRequest_11()
        {
            var s = new XacmlContextSubject(new XacmlContextAttribute(new Uri("uri:action"), new Uri("uri:type"), new XacmlContextAttributeValue()));
            var r = new XacmlContextResource(new XacmlContextAttribute(new Uri("uri:action"), new Uri("uri:type"), new XacmlContextAttributeValue()));
            var a = new XacmlContextAction(new XacmlContextAttribute(new Uri("uri:action"), new Uri("uri:type"), new XacmlContextAttributeValue()));
            var request = new XacmlContextRequest(r, a, s);

            StringBuilder builder = new StringBuilder();
            using (XmlWriter writer = XmlWriter.Create(builder))
            {
                var serializer = new Xacml10ProtocolSerializer();
                serializer.WriteContextRequest(writer, request);
            }

            string xml = builder.ToString();
            ValidateMessage(xml, @"..\..\_Data\cs-xacml-schema-context-01.xsd");
        }
        /// <summary>
        /// public void WriteRequest
        /// </summary>
        /// <param name="writer">XmlWriter writer</param>
        /// <param name="data">XacmlContextRequest data</param>
        public virtual void WriteContextRequest(XmlWriter writer, XacmlContextRequest data) {
            Contract.Requires<ArgumentNullException>(writer != null);
            Contract.Requires<ArgumentNullException>(data != null);

            writer.WriteStartElement(XacmlConstants.Prefixes.Context, XacmlConstants.ElementNames.Request, this.version.NamespaceContext);

            // Subject
            foreach (var subject in data.Subjects) {
                this.WriteContextSubject(writer, subject);
            }

            this.WriteContextResource(writer, data.Resources.First());

            this.WriteContextAction(writer, data.Action);

            if (data.Environment != null) {
                this.WriteContextEnvironment(writer, data.Environment);
            }

            writer.WriteEndElement();
        }
        public override XacmlContextResponse Evaluate(XacmlContextRequest request, XmlDocument requestDoc = null) {
            this.advices = new Dictionary<XacmlEffectType, List<XacmlAdvice>>()
            {
                { XacmlEffectType.Permit, new List<XacmlAdvice>() },
                { XacmlEffectType.Deny, new List<XacmlAdvice>() }
            };

            this.applicablePolicies = new Dictionary<XacmlEffectType, List<XacmlContextPolicyIdReference>>()
            {
                { XacmlEffectType.Permit, new List<XacmlContextPolicyIdReference>() },
                { XacmlEffectType.Deny, new List<XacmlContextPolicyIdReference>() }
            };

            this.applicablePolicySets = new Dictionary<XacmlEffectType, List<XacmlContextPolicySetIdReference>>()
            {
                { XacmlEffectType.Permit, new List<XacmlContextPolicySetIdReference>() },
                { XacmlEffectType.Deny, new List<XacmlContextPolicySetIdReference>() }
            };

            return base.Evaluate(request, requestDoc);
        }
        public virtual XacmlContextRequest ReadContextRequest(XmlReader reader) {
            Contract.Requires<ArgumentNullException>(reader != null, "reader");

            if (!XacmlProtocolSerializer.CanReadContext(reader, XacmlConstants.ElementNames.Request, this.version.NamespaceContext)) {
                throw Diagnostic.DiagnosticTools.ExceptionUtil.ThrowHelperError(new InvalidOperationException());
            }

            reader.ReadStartElement(XacmlConstants.ElementNames.Request, this.version.NamespaceContext);

            List<XacmlContextSubject> subjects = new List<XacmlContextSubject>();
            this.ReadList(subjects, XacmlConstants.ElementNames.Subject, this.version.NamespaceContext, ReadContextSubject, reader, isRequired: true);

            XacmlContextRequest result = new XacmlContextRequest(
                this.ReadRequired(XacmlConstants.ElementNames.Resource, this.version.NamespaceContext, this.ReadContextResource, reader),
                this.ReadRequired(XacmlConstants.ElementNames.Action, this.version.NamespaceContext, this.ReadContextAction, reader),
                subjects
                );

            result.Environment = this.ReadOptional(XacmlConstants.ElementNames.Environment, this.version.NamespaceContext, this.ReadContextEnvironment, reader);

            reader.ReadEndElement();

            return result;
        }
        protected override IEnumerable<XacmlContextResult> RequestEvaluate(XacmlContextRequest request) {
            // MultiRequests element in a Request
            if (request.RequestReferences.Count > 0) {
                var results = new List<XacmlContextResult>(request.RequestReferences.Count);
                foreach (var reference in request.RequestReferences) {
                    var refAttributes = request.Attributes.Where(x => reference.AttributeReferences.Contains(x.Id));
                    if (refAttributes.Count() != reference.AttributeReferences.Count) {
                        throw new XacmlInvalidSyntaxException("<RequestReference> contains an invalid reference.");
                    }

                    var refRequest = new XacmlContextRequest(request.ReturnPolicyIdList, false, refAttributes) { XPathVersion = request.XPathVersion };
                    results.AddRange(this.RequestEvaluate(refRequest));
                }

                return results;
            }

            // multiple instances of an Attributes element with the same category ID
            var category = request.Attributes
                .GroupBy(o => o.Category.OriginalString)
                .Where(x => x.Count() > 1)
                .Select(o => o.Key).FirstOrDefault();
            if (category != null) {
                var results = new List<XacmlContextResult>();
                var otherAttributes = request.Attributes.Where(x => x.Category.OriginalString != category);
                foreach (XacmlContextAttributes categoryAttribute in request.Attributes.Where(o => o.Category.OriginalString == category)) {
                    var refAttributes = otherAttributes.Concat(new XacmlContextAttributes[] { categoryAttribute });
                    var refRequest = new XacmlContextRequest(request.ReturnPolicyIdList, false, refAttributes) { XPathVersion = request.XPathVersion };
                    results.AddRange(this.RequestEvaluate(refRequest));
                }

                return results;
            }

            return base.RequestEvaluate(request);
        }
        /// <summary>
        /// Writes the context request.
        /// </summary>
        /// <param name="writer">The writer.</param>
        /// <param name="data">The data.</param>
        public override void WriteContextRequest(XmlWriter writer, XacmlContextRequest data) {
            writer.WriteStartElement(XacmlConstants.Prefixes.Context, XacmlConstants.ElementNames.Request, this.version.NamespaceContext);
            writer.WriteAttributeString(XacmlConstants.AttributeNames.ReturnPolicyIdList, data.ReturnPolicyIdList.ToString());
            writer.WriteAttributeString(XacmlConstants.AttributeNames.CombinedDecision, data.CombinedDecision.ToString());

            if (data.XPathVersion != null) {
                writer.WriteStartElement(XacmlConstants.Prefixes.Context, XacmlConstants.ElementNames.RequestDefaults, this.version.NamespacePolicy);
                writer.WriteElementString(XacmlConstants.Prefixes.Context, XacmlConstants.ElementNames.XPathVersion, this.version.NamespacePolicy, data.XPathVersion.OriginalString);
                writer.WriteEndElement();
            }

            foreach (XacmlContextAttributes attr in data.Attributes) {
                this.WriteContextAttributes(writer, attr);
            }

            if (data.RequestReferences.Count > 0) {
                writer.WriteStartElement(XacmlConstants.Prefixes.Context, XacmlConstants.ElementNames.MultiRequests, this.version.NamespaceContext);

                foreach (var referCol in data.RequestReferences) {
                    writer.WriteStartElement(XacmlConstants.Prefixes.Context, XacmlConstants.ElementNames.RequestReference, this.version.NamespaceContext);

                    foreach (string refer in referCol.AttributeReferences) {
                        writer.WriteStartElement(XacmlConstants.Prefixes.Context, XacmlConstants.ElementNames.AttributesReference, this.version.NamespaceContext);
                        writer.WriteAttributeString(XacmlConstants.AttributeNames.ReferenceId, refer);
                        writer.WriteEndElement();
                    }

                    writer.WriteEndElement();
                }

                writer.WriteEndElement();
            }

            writer.WriteEndElement();
        }
        /// <summary>
        /// Reads the context request.
        /// </summary>
        /// <param name="reader">The reader.</param>
        /// <returns></returns>
        /// <exception cref="System.InvalidOperationException"></exception>
        public override XacmlContextRequest ReadContextRequest(XmlReader reader) {
            Contract.Requires<ArgumentNullException>(reader != null, "reader");

            if (!XacmlProtocolSerializer.CanReadContext(reader, XacmlConstants.ElementNames.Request, this.version.NamespaceContext)) {
                throw Diagnostic.DiagnosticTools.ExceptionUtil.ThrowHelperError(new InvalidOperationException());
            }

            bool returnPolicyIdList = this.ReadAttribute<bool>(reader, XacmlConstants.AttributeNames.ReturnPolicyIdList);

            //PROFILE - Multiple Decision Profile - #POL01 - #SPEC2760
            bool combinedDecision = this.ReadAttribute<bool>(reader, XacmlConstants.AttributeNames.CombinedDecision);
            if (combinedDecision) {
                throw Diagnostic.DiagnosticTools.ExceptionUtil.ThrowHelperError(new Abc.Xacml.Runtime.XacmlInvalidDataTypeException("Multiple Decision Profile not implemented"));
            }

            reader.ReadStartElement(XacmlConstants.ElementNames.Request, this.version.NamespaceContext);

            Uri pathVersion = null;
            if (reader.IsStartElement(XacmlConstants.ElementNames.RequestDefaults, this.version.NamespacePolicy)) {
                reader.ReadStartElement(XacmlConstants.ElementNames.RequestDefaults, this.version.NamespacePolicy);
                if (!reader.IsStartElement(XacmlConstants.ElementNames.XPathVersion, this.version.NamespacePolicy)) {
                    throw new XmlException("XPathVerison NotStartElement");
                }

                pathVersion = new Uri(reader.ReadElementContentAsString(XacmlConstants.ElementNames.XPathVersion, this.version.NamespacePolicy), UriKind.RelativeOrAbsolute);
                reader.ReadEndElement();
            }

            List<XacmlContextAttributes> attributes = new List<XacmlContextAttributes>();
            this.ReadList<XacmlContextAttributes>(attributes, XacmlConstants.ElementNames.Attributes, this.version.NamespaceContext, this.ReadContextAttributes, reader, isRequired: true);

            XacmlContextRequest result = new XacmlContextRequest(returnPolicyIdList, combinedDecision, attributes) {
                XPathVersion = pathVersion
            };

            if (reader.IsStartElement(XacmlConstants.ElementNames.MultiRequests, this.version.NamespacePolicy)) {
                reader.ReadStartElement(XacmlConstants.ElementNames.MultiRequests, this.version.NamespaceContext);

                this.ReadList<XacmlContextRequestReference>(result.RequestReferences, XacmlConstants.ElementNames.RequestReference, this.version.NamespaceContext,
                    o => {
                        reader.ReadStartElement(XacmlConstants.ElementNames.RequestReference, this.version.NamespaceContext);
                        ICollection<string> refer = new List<string>();
                        this.ReadList<string>(refer, XacmlConstants.ElementNames.AttributesReference, this.version.NamespaceContext,
                            b => {
                                var referenceId = this.ReadAttribute<string>(b, XacmlConstants.AttributeNames.ReferenceId);
                                b.Read();
                                return referenceId;
                            },
                            o, isRequired: true);
                        reader.ReadEndElement();
                        return new XacmlContextRequestReference(refer);
                    }, reader, isRequired: true);

                reader.ReadEndElement();
            }

            reader.ReadEndElement();

            return result;
        }
        /// <summary>
        /// Evaluates the specified request.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="requestDoc">The request document.</param>
        /// <returns>The response.</returns>
        public virtual XacmlContextResponse Evaluate(XacmlContextRequest request, XmlDocument requestDoc) {
            Contract.Requires<ArgumentNullException>(request != null);
            Contract.Requires<ArgumentNullException>(requestDoc != null);

            this.requestDoc = requestDoc;
            this.obligations = new Dictionary<XacmlEffectType, List<XacmlObligation>>()
            {
                { XacmlEffectType.Permit, new List<XacmlObligation>() },
                { XacmlEffectType.Deny, new List<XacmlObligation>() }
            };

            return new XacmlContextResponse(this.RequestEvaluate(request));
        }
        protected virtual IEnumerable<XacmlContextResult> RequestEvaluate(XacmlContextRequest request) {
            Contract.Requires<ArgumentNullException>(request != null);
            Contract.Assert(this.requestDoc != null);

            this.pip = new PolicyInformationPoint(request, this.requestDoc);

            // Hierarchical resources
            /*
            var scopeAttribute = request.Resources.SelectMany(x => x.Attributes).FirstOrDefault(y => y.AttributeId.OriginalString == "urn:oasis:names:tc:xacml:1.0:resource:scope");
            if (scopeAttribute != null) {
                
                var resourceAttrubute = request.Resources.SelectMany(x => x.Attributes).FirstOrDefault(y => y.AttributeId.OriginalString == "urn:oasis:names:tc:xacml:1.0:resource:resource-id");
                if (resourceAttrubute == null) {
                    // TODO: throw new XacmlPo
                }

                var resource = new XacmlContextResource(resourceAttrubute); 

                var refRequest = new XacmlContextRequest(resource, request.Action, request.Subjects);
                return this.RequestEvaluate(refRequest);
            }
             */ 

            XacmlContextResult result = null;
            try {
                XacmlDecisionResult decisionResult; 
                if (this.policySet != null) {
                    decisionResult = this.PolicySetEvaluate(this.policySet);
                }
                else if (this.policy != null) {
                    decisionResult = this.PolicyEvaluate(this.policy);
                }
                else {
                    throw Diagnostic.DiagnosticTools.ExceptionUtil.ThrowHelperError(new InvalidOperationException("Policy missing"));
                }

                result = this.MakeResult(decisionResult, new XacmlContextStatus(XacmlContextStatusCode.Success));
            }
            catch (XacmlException ex) {
                Diagnostic.DiagnosticTools.ExceptionUtil.ThrowHelperError(ex);
                result = this.MakeResult(XacmlDecisionResult.Indeterminate, new XacmlContextStatus(new XacmlContextStatusCode(ex.StatusCode)) { StatusMessage = ex.Message });
            }

            return new XacmlContextResult[] { result };
        }
        /// <summary>
        /// Writes the context request.
        /// </summary>
        /// <param name="writer">The writer.</param>
        /// <param name="data">The data.</param>
        public override void WriteContextRequest(XmlWriter writer, XacmlContextRequest data) {
            writer.WriteStartElement(XacmlConstants.Prefixes.Context, XacmlConstants.ElementNames.Request, this.version.NamespaceContext);

            // Subject
            foreach (var subject in data.Subjects) {
                this.WriteContextSubject(writer, subject);
            }

            foreach (var resource in data.Resources) {
                this.WriteContextResource(writer, resource);
            }

            this.WriteContextAction(writer, data.Action);

            this.WriteContextEnvironment(writer, data.Environment);

            writer.WriteEndElement();
        }