protected virtual void AddToXlinq(string attributeValue) { Debug.Assert(XObject == null, "Object already serialized"); // sometimes we want to lazy create the attribute when it's first set if (!string.IsNullOrEmpty(EFTypeName)) { Debug.Assert( Parent != null && Parent.XContainer != null, "Can't serialize this if the Parent or it's XContainer is missing"); XAttribute attribute = null; if (Namespace != null) { attribute = new XAttribute(Namespace + EFTypeName, attributeValue); } else { attribute = new XAttribute(EFTypeName, attributeValue); } SetXObject(attribute); ModelItemAnnotation.SetModelItem(attribute, this); // always append attributes Parent.XContainer.Add(attribute); } }
/// <summary> /// NOTE: this is called from the EFObject c'tor so only that class is fully instantiated! /// </summary> /// <param name="parent"></param> /// <returns></returns> protected override void AddToXlinq() { State = EFElementState.ParseAttempted; Debug.Assert(XObject == null, "Object already serialized"); Debug.Assert(!string.IsNullOrEmpty(EFTypeName), "Can't create XElement without the name"); Debug.Assert(Parent != null && Parent.XContainer != null, "Can't serialize this if the Parent or it's XContainer is missing"); var ns = Artifact.ModelManager.GetRootNamespace(Parent); var element = new XElement(ns == null ? EFTypeName : ns + EFTypeName); SetXObject(element); ModelItemAnnotation.SetModelItem(element, this); AddXElementToParent(element); State = EFElementState.Parsed; }
protected EFObject(EFContainer parent, XObject xobject) { // note that it is OK if parent is null here; derived classes should assert // in their own constructor if they want to ensure that parent is not null _parent = parent; if (xobject == null) { // Incase parent is null, elements will be created without any namespace // the implementation of AddToXLinq will add the model item annotation. AddToXlinq(); } else { _xobject = xobject; ModelItemAnnotation.SetModelItem(_xobject, this); } Debug.Assert(_xobject == null || ModelItemAnnotation.GetModelItem(_xobject) != null, "xobject didn't have a model annotation!"); SetState(IS_CONSTRUCTION_COMPLETED); }
private void ProcessExternalAddOrUpdateChange( EfiChangeGroup changeGroup, List <EFContainer> containersToRenormalize, ExternalXMLModelChange undoModelChange) { var changedEFObject = undoModelChange.ChangedEFObject; var parentEFObject = undoModelChange.Parent; if (parentEFObject != null) { var parentEFContainer = parentEFObject as EFContainer; Debug.Assert(parentEFContainer != null, "parentEFObject was not an EFContainer!"); // if this is an "add" for an element, then the parent will parse the new child. // if this is an "add" for an attribute, then we have to directly set the XObject // if this is an "update" then we will just record it in our model if (parentEFContainer != null && undoModelChange.Action == XObjectChange.Add) { var newXElement = undoModelChange.XNode as XElement; if (newXElement != null && parentEFContainer.ReparseSingleElement(new List <XName>(), newXElement)) { var newEFObject = ModelItemAnnotation.GetModelItem(undoModelChange.XNode); Debug.Assert(newEFObject != null, "Couldn't find the ModelItemAnnotation for the newly created XLinq node"); if (newEFObject != null) { var newEFElement = newEFObject as EFElement; Debug.Assert(newEFElement != null, "If the XObject was an XElement, then we should have an EFElement as well"); if (newEFElement != null) { XmlModelHelper.NormalizeAndResolve(newEFElement); } // we need to rediscover the XLinq annotation to pick up the new EFObject that just got created changedEFObject = ModelItemAnnotation.GetModelItem(undoModelChange.XNode); } } else if (undoModelChange.XNode is XAttribute) { var staleItemBinding = changedEFObject as ItemBinding; var staleDefaultableValue = changedEFObject as DefaultableValue; // item bindings might have gotten added after their parents got added in which case the MIA will be stale. // we have to step through the new parent, set the xobject manually, rebind, and reset the annotation on the xlinq node. if (staleItemBinding != null) { foreach (var child in parentEFContainer.Children) { var updatedItemBinding = child as ItemBinding; if (updatedItemBinding != null && updatedItemBinding.EFTypeName == staleItemBinding.EFTypeName) { updatedItemBinding.SetXObject(undoModelChange.XNode); updatedItemBinding.Rebind(); changedEFObject = updatedItemBinding; } } } // for defaultable values that got added after parents were added, we have to discover the actual EFObject in the Escher tree, // set the xobject, and reset the annotation on the existing xlinq node else if (staleDefaultableValue != null) { foreach (var child in parentEFContainer.Children) { var updatedDefaultableValue = child as DefaultableValue; if (updatedDefaultableValue != null && updatedDefaultableValue.EFTypeName == staleDefaultableValue.EFTypeName) { updatedDefaultableValue.SetXObject(undoModelChange.XNode); changedEFObject = updatedDefaultableValue; } } } ModelItemAnnotation.SetModelItem(undoModelChange.XNode, changedEFObject); } } else if (undoModelChange.Action == XObjectChange.Value) { Debug.Assert(undoModelChange.XNode is XAttribute, "The only 'value' change Escher supports is to XAttributes"); if (undoModelChange.XNode is XAttribute) { var existingItemBinding = changedEFObject as ItemBinding; if (existingItemBinding != null) { existingItemBinding.Rebind(); } // we have to normalize and resolve the parents of DefaultableValues // because this change could affect the parent's RefName, affecting SingleItemBindings var defaultableValue = changedEFObject as DefaultableValue; if (defaultableValue != null) { XmlModelHelper.NormalizeAndResolve(parentEFContainer); #if DEBUG var xattr = undoModelChange.XNode as XAttribute; var defaultableValueValue = defaultableValue.ObjectValue as string; if (defaultableValueValue != null) { // verify that the defaultableValue's value & the XAttribute's value are the same Debug.Assert(xattr.Value == defaultableValueValue); } #endif } } } else { Debug.Assert(false, "Encountered a non-valid type of change to the XML: " + undoModelChange.Action.ToString()); throw new ChangeProcessingFailedException(); } // if an object's state is unresolved, then queue it for re-normalization. This occurs // for example, if itembinding changes occur before the 'add' changes for their targeted objects var itemBinding = changedEFObject as ItemBinding; if ((itemBinding != null && false == itemBinding.Resolved) || parentEFContainer.State != EFElementState.Resolved) { containersToRenormalize.Add(parentEFContainer); } CheckObjectToRenormalize(changedEFObject, ref containersToRenormalize); // now tell the views that a new item has been created; this will happen for both an 'add' and a 'change' string oldValue = null; string newValue = null; string property = null; GetOldAndNewValues(undoModelChange.XmlChange, out property, out oldValue, out newValue); changeGroup.RecordModelChange(GetChangeType(undoModelChange.XmlChange), changedEFObject, property, oldValue, newValue); } else { // we got a change event on a root-node in the document. // This will cause an NRE when looking for the model-item annotation, // so throw this exception to cause the caller to reload the doc throw new ChangeProcessingFailedException(); } }
/// <summary> /// Move Property's XElement before the specified position. /// If position parameter is null, the property XElement will be moved to the last position. /// </summary> internal void MoveTo(InsertPropertyPosition position) { Debug.Assert( PreviousSiblingInPropertyXElementOrder != null || NextSiblingInPropertyXElementOrder != null, "Why do we need to move the property if it is the only property?"); Debug.Assert(position != null, "InsertPropertyPosition parameter is null."); if (position != null) { // Check if the InsertPropertyPosition's InsertAt is not null. Debug.Assert(position.InsertAtProperty != null, "Why InsertPropertyPosition's InsertAt is null?"); if (position.InsertAtProperty != null) { // Instead of re-parenting the property's XElement, we are going to clone the XElement, insert the clone and delete the old XElement. // This is a workaround for an XML editor bug where re-parenting an element causes asserts. // First create the new XElement. var tempDoc = XDocument.Parse(XElement.ToString(SaveOptions.None), LoadOptions.None); var newPropertyXElement = tempDoc.Root; newPropertyXElement.Remove(); // Remove known namespaces from the element since the namespaces are already set in the parent node. // This is workaround because XDocument automatically appends the default namespace in the property XElement. foreach (var a in newPropertyXElement.Attributes()) { if (a.IsNamespaceDeclaration && (a.Value == SchemaManager.GetCSDLNamespaceName(Artifact.SchemaVersion) || a.Value == SchemaManager.GetSSDLNamespaceName(Artifact.SchemaVersion) || a.Value == SchemaManager.GetAnnotationNamespaceName())) { a.Remove(); } } var toBeDeleteElement = XElement; // format the XML we just parsed so that the XElement will have the same indenting. Utils.FormatXML(newPropertyXElement, GetIndentLevel()); // Call method that will insert the XElement to the specified location. InsertPosition = position; AddXElementToParent(newPropertyXElement); // Re-establish the links between EFElement and XElement. SetXObject(newPropertyXElement); Debug.Assert( XElement == newPropertyXElement, "Unexpected XElement value. Expected:" + newPropertyXElement + " , Actual:" + XElement); ModelItemAnnotation.SetModelItem(newPropertyXElement, this); Debug.Assert( ModelItemAnnotation.GetModelItem(newPropertyXElement) == this, "The new XElement should contain annotation to the model property."); // Delete both old XElement and the preceding whitespace. // Preceding whitespace is preferred over trailing whitespace because we don't want to remove the last property's trailing white-space since // it has different indent level than the rest (see EFElement's EnsureFirstNodeWhitespaceSeparation method). var preceedingNewLine = toBeDeleteElement.PreviousNode as XText; while (preceedingNewLine != null && String.IsNullOrWhiteSpace(preceedingNewLine.Value)) { var toBeDeletedWhiteSpace = preceedingNewLine; preceedingNewLine = preceedingNewLine.PreviousNode as XText; toBeDeletedWhiteSpace.Remove(); } toBeDeleteElement.Remove(); #if DEBUG // Assert if the property is not moved to the correct location. if (position.InsertBefore) { Debug.Assert( position.InsertAtProperty == NextSiblingInPropertyXElementOrder, "Expected next sibling property: " + position.InsertAtProperty.DisplayName + " , Actual next sibling:" + NextSiblingInPropertyXElementOrder.DisplayName); } else { Debug.Assert( position.InsertAtProperty == PreviousSiblingInPropertyXElementOrder, "Expected previous sibling property: " + position.InsertAtProperty.DisplayName + " , Actual previous sibling:" + PreviousSiblingInPropertyXElementOrder.DisplayName); } #endif } } }