コード例 #1
0
ファイル: UIBinding.cs プロジェクト: nxkit/nxkit
        /// <summary>
        /// Initializes a new instance.
        /// </summary>
        /// <param name="element"></param>
        /// <param name="invoker"></param>
        /// <param name="binding"></param>
        internal UIBinding(XElement element, IInvoker invoker, Binding binding)
            : this(element, invoker,binding.ModelItem)
        {
            Contract.Requires<ArgumentNullException>(element != null);
            Contract.Requires<ArgumentNullException>(invoker != null);
            Contract.Requires<ArgumentNullException>(binding != null);

            this.binding = binding;
        }
コード例 #2
0
ファイル: Insert.cs プロジェクト: nxkit/nxkit
        /// <summary>
        /// Gets the 'origin node-sequence'.
        /// </summary>
        /// <param name="insertContext"></param>
        /// <param name="sequenceBindingNodeSequence"></param>
        /// <returns></returns>
        XObject[] GetOriginNodeSequence(EvaluationContext insertContext, XObject[] sequenceBindingNodeSequence)
        {
            Contract.Requires<ArgumentNullException>(insertContext != null);
            Contract.Requires<ArgumentNullException>(sequenceBindingNodeSequence != null);
            Contract.Ensures(Contract.Result<XObject[]>() != null);

            XObject[] result = null;

            // If the origin attribute is not given and the Sequence Binding sequence is empty, then the origin
            // node-sequence is the empty sequence.
            if (insertProperties.Origin == null &&
                sequenceBindingNodeSequence.Length == 0)
                result = new XObject[0];

            // Otherwise, if the origin attribute is not given, then the origin node-sequence consists of the last
            // node of the Sequence Binding node-sequence.
            else if (insertProperties.Origin == null)
                result = new[] { sequenceBindingNodeSequence[sequenceBindingNodeSequence.Length - 1] };

            // If the origin attribute is given, the origin node-sequence is the result of the evaluation of the origin
            // attribute in the insert context.
            else if (insertProperties.Origin != null)
                result = new Binding(Element, insertContext, insertProperties.Origin).ModelItems
                    .Select(i => i.Xml)
                    .ToArray();

            else
                result = new XObject[0];

            // preempt empty result set
            if (result.Length == 0)
                return result;

            // Namespace nodes and root nodes (parents of document elements) are removed from the origin node-sequence.
            return result
                .Where(i => i.NodeType != XmlNodeType.Attribute || !((XAttribute)i).IsNamespaceDeclaration)
                .Where(i => i.Parent != null || i == i.Document.Root)
                .ToArray();
        }
コード例 #3
0
ファイル: Insert.cs プロジェクト: nxkit/nxkit
        /// <summary>
        /// Gets the 'insert location node'.
        /// </summary>
        /// <param name="insertContext"></param>
        /// <param name="sequenceBindingNodeSequence"></param>
        /// <returns></returns>
        XObject GetInsertLocationNode(EvaluationContext insertContext, XObject[] sequenceBindingNodeSequence)
        {
            Contract.Requires<ArgumentNullException>(insertContext != null);
            Contract.Requires<ArgumentNullException>(sequenceBindingNodeSequence != null);
            Contract.Ensures(Contract.Result<XObject>() != null);

            // If the Sequence Binding node-sequence is not specified or empty, the insert location node is the insert
            // context node.
            if (sequenceBindingNodeSequence.Length == 0)
                return insertContext.ModelItem.Xml;

            // Otherwise, if the at attribute is not given, then the insert location node is the last node of the
            // Sequence Binding sequence.
            else if (insertProperties.At == null)
                return sequenceBindingNodeSequence[sequenceBindingNodeSequence.Length - 1];

            // Otherwise, an insert location node is determined from the at attribute as follows:
            else
            {
                // 1. The evaluation context node is the first node in document order from the Sequence Binding
                // node-sequence, the context size is the size of the Sequence Binding node-sequence, and the context
                // position is 1.
                var at = new Binding(
                    Element,
                    new EvaluationContext(ModelItem.Get(sequenceBindingNodeSequence[0]), 1, sequenceBindingNodeSequence.Length),
                    insertProperties.At).Value;

                // 2. The return value is processed according to the rules of the XPath function round(). For example,
                // the literal 1.5 becomes 2, and the literal 'string' becomes NaN.
                double location;
                if (double.TryParse(at, out location))
                    location = Math.Round(location, 0, MidpointRounding.AwayFromZero);
                else
                    location = double.NaN;

                // 3. If the result is in the range 1 to the Sequence Binding node-sequence size, then the insert
                // location is equal to the result. If the result is non-positive, then the insert location is 1.
                // Otherwise, the result is NaN or exceeds the Sequence Binding sequence size, so the insert location
                // is the Sequence Binding sequence size.
                if (location <= 0)
                    location = 1;
                else if (double.IsNaN(location) || location > sequenceBindingNodeSequence.Length)
                    location = sequenceBindingNodeSequence.Length;

                // 4. The insert location node is the node in the Sequence Binding sequence at the position given by
                // the insert location.
                return sequenceBindingNodeSequence[(int)location - 1];
            }
        }
コード例 #4
0
ファイル: Insert.cs プロジェクト: nxkit/nxkit
        /// <summary>
        /// The insert context is determined. If the context attribute is not given, the insert context is the 
        /// in-scope evaluation context. Otherwise, the expression provided by the context attribute is evaluated
        /// using the in-scope evaluation context, and the first-item rule is applied to obtain the insert context.
        /// The insert action is terminated with no effect if the insert context is the empty sequence.
        /// </summary>
        /// <returns></returns>
        EvaluationContext GetInsertContext()
        {
            var insertContext = context.Value.GetInScopeEvaluationContext();
            if (commonProperties.Context != null)
            {
                var item = new Binding(Element, insertContext, commonProperties.Context).ModelItems.FirstOrDefault();
                if (item == null)
                    return null;

                insertContext = new EvaluationContext(item.Model, item.Instance, item, 1, 1);
            }

            return insertContext;
        }
コード例 #5
0
ファイル: Bind.cs プロジェクト: nxkit/nxkit
        /// <summary>
        /// Applies the bindings to the model item.
        /// </summary>
        public void Apply()
        {
            // TODO this is a poor implementation of nested bind elements
            var modelItems = GetBoundNodes();

            for (int i = 1; i <= modelItems.Length; i++)
            {
                var modelItem = modelItems[i - 1];
                if (modelItem == null)
                    continue;

                var state = modelItem.State;
                if (state == null)
                    continue;

                if (Type != null)
                    if (state.Type != Type)
                        state.Type = Type;

                var ec = new EvaluationContext(modelItem.Model, modelItem.Instance, modelItem, i, modelItems.Length);

                if (!string.IsNullOrWhiteSpace(attributes.ReadOnly))
                {
                    var readOnly = ParseBooleanValue(new Binding(Element, ec, attributes.ReadOnly));
                    if (readOnly != null)
                        state.ReadOnly = readOnly;
                }

                if (!string.IsNullOrWhiteSpace(attributes.Required))
                {
                    var required = ParseBooleanValue(new Binding(Element, ec, attributes.Required));
                    if (required != null)
                        state.Required = required;
                }

                if (!string.IsNullOrWhiteSpace(attributes.Relevant))
                {
                    var relevant = ParseBooleanValue(new Binding(Element, ec, attributes.Relevant));
                    if (relevant != null &&
                        relevant != state.Relevant)
                    {
                        state.Relevant = relevant;
                        Debug.WriteLine("ModelItem relevancy changed: {0}", state.Relevant);
                    }
                }

                if (!string.IsNullOrWhiteSpace(attributes.Constraint))
                {
                    var constraint = ParseBooleanValue(new Binding(Element, ec, attributes.Constraint));
                    if (constraint != null)
                        state.Constraint = constraint;
                }

                if (!string.IsNullOrWhiteSpace(attributes.Calculate))
                {
                    var calculate = new Binding(Element, ec, attributes.Calculate).Value;
                    if (calculate != null)
                    {
                        if (state.ReadOnly == false)
                            state.ReadOnly = true;

                        modelItem.Value = calculate;
                    }
                }
            }
        }
コード例 #6
0
ファイル: Bind.cs プロジェクト: nxkit/nxkit
        /// <summary>
        /// Extracts a boolean value from the given binding.
        /// </summary>
        /// <param name="binding"></param>
        /// <returns></returns>
        bool? ParseBooleanValue(Binding binding)
        {
            Contract.Requires<ArgumentNullException>(binding != null);

            if (binding.Result is bool)
                return (bool?)binding.Result;
            else if (binding.Result is bool?)
                return (bool?)binding.Result;
            else if (binding.Result is string && !string.IsNullOrWhiteSpace((string)binding.Result))
                return bool.Parse((string)binding.Result);
            else
                throw new DOMTargetEventException(Element, Events.BindingException,
                    string.Format("{0}", binding.Result));
        }
コード例 #7
0
        /// <summary>
        /// Returns the <see cref="EvaluationContext"/> provided by any specified 'context' attribute.
        /// </summary>
        /// <returns></returns>
        internal EvaluationContext GetSpecifiedContextEvaluationContext()
        {
            if (Attributes.Context != null)
            {
                var context = GetContextForSpecifiedContext();
                if (context == null)
                    throw new DOMTargetEventException(Element, Events.BindingException,
                        "Null Context for specified Context.");

                var binding = new Binding(Element, context, Attributes.Context);
                if (binding.ModelItem == null)
                    return null;

                return new EvaluationContext(binding.ModelItem.Model, binding.ModelItem.Instance, binding.ModelItem, 1, 1);
            }

            return null;
        }
コード例 #8
0
ファイル: Delete.cs プロジェクト: nxkit/nxkit
        public void Invoke()
        {
            var deleteContext = GetDeleteContext();
            if (deleteContext == null)
                return;

            // The Sequence Binding node-sequence is determined.
            var sequenceBindingNodeSequence = GetSequenceBindingNodeSequence(deleteContext);

            // The delete action is terminated with no effect if the Sequence Binding is expressed and the Sequence
            // Binding node-sequence is the empty sequence.
            if (sequenceBindingNodeSequence != null &&
                sequenceBindingNodeSequence.Length == 0)
                return;

            // The behavior of the delete action is undefined if the Sequence Binding node-sequence contains nodes
            // from more than one instance.
            if (sequenceBindingNodeSequence != null &&
                sequenceBindingNodeSequence.Select(i => ModelItem.Get(i).Instance).Distinct().Count() > 1)
                return;

            // Otherwise, the Sequence Binding is not expressed, so the Sequence Binding node-sequence is set equal to
            // the delete context node with a position and size of 1.
            if (sequenceBindingNodeSequence == null)
                sequenceBindingNodeSequence = new XObject[] { deleteContext.ModelItem.Xml };

            // The delete location is determined. If the at attribute is not specified, there is no delete location.
            // Otherwise, the delete location is determined by evaluating the expression specified by the at attribute
            // as follows:
            var deleteLocation = 0d;
            if (deleteProperties.At != null)
            {
                // 1. The evaluation context node is the first node in document order from the Sequence Binding
                // node-sequence, the context size is the size of the Sequence Binding node-sequence, and the context
                // position is 1.
                var at = new Binding(
                    Element,
                    new EvaluationContext(ModelItem.Get(sequenceBindingNodeSequence[0]), 1, sequenceBindingNodeSequence.Length),
                    deleteProperties.At).Value;

                // 2. The return value is processed according to the rules of the XPath function round(). For example,
                // the literal 1.5 becomes 2, and the literal 'string' becomes NaN.
                if (double.TryParse(at, out deleteLocation))
                    deleteLocation = Math.Round(deleteLocation, 0, MidpointRounding.AwayFromZero);
                else
                    deleteLocation = double.NaN;

                // 3. If the result is in the range 1 to the Sequence Binding node-sequence size, then the delete
                // location is equal to the result. If the result is non-positive, then the delete location is 1.
                // Otherwise, if the result is NaN or exceeds the Sequence Binding node-sequence size, the delete
                // location is the Sequence Binding node-sequence size.
                if (deleteLocation <= 0)
                    deleteLocation = 1;
                else if (deleteLocation == double.NaN || deleteLocation > sequenceBindingNodeSequence.Length)
                    deleteLocation = sequenceBindingNodeSequence.Length;
            }

            // If there is no delete location, each node in the Sequence Binding node-sequence is deleted, except if
            // the node is a readonly node, a namespace node, a root node, or the root document element of an instance,
            // then that particular node is not deleted. Otherwise, if there is a delete location, the node at the
            // delete location in the Sequence Binding node-sequence is deleted, except if the node is the root
            // document element of an instance or has a readonly parent node, then that node is not deleted.
            var deleteCount = 0;
            var deleteNodes = deleteLocation == 0d ? sequenceBindingNodeSequence : new XObject[] { sequenceBindingNodeSequence[(int)deleteLocation - 1] };
            foreach (var deleteNode in deleteNodes)
            {
                if (ModelItem.Get(deleteNode).ReadOnly)
                    continue;

                if (deleteNode.NodeType == XmlNodeType.Attribute)
                    if (((XAttribute)deleteNode).IsNamespaceDeclaration)
                        continue;

                if (deleteNode.Parent == null)
                    continue;

                if (deleteNode is XAttribute)
                    ((XAttribute)deleteNode).Remove();
                else if (deleteNode is XNode)
                    ((XNode)deleteNode).Remove();
                else
                    throw new InvalidOperationException();

                deleteCount++;
            }

            // The delete action is terminated with no effect if no node is deleted.
            if (deleteCount == 0)
                return;

            // 5. The XForms action system's deferred update flags for rebuild, recalculate, revalidate and refresh are
            // set.
            deleteContext.Model.State.Rebuild = true;
            deleteContext.Model.State.Recalculate = true;
            deleteContext.Model.State.Revalidate = true;
            deleteContext.Model.State.Refresh = true;

            // 6. The delete action is successfully completed by dispatching the xforms-delete event with appropriate
            // context information.
            deleteContext.Instance.Element.Interface<EventTarget>()
                .Dispatch(Events.Delete);
        }
コード例 #9
0
ファイル: Delete.cs プロジェクト: nxkit/nxkit
        /// <summary>
        /// The delete context is determined. It is set to the in-scope evaluation context, possibly overridden by the
        /// context attribute if that attribute is present. The delete action is terminated with no effect if the
        /// delete context is the empty sequence.
        /// </summary>
        /// <returns></returns>
        EvaluationContext GetDeleteContext()
        {
            var deleteContext = resolver.Value.GetInScopeEvaluationContext();
            if (commonProperties.Context != null)
            {
                var item = new Binding(Element, deleteContext, commonProperties.Context).ModelItems.First();
                if (item == null)
                    return null;

                deleteContext = new EvaluationContext(item.Model, item.Instance, item, 1, 1);
            }

            return deleteContext;
        }
コード例 #10
0
ファイル: Submission.cs プロジェクト: nxkit/nxkit
        /// <summary>
        /// Finishes a submission with instance replacement.
        /// </summary>
        /// <param name="response"></param>
        /// <param name="modelItem">Instance data node that was submitted.</param>
        void FinishWithReplaceInstance(ModelResponse response, ModelItem modelItem)
        {
            Contract.Requires<ArgumentNullException>(response != null);
            Contract.Requires<ArgumentException>(response.Body != null);
            Contract.Requires<ArgumentNullException>(modelItem != null);

            // When the attribute is absent, then the default is the instance that contains the submission data.
            var instance = modelItem != null ? modelItem.Instance : null;

            // Author-optional attribute specifying the instance to replace when the replace attribute value is
            // "instance". When the attribute is absent, then the default is the instance that contains the submission
            // data. An xforms-binding-exception (The xforms-binding-exception Event) occurs if this attribute does not
            // indicate an instance in the same model as the submission.
            if (properties.Instance != null)
            {
                var instanceElement = Element.ResolveId(properties.Instance);
                if (instanceElement != null)
                    instance = instanceElement.Interface<Instance>();
            }

            if (instance == null ||
                instance.Element.Parent != Element.Parent)
                throw new DOMTargetEventException(Element, Events.BindingException,
                    "Submission cannot specify foreign model instance.");

            var target = instance.State.Document.Root.Annotation<ModelItem>();
            if (target == null)
                throw new InvalidOperationException();

            // Author-optional attribute containing an expression that indicates the target node for data replacement.
            if (properties.TargetRef != null)
            {
                // The evaluation context for this attribute is the in-scope evaluation context for the submission
                // element, except the context node is modified to be the document element of the instance identified
                // by the instance attribute if present.
                var ec = new EvaluationContext(
                    ModelItem.Get(context.Value.Context.Instance.State.Document.Root),
                    1,
                    1);

                // If the submission element has a targetref attribute, the attribute value is interpreted as a binding
                // expression to which the first-item rule is applied to obtain the replacement target node.
                target = new Binding(Element, ec, properties.TargetRef).ModelItem;
            }

            // final check
            if (target == null)
                throw new DOMTargetEventException(Element, Events.BindingException,
                    "Null submission replacement target.");

            // Otherwise, those processing instructions and comments replace any processing instructions and comments
            // that previously appeared outside of the document element of the instance being replaced.
            target.Replace(response.Body);
        }
コード例 #11
0
ファイル: Submission.cs プロジェクト: nxkit/nxkit
        void OnSubmitImpl()
        {
            // The data model is updated based on some of the flags defined for deferred updates. Specifically, if the
            // deferred update rebuild flag is set for the model containing this submission, then the rebuild operation
            // is performed without dispatching an event to invoke the operation. Then, if the deferred update
            // recalculate flag is set for the model containing this submission, then the recalculate operation is
            // performed without dispatching an event to invoke the operation. This sequence of operations affects the
            // deferred update behavior by clearing the deferred update flags associated with the operations performed.
            var model = Element.Ancestors(Constants.XForms_1_0 + "model").First().Interface<Model>();
            if (model.State.Rebuild)
                model.OnRebuild();
            if (model.State.Recalculate)
                model.OnRecalculate();

            // If the binding attributes of submission indicate an empty sequence or an item other than an element or
            // an instance document root node, then submission fails with no-data. Otherwise, the binding attributes of
            // submission indicate a node of instance data.
            var modelItems = new Binding(Element, context.Value.Context, properties.Ref).ModelItems;
            if (modelItems == null ||
                modelItems.Length != 1 ||
                modelItems.Any(i => i.Xml.NodeType != XmlNodeType.Document && i.Xml.NodeType != XmlNodeType.Element))
                throw new DOMTargetEventException(Element, Events.SubmitError, new SubmitErrorContextInfo(
                    SubmitErrorErrorType.NoData
                ));

            // The indicated node and all nodes for which it is an ancestor are selected. If the attribute relevant is
            // true, whether by default or declaration, then any selected node which is not relevant as defined in The
            // relevant Property is deselected (pruned). If all instance nodes are deselected, then submission fails
            // with no-data.
            var node = (XNode)new SubmitTransformer(properties.Relevant)
                .Visit(modelItems[0].Xml);
            if (node == null)
                throw new DOMTargetEventException(Element, Events.SubmitError, new SubmitErrorContextInfo(
                    SubmitErrorErrorType.NoData
                ));

            // If the attribute validate is true, whether by default or declaration, then all selected instance data
            // nodes are checked for validity according to the definition in The xforms-revalidate Event (no
            // notification events are marked for dispatching due to this operation). If any selected instance data
            // node is found to be invalid, submission fails with validation-error.
            if (properties.Validate &&
                new ValidationVisitor().Validate(node) == false)
                throw new DOMTargetEventException(Element, Events.SubmitError, new SubmitErrorContextInfo(
                    SubmitErrorErrorType.ValidationError
                ));

            // The submission method is determined.
            // The submission method may be specified by the method attribute. The submission element can have a child
            // element named method, which overrides the submission method setting obtained from the method attribute
            // if both are specified. If more than one method element is given, the first occurrence in document order
            // must be selected for use. Individually, the method element and the method attribute are not required.
            // However, one of the two is mandatory as there is no default submission method.
            var method = GetMethod();
            if (method == null)
                throw new DOMTargetEventException(Element, Events.SubmitError, "Unknown ModelMethod.");

            // The resource element provides the submission URI, overriding the resource attribute and the action
            // attribute. If a submission has more than one resource child element, the first resource element child
            // must be selected for use. Individually, the resource element, the resource attribute and the action
            // attribute are not required. However, one of the three is mandatory as there is no default submission
            // resource.
            var resource = GetResource();
            if (resource == null)
                throw new DOMTargetEventException(Element, Events.SubmitError, new SubmitErrorContextInfo(
                    SubmitErrorErrorType.ResourceError));

            // If the serialization attribute value is "none", then the submission data serialization is the empty
            // string. Otherwise, the event xforms-submit-serialize is dispatched; if the submission-body property
            // of the event is changed from the initial value of empty string, then the content of the submission-body
            // property string is used as the submission data serialization. Otherwise, the submission data
            // serialization consists of a serialization of the selected instance data according to the rules stated
            // in Serialization.
            if (properties.Serialization.None)
                node = null;
            else
            {
                var evt = Element.Interface<EventTarget>().Dispatch(Events.SubmitSerialize, new SubmitSerializeContextInfo());
                var ctx = evt.Context as SubmitSerializeContextInfo;
                if (ctx != null &&
                    ctx.SubmissionBody != "")
                    // wrap in XText
                    node = new XText(ctx.SubmissionBody);
            }

            // The submission is performed based on the submission headers, submission method, submission resource, and
            // submission data serialization. The exact rules of submission are based on the URI scheme and the
            // submission method, as defined in Submission Options.
            var request = new ModelRequest(resource, (ModelMethod)method);
            request.MediaType = properties.MediaType;
            request.Body = node;
            request.Encoding = properties.Encoding;
            request.Headers.Add(GetHeaders());

            // submit and check for response
            var response = requestService.Submit(request);
            if (response == null)
                throw new DOMTargetEventException(Element, Events.SubmitError, new SubmitErrorContextInfo(
                    SubmitErrorErrorType.ResourceError));

            // For error responses, processing depends on the value of the replace attribute on element submission:
            // all: either the document is replaced with an implementation-specific indication of an error or submission fails with resource-error.
            // any other value: nothing in the document is replaced, and submission fails with resource-error.
            if (response.Status == ModelResponseStatus.Error)
                throw new DOMTargetEventException(Element, Events.SubmitError, new SubmitErrorContextInfo(
                    SubmitErrorErrorType.ResourceError));

            // For success responses, if the response does not include a body, submission succeeds.
            if (response.Body == null)
            {
                Element.DispatchEvent(Events.SubmitDone);
                return;
            }

            // handle result based on 'replace' property
            switch (properties.Replace)
            {
                // none: submission succeeds.
                case SubmissionReplace.None:
                    Element.DispatchEvent(Events.SubmitDone);
                    break;

                // all: the event xforms-submit-done may be dispatched with appropriate context information, and submit
                // processing concludes with the entire containing document being replaced with the returned body.
                case SubmissionReplace.All:
                    throw new NotImplementedException();

                // instance: If the body is not of type accepted by the processor, as specified in Creating instance data
                // from external resources, nothing in the document is replaced and submission fails with resource-error.
                // Otherwise the body is parsed to give an XPath Data Model according to Creating instance data from
                // external resources. If the parse fails, then submission fails with parse-error. If the parse succeeds,
                // then instance data replacement is performed according to Replacing Data with the Submission Response.
                // If this operation fails, submission fails with target-error. Otherwise, submission succeeds.
                case SubmissionReplace.Instance:
                    FinishWithReplaceInstance(response, modelItems[0]);
                    break;

                // text: If the body is neither an XML media type (i.e. with a content type not matching any of the
                // specifiers in [RFC 3023]) nor a text type (i.e. with a content type not matching text/*), nothing in the
                // document is replaced and submission fails with resource-error. Otherwise the content replacement is
                // performed according to Replacing Data with the Submission Response. If this operation fails, then the
                // submission fails with target-error. Otherwise, submission succeeds.
                case SubmissionReplace.Text:
                    throw new NotImplementedException();
            }
        }