/// <summary>
        /// Updates the shared project import nodes that are shown under the 'Dependencies/Projects' node.
        /// </summary>
        /// <param name="sharedFolders">Snapshot of shared folders.</param>
        /// <param name="dependenciesChange"></param>
        /// <returns></returns>
        protected override void ProcessSharedProjectImportNodes(IProjectSharedFoldersSnapshot sharedFolders,
                                                                DependenciesChange dependenciesChange)
        {
            Requires.NotNull(sharedFolders, nameof(sharedFolders));
            Requires.NotNull(dependenciesChange, nameof(dependenciesChange));

            var sharedFolderProjectPaths = sharedFolders.Value.Select(sf => sf.ProjectPath);
            var rootNodeChildren         = RootNode.Children;
            var currentSharedImportNodes = rootNodeChildren
                                           .Where(x => x.Flags.Contains(ProjectTreeFlags.Common.SharedProjectImportReference));
            var currentSharedImportNodePaths = currentSharedImportNodes.Select(x => x.Id.ItemSpec);

            // process added nodes
            IEnumerable <string> addedSharedImportPaths = sharedFolderProjectPaths.Except(currentSharedImportNodePaths);
            var itemType = ResolvedProjectReference.PrimaryDataSourceItemType;

            foreach (string addedSharedImportPath in addedSharedImportPaths)
            {
                rootNodeChildren = RootNode.Children;
                var node = rootNodeChildren.FindNode(addedSharedImportPath, itemType);
                if (node == null)
                {
                    var sharedFlags = ProjectTreeFlags.Create(ProjectTreeFlags.Common.SharedProjectImportReference);

                    var id = new DependencyNodeId(ProviderType,
                                                  addedSharedImportPath,
                                                  itemType);
                    node = new SharedProjectDependencyNode(id, flags: sharedFlags);
                    dependenciesChange.AddedNodes.Add(node);
                }
            }

            // process removed nodes
            var removedSharedImportPaths = currentSharedImportNodePaths.Except(sharedFolderProjectPaths);

            foreach (string removedSharedImportPath in removedSharedImportPaths)
            {
                var existingImportNode = currentSharedImportNodes
                                         .Where(node => PathHelper.IsSamePath(node.Id.ItemSpec, removedSharedImportPath))
                                         .FirstOrDefault();

                if (existingImportNode != null)
                {
                    dependenciesChange.RemovedNodes.Add(existingImportNode);
                }
            }
        }
Esempio n. 2
0
        protected virtual void ProcessDuplicatedNodes(DependenciesChange dependenciesChange)
        {
            // Now add new nodes and dedupe any nodes that might have same caption.
            // For dedupping we apply aliases to all nodes with similar Caption. Alias
            // is a "Caption (ItemSpec)" and is unique. We try to find existing node with
            // with the same caption, if found we apply alias to both nodes. If not found
            // we also check if there are nodes with alias already applied earlier and having
            // same caption. If yes, we just need to apply alias to our current node only.
            foreach (var nodeToAdd in dependenciesChange.AddedNodes)
            {
                var rootNodeChildren = RootNode.Children;
                var shouldApplyAlias = false;
                var matchingChild    = rootNodeChildren.FirstOrDefault(
                    x => x.Caption.Equals(nodeToAdd.Caption, StringComparison.OrdinalIgnoreCase));
                if (matchingChild == null)
                {
                    shouldApplyAlias = rootNodeChildren.Any(
                        x => x.Caption.Equals(
                            string.Format(CultureInfo.CurrentCulture, "{0} ({1})", nodeToAdd.Caption, x.Id.ItemSpec),
                            StringComparison.OrdinalIgnoreCase));
                }
                else
                {
                    shouldApplyAlias = true;
                }

                if (shouldApplyAlias)
                {
                    if (matchingChild != null)
                    {
                        matchingChild.SetProperties(caption: matchingChild.Alias);
                        dependenciesChange.UpdatedNodes.Add(matchingChild);
                    }

                    nodeToAdd.SetProperties(caption: nodeToAdd.Alias);
                }

                RootNode.AddChild(nodeToAdd);
            }
        }
 /// <summary>
 /// Updates the shared project import nodes that are shown under the 'Dependencies/Projects' node.
 /// </summary>
 /// <param name="sharedFolders">Snapshot of shared folders.</param>
 /// <param name="dependenciesChange"></param>
 /// <returns></returns>
 protected virtual void ProcessSharedProjectImportNodes(IProjectSharedFoldersSnapshot sharedFolders,
                                                        DependenciesChange dependenciesChange)
 {
     // does nothing by default
 }
        protected virtual DependenciesChange ProcessDependenciesChanges(
            IProjectSubscriptionUpdate projectSubscriptionUpdate,
            IProjectCatalogSnapshot catalogs)
        {
            var changes = projectSubscriptionUpdate.ProjectChanges;
            var resolvedReferenceChanges =
                ResolvedReferenceRuleNames.Where(x => changes.Keys.Contains(x))
                .Select(ruleName => changes[ruleName]).ToImmutableHashSet();

            var unresolvedReferenceSnapshots = changes.Values
                                               .Where(cd => !ResolvedReferenceRuleNames.Any(ruleName =>
                                                                                            string.Equals(ruleName,
                                                                                                          cd.After.RuleName,
                                                                                                          StringComparison.OrdinalIgnoreCase)))
                                               .ToDictionary(d => d.After.RuleName, d => d, StringComparer.OrdinalIgnoreCase);

            var rootTreeNodes      = new HashSet <IDependencyNode>(RootNode.Children);
            var dependenciesChange = new DependenciesChange();

            foreach (var unresolvedChange in unresolvedReferenceSnapshots.Values)
            {
                if (!unresolvedChange.Difference.AnyChanges)
                {
                    continue;
                }

                var itemType = GetItemTypeFromRuleName(unresolvedChange.After.RuleName,
                                                       catalogs,
                                                       true);
                if (itemType == null)
                {
                    // We must be missing that rule. Skip it.
                    continue;
                }

                foreach (string removedItemSpec in unresolvedChange.Difference.RemovedItems)
                {
                    var node = rootTreeNodes.FindNode(removedItemSpec, itemType);
                    if (node != null)
                    {
                        dependenciesChange.RemovedNodes.Add(node);
                    }
                }

                foreach (string addedItemSpec in unresolvedChange.Difference.AddedItems)
                {
                    var node = rootTreeNodes.FindNode(addedItemSpec, itemType);
                    if (node == null)
                    {
                        var properties = GetProjectItemProperties(unresolvedChange.After, addedItemSpec);
                        node = CreateDependencyNode(addedItemSpec,
                                                    itemType,
                                                    properties: properties,
                                                    resolved: false);
                        dependenciesChange.AddedNodes.Add(node);
                    }
                }
            }

            var updatedUnresolvedSnapshots = unresolvedReferenceSnapshots.Values.Select(cd => cd.After);

            foreach (var resolvedReferenceRuleChanges in resolvedReferenceChanges)
            {
                if (!resolvedReferenceRuleChanges.Difference.AnyChanges)
                {
                    continue;
                }

                // if resolved reference appears in Removed list, it means that it is either removed from
                // project or can not be resolved anymore. In case when it can not be resolved,
                // we must remove old "resolved" node and add new unresolved node with corresponding
                // properties changes (rules, icon, etc)
                // Note: removed resolved node is not added to "added unresolved diff", which we process
                // above, thus we need to do this properties update here. It is just cleaner to re-add node
                // instead of modifying properties.
                foreach (string removedItemSpec in resolvedReferenceRuleChanges.Difference.RemovedItems)
                {
                    string unresolvedItemSpec = resolvedReferenceRuleChanges.Before
                                                .Items[removedItemSpec][OriginalItemSpecPropertyName];
                    IProjectRuleSnapshot unresolvedReferenceSnapshot = null;
                    string unresolvedItemType = GetUnresolvedReferenceItemType(unresolvedItemSpec,
                                                                               updatedUnresolvedSnapshots,
                                                                               catalogs,
                                                                               out unresolvedReferenceSnapshot);
                    var node = rootTreeNodes.FindNode(removedItemSpec, unresolvedItemType);
                    if (node != null)
                    {
                        dependenciesChange.RemovedNodes.Add(node);

                        IImmutableDictionary <string, string> properties = null;
                        if (unresolvedReferenceSnapshot != null)
                        {
                            properties = GetProjectItemProperties(unresolvedReferenceSnapshot, unresolvedItemSpec);
                        }

                        node = CreateDependencyNode(unresolvedItemSpec,
                                                    unresolvedItemType,
                                                    properties: properties,
                                                    resolved: false);
                        dependenciesChange.AddedNodes.Add(node);
                    }
                }

                foreach (string addedItemSpec in resolvedReferenceRuleChanges.Difference.AddedItems)
                {
                    var properties = GetProjectItemProperties(resolvedReferenceRuleChanges.After, addedItemSpec);
                    if (properties == null || !properties.Keys.Contains(OriginalItemSpecPropertyName))
                    {
                        // if there no OriginalItemSpec, we can not associate item with the rule
                        continue;
                    }

                    var originalItemSpec = properties[OriginalItemSpecPropertyName];
                    IProjectRuleSnapshot unresolvedReferenceSnapshot = null;
                    var itemType = GetUnresolvedReferenceItemType(originalItemSpec,
                                                                  updatedUnresolvedSnapshots,
                                                                  catalogs,
                                                                  out unresolvedReferenceSnapshot);
                    if (string.IsNullOrEmpty(itemType))
                    {
                        // Note: design time build resolves not only our unresolved assemblies, but also
                        // all transitive assembly dependencies, which ar enot direct references and
                        // we should not show them. If reference does not have an unresolved reference
                        // corresponded to it, i.e. itemType = null here - we skip it.
                        continue;
                    }

                    // avoid adding unresolved dependency along with resolved one
                    var existingUnresolvedNode = dependenciesChange.AddedNodes.FindNode(originalItemSpec, itemType);
                    if (existingUnresolvedNode != null)
                    {
                        dependenciesChange.AddedNodes.Remove(existingUnresolvedNode);
                    }

                    // if unresolved dependency was added earlier, remove it, since it will be substituted by resolved one
                    existingUnresolvedNode = rootTreeNodes.FindNode(originalItemSpec, itemType);
                    if (existingUnresolvedNode != null)
                    {
                        dependenciesChange.RemovedNodes.Add(existingUnresolvedNode);
                    }

                    var newNode = CreateDependencyNode(originalItemSpec,
                                                       itemType: itemType,
                                                       properties: properties);
                    dependenciesChange.AddedNodes.Add(newNode);
                }
            }

            return(dependenciesChange);
        }
Esempio n. 5
0
 public void TestProcessDuplicatedNodes(DependenciesChange dependenciesChange)
 {
     ProcessDuplicatedNodes(dependenciesChange);
 }
Esempio n. 6
0
 public void TestProcessDuplicatedNodes(DependenciesChange changes)
 {
     ProcessDuplicatedNodes(changes);
 }
Esempio n. 7
0
 protected override void ProcessDuplicatedNodes(DependenciesChange dependenciesChange)
 {
     // do nothing - we don't need to do anything special for duplicated nodes,
     // since we make sure that there no duplicated packages earlier.
 }
Esempio n. 8
0
        protected override DependenciesChange ProcessDependenciesChanges(
            IProjectSubscriptionUpdate projectSubscriptionUpdate,
            IProjectCatalogSnapshot catalogs)
        {
            var changes            = projectSubscriptionUpdate.ProjectChanges;
            var dependenciesChange = new DependenciesChange();

            lock (_snapshotLock)
            {
                var newDependencies = new HashSet <DependencyMetadata>();
                foreach (var change in changes.Values)
                {
                    if (!change.Difference.AnyChanges)
                    {
                        continue;
                    }

                    foreach (string removedItemSpec in change.Difference.RemovedItems)
                    {
                        CurrentSnapshot.RemoveDependency(removedItemSpec);

                        var itemNode = RootNode.Children.FirstOrDefault(
                            x => x.Id.ItemSpec.Equals(removedItemSpec, StringComparison.OrdinalIgnoreCase));
                        if (itemNode != null)
                        {
                            dependenciesChange.RemovedNodes.Add(itemNode);
                        }
                    }

                    foreach (string changedItemSpec in change.Difference.ChangedItems)
                    {
                        var properties = GetProjectItemProperties(change.After, changedItemSpec);
                        if (properties == null)
                        {
                            continue;
                        }

                        CurrentSnapshot.UpdateDependency(changedItemSpec, properties);

                        var itemNode = RootNode.Children.FirstOrDefault(
                            x => x.Id.ItemSpec.Equals(changedItemSpec, StringComparison.OrdinalIgnoreCase));
                        if (itemNode != null)
                        {
                            dependenciesChange.UpdatedNodes.Add(itemNode);
                        }
                    }

                    foreach (string addedItemSpec in change.Difference.AddedItems)
                    {
                        var properties = GetProjectItemProperties(change.After, addedItemSpec);
                        if (properties == null)
                        {
                            continue;
                        }

                        var newDependency = CurrentSnapshot.AddDependency(addedItemSpec, properties);

                        newDependencies.Add(newDependency);
                    }
                }

                // since we have limited implementation for multi targeted projects,
                // we assume that there is only one target - take first target and add
                // top level nodes for it

                var currentTarget = CurrentSnapshot.Targets.Keys.FirstOrDefault();
                if (currentTarget == null)
                {
                    return(dependenciesChange);
                }

                var currentTargetDependency           = CurrentSnapshot.DependenciesWorld[currentTarget];
                var currentTargetTopLevelDependencies = currentTargetDependency.DependenciesItemSpecs;
                var addedTopLevelDependencies         = newDependencies.Where(
                    x => currentTargetTopLevelDependencies.Contains(x.ItemSpec));
                foreach (var addedDependency in addedTopLevelDependencies)
                {
                    var itemNode = RootNode.Children.FirstOrDefault(
                        x => x.Id.ItemSpec.Equals(addedDependency.ItemSpec,
                                                  StringComparison.OrdinalIgnoreCase));
                    if (itemNode == null)
                    {
                        itemNode = CreateDependencyNode(addedDependency, topLevel: true);
                        dependenciesChange.AddedNodes.Add(itemNode);
                    }
                }
            }

            return(dependenciesChange);
        }
Esempio n. 9
0
        protected override DependenciesChange ProcessDependenciesChanges(
            IProjectSubscriptionUpdate projectSubscriptionUpdate,
            IProjectCatalogSnapshot catalogs)
        {
            var dependenciesChange = new DependenciesChange();
            // take a snapshot for current top level tree nodes
            var rootNodes = new HashSet <IDependencyNode>(RootNode.Children);

            lock (_snapshotLock)
            {
                var unresolvedChanges = ProcessUnresolvedChanges(projectSubscriptionUpdate);
                var resolvedChanges   = ProcessResolvedChanges(projectSubscriptionUpdate, rootNodes);

                // Logic below merges unresolved and resolved pending changes. Resolved should win if there
                // is similar unresolved dependency. Thus
                //  - for pending removals, we don't care about the order and just remove whatever valid
                //    pending changes are there (valid=existing in the RootNode top level children)
                //  - for pending updates, since ItemSpecs must exist (otherwise it would not be an Update pending
                //    change, but an Add or Remove), we first try to merge resolved pending changes, where for
                //    each change we try to remove similar unresolved pending Update request.
                //  - for pending additions we need to match itemSpecs of the resolved dependencies to names of the
                //    unresolved, since resolved ItemSpec is "tfm/packagename/version", but unresolved is just
                //    "packagename". Thus to avoid same tree node name collision we need to be smart when detecting
                //    similar resolved vs unresolved pending changes.
                //    The algorithm is, first merge resolved changes and if there is a
                //          - matching unresolved item in the RootNode already, submit a removal
                //          - matching unresolved item in pending unresolved additions - remove it form there too
                //    Then, process remaining unresolved changes and before mergin each of them, check if matching
                //    name already exists in RootNode or already merged additions.
                //  Note: if it would became too complicated with time, create a separate PackageDependencyChangesResolver
                //  class that would hide it and make logic here simpler.

                // remove
                foreach (var metadata in unresolvedChanges.RemovedNodes)
                {
                    var itemNode = rootNodes.FirstOrDefault(
                        x => x.Id.ItemSpec.Equals(metadata.ItemSpec, StringComparison.OrdinalIgnoreCase));
                    if (itemNode != null)
                    {
                        dependenciesChange.RemovedNodes.Add(itemNode);
                    }
                }

                foreach (var metadata in resolvedChanges.RemovedNodes)
                {
                    var itemNode = rootNodes.FirstOrDefault(
                        x => x.Id.ItemSpec.Equals(metadata.ItemSpec, StringComparison.OrdinalIgnoreCase));
                    if (itemNode != null)
                    {
                        dependenciesChange.RemovedNodes.Add(itemNode);
                    }
                }

                // update
                foreach (var resolvedMetadata in resolvedChanges.UpdatedNodes)
                {
                    // since it is an update root node must have those item specs, so we can check them
                    var itemNode = rootNodes.FirstOrDefault(
                        x => x.Id.ItemSpec.Equals(resolvedMetadata.ItemSpec, StringComparison.OrdinalIgnoreCase));
                    if (itemNode != null)
                    {
                        itemNode = CreateDependencyNode(resolvedMetadata, topLevel: true);
                        dependenciesChange.UpdatedNodes.Add(itemNode);
                    }

                    var unresolvedMatch = unresolvedChanges.UpdatedNodes.FirstOrDefault(x => x.ItemSpec.Equals(resolvedMetadata.Name));
                    if (unresolvedMatch != null)
                    {
                        unresolvedChanges.UpdatedNodes.Remove(unresolvedMatch);
                    }
                }

                foreach (var unresolvedMetadata in unresolvedChanges.UpdatedNodes)
                {
                    // since it is an update root node must have those item specs, so we can check them
                    var itemNode = rootNodes.FirstOrDefault(
                        x => x.Id.ItemSpec.Equals(unresolvedMetadata.ItemSpec, StringComparison.OrdinalIgnoreCase));
                    if (itemNode != null)
                    {
                        dependenciesChange.UpdatedNodes.Add(itemNode);
                    }
                }

                // add
                foreach (var resolvedMetadata in resolvedChanges.AddedNodes)
                {
                    // see if there is already node created for unresolved package - if yes, delete it
                    // Note: unresolved packages ItemSpec contain only package name, when resolved package ItemSpec
                    // contains TFM/PackageName/version, so we need to check for name if we want to find unresolved
                    // packages.
                    var itemNode = rootNodes.FirstOrDefault(
                        x => x.Id.ItemSpec.Equals(resolvedMetadata.Name, StringComparison.OrdinalIgnoreCase));
                    if (itemNode != null)
                    {
                        dependenciesChange.RemovedNodes.Add(itemNode);
                    }

                    // see if there no node with the same resolved metadata - if no, create it
                    itemNode = rootNodes.FirstOrDefault(
                        x => x.Id.ItemSpec.Equals(resolvedMetadata.ItemSpec, StringComparison.OrdinalIgnoreCase));
                    if (itemNode == null)
                    {
                        itemNode = CreateDependencyNode(resolvedMetadata, topLevel: true);
                        dependenciesChange.AddedNodes.Add(itemNode);
                    }

                    // avoid adding matching unresolved packages
                    var unresolvedMatch = unresolvedChanges.AddedNodes.FirstOrDefault(x => x.ItemSpec.Equals(resolvedMetadata.Name));
                    if (unresolvedMatch != null)
                    {
                        unresolvedChanges.AddedNodes.Remove(unresolvedMatch);
                    }
                }

                foreach (var unresolvedMetadata in unresolvedChanges.AddedNodes)
                {
                    var itemNode = rootNodes.FirstOrDefault(x => DoesNodeMatchByNameOrItemSpec(x, unresolvedMetadata.ItemSpec));
                    if (itemNode == null)
                    {
                        // in case when unresolved come together with resolved data, root nodes might not yet have
                        // an unresolved node and we need to check if we did add resolved one above to avoid collision.
                        itemNode = dependenciesChange.AddedNodes.FirstOrDefault(
                            x => DoesNodeMatchByNameOrItemSpec(x, unresolvedMetadata.ItemSpec));
                    }

                    if (itemNode == null)
                    {
                        itemNode = CreateDependencyNode(unresolvedMetadata, topLevel: true);
                        dependenciesChange.AddedNodes.Add(itemNode);
                    }
                }
            }

            return(dependenciesChange);
        }