/// <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)); }
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); } }
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); } }
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; }
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); }
/// <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]); } }
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); } }
/// <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); }