Beispiel #1
0
 private AggregateContainsRelationCollection(IRelatableItem item, ImmutableArray <IRelation> containsRelations)
 {
     _item  = item;
     _spans = containsRelations
              .Select(relation => new AggregateContainsRelationCollectionSpan(this, relation))
              .ToArray();
 }
 void IRelation.UpdateContainsCollection(IRelatableItem parent, AggregateContainsRelationCollectionSpan span)
 {
     if (parent is TParent typedParent)
     {
         UpdateContainsCollection(typedParent, span);
     }
 }
        protected override bool TryGetProjectNode(IProjectTree targetRootNode, IRelatableItem item, [NotNullWhen(returnValue: true)] out IProjectTree?projectTree)
        {
            IProjectTree?typeGroupNode = targetRootNode.FindChildWithFlags(DependencyTreeFlags.ProjectDependencyGroup);

            projectTree = typeGroupNode?.FindChildWithFlags(ProjectTreeFlags.Create("$ID:" + Library.Name));

            return(projectTree != null);
        }
        IEnumerable <IRelatableItem>?IRelation.CreateContainedByItems(IRelatableItem child)
        {
            if (child is TChild typedChild)
            {
                return(CreateContainedByItems(typedChild));
            }

            return(null);
        }
Beispiel #5
0
        /// <summary>
        /// Attempts to create a collection for the children of <paramref name="parentItem"/>.
        /// Fails only when no relations exist to produce child items for the given item's type.
        /// </summary>
        public static bool TryCreate(IRelatableItem parentItem, IRelationProvider relationProvider, [NotNullWhen(returnValue: true)] out AggregateContainsRelationCollection?collection)
        {
            ImmutableArray <IRelation> containsRelations = relationProvider.GetContainsRelationsFor(parentItem.GetType());

            if (containsRelations.IsEmpty)
            {
                collection = null;
                return(false);
            }

            collection = new AggregateContainsRelationCollection(parentItem, containsRelations);
            return(true);
        }
 bool IRelation.HasContainedItem(IRelatableItem parent) => HasContainedItems((TParent)parent);
 protected virtual bool TryGetProjectNode(IProjectTree targetRootNode, IRelatableItem item, [NotNullWhen(returnValue: true)] out IProjectTree?projectTree)
 {
     projectTree = null;
     return(false);
 }
 bool IRelatableItem.TryGetProjectNode(IProjectTree targetRootNode, IRelatableItem item, [NotNullWhen(returnValue: true)] out IProjectTree?projectTree)
 {
     return(TryGetProjectNode(targetRootNode, item, out projectTree));
 }
Beispiel #9
0
        /// <summary>
        /// Updates the items contained within this span, which ultimately contributes to the
        /// parent <see cref="AggregateContainsRelationCollection"/>.
        /// </summary>
        /// <remarks>
        /// <para>
        /// This method runs on the UI thread, recursively across all materialized collections in the tree.
        /// In order to provide linear time complexity, <paramref name="sources"/> must have a stable sorted order across invocations
        /// of this method. Failing this requirement will result in increased numbers of updates to items, degrading performance.
        /// </para>
        /// <para>
        /// This method operates over the ordered sequence of <typeparamref name="TData"/> values in <paramref name="sources"/>,
        /// comparing them each in turn (via <paramref name="comparer"/>) to any existing <typeparamref name="TItem"/> items
        /// in the span. This comparison only considers the 'identity' of its operands, not their state. The comparison determines
        /// what happens for that data/item pair:
        /// <list type="bullet">
        ///   <item>
        ///     <paramref name="comparer"/> returns zero -- the source value and existing item match. <paramref name="update"/> is
        ///     called with both, allowing the data value to update the item in-place. If <paramref name="update"/> returns
        ///     <see langword="true"/> then any materialized descendents of the item are updated recursively via their relations.
        ///   </item>
        ///   <item>
        ///     <paramref name="comparer"/> returns negative -- the source value does not exist in the current collection and should
        ///     be added. <paramref name="factory"/> is called to produce the new item to insert.
        ///   </item>
        ///   <item>
        ///     <paramref name="comparer"/> returns positive -- the source no longer contains the item, and it should be removed.
        ///   </item>
        /// </list>
        /// This method ensures the appropriate <see cref="INotifyCollectionChanged"/> events are triggered on the parent
        /// <see cref="AggregateContainsRelationCollection"/> in response to these updates.
        /// </para>
        /// </remarks>
        /// <param name="sources">
        /// The sequence of data values that the resulting items in this span will reflect when this method completes. The sequence must
        /// be ordered in a way that <paramref name="comparer"/>.
        /// </param>
        /// <param name="comparer">
        /// Compares a source value with an existing item to determine whether they have equivalent identity.
        /// Does not consider the state within either type while producing its result.
        /// </param>
        /// <param name="update">
        /// Updates a tree item based on a data value. Returns <see langword="true"/> is the update mutated the tree item in
        /// some way, otherwise <see langword="false"/>.</param>
        /// <param name="factory">
        /// Creates a new item for a given data value.
        /// </param>
        public void UpdateContainsItems <TData, TItem>(
            IEnumerable <TData> sources,
            Func <TData, TItem, int> comparer,
            Func <TData, TItem, bool> update,
            Func <TData, TItem> factory)
            where TItem : class, IRelatableItem
        {
            using IEnumerator <TData> src = sources.GetEnumerator();

            bool?srcConsumed = null;

            for (int itemIndex = 0; Items != null && itemIndex < Items.Count; itemIndex++)
            {
                if (srcConsumed != false && !src.MoveNext())
                {
                    // Source stream ended, all remaining items are invalid. Remove each in turn.
                    // Note we do not reset as that reset would refresh the entire parent collection, not just this span of items.
                    // We remove items in reverse order to reduce shuffling items in collections.
                    for (int removeAtIndex = Items.Count - 1; removeAtIndex >= itemIndex; removeAtIndex--)
                    {
                        IRelatableItem removedItem = Items[removeAtIndex];
                        Items.RemoveAt(removeAtIndex);
                        _parent.RaiseChange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removedItem, BaseIndex + removeAtIndex));
                    }

                    srcConsumed = true;
                    break;
                }

                TData source = src.Current;

                var item = (TItem)Items[itemIndex];

                int comparison = comparer(source, item);

                if (comparison == 0)
                {
                    // Items match, update in place
                    if (update(source, item))
                    {
                        // The update changed state, so notify its contains collection to update any materialized children via its relations
                        item.ContainsCollection?.OnStateUpdated();
                    }
                    srcConsumed = true;
                }
                else if (comparison < 0)
                {
                    // Source contains a new item to insert
                    TItem newItem = factory(source);
                    Items.Insert(itemIndex, newItem);
                    _parent.RaiseChange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newItem, BaseIndex + itemIndex));
                    srcConsumed = true;
                }
                else
                {
                    // Source is missing this item, remove it
                    Items.RemoveAt(itemIndex);
                    _parent.RaiseChange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, BaseIndex + itemIndex));

                    // decrement the index as we've removed the item from this index and need to consider the one which is now at this index
                    itemIndex--;
                    srcConsumed = false;
                }
            }

            while (srcConsumed == false || src.MoveNext())
            {
                // Add extra source items to end of list
                TData source  = src.Current;
                TItem newItem = factory(source);
                Items ??= new List <IRelatableItem>();
                Items.Add(newItem);
                _parent.RaiseChange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newItem, BaseIndex + Items.Count - 1));
                srcConsumed = true;
            }

            if (Items?.Count == 0)
            {
                Items = null;
            }
        }
Beispiel #10
0
        public void SubmitResult(IRelatableItem?item)
        {
            if (item == null)
            {
                return;
            }

            item = DeduplicateItem(item);

            PopulateAncestors(item);

            _inner.SubmitResult(item);

            void PopulateAncestors(IRelatableItem childItem)
            {
                if (childItem.ContainedByCollection != null)
                {
                    // We've already populated this item's ancestors. It's likely an ancestor of
                    // another search result. This also prevents runaway in case of cycles.
                    return;
                }

                ImmutableArray <IRelation> containedByRelations = _relationProvider.GetContainedByRelationsFor(childItem.GetType());

                if (containedByRelations.IsEmpty)
                {
                    // We should never have a scenario where an item type does not have a parent.
                    TraceUtilities.TraceError($"No IRelation exports exist that provide parent (ContainedBy) items for type {childItem.GetType()}.");
                    return;
                }

                var allParentItems = new List <object>();

                foreach (IRelation relation in containedByRelations)
                {
                    IEnumerable <IRelatableItem>?relationParentItems = relation.CreateContainedByItems(childItem);

                    if (relationParentItems != null)
                    {
                        foreach (IRelatableItem parentItem in relationParentItems)
                        {
                            IRelatableItem deduplicateItem = DeduplicateItem(parentItem);
                            allParentItems.Add(deduplicateItem);

                            if (deduplicateItem.TryGetProjectNode(_targetRootNode, parentItem, out IProjectTree? projectNode))
                            {
                                uint             itemId        = (uint)projectNode.Identity.ToInt32();
                                IVsHierarchyItem hierarchyItem = _hierarchyItemManager.GetHierarchyItem(_projectVsServices.VsHierarchy, itemId);
                                allParentItems.Add(hierarchyItem);
                            }

                            if (deduplicateItem.ContainedByCollection == null)
                            {
                                PopulateAncestors(deduplicateItem);
                            }
                        }
                    }
                }

                childItem.ContainedByCollection = new AggregateContainedByRelationCollection(allParentItems);

                return;
            }

            IRelatableItem DeduplicateItem(IRelatableItem item)
            {
                object key = GetItemKey(item);

                if (_itemByKey.TryGetValue(key, out IRelatableItem existingItem))
                {
                    return(existingItem);
                }

                _itemByKey.Add(key, item);
                return(item);
            }
        }
Beispiel #11
0
 private static object GetItemKey(IRelatableItem item) => (item.GetType(), item.Identity);