Example #1
0
        /// <summary>
        /// Gets the <see cref="EvaluationContext"/> for a specific item.
        /// </summary>
        /// <param name="element"></param>
        /// <returns></returns>
        internal EvaluationContext GetItemContext(XElement element)
        {
            var item = element.Annotation <RepeatItemState>();

            if (item == null)
            {
                throw new InvalidOperationException();
            }

            if (Binding == null ||
                Binding.ModelItems.Length == 0)
            {
                return(null);
            }

            var xml = Binding.ModelItem.Instance.State.Document.ResolveObjectId(item.ModelObjectId);

            if (xml == null)
            {
                var d = serializer.Serialize(Binding.ModelItem.Instance.State.Document);
                var s = d.ToString();
                var l = Binding.ModelItem.Instance.State.Document.ToString();
                throw new InvalidOperationException();
            }

            return(new EvaluationContext(
                       ModelItem.Get(xml),
                       item.Index,
                       item.Size));
        }
Example #2
0
 ModelItem[] GetModelItems()
 {
     if (Result is XPathNodeIterator)
     {
         return(((XPathNodeIterator)Result)
                .Cast <XPathNavigator>()
                .Select(i => (XObject)i.UnderlyingObject)
                .Select(i => ModelItem.Get(i))
                .ToArray());
     }
     else
     {
         return(EmptyModelItemSequence);
     }
 }
Example #3
0
            public override XObject Visit(XObject obj)
            {
                var modelItem = ModelItem.Get(obj);

                if (modelItem == null || modelItem.Relevant || !excludeRelevant)
                {
                    // visit node
                    var o = base.Visit(obj);

                    // attach validity to annotation for usage later
                    if (modelItem != null)
                    {
                        o.AddAnnotation(new ValidityAnnotation(modelItem.Valid));
                    }

                    // return new object
                    return(o);
                }
                else
                {
                    return(null);
                }
            }
Example #4
0
        public void HandleEvent(Event ev)
        {
            var context = GetContext();

            if (context == null)
            {
                return;
            }

            var sequenceBindingNodeSequence = GetSequenceBindingNodeSequence(context);

            if (sequenceBindingNodeSequence.Length == 0)
            {
                return;
            }

            var insertNode = sequenceBindingNodeSequence[0];
            var insertItem = ModelItem.Get(insertNode);

            if (insertItem.ReadOnly)
            {
                return;
            }

            if (insertNode is XElement)
            {
                if (((XElement)insertNode).HasElements)
                {
                    throw new DOMTargetEventException(Element, Events.BindingException);
                }
            }

            insertItem.Value = Element.Value;
            insertItem.Model.State.Recalculate = true;
            insertItem.Model.State.Revalidate  = true;
            insertItem.Model.State.Refresh     = true;
        }
Example #5
0
        public void Invoke()
        {
            var insertContext = GetInsertContext();

            if (insertContext == null)
            {
                return;
            }

            var sequenceBindingNodeSequence = GetSequenceBindingNodeSequence(insertContext);

            // 1. The context attribute is not given and the Sequence Binding node-sequence is the empty sequence.
            if (commonProperties.Context == null &&
                sequenceBindingNodeSequence.Length == 0)
            {
                return;
            }

            // 2. The context attribute is given, the insert context does not evaluate to an element node and the
            //    Sequence Binding node-sequence is the empty sequence.
            if (commonProperties.Context != null &&
                sequenceBindingNodeSequence.Length == 0 &&
                insertContext.ModelItem.Xml.NodeType != XmlNodeType.Element)
            {
                return;
            }

            // The insert action is terminated with no effect if the origin node-sequence is the empty sequence.
            var originNodeSequence = GetOriginNodeSequence(insertContext, sequenceBindingNodeSequence);

            if (originNodeSequence.Length == 0)
            {
                return;
            }

            var insertLocationNode = GetInsertLocationNode(insertContext, sequenceBindingNodeSequence);

            // The insert action is terminated with no effect if the insertion will create nodes whose parent is
            // readonly. This occurs if the insert location node is readonly and the Sequence Binding sequence is not
            // specified or empty, or otherwise if the parent of the insert location node is readonly.
            if (ModelItem.Get(insertLocationNode).ReadOnly)
            {
                return;
            }

            // The target location of each of the cloned nodes is determined.
            var targets = originNodeSequence
                          .Select(i => GetTargetLocation(insertContext, sequenceBindingNodeSequence, insertLocationNode, i.Clone()))
                          .ToArray();

            // The cloned node or nodes are inserted in the order they were cloned into their target locations
            // depending on their node type.
            var inserts = targets
                          .Select(i => InsertTarget(i))
                          .ToArray();

            // 9. If the list of inserted-nodes is empty, then the insert action is terminated with no effect.
            if (inserts.Length == 0)
            {
                return;
            }

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

            // 11. The insert action is successfully completed by dispatching the xforms-insert event with appropriate
            // context information.
            insertContext.Instance.Element.Interface <EventTarget>()
            .Dispatch(Events.Insert, inserts);
        }
Example #6
0
        /// <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]);
            }
        }
Example #7
0
        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);
        }
    }
Example #8
0
        /// <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);
        }