protected override IList<FileChange> GetFileChanges()
        {
            var fileChangeMap = new Dictionary<string, FileChange>();

            using (WaitCursorHelper.NewWaitCursor())
            {
                var artifact = _contributorInput.ObjectToBeRenamed.Artifact;
                var artifactProjectItem = VsUtils.GetProjectItemForDocument(artifact.Uri.LocalPath, Services.ServiceProvider);

                if (artifactProjectItem != null)
                {
                    // Run the custom tool to ensure the generated code is up-to-date
                    VsUtils.RunCustomTool(artifactProjectItem);
                    var generatedCodeProjectItem = VsUtils.GetGeneratedCodeProjectItem(artifactProjectItem);

                    if (generatedCodeProjectItem != null)
                    {
                        var generatedItemPath = generatedCodeProjectItem.get_FileNames(1);
                        var objectSearchLanguage = FileExtensions.VbExt.Equals(
                            Path.GetExtension(generatedItemPath), StringComparison.OrdinalIgnoreCase)
                                                       ? ObjectSearchLanguage.VB
                                                       : ObjectSearchLanguage.CSharp;

                        var codeElements = generatedCodeProjectItem.FileCodeModel.CodeElements.OfType<CodeElement2>();
                        CodeElementRenameData renameData = null;

                        // Check if we're refactoring an EntityType or Property
                        var entityType = _contributorInput.ObjectToBeRenamed as EntityType;
                        var property = _contributorInput.ObjectToBeRenamed as Property;

                        if (entityType != null)
                        {
                            var newEntitySetName = _contributorInput.NewName;
                            var pluralize = ModelHelper.GetDesignerPropertyValueFromArtifactAsBool(
                                OptionsDesignerInfo.ElementName, OptionsDesignerInfo.AttributeEnablePluralization,
                                OptionsDesignerInfo.EnablePluralizationDefault, artifact);

                            // Pluralize the entity set name if the setting it turned on
                            if (pluralize)
                            {
                                var pluralizationService = DependencyResolver.GetService<IPluralizationService>();
                                newEntitySetName = pluralizationService.Pluralize(_contributorInput.NewName);
                            }

                            renameData = new CodeElementRenameData(
                                _contributorInput.NewName, newEntitySetName, _contributorInput.OldName, entityType.EntitySet.Name.Value);
                        }
                        else if (property != null)
                        {
                            if (property.EntityType != null)
                            {
                                renameData = new CodeElementRenameData(
                                    _contributorInput.NewName, _contributorInput.OldName, property.EntityType.Name.Value);
                            }
                        }

                        if (renameData != null)
                        {
                            var codeElementsToRename = new Dictionary<CodeElement2, Tuple<string, string>>();
                            CodeElementUtilities.FindRootCodeElementsToRename(
                                codeElements, renameData, generatedItemPath, objectSearchLanguage, ref codeElementsToRename);
                            var changeProposals = new List<ChangeProposal>();

                            // We may need to rename more than one object, as type names affect functions and entity set properties. This means we need to loop through and
                            // process each root item change in the generated code designer file.
                            foreach (var codeElementToRename in codeElementsToRename.Keys)
                            {
                                var nameTuple = codeElementsToRename[codeElementToRename];

                                CodeElementUtilities.CreateChangeProposals(
                                    codeElementToRename, nameTuple.Item1, nameTuple.Item2, generatedItemPath,
                                    changeProposals, objectSearchLanguage);
                            }

                            // Now sort the change proposals by filename so that we can return a list of file changes
                            foreach (var changeProposal in changeProposals)
                            {
                                FileChange fileChange;
                                HashSet<ChangeProposal> fileChangeProposals;

                                if (fileChangeMap.TryGetValue(changeProposal.FileName, out fileChange))
                                {
                                    fileChangeProposals = fileChange.ChangeList.Values.First();
                                }
                                else
                                {
                                    fileChange = new FileChange(changeProposal.FileName);
                                    fileChangeProposals = new HashSet<ChangeProposal>();
                                    fileChange.ChangeList.Add(
                                        new KeyValuePair<RefactoringPreviewGroup, HashSet<ChangeProposal>>(
                                            new RefactoringPreviewGroup(Resources.RefactorPreviewGroupName), fileChangeProposals));
                                    fileChangeMap.Add(changeProposal.FileName, fileChange);
                                }

                                if (!fileChangeProposals.Contains(changeProposal))
                                {
                                    fileChangeProposals.Add(changeProposal);
                                }
                            }
                        }
                    }
                }
            }

            return fileChangeMap.Values.ToList();
        }
        internal static void FindRootCodeElementsToRename(
            IEnumerable <CodeElement2> codeElements,
            CodeElementRenameData renameData,
            string generatedItemPath,
            ObjectSearchLanguage objectSearchLanguage,
            ref Dictionary <CodeElement2, Tuple <string, string> > codeElementsToRename)
        {
            if (codeElementsToRename == null)
            {
                codeElementsToRename = new Dictionary <CodeElement2, Tuple <string, string> >();
            }

            var newName    = renameData.NewName;
            var oldName    = renameData.OldName;
            var targetType = renameData.RefactorTargetType;

            // Rename the symbols that match the old name
            foreach (var codeElement in codeElements)
            {
                if (codeElement.Kind == vsCMElement.vsCMElementNamespace)
                {
                    FindRootCodeElementsToRename(
                        codeElement.Children.OfType <CodeElement2>(), renameData, generatedItemPath, objectSearchLanguage,
                        ref codeElementsToRename);
                }
                else if (codeElement.Kind == vsCMElement.vsCMElementClass)
                {
                    if (targetType == RefactorTargetType.Class)
                    {
                        if (codeElement.Name.Equals(oldName, StringComparison.Ordinal))
                        {
                            codeElementsToRename.Add(codeElement, new Tuple <string, string>(newName, oldName));
                        }

                        // If we're refactoring a class, we need to iterate another level deeper even if the name of this Type doesn't match what we're refactoring
                        // since we need to rename the AddTo*() functions
                        FindRootCodeElementsToRename(
                            codeElement.Children.OfType <CodeElement2>(), renameData, generatedItemPath, objectSearchLanguage,
                            ref codeElementsToRename);
                    }
                    else if (targetType == RefactorTargetType.Property)
                    {
                        // If we're refactoring a property we need to iterate another level deeper to find the property elements.
                        FindRootCodeElementsToRename(
                            codeElement.Children.OfType <CodeElement2>(), renameData, generatedItemPath, objectSearchLanguage,
                            ref codeElementsToRename);
                    }
                }
                else if (codeElement.Kind == vsCMElement.vsCMElementFunction &&
                         targetType == RefactorTargetType.Class)
                {
                    // Functions use the entity set name, so check for pluralization
                    string oldFuncName;
                    string newFuncName;

                    var function           = (CodeFunction)codeElement;
                    var parentType         = function.Parent as CodeType;
                    var oldFactoryFuncName = string.Format(CultureInfo.InvariantCulture, CreateFuncFormat, oldName);
                    if (parentType != null &&
                        parentType.Name.Equals(oldName, StringComparison.Ordinal) &&
                        function.Name.Equals(oldFactoryFuncName, StringComparison.Ordinal))
                    {
                        // We need to rename Create* factory funcs as they use the class name.
                        codeElementsToRename.Add(
                            codeElement,
                            new Tuple <string, string>(
                                string.Format(CultureInfo.InvariantCulture, CreateFuncFormat, newName), oldFactoryFuncName));
                    }
                    else
                    {
                        // We also need to rename the AddTo* funcs when we rename classes, since the class name is used in the generated func name.
                        oldFuncName = string.Format(CultureInfo.InvariantCulture, AddToFuncFormat, renameData.OldEntitySetName);

                        if (codeElement.Name.Equals(oldFuncName, StringComparison.Ordinal))
                        {
                            newFuncName = string.Format(CultureInfo.InvariantCulture, AddToFuncFormat, renameData.NewEntitySetName);
                            codeElementsToRename.Add(codeElement, new Tuple <string, string>(newFuncName, oldFuncName));
                        }
                    }
                }
                else if (codeElement.Kind == vsCMElement.vsCMElementProperty)
                {
                    if (targetType == RefactorTargetType.Property)
                    {
                        if (codeElement.Name.Equals(oldName, StringComparison.Ordinal))
                        {
                            // Ensure the class name matches as well
                            var splitFullName = codeElement.FullName.Split('.');

                            if (splitFullName.Length >= 2 &&
                                splitFullName[splitFullName.Length - 2].Equals(renameData.ParentEntityTypeName, StringComparison.Ordinal))
                            {
                                codeElementsToRename.Add(codeElement, new Tuple <string, string>(newName, oldName));
                            }
                        }
                    }
                    else if (targetType == RefactorTargetType.Class)
                    {
                        // There is a property on the container which contains the entity set.
                        if (codeElement.Name.Equals(renameData.OldEntitySetName, StringComparison.Ordinal))
                        {
                            codeElementsToRename.Add(
                                codeElement, new Tuple <string, string>(renameData.NewEntitySetName, renameData.OldEntitySetName));
                        }
                    }
                }
            }
        }
Exemple #3
0
        protected override IList <FileChange> GetFileChanges()
        {
            var fileChangeMap = new Dictionary <string, FileChange>();

            using (WaitCursorHelper.NewWaitCursor())
            {
                var artifact            = _contributorInput.ObjectToBeRenamed.Artifact;
                var artifactProjectItem = VsUtils.GetProjectItemForDocument(artifact.Uri.LocalPath, Services.ServiceProvider);

                if (artifactProjectItem != null)
                {
                    // Run the custom tool to ensure the generated code is up-to-date
                    VsUtils.RunCustomTool(artifactProjectItem);
                    var generatedCodeProjectItem = VsUtils.GetGeneratedCodeProjectItem(artifactProjectItem);

                    if (generatedCodeProjectItem != null)
                    {
                        var generatedItemPath    = generatedCodeProjectItem.get_FileNames(1);
                        var objectSearchLanguage = FileExtensions.VbExt.Equals(
                            Path.GetExtension(generatedItemPath), StringComparison.OrdinalIgnoreCase)
                                                       ? ObjectSearchLanguage.VB
                                                       : ObjectSearchLanguage.CSharp;

                        var codeElements = generatedCodeProjectItem.FileCodeModel.CodeElements.OfType <CodeElement2>();
                        CodeElementRenameData renameData = null;

                        // Check if we're refactoring an EntityType or Property
                        var entityType = _contributorInput.ObjectToBeRenamed as EntityType;
                        var property   = _contributorInput.ObjectToBeRenamed as Property;

                        if (entityType != null)
                        {
                            var newEntitySetName = _contributorInput.NewName;
                            var pluralize        = ModelHelper.GetDesignerPropertyValueFromArtifactAsBool(
                                OptionsDesignerInfo.ElementName, OptionsDesignerInfo.AttributeEnablePluralization,
                                OptionsDesignerInfo.EnablePluralizationDefault, artifact);

                            // Pluralize the entity set name if the setting it turned on
                            if (pluralize)
                            {
                                var pluralizationService = DependencyResolver.GetService <IPluralizationService>();
                                newEntitySetName = pluralizationService.Pluralize(_contributorInput.NewName);
                            }

                            renameData = new CodeElementRenameData(
                                _contributorInput.NewName, newEntitySetName, _contributorInput.OldName, entityType.EntitySet.Name.Value);
                        }
                        else if (property != null)
                        {
                            if (property.EntityType != null)
                            {
                                renameData = new CodeElementRenameData(
                                    _contributorInput.NewName, _contributorInput.OldName, property.EntityType.Name.Value);
                            }
                        }

                        if (renameData != null)
                        {
                            var codeElementsToRename = new Dictionary <CodeElement2, Tuple <string, string> >();
                            CodeElementUtilities.FindRootCodeElementsToRename(
                                codeElements, renameData, generatedItemPath, objectSearchLanguage, ref codeElementsToRename);
                            var changeProposals = new List <ChangeProposal>();

                            // We may need to rename more than one object, as type names affect functions and entity set properties. This means we need to loop through and
                            // process each root item change in the generated code designer file.
                            foreach (var codeElementToRename in codeElementsToRename.Keys)
                            {
                                var nameTuple = codeElementsToRename[codeElementToRename];

                                CodeElementUtilities.CreateChangeProposals(
                                    codeElementToRename, nameTuple.Item1, nameTuple.Item2, generatedItemPath,
                                    changeProposals, objectSearchLanguage);
                            }

                            // Now sort the change proposals by filename so that we can return a list of file changes
                            foreach (var changeProposal in changeProposals)
                            {
                                FileChange fileChange;
                                HashSet <ChangeProposal> fileChangeProposals;

                                if (fileChangeMap.TryGetValue(changeProposal.FileName, out fileChange))
                                {
                                    fileChangeProposals = fileChange.ChangeList.Values.First();
                                }
                                else
                                {
                                    fileChange          = new FileChange(changeProposal.FileName);
                                    fileChangeProposals = new HashSet <ChangeProposal>();
                                    fileChange.ChangeList.Add(
                                        new KeyValuePair <RefactoringPreviewGroup, HashSet <ChangeProposal> >(
                                            new RefactoringPreviewGroup(Resources.RefactorPreviewGroupName), fileChangeProposals));
                                    fileChangeMap.Add(changeProposal.FileName, fileChange);
                                }

                                if (!fileChangeProposals.Contains(changeProposal))
                                {
                                    fileChangeProposals.Add(changeProposal);
                                }
                            }
                        }
                    }
                }
            }

            return(fileChangeMap.Values.ToList());
        }
        internal static void FindRootCodeElementsToRename(
            IEnumerable<CodeElement2> codeElements,
            CodeElementRenameData renameData,
            string generatedItemPath,
            ObjectSearchLanguage objectSearchLanguage,
            ref Dictionary<CodeElement2, Tuple<string, string>> codeElementsToRename)
        {
            if (codeElementsToRename == null)
            {
                codeElementsToRename = new Dictionary<CodeElement2, Tuple<string, string>>();
            }

            var newName = renameData.NewName;
            var oldName = renameData.OldName;
            var targetType = renameData.RefactorTargetType;

            // Rename the symbols that match the old name
            foreach (var codeElement in codeElements)
            {
                if (codeElement.Kind == vsCMElement.vsCMElementNamespace)
                {
                    FindRootCodeElementsToRename(
                        codeElement.Children.OfType<CodeElement2>(), renameData, generatedItemPath, objectSearchLanguage,
                        ref codeElementsToRename);
                }
                else if (codeElement.Kind == vsCMElement.vsCMElementClass)
                {
                    if (targetType == RefactorTargetType.Class)
                    {
                        if (codeElement.Name.Equals(oldName, StringComparison.Ordinal))
                        {
                            codeElementsToRename.Add(codeElement, new Tuple<string, string>(newName, oldName));
                        }

                        // If we're refactoring a class, we need to iterate another level deeper even if the name of this Type doesn't match what we're refactoring
                        // since we need to rename the AddTo*() functions
                        FindRootCodeElementsToRename(
                            codeElement.Children.OfType<CodeElement2>(), renameData, generatedItemPath, objectSearchLanguage,
                            ref codeElementsToRename);
                    }
                    else if (targetType == RefactorTargetType.Property)
                    {
                        // If we're refactoring a property we need to iterate another level deeper to find the property elements.
                        FindRootCodeElementsToRename(
                            codeElement.Children.OfType<CodeElement2>(), renameData, generatedItemPath, objectSearchLanguage,
                            ref codeElementsToRename);
                    }
                }
                else if (codeElement.Kind == vsCMElement.vsCMElementFunction
                         && targetType == RefactorTargetType.Class)
                {
                    // Functions use the entity set name, so check for pluralization
                    string oldFuncName;
                    string newFuncName;

                    var function = (CodeFunction)codeElement;
                    var parentType = function.Parent as CodeType;
                    var oldFactoryFuncName = string.Format(CultureInfo.InvariantCulture, CreateFuncFormat, oldName);
                    if (parentType != null
                        && parentType.Name.Equals(oldName, StringComparison.Ordinal)
                        && function.Name.Equals(oldFactoryFuncName, StringComparison.Ordinal))
                    {
                        // We need to rename Create* factory funcs as they use the class name.
                        codeElementsToRename.Add(
                            codeElement,
                            new Tuple<string, string>(
                                string.Format(CultureInfo.InvariantCulture, CreateFuncFormat, newName), oldFactoryFuncName));
                    }
                    else
                    {
                        // We also need to rename the AddTo* funcs when we rename classes, since the class name is used in the generated func name.
                        oldFuncName = string.Format(CultureInfo.InvariantCulture, AddToFuncFormat, renameData.OldEntitySetName);

                        if (codeElement.Name.Equals(oldFuncName, StringComparison.Ordinal))
                        {
                            newFuncName = string.Format(CultureInfo.InvariantCulture, AddToFuncFormat, renameData.NewEntitySetName);
                            codeElementsToRename.Add(codeElement, new Tuple<string, string>(newFuncName, oldFuncName));
                        }
                    }
                }
                else if (codeElement.Kind == vsCMElement.vsCMElementProperty)
                {
                    if (targetType == RefactorTargetType.Property)
                    {
                        if (codeElement.Name.Equals(oldName, StringComparison.Ordinal))
                        {
                            // Ensure the class name matches as well
                            var splitFullName = codeElement.FullName.Split('.');

                            if (splitFullName.Length >= 2
                                && splitFullName[splitFullName.Length - 2].Equals(renameData.ParentEntityTypeName, StringComparison.Ordinal))
                            {
                                codeElementsToRename.Add(codeElement, new Tuple<string, string>(newName, oldName));
                            }
                        }
                    }
                    else if (targetType == RefactorTargetType.Class)
                    {
                        // There is a property on the container which contains the entity set.
                        if (codeElement.Name.Equals(renameData.OldEntitySetName, StringComparison.Ordinal))
                        {
                            codeElementsToRename.Add(
                                codeElement, new Tuple<string, string>(renameData.NewEntitySetName, renameData.OldEntitySetName));
                        }
                    }
                }
            }
        }