Пример #1
0
        // 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);
        }
Пример #2
0
 public ExternalXMLModelChange(IXmlChange xmlChange, ExpectEFObjectParentForXObject assertDelegate)
 {
     _xmlChange       = xmlChange;
     _changedEFObject = ModelItemAnnotation.GetModelItem(_xmlChange.Node);
     _expectEFObjectParentForXObject = assertDelegate;
     ResolveParentEFObject();
 }
Пример #3
0
            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
            }
Пример #4
0
        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);
        }
Пример #5
0
        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);
            }
        }
Пример #6
0
        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);
 }
Пример #8
0
        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);
        }
Пример #9
0
        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);
        }
Пример #10
0
        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);
 }
Пример #12
0
        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);
                                }
                            }
                        }
                    }
                }
            }
        }
Пример #13
0
        /// <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);
                    }
                }
            }
        }