private bool ApplySuppressionFix(Func<Project, bool> shouldFixInProject, bool selectedEntriesOnly, bool isAddSuppression, bool isSuppressionInSource, bool onlyCompilerDiagnostics, bool showPreviewChangesDialog)
        {
            ImmutableDictionary<Document, ImmutableArray<Diagnostic>> documentDiagnosticsToFixMap = null;
            ImmutableDictionary<Project, ImmutableArray<Diagnostic>> projectDiagnosticsToFixMap = null;

            var title = isAddSuppression ? ServicesVSResources.SuppressMultipleOccurrences : ServicesVSResources.RemoveSuppressMultipleOccurrences;
            var waitDialogMessage = isAddSuppression ? ServicesVSResources.ComputingSuppressionFix : ServicesVSResources.ComputingRemoveSuppressionFix;

            // Get the diagnostics to fix from the suppression state service.
            Action<CancellationToken> computeDiagnosticsToFix = cancellationToken =>
            {
                var diagnosticsToFix = _suppressionStateService.GetItemsAsync(
                                selectedEntriesOnly,
                                isAddSuppression,
                                isSuppressionInSource,
                                onlyCompilerDiagnostics,
                                cancellationToken)
                            .WaitAndGetResult(cancellationToken);

                if (diagnosticsToFix.IsEmpty)
                {
                    return;
                }

                cancellationToken.ThrowIfCancellationRequested();
                documentDiagnosticsToFixMap = GetDocumentDiagnosticsToFixAsync(diagnosticsToFix, shouldFixInProject, cancellationToken).WaitAndGetResult(cancellationToken);

                cancellationToken.ThrowIfCancellationRequested();
                projectDiagnosticsToFixMap = isSuppressionInSource ?
                    ImmutableDictionary<Project, ImmutableArray<Diagnostic>>.Empty :
                    GetProjectDiagnosticsToFixAsync(diagnosticsToFix, shouldFixInProject, cancellationToken).WaitAndGetResult(cancellationToken);
            };

            var result = InvokeWithWaitDialog(computeDiagnosticsToFix, title, waitDialogMessage);

            // Bail out if the user cancelled.
            if (result == WaitIndicatorResult.Canceled ||
                documentDiagnosticsToFixMap == null ||
                projectDiagnosticsToFixMap == null)
            {
                return false;
            }

            if (documentDiagnosticsToFixMap.IsEmpty && projectDiagnosticsToFixMap.IsEmpty)
            {
                // Nothing to fix.
                return true;
            }

            // Equivalence key determines what fix will be applied.
            // Make sure we don't include any specific diagnostic ID, as we want all of the given diagnostics (which can have varied ID) to be fixed.
            var equivalenceKey = isAddSuppression ?
                (isSuppressionInSource ? FeaturesResources.SuppressWithPragma : FeaturesResources.SuppressWithGlobalSuppressMessage) :
                FeaturesResources.RemoveSuppressionEquivalenceKeyPrefix;

            // We have different suppression fixers for every language.
            // So we need to group diagnostics by the containing project language and apply fixes separately.
            var languages = new HashSet<string>(projectDiagnosticsToFixMap.Keys.Select(p => p.Language).Concat(documentDiagnosticsToFixMap.Select(kvp => kvp.Key.Project.Language)));
            var newSolution = _workspace.CurrentSolution;

            foreach (var language in languages)
            {
                // Use the Fix multiple occurrences service to compute a bulk suppression fix for the specified document and project diagnostics,
                // show a preview changes dialog and then apply the fix to the workspace.

                var documentDiagnosticsPerLanguage = GetDocumentDiagnosticsMappedToNewSolution(documentDiagnosticsToFixMap, newSolution, language);
                if (!documentDiagnosticsPerLanguage.IsEmpty)
                {
                    var suppressionFixer = GetSuppressionFixer(documentDiagnosticsPerLanguage.SelectMany(kvp => kvp.Value), language, _codeFixService);
                    if (suppressionFixer != null)
                    {
                        var suppressionFixAllProvider = suppressionFixer.GetFixAllProvider();
                        newSolution = _fixMultipleOccurencesService.GetFix(
                            documentDiagnosticsPerLanguage,
                            _workspace,
                            suppressionFixer,
                            suppressionFixAllProvider,
                            equivalenceKey,
                            title,
                            waitDialogMessage,
                            cancellationToken: CancellationToken.None);
                        if (newSolution == null)
                        {
                            // User cancelled or fixer threw an exception, so we just bail out.
                            return false;
                        }
                    }
                }

                var projectDiagnosticsPerLanguage = GetProjectDiagnosticsMappedToNewSolution(projectDiagnosticsToFixMap, newSolution, language);
                if (!projectDiagnosticsPerLanguage.IsEmpty)
                {
                    var suppressionFixer = GetSuppressionFixer(projectDiagnosticsPerLanguage.SelectMany(kvp => kvp.Value), language, _codeFixService);
                    if (suppressionFixer != null)
                    {
                        var suppressionFixAllProvider = suppressionFixer.GetFixAllProvider();
                        newSolution = _fixMultipleOccurencesService.GetFix(
                             projectDiagnosticsPerLanguage,
                             _workspace,
                             suppressionFixer,
                             suppressionFixAllProvider,
                             equivalenceKey,
                             title,
                             waitDialogMessage,
                             CancellationToken.None);
                        if (newSolution == null)
                        {
                            // User cancelled or fixer threw an exception, so we just bail out.
                            return false;
                        }
                    }
                }
            }

            if (showPreviewChangesDialog)
            {
                newSolution = FixAllGetFixesService.PreviewChanges(
                    _workspace.CurrentSolution,
                    newSolution,
                    fixAllPreviewChangesTitle: title,
                    fixAllTopLevelHeader: title,
                    languageOpt: languages.SingleOrDefault(),
                    workspace: _workspace);
                if (newSolution == null)
                {
                    return false;
                }
            }

            waitDialogMessage = isAddSuppression ? ServicesVSResources.ApplyingSuppressionFix : ServicesVSResources.ApplyingRemoveSuppressionFix;
            Action<CancellationToken> applyFix = cancellationToken =>
            {
                var operations = SpecializedCollections.SingletonEnumerable<CodeActionOperation>(new ApplyChangesOperation(newSolution));
                _editHandlerService.Apply(
                    _workspace,
                    fromDocument: null,
                    operations: operations,
                    title: title,
                    cancellationToken: cancellationToken);
            };

            result = InvokeWithWaitDialog(applyFix, title, waitDialogMessage);
            return result == WaitIndicatorResult.Completed;
        }
        /// <summary>
        /// Updating the repo:
        /// 1. load saved share meta data (meta data will be as current as last time online user was online)
        /// 2. load current state of share folder
        /// 3. compare the two sets for adds/mods/deletes
        /// 4. push changes to the server, updating meta data on success
        ///    a. if there is a conflicted file, copy it locally using name server gives
        /// 5. ask server for current file list
        /// 6. compare this list against local share
        /// 7. download out-of-date and new files, updating meta data as we go
        /// 8. write out meta data
        /// </summary>
        public void SyncWithTheCloud()
        {
            // 1. load saved share meta data (meta data will be as current as last time online user was online)
            ShareMetaData = this.loadMetaData();

            // 2. load current state of share folder
            LocalShare = this.GetFiles();

            // 3. compare the two sets for adds/mods/deletes
            HashSet<File> changeSet = this.GetChangeSet(this.LocalShare, this.ShareMetaData, new StartupComparer());

            // 4. push changes to the server, updating meta data on success
            foreach (File f in changeSet)
            {
                // differentiate deletes from adds
                if (f.content == null) // delete
                {
                    File deleteResult = OrangeCloudServer.DeleteFile(f);

                    // if the file is null, the server didnt respond
                    // we'll do nothing now and try to push again later
                    if (deleteResult == null)
                    {
                        continue;
                    }
                    // if server returns -1, we have a conflict
                    else if (deleteResult.version == -1)
                    {
                        // do something
                    }
                    // operation was successful with server
                    else
                    {
                        // update file meta data
                        File metaFile = ShareMetaData.Where(file => file.fullPath == f.fullPath).First();

                        if (metaFile != null)
                        {
                            ShareMetaData.Remove(metaFile);
                        }
                    }
                }
                else // add
                {
                    File addResult = OrangeCloudServer.Add(f);

                    // if the file is null, the server didnt respond
                    // we'll do nothing now and try to push again later
                    if (addResult == null)
                    {
                        continue;
                    }
                    // if server returns -1, we have a conflict
                    else if (addResult.version == -1)
                    {
                        // do something
                    }
                    // operation was successful with server
                    else
                    {
                        // update file meta data

                        // if the file is in the meta data already
                        if (ShareMetaData.Contains(f))
                        {
                            // update the version
                            ShareMetaData.SingleOrDefault(file => file.fullPath == f.fullPath).version = addResult.version;
                        }
                        // add it to the meta data
                        else
                        {
                            f.version = addResult.version;
                            ShareMetaData.Add(f);
                        }
                        
                    }
                }

            }

            changeSet.RemoveWhere(files => files.content == null);

            // 5. ask server for current file list
            HashSet<File> ServerList = OrangeCloudServer.GetFiles();

            // 6. compare this list against local share
            HashSet<File> newFiles = this.GetChangeSet(ServerList, LocalShare, new DefaultComparer());

            // 7. download out-of-date and new files, updating meta data as we go
            this.DownloadNewFiles(newFiles);

            // 8. write out meta data
            System.IO.File.WriteAllText(Environment.CurrentDirectory + "\\metadata.json", JsonConvert.SerializeObject(ShareMetaData));

        }
        /// <summary>
        /// Validate And Import Products With Variants
        /// </summary>
        /// <param name="spreadsheet"></param>
        /// <param name="parseErrors"></param>
        /// <returns></returns>
        public HashSet<ProductImportDataTransferObject> ValidateAndImportProductsWithVariants(ExcelPackage spreadsheet, ref Dictionary<string, List<string>> parseErrors)
        {
            var productsToImport = new HashSet<ProductImportDataTransferObject>();

            if (!parseErrors.Any())
            {
                if (spreadsheet != null)
                {
                    if (spreadsheet.Workbook != null)
                    {
                        var worksheet = spreadsheet.Workbook.Worksheets.SingleOrDefault(x => x.Name == "Items");
                        if (worksheet != null)
                        {
                            var totalRows = worksheet.Dimension.End.Row;
                            for (var rowId = 2; rowId <= totalRows; rowId++)
                            {
                                if (!worksheet.GetValue<string>(rowId, 1).HasValue() &&
                                    !worksheet.GetValue<string>(rowId, 2).HasValue() &&
                                    !worksheet.GetValue<string>(rowId, 3).HasValue()) continue;

                                var product = new ProductImportDataTransferObject();

                                //Prepare handle name for storing and grouping errors
                                string url = worksheet.GetValue<string>(rowId, 1),
                                       name = worksheet.GetValue<string>(rowId, 2);
                                var handle = url.HasValue() ? url : SeoHelper.TidyUrl(name);

                                if (!productsToImport.Any(x => x.Name == name || x.UrlSegment == url))
                                {
                                    if (parseErrors.All(x => x.Key != handle))
                                        parseErrors.Add(handle, new List<string>());

                                    product.UrlSegment = worksheet.GetValue<string>(rowId, 1).HasValue()
                                        ? worksheet.GetValue<string>(rowId, 1)
                                        : _urlService.Suggest(null,
                                            new SuggestParams
                                            {
                                                PageName = name,
                                                DocumentType = typeof (Product).FullName
                                            });
                                    //skip duplicate url
                                    if (productsToImport.Any(x => x.UrlSegment == product.UrlSegment))
                                        continue;

                                    GetBasicData(parseErrors, worksheet, rowId, product, handle);

                                    GetCategories(parseErrors, worksheet, rowId, product, handle);

                                    GetSpecifications(parseErrors, worksheet, rowId, handle, product);

                                    GetImages(worksheet, rowId, product);

                                    GetUrlHistory(parseErrors, worksheet, rowId, product, handle);

                                    productsToImport.Add(product);
                                }
                                else
                                {
                                    product = !string.IsNullOrWhiteSpace(url) ? 
                                        productsToImport.SingleOrDefault(x => x.Name == name && x.UrlSegment == url) 
                                        : productsToImport.SingleOrDefault(x => x.Name == name);
                                }

                                //Variants
                                if (product != null)
                                {
                                    var productVariant = GetProductVariant(parseErrors, worksheet, rowId, handle);
                                    if (productVariant != null)
                                    {
                                        //Options
                                        GetProductVariantOptions(worksheet, rowId, productVariant);

                                        //Price Breaks
                                        GetPriceBreaks(parseErrors, worksheet, rowId, handle, productVariant);

                                        product.ProductVariants.Add(productVariant);
                                    }
                                }
                            }
                        }
                    }
                }

                //Remove handles with no errors
                parseErrors = parseErrors.Where(x => x.Value.Any()).ToDictionary(pair => pair.Key, pair => pair.Value);
            }
            var i = productsToImport.Where(x => x.ProductVariants.Count == 0).ToList();
            return productsToImport;
        }
        public void TypeList_Resolves_Explicit_Types()
        {
            var types = new HashSet<PluginManager.TypeList>();

            var propEditors = new PluginManager.TypeList<PropertyEditor>(PluginManager.TypeResolutionKind.FindAllTypes);
            propEditors.AddType(typeof(LabelPropertyEditor));
            types.Add(propEditors);

            var found = types.SingleOrDefault(x => x.IsTypeList<PropertyEditor>(PluginManager.TypeResolutionKind.FindAllTypes));

            Assert.IsNotNull(found);

            //This should not find a type list of this type
            var shouldNotFind = types.SingleOrDefault(x => x.IsTypeList<IParameterEditor>(PluginManager.TypeResolutionKind.FindAllTypes));

            Assert.IsNull(shouldNotFind);
        }
        public void HashSetExtensions_SingleOrDefault_ThrowsExceptionIfHashSetHasMultipleItems()
        {
            var set = new HashSet<Int32>() { 1, 2 };

            Assert.That(() => set.SingleOrDefault(),
                Throws.TypeOf<InvalidOperationException>());
        }
        public void HashSetExtensions_SingleOrDefault_ReturnsSingleItemInHashSet()
        {
            var set = new HashSet<Int32>() { 4 };

            var result = set.SingleOrDefault();

            TheResultingValue(result).ShouldBe(4);
        }
        public void HashSetExtensions_SingleOrDefault_ReturnsDefaultValueIfHashSetIsEmpty()
        {
            var set = new HashSet<Int32>();

            var result = set.SingleOrDefault();

            TheResultingValue(result).ShouldBe(default(Int32));
        }
        public void HashSetExtensions_SingleOrDefault_ThrowsExceptionIfHashSetHasMultipleItems()
        {
            var set = new HashSet<Int32>() { 1, 2 };

            set.SingleOrDefault();
        }