private bool MergeChanges(
            IDependenciesChanges changes,
            IEnumerable <IDependenciesSnapshotFilter> snapshotFilters)
        {
            var worldBuilder = ImmutableDictionary.CreateBuilder <string, IDependency>(
                StringComparer.OrdinalIgnoreCase);

            worldBuilder.AddRange(DependenciesWorld);
            var topLevelBuilder = ImmutableHashSet.CreateBuilder <IDependency>();

            topLevelBuilder.AddRange(TopLevelDependencies);

            var anyChanges = false;

            foreach (var removed in changes.RemovedNodes)
            {
                var targetedId = Dependency.GetID(TargetFramework, removed.ProviderType, removed.Id);
                if (!worldBuilder.TryGetValue(targetedId, out IDependency dependency))
                {
                    continue;
                }

                if (snapshotFilters != null)
                {
                    foreach (var filter in snapshotFilters)
                    {
                        dependency = filter.BeforeRemove(
                            ProjectPath, TargetFramework, dependency, worldBuilder, topLevelBuilder, out bool filterAnyChanges);

                        anyChanges |= filterAnyChanges;

                        if (dependency == null)
                        {
                            break;
                        }
                    }
                }

                if (dependency == null)
                {
                    continue;
                }

                anyChanges = true;

                worldBuilder.Remove(targetedId);
                topLevelBuilder.Remove(dependency);
            }

            foreach (var added in changes.AddedNodes)
            {
                IDependency newDependency = new Dependency(added, TargetFramework);

                if (snapshotFilters != null)
                {
                    foreach (var filter in snapshotFilters)
                    {
                        newDependency = filter.BeforeAdd(
                            ProjectPath, TargetFramework, newDependency, worldBuilder, topLevelBuilder, out bool filterAnyChanges);

                        anyChanges |= filterAnyChanges;

                        if (newDependency == null)
                        {
                            break;
                        }
                    }
                }

                if (newDependency == null)
                {
                    continue;
                }

                anyChanges = true;

                worldBuilder.Remove(newDependency.Id);
                worldBuilder.Add(newDependency.Id, newDependency);
                if (newDependency.TopLevel)
                {
                    topLevelBuilder.Remove(newDependency);
                    topLevelBuilder.Add(newDependency);
                }
            }

            DependenciesWorld    = worldBuilder.ToImmutable();
            TopLevelDependencies = topLevelBuilder.ToImmutable();

            ConstructTopLevelDependenciesByPathMap();

            return(anyChanges);
        }
Exemplo n.º 2
0
        public void FromChanges_NoChangesAfterBeforeAddFilterDeclinedChange()
        {
            const string projectPath     = @"c:\somefolder\someproject\a.csproj";
            var          targetFramework = new TargetFramework("tfm1");

            var dependencyTop1 = new TestDependency
            {
                ProviderType   = "Xxx",
                Id             = Dependency.GetID(targetFramework, "Xxx", "topdependency1"),
                Name           = "TopDependency1",
                Caption        = "TopDependency1",
                SchemaItemType = "Xxx",
                Resolved       = true,
                TopLevel       = true
            };

            var dependencyChild1 = new TestDependency
            {
                ProviderType   = "Xxx",
                Id             = Dependency.GetID(targetFramework, "Xxx", "childdependency1"),
                Name           = "ChildDependency1",
                Caption        = "ChildDependency1",
                SchemaItemType = "Xxx",
                Resolved       = true,
                TopLevel       = false
            };

            var dependencyModelNew1 = new TestDependencyModel
            {
                ProviderType   = "Xxx",
                Id             = "newdependency1",
                Name           = "NewDependency1",
                Caption        = "NewDependency1",
                SchemaItemType = "Xxx",
                Resolved       = true,
                Icon           = KnownMonikers.Uninstall,
                ExpandedIcon   = KnownMonikers.Uninstall
            };

            var catalogs         = IProjectCatalogSnapshotFactory.Create();
            var previousSnapshot = ITargetedDependenciesSnapshotFactory.Implement(
                projectPath: projectPath,
                targetFramework: targetFramework,
                catalogs: catalogs,
                dependenciesWorld: new [] { dependencyTop1, dependencyChild1 },
                topLevelDependencies: new [] { dependencyTop1 });

            var changes = new DependenciesChangesBuilder();

            changes.Added(dependencyModelNew1);

            var snapshotFilter = new TestDependenciesSnapshotFilter()
                                 .BeforeAddReject(@"tfm1\xxx\newdependency1");

            var snapshot = TargetedDependenciesSnapshot.FromChanges(
                projectPath,
                previousSnapshot,
                changes.Build(),
                catalogs,
                ImmutableArray.Create <IDependenciesSnapshotFilter>(snapshotFilter),
                new Dictionary <string, IProjectDependenciesSubTreeProvider>(),
                null);

            Assert.Same(previousSnapshot, snapshot);
        }
Exemplo n.º 3
0
 /// <summary>
 /// Returns id having full path instead of OriginalItemSpec
 /// </summary>
 public static bool TopLevelIdEquals(this IDependency self, string id)
 {
     return(string.IsNullOrEmpty(self.Path)
         ? string.Equals(self.Id, id, StringComparisons.DependencyTreeIds)
         : Dependency.IdEquals(id, self.TargetFramework, self.ProviderType, self.Path));
 }
Exemplo n.º 4
0
 /// <summary>
 /// Returns id having full path instead of OriginalItemSpec
 /// </summary>
 public static string GetTopLevelId(this IDependency self)
 {
     return(string.IsNullOrEmpty(self.Path)
         ? self.Id
         : Dependency.GetID(self.TargetFramework, self.ProviderType, self.Path));
 }
        /// <summary>
        /// Applies changes to <paramref name="previousSnapshot"/> and produces a new snapshot if required.
        /// If no changes are made, <paramref name="previousSnapshot"/> is returned unmodified.
        /// </summary>
        /// <returns>An updated snapshot, or <paramref name="previousSnapshot"/> if no changes occured.</returns>
        public static ITargetedDependenciesSnapshot FromChanges(
            string projectPath,
            ITargetedDependenciesSnapshot previousSnapshot,
            IDependenciesChanges changes,
            IProjectCatalogSnapshot catalogs,
            ImmutableArray <IDependenciesSnapshotFilter> snapshotFilters,
            IReadOnlyDictionary <string, IProjectDependenciesSubTreeProvider> subTreeProviderByProviderType,
            IImmutableSet <string> projectItemSpecs)
        {
            Requires.NotNullOrWhiteSpace(projectPath, nameof(projectPath));
            Requires.NotNull(previousSnapshot, nameof(previousSnapshot));
            // catalogs can be null
            Requires.NotNull(changes, nameof(changes));
            Requires.Argument(!snapshotFilters.IsDefault, nameof(snapshotFilters), "Cannot be default.");
            Requires.NotNull(subTreeProviderByProviderType, nameof(subTreeProviderByProviderType));
            // projectItemSpecs can be null

            bool anyChanges = false;

            ITargetFramework targetFramework = previousSnapshot.TargetFramework;

            var worldBuilder = previousSnapshot.DependenciesWorld.ToBuilder();

            if (changes.RemovedNodes.Count != 0)
            {
                var context = new RemoveDependencyContext(worldBuilder);

                foreach (IDependencyModel removed in changes.RemovedNodes)
                {
                    Remove(context, removed);
                }
            }

            if (changes.AddedNodes.Count != 0)
            {
                var context = new AddDependencyContext(worldBuilder);

                foreach (IDependencyModel added in changes.AddedNodes)
                {
                    Add(context, added);
                }
            }

            // Also factor in any changes to path/framework/catalogs
            anyChanges =
                anyChanges ||
                !StringComparers.Paths.Equals(projectPath, previousSnapshot.ProjectPath) ||
                !targetFramework.Equals(previousSnapshot.TargetFramework) ||
                !Equals(catalogs, previousSnapshot.Catalogs);

            if (anyChanges)
            {
                return(new TargetedDependenciesSnapshot(
                           projectPath,
                           targetFramework,
                           catalogs,
                           worldBuilder.ToImmutable()));
            }

            return(previousSnapshot);

            void Remove(RemoveDependencyContext context, IDependencyModel dependencyModel)
            {
                string dependencyId = Dependency.GetID(
                    targetFramework, dependencyModel.ProviderType, dependencyModel.Id);

                if (!context.TryGetDependency(dependencyId, out IDependency dependency))
                {
                    return;
                }

                context.Reset();

                foreach (IDependenciesSnapshotFilter filter in snapshotFilters)
                {
                    filter.BeforeRemove(
                        targetFramework,
                        dependency,
                        context);

                    anyChanges |= context.Changed;

                    if (!context.GetResult(filter))
                    {
                        // TODO breaking here denies later filters the opportunity to modify builders
                        return;
                    }
                }

                worldBuilder.Remove(dependencyId);
                anyChanges = true;
            }

            void Add(AddDependencyContext context, IDependencyModel dependencyModel)
            {
                // Create the unfiltered dependency
                IDependency dependency = new Dependency(dependencyModel, targetFramework, projectPath);

                context.Reset();

                foreach (IDependenciesSnapshotFilter filter in snapshotFilters)
                {
                    filter.BeforeAddOrUpdate(
                        targetFramework,
                        dependency,
                        subTreeProviderByProviderType,
                        projectItemSpecs,
                        context);

                    dependency = context.GetResult(filter);

                    if (dependency == null)
                    {
                        break;
                    }
                }

                if (dependency != null)
                {
                    // A dependency was accepted
                    worldBuilder.Remove(dependency.Id);
                    worldBuilder.Add(dependency.Id, dependency);
                    anyChanges = true;
                }
                else
                {
                    // Even though the dependency was rejected, it's possible that filters made
                    // changes to other dependencies.
                    anyChanges |= context.Changed;
                }
            }
        }
Exemplo n.º 6
0
 public void GetID_CreatesCorrectString(string modelId, string expected)
 {
     Assert.Equal(expected, Dependency.GetID(TargetFramework.Any, "providerType", modelId));
 }
Exemplo n.º 7
0
        private bool MergeChanges(
            IDependenciesChanges changes,
            IEnumerable <IDependenciesSnapshotFilter> snapshotFilters,
            IEnumerable <IProjectDependenciesSubTreeProvider> subTreeProviders,
            HashSet <string> projectItemSpecs)
        {
            var worldBuilder    = DependenciesWorld.ToBuilder();
            var topLevelBuilder = TopLevelDependencies.ToBuilder();

            var anyChanges = false;

            foreach (var removed in changes.RemovedNodes)
            {
                var targetedId = Dependency.GetID(TargetFramework, removed.ProviderType, removed.Id);
                if (!worldBuilder.TryGetValue(targetedId, out IDependency dependency))
                {
                    continue;
                }

                if (snapshotFilters != null)
                {
                    foreach (var filter in snapshotFilters)
                    {
                        dependency = filter.BeforeRemove(
                            ProjectPath, TargetFramework, dependency, worldBuilder, topLevelBuilder, out bool filterAnyChanges);

                        anyChanges |= filterAnyChanges;

                        if (dependency == null)
                        {
                            break;
                        }
                    }
                }

                if (dependency == null)
                {
                    continue;
                }

                anyChanges = true;

                worldBuilder.Remove(targetedId);
                topLevelBuilder.Remove(dependency);
            }

            var subTreeProvidersMap = GetSubTreeProviderMap(subTreeProviders);

            foreach (var added in changes.AddedNodes)
            {
                IDependency newDependency = new Dependency(added, TargetFramework, ProjectPath);

                if (snapshotFilters != null)
                {
                    foreach (var filter in snapshotFilters)
                    {
                        newDependency = filter.BeforeAdd(
                            ProjectPath,
                            TargetFramework,
                            newDependency,
                            worldBuilder,
                            topLevelBuilder,
                            subTreeProvidersMap,
                            projectItemSpecs,
                            out bool filterAnyChanges);

                        anyChanges |= filterAnyChanges;

                        if (newDependency == null)
                        {
                            break;
                        }
                    }
                }

                if (newDependency == null)
                {
                    continue;
                }

                anyChanges = true;

                worldBuilder.Remove(newDependency.Id);
                worldBuilder.Add(newDependency.Id, newDependency);
                if (newDependency.TopLevel)
                {
                    topLevelBuilder.Remove(newDependency);
                    topLevelBuilder.Add(newDependency);
                }
            }

            DependenciesWorld    = worldBuilder.ToImmutable();
            TopLevelDependencies = topLevelBuilder.ToImmutable();

            ConstructTopLevelDependenciesByPathMap();

            return(anyChanges);
        }