// virtual to allow mocking internal virtual EFObject FindEFObjectForLineAndColumn(int lineNumber, int columnNumber) { var o = FindXObjectForLineAndColumn(lineNumber, columnNumber); Debug.Assert(o != null || (lineNumber == 0 && columnNumber == 0), "Unexpected null value for XObject"); if (o != null) { var mia = ModelItemAnnotation.GetModelItem(o); while (mia == null && o != null) { // in some cases, we have an XObject without a corresponding EFObject. // here we find the first enclosing EFObject. o = o.Parent; if (o != null) { mia = ModelItemAnnotation.GetModelItem(o); } } if (mia != null) { return(mia); } } return(this); }
public ExternalXMLModelChange(IXmlChange xmlChange, ExpectEFObjectParentForXObject assertDelegate) { _xmlChange = xmlChange; _changedEFObject = ModelItemAnnotation.GetModelItem(_xmlChange.Node); _expectEFObjectParentForXObject = assertDelegate; ResolveParentEFObject(); }
private void ResolveParentEFObject() { var parent = _xmlChange.Node.Parent; if (parent != null) { // get the parent EFObject for the changed object _parentOfChangedEFObject = ModelItemAnnotation.GetModelItem(parent); } // in case of a 'delete' change, then the changed XObject's parent will be null so we should try to // resolve the parent through our model tree. if (_parentOfChangedEFObject == null) { // try another way to get the parent if (ChangedEFObject != null) { _parentOfChangedEFObject = ChangedEFObject.Parent; } } #if DEBUG if (_parentOfChangedEFObject == null) { if (_expectEFObjectParentForXObject != null && _expectEFObjectParentForXObject(_xmlChange.Node)) { Debug.Fail("Why weren't we able to find the parent of the changed EFObject?"); } } #endif }
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); } }
private bool CheckForMissingAnnotations(EFObject efObject) { if (_checkAnnotations) { var miaEFobject = ModelItemAnnotation.GetModelItem(efObject.XObject); var isArtifact = efObject is EFArtifact; if (miaEFobject == null && !isArtifact) { if (!_noMIAList.Contains(efObject.XObject)) { _noMIAList.Add(efObject.XObject); } return(true); } // Check for incorrect/stale annotations if (miaEFobject != efObject && !isArtifact && !IsValidGhostNode(efObject)) { if (!_incorrectMIAList.ContainsKey(efObject.XObject)) { _incorrectMIAList.Add(efObject.XObject, miaEFobject); } return(true); } } return(false); }
internal static void SetXAttributeValue(XAttribute attribute, string newValue) { var attr = ModelItemAnnotation.GetModelItem(attribute) as EFAttribute; Debug.Assert(attr != null); if (attr != null) { attr.SetXAttributeValue(newValue); } }
internal static string GetXAttributeValue(XAttribute attribute) { var attr = ModelItemAnnotation.GetModelItem(attribute) as EFAttribute; Debug.Assert(attr != null); if (attr != null) { return(attr.GetXAttributeValue()); } return(null); }
internal override bool ParseSingleElement(ICollection <XName> unprocessedElements, XElement elem) { // Conceptual EntityModel needs to create Conceptual EntityContainer objects if (elem.Name.LocalName == BaseEntityContainer.ElementName) { if (_entityContainers.Count > 0) { // multiple EntityContainers detected, report an error var msg = String.Format(CultureInfo.CurrentCulture, Resources.TOO_MANY_ENTITY_CONTAINER_ELEMENTS, Namespace.Value); var error = new ErrorInfo( ErrorInfo.Severity.ERROR, msg, this, ErrorCodes.TOO_MANY_ENTITY_CONTAINER_ELEMENTS, ErrorClass.ParseError); Artifact.AddParseErrorForObject(this, error); } var ec = new ConceptualEntityContainer(this, elem); _entityContainers.Add(ec); ec.Parse(unprocessedElements); } else if (elem.Name.LocalName == ComplexType.ElementName) { var complexType = new ComplexType(this, elem); _complexTypes.Add(complexType); complexType.Parse(unprocessedElements); } else if (elem.Name.LocalName == UsingElement.ElementName) { var use = new UsingElement(this, elem); _usings.Add(use); use.Parse(unprocessedElements); } else if (elem.Name.LocalName == EnumType.ElementName) { // Check if enumType that represents the XElement <see DoParse method> var enumType = ModelItemAnnotation.GetModelItem(elem) as EnumType; if (enumType == null || enumType.IsDisposed) { enumType = new EnumType(this, elem); _enumTypes.Add(enumType); enumType.Parse(unprocessedElements); } } else { return(base.ParseSingleElement(unprocessedElements, elem)); } return(true); }
private static EfiChangeGroup ProcessDesignerChange(XmlTransactionEventArgs xmlTransactionEventArgs, EfiTransaction efiTransaction) { // if the transaction has one of our transactions in the UserState, then // this will not be null var changeGroup = efiTransaction.ChangeGroup; // Here, we need to call RecordModelChange for each change that occurred in the XmlTransaction // Since the XmlTransaction started in our model, the model already reflects these changes. foreach (var xmlChange in xmlTransactionEventArgs.Transaction.Changes()) { Debug.Assert(xmlChange.Node != null); if (xmlChange.Node.NodeType == XmlNodeType.Element || xmlChange.Node.NodeType == XmlNodeType.Attribute || xmlChange.Node.NodeType == XmlNodeType.Document) { var efObject = ModelItemAnnotation.GetModelItem(xmlChange.Node); if (efObject != null) { if (xmlChange.Node.NodeType == XmlNodeType.Element && xmlChange.Action == XObjectChange.Value) { var nodeValueChange = xmlChange as IXmlNodeValueChange; Debug.Assert( nodeValueChange != null && string.IsNullOrEmpty(nodeValueChange.OldValue) && string.IsNullOrEmpty(nodeValueChange.NewValue)); // at times (like when the last child is removed), the XLinq tree will // collapse whitespace nodes and we will get 'set value' on the // preceeding element, we can ignore these continue; } string oldValue = null; string newValue = null; string property = null; GetOldAndNewValues(xmlChange, out property, out oldValue, out newValue); changeGroup.RecordModelChange(GetChangeType(xmlChange), efObject, property, oldValue, newValue); } } } return(changeGroup); }
/// <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> /// Return true if the XObject contains a link to an EFObject. /// </summary> /// <param name="xobject"></param> /// <returns></returns> protected internal override bool ExpectEFObjectForXObject(XObject xobject) { return(ModelItemAnnotation.GetModelItem(xobject) != null); }
private void OnBeforeModelChangesCommitted(object sender, EfiChangingEventArgs e) { if (e.CommandProcessorContext != null) { var artifact = e.CommandProcessorContext.Artifact; if (_diagrams != null) { // Copy the current Xml changes over since we are going to modifying the transaction var xmlChangesCopy = new List <IXmlChange>(); var shapeChangeInfoSet = new HashSet <ShapeChangeInformation>(); foreach (var xmlChange in e.CommandProcessorContext.EfiTransaction.XmlChanges) { // if the changed node is whitespace, ignore it as we don't want to pick up false // annotations for it if (xmlChange.Node.NodeType == XmlNodeType.Text) { var text = xmlChange.Node as XText; var trimmedValue = text.Value.Trim(); if (String.IsNullOrEmpty(trimmedValue)) { continue; } } // Ignore changes to annotations var externalModelChange = new EFArtifact.ExternalXMLModelChange(xmlChange, artifact.ExpectEFObjectForXObject); if (externalModelChange.IsAnnotationChange(artifact.GetNamespaces())) { continue; } // Construct a fast-queryable lookup for the shape change information that already // exists in this transaction. The transaction may have already fired some rules that // injected shape (DiagramEFObjects) information into the transaction. The purpose // of this lookup is to skip duplicate additions/removals. var changedEFObject = externalModelChange.ChangedEFObject; if (changedEFObject != null) { var diagramObject = changedEFObject as ModelDiagram.BaseDiagramObject; if (diagramObject != null) { var changedEntityTypeShape = changedEFObject as EntityTypeShape; var changedAssociationConnector = changedEFObject as AssociationConnector; var changedInheritanceConnector = changedEFObject as InheritanceConnector; var shapeChangeInformation = new ShapeChangeInformation(); if (changedEntityTypeShape != null) { shapeChangeInformation.ModelEFObject = changedEntityTypeShape.EntityType.Target; } else if (changedAssociationConnector != null) { shapeChangeInformation.ModelEFObject = changedAssociationConnector.Association.Target; } else if (changedInheritanceConnector != null) { shapeChangeInformation.ModelEFObject = changedInheritanceConnector.EntityType.Target; } shapeChangeInformation.ChangeType = xmlChange.Action; Debug.Assert( diagramObject.Diagram != null, changedEFObject.ToPrettyString() + " doesn't belong to any diagram."); if (diagramObject.Diagram != null) { shapeChangeInformation.DiagramId = diagramObject.Diagram.Id; } shapeChangeInfoSet.Add(shapeChangeInformation); } } xmlChangesCopy.Add(xmlChange); } // Call resolve on the diagram objects in the model here. When the EDMX is being incrementally updated by some // external factor (for example, a database project in a by-ref edmx), there may be situations where the existing // diagram objects are not resolved yet. This means that when we try to create entity type shapes or association connectors // the logic assumes that there aren't any anti-dependencies of the model elements, so it adds duplicate shapes/connectors // which can affect the loading of the model later (it will hit DSL diagram validation logic). XmlModelHelper.NormalizeAndResolve(this); // If we see that an Association, EntityType, or EntityTypeBaseType has been added in the model // changes, then we create the subsequent diagram EFObjects (AssociationConnector, EntityTypeShape, // and InheritanceConnector). By creating them here, this will automatically push new changes onto the // Xml transaction foreach (var xmlChange in xmlChangesCopy) { var changedEFObject = ModelItemAnnotation.GetModelItem(xmlChange.Node); if (changedEFObject != null && ModelHelper.GetBaseModelRoot(changedEFObject) is ConceptualEntityModel) { var entityType = changedEFObject as ConceptualEntityType; var association = changedEFObject as Association; var baseType = changedEFObject as EntityTypeBaseType; foreach (var diagram in _diagrams) { if (entityType != null) { InjectEntityTypeShapeCommand( e.CommandProcessorContext, shapeChangeInfoSet, diagram, entityType, xmlChange.Action); } else if (association != null) { InjectAssociationConnectorCommand( e.CommandProcessorContext, shapeChangeInfoSet, diagram, association, xmlChange.Action); } else if (baseType != null) { InjectInheritanceConnectorCommand( e.CommandProcessorContext, shapeChangeInfoSet, diagram, baseType, xmlChange.Action); } } } } } } }
/// <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 } } }
private static void EscherDesignerNavigate(IServiceProvider serviceProvider, Uri uri, ErrorTask task) { // // Try to open the document in Escher. If the designer is already open, then this call will only activate the // frame; it might fire a selection change event but it will not reload the artifact. // // Do this first so if there is any problem in the code below with out-of-date line numbers, the document will open // if it is closed, and the error list will be refreshed with correct line-numbers. // IVsUIHierarchy ppHierOpen; uint itemID; IVsWindowFrame windowFrame; // Check if there is already primary or logical view opened for the document. // If not, open the primary view for the document. if ( !VsShellUtilities.IsDocumentOpen( serviceProvider, uri.LocalPath, VSConstants.LOGVIEWID_Primary, out ppHierOpen, out itemID, out windowFrame) && !VsShellUtilities.IsDocumentOpen( serviceProvider, uri.LocalPath, PackageConstants.guidLogicalView, out ppHierOpen, out itemID, out windowFrame)) { VsShellUtilities.OpenDocumentWithSpecificEditor( serviceProvider, uri.LocalPath, PackageConstants.guidEscherEditorFactory, VSConstants.LOGVIEWID_Primary); } // sanity check. If this is null, something bad happened with loading our package if (_dslDesignerOnNavigate == null) { Debug.Fail("_dslDesignerOnNavigate is null!"); return; } // get the artifact from the model manager var artifact = PackageManager.Package.ModelManager.GetArtifact(uri); if (artifact == null) { Debug.Fail( "We determined the artifact was designer-safe and we tried to open it in the designer, but where is the artifact?"); return; } var xobject = artifact.FindXObjectForLineAndColumn(task.Line, task.Column); Debug.Assert( xobject != null, "couldn't get XObject for artifact " + uri + ", line " + task.Line + ", column " + task.Column); var efobject = ModelItemAnnotation.GetModelItem(xobject); // we got the root xobject node, so fix this up. if (efobject == null) { efobject = artifact; Debug.Assert( task.Line <= 0 && task.Column <= 0, "non-zero line/column didn't find an efobject linked to an xobject!"); } var cModel = efobject.GetParentOfType(typeof(ConceptualEntityModel)) as ConceptualEntityModel; var sModel = efobject.GetParentOfType(typeof(StorageEntityModel)) as StorageEntityModel; var mModel = efobject.GetParentOfType(typeof(MappingModel)) as MappingModel; if (cModel != null) { var isComplexTypeOrFunctionImportOrChild = false; var obj = efobject; while (obj != null) { if (obj is ComplexType || obj is FunctionImport) { isComplexTypeOrFunctionImportOrChild = true; } obj = obj.Parent; } // node was in c-space, so navigate to appropriate node in the explorer and the designer ExplorerNavigationHelper.NavigateTo(efobject); if (_dslDesignerOnNavigate != null && !isComplexTypeOrFunctionImportOrChild) { _dslDesignerOnNavigate(efobject); } } else if (sModel != null) { // node is in s-space, so navigate to the appropriate node in the explorer. ExplorerNavigationHelper.NavigateTo(efobject); } else if (mModel != null) { // see if this is a function import error var fim = efobject.GetParentOfType(typeof(FunctionImportMapping)) as FunctionImportMapping; if (fim != null) { ExplorerNavigationHelper.NavigateTo(fim.FunctionImportName.Target); } else { // node was in m-space, so navigate to mapped c-space node, and show the mapping editor. // show this first, so the node in the entity-designer will be highlighted. PackageManager.Package.MappingDetailsWindow.Show(); if (_dslDesignerOnNavigate != null) { _dslDesignerOnNavigate(efobject); } } } }