Skip to content

Commit

Permalink
Merge pull request #5578 from BZngr/EF_SplitRefactoring
Browse files Browse the repository at this point in the history
EncapsulateFieldRefactoring - Split Refactoring
  • Loading branch information
bclothier committed Oct 24, 2020
2 parents 136889f + 9db29d9 commit 3c10fca
Show file tree
Hide file tree
Showing 87 changed files with 6,043 additions and 2,933 deletions.
22 changes: 22 additions & 0 deletions Rubberduck.Main/Root/RubberduckIoCInstaller.cs
Expand Up @@ -380,7 +380,14 @@ private void RegisterSpecialFactories(IWindsorContainer container)
container.Register(Component.For<IAnnotationArgumentViewModelFactory>()
.ImplementedBy<AnnotationArgumentViewModelFactory>()
.LifestyleSingleton());

container.Register(Component.For<IReplacePrivateUDTMemberReferencesModelFactory>()
.ImplementedBy<ReplacePrivateUDTMemberReferencesModelFactory>()
.LifestyleSingleton());

RegisterUnreachableCaseFactories(container);

RegisterEncapsulateFieldRefactoringFactories(container);
}

private void RegisterUnreachableCaseFactories(IWindsorContainer container)
Expand All @@ -390,6 +397,21 @@ private void RegisterUnreachableCaseFactories(IWindsorContainer container)
.LifestyleSingleton());
}

private void RegisterEncapsulateFieldRefactoringFactories(IWindsorContainer container)
{
container.Register(Component.For<IEncapsulateFieldCandidateFactory>()
.ImplementedBy<EncapsulateFieldCandidateFactory>()
.LifestyleSingleton());
container.Register(Component.For<IEncapsulateFieldUseBackingUDTMemberModelFactory>()
.ImplementedBy<EncapsulateFieldUseBackingUDTMemberModelFactory>()
.LifestyleSingleton());
container.Register(Component.For<IEncapsulateFieldUseBackingFieldModelFactory>()
.ImplementedBy<EncapsulateFieldUseBackingFieldModelFactory>()
.LifestyleSingleton());
container.Register(Component.For<IEncapsulateFieldModelFactory>()
.ImplementedBy<EncapsulateFieldModelFactory>()
.LifestyleSingleton());
}

private void RegisterQuickFixes(IWindsorContainer container, Assembly[] assembliesToRegister)
{
Expand Down
Expand Up @@ -20,7 +20,7 @@ public abstract class RefactoringPreviewProviderWrapperBase<TModel> : IRefactori

protected abstract QualifiedModuleName ComponentToShow(TModel model);

public string Preview(TModel model)
public virtual string Preview(TModel model)
{
var rewriteSession = RewriteSession(RewriteSessionCodeKind);
_refactoringAction.Refactor(model, rewriteSession);
Expand Down
259 changes: 165 additions & 94 deletions Rubberduck.Refactorings/Common/CodeBuilder.cs

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -0,0 +1,65 @@
using Rubberduck.Parsing.Symbols;
using Rubberduck.Parsing.VBA;
using Rubberduck.VBEditor;
using System.Collections.Generic;
using System.Linq;

namespace Rubberduck.Refactorings.EncapsulateField
{
public interface IEncapsulateFieldCandidateSetsProviderFactory
{
IEncapsulateFieldCandidateSetsProvider Create(IDeclarationFinderProvider declarationFinderProvider,
IEncapsulateFieldCandidateFactory encapsulateFieldCandidateFactory,
QualifiedModuleName qualifiedModuleName);
}

public interface IEncapsulateFieldCandidateSetsProvider
{
IReadOnlyCollection<IEncapsulateFieldCandidate> EncapsulateFieldUseBackingFieldCandidates { get; }
IReadOnlyCollection<IEncapsulateFieldAsUDTMemberCandidate> EncapsulateFieldUseBackingUDTMemberCandidates { get; }
IReadOnlyCollection<IObjectStateUDT> ObjectStateFieldCandidates { get; }
}

/// <summary>
/// EncapsulateFieldCandidateSetsProvider provides access to a sets of
/// EncapsulateField candidate instances to be shared among EncapsulateFieldRefactoringActions.
/// </summary>
public class EncapsulateFieldCandidateSetsProvider : IEncapsulateFieldCandidateSetsProvider
{
public EncapsulateFieldCandidateSetsProvider(IDeclarationFinderProvider declarationFinderProvider,
IEncapsulateFieldCandidateFactory encapsulateFieldCandidateFactory,
QualifiedModuleName qualifiedModuleName)
{
EncapsulateFieldUseBackingFieldCandidates = declarationFinderProvider.DeclarationFinder.Members(qualifiedModuleName, DeclarationType.Variable)
.Where(v => v.ParentDeclaration is ModuleDeclaration
&& !v.IsWithEvents)
.Select(f => encapsulateFieldCandidateFactory.CreateFieldCandidate(f))
.ToList();

var objectStateUDTCandidates = EncapsulateFieldUseBackingFieldCandidates
.OfType<IUserDefinedTypeCandidate>()
.Where(fc => fc.Declaration.Accessibility == Accessibility.Private
&& fc.Declaration.AsTypeDeclaration.Accessibility == Accessibility.Private)
.Select(udtc => encapsulateFieldCandidateFactory.CreateObjectStateField(udtc))
//If multiple fields of the same UserDefinedType exist, they are all disqualified as candidates to host a module's state.
.ToLookup(objectStateUDTCandidate => objectStateUDTCandidate.Declaration.AsTypeDeclaration.IdentifierName)
.Where(osc => osc.Count() == 1)
.SelectMany(osc => osc)
.ToList();

var defaultObjectStateUDT = encapsulateFieldCandidateFactory.CreateDefaultObjectStateField(qualifiedModuleName);
objectStateUDTCandidates.Add(defaultObjectStateUDT);
ObjectStateFieldCandidates = objectStateUDTCandidates;

EncapsulateFieldUseBackingUDTMemberCandidates = EncapsulateFieldUseBackingFieldCandidates
.Select(fc => encapsulateFieldCandidateFactory.CreateUDTMemberCandidate(fc, defaultObjectStateUDT))
.ToList();
}

public IReadOnlyCollection<IEncapsulateFieldCandidate> EncapsulateFieldUseBackingFieldCandidates { get; }

public IReadOnlyCollection<IEncapsulateFieldAsUDTMemberCandidate> EncapsulateFieldUseBackingUDTMemberCandidates { get; }

public IReadOnlyCollection<IObjectStateUDT> ObjectStateFieldCandidates { get; }
}
}

This file was deleted.

@@ -0,0 +1,95 @@
using Rubberduck.Parsing.Grammar;
using Rubberduck.Parsing.Symbols;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Rubberduck.Refactorings.EncapsulateField
{
public interface IEncapsulateFieldCodeBuilder
{
(string Get, string Let, string Set) BuildPropertyBlocks(PropertyAttributeSet propertyAttributeSet);
string BuildUserDefinedTypeDeclaration(IObjectStateUDT objectStateUDT, IEnumerable<IEncapsulateFieldCandidate> candidates);
string BuildObjectStateFieldDeclaration(IObjectStateUDT objectStateUDT);
string BuildFieldDeclaration(Declaration target, string identifier);
}

/// <summary>
/// EncapsulateFieldCodeBuilder wraps an ICodeBuilder instance to extend it for the
/// specific needs of an EncapsulateField refactoring action.
/// </summary>
public class EncapsulateFieldCodeBuilder : IEncapsulateFieldCodeBuilder
{
private readonly ICodeBuilder _codeBuilder;

public EncapsulateFieldCodeBuilder(ICodeBuilder codeBuilder)
{
_codeBuilder = codeBuilder;
}

public (string Get, string Let, string Set) BuildPropertyBlocks(PropertyAttributeSet propertyAttributes)
{
if (!(propertyAttributes.Declaration.DeclarationType.HasFlag(DeclarationType.Variable)
|| propertyAttributes.Declaration.DeclarationType.HasFlag(DeclarationType.UserDefinedTypeMember)))
{
throw new ArgumentException("Invalid prototype DeclarationType", nameof(propertyAttributes));
}

(string Get, string Let, string Set) blocks = (string.Empty, string.Empty, string.Empty);

var mutatorBody = $"{propertyAttributes.BackingField} = {propertyAttributes.RHSParameterIdentifier}";

if (propertyAttributes.GeneratePropertyLet)
{
_codeBuilder.TryBuildPropertyLetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out blocks.Let, content: mutatorBody);
}

if (propertyAttributes.GeneratePropertySet)
{
_codeBuilder.TryBuildPropertySetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out blocks.Set, content: $"{Tokens.Set} {mutatorBody}");
}

var propertyGetBody = propertyAttributes.UsesSetAssignment
? $"{Tokens.Set} {propertyAttributes.PropertyName} = {propertyAttributes.BackingField}"
: $"{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}";

if (propertyAttributes.AsTypeName.Equals(Tokens.Variant) && !propertyAttributes.Declaration.IsArray)
{
propertyGetBody = string.Join(
$"{Tokens.If} IsObject({propertyAttributes.BackingField}) {Tokens.Then}",
$"{Tokens.Set} {propertyAttributes.PropertyName} = {propertyAttributes.BackingField}",
Tokens.Else,
$"{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}",
$"{Tokens.End} {Tokens.If}");
}

_codeBuilder.TryBuildPropertyGetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out blocks.Get, content: propertyGetBody);

return (blocks.Get, blocks.Let, blocks.Set);
}

public string BuildUserDefinedTypeDeclaration(IObjectStateUDT objectStateUDT, IEnumerable<IEncapsulateFieldCandidate> candidates)
{
var newUDTMembers = candidates.Where(c => c.EncapsulateFlag)
.Select(m => (m.Declaration, m.BackingIdentifier));

if (_codeBuilder.TryBuildUserDefinedTypeDeclaration(objectStateUDT.AsTypeName, newUDTMembers, out var declaration))
{
return declaration;
}

return string.Empty;
}

public string BuildObjectStateFieldDeclaration(IObjectStateUDT objectStateUDT)
=> $"{Accessibility.Private} {objectStateUDT.IdentifierName} {Tokens.As} {objectStateUDT.AsTypeName}";

public string BuildFieldDeclaration(Declaration target, string identifier)
{
var identifierExpressionSansVisibility = target.Context.GetText().Replace(target.IdentifierName, identifier);
return target.IsTypeSpecified
? $"{Tokens.Private} {identifierExpressionSansVisibility}"
: $"{Tokens.Private} {identifierExpressionSansVisibility} {Tokens.As} {target.AsTypeName}";
}
}
}

0 comments on commit 3c10fca

Please sign in to comment.