diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitByRefModifierInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitByRefModifierInspection.cs
index 686a8fb83d..72a01905bb 100644
--- a/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitByRefModifierInspection.cs
+++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitByRefModifierInspection.cs
@@ -3,6 +3,7 @@
using Rubberduck.Parsing.VBA;
using Rubberduck.Parsing.VBA.DeclarationCaching;
using Rubberduck.Resources.Inspections;
+using System.Linq;
namespace Rubberduck.CodeAnalysis.Inspections.Concrete
{
@@ -10,8 +11,11 @@ namespace Rubberduck.CodeAnalysis.Inspections.Concrete
/// Highlights implicit ByRef modifiers in user code.
///
///
- /// In modern VB (VB.NET), the implicit modifier is ByVal, as it is in most other programming languages.
- /// Making the ByRef modifiers explicit can help surface potentially unexpected language defaults.
+ /// VBA parameters are implicitly ByRef, which differs from modern VB (VB.NET) and most other programming languages which are implicitly ByVal.
+ /// So, explicitly identifing VBA parameter mechanisms (the ByRef and ByVal modifiers) can help surface potentially unexpected language results.
+ /// The inspection does not flag an implicit parameter mechanism for the last parameter of Property mutators (Let or Set).
+ /// VBA applies a ByVal parameter mechanism to the last parameter in the absence (or presence!) of a modifier.
+ /// Exception: UserDefinedType parameters must always be passed as ByRef.
///
///
///
@@ -31,6 +35,16 @@ namespace Rubberduck.CodeAnalysis.Inspections.Concrete
/// ]]>
///
///
+ ///
+ ///
+ ///
+ ///
+ ///
internal sealed class ImplicitByRefModifierInspection : DeclarationInspectionBase
{
public ImplicitByRefModifierInspection(IDeclarationFinderProvider declarationFinderProvider)
@@ -41,21 +55,23 @@ protected override bool IsResultDeclaration(Declaration declaration, Declaration
{
if (!(declaration is ParameterDeclaration parameter)
|| !parameter.IsImplicitByRef
- || parameter.IsParamArray)
+ || parameter.IsParamArray
+ //Exclude parameters of Declare statements
+ || !(parameter.ParentDeclaration is ModuleBodyElementDeclaration enclosingMethod))
{
return false;
}
- var parentDeclaration = parameter.ParentDeclaration;
-
- if (parentDeclaration is ModuleBodyElementDeclaration enclosingMethod)
- {
- return !enclosingMethod.IsInterfaceImplementation
- && !finder.FindEventHandlers().Contains(enclosingMethod);
- }
+ return !IsPropertyMutatorRHSParameter(enclosingMethod, parameter)
+ && !enclosingMethod.IsInterfaceImplementation
+ && !finder.FindEventHandlers().Contains(enclosingMethod);
+ }
- return parentDeclaration.DeclarationType != DeclarationType.LibraryFunction
- && parentDeclaration.DeclarationType != DeclarationType.LibraryProcedure;
+ private static bool IsPropertyMutatorRHSParameter(ModuleBodyElementDeclaration enclosingMethod, ParameterDeclaration implicitByRefParameter)
+ {
+ return (enclosingMethod.DeclarationType.HasFlag(DeclarationType.PropertyLet)
+ || enclosingMethod.DeclarationType.HasFlag(DeclarationType.PropertySet))
+ && enclosingMethod.Parameters.Last().Equals(implicitByRefParameter);
}
protected override string ResultDescription(Declaration declaration)
diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/MisleadingByRefParameterInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/MisleadingByRefParameterInspection.cs
new file mode 100644
index 0000000000..7c58237a9d
--- /dev/null
+++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/MisleadingByRefParameterInspection.cs
@@ -0,0 +1,68 @@
+using Rubberduck.CodeAnalysis.Inspections.Abstract;
+using Rubberduck.Parsing.Symbols;
+using Rubberduck.Parsing.VBA;
+using Rubberduck.Parsing.VBA.DeclarationCaching;
+using Rubberduck.Resources.Inspections;
+using System.Linq;
+
+namespace Rubberduck.CodeAnalysis.Inspections.Concrete
+{
+ ///
+ /// Flags the value-parameter of a property mutators that are declared with an explict ByRef modifier.
+ ///
+ ///
+ /// Regardless of the presence or absence of an explicit ByRef or ByVal modifier, the value-parameter
+ /// of a property mutator is always treated as though it had an explicit ByVal modifier.
+ /// Exception: UserDefinedType parameters are always passed by reference.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal sealed class MisleadingByRefParameterInspection : DeclarationInspectionBase
+ {
+ public MisleadingByRefParameterInspection(IDeclarationFinderProvider declarationFinderProvider)
+ : base(declarationFinderProvider, DeclarationType.Parameter)
+ { }
+
+ protected override bool IsResultDeclaration(Declaration declaration, DeclarationFinder finder)
+ {
+ return declaration is ParameterDeclaration parameter
+ && !(parameter.AsTypeDeclaration?.DeclarationType.HasFlag(DeclarationType.UserDefinedType) ?? false)
+ && parameter.ParentDeclaration is ModuleBodyElementDeclaration enclosingMethod
+ && (enclosingMethod.DeclarationType.HasFlag(DeclarationType.PropertyLet)
+ || enclosingMethod.DeclarationType.HasFlag(DeclarationType.PropertySet))
+ && enclosingMethod.Parameters.Last() == parameter
+ && parameter.IsByRef && !parameter.IsImplicitByRef;
+ }
+
+ protected override string ResultDescription(Declaration declaration)
+ {
+ return string.Format(
+ InspectionResults.MisleadingByRefParameterInspection,
+ declaration.IdentifierName, declaration.ParentDeclaration.QualifiedName.MemberName);
+ }
+ }
+}
diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/ThunderCode/NegativeLineNumberInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/ThunderCode/NegativeLineNumberInspection.cs
index d0f7eac9a8..76c391197a 100644
--- a/Rubberduck.CodeAnalysis/Inspections/Concrete/ThunderCode/NegativeLineNumberInspection.cs
+++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/ThunderCode/NegativeLineNumberInspection.cs
@@ -1,9 +1,12 @@
-using Antlr4.Runtime;
+using System.Linq;
+using Antlr4.Runtime;
using Antlr4.Runtime.Tree;
using Rubberduck.CodeAnalysis.Inspections.Abstract;
using Rubberduck.Parsing;
using Rubberduck.Parsing.Grammar;
+using Rubberduck.Parsing.Symbols;
using Rubberduck.Parsing.VBA;
+using Rubberduck.Parsing.VBA.DeclarationCaching;
using Rubberduck.Resources.Inspections;
namespace Rubberduck.CodeAnalysis.Inspections.Concrete.ThunderCode
@@ -31,6 +34,27 @@ protected override string ResultDescription(QualifiedContext
return InspectionResults.NegativeLineNumberInspection.ThunderCodeFormat();
}
+ protected override bool IsResultContext(QualifiedContext context, DeclarationFinder finder)
+ {
+ return !IsOnErrorGotoMinusOne(context.Context)
+ || ProcedureHasMinusOneLabel(finder, context);
+ }
+
+ private static bool IsOnErrorGotoMinusOne(ParserRuleContext context)
+ {
+ return context is VBAParser.OnErrorStmtContext onErrorStatement
+ && "-1".Equals(onErrorStatement.expression()?.GetText().Trim());
+ }
+
+ private static bool ProcedureHasMinusOneLabel(DeclarationFinder finder, QualifiedContext context)
+ {
+ return finder.Members(context.ModuleName, DeclarationType.LineLabel)
+ .Any(label => label.IdentifierName.Equals("-1")
+ && (label.ParentScopeDeclaration
+ .Context?.GetSelection()
+ .Contains(context.Context.GetSelection()) ?? false));
+ }
+
private class NegativeLineNumberKeywordsListener : InspectionListenerBase
{
public override void EnterOnErrorStmt(VBAParser.OnErrorStmtContext context)
diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/PassParameterByValueQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/PassParameterByValueQuickFix.cs
index a036e0e318..90e2ae795f 100644
--- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/PassParameterByValueQuickFix.cs
+++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/PassParameterByValueQuickFix.cs
@@ -38,7 +38,7 @@ internal sealed class PassParameterByValueQuickFix : QuickFixBase
private readonly IDeclarationFinderProvider _declarationFinderProvider;
public PassParameterByValueQuickFix(IDeclarationFinderProvider declarationFinderProvider)
- : base(typeof(ParameterCanBeByValInspection))
+ : base(typeof(ParameterCanBeByValInspection), typeof(MisleadingByRefParameterInspection))
{
_declarationFinderProvider = declarationFinderProvider;
}
diff --git a/Rubberduck.Main/Root/RubberduckIoCInstaller.cs b/Rubberduck.Main/Root/RubberduckIoCInstaller.cs
index 7438914169..97f4845c49 100644
--- a/Rubberduck.Main/Root/RubberduckIoCInstaller.cs
+++ b/Rubberduck.Main/Root/RubberduckIoCInstaller.cs
@@ -380,7 +380,14 @@ private void RegisterSpecialFactories(IWindsorContainer container)
container.Register(Component.For()
.ImplementedBy()
.LifestyleSingleton());
+
+ container.Register(Component.For()
+ .ImplementedBy()
+ .LifestyleSingleton());
+
RegisterUnreachableCaseFactories(container);
+
+ RegisterEncapsulateFieldRefactoringFactories(container);
}
private void RegisterUnreachableCaseFactories(IWindsorContainer container)
@@ -390,6 +397,21 @@ private void RegisterUnreachableCaseFactories(IWindsorContainer container)
.LifestyleSingleton());
}
+ private void RegisterEncapsulateFieldRefactoringFactories(IWindsorContainer container)
+ {
+ container.Register(Component.For()
+ .ImplementedBy()
+ .LifestyleSingleton());
+ container.Register(Component.For()
+ .ImplementedBy()
+ .LifestyleSingleton());
+ container.Register(Component.For()
+ .ImplementedBy()
+ .LifestyleSingleton());
+ container.Register(Component.For()
+ .ImplementedBy()
+ .LifestyleSingleton());
+ }
private void RegisterQuickFixes(IWindsorContainer container, Assembly[] assembliesToRegister)
{
diff --git a/Rubberduck.Parsing/Binding/Bindings/IndexDefaultBinding.cs b/Rubberduck.Parsing/Binding/Bindings/IndexDefaultBinding.cs
index e7d819492c..70d959abb0 100644
--- a/Rubberduck.Parsing/Binding/Bindings/IndexDefaultBinding.cs
+++ b/Rubberduck.Parsing/Binding/Bindings/IndexDefaultBinding.cs
@@ -356,8 +356,11 @@ declared type.
private static bool ArgumentListIsCompatible(ICollection parameters, ArgumentList argumentList)
{
return (parameters.Count >= (argumentList?.Arguments.Count ?? 0)
- || parameters.Any(parameter => parameter.IsParamArray))
- && parameters.Count(parameter => !parameter.IsOptional && !parameter.IsParamArray) <= (argumentList?.Arguments.Count ?? 0);
+ || parameters.Any(parameter => parameter.IsParamArray))
+ && parameters.Count(parameter => !parameter.IsOptional && !parameter.IsParamArray) <= (argumentList?.Arguments.Count ?? 0)
+ || parameters.Count == 0
+ && argumentList?.Arguments.Count == 1
+ && argumentList.Arguments.Single().ArgumentType == ArgumentListArgumentType.Missing;
}
private IBoundExpression ResolveRecursiveDefaultMember(Declaration defaultMember, ExpressionClassification defaultMemberClassification, ArgumentList argumentList, ParserRuleContext expression, Declaration parent, int defaultMemberResolutionRecursionDepth, RecursiveDefaultMemberAccessExpression containedExpression)
diff --git a/Rubberduck.Parsing/VBA/DeclarationResolving/DeclarationSymbolsListener.cs b/Rubberduck.Parsing/VBA/DeclarationResolving/DeclarationSymbolsListener.cs
index 02c4c87328..6e9d19ffe6 100644
--- a/Rubberduck.Parsing/VBA/DeclarationResolving/DeclarationSymbolsListener.cs
+++ b/Rubberduck.Parsing/VBA/DeclarationResolving/DeclarationSymbolsListener.cs
@@ -646,8 +646,8 @@ private void AddIdentifierStatementLabelDeclaration(VBAParser.IdentifierStatemen
private void AddLineNumberLabelDeclaration(VBAParser.LineNumberLabelContext context)
{
- var statementText = context.numberLiteral().GetText();
- var statementSelection = context.numberLiteral().GetSelection();
+ var statementText = context.GetText().Trim();
+ var statementSelection = context.GetSelection();
AddDeclaration(
CreateDeclaration(
diff --git a/Rubberduck.Refactorings/Abstract/RefactoringPreviewProviderWrapperBase.cs b/Rubberduck.Refactorings/Abstract/RefactoringPreviewProviderWrapperBase.cs
index 5d4be37753..1ddfbfd46b 100644
--- a/Rubberduck.Refactorings/Abstract/RefactoringPreviewProviderWrapperBase.cs
+++ b/Rubberduck.Refactorings/Abstract/RefactoringPreviewProviderWrapperBase.cs
@@ -20,7 +20,7 @@ public abstract class RefactoringPreviewProviderWrapperBase : 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);
diff --git a/Rubberduck.Refactorings/Common/CodeBuilder.cs b/Rubberduck.Refactorings/Common/CodeBuilder.cs
index 1f884a1fd3..aff28204e7 100644
--- a/Rubberduck.Refactorings/Common/CodeBuilder.cs
+++ b/Rubberduck.Refactorings/Common/CodeBuilder.cs
@@ -1,6 +1,7 @@
-using Rubberduck.Common;
+using Rubberduck.Parsing;
using Rubberduck.Parsing.Grammar;
using Rubberduck.Parsing.Symbols;
+using Rubberduck.SmartIndenter;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -20,9 +21,9 @@ public interface ICodeBuilder
///
/// Main body content/logic of the member
string BuildMemberBlockFromPrototype(ModuleBodyElementDeclaration declaration,
- string content = null,
- string accessibility = null,
- string newIdentifier = null);
+ string content = null,
+ Accessibility accessibility = Accessibility.Public,
+ string newIdentifier = null);
///
/// Returns the argument list for the input ModuleBodyElementDeclaration with the following improvements:
@@ -34,90 +35,116 @@ public interface ICodeBuilder
///
/// Generates a Property Get codeblock based on the prototype declaration
///
- /// VariableDeclaration or UserDefinedTypeMember
- /// Member body content. Formatting is the responsibility of the caller
+ /// DeclarationType with flags: Variable, Constant, UserDefinedTypeMember, or Function
+ /// Member body content.
/// Defaults to 'Value' unless otherwise specified
bool TryBuildPropertyGetCodeBlock(Declaration prototype,
- string propertyIdentifier,
- out string codeBlock,
- string accessibility = null,
- string content = null);
+ string propertyIdentifier,
+ out string codeBlock,
+ Accessibility accessibility = Accessibility.Public,
+ string content = null);
///
/// Generates a Property Let codeblock based on the prototype declaration
///
- /// VariableDeclaration or UserDefinedTypeMember
- /// Member body content. Formatting is the responsibility of the caller
- /// Defaults to 'Value' unless otherwise specified
+ /// DeclarationType with flags: Variable, Constant, UserDefinedTypeMember, or Function
+ /// Member body content.
+ /// Defaults to 'RHS' unless otherwise specified
bool TryBuildPropertyLetCodeBlock(Declaration prototype,
- string propertyIdentifier,
- out string codeBlock,
- string accessibility = null,
- string content = null,
- string parameterIdentifier = null);
+ string propertyIdentifier,
+ out string codeBlock,
+ Accessibility accessibility = Accessibility.Public,
+ string content = null,
+ string parameterIdentifier = null);
///
/// Generates a Property Set codeblock based on the prototype declaration
///
- /// VariableDeclaration or UserDefinedTypeMember
- /// Member body content. Formatting is the responsibility of the caller
- /// Defaults to 'Value' unless otherwise specified
+ /// DeclarationType with flags: Variable, Constant, UserDefinedTypeMember, or Function
+ /// Member body content.
+ /// Defaults to 'RHS' unless otherwise specified
bool TryBuildPropertySetCodeBlock(Declaration prototype,
- string propertyIdentifier,
- out string codeBlock,
- string accessibility = null,
- string content = null,
- string parameterIdentifier = null);
+ string propertyIdentifier,
+ out string codeBlock,
+ Accessibility accessibility = Accessibility.Public,
+ string content = null,
+ string parameterIdentifier = null);
+
+ ///
+ /// Generates a UserDefinedType (UDT) declaration using the prototype declarations for
+ /// creating the UserDefinedTypeMember declarations.
+ ///
+ ///
+ /// No validation or conflict analysis is applied to the identifiers.
+ ///
+ /// DeclarationTypes with flags: Variable, Constant, UserDefinedTypeMember, or Function
+ bool TryBuildUserDefinedTypeDeclaration(string udtIdentifier, IEnumerable<(Declaration Prototype, string UDTMemberIdentifier)> memberPrototypes, out string declaration, Accessibility accessibility = Accessibility.Private);
+
+ ///
+ /// Generates a UserDefinedTypeMember declaration expression based on the prototype declaration
+ ///
+ ///
+ /// No validation or conflict analysis is applied to the identifiers.
+ ///
+ /// DeclarationType with flags: Variable, Constant, UserDefinedTypeMember, or Function
+ bool TryBuildUDTMemberDeclaration(Declaration prototype, string identifier, out string declaration);
+
+ IIndenter Indenter { get; }
}
public class CodeBuilder : ICodeBuilder
{
- public string BuildMemberBlockFromPrototype(ModuleBodyElementDeclaration declaration,
- string content = null,
- string accessibility = null,
- string newIdentifier = null)
+ public CodeBuilder(IIndenter indenter)
{
+ Indenter = indenter;
+ }
+
+ public IIndenter Indenter { get; }
+ public string BuildMemberBlockFromPrototype(ModuleBodyElementDeclaration declaration,
+ string content = null,
+ Accessibility accessibility = Accessibility.Public,
+ string newIdentifier = null)
+ {
var elements = new List()
{
ImprovedFullMemberSignatureInternal(declaration, accessibility, newIdentifier),
Environment.NewLine,
string.IsNullOrEmpty(content) ? null : $"{content}{Environment.NewLine}",
- ProcedureEndStatement(declaration.DeclarationType),
+ EndStatement(declaration.DeclarationType),
Environment.NewLine,
};
return string.Concat(elements);
}
- public bool TryBuildPropertyGetCodeBlock(Declaration prototype, string propertyIdentifier, out string codeBlock, string accessibility = null, string content = null)
+ public bool TryBuildPropertyGetCodeBlock(Declaration prototype, string propertyIdentifier, out string codeBlock, Accessibility accessibility = Accessibility.Public, string content = null)
=> TryBuildPropertyBlockFromTarget(prototype, DeclarationType.PropertyGet, propertyIdentifier, out codeBlock, accessibility, content);
- public bool TryBuildPropertyLetCodeBlock(Declaration prototype, string propertyIdentifier, out string codeBlock, string accessibility = null, string content = null, string parameterIdentifier = null)
+ public bool TryBuildPropertyLetCodeBlock(Declaration prototype, string propertyIdentifier, out string codeBlock, Accessibility accessibility = Accessibility.Public, string content = null, string parameterIdentifier = null)
=> TryBuildPropertyBlockFromTarget(prototype, DeclarationType.PropertyLet, propertyIdentifier, out codeBlock, accessibility, content, parameterIdentifier);
- public bool TryBuildPropertySetCodeBlock(Declaration prototype, string propertyIdentifier, out string codeBlock, string accessibility = null, string content = null, string parameterIdentifier = null)
+ public bool TryBuildPropertySetCodeBlock(Declaration prototype, string propertyIdentifier, out string codeBlock, Accessibility accessibility = Accessibility.Public, string content = null, string parameterIdentifier = null)
=> TryBuildPropertyBlockFromTarget(prototype, DeclarationType.PropertySet, propertyIdentifier, out codeBlock, accessibility, content, parameterIdentifier);
- private bool TryBuildPropertyBlockFromTarget(T prototype, DeclarationType letSetGetType, string propertyIdentifier, out string codeBlock, string accessibility = null, string content = null, string parameterIdentifier = null) where T : Declaration
+ private bool TryBuildPropertyBlockFromTarget(T prototype, DeclarationType letSetGetType, string propertyIdentifier, out string codeBlock, Accessibility accessibility, string content = null, string parameterIdentifier = null) where T : Declaration
{
codeBlock = string.Empty;
- if (!letSetGetType.HasFlag(DeclarationType.Property))
- {
- throw new ArgumentException();
- }
-
- if (!(prototype is VariableDeclaration || prototype.DeclarationType.HasFlag(DeclarationType.UserDefinedTypeMember)))
+ if (!letSetGetType.HasFlag(DeclarationType.Property)
+ || !IsValidPrototypeDeclarationType(prototype.DeclarationType))
{
return false;
}
var propertyValueParam = parameterIdentifier ?? Resources.Refactorings.Refactorings.CodeBuilder_DefaultPropertyRHSParam;
+ //TODO: Improve generated Array properties
+ //Add logic to conditionally return Arrays or Variant depending on Office version.
+ //Ability to return an Array from a Function or Property was added in Office 2000 http://www.cpearson.com/excel/passingandreturningarrays.htm
var asType = prototype.IsArray
? $"{Tokens.Variant}"
: IsEnumField(prototype) && prototype.AsTypeDeclaration.Accessibility.Equals(Accessibility.Private)
- ? $"{Tokens.Long}"
- : $"{prototype.AsTypeName}";
+ ? $"{Tokens.Long}"
+ : $"{prototype.AsTypeName}";
var asTypeClause = $"{Tokens.As} {asType}";
@@ -126,34 +153,32 @@ public bool TryBuildPropertySetCodeBlock(Declaration prototype, string propertyI
var letSetParamExpression = $"{paramMechanism} {propertyValueParam} {asTypeClause}";
codeBlock = letSetGetType.HasFlag(DeclarationType.PropertyGet)
- ? string.Join(Environment.NewLine, $"{accessibility ?? Tokens.Public} {ProcedureTypeStatement(letSetGetType)} {propertyIdentifier}() {asTypeClause}", content, ProcedureEndStatement(letSetGetType))
- : string.Join(Environment.NewLine, $"{accessibility ?? Tokens.Public} {ProcedureTypeStatement(letSetGetType)} {propertyIdentifier}({letSetParamExpression})", content, ProcedureEndStatement(letSetGetType));
+ ? string.Join(Environment.NewLine, $"{AccessibilityToken(accessibility)} {TypeToken(letSetGetType)} {propertyIdentifier}() {asTypeClause}", content, EndStatement(letSetGetType))
+ : string.Join(Environment.NewLine, $"{AccessibilityToken(accessibility)} {TypeToken(letSetGetType)} {propertyIdentifier}({letSetParamExpression})", content, EndStatement(letSetGetType));
+
+ codeBlock = string.Join(Environment.NewLine, Indenter.Indent(codeBlock));
return true;
}
public string ImprovedFullMemberSignature(ModuleBodyElementDeclaration declaration)
- => ImprovedFullMemberSignatureInternal(declaration);
+ => ImprovedFullMemberSignatureInternal(declaration, declaration.Accessibility);
- private string ImprovedFullMemberSignatureInternal(ModuleBodyElementDeclaration declaration, string accessibility = null, string newIdentifier = null)
+ private string ImprovedFullMemberSignatureInternal(ModuleBodyElementDeclaration declaration, Accessibility accessibility, string newIdentifier = null)
{
- var accessibilityToken = declaration.Accessibility.Equals(Accessibility.Implicit)
- ? Tokens.Public
- : $"{declaration.Accessibility.ToString()}";
-
var asTypeName = string.IsNullOrEmpty(declaration.AsTypeName)
- ? string.Empty
- : $" {Tokens.As} {declaration.AsTypeName}";
-
+ ? string.Empty
+ : $" {Tokens.As} {declaration.AsTypeName}";
+
var elements = new List()
{
- accessibility ?? accessibilityToken,
- $" {ProcedureTypeStatement(declaration.DeclarationType)} ",
+ AccessibilityToken(accessibility),
+ $" {TypeToken(declaration.DeclarationType)} ",
newIdentifier ?? declaration.IdentifierName,
$"({ImprovedArgumentList(declaration)})",
asTypeName
};
- return string.Concat(elements).Trim();
+ return string.Concat(elements).Trim();
}
public string ImprovedArgumentList(ModuleBodyElementDeclaration declaration)
@@ -164,11 +189,12 @@ public string ImprovedArgumentList(ModuleBodyElementDeclaration declaration)
arguments = parameterizedDeclaration.Parameters
.OrderBy(parameter => parameter.Selection)
.Select(parameter => BuildParameterDeclaration(
- parameter,
- parameter.Equals(parameterizedDeclaration.Parameters.LastOrDefault())
- && declaration.DeclarationType.HasFlag(DeclarationType.Property)
- && !declaration.DeclarationType.Equals(DeclarationType.PropertyGet)));
+ parameter,
+ parameter.Equals(parameterizedDeclaration.Parameters.LastOrDefault())
+ && declaration.DeclarationType.HasFlag(DeclarationType.Property)
+ && !declaration.DeclarationType.Equals(DeclarationType.PropertyGet)));
}
+
return $"{string.Join(", ", arguments)}";
}
@@ -183,8 +209,8 @@ private static string BuildParameterDeclaration(ParameterDeclaration parameter,
: parameter.IsByRef ? Tokens.ByRef : Tokens.ByVal;
if (forceExplicitByValAccess
- && (string.IsNullOrEmpty(paramMechanism) || paramMechanism.Equals(Tokens.ByRef))
- && !IsUserDefinedType(parameter))
+ && (string.IsNullOrEmpty(paramMechanism) || paramMechanism.Equals(Tokens.ByRef))
+ && !IsUserDefinedType(parameter))
{
paramMechanism = Tokens.ByVal;
}
@@ -206,48 +232,93 @@ private static string BuildParameterDeclaration(ParameterDeclaration parameter,
}
private static string FormatOptionalElement(string element)
- => string.IsNullOrEmpty(element) ? string.Empty : $"{element} ";
+ => string.IsNullOrEmpty(element) ? string.Empty : $"{element} ";
private static string FormatAsTypeName(string AsTypeName)
- => string.IsNullOrEmpty(AsTypeName) ? string.Empty : $"As {AsTypeName} ";
+ => string.IsNullOrEmpty(AsTypeName) ? string.Empty : $"As {AsTypeName} ";
private static string FormatDefaultValue(string DefaultValue)
- => string.IsNullOrEmpty(DefaultValue) ? string.Empty : $"= {DefaultValue}";
+ => string.IsNullOrEmpty(DefaultValue) ? string.Empty : $"= {DefaultValue}";
+
+ private static Dictionary _declarationTypeTokens
+ = new Dictionary()
+ {
+ [DeclarationType.Function] = (Tokens.Function, $"{Tokens.End} {Tokens.Function}"),
+ [DeclarationType.Procedure] = (Tokens.Sub, $"{Tokens.End} {Tokens.Sub}"),
+ [DeclarationType.PropertyGet] = ($"{Tokens.Property} {Tokens.Get}", $"{Tokens.End} {Tokens.Property}"),
+ [DeclarationType.PropertyLet] = ($"{Tokens.Property} {Tokens.Let}", $"{Tokens.End} {Tokens.Property}"),
+ [DeclarationType.PropertySet] = ($"{Tokens.Property} {Tokens.Set}", $"{Tokens.End} {Tokens.Property}"),
+ };
+
+ private static string EndStatement(DeclarationType declarationType)
+ => _declarationTypeTokens[declarationType].EndStatement;
+
+ private static string TypeToken(DeclarationType declarationType)
+ => _declarationTypeTokens[declarationType].TypeToken;
- private static string ProcedureEndStatement(DeclarationType declarationType)
+ public bool TryBuildUserDefinedTypeDeclaration(string udtIdentifier, IEnumerable<(Declaration Prototype, string UDTMemberIdentifier)> memberPrototypes, out string declaration, Accessibility accessibility = Accessibility.Private)
{
- switch (declarationType)
+ if (udtIdentifier is null
+ ||!memberPrototypes.Any()
+ || memberPrototypes.Any(p => p.Prototype is null || p.UDTMemberIdentifier is null)
+ || memberPrototypes.Any(mp => !IsValidPrototypeDeclarationType(mp.Prototype.DeclarationType)))
{
- case DeclarationType.Function:
- return $"{Tokens.End} {Tokens.Function}";
- case DeclarationType.Procedure:
- return $"{Tokens.End} {Tokens.Sub}";
- case DeclarationType.PropertyGet:
- case DeclarationType.PropertyLet:
- case DeclarationType.PropertySet:
- return $"{Tokens.End} {Tokens.Property}";
- default:
- throw new ArgumentException();
+ declaration = string.Empty;
+ return false;
}
+
+ var blockLines = memberPrototypes
+ .Select(m => BuildUDTMemberDeclaration(m.UDTMemberIdentifier, m.Prototype))
+ .ToList();
+
+ blockLines.Insert(0, $"{accessibility.TokenString()} {Tokens.Type} {udtIdentifier}");
+
+ blockLines.Add($"{Tokens.End} {Tokens.Type}");
+
+ declaration = string.Join(Environment.NewLine, Indenter.Indent(blockLines));
+
+ return true;
}
- private static string ProcedureTypeStatement(DeclarationType declarationType)
+ public bool TryBuildUDTMemberDeclaration(Declaration prototype, string udtMemberIdentifier, out string declaration)
{
- switch (declarationType)
+ declaration = string.Empty;
+
+ if (udtMemberIdentifier is null
+ || prototype is null
+ || !IsValidPrototypeDeclarationType(prototype.DeclarationType))
{
- case DeclarationType.Function:
- return Tokens.Function;
- case DeclarationType.Procedure:
- return Tokens.Sub;
- case DeclarationType.PropertyGet:
- return $"{Tokens.Property} {Tokens.Get}";
- case DeclarationType.PropertyLet:
- return $"{Tokens.Property} {Tokens.Let}";
- case DeclarationType.PropertySet:
- return $"{Tokens.Property} {Tokens.Set}";
- default:
- throw new ArgumentException();
+ return false;
}
+
+ declaration = BuildUDTMemberDeclaration(udtMemberIdentifier, prototype);
+ return true;
+ }
+
+ private static string BuildUDTMemberDeclaration(string udtMemberIdentifier, Declaration prototype)
+ {
+ var identifierExpression = udtMemberIdentifier;
+ if (prototype.IsArray)
+ {
+ identifierExpression = prototype.Context.TryGetChildContext(out var ctxt)
+ ? $"{udtMemberIdentifier}({ctxt.GetText()})"
+ : $"{udtMemberIdentifier}()";
+ }
+
+ return $"{identifierExpression} {Tokens.As} {prototype.AsTypeName}";
+ }
+
+ private static string AccessibilityToken(Accessibility accessibility)
+ => accessibility.Equals(Accessibility.Implicit)
+ ? Tokens.Public
+ : $"{accessibility.ToString()}";
+
+ private static bool IsValidPrototypeDeclarationType(DeclarationType declarationType)
+ {
+ return declarationType.HasFlag(DeclarationType.Variable)
+ || declarationType.HasFlag(DeclarationType.UserDefinedTypeMember)
+ || declarationType.HasFlag(DeclarationType.Constant)
+ || declarationType.HasFlag(DeclarationType.Function);
}
private static bool IsEnumField(VariableDeclaration declaration)
@@ -259,10 +330,10 @@ private static bool IsEnumField(Declaration declaration)
&& (declaration.AsTypeDeclaration?.DeclarationType.Equals(DeclarationType.Enumeration) ?? false);
private static bool IsUserDefinedType(Declaration declaration)
- => (declaration.AsTypeDeclaration?.DeclarationType.Equals(DeclarationType.UserDefinedType) ?? false);
+ => (declaration.AsTypeDeclaration?.DeclarationType.Equals(DeclarationType.UserDefinedType) ?? false);
private static bool IsMemberVariable(Declaration declaration)
=> declaration.DeclarationType.HasFlag(DeclarationType.Variable)
- && !declaration.ParentDeclaration.DeclarationType.HasFlag(DeclarationType.Member);
+ && !declaration.ParentDeclaration.DeclarationType.HasFlag(DeclarationType.Member);
}
}
diff --git a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinder.cs b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinder.cs
new file mode 100644
index 0000000000..f73143112d
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinder.cs
@@ -0,0 +1,324 @@
+using Rubberduck.Parsing;
+using Rubberduck.Parsing.Grammar;
+using Rubberduck.Parsing.Symbols;
+using Rubberduck.Parsing.VBA;
+using Rubberduck.Refactorings.Common;
+using Rubberduck.Refactorings.EncapsulateField.Extensions;
+using Rubberduck.Resources;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Rubberduck.Refactorings.EncapsulateField
+{
+ public interface IEncapsulateFieldConflictFinderFactory
+ {
+ IEncapsulateFieldConflictFinder Create(IDeclarationFinderProvider declarationFinderProvider,
+ IEnumerable candidates,
+ IEnumerable objectStateUDTs);
+ }
+
+ public interface IEncapsulateFieldConflictFinder
+ {
+ bool IsConflictingIdentifier(IEncapsulateFieldCandidate field, string identifierToCompare, out string errorMessage);
+ (bool IsValid, string ValidationError) ValidateEncapsulationAttributes(IEncapsulateFieldCandidate field);
+ void AssignNoConflictIdentifiers(IEncapsulateFieldCandidate candidate);
+ void AssignNoConflictIdentifiers(IObjectStateUDT stateUDT);
+ void AssignNoConflictIdentifiers(IEnumerable candidates);
+ void AssignNoConflictBackingFieldIdentifier(IEncapsulateFieldCandidate candidate);
+ }
+
+ public class EncapsulateFieldConflictFinder : IEncapsulateFieldConflictFinder
+ {
+ private static List _declarationTypesThatNeverConflictWithFieldAndPropertyIdentifiers = new List()
+ {
+ DeclarationType.Project,
+ DeclarationType.ProceduralModule,
+ DeclarationType.ClassModule,
+ DeclarationType.Parameter,
+ DeclarationType.Enumeration,
+ DeclarationType.UserDefinedType,
+ DeclarationType.UserDefinedTypeMember
+ };
+
+ private readonly IDeclarationFinderProvider _declarationFinderProvider;
+ private readonly List _fieldCandidates;
+ private readonly List _udtMemberCandidates;
+ private readonly List _allCandidates;
+ private readonly List _objectStateUDTs;
+ private readonly List _members;
+ private readonly List _membersThatCanConflictWithFieldAndPropertyIdentifiers;
+ private readonly List _existingUserUDTsAndEnums;
+
+ public EncapsulateFieldConflictFinder(IDeclarationFinderProvider declarationFinderProvider, IEnumerable candidates, IEnumerable objectStateUDTs)
+ {
+ _declarationFinderProvider = declarationFinderProvider;
+
+ _fieldCandidates = candidates.ToList();
+
+ _udtMemberCandidates = new List();
+
+ _fieldCandidates.ForEach(c => LoadUDTMemberCandidates(c, _udtMemberCandidates));
+
+ _allCandidates = _fieldCandidates.Concat(_udtMemberCandidates).ToList();
+
+ _objectStateUDTs = objectStateUDTs.ToList();
+
+ _members = _declarationFinderProvider.DeclarationFinder.Members(_allCandidates.First().QualifiedModuleName).ToList();
+
+ _membersThatCanConflictWithFieldAndPropertyIdentifiers =
+ _members.Where(m => !_declarationTypesThatNeverConflictWithFieldAndPropertyIdentifiers.Contains(m.DeclarationType)).ToList();
+
+ _existingUserUDTsAndEnums = _members.Where(m => m.DeclarationType.HasFlag(DeclarationType.UserDefinedType)
+ || m.DeclarationType.HasFlag(DeclarationType.Enumeration)).ToList();
+ }
+
+ public (bool IsValid, string ValidationError) ValidateEncapsulationAttributes(IEncapsulateFieldCandidate field)
+ {
+ if (!field.EncapsulateFlag)
+ {
+ return (true, string.Empty);
+ }
+
+ var declarationType = field is IEncapsulateFieldAsUDTMemberCandidate
+ ? DeclarationType.UserDefinedTypeMember
+ : field.Declaration.DeclarationType;
+
+ var errorMessage = string.Empty;
+
+ if (field.Declaration.IsArray)
+ {
+ if (field.Declaration.References.Any(rf => rf.QualifiedModuleName != field.QualifiedModuleName
+ && rf.Context.TryGetAncestor(out _)))
+ {
+ errorMessage = string.Format(RubberduckUI.EncapsulateField_ArrayHasExternalRedimFormat, field.IdentifierName);
+ return (false, errorMessage);
+ }
+
+ if (field is IEncapsulateFieldAsUDTMemberCandidate udtMember
+ && VBAIdentifierValidator.TryMatchInvalidIdentifierCriteria(udtMember.UserDefinedTypeMemberIdentifier, declarationType, out errorMessage, true))
+ {
+ return (false, errorMessage);
+ }
+ }
+
+ var hasConflictFreeValidIdentifiers =
+ !VBAIdentifierValidator.TryMatchInvalidIdentifierCriteria(field.PropertyIdentifier, declarationType, out errorMessage, field.Declaration.IsArray)
+ && !IsConflictingIdentifier(field, field.PropertyIdentifier, out errorMessage)
+ && !IsConflictingIdentifier(field, field.BackingIdentifier, out errorMessage)
+ && !(field is IEncapsulateFieldAsUDTMemberCandidate && ConflictsWithExistingUDTMembers(SelectedObjectStateUDT(), field.BackingIdentifier, out errorMessage));
+
+ return (hasConflictFreeValidIdentifiers, errorMessage);
+ }
+
+ public bool IsConflictingIdentifier(IEncapsulateFieldCandidate field, string identifierToCompare, out string errorMessage)
+ {
+ errorMessage = string.Empty;
+ if (HasConflictIdentifiers(field, identifierToCompare))
+ {
+ errorMessage = RubberduckUI.EncapsulateField_NameConflictDetected;
+ }
+ return !string.IsNullOrEmpty(errorMessage);
+ }
+
+ public void AssignNoConflictIdentifiers(IEnumerable candidates)
+ {
+ foreach (var candidate in candidates.Where(c => c.EncapsulateFlag))
+ {
+ ResolveIdentifierConflicts(candidate);
+ }
+ }
+
+ private void ResolveIdentifierConflicts(IEncapsulateFieldCandidate candidate)
+ {
+ AssignNoConflictIdentifiers(candidate);
+ if (candidate is IUserDefinedTypeCandidate udtCandidate)
+ {
+ ResolveUDTMemberIdentifierConflicts(udtCandidate.Members);
+ }
+ }
+
+ private void ResolveUDTMemberIdentifierConflicts(IEnumerable members)
+ {
+ foreach (var member in members)
+ {
+ AssignNoConflictIdentifiers(member);
+ if (member.WrappedCandidate is IUserDefinedTypeCandidate childUDT
+ && childUDT.Declaration.AsTypeDeclaration.HasPrivateAccessibility())
+ {
+ ResolveIdentifierConflicts(childUDT);
+ }
+ }
+ }
+
+ public void AssignNoConflictIdentifiers(IEncapsulateFieldCandidate candidate)
+ {
+ if (candidate is IEncapsulateFieldAsUDTMemberCandidate udtMember)
+ {
+ AssignIdentifier(
+ () => ConflictsWithExistingUDTMembers(SelectedObjectStateUDT(), udtMember.UserDefinedTypeMemberIdentifier, out _),
+ () => udtMember.UserDefinedTypeMemberIdentifier = udtMember.UserDefinedTypeMemberIdentifier.IncrementEncapsulationIdentifier());
+ return;
+ }
+
+ AssignNoConflictPropertyIdentifier(candidate);
+ AssignNoConflictBackingFieldIdentifier(candidate);
+ }
+
+ public void AssignNoConflictIdentifiers(IObjectStateUDT stateUDT)
+ {
+ AssignIdentifier(
+ () => _existingUserUDTsAndEnums.Any(m => m.IdentifierName.IsEquivalentVBAIdentifierTo(stateUDT.TypeIdentifier)),
+ () => stateUDT.TypeIdentifier = stateUDT.TypeIdentifier.IncrementEncapsulationIdentifier());
+
+ AssignIdentifier(
+ () => HasConflictingFieldIdentifier(stateUDT, stateUDT.FieldIdentifier),
+ () => stateUDT.FieldIdentifier = stateUDT.FieldIdentifier.IncrementEncapsulationIdentifier());
+ }
+
+ private IObjectStateUDT SelectedObjectStateUDT()
+ => _objectStateUDTs.SingleOrDefault(os => os.IsSelected);
+
+ private static bool ConflictsWithExistingUDTMembers(IObjectStateUDT objectStateUDT, string identifier, out string errorMessage)
+ {
+ errorMessage = string.Empty;
+ if (objectStateUDT?.ExistingMembers.Any(nm => nm.IdentifierName.IsEquivalentVBAIdentifierTo(identifier)) ?? false)
+ {
+ errorMessage = RubberduckUI.EncapsulateField_NameConflictDetected;
+ }
+ return !string.IsNullOrEmpty(errorMessage);
+ }
+
+ private void AssignNoConflictPropertyIdentifier(IEncapsulateFieldCandidate candidate)
+ {
+ AssignIdentifier(
+ () => IsConflictingIdentifier(candidate, candidate.PropertyIdentifier, out _),
+ () => candidate.PropertyIdentifier = candidate.PropertyIdentifier.IncrementEncapsulationIdentifier());
+ }
+
+ public void AssignNoConflictBackingFieldIdentifier(IEncapsulateFieldCandidate candidate)
+ {
+ if (candidate.BackingIdentifierMutator != null)
+ {
+ AssignIdentifier(
+ () => IsConflictingIdentifier(candidate, candidate.BackingIdentifier, out _),
+ () => candidate.BackingIdentifierMutator(candidate.BackingIdentifier.IncrementEncapsulationIdentifier()));
+ }
+ }
+
+ private static void AssignIdentifier(Func hasConflict, Action incrementIdentifier, int maxAttempts = 20)
+ {
+ var guard = 0;
+ while (guard++ < maxAttempts && hasConflict())
+ {
+ incrementIdentifier();
+ }
+
+ if (guard >= maxAttempts)
+ {
+ throw new OverflowException("Unable to assign a non conflicting identifier");
+ }
+ }
+
+ private bool HasConflictIdentifiers(IEncapsulateFieldCandidate candidate, string identifierToCompare)
+ {
+ return HasInternalPropertyAndBackingFieldConflict(candidate)
+ || HasConflictsWithOtherEncapsulationPropertyIdentifiers(candidate, identifierToCompare)
+ || HasConflictsWithUnmodifiedPropertyAndFieldIdentifiers(candidate, identifierToCompare)
+ || HasConflictWithLocalDeclarationIdentifiers(candidate, identifierToCompare);
+ }
+
+ private bool HasInternalPropertyAndBackingFieldConflict(IEncapsulateFieldCandidate candidate)
+ => candidate.BackingIdentifierMutator != null
+ && candidate.EncapsulateFlag
+ && candidate.PropertyIdentifier.IsEquivalentVBAIdentifierTo(candidate.BackingIdentifier);
+
+ private bool HasConflictsWithOtherEncapsulationPropertyIdentifiers(IEncapsulateFieldCandidate candidate, string identifierToCompare)
+ => _allCandidates.Where(c => c.TargetID != candidate.TargetID
+ && c.EncapsulateFlag
+ && c.PropertyIdentifier.IsEquivalentVBAIdentifierTo(identifierToCompare)).Any();
+
+ private bool HasConflictsWithUnmodifiedPropertyAndFieldIdentifiers(IEncapsulateFieldCandidate candidate, string identifierToCompare)
+ {
+ var membersToEvaluate = _members.Where(d => d != candidate.Declaration);
+
+ if (candidate is IEncapsulateFieldAsUDTMemberCandidate)
+ {
+ membersToEvaluate = membersToEvaluate.Except(
+ _fieldCandidates.Where(fc => fc.EncapsulateFlag && fc.Declaration.DeclarationType.HasFlag(DeclarationType.Variable))
+ .Select(f => f.Declaration));
+ }
+
+ var nameConflictCandidates = membersToEvaluate.Where(member => !(member.IsLocalVariable() || member.IsLocalConstant()
+ || _declarationTypesThatNeverConflictWithFieldAndPropertyIdentifiers.Contains(member.DeclarationType)));
+
+ return nameConflictCandidates.Any(m => m.IdentifierName.IsEquivalentVBAIdentifierTo(identifierToCompare));
+ }
+
+ private bool HasConflictWithLocalDeclarationIdentifiers(IEncapsulateFieldCandidate candidate, string identifierToCompare)
+ {
+ var membersToEvaluate = _members.Where(d => d != candidate.Declaration);
+
+ if (candidate is IEncapsulateFieldAsUDTMemberCandidate)
+ {
+ membersToEvaluate = membersToEvaluate.Except(
+ _fieldCandidates.Where(fc => fc.EncapsulateFlag && fc.Declaration.DeclarationType.HasFlag(DeclarationType.Variable))
+ .Select(f => f.Declaration));
+ }
+
+ //Only check IdentifierReferences in the declaring module because encapsulated field
+ //references in other modules will be module-qualified.
+ var candidateLocalReferences = candidate.Declaration.References.Where(rf => rf.QualifiedModuleName == candidate.QualifiedModuleName);
+
+ var localDeclarationConflictCandidates = membersToEvaluate.Where(localDec => candidateLocalReferences
+ .Any(cr => cr.ParentScoping == localDec.ParentScopeDeclaration));
+
+ return localDeclarationConflictCandidates.Any(m => m.IdentifierName.IsEquivalentVBAIdentifierTo(identifierToCompare));
+ }
+
+ private bool HasConflictingFieldIdentifier(IObjectStateUDT candidate, string identifierToCompare)
+ {
+ if (candidate.IsExistingDeclaration)
+ {
+ return false;
+ }
+
+ if (_allCandidates.Where(c => c.EncapsulateFlag
+ && c.PropertyIdentifier.IsEquivalentVBAIdentifierTo(identifierToCompare)).Any())
+ {
+ return true;
+ }
+
+ var fieldsToRemoveFromConflictCandidates = _fieldCandidates
+ .Where(fc => fc.EncapsulateFlag && fc.Declaration.DeclarationType.HasFlag(DeclarationType.Variable))
+ .Select(fc => fc.Declaration);
+
+ var nameConflictCandidates =
+ _members.Except(fieldsToRemoveFromConflictCandidates)
+ .Where(member => !(member.IsLocalVariable() || member.IsLocalConstant()
+ || _declarationTypesThatNeverConflictWithFieldAndPropertyIdentifiers.Contains(member.DeclarationType)));
+
+ return nameConflictCandidates.Any(m => m.IdentifierName.IsEquivalentVBAIdentifierTo(identifierToCompare));
+ }
+
+ private void LoadUDTMemberCandidates(IEncapsulateFieldCandidate candidate, List udtMemberCandidates)
+ {
+ if (!(candidate is IUserDefinedTypeCandidate udtCandidate))
+ {
+ return;
+ }
+
+ foreach (var member in udtCandidate.Members)
+ {
+ udtMemberCandidates.Add(member);
+
+ if (member.WrappedCandidate is IUserDefinedTypeCandidate childUDT
+ && childUDT.Declaration.AsTypeDeclaration.HasPrivateAccessibility())
+ {
+ //recursive till a non-UserDefinedType member is found
+ LoadUDTMemberCandidates(childUDT, udtMemberCandidates);
+ }
+ }
+ }
+ }
+}
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCandidateSetsProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCandidateSetsProvider.cs
new file mode 100644
index 0000000000..f5a705d79c
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCandidateSetsProvider.cs
@@ -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 EncapsulateFieldUseBackingFieldCandidates { get; }
+ IReadOnlyCollection EncapsulateFieldUseBackingUDTMemberCandidates { get; }
+ IReadOnlyCollection ObjectStateFieldCandidates { get; }
+ }
+
+ ///
+ /// EncapsulateFieldCandidateSetsProvider provides access to a sets of
+ /// EncapsulateField candidate instances to be shared among EncapsulateFieldRefactoringActions.
+ ///
+ 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()
+ .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 EncapsulateFieldUseBackingFieldCandidates { get; }
+
+ public IReadOnlyCollection EncapsulateFieldUseBackingUDTMemberCandidates { get; }
+
+ public IReadOnlyCollection ObjectStateFieldCandidates { get; }
+ }
+}
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldElementsBuilder.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldElementsBuilder.cs
deleted file mode 100644
index 6b36b7bb02..0000000000
--- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldElementsBuilder.cs
+++ /dev/null
@@ -1,162 +0,0 @@
-using Rubberduck.Parsing.Symbols;
-using Rubberduck.Parsing.VBA;
-using Rubberduck.Refactorings.Common;
-using Rubberduck.VBEditor;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-
-namespace Rubberduck.Refactorings.EncapsulateField
-{
- public class EncapsulateFieldElementsBuilder
- {
- private readonly IDeclarationFinderProvider _declarationFinderProvider;
- private QualifiedModuleName _targetQMN;
- private string _defaultObjectStateUDTTypeName;
- private ICodeBuilder _codeBuilder;
-
- public EncapsulateFieldElementsBuilder(IDeclarationFinderProvider declarationFinderProvider, QualifiedModuleName targetQMN)
- {
- _declarationFinderProvider = declarationFinderProvider;
- _targetQMN = targetQMN;
- _defaultObjectStateUDTTypeName = $"T{_targetQMN.ComponentName}";
- _codeBuilder = new CodeBuilder();
- CreateRefactoringElements();
- }
-
- public IObjectStateUDT DefaultObjectStateUDT { private set; get; }
-
- public IObjectStateUDT ObjectStateUDT { private set; get; }
-
- public IEncapsulateFieldValidationsProvider ValidationsProvider { private set; get; }
-
- public IEnumerable Candidates { private set; get; }
-
- public IEnumerable ObjectStateUDTCandidates { private set; get; } = new List();
-
- private void CreateRefactoringElements()
- {
- var fieldDeclarations = _declarationFinderProvider.DeclarationFinder
- .Members(_targetQMN)
- .Where(v => v.IsMemberVariable() && !v.IsWithEvents);
-
- var defaultNamesValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.Default);
-
- var candidates = new List();
- foreach (var fieldDeclaration in fieldDeclarations)
- {
- Debug.Assert(!fieldDeclaration.DeclarationType.Equals(DeclarationType.UserDefinedTypeMember));
-
- var fieldEncapsulationCandidate = CreateCandidate(fieldDeclaration, defaultNamesValidator);
-
- candidates.Add(fieldEncapsulationCandidate);
- }
-
- Candidates = candidates;
-
- ObjectStateUDTCandidates = BuildObjectStateUDTCandidates(candidates).ToList();
-
- ObjectStateUDT = ObjectStateUDTCandidates.FirstOrDefault(os => os.AsTypeDeclaration.IdentifierName.StartsWith(_defaultObjectStateUDTTypeName, StringComparison.InvariantCultureIgnoreCase));
-
- DefaultObjectStateUDT = CreateStateUDTField();
- DefaultObjectStateUDT.IsSelected = true;
- if (ObjectStateUDT != null)
- {
- ObjectStateUDT.IsSelected = true;
- DefaultObjectStateUDT.IsSelected = false;
- }
-
- ObjectStateUDTCandidates = ObjectStateUDTCandidates.Concat(new IObjectStateUDT[] { DefaultObjectStateUDT });
-
- ValidationsProvider = new EncapsulateFieldValidationsProvider(Candidates, ObjectStateUDTCandidates);
-
- var conflictsFinder = ValidationsProvider.ConflictDetector(EncapsulateFieldStrategy.UseBackingFields, _declarationFinderProvider);
- foreach (var candidate in candidates)
- {
- candidate.ConflictFinder = conflictsFinder;
- }
- }
-
- private IEnumerable BuildObjectStateUDTCandidates(IEnumerable candidates)
- {
- var udtCandidates = candidates.Where(c => c is IUserDefinedTypeCandidate udt
- && udt.CanBeObjectStateUDT);
-
- var objectStateUDTs = new List();
- foreach (var udt in udtCandidates)
- {
- objectStateUDTs.Add(new ObjectStateUDT(udt as IUserDefinedTypeCandidate));
- }
-
- var objectStateUDT = objectStateUDTs.FirstOrDefault(os => os.AsTypeDeclaration.IdentifierName.StartsWith(_defaultObjectStateUDTTypeName, StringComparison.InvariantCultureIgnoreCase));
-
- return objectStateUDTs;
- }
-
- private IObjectStateUDT CreateStateUDTField()
- {
- var stateUDT = new ObjectStateUDT(_targetQMN) as IObjectStateUDT;
-
- EncapsulateFieldValidationsProvider.AssignNoConflictIdentifiers(stateUDT, _declarationFinderProvider);
-
- stateUDT.IsSelected = true;
-
- return stateUDT;
- }
-
- private IEncapsulateFieldCandidate CreateCandidate(Declaration target, IValidateVBAIdentifiers validator)
- {
- if (target.IsUserDefinedType())
- {
- var udtValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedType);
- var udtField = new UserDefinedTypeCandidate(target, udtValidator) as IUserDefinedTypeCandidate;
-
- (Declaration udtDeclaration, IEnumerable udtMembers) = GetUDTAndMembersForField(udtField);
-
- udtField.TypeDeclarationIsPrivate = udtDeclaration.HasPrivateAccessibility();
-
- udtField.NameValidator = udtValidator;
-
- foreach (var udtMemberDeclaration in udtMembers)
- {
- var udtMemberValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedTypeMember);
- if (udtMemberDeclaration.IsArray)
- {
- udtMemberValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedTypeMemberArray);
- }
- var candidateUDTMember = new UserDefinedTypeMemberCandidate(CreateCandidate(udtMemberDeclaration, udtMemberValidator), udtField) as IUserDefinedTypeMemberCandidate;
-
- udtField.AddMember(candidateUDTMember);
- }
-
- var udtVariablesOfSameType = _declarationFinderProvider.DeclarationFinder.UserDeclarations(DeclarationType.Variable)
- .Where(v => v.AsTypeDeclaration == udtDeclaration);
-
- udtField.CanBeObjectStateUDT = udtField.TypeDeclarationIsPrivate
- && udtField.Declaration.HasPrivateAccessibility()
- && udtVariablesOfSameType.Count() == 1;
-
- return udtField;
- }
- else if (target.IsArray)
- {
- return new ArrayCandidate(target, validator);
- }
-
- var candidate = new EncapsulateFieldCandidate(target, validator);
- return candidate;
- }
-
- private (Declaration TypeDeclaration, IEnumerable Members) GetUDTAndMembersForField(IUserDefinedTypeCandidate udtField)
- {
- var userDefinedTypeDeclaration = udtField.Declaration.AsTypeDeclaration;
-
- var udtMembers = _declarationFinderProvider.DeclarationFinder
- .UserDeclarations(DeclarationType.UserDefinedTypeMember)
- .Where(utm => userDefinedTypeDeclaration == utm.ParentDeclaration);
-
- return (userDefinedTypeDeclaration, udtMembers);
- }
- }
-}
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs
new file mode 100644
index 0000000000..944bf911f1
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs
@@ -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 candidates);
+ string BuildObjectStateFieldDeclaration(IObjectStateUDT objectStateUDT);
+ string BuildFieldDeclaration(Declaration target, string identifier);
+ }
+
+ ///
+ /// EncapsulateFieldCodeBuilder wraps an ICodeBuilder instance to extend it for the
+ /// specific needs of an EncapsulateField refactoring action.
+ ///
+ 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 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}";
+ }
+ }
+}
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeModel.cs
new file mode 100644
index 0000000000..04a6d76d73
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeModel.cs
@@ -0,0 +1,31 @@
+using Rubberduck.VBEditor;
+using System.Collections.Generic;
+using System.Linq;
+using Rubberduck.Refactorings.EncapsulateField;
+
+namespace Rubberduck.Refactorings.EncapsulateFieldInsertNewCode
+{
+ public class EncapsulateFieldInsertNewCodeModel : IRefactoringModel
+ {
+ public EncapsulateFieldInsertNewCodeModel(IEnumerable selectedFieldCandidates)
+ {
+ SelectedFieldCandidates = selectedFieldCandidates.ToList();
+ if (SelectedFieldCandidates.Any())
+ {
+ QualifiedModuleName = SelectedFieldCandidates.First().QualifiedModuleName;
+ }
+ }
+
+ public INewContentAggregator NewContentAggregator { set; get; }
+
+ public QualifiedModuleName QualifiedModuleName { get; } = new QualifiedModuleName();
+
+ public bool CreateNewObjectStateUDT => !ObjectStateUDTField?.IsExistingDeclaration ?? false;
+
+ public IObjectStateUDT ObjectStateUDTField { set; get; }
+
+ public IReadOnlyCollection SelectedFieldCandidates { get; }
+
+ public IEnumerable CandidatesRequiringNewBackingFields { set; get; } = Enumerable.Empty();
+ }
+}
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs
new file mode 100644
index 0000000000..98fb44da45
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs
@@ -0,0 +1,116 @@
+using Rubberduck.Parsing.Rewriter;
+using Rubberduck.Parsing.Symbols;
+using Rubberduck.Parsing.VBA;
+using Rubberduck.Refactorings.Common;
+using Rubberduck.Resources;
+using System;
+using System.Diagnostics;
+using System.Linq;
+using Rubberduck.Refactorings.EncapsulateField;
+using System.Collections.Generic;
+
+namespace Rubberduck.Refactorings.EncapsulateFieldInsertNewCode
+{
+ ///
+ /// EncapsulateFieldInsertNewCodeRefactoringAction is a refactoring action dedicated to
+ /// the insertion of new code content generated by EncapsulateField refactoring actions
+ ///
+ public class EncapsulateFieldInsertNewCodeRefactoringAction : CodeOnlyRefactoringActionBase
+ {
+ private readonly IDeclarationFinderProvider _declarationFinderProvider;
+ private readonly IPropertyAttributeSetsGenerator _propertyAttributeSetsGenerator;
+ private readonly IEncapsulateFieldCodeBuilder _encapsulateFieldCodeBuilder;
+
+ public EncapsulateFieldInsertNewCodeRefactoringAction(
+ IDeclarationFinderProvider declarationFinderProvider,
+ IRewritingManager rewritingManager,
+ IPropertyAttributeSetsGenerator propertyAttributeSetsGenerator,
+ IEncapsulateFieldCodeBuilder encapsulateFieldCodeBuilder)
+ : base(rewritingManager)
+ {
+ _declarationFinderProvider = declarationFinderProvider;
+ _propertyAttributeSetsGenerator = propertyAttributeSetsGenerator;
+ _encapsulateFieldCodeBuilder = encapsulateFieldCodeBuilder;
+ }
+
+ public override void Refactor(EncapsulateFieldInsertNewCodeModel model, IRewriteSession rewriteSession)
+ {
+ if (model.CreateNewObjectStateUDT)
+ {
+ CreateObjectStateUDTElements(model, rewriteSession);
+ }
+
+ CreateBackingFields(model, rewriteSession);
+
+ CreatePropertyBlocks(model, rewriteSession);
+
+ InsertBlocks(model, rewriteSession);
+ }
+
+ private void CreateObjectStateUDTElements(EncapsulateFieldInsertNewCodeModel model, IRewriteSession rewriteSession)
+ {
+ var objectStateFieldDeclaration = _encapsulateFieldCodeBuilder.BuildObjectStateFieldDeclaration(model.ObjectStateUDTField);
+ model.NewContentAggregator.AddNewContent(NewContentType.DeclarationBlock, objectStateFieldDeclaration);
+
+ var objectStateTypeDeclarationBlock = _encapsulateFieldCodeBuilder.BuildUserDefinedTypeDeclaration(model.ObjectStateUDTField, model.SelectedFieldCandidates);
+ model.NewContentAggregator.AddNewContent(NewContentType.UserDefinedTypeDeclaration, objectStateTypeDeclarationBlock);
+ }
+
+ private void CreateBackingFields(EncapsulateFieldInsertNewCodeModel model, IRewriteSession rewriteSession)
+ {
+ foreach (var field in model.CandidatesRequiringNewBackingFields)
+ {
+ var newFieldDeclaration = _encapsulateFieldCodeBuilder.BuildFieldDeclaration(field.Declaration, field.BackingIdentifier);
+ model.NewContentAggregator.AddNewContent(NewContentType.DeclarationBlock, newFieldDeclaration);
+ }
+ }
+
+ private void CreatePropertyBlocks(EncapsulateFieldInsertNewCodeModel model, IRewriteSession rewriteSession)
+ {
+ var propAttributeSets = model.SelectedFieldCandidates
+ .SelectMany(f => _propertyAttributeSetsGenerator.GeneratePropertyAttributeSets(f)).ToList();
+
+ foreach (var propertyAttributeSet in propAttributeSets)
+ {
+ Debug.Assert(propertyAttributeSet.Declaration.DeclarationType.HasFlag(DeclarationType.Variable) || propertyAttributeSet.Declaration.DeclarationType.HasFlag(DeclarationType.UserDefinedTypeMember));
+
+ var (Get, Let, Set) = _encapsulateFieldCodeBuilder.BuildPropertyBlocks(propertyAttributeSet);
+
+ var blocks = new List() { Get, Let, Set };
+ blocks.ForEach(s => model.NewContentAggregator.AddNewContent(NewContentType.CodeSectionBlock, s));
+ }
+ }
+
+ private void InsertBlocks(EncapsulateFieldInsertNewCodeModel model, IRewriteSession rewriteSession)
+ {
+
+ var newDeclarationSectionBlock = model.NewContentAggregator.RetrieveBlock(NewContentType.UserDefinedTypeDeclaration, NewContentType.DeclarationBlock, NewContentType.CodeSectionBlock);
+ if (string.IsNullOrEmpty(newDeclarationSectionBlock))
+ {
+ return;
+ }
+
+ var allNewContent = string.Join(NewLines.DOUBLE_SPACE, new string[] { newDeclarationSectionBlock });
+
+ var previewMarker = model.NewContentAggregator.RetrieveBlock(RubberduckUI.EncapsulateField_PreviewMarker);
+ if (!string.IsNullOrEmpty(previewMarker))
+ {
+ allNewContent = $"{allNewContent}{Environment.NewLine}{previewMarker}";
+ }
+
+ var rewriter = rewriteSession.CheckOutModuleRewriter(model.QualifiedModuleName);
+
+ var codeSectionStartIndex = _declarationFinderProvider.DeclarationFinder
+ .Members(model.QualifiedModuleName).Where(m => m.IsMember())
+ .OrderBy(c => c.Selection)
+ .FirstOrDefault()?.Context.Start.TokenIndex;
+
+ if (codeSectionStartIndex.HasValue)
+ {
+ rewriter.InsertBefore(codeSectionStartIndex.Value, $"{allNewContent}{NewLines.DOUBLE_SPACE}");
+ return;
+ }
+ rewriter.InsertBefore(rewriter.TokenStream.Size - 1, $"{NewLines.DOUBLE_SPACE}{allNewContent}");
+ }
+ }
+}
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs
new file mode 100644
index 0000000000..09924c6198
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs
@@ -0,0 +1,146 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Rubberduck.Resources;
+
+namespace Rubberduck.Refactorings.EncapsulateField
+{
+ public enum NewContentType
+ {
+ UserDefinedTypeDeclaration,
+ DeclarationBlock,
+ CodeSectionBlock,
+ }
+
+ public interface INewContentAggregatorFactory
+ {
+ INewContentAggregator Create();
+ }
+
+ public interface INewContentAggregator
+ {
+ ///
+ /// Allows gouping content blocks by NewContentType.
+ ///
+ void AddNewContent(NewContentType contentType, string block);
+ ///
+ /// Allows gouping content blocks by an adhoc identifier.
+ ///
+ void AddNewContent(string contentIdentifier, string block);
+ ///
+ /// Retrieves a block of content aggregated by NewContentType.
+ ///
+ /// NewContentType blocks to aggregate
+ string RetrieveBlock(params NewContentType[] newContentTypes);
+ ///
+ /// Retrieves a block of content aggregated by a user-determined identifier.
+ ///
+ /// NewContentType blocks to aggregate
+ string RetrieveBlock(params string[] contentIdentifiers);
+ ///
+ /// Sets default number of NewLines between blocks of code after
+ /// all retrieving block(s) of code. The default value is 2.
+ ///
+ int NewLineLimit { set; get; }
+ }
+
+ ///
+ /// NewContentAggregator provides a repository for caching generated code blocks
+ /// and retrieving them as an aggregated single block of code organized by NewContentType.
+ ///
+ public class NewContentAggregator : INewContentAggregator
+ {
+ private readonly Dictionary> _newContent;
+ private Dictionary> _unStructuredContent;
+
+ public NewContentAggregator()
+ {
+ _newContent = new Dictionary>
+ {
+ { NewContentType.UserDefinedTypeDeclaration, new List() },
+ { NewContentType.DeclarationBlock, new List() },
+ { NewContentType.CodeSectionBlock, new List() },
+ };
+
+ _unStructuredContent = new Dictionary>();
+ }
+
+ public void AddNewContent(NewContentType contentType, string newContent)
+ {
+ if (!string.IsNullOrEmpty(newContent))
+ {
+ _newContent[contentType].Add(newContent);
+ }
+ }
+
+ public void AddNewContent(string contentIdentifier, string newContent)
+ {
+ if (!string.IsNullOrEmpty(newContent))
+ {
+ if (!_unStructuredContent.ContainsKey(contentIdentifier))
+ {
+ _unStructuredContent.Add(contentIdentifier, new List());
+ }
+ _unStructuredContent[contentIdentifier].Add(newContent);
+ }
+ }
+
+ public string RetrieveBlock(params NewContentType[] newContentTypes)
+ {
+ var block = string.Empty;
+ foreach (var newContentType in newContentTypes)
+ {
+ var newContent = string.Join(NewLines.DOUBLE_SPACE, _newContent[newContentType]);
+ if (!string.IsNullOrEmpty(newContent))
+ {
+ block = string.IsNullOrEmpty(block)
+ ? newContent
+ : $"{block}{NewLines.DOUBLE_SPACE}{newContent}";
+ }
+ }
+ return LimitNewLines(block.Trim(), NewLineLimit);
+ }
+
+ public string RetrieveBlock(params string[] contentIdentifiers)
+ {
+ var block = string.Empty;
+ foreach (var identifier in contentIdentifiers)
+ {
+ if (_unStructuredContent.TryGetValue(identifier, out var adHocContent))
+ {
+ var newContent = string.Join(NewLines.DOUBLE_SPACE, adHocContent);
+ if (!string.IsNullOrEmpty(newContent))
+ {
+ block = string.IsNullOrEmpty(block)
+ ? newContent
+ : $"{block}{NewLines.DOUBLE_SPACE}{newContent}";
+ }
+ }
+ }
+
+ return string.IsNullOrEmpty(block)
+ ? null
+ : LimitNewLines(block.Trim(), NewLineLimit);
+ }
+
+ public int NewLineLimit { set; get; } = 2;
+
+ private static string LimitNewLines(string content, int maxConsecutiveNewlines = 2)
+ {
+ var target = string.Concat(Enumerable.Repeat(Environment.NewLine, maxConsecutiveNewlines + 1).ToList());
+ var replacement = string.Concat(Enumerable.Repeat(Environment.NewLine, maxConsecutiveNewlines).ToList());
+ var guard = 0;
+ var maxAttempts = 100;
+ while (++guard < maxAttempts && content.Contains(target))
+ {
+ content = content.Replace(target, replacement);
+ }
+
+ if (guard >= maxAttempts)
+ {
+ throw new FormatException($"Unable to limit consecutive '{Environment.NewLine}' strings to {maxConsecutiveNewlines}");
+ }
+ return content;
+ }
+ }
+}
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs
index bdc19145c6..181a743582 100644
--- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs
+++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs
@@ -1,194 +1,83 @@
-using System;
+using Rubberduck.Refactorings.EncapsulateFieldUseBackingField;
+using Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember;
+using System;
using System.Collections.Generic;
using System.Linq;
-using Rubberduck.Parsing.Symbols;
-using Rubberduck.Parsing.VBA;
-using Rubberduck.VBEditor;
namespace Rubberduck.Refactorings.EncapsulateField
{
+ ///
+ /// The EncapsulateFieldModel provides a facade to the EncapsulateFieldRefactoring
+ /// by aggregating and simplifying access to the EncapsulateFieldUseBackingFieldModel
+ /// and the EncapsulateFieldUseBackingUDTMemberModel.
+ ///
public class EncapsulateFieldModel : IRefactoringModel
{
- private readonly Func _previewDelegate;
- private QualifiedModuleName _targetQMN;
- private IDeclarationFinderProvider _declarationFinderProvider;
- private IEncapsulateFieldValidationsProvider _validationsProvider;
- private IObjectStateUDT _newObjectStateUDT;
-
- private List _convertedFields;
- private HashSet _objStateCandidates;
-
- private IDictionary)> _udtFieldToUdtDeclarationMap = new Dictionary)>();
-
- public EncapsulateFieldModel(
- Declaration target,
- IEnumerable candidates,
- IEnumerable objectStateUDTCandidates,
- IObjectStateUDT stateUDTField,
- Func previewDelegate,
- IDeclarationFinderProvider declarationFinderProvider,
- IEncapsulateFieldValidationsProvider validationsProvider)
+ public EncapsulateFieldModel(EncapsulateFieldUseBackingFieldModel backingFieldModel,
+ EncapsulateFieldUseBackingUDTMemberModel udtModel,
+ IEncapsulateFieldConflictFinder conflictFinder)
{
- _previewDelegate = previewDelegate;
- _targetQMN = target.QualifiedModuleName;
- _newObjectStateUDT = stateUDTField;
- _declarationFinderProvider = declarationFinderProvider;
- _validationsProvider = validationsProvider;
-
- _useBackingFieldCandidates = candidates.ToList();
- _objStateCandidates = new HashSet(objectStateUDTCandidates);
- _objStateCandidates.Add(_newObjectStateUDT);
-
- EncapsulateFieldStrategy = EncapsulateFieldStrategy.UseBackingFields;
- _activeObjectStateUDT = ObjectStateUDTField;
+ EncapsulateFieldUseBackingFieldModel = backingFieldModel;
+ EncapsulateFieldUseBackingUDTMemberModel = udtModel;
+ ObjectStateUDTCandidates = udtModel.ObjectStateUDTCandidates;
+ ConflictFinder = conflictFinder;
+ EncapsulateFieldUseBackingFieldModel.ConflictFinder = conflictFinder;
+ EncapsulateFieldUseBackingUDTMemberModel.ConflictFinder = conflictFinder;
}
- public QualifiedModuleName QualifiedModuleName => _targetQMN;
+ public EncapsulateFieldUseBackingUDTMemberModel EncapsulateFieldUseBackingUDTMemberModel { get; }
- public string PreviewRefactoring() => _previewDelegate(this);
+ public EncapsulateFieldUseBackingFieldModel EncapsulateFieldUseBackingFieldModel { get; }
- public IEnumerable ObjectStateUDTCandidates => _objStateCandidates;
+ public string PreviewRefactoring() => PreviewProvider?.Preview(this) ?? string.Empty;
- private EncapsulateFieldStrategy _encapsulationFieldStategy;
- public EncapsulateFieldStrategy EncapsulateFieldStrategy
- {
- get => _encapsulationFieldStategy;
- set
- {
- if (_encapsulationFieldStategy == value) { return; }
-
- _encapsulationFieldStategy = value;
+ public IRefactoringPreviewProvider PreviewProvider { set; get; }
- if (_encapsulationFieldStategy == EncapsulateFieldStrategy.UseBackingFields)
- {
- UpdateFieldCandidatesForUseBackingFieldsStrategy();
- return;
- }
- UpdateFieldCandidatesForConvertFieldsToUDTMembersStrategy();
- }
- }
-
- public IEncapsulateFieldValidationsProvider ValidationsProvider => _validationsProvider;
-
- private List _useBackingFieldCandidates;
- public List EncapsulationCandidates
- {
- get
- {
- if (EncapsulateFieldStrategy == EncapsulateFieldStrategy.UseBackingFields)
- {
- return _useBackingFieldCandidates;
- }
-
- if (_convertedFields is null)
- {
- _convertedFields = new List();
- foreach (var field in _useBackingFieldCandidates)
- {
- _convertedFields.Add(new ConvertToUDTMember(field, ObjectStateUDTField));
- }
- }
- return _convertedFields;
- }
- }
-
- public IEnumerable SelectedFieldCandidates
- => EncapsulationCandidates.Where(v => v.EncapsulateFlag);
+ public Action StrategyChangedAction { set; get; } = (m) => { };
- public IEnumerable UDTFieldCandidates
- => EncapsulationCandidates
- .Where(v => v is IUserDefinedTypeCandidate)
- .Cast();
+ public Action ObjectStateFieldChangedAction { set; get; } = (m) => { };
- public IEnumerable SelectedUDTFieldCandidates
- => SelectedFieldCandidates
- .Where(v => v is IUserDefinedTypeCandidate)
- .Cast();
+ public IReadOnlyCollection ObjectStateUDTCandidates { private set; get; }
- public IEncapsulateFieldCandidate this[string encapsulatedFieldTargetID]
- => EncapsulationCandidates.Where(c => c.TargetID.Equals(encapsulatedFieldTargetID)).Single();
+ public IEncapsulateFieldConflictFinder ConflictFinder { set; get; }
- public IEncapsulateFieldCandidate this[Declaration fieldDeclaration]
- => EncapsulationCandidates.Where(c => c.Declaration == fieldDeclaration).Single();
-
- private IObjectStateUDT _activeObjectStateUDT;
public IObjectStateUDT ObjectStateUDTField
{
- get
- {
- _activeObjectStateUDT = ObjectStateUDTCandidates
- .SingleOrDefault(os => os.IsSelected) ?? _newObjectStateUDT;
-
- return _activeObjectStateUDT;
- }
set
{
- if (_activeObjectStateUDT.FieldIdentifier == (value?.FieldIdentifier ?? string.Empty))
+ if (EncapsulateFieldUseBackingUDTMemberModel.ObjectStateUDTField != value)
{
- return;
- }
-
- foreach (var objectStateUDT in ObjectStateUDTCandidates)
- {
- objectStateUDT.IsSelected = false;
- }
-
- _activeObjectStateUDT =
- ObjectStateUDTCandidates.SingleOrDefault(os => os.FieldIdentifier.Equals(value?.FieldIdentifier ?? null))
- ?? _newObjectStateUDT;
-
- _activeObjectStateUDT.IsSelected = true;
-
- if (EncapsulateFieldStrategy == EncapsulateFieldStrategy.ConvertFieldsToUDTMembers)
- {
- foreach (var field in EncapsulationCandidates)
- {
- if (field is IConvertToUDTMember convertedField)
- {
- convertedField.ObjectStateUDT = _activeObjectStateUDT;
- convertedField.ConflictFinder = _validationsProvider.ConflictDetector(EncapsulateFieldStrategy, _declarationFinderProvider);
- convertedField.ConflictFinder.AssignNoConflictIdentifiers(convertedField);
- }
- }
+ EncapsulateFieldUseBackingUDTMemberModel.ObjectStateUDTField = value;
+ ObjectStateFieldChangedAction(this);
}
}
+ get => EncapsulateFieldStrategy == EncapsulateFieldStrategy.ConvertFieldsToUDTMembers
+ ? EncapsulateFieldUseBackingUDTMemberModel.ObjectStateUDTField
+ : null;
}
- private void UpdateFieldCandidatesForUseBackingFieldsStrategy()
+ private EncapsulateFieldStrategy _strategy;
+ public EncapsulateFieldStrategy EncapsulateFieldStrategy
{
- foreach (var candidate in EncapsulationCandidates)
+ set
{
- switch (candidate)
+ if (_strategy != value)
{
- case IUserDefinedTypeCandidate udt:
- candidate.NameValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedType);
- break;
- case IUserDefinedTypeMemberCandidate udtm:
- candidate.NameValidator = candidate.Declaration.IsArray
- ? EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedTypeMemberArray)
- : EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedTypeMember);
- break;
- default:
- candidate.NameValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.Default);
- break;
+ _strategy = value;
+ StrategyChangedAction(this);
}
- candidate.ConflictFinder = _validationsProvider.ConflictDetector(EncapsulateFieldStrategy, _declarationFinderProvider);
- candidate.ConflictFinder.AssignNoConflictIdentifiers(candidate);
}
+ get => _strategy;
}
- private void UpdateFieldCandidatesForConvertFieldsToUDTMembersStrategy()
- {
- foreach (var candidate in EncapsulationCandidates.Cast())
- {
- candidate.ObjectStateUDT = ObjectStateUDTField;
- candidate.NameValidator = candidate.Declaration.IsArray
- ? EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedTypeMemberArray)
- : EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedTypeMember);
+ public IReadOnlyCollection EncapsulationCandidates => EncapsulateFieldStrategy == EncapsulateFieldStrategy.UseBackingFields
+ ? EncapsulateFieldUseBackingFieldModel.EncapsulationCandidates
+ : EncapsulateFieldUseBackingUDTMemberModel.EncapsulationCandidates;
- candidate.ConflictFinder = _validationsProvider.ConflictDetector(EncapsulateFieldStrategy, _declarationFinderProvider);
- candidate.ConflictFinder.AssignNoConflictIdentifiers(candidate);
- }
- }
+ public IEnumerable SelectedFieldCandidates
+ => EncapsulationCandidates.Where(v => v.EncapsulateFlag);
+
+ public IEncapsulateFieldCandidate this[string encapsulatedFieldTargetID]
+ => EncapsulationCandidates.Where(c => c.TargetID.Equals(encapsulatedFieldTargetID)).Single();
}
}
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs
new file mode 100644
index 0000000000..5e7a97d221
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs
@@ -0,0 +1,74 @@
+using Rubberduck.Parsing.Symbols;
+using Rubberduck.Parsing.VBA;
+using Rubberduck.Refactorings.EncapsulateField;
+using System;
+using System.Collections.Generic;
+
+namespace Rubberduck.Refactorings
+{
+ public interface IEncapsulateFieldModelFactory
+ {
+ ///
+ /// Creates the supporting EncapsulateFieldRefactoringAction models for the EncapsulateFieldRefactoring.
+ ///
+ EncapsulateFieldModel Create(Declaration target);
+ }
+
+ public class EncapsulateFieldModelFactory : IEncapsulateFieldModelFactory
+ {
+ private readonly IDeclarationFinderProvider _declarationFinderProvider;
+ private readonly IEncapsulateFieldCandidateFactory _candidatesFactory;
+ private readonly IEncapsulateFieldUseBackingUDTMemberModelFactory _useBackingUDTMemberModelFactory;
+ private readonly IEncapsulateFieldUseBackingFieldModelFactory _useBackingFieldModelFactory;
+ private readonly IEncapsulateFieldCandidateSetsProviderFactory _candidateSetsFactory;
+ private readonly IEncapsulateFieldConflictFinderFactory _encapsulateFieldConflictFinderFactory;
+
+ public EncapsulateFieldModelFactory(IDeclarationFinderProvider declarationFinderProvider,
+ IEncapsulateFieldCandidateFactory candidatesFactory,
+ IEncapsulateFieldUseBackingUDTMemberModelFactory encapsulateFieldUseBackingUDTMemberModelFactory,
+ IEncapsulateFieldUseBackingFieldModelFactory encapsulateFieldUseBackingFieldModelFactory,
+ IEncapsulateFieldCandidateSetsProviderFactory candidateSetsProviderFactory,
+ IEncapsulateFieldConflictFinderFactory encapsulateFieldConflictFinderFactory)
+ {
+ _declarationFinderProvider = declarationFinderProvider;
+ _candidatesFactory = candidatesFactory;
+ _useBackingUDTMemberModelFactory = encapsulateFieldUseBackingUDTMemberModelFactory as IEncapsulateFieldUseBackingUDTMemberModelFactory;
+ _useBackingFieldModelFactory = encapsulateFieldUseBackingFieldModelFactory;
+ _candidateSetsFactory = candidateSetsProviderFactory;
+ _encapsulateFieldConflictFinderFactory = encapsulateFieldConflictFinderFactory;
+ }
+
+ public EncapsulateFieldModel Create(Declaration target)
+ {
+ if (!(target is VariableDeclaration targetField))
+ {
+ throw new ArgumentException();
+ }
+
+ var fieldEncapsulationModels = new List()
+ {
+ new FieldEncapsulationModel(targetField)
+ };
+
+ var contextCollections = _candidateSetsFactory.Create(_declarationFinderProvider, _candidatesFactory, target.QualifiedModuleName);
+
+ var useBackingFieldModel = _useBackingFieldModelFactory.Create(contextCollections, fieldEncapsulationModels);
+ var useBackingUDTMemberModel = _useBackingUDTMemberModelFactory.Create(contextCollections, fieldEncapsulationModels);
+
+ var initialStrategy = useBackingUDTMemberModel.ObjectStateUDTField.IsExistingDeclaration
+ ? EncapsulateFieldStrategy.ConvertFieldsToUDTMembers
+ : EncapsulateFieldStrategy.UseBackingFields;
+
+ var conflictFinder = _encapsulateFieldConflictFinderFactory.Create(_declarationFinderProvider,
+ contextCollections.EncapsulateFieldUseBackingFieldCandidates,
+ contextCollections.ObjectStateFieldCandidates);
+
+ var model = new EncapsulateFieldModel(useBackingFieldModel, useBackingUDTMemberModel, conflictFinder)
+ {
+ EncapsulateFieldStrategy = initialStrategy,
+ };
+
+ return model;
+ }
+ }
+}
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs
new file mode 100644
index 0000000000..8c6765cfe2
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs
@@ -0,0 +1,28 @@
+using Rubberduck.Parsing.VBA;
+using Rubberduck.Refactorings.EncapsulateFieldUseBackingField;
+using Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember;
+
+namespace Rubberduck.Refactorings.EncapsulateField
+{
+ public class EncapsulateFieldPreviewProvider : IRefactoringPreviewProvider
+ {
+ private readonly EncapsulateFieldUseBackingFieldPreviewProvider _useBackingFieldPreviewer;
+ private readonly EncapsulateFieldUseBackingUDTMemberPreviewProvider _useBackingUDTMemberPreviewer;
+ public EncapsulateFieldPreviewProvider(
+ EncapsulateFieldUseBackingFieldPreviewProvider useBackingFieldPreviewProvider,
+ EncapsulateFieldUseBackingUDTMemberPreviewProvider useBackingUDTMemberPreviewProvide)
+ {
+ _useBackingFieldPreviewer = useBackingFieldPreviewProvider;
+ _useBackingUDTMemberPreviewer = useBackingUDTMemberPreviewProvide;
+ }
+
+ public string Preview(EncapsulateFieldModel model)
+ {
+ var preview = model.EncapsulateFieldStrategy == EncapsulateFieldStrategy.ConvertFieldsToUDTMembers
+ ? _useBackingUDTMemberPreviewer.Preview(model.EncapsulateFieldUseBackingUDTMemberModel)
+ : _useBackingFieldPreviewer.Preview(model.EncapsulateFieldUseBackingFieldModel);
+
+ return preview;
+ }
+ }
+}
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs
index 6e99ee7304..20e3c2143e 100644
--- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs
+++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs
@@ -1,10 +1,8 @@
using System.Linq;
-using Rubberduck.Parsing.Rewriter;
using Rubberduck.Parsing.Symbols;
using Rubberduck.Parsing.VBA;
using Rubberduck.Refactorings.Exceptions;
using Rubberduck.VBEditor;
-using Rubberduck.SmartIndenter;
using Rubberduck.VBEditor.Utility;
namespace Rubberduck.Refactorings.EncapsulateField
@@ -17,40 +15,35 @@ public enum EncapsulateFieldStrategy
public class EncapsulateFieldRefactoring : InteractiveRefactoringBase
{
- private readonly IDeclarationFinderProvider _declarationFinderProvider;
private readonly ISelectedDeclarationProvider _selectedDeclarationProvider;
- private readonly IIndenter _indenter;
- private readonly ICodeBuilder _codeBuilder;
- private readonly IRewritingManager _rewritingManager;
+ private readonly EncapsulateFieldRefactoringAction _refactoringAction;
+ private readonly EncapsulateFieldPreviewProvider _previewProvider;
+ private readonly IEncapsulateFieldModelFactory _modelFactory;
public EncapsulateFieldRefactoring(
- IDeclarationFinderProvider declarationFinderProvider,
- IIndenter indenter,
- RefactoringUserInteraction userInteraction,
- IRewritingManager rewritingManager,
- ISelectionProvider selectionProvider,
- ISelectedDeclarationProvider selectedDeclarationProvider,
- ICodeBuilder codeBuilder)
- :base(selectionProvider, userInteraction)
+ EncapsulateFieldRefactoringAction refactoringAction,
+ EncapsulateFieldPreviewProvider previewProvider,
+ IEncapsulateFieldModelFactory encapsulateFieldModelFactory,
+ RefactoringUserInteraction userInteraction,
+ ISelectionProvider selectionProvider,
+ ISelectedDeclarationProvider selectedDeclarationProvider)
+ :base(selectionProvider, userInteraction)
{
- _declarationFinderProvider = declarationFinderProvider;
+ _refactoringAction = refactoringAction;
+ _previewProvider = previewProvider;
_selectedDeclarationProvider = selectedDeclarationProvider;
- _indenter = indenter;
- _codeBuilder = codeBuilder;
- _rewritingManager = rewritingManager;
+ _modelFactory = encapsulateFieldModelFactory;
}
protected override Declaration FindTargetDeclaration(QualifiedSelection targetSelection)
{
var selectedDeclaration = _selectedDeclarationProvider.SelectedDeclaration(targetSelection);
- if (selectedDeclaration == null
+
+ var isInvalidSelection = selectedDeclaration == null
|| selectedDeclaration.DeclarationType != DeclarationType.Variable
- || selectedDeclaration.ParentScopeDeclaration.DeclarationType.HasFlag(DeclarationType.Member))
- {
- return null;
- }
+ || selectedDeclaration.ParentScopeDeclaration.DeclarationType.HasFlag(DeclarationType.Member);
- return selectedDeclaration;
+ return isInvalidSelection ? null : selectedDeclaration;
}
protected override EncapsulateFieldModel InitializeModel(Declaration target)
@@ -65,58 +58,49 @@ protected override EncapsulateFieldModel InitializeModel(Declaration target)
throw new InvalidDeclarationTypeException(target);
}
- var builder = new EncapsulateFieldElementsBuilder(_declarationFinderProvider, target.QualifiedModuleName);
+ var model = _modelFactory.Create(target);
- var selected = builder.Candidates.Single(c => c.Declaration == target);
- selected.EncapsulateFlag = true;
+ model.PreviewProvider = _previewProvider;
- var model = new EncapsulateFieldModel(
- target,
- builder.Candidates,
- builder.ObjectStateUDTCandidates,
- builder.DefaultObjectStateUDT,
- PreviewRewrite,
- _declarationFinderProvider,
- builder.ValidationsProvider);
+ model.StrategyChangedAction = OnStrategyChanged;
- if (builder.ObjectStateUDT != null)
- {
- model.EncapsulateFieldStrategy = EncapsulateFieldStrategy.ConvertFieldsToUDTMembers;
- model.ObjectStateUDTField = builder.ObjectStateUDT;
- }
+ model.ObjectStateFieldChangedAction = OnObjectStateUDTChanged;
+
+ model.ConflictFinder.AssignNoConflictIdentifiers(model.EncapsulationCandidates);
return model;
}
protected override void RefactorImpl(EncapsulateFieldModel model)
{
- var executableRewriteSession = _rewritingManager.CheckOutCodePaneSession();
-
- RefactorRewrite(model, executableRewriteSession);
-
- if (!executableRewriteSession.TryRewrite())
+ if (!model.SelectedFieldCandidates.Any())
{
- throw new RewriteFailedException(executableRewriteSession);
+ return;
}
+
+ _refactoringAction.Refactor(model);
}
- private string PreviewRewrite(EncapsulateFieldModel model)
+ private void OnStrategyChanged(EncapsulateFieldModel model)
{
- var previewSession = RefactorRewrite(model, _rewritingManager.CheckOutCodePaneSession(), true);
+ if (model.EncapsulateFieldStrategy == EncapsulateFieldStrategy.UseBackingFields)
+ {
+ foreach (var objectStateCandidate in model.EncapsulateFieldUseBackingUDTMemberModel.ObjectStateUDTCandidates)
+ {
+ objectStateCandidate.IsSelected = !objectStateCandidate.IsExistingDeclaration;
+ }
+ }
- return previewSession.CheckOutModuleRewriter(model.QualifiedModuleName)
- .GetText();
+ var candidates = model.EncapsulateFieldStrategy == EncapsulateFieldStrategy.UseBackingFields
+ ? model.EncapsulateFieldUseBackingFieldModel.EncapsulationCandidates
+ : model.EncapsulateFieldUseBackingUDTMemberModel.EncapsulationCandidates;
+
+ model.ConflictFinder.AssignNoConflictIdentifiers(candidates);
}
- private IRewriteSession RefactorRewrite(EncapsulateFieldModel model, IRewriteSession refactorRewriteSession, bool asPreview = false)
+ private void OnObjectStateUDTChanged(EncapsulateFieldModel model)
{
- if (!model.SelectedFieldCandidates.Any()) { return refactorRewriteSession; }
-
- var strategy = model.EncapsulateFieldStrategy == EncapsulateFieldStrategy.ConvertFieldsToUDTMembers
- ? new ConvertFieldsToUDTMembers(_declarationFinderProvider, model, _indenter, _codeBuilder) as IEncapsulateStrategy
- : new UseBackingFields(_declarationFinderProvider, model, _indenter, _codeBuilder) as IEncapsulateStrategy;
-
- return strategy.RefactorRewrite(refactorRewriteSession, asPreview);
+ model.ConflictFinder.AssignNoConflictIdentifiers(model.EncapsulateFieldUseBackingUDTMemberModel.EncapsulationCandidates);
}
}
}
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs
new file mode 100644
index 0000000000..b33b298064
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs
@@ -0,0 +1,36 @@
+using Rubberduck.Refactorings.EncapsulateFieldUseBackingField;
+using Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember;
+using System.Linq;
+
+namespace Rubberduck.Refactorings.EncapsulateField
+{
+ public class EncapsulateFieldRefactoringAction : IRefactoringAction
+ {
+ private readonly EncapsulateFieldUseBackingFieldRefactoringAction _useBackingField;
+ private readonly EncapsulateFieldUseBackingUDTMemberRefactoringAction _useBackingUDTMember;
+
+ public EncapsulateFieldRefactoringAction(
+ EncapsulateFieldUseBackingFieldRefactoringAction encapsulateFieldUseBackingField,
+ EncapsulateFieldUseBackingUDTMemberRefactoringAction encapsulateFieldUseUDTMember)
+ {
+ _useBackingField = encapsulateFieldUseBackingField;
+ _useBackingUDTMember = encapsulateFieldUseUDTMember;
+ }
+
+ public void Refactor(EncapsulateFieldModel model)
+ {
+ if (!model?.EncapsulationCandidates.Any() ?? true)
+ {
+ return;
+ }
+
+ if (model.EncapsulateFieldStrategy == EncapsulateFieldStrategy.ConvertFieldsToUDTMembers)
+ {
+ _useBackingUDTMember.Refactor(model.EncapsulateFieldUseBackingUDTMemberModel);
+ return;
+ }
+
+ _useBackingField.Refactor(model.EncapsulateFieldUseBackingFieldModel);
+ }
+ }
+}
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs
new file mode 100644
index 0000000000..4e73441eeb
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs
@@ -0,0 +1,59 @@
+using Rubberduck.Refactorings.ReplaceDeclarationIdentifier;
+using Rubberduck.Refactorings.ReplaceReferences;
+using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences;
+using Rubberduck.Refactorings.EncapsulateFieldInsertNewCode;
+using Rubberduck.Refactorings.ModifyUserDefinedType;
+
+namespace Rubberduck.Refactorings.EncapsulateField
+{
+ public interface IEncapsulateFieldRefactoringActionsProvider
+ {
+ ICodeOnlyRefactoringAction ReplaceReferences { get; }
+ ICodeOnlyRefactoringAction ReplaceUDTMemberReferences { get; }
+ ICodeOnlyRefactoringAction ReplaceDeclarationIdentifiers { get; }
+ ICodeOnlyRefactoringAction ModifyUserDefinedType { get; }
+ ICodeOnlyRefactoringAction EncapsulateFieldInsertNewCode { get; }
+ }
+
+ ///
+ /// EncapsulateFieldRefactoringActionsProvider reduces the number of EncapsulateField refactoring action
+ /// constructor parameters. It provides Refactoring Actions common to the EncapsulateFieldRefactoringActions
+ ///
+ public class EncapsulateFieldRefactoringActionsProvider : IEncapsulateFieldRefactoringActionsProvider
+ {
+ private readonly ReplaceReferencesRefactoringAction _replaceReferences;
+ private readonly ReplaceDeclarationIdentifierRefactoringAction _replaceDeclarationIdentifiers;
+ private readonly ReplacePrivateUDTMemberReferencesRefactoringAction _replaceUDTMemberReferencesRefactoringAction;
+ private readonly ModifyUserDefinedTypeRefactoringAction _modifyUDTRefactoringAction;
+ private readonly EncapsulateFieldInsertNewCodeRefactoringAction _encapsulateFieldInsertNewCodeRefactoringAction;
+
+ public EncapsulateFieldRefactoringActionsProvider(
+ ReplaceReferencesRefactoringAction replaceReferencesRefactoringAction,
+ ReplacePrivateUDTMemberReferencesRefactoringAction replaceUDTMemberReferencesRefactoringAction,
+ ReplaceDeclarationIdentifierRefactoringAction replaceDeclarationIdentifierRefactoringAction,
+ ModifyUserDefinedTypeRefactoringAction modifyUserDefinedTypeRefactoringAction,
+ EncapsulateFieldInsertNewCodeRefactoringAction encapsulateFieldInsertNewCodeRefactoringAction)
+ {
+ _replaceReferences = replaceReferencesRefactoringAction;
+ _replaceUDTMemberReferencesRefactoringAction = replaceUDTMemberReferencesRefactoringAction;
+ _replaceDeclarationIdentifiers = replaceDeclarationIdentifierRefactoringAction;
+ _modifyUDTRefactoringAction = modifyUserDefinedTypeRefactoringAction;
+ _encapsulateFieldInsertNewCodeRefactoringAction = encapsulateFieldInsertNewCodeRefactoringAction;
+ }
+
+ public ICodeOnlyRefactoringAction ReplaceReferences
+ => _replaceReferences;
+
+ public ICodeOnlyRefactoringAction ReplaceDeclarationIdentifiers
+ => _replaceDeclarationIdentifiers;
+
+ public ICodeOnlyRefactoringAction ReplaceUDTMemberReferences
+ => _replaceUDTMemberReferencesRefactoringAction;
+
+ public ICodeOnlyRefactoringAction ModifyUserDefinedType
+ => _modifyUDTRefactoringAction;
+
+ public ICodeOnlyRefactoringAction EncapsulateFieldInsertNewCode
+ => _encapsulateFieldInsertNewCodeRefactoringAction;
+ }
+}
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModel.cs
new file mode 100644
index 0000000000..647ff2e2a2
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModel.cs
@@ -0,0 +1,30 @@
+using System.Collections.Generic;
+using System.Linq;
+using Rubberduck.VBEditor;
+using Rubberduck.Refactorings.EncapsulateField;
+
+namespace Rubberduck.Refactorings.EncapsulateFieldUseBackingField
+{
+ public class EncapsulateFieldUseBackingFieldModel : IRefactoringModel
+ {
+ public EncapsulateFieldUseBackingFieldModel(IEnumerable candidates)
+ {
+ EncapsulationCandidates = candidates.ToList();
+ if (EncapsulationCandidates.Any())
+ {
+ QualifiedModuleName = EncapsulationCandidates.First().QualifiedModuleName;
+ }
+ }
+
+ public INewContentAggregator NewContentAggregator { set; get; }
+
+ public IEncapsulateFieldConflictFinder ConflictFinder { set; get; }
+
+ public IReadOnlyCollection EncapsulationCandidates { get; }
+
+ public IReadOnlyCollection SelectedFieldCandidates
+ => EncapsulationCandidates.Where(c => c.EncapsulateFlag).ToList();
+
+ public QualifiedModuleName QualifiedModuleName { get; } = new QualifiedModuleName();
+ }
+}
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs
new file mode 100644
index 0000000000..9c7a81bbc3
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs
@@ -0,0 +1,81 @@
+using Rubberduck.Parsing.VBA;
+using Rubberduck.Refactorings.EncapsulateField;
+using Rubberduck.Refactorings.EncapsulateFieldUseBackingField;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Rubberduck.Refactorings
+{
+ public interface IEncapsulateFieldUseBackingFieldModelFactory
+ {
+ ///
+ /// Creates an EncapsulateFieldUseBackingFieldModel used by the EncapsulateFieldUseBackingFieldRefactoringAction.
+ ///
+ EncapsulateFieldUseBackingFieldModel Create(IEnumerable fieldModels);
+
+ ///
+ /// Creates an EncapsulateFieldUseBackingFieldModel based upon collection of
+ /// IEncapsulateFieldCandidate instances.
+ /// This function is intended for exclusive use by the EncapsulateFieldModelFactory
+ ///
+ EncapsulateFieldUseBackingFieldModel Create(IEncapsulateFieldCandidateSetsProvider contextCollections, IEnumerable fieldModels);
+ }
+
+ public class EncapsulateFieldUseBackingFieldModelFactory : IEncapsulateFieldUseBackingFieldModelFactory
+ {
+ private readonly IDeclarationFinderProvider _declarationFinderProvider;
+ private readonly IEncapsulateFieldCandidateFactory _candidatesFactory;
+ private readonly IEncapsulateFieldCandidateSetsProviderFactory _candidateSetsFactory;
+ private readonly IEncapsulateFieldConflictFinderFactory _conflictFinderFactory;
+
+ public EncapsulateFieldUseBackingFieldModelFactory(IDeclarationFinderProvider declarationFinderProvider,
+ IEncapsulateFieldCandidateFactory candidatesFactory,
+ IEncapsulateFieldCandidateSetsProviderFactory candidateSetsFactory,
+ IEncapsulateFieldConflictFinderFactory encapsulateFieldConflictFinderFactory)
+ {
+ _declarationFinderProvider = declarationFinderProvider;
+ _candidatesFactory = candidatesFactory;
+ _candidateSetsFactory = candidateSetsFactory;
+ _conflictFinderFactory = encapsulateFieldConflictFinderFactory;
+ }
+
+ public EncapsulateFieldUseBackingFieldModel Create(IEnumerable fieldModels)
+ {
+ if (!fieldModels.Any())
+ {
+ return new EncapsulateFieldUseBackingFieldModel(Enumerable.Empty());
+ }
+
+ var contextCollections = _candidateSetsFactory.Create(_declarationFinderProvider, _candidatesFactory, fieldModels.First().Declaration.QualifiedModuleName);
+
+ return Create(contextCollections, fieldModels);
+ }
+
+ public EncapsulateFieldUseBackingFieldModel Create(IEncapsulateFieldCandidateSetsProvider contextCollections, IEnumerable fieldModels)
+ {
+ var fieldCandidates = contextCollections.EncapsulateFieldUseBackingFieldCandidates.ToList();
+
+ foreach (var fieldModel in fieldModels)
+ {
+ var candidate = fieldCandidates.Single(c => c.Declaration.Equals(fieldModel.Declaration));
+ candidate.EncapsulateFlag = true;
+ candidate.IsReadOnly = fieldModel.IsReadOnly;
+ if (fieldModel.PropertyIdentifier != null)
+ {
+ candidate.PropertyIdentifier = fieldModel.PropertyIdentifier;
+ }
+ }
+
+ var conflictsFinder = _conflictFinderFactory.Create(_declarationFinderProvider,
+ contextCollections.EncapsulateFieldUseBackingFieldCandidates,
+ contextCollections.ObjectStateFieldCandidates);
+
+ fieldCandidates.ForEach(c => c.ConflictFinder = conflictsFinder);
+
+ return new EncapsulateFieldUseBackingFieldModel(fieldCandidates)
+ {
+ ConflictFinder = conflictsFinder
+ };
+ }
+ }
+}
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldPreviewProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldPreviewProvider.cs
new file mode 100644
index 0000000000..26ee695e82
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldPreviewProvider.cs
@@ -0,0 +1,32 @@
+using Rubberduck.Parsing.Rewriter;
+using Rubberduck.Refactorings.EncapsulateField;
+using Rubberduck.Resources;
+using Rubberduck.VBEditor;
+
+namespace Rubberduck.Refactorings.EncapsulateFieldUseBackingField
+{
+ public class EncapsulateFieldUseBackingFieldPreviewProvider : RefactoringPreviewProviderWrapperBase
+ {
+ private readonly INewContentAggregatorFactory _aggregatorFactory;
+
+ public EncapsulateFieldUseBackingFieldPreviewProvider(EncapsulateFieldUseBackingFieldRefactoringAction refactoringAction,
+ IRewritingManager rewritingManager,
+ INewContentAggregatorFactory aggregatorFactory)
+ : base(refactoringAction, rewritingManager)
+ {
+ _aggregatorFactory = aggregatorFactory;
+ }
+
+ public override string Preview(EncapsulateFieldUseBackingFieldModel model)
+ {
+ model.NewContentAggregator = _aggregatorFactory.Create();
+ model.NewContentAggregator.AddNewContent(RubberduckUI.EncapsulateField_PreviewMarker, RubberduckUI.EncapsulateField_PreviewMarker);
+ return base.Preview(model);
+ }
+
+ protected override QualifiedModuleName ComponentToShow(EncapsulateFieldUseBackingFieldModel model)
+ {
+ return model.QualifiedModuleName;
+ }
+ }
+}
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringAction.cs
new file mode 100644
index 0000000000..b41fff423a
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringAction.cs
@@ -0,0 +1,208 @@
+using Rubberduck.Parsing;
+using Rubberduck.Parsing.Grammar;
+using Rubberduck.Parsing.Rewriter;
+using Rubberduck.Parsing.Symbols;
+using Rubberduck.Refactorings.Common;
+using Rubberduck.Refactorings.ReplaceDeclarationIdentifier;
+using Rubberduck.Refactorings.ReplaceReferences;
+using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Rubberduck.Refactorings.EncapsulateField;
+using Rubberduck.Refactorings.EncapsulateFieldInsertNewCode;
+
+namespace Rubberduck.Refactorings.EncapsulateFieldUseBackingField
+{
+ public class EncapsulateFieldUseBackingFieldRefactoringAction : CodeOnlyRefactoringActionBase
+ {
+ private readonly ICodeOnlyRefactoringAction _replaceUDTMemberReferencesRefactoringAction;
+ private readonly ICodeOnlyRefactoringAction _replaceReferencesRefactoringAction;
+ private readonly ICodeOnlyRefactoringAction _replaceDeclarationIdentifiers;
+ private readonly ICodeOnlyRefactoringAction _encapsulateFieldInsertNewCodeRefactoringAction;
+ private readonly IReplacePrivateUDTMemberReferencesModelFactory _replaceUDTMemberReferencesModelFactory;
+ private readonly INewContentAggregatorFactory _newContentAggregatorFactory;
+
+ public EncapsulateFieldUseBackingFieldRefactoringAction(
+ IEncapsulateFieldRefactoringActionsProvider refactoringActionsProvider,
+ IReplacePrivateUDTMemberReferencesModelFactory replaceUDTMemberReferencesModelFactory,
+ IRewritingManager rewritingManager,
+ INewContentAggregatorFactory newContentAggregatorFactory)
+ :base(rewritingManager)
+ {
+ _replaceUDTMemberReferencesRefactoringAction = refactoringActionsProvider.ReplaceUDTMemberReferences;
+ _replaceReferencesRefactoringAction = refactoringActionsProvider.ReplaceReferences;
+ _replaceDeclarationIdentifiers = refactoringActionsProvider.ReplaceDeclarationIdentifiers;
+ _encapsulateFieldInsertNewCodeRefactoringAction = refactoringActionsProvider.EncapsulateFieldInsertNewCode;
+ _replaceUDTMemberReferencesModelFactory = replaceUDTMemberReferencesModelFactory;
+ _newContentAggregatorFactory = newContentAggregatorFactory;
+ }
+
+ public override void Refactor(EncapsulateFieldUseBackingFieldModel model, IRewriteSession rewriteSession)
+ {
+ if (!model.SelectedFieldCandidates.Any())
+ {
+ return;
+ }
+
+ var publicFieldsDeclaredInListsToReDeclareAsPrivateBackingFields
+ = model.SelectedFieldCandidates
+ .Where(f => f.Declaration.IsDeclaredInList()
+ && !f.Declaration.HasPrivateAccessibility())
+ .ToList();
+
+ ModifyFields(model, publicFieldsDeclaredInListsToReDeclareAsPrivateBackingFields, rewriteSession);
+
+ ModifyReferences(model, rewriteSession);
+
+ InsertNewContent(model, publicFieldsDeclaredInListsToReDeclareAsPrivateBackingFields, rewriteSession);
+ }
+
+ private void ModifyFields(EncapsulateFieldUseBackingFieldModel model, List publicFieldsToRemove, IRewriteSession rewriteSession)
+ {
+ var rewriter = rewriteSession.CheckOutModuleRewriter(model.QualifiedModuleName);
+ rewriter.RemoveVariables(publicFieldsToRemove.Select(f => f.Declaration)
+ .Cast());
+
+ var retainedFieldDeclarations = model.SelectedFieldCandidates
+ .Except(publicFieldsToRemove)
+ .ToList();
+
+ if (retainedFieldDeclarations.Any())
+ {
+ MakeImplicitDeclarationTypeExplicit(retainedFieldDeclarations, rewriter);
+
+ SetPrivateVariableVisiblity(retainedFieldDeclarations, rewriter);
+
+ Rename(retainedFieldDeclarations, rewriteSession);
+ }
+ }
+
+ private void ModifyReferences(EncapsulateFieldUseBackingFieldModel model, IRewriteSession rewriteSession)
+ {
+ var privateUdtInstances = model.SelectedFieldCandidates
+ .Where(f => (f.Declaration.AsTypeDeclaration?.DeclarationType.HasFlag(DeclarationType.UserDefinedType) ?? false)
+ && f.Declaration.AsTypeDeclaration.Accessibility == Accessibility.Private);
+
+ ReplaceUDTMemberReferencesOfPrivateUDTFields(privateUdtInstances, rewriteSession);
+
+ ReplaceEncapsulatedFieldReferences(model.SelectedFieldCandidates.Except(privateUdtInstances), rewriteSession);
+ }
+
+ private void InsertNewContent(EncapsulateFieldUseBackingFieldModel model, List candidatesRequiringNewBackingFields, IRewriteSession rewriteSession)
+ {
+ var aggregator = model.NewContentAggregator ?? _newContentAggregatorFactory.Create();
+ model.NewContentAggregator = null;
+
+ var encapsulateFieldInsertNewCodeModel = new EncapsulateFieldInsertNewCodeModel(model.SelectedFieldCandidates)
+ {
+ CandidatesRequiringNewBackingFields = candidatesRequiringNewBackingFields,
+ NewContentAggregator = aggregator
+ };
+
+ _encapsulateFieldInsertNewCodeRefactoringAction.Refactor(encapsulateFieldInsertNewCodeModel, rewriteSession);
+ }
+
+ private void ReplaceEncapsulatedFieldReferences(IEnumerable fieldCandidates, IRewriteSession rewriteSession)
+ {
+ var model = new ReplaceReferencesModel()
+ {
+ ModuleQualifyExternalReferences = true
+ };
+
+ foreach (var field in fieldCandidates)
+ {
+ InitializeModel(model, field);
+ }
+
+ _replaceReferencesRefactoringAction.Refactor(model, rewriteSession);
+ }
+
+ private void ReplaceUDTMemberReferencesOfPrivateUDTFields(IEnumerable udtFieldCandidates, IRewriteSession rewriteSession)
+ {
+ if (!udtFieldCandidates.Any())
+ {
+ return;
+ }
+
+ var replacePrivateUDTMemberReferencesModel
+ = _replaceUDTMemberReferencesModelFactory.Create(udtFieldCandidates.Select(f => f.Declaration).Cast());
+
+ foreach (var udtfield in udtFieldCandidates)
+ {
+ InitializeModel(replacePrivateUDTMemberReferencesModel, udtfield);
+ }
+ _replaceUDTMemberReferencesRefactoringAction.Refactor(replacePrivateUDTMemberReferencesModel, rewriteSession);
+ }
+
+ private void InitializeModel(ReplaceReferencesModel model, IEncapsulateFieldCandidate field)
+ {
+ foreach (var idRef in field.Declaration.References)
+ {
+ var replacementExpression = field.PropertyIdentifier;
+
+ if (idRef.QualifiedModuleName == field.QualifiedModuleName && field.Declaration.IsArray)
+ {
+ replacementExpression = field.BackingIdentifier;
+ }
+
+ model.AssignReferenceReplacementExpression(idRef, replacementExpression);
+ }
+ }
+
+ private void InitializeModel(ReplacePrivateUDTMemberReferencesModel model, IEncapsulateFieldCandidate udtfield)
+ {
+ foreach (var udtMember in model.UDTMembers)
+ {
+ var udtExpressions = new PrivateUDTMemberReferenceReplacementExpressions($"{udtfield.IdentifierName}.{udtMember.IdentifierName}")
+ {
+ LocalReferenceExpression = udtMember.IdentifierName,
+ };
+
+ model.AssignUDTMemberReferenceExpressions(udtfield.Declaration as VariableDeclaration, udtMember, udtExpressions);
+ }
+ }
+
+ private static void MakeImplicitDeclarationTypeExplicit(IReadOnlyCollection fields, IModuleRewriter rewriter)
+ {
+ var fieldsToChange = fields.Where(f => !f.Declaration.Context.TryGetChildContext(out _))
+ .Select(f => f.Declaration);
+
+ foreach (var field in fieldsToChange)
+ {
+ rewriter.InsertAfter(field.Context.Stop.TokenIndex, $" {Tokens.As} {field.AsTypeName}");
+ }
+ }
+
+ private static void SetPrivateVariableVisiblity(IReadOnlyCollection fields, IModuleRewriter rewriter)
+ {
+ var visibility = Accessibility.Private.TokenString();
+ foreach (var element in fields.Where(f => !f.Declaration.HasPrivateAccessibility()).Select(f => f.Declaration))
+ {
+ if (!element.IsVariable())
+ {
+ throw new ArgumentException();
+ }
+
+ var variableStmtContext = element.Context.GetAncestor();
+ var visibilityContext = variableStmtContext.GetChild();
+
+ if (visibilityContext != null)
+ {
+ rewriter.Replace(visibilityContext, visibility);
+ continue;
+ }
+ rewriter.InsertBefore(element.Context.Start.TokenIndex, $"{visibility} ");
+ }
+ }
+
+ private void Rename(IReadOnlyCollection fields, IRewriteSession rewriteSession)
+ {
+ var fieldToNewNamePairs = fields.Where(f => !f.BackingIdentifier.Equals(f.Declaration.IdentifierName, StringComparison.InvariantCultureIgnoreCase))
+ .Select(f => (f.Declaration, f.BackingIdentifier));
+
+ var model = new ReplaceDeclarationIdentifierModel(fieldToNewNamePairs);
+ _replaceDeclarationIdentifiers.Refactor(model, rewriteSession);
+ }
+ }
+}
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModel.cs
new file mode 100644
index 0000000000..7dae8bb453
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModel.cs
@@ -0,0 +1,60 @@
+using System.Collections.Generic;
+using System.Linq;
+using Rubberduck.VBEditor;
+using Rubberduck.Refactorings.EncapsulateField;
+
+namespace Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember
+{
+ public class EncapsulateFieldUseBackingUDTMemberModel : IRefactoringModel
+ {
+ private List _encapsulateAsUDTMemberCandidates;
+
+ public EncapsulateFieldUseBackingUDTMemberModel(IObjectStateUDT targetObjectStateUserDefinedTypeField,
+ IEnumerable encapsulateAsUDTMemberCandidates,
+ IEnumerable objectStateUserDefinedTypeCandidates)
+ {
+ _encapsulateAsUDTMemberCandidates = encapsulateAsUDTMemberCandidates.ToList();
+ EncapsulationCandidates = _encapsulateAsUDTMemberCandidates.Cast().ToList();
+
+ ObjectStateUDTField = targetObjectStateUserDefinedTypeField;
+
+ ObjectStateUDTCandidates = objectStateUserDefinedTypeCandidates.ToList();
+
+ QualifiedModuleName = encapsulateAsUDTMemberCandidates.First().QualifiedModuleName;
+ }
+
+ public INewContentAggregator NewContentAggregator { set; get; }
+
+ public IReadOnlyCollection ObjectStateUDTCandidates { get; }
+
+ public IEncapsulateFieldConflictFinder ConflictFinder { set; get; }
+
+ public IReadOnlyCollection EncapsulationCandidates { get; }
+
+ public IReadOnlyCollection SelectedFieldCandidates
+ => _encapsulateAsUDTMemberCandidates
+ .Where(v => v.EncapsulateFlag)
+ .ToList();
+
+ public QualifiedModuleName QualifiedModuleName { get; }
+
+ public IObjectStateUDT ObjectStateUDTField
+ {
+ set
+ {
+ if (ObjectStateUDTField != null)
+ {
+ ObjectStateUDTField.IsSelected = false;
+ }
+
+ if (value != null)
+ {
+ value.IsSelected = true;
+ }
+
+ _encapsulateAsUDTMemberCandidates.ForEach(cf => cf.ObjectStateUDT = value);
+ }
+ get => _encapsulateAsUDTMemberCandidates.FirstOrDefault()?.ObjectStateUDT;
+ }
+ }
+}
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs
new file mode 100644
index 0000000000..f9456aa6b5
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs
@@ -0,0 +1,130 @@
+using Rubberduck.Parsing.Symbols;
+using Rubberduck.Parsing.VBA;
+using Rubberduck.Refactorings.EncapsulateField;
+using Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Rubberduck.Refactorings
+{
+ public interface IEncapsulateFieldUseBackingUDTMemberModelFactory
+ {
+ ///
+ /// Creates an EncapsulateFieldUseBackingUDTMemberModel used by the EncapsulateFieldUseBackingUDTMemberRefactoringAction.
+ ///
+ /// Optional: UserDefinedType Field to host the Encapsulated Field(s)
+ EncapsulateFieldUseBackingUDTMemberModel Create(IEnumerable fieldModels, Declaration objectStateField = null);
+
+ ///
+ /// Creates an EncapsulateFieldUseBackingUDTMemberModel based upon collection of
+ /// IEncapsulateFieldCandidate instances.
+ /// This function is intended for exclusive use by the EncapsulateFieldModelFactory
+ ///
+ /// Optional: UserDefinedType Field to host the Encapsulated Field(s)
+ EncapsulateFieldUseBackingUDTMemberModel Create(IEncapsulateFieldCandidateSetsProvider contextCollections, IEnumerable fieldModels, Declaration objectStateField = null);
+ }
+
+ public class EncapsulateFieldUseBackingUDTMemberModelFactory : IEncapsulateFieldUseBackingUDTMemberModelFactory
+ {
+ private readonly IDeclarationFinderProvider _declarationFinderProvider;
+ private readonly IEncapsulateFieldCandidateFactory _candidatesFactory;
+ private readonly IEncapsulateFieldCandidateSetsProviderFactory _candidateSetsFactory;
+ private readonly IEncapsulateFieldConflictFinderFactory _conflictFinderFactory;
+
+ public EncapsulateFieldUseBackingUDTMemberModelFactory(IDeclarationFinderProvider declarationFinderProvider,
+ IEncapsulateFieldCandidateFactory candidatesFactory,
+ IEncapsulateFieldCandidateSetsProviderFactory candidateSetsFactory,
+ IEncapsulateFieldConflictFinderFactory encapsulateFieldConflictFinderFactory)
+ {
+ _declarationFinderProvider = declarationFinderProvider;
+ _candidatesFactory = candidatesFactory;
+ _candidateSetsFactory = candidateSetsFactory;
+ _conflictFinderFactory = encapsulateFieldConflictFinderFactory;
+ }
+
+ public EncapsulateFieldUseBackingUDTMemberModel Create(IEnumerable fieldModels, Declaration objectStateField)
+ {
+ if (!fieldModels.Any())
+ {
+ throw new ArgumentException();
+ }
+
+ var contextCollections = _candidateSetsFactory.Create(_declarationFinderProvider, _candidatesFactory, fieldModels.First().Declaration.QualifiedModuleName);
+
+ return Create(contextCollections, fieldModels, objectStateField);
+ }
+
+ public EncapsulateFieldUseBackingUDTMemberModel Create(IEncapsulateFieldCandidateSetsProvider contextCollections, IEnumerable fieldModels, Declaration objectStateField = null)
+ {
+ var fieldCandidates = contextCollections.EncapsulateFieldUseBackingUDTMemberCandidates.ToList();
+
+ if (objectStateField != null
+ && (objectStateField.Accessibility != Accessibility.Private
+ || !fieldCandidates.Any(c => c.Declaration == objectStateField && c.WrappedCandidate is IUserDefinedTypeCandidate)))
+ {
+ throw new ArgumentException("The object state Field must be a Private UserDefinedType");
+ }
+
+ var objectStateFieldCandidates = contextCollections.ObjectStateFieldCandidates;
+
+ var defaultObjectStateUDT = objectStateFieldCandidates.FirstOrDefault(os => !os.IsExistingDeclaration);
+
+ var targetStateUDT = DetermineObjectStateFieldTarget(defaultObjectStateUDT, objectStateField, objectStateFieldCandidates);
+
+ foreach (var fieldModel in fieldModels)
+ {
+ var candidate = fieldCandidates.Single(c => c.Declaration.Equals(fieldModel.Declaration));
+ candidate.EncapsulateFlag = true;
+ candidate.IsReadOnly = fieldModel.IsReadOnly;
+ if (fieldModel.PropertyIdentifier != null)
+ {
+ candidate.PropertyIdentifier = fieldModel.PropertyIdentifier;
+ }
+ }
+
+ var conflictsFinder = _conflictFinderFactory.Create(_declarationFinderProvider,
+ contextCollections.EncapsulateFieldUseBackingFieldCandidates,
+ contextCollections.ObjectStateFieldCandidates);
+
+ fieldCandidates.ForEach(c => c.ConflictFinder = conflictsFinder);
+
+ if (objectStateField == null && !targetStateUDT.IsExistingDeclaration)
+ {
+ conflictsFinder.AssignNoConflictIdentifiers(targetStateUDT);
+ }
+
+ fieldCandidates.ForEach(c => conflictsFinder.AssignNoConflictIdentifiers(c));
+
+ return new EncapsulateFieldUseBackingUDTMemberModel(targetStateUDT, fieldCandidates, objectStateFieldCandidates)
+ {
+ ConflictFinder = conflictsFinder
+ };
+ }
+
+ IObjectStateUDT DetermineObjectStateFieldTarget(IObjectStateUDT defaultObjectStateField, Declaration objectStateFieldTarget, IReadOnlyCollection objectStateFieldCandidates)
+ {
+ var targetStateUDT = defaultObjectStateField;
+
+ if (objectStateFieldTarget != null)
+ {
+ targetStateUDT = objectStateFieldCandidates.Single(osc => objectStateFieldTarget == osc.Declaration);
+ }
+ else
+ {
+ var preExistingDefaultUDTField =
+ objectStateFieldCandidates.Where(osc => osc.TypeIdentifier == defaultObjectStateField.TypeIdentifier
+ && osc.IsExistingDeclaration);
+
+ if (preExistingDefaultUDTField.Any() && preExistingDefaultUDTField.Count() == 1)
+ {
+ targetStateUDT = preExistingDefaultUDTField.First();
+ }
+ }
+
+ targetStateUDT.IsSelected = true;
+
+ return targetStateUDT;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberPreviewProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberPreviewProvider.cs
new file mode 100644
index 0000000000..28ced18cf4
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberPreviewProvider.cs
@@ -0,0 +1,32 @@
+using Rubberduck.Parsing.Rewriter;
+using Rubberduck.Refactorings.EncapsulateField;
+using Rubberduck.Resources;
+using Rubberduck.VBEditor;
+
+namespace Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember
+{
+ public class EncapsulateFieldUseBackingUDTMemberPreviewProvider : RefactoringPreviewProviderWrapperBase
+ {
+ private readonly INewContentAggregatorFactory _aggregatorFactory;
+
+ public EncapsulateFieldUseBackingUDTMemberPreviewProvider(EncapsulateFieldUseBackingUDTMemberRefactoringAction refactoringAction,
+ IRewritingManager rewritingManager,
+ INewContentAggregatorFactory aggregatorFactory)
+ : base(refactoringAction, rewritingManager)
+ {
+ _aggregatorFactory = aggregatorFactory;
+ }
+
+ public override string Preview(EncapsulateFieldUseBackingUDTMemberModel model)
+ {
+ model.NewContentAggregator = _aggregatorFactory.Create();
+ model.NewContentAggregator.AddNewContent(RubberduckUI.EncapsulateField_PreviewMarker, RubberduckUI.EncapsulateField_PreviewMarker);
+ return base.Preview(model);
+ }
+
+ protected override QualifiedModuleName ComponentToShow(EncapsulateFieldUseBackingUDTMemberModel model)
+ {
+ return model.QualifiedModuleName;
+ }
+ }
+}
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs
new file mode 100644
index 0000000000..5ca49ff474
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs
@@ -0,0 +1,163 @@
+using Rubberduck.Parsing.Rewriter;
+using Rubberduck.Parsing.Symbols;
+using Rubberduck.Refactorings.Common;
+using Rubberduck.Refactorings.ReplaceReferences;
+using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences;
+using System.Linq;
+using Rubberduck.Refactorings.EncapsulateField;
+using Rubberduck.Refactorings.EncapsulateFieldInsertNewCode;
+using System.Collections.Generic;
+using Rubberduck.Refactorings.ModifyUserDefinedType;
+
+namespace Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember
+{
+ public class EncapsulateFieldUseBackingUDTMemberRefactoringAction : CodeOnlyRefactoringActionBase
+ {
+ private readonly ICodeOnlyRefactoringAction _modifyUDTRefactoringAction;
+ private readonly ICodeOnlyRefactoringAction _replacePrivateUDTMemberReferencesRefactoringAction;
+ private readonly ICodeOnlyRefactoringAction _replaceReferencesRefactoringAction;
+ private readonly ICodeOnlyRefactoringAction _encapsulateFieldInsertNewCodeRefactoringAction;
+ private readonly INewContentAggregatorFactory _newContentAggregatorFactory;
+ private readonly IReplacePrivateUDTMemberReferencesModelFactory _replaceUDTMemberReferencesModelFactory;
+
+ public EncapsulateFieldUseBackingUDTMemberRefactoringAction(
+ IEncapsulateFieldRefactoringActionsProvider refactoringActionsProvider,
+ IReplacePrivateUDTMemberReferencesModelFactory replaceUDTMemberReferencesModelFactory,
+ IRewritingManager rewritingManager,
+ INewContentAggregatorFactory newContentAggregatorFactory)
+ : base(rewritingManager)
+ {
+ _modifyUDTRefactoringAction = refactoringActionsProvider.ModifyUserDefinedType;
+ _replacePrivateUDTMemberReferencesRefactoringAction = refactoringActionsProvider.ReplaceUDTMemberReferences;
+ _replaceReferencesRefactoringAction = refactoringActionsProvider.ReplaceReferences;
+ _encapsulateFieldInsertNewCodeRefactoringAction = refactoringActionsProvider.EncapsulateFieldInsertNewCode;
+ _replaceUDTMemberReferencesModelFactory = replaceUDTMemberReferencesModelFactory;
+ _newContentAggregatorFactory = newContentAggregatorFactory;
+ }
+
+ public override void Refactor(EncapsulateFieldUseBackingUDTMemberModel model, IRewriteSession rewriteSession)
+ {
+ if (!model.SelectedFieldCandidates.Any())
+ {
+ return;
+ }
+
+ ModifyFields(model, rewriteSession);
+
+ ModifyReferences(model, rewriteSession);
+
+ InsertNewContent(model, rewriteSession);
+ }
+
+ private void ModifyFields(EncapsulateFieldUseBackingUDTMemberModel encapsulateFieldModel, IRewriteSession rewriteSession)
+ {
+ var rewriter = rewriteSession.CheckOutModuleRewriter(encapsulateFieldModel.QualifiedModuleName);
+
+ if (encapsulateFieldModel.ObjectStateUDTField.IsExistingDeclaration)
+ {
+ var model = new ModifyUserDefinedTypeModel(encapsulateFieldModel.ObjectStateUDTField.AsTypeDeclaration);
+
+ foreach (var candidate in encapsulateFieldModel.SelectedFieldCandidates)
+ {
+ model.AddNewMemberPrototype(candidate.Declaration, candidate.BackingIdentifier);
+ }
+
+ _modifyUDTRefactoringAction.Refactor(model,rewriteSession);
+ }
+
+ rewriter.RemoveVariables(encapsulateFieldModel.SelectedFieldCandidates.Select(f => f.Declaration)
+ .Cast());
+ }
+
+ private void ModifyReferences(EncapsulateFieldUseBackingUDTMemberModel model, IRewriteSession rewriteSession)
+ {
+ var privateUDTFields = model.SelectedFieldCandidates
+ .Where(f => (f.Declaration.AsTypeDeclaration?.DeclarationType.HasFlag(DeclarationType.UserDefinedType) ?? false)
+ && f.Declaration.AsTypeDeclaration.Accessibility == Accessibility.Private);
+
+ ReplaceUDTMemberReferencesOfPrivateUDTFields(privateUDTFields, rewriteSession);
+
+ ReplaceEncapsulatedFieldReferences(model.SelectedFieldCandidates.Except(privateUDTFields), model.ObjectStateUDTField, rewriteSession);
+ }
+
+ private void ReplaceUDTMemberReferencesOfPrivateUDTFields(IEnumerable udtFields, IRewriteSession rewriteSession)
+ {
+ if (!udtFields.Any())
+ {
+ return;
+ }
+
+ var replacePrivateUDTMemberReferencesModel
+ = _replaceUDTMemberReferencesModelFactory.Create(udtFields.Select(f => f.Declaration).Cast());
+
+ foreach (var udtfield in udtFields)
+ {
+ InitializeModel(replacePrivateUDTMemberReferencesModel, udtfield);
+ }
+
+ _replacePrivateUDTMemberReferencesRefactoringAction.Refactor(replacePrivateUDTMemberReferencesModel, rewriteSession);
+ }
+
+ private void ReplaceEncapsulatedFieldReferences(IEnumerable nonPrivateUDTFields, IObjectStateUDT objectStateUDTField, IRewriteSession rewriteSession)
+ {
+ if (!nonPrivateUDTFields.Any())
+ {
+ return;
+ }
+
+ var replaceReferencesModel = new ReplaceReferencesModel()
+ {
+ ModuleQualifyExternalReferences = true,
+ };
+
+ foreach (var field in nonPrivateUDTFields)
+ {
+ InitializeModel(replaceReferencesModel, field, objectStateUDTField);
+ }
+
+ _replaceReferencesRefactoringAction.Refactor(replaceReferencesModel, rewriteSession);
+ }
+
+ private void InitializeModel(ReplacePrivateUDTMemberReferencesModel model, IEncapsulateFieldCandidate udtfield)
+ {
+ foreach (var udtMember in model.UDTMembers)
+ {
+ var udtExpressions = new PrivateUDTMemberReferenceReplacementExpressions($"{udtfield.IdentifierName}.{udtMember.IdentifierName}")
+ {
+ LocalReferenceExpression = udtMember.IdentifierName,
+ };
+
+ model.AssignUDTMemberReferenceExpressions(udtfield.Declaration as VariableDeclaration, udtMember, udtExpressions);
+ }
+ }
+
+ private void InitializeModel(ReplaceReferencesModel model, IEncapsulateFieldCandidate field, IObjectStateUDT objectStateUDTField)
+ {
+ foreach (var idRef in field.Declaration.References)
+ {
+ var replacementExpression = field.PropertyIdentifier;
+
+ if (idRef.QualifiedModuleName == field.QualifiedModuleName && field.Declaration.IsArray)
+ {
+ replacementExpression = $"{objectStateUDTField.FieldIdentifier}.{field.BackingIdentifier}";
+ }
+
+ model.AssignReferenceReplacementExpression(idRef, replacementExpression);
+ }
+ }
+
+ private void InsertNewContent(EncapsulateFieldUseBackingUDTMemberModel model, IRewriteSession rewriteSession)
+ {
+ var aggregator = model.NewContentAggregator ?? _newContentAggregatorFactory.Create();
+ model.NewContentAggregator = null;
+
+ var encapsulateFieldInsertNewCodeModel = new EncapsulateFieldInsertNewCodeModel(model.SelectedFieldCandidates)
+ {
+ NewContentAggregator = aggregator,
+ ObjectStateUDTField = model.ObjectStateUDTField
+ };
+
+ _encapsulateFieldInsertNewCodeRefactoringAction.Refactor(encapsulateFieldInsertNewCodeModel, rewriteSession);
+ }
+ }
+}
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/ConvertFieldsToUDTMembers.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/ConvertFieldsToUDTMembers.cs
deleted file mode 100644
index 90541ab1aa..0000000000
--- a/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/ConvertFieldsToUDTMembers.cs
+++ /dev/null
@@ -1,100 +0,0 @@
-using Rubberduck.Parsing.Rewriter;
-using Rubberduck.Parsing.Symbols;
-using Rubberduck.Parsing.VBA;
-using Rubberduck.Refactorings.Common;
-using Rubberduck.SmartIndenter;
-using System.Diagnostics;
-using System.Linq;
-
-namespace Rubberduck.Refactorings.EncapsulateField
-{
- public class ConvertFieldsToUDTMembers : EncapsulateFieldStrategyBase
- {
- private IObjectStateUDT _stateUDTField;
-
- public ConvertFieldsToUDTMembers(IDeclarationFinderProvider declarationFinderProvider, EncapsulateFieldModel model, IIndenter indenter, ICodeBuilder codeBuilder)
- : base(declarationFinderProvider, model, indenter, codeBuilder)
- {
- _stateUDTField = model.ObjectStateUDTField;
- }
-
- protected override void ModifyFields(IRewriteSession refactorRewriteSession)
- {
- var rewriter = refactorRewriteSession.CheckOutModuleRewriter(_targetQMN);
-
- rewriter.RemoveVariables(SelectedFields.Select(f => f.Declaration)
- .Cast());
-
- if (_stateUDTField.IsExistingDeclaration)
- {
- _stateUDTField.AddMembers(SelectedFields.Cast());
-
- rewriter.Replace(_stateUDTField.AsTypeDeclaration, _stateUDTField.TypeDeclarationBlock(_indenter));
- }
- }
-
- protected override void ModifyReferences(IRewriteSession refactorRewriteSession)
- {
- foreach (var field in SelectedFields)
- {
- LoadFieldReferenceContextReplacements(field);
- }
-
- RewriteReferences(refactorRewriteSession);
- }
-
- protected override void LoadNewDeclarationBlocks()
- {
- if (_stateUDTField.IsExistingDeclaration) { return; }
-
- _stateUDTField.AddMembers(SelectedFields.Cast());
-
- AddContentBlock(NewContentTypes.TypeDeclarationBlock, _stateUDTField.TypeDeclarationBlock(_indenter));
-
- AddContentBlock(NewContentTypes.DeclarationBlock, _stateUDTField.FieldDeclarationBlock);
- return;
- }
-
- protected override void LoadFieldReferenceContextReplacements(IEncapsulateFieldCandidate field)
- {
- Debug.Assert(field is IConvertToUDTMember);
-
- var converted = field as IConvertToUDTMember;
- if (converted.WrappedCandidate is IUserDefinedTypeCandidate udt && udt.TypeDeclarationIsPrivate)
- {
- foreach (var member in udt.Members)
- {
- foreach (var idRef in member.FieldContextReferences)
- {
- var replacementText = member.IdentifierForReference(idRef);
- if (IsExternalReferenceRequiringModuleQualification(idRef))
- {
- replacementText = $"{udt.QualifiedModuleName.ComponentName}.{replacementText}";
- }
-
- SetUDTMemberReferenceRewriteContent(idRef, replacementText);
- }
- }
- }
- else
- {
- foreach (var idRef in field.Declaration.References)
- {
- var replacementText = converted.IdentifierForReference(idRef);
-
- if (IsExternalReferenceRequiringModuleQualification(idRef))
- {
- replacementText = $"{converted.QualifiedModuleName.ComponentName}.{replacementText}";
- }
-
- if (converted.Declaration.IsArray)
- {
- replacementText = $"{_stateUDTField.FieldIdentifier}.{replacementText}";
- }
-
- SetReferenceRewriteContent(idRef, replacementText);
- }
- }
- }
- }
-}
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/EncapsulateFieldStrategyBase.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/EncapsulateFieldStrategyBase.cs
deleted file mode 100644
index 1e78eff2b5..0000000000
--- a/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/EncapsulateFieldStrategyBase.cs
+++ /dev/null
@@ -1,279 +0,0 @@
-using Antlr4.Runtime;
-using Rubberduck.Parsing;
-using Rubberduck.Parsing.Grammar;
-using Rubberduck.Parsing.Rewriter;
-using Rubberduck.Parsing.Symbols;
-using Rubberduck.Parsing.VBA;
-using Rubberduck.Refactorings.Common;
-using Rubberduck.Refactorings.EncapsulateField.Extensions;
-using Rubberduck.Resources;
-using Rubberduck.SmartIndenter;
-using Rubberduck.VBEditor;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-
-namespace Rubberduck.Refactorings.EncapsulateField
-{
-
- public struct PropertyAttributeSet
- {
- public string PropertyName { get; set; }
- public string BackingField { get; set; }
- public string AsTypeName { get; set; }
- public string ParameterName { get; set; }
- public bool GenerateLetter { get; set; }
- public bool GenerateSetter { get; set; }
- public bool UsesSetAssignment { get; set; }
- public bool IsUDTProperty { get; set; }
- public Declaration Declaration { get; set; }
- }
-
- public interface IEncapsulateStrategy
- {
- IRewriteSession RefactorRewrite(IRewriteSession refactorRewriteSession, bool asPreview);
- }
-
- public abstract class EncapsulateFieldStrategyBase : IEncapsulateStrategy
- {
- protected readonly IIndenter _indenter;
- protected QualifiedModuleName _targetQMN;
- private readonly int? _codeSectionStartIndex;
- protected const string _defaultIndent = " "; //4 spaces
- protected ICodeBuilder _codeBuilder;
-
- protected Dictionary IdentifierReplacements { get; } = new Dictionary();
-
- protected enum NewContentTypes { TypeDeclarationBlock, DeclarationBlock, MethodBlock, PostContentMessage };
- protected Dictionary> _newContent { set; get; }
- private static string DoubleSpace => $"{Environment.NewLine}{Environment.NewLine}";
-
- protected IEnumerable SelectedFields { private set; get; }
-
- public EncapsulateFieldStrategyBase(IDeclarationFinderProvider declarationFinderProvider, EncapsulateFieldModel model, IIndenter indenter, ICodeBuilder codeBuilder)
- {
- _targetQMN = model.QualifiedModuleName;
- _indenter = indenter;
- _codeBuilder = codeBuilder;
- SelectedFields = model.SelectedFieldCandidates.ToList();
-
- _codeSectionStartIndex = declarationFinderProvider.DeclarationFinder
- .Members(_targetQMN).Where(m => m.IsMember())
- .OrderBy(c => c.Selection)
- .FirstOrDefault()?.Context.Start.TokenIndex ?? null;
- }
-
- public IRewriteSession RefactorRewrite(IRewriteSession refactorRewriteSession, bool asPreview)
- {
- ModifyFields(refactorRewriteSession);
-
- ModifyReferences(refactorRewriteSession);
-
- InsertNewContent(refactorRewriteSession, asPreview);
-
- return refactorRewriteSession;
- }
-
- protected abstract void ModifyFields(IRewriteSession rewriteSession);
-
- protected abstract void ModifyReferences(IRewriteSession refactorRewriteSession);
-
- protected abstract void LoadNewDeclarationBlocks();
-
- protected void RewriteReferences(IRewriteSession refactorRewriteSession)
- {
- foreach (var replacement in IdentifierReplacements)
- {
- (ParserRuleContext Context, string Text) = replacement.Value;
- var rewriter = refactorRewriteSession.CheckOutModuleRewriter(replacement.Key.QualifiedModuleName);
- rewriter.Replace(Context, Text);
- }
- }
-
- protected void AddContentBlock(NewContentTypes contentType, string block)
- => _newContent[contentType].Add(block);
-
- private void InsertNewContent(IRewriteSession refactorRewriteSession, bool isPreview = false)
- {
- _newContent = new Dictionary>
- {
- { NewContentTypes.PostContentMessage, new List() },
- { NewContentTypes.DeclarationBlock, new List() },
- { NewContentTypes.MethodBlock, new List() },
- { NewContentTypes.TypeDeclarationBlock, new List() }
- };
-
- LoadNewDeclarationBlocks();
-
- LoadNewPropertyBlocks();
-
- if (isPreview)
- {
- AddContentBlock(NewContentTypes.PostContentMessage, RubberduckUI.EncapsulateField_PreviewMarker);
- }
-
- var newContentBlock = string.Join(DoubleSpace,
- (_newContent[NewContentTypes.TypeDeclarationBlock])
- .Concat(_newContent[NewContentTypes.DeclarationBlock])
- .Concat(_newContent[NewContentTypes.MethodBlock])
- .Concat(_newContent[NewContentTypes.PostContentMessage]))
- .Trim()
- .LimitNewlines();
-
- var rewriter = refactorRewriteSession.CheckOutModuleRewriter(_targetQMN);
- if (_codeSectionStartIndex.HasValue)
- {
- rewriter.InsertBefore(_codeSectionStartIndex.Value, $"{newContentBlock}{DoubleSpace}");
- }
- else
- {
- rewriter.InsertAtEndOfFile($"{DoubleSpace}{newContentBlock}");
- }
- }
-
- protected void LoadNewPropertyBlocks()
- {
- foreach (var propertyAttributes in SelectedFields.SelectMany(f => f.PropertyAttributeSets))
- {
- AddPropertyCodeBlocks(propertyAttributes);
- }
- }
-
- private void AddPropertyCodeBlocks(PropertyAttributeSet propertyAttributes)
- {
- Debug.Assert(propertyAttributes.Declaration.DeclarationType.HasFlag(DeclarationType.Variable) || propertyAttributes.Declaration.DeclarationType.HasFlag(DeclarationType.UserDefinedTypeMember));
-
- var getContent = $"{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}";
- if (propertyAttributes.UsesSetAssignment)
- {
- getContent = $"{Tokens.Set} {getContent}";
- }
-
- if (propertyAttributes.AsTypeName.Equals(Tokens.Variant) && !propertyAttributes.Declaration.IsArray)
- {
- getContent = string.Join(Environment.NewLine,
- $"{Tokens.If} IsObject({propertyAttributes.BackingField}) {Tokens.Then}",
- $"{_defaultIndent}{Tokens.Set} {propertyAttributes.PropertyName} = {propertyAttributes.BackingField}",
- Tokens.Else,
- $"{_defaultIndent}{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}",
- $"{Tokens.End} {Tokens.If}",
- Environment.NewLine);
- }
-
- if (!_codeBuilder.TryBuildPropertyGetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertyGet, content: $"{_defaultIndent}{getContent}"))
- {
- throw new ArgumentException();
- }
- AddContentBlock(NewContentTypes.MethodBlock, propertyGet);
-
- if (!(propertyAttributes.GenerateLetter || propertyAttributes.GenerateSetter))
- {
- return;
- }
-
- if (propertyAttributes.GenerateLetter)
- {
- if (!_codeBuilder.TryBuildPropertyLetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertyLet, content: $"{_defaultIndent}{propertyAttributes.BackingField} = {propertyAttributes.ParameterName}"))
- {
- throw new ArgumentException();
- }
- AddContentBlock(NewContentTypes.MethodBlock, propertyLet);
- }
-
- if (propertyAttributes.GenerateSetter)
- {
- if (!_codeBuilder.TryBuildPropertySetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertySet, content: $"{_defaultIndent}{Tokens.Set} {propertyAttributes.BackingField} = {propertyAttributes.ParameterName}"))
- {
- throw new ArgumentException();
- }
- AddContentBlock(NewContentTypes.MethodBlock, propertySet);
- }
- }
-
- protected virtual void LoadFieldReferenceContextReplacements(IEncapsulateFieldCandidate field)
- {
- if (field is IUserDefinedTypeCandidate udt && udt.TypeDeclarationIsPrivate)
- {
- foreach (var member in udt.Members)
- {
- foreach (var idRef in member.FieldContextReferences)
- {
- var replacementText = member.IdentifierForReference(idRef);
- SetUDTMemberReferenceRewriteContent(idRef, replacementText);
- }
- }
- }
- else
- {
- foreach (var idRef in field.Declaration.References)
- {
- var replacementText = field.IdentifierForReference(idRef);
- if (IsExternalReferenceRequiringModuleQualification(idRef))
- {
- replacementText = $"{field.QualifiedModuleName.ComponentName}.{replacementText}";
- }
- SetReferenceRewriteContent(idRef, replacementText);
- }
- }
- }
-
- protected bool IsExternalReferenceRequiringModuleQualification(IdentifierReference idRef)
- {
- var isLHSOfMemberAccess =
- (idRef.Context.Parent is VBAParser.MemberAccessExprContext
- || idRef.Context.Parent is VBAParser.WithMemberAccessExprContext)
- && !(idRef.Context == idRef.Context.Parent.GetChild(0));
-
- return idRef.QualifiedModuleName != idRef.Declaration.QualifiedModuleName
- && !isLHSOfMemberAccess;
- }
-
- protected virtual void SetReferenceRewriteContent(IdentifierReference idRef, string replacementText)
- {
- if (idRef.Context is VBAParser.IndexExprContext idxExpression)
- {
- AddIdentifierReplacement(idRef, idxExpression.children.ElementAt(0) as ParserRuleContext, replacementText);
- }
- else if (idRef.Context is VBAParser.UnrestrictedIdentifierContext
- || idRef.Context is VBAParser.SimpleNameExprContext)
- {
- AddIdentifierReplacement(idRef, idRef.Context, replacementText);
- }
- else if (idRef.Context.TryGetAncestor(out var wmac))
- {
- AddIdentifierReplacement(idRef, wmac.GetChild(), replacementText);
- }
- else if (idRef.Context.TryGetAncestor(out var maec))
- {
- AddIdentifierReplacement(idRef, maec, replacementText);
- }
- }
-
- protected virtual void SetUDTMemberReferenceRewriteContent(IdentifierReference idRef, string replacementText)
- {
- if (idRef.Context is VBAParser.IndexExprContext idxExpression)
- {
- AddIdentifierReplacement(idRef, idxExpression.children.ElementAt(0) as ParserRuleContext, replacementText);
- }
- else if (idRef.Context.TryGetAncestor(out var wmac))
- {
- AddIdentifierReplacement(idRef, wmac.GetChild(), replacementText);
- }
- else if (idRef.Context.TryGetAncestor(out var maec))
- {
- AddIdentifierReplacement(idRef, maec, replacementText);
- }
- }
-
- private void AddIdentifierReplacement( IdentifierReference idRef, ParserRuleContext context, string replacementText)
- {
- if (IdentifierReplacements.ContainsKey(idRef))
- {
- IdentifierReplacements[idRef] = (context, replacementText);
- return;
- }
- IdentifierReplacements.Add(idRef, (context, replacementText));
- }
- }
-}
diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/UseBackingFields.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/UseBackingFields.cs
deleted file mode 100644
index 392a806849..0000000000
--- a/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/UseBackingFields.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using Rubberduck.Parsing.Grammar;
-using Rubberduck.Parsing.Rewriter;
-using Rubberduck.Parsing.Symbols;
-using Rubberduck.Parsing.VBA;
-using Rubberduck.Refactorings.Common;
-using Rubberduck.Refactorings.EncapsulateField.Extensions;
-using Rubberduck.SmartIndenter;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Rubberduck.Refactorings.EncapsulateField
-{
- public class UseBackingFields : EncapsulateFieldStrategyBase
- {
- private IEnumerable _fieldsToDeleteAndReplace;
-
- public UseBackingFields(IDeclarationFinderProvider declarationFinderProvider, EncapsulateFieldModel model, IIndenter indenter, ICodeBuilder codeBuilder)
- : base(declarationFinderProvider, model, indenter, codeBuilder)
- {
- _fieldsToDeleteAndReplace = SelectedFields.Where(f => f.Declaration.IsDeclaredInList() && !f.Declaration.HasPrivateAccessibility()).ToList();
- }
-
-
- protected override void ModifyFields(IRewriteSession refactorRewriteSession)
- {
- var rewriter = refactorRewriteSession.CheckOutModuleRewriter(_targetQMN);
-
- rewriter.RemoveVariables(_fieldsToDeleteAndReplace.Select(f => f.Declaration).Cast());
-
- foreach (var field in SelectedFields.Except(_fieldsToDeleteAndReplace))
- {
- if (field.Declaration.HasPrivateAccessibility() && field.BackingIdentifier.Equals(field.Declaration.IdentifierName))
- {
- rewriter.MakeImplicitDeclarationTypeExplicit(field.Declaration);
- continue;
- }
-
- rewriter.Rename(field.Declaration, field.BackingIdentifier);
- rewriter.SetVariableVisiblity(field.Declaration, Accessibility.Private.TokenString());
- rewriter.MakeImplicitDeclarationTypeExplicit(field.Declaration);
- }
- }
-
- protected override void ModifyReferences(IRewriteSession refactorRewriteSession)
- {
- foreach (var field in SelectedFields)
- {
- LoadFieldReferenceContextReplacements(field);
- }
-
- RewriteReferences(refactorRewriteSession);
- }
-
- protected override void LoadNewDeclarationBlocks()
- {
- //New field declarations created here were removed from their
- //variable list statement within ModifyFields(...)
- foreach (var field in _fieldsToDeleteAndReplace)
- {
- var targetIdentifier = field.Declaration.Context.GetText().Replace(field.IdentifierName, field.BackingIdentifier);
- var newField = field.Declaration.IsTypeSpecified
- ? $"{Tokens.Private} {targetIdentifier}"
- : $"{Tokens.Private} {targetIdentifier} {Tokens.As} {field.Declaration.AsTypeName}";
-
- AddContentBlock(NewContentTypes.DeclarationBlock, newField);
- }
- }
- }
-}
diff --git a/Rubberduck.Refactorings/EncapsulateField/Extensions/IModuleRewriterExtensions.cs b/Rubberduck.Refactorings/EncapsulateField/Extensions/IModuleRewriterExtensions.cs
deleted file mode 100644
index 8a210bfeab..0000000000
--- a/Rubberduck.Refactorings/EncapsulateField/Extensions/IModuleRewriterExtensions.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using Rubberduck.Parsing;
-using Rubberduck.Parsing.Grammar;
-using Rubberduck.Parsing.Rewriter;
-using Rubberduck.Parsing.Symbols;
-using Rubberduck.Refactorings.Common;
-using System;
-
-namespace Rubberduck.Refactorings.EncapsulateField.Extensions
-{
- public static class IModuleRewriterExtensions
- {
- public static void InsertAtEndOfFile(this IModuleRewriter rewriter, string content)
- {
- if (content == string.Empty) { return; }
-
- rewriter.InsertBefore(rewriter.TokenStream.Size - 1, content);
- }
-
- public static void MakeImplicitDeclarationTypeExplicit(this IModuleRewriter rewriter, Declaration element)
- {
- if (!element.Context.TryGetChildContext(out _))
- {
- rewriter.InsertAfter(element.Context.Stop.TokenIndex, $" {Tokens.As} {element.AsTypeName}");
- }
- }
-
- public static void Rename(this IModuleRewriter rewriter, Declaration target, string newName)
- {
- if (!(target.Context is IIdentifierContext context))
- {
- throw new ArgumentException();
- }
-
- rewriter.Replace(context.IdentifierTokens, newName);
- }
-
- public static void SetVariableVisiblity(this IModuleRewriter rewriter, Declaration element, string visibility)
- {
- if (!element.IsVariable()) { throw new ArgumentException(); }
-
- var variableStmtContext = element.Context.GetAncestor();
- var visibilityContext = variableStmtContext.GetChild();
-
- if (visibilityContext != null)
- {
- rewriter.Replace(visibilityContext, visibility);
- return;
- }
- rewriter.InsertBefore(element.Context.Start.TokenIndex, $"{visibility} ");
- }
- }
-}
diff --git a/Rubberduck.Refactorings/EncapsulateField/Extensions/StringExtensions.cs b/Rubberduck.Refactorings/EncapsulateField/Extensions/StringExtensions.cs
index 31a2a18aa2..3496b48564 100644
--- a/Rubberduck.Refactorings/EncapsulateField/Extensions/StringExtensions.cs
+++ b/Rubberduck.Refactorings/EncapsulateField/Extensions/StringExtensions.cs
@@ -1,5 +1,4 @@
using System;
-using System.Linq;
namespace Rubberduck.Refactorings.EncapsulateField.Extensions
{
@@ -11,7 +10,10 @@ public static bool IsEquivalentVBAIdentifierTo(this string lhs, string identifie
public static string IncrementEncapsulationIdentifier(this string identifier)
{
var fragments = identifier.Split('_');
- if (fragments.Length == 1) { return $"{identifier}_1"; }
+ if (fragments.Length == 1)
+ {
+ return $"{identifier}_1";
+ }
var lastFragment = fragments[fragments.Length - 1];
if (long.TryParse(lastFragment, out var number))
@@ -22,23 +24,5 @@ public static string IncrementEncapsulationIdentifier(this string identifier)
}
return $"{identifier}_1"; ;
}
-
- public static string LimitNewlines(this string content, int maxConsecutiveNewlines = 2)
- {
- var target = string.Concat(Enumerable.Repeat(Environment.NewLine, maxConsecutiveNewlines + 1).ToList());
- var replacement = string.Concat(Enumerable.Repeat(Environment.NewLine, maxConsecutiveNewlines).ToList());
- var guard = 0;
- var maxAttempts = 100;
- while (++guard < maxAttempts && content.Contains(target))
- {
- content = content.Replace(target, replacement);
- }
-
- if (guard >= maxAttempts)
- {
- throw new FormatException($"Unable to limit consecutive '{Environment.NewLine}' strings to {maxConsecutiveNewlines}");
- }
- return content;
- }
}
}
diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs
deleted file mode 100644
index b2a0612f5c..0000000000
--- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-using Rubberduck.Parsing;
-using Rubberduck.Parsing.Grammar;
-using Rubberduck.Parsing.Symbols;
-using Rubberduck.Resources;
-using System.Linq;
-
-namespace Rubberduck.Refactorings.EncapsulateField
-{
- public interface IArrayCandidate : IEncapsulateFieldCandidate
- {
- string UDTMemberDeclaration { get;}
- bool HasExternalRedimOperation(out string errorMessage);
- }
-
- public class ArrayCandidate : EncapsulateFieldCandidate, IArrayCandidate
- {
- private string _subscripts;
- public ArrayCandidate(Declaration declaration, IValidateVBAIdentifiers validator)
- :base(declaration, validator)
- {
- ImplementLet = false;
- ImplementSet = false;
- PropertyAsTypeName = Tokens.Variant;
- CanBeReadWrite = false;
- IsReadOnly = true;
-
- _subscripts = string.Empty;
- if (declaration.Context.TryGetChildContext(out var ctxt))
- {
- _subscripts = ctxt.GetText();
- }
- }
-
- public override bool TryValidateEncapsulationAttributes(out string errorMessage)
- {
- errorMessage = string.Empty;
- if (!EncapsulateFlag) { return true; }
-
- if (HasExternalRedimOperation(out errorMessage))
- {
- return false;
- }
- return ConflictFinder.TryValidateEncapsulationAttributes(this, out errorMessage);
- }
-
- public string UDTMemberDeclaration
- => $"{PropertyIdentifier}({_subscripts}) {Tokens.As} {Declaration.AsTypeName}";
-
- protected override string IdentifierForLocalReferences(IdentifierReference idRef)
- => BackingIdentifier;
-
- public bool HasExternalRedimOperation(out string errorMessage)
- {
- errorMessage = string.Empty;
- if (Declaration.References.Any(rf => rf.QualifiedModuleName != QualifiedModuleName
- && rf.Context.TryGetAncestor(out _)))
- {
- errorMessage = string.Format(RubberduckUI.EncapsulateField_ArrayHasExternalRedimFormat, IdentifierName);
- return true;
- }
- return false;
- }
- }
-}
diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ConvertToUDTMemberCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ConvertToUDTMemberCandidate.cs
deleted file mode 100644
index bb2720dd04..0000000000
--- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ConvertToUDTMemberCandidate.cs
+++ /dev/null
@@ -1,195 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using Rubberduck.Common;
-using Rubberduck.Parsing.Symbols;
-using Rubberduck.VBEditor;
-
-namespace Rubberduck.Refactorings.EncapsulateField
-{
-
- public interface IConvertToUDTMember : IEncapsulateFieldCandidate
- {
- string UDTMemberDeclaration { get; }
- IEncapsulateFieldCandidate WrappedCandidate { get; }
- IObjectStateUDT ObjectStateUDT { set; get; }
- }
-
- public class ConvertToUDTMember : IConvertToUDTMember
- {
- private int _hashCode;
- private readonly string _uniqueID;
- private readonly IEncapsulateFieldCandidate _wrapped;
- public ConvertToUDTMember(IEncapsulateFieldCandidate candidate, IObjectStateUDT objStateUDT)
- {
- _wrapped = candidate;
- PropertyIdentifier = _wrapped.PropertyIdentifier;
- ObjectStateUDT = objStateUDT;
- _uniqueID = BuildUniqueID(candidate, objStateUDT);
- _hashCode = _uniqueID.GetHashCode();
- }
-
- public virtual string UDTMemberDeclaration
- {
- get
- {
- if (_wrapped is IArrayCandidate array)
- {
- return array.UDTMemberDeclaration;
- }
- return $"{BackingIdentifier} As {_wrapped.AsTypeName}";
- }
- }
-
- public IEncapsulateFieldCandidate WrappedCandidate => _wrapped;
-
- public IObjectStateUDT ObjectStateUDT { set; get; }
-
- public string TargetID => _wrapped.TargetID;
-
- public Declaration Declaration => _wrapped.Declaration;
-
- public bool EncapsulateFlag
- {
- set => _wrapped.EncapsulateFlag = value;
- get => _wrapped.EncapsulateFlag;
- }
-
- public string PropertyIdentifier
- {
- set => _wrapped.PropertyIdentifier = value;
- get => _wrapped.PropertyIdentifier;
- }
-
- public string PropertyAsTypeName => _wrapped.PropertyAsTypeName;
-
- public string BackingIdentifier
- {
- set { }
- get => PropertyIdentifier;
- }
- public string BackingAsTypeName => Declaration.AsTypeName;
-
- public bool CanBeReadWrite
- {
- set => _wrapped.CanBeReadWrite = value;
- get => _wrapped.CanBeReadWrite;
- }
-
- public bool ImplementLet => _wrapped.ImplementLet;
-
- public bool ImplementSet => _wrapped.ImplementSet;
-
- public bool IsReadOnly
- {
- set => _wrapped.IsReadOnly = value;
- get => _wrapped.IsReadOnly;
- }
-
- public string ParameterName => _wrapped.ParameterName;
-
- public IValidateVBAIdentifiers NameValidator
- {
- set => _wrapped.NameValidator = value;
- get => _wrapped.NameValidator;
- }
-
- public IEncapsulateFieldConflictFinder ConflictFinder
- {
- set => _wrapped.ConflictFinder = value;
- get => _wrapped.ConflictFinder;
- }
-
- private string AccessorInProperty
- {
- get
- {
- if (_wrapped is IUserDefinedTypeMemberCandidate udtm)
- {
- return $"{ObjectStateUDT.FieldIdentifier}.{udtm.UDTField.PropertyIdentifier}.{BackingIdentifier}";
- }
- return $"{ObjectStateUDT.FieldIdentifier}.{BackingIdentifier}";
- }
- }
-
- public string IdentifierForReference(IdentifierReference idRef)
- {
- if (idRef.QualifiedModuleName != QualifiedModuleName)
- {
- return PropertyIdentifier;
- }
- return BackingIdentifier;
- }
-
- public string IdentifierName => _wrapped.IdentifierName;
-
- public QualifiedModuleName QualifiedModuleName => _wrapped.QualifiedModuleName;
-
- public string AsTypeName => _wrapped.AsTypeName;
-
- public bool TryValidateEncapsulationAttributes(out string errorMessage)
- {
- errorMessage = string.Empty;
- if (!_wrapped.EncapsulateFlag) { return true; }
-
- if (_wrapped is IArrayCandidate ac)
- {
- if (ac.HasExternalRedimOperation(out errorMessage))
- {
- return false;
- }
- }
- return ConflictFinder.TryValidateEncapsulationAttributes(this, out errorMessage);
- }
-
- public IEnumerable PropertyAttributeSets
- {
- get
- {
- var modifiedSets = new List();
- var sets = _wrapped.PropertyAttributeSets;
- for (var idx = 0; idx < sets.Count(); idx++)
- {
- var attributeSet = sets.ElementAt(idx);
- var fields = attributeSet.BackingField.Split(new char[] { '.' });
-
- attributeSet.BackingField = fields.Count() > 1
- ? $"{ObjectStateUDT.FieldIdentifier}.{attributeSet.BackingField.CapitalizeFirstLetter()}"
- : $"{ObjectStateUDT.FieldIdentifier}.{attributeSet.PropertyName.CapitalizeFirstLetter()}";
-
- modifiedSets.Add(attributeSet);
- }
- return modifiedSets;
- }
- }
-
- public override bool Equals(object obj)
- {
- return obj != null
- && obj is ConvertToUDTMember convertWrapper
- && BuildUniqueID(convertWrapper, convertWrapper.ObjectStateUDT) == _uniqueID;
- }
-
- public override int GetHashCode() => _hashCode;
-
- private static string BuildUniqueID(IEncapsulateFieldCandidate candidate, IObjectStateUDT field)
- => $"{candidate.QualifiedModuleName.Name}.{field.IdentifierName}.{candidate.IdentifierName}";
-
- private PropertyAttributeSet AsPropertyAttributeSet
- {
- get
- {
- return new PropertyAttributeSet()
- {
- PropertyName = PropertyIdentifier,
- BackingField = AccessorInProperty,
- AsTypeName = PropertyAsTypeName,
- ParameterName = ParameterName,
- GenerateLetter = ImplementLet,
- GenerateSetter = ImplementSet,
- UsesSetAssignment = Declaration.IsObject,
- IsUDTProperty = true
- };
- }
- }
- }
-}
diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs
new file mode 100644
index 0000000000..0e2ccaee2b
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs
@@ -0,0 +1,112 @@
+using Rubberduck.Parsing.Symbols;
+using Rubberduck.VBEditor;
+using System;
+
+namespace Rubberduck.Refactorings.EncapsulateField
+{
+ public interface IEncapsulateFieldAsUDTMemberCandidate : IEncapsulateFieldCandidate
+ {
+ IObjectStateUDT ObjectStateUDT { set; get; }
+ IEncapsulateFieldCandidate WrappedCandidate { get; }
+ string UserDefinedTypeMemberIdentifier { set; get; }
+ }
+
+ ///
+ /// EncapsulateFieldAsUDTMemberCandidate wraps an IEncapusulateFieldCandidate instance
+ /// for the purposes of declaring it as a new UserDefinedTypeMember
+ /// within an existing or new UserDefinedType
+ ///
+ public class EncapsulateFieldAsUDTMemberCandidate : IEncapsulateFieldAsUDTMemberCandidate
+ {
+ private readonly int _hashCode;
+ private IEncapsulateFieldCandidate _wrapped;
+ public EncapsulateFieldAsUDTMemberCandidate(IEncapsulateFieldCandidate candidate, IObjectStateUDT objStateUDT)
+ {
+ _wrapped = candidate;
+ ObjectStateUDT = objStateUDT;
+ _hashCode = $"{candidate.QualifiedModuleName.Name}.{candidate.IdentifierName}".GetHashCode();
+ }
+
+ public IEncapsulateFieldCandidate WrappedCandidate => _wrapped;
+
+ private IObjectStateUDT _objectStateUDT;
+ public IObjectStateUDT ObjectStateUDT
+ {
+ set
+ {
+ _objectStateUDT = value;
+ if (_objectStateUDT?.Declaration == _wrapped.Declaration)
+ {
+ //Cannot wrap itself if it is used as the ObjectStateUDT
+ _wrapped.EncapsulateFlag = false;
+ }
+ }
+ get => _objectStateUDT;
+ }
+
+ public string TargetID => _wrapped.TargetID;
+
+ public Declaration Declaration => _wrapped.Declaration;
+
+ public bool EncapsulateFlag
+ {
+ set => _wrapped.EncapsulateFlag = value;
+ get => _wrapped.EncapsulateFlag;
+ }
+
+ public string UserDefinedTypeMemberIdentifier
+ {
+ set => PropertyIdentifier = value;
+ get => PropertyIdentifier;
+ }
+
+ public string PropertyIdentifier
+ {
+ set => _wrapped.PropertyIdentifier = value;
+ get => _wrapped.PropertyIdentifier;
+ }
+
+ public virtual Action BackingIdentifierMutator { get; } = null;
+
+ public string BackingIdentifier => PropertyIdentifier;
+
+ public string PropertyAsTypeName => _wrapped.PropertyAsTypeName;
+
+ public bool CanBeReadWrite => !_wrapped.Declaration.IsArray;
+
+ public bool IsReadOnly
+ {
+ set => _wrapped.IsReadOnly = value;
+ get => _wrapped.IsReadOnly;
+ }
+
+ public IEncapsulateFieldConflictFinder ConflictFinder
+ {
+ set => _wrapped.ConflictFinder = value;
+ get => _wrapped.ConflictFinder;
+ }
+
+ public string IdentifierName => _wrapped.IdentifierName;
+
+ public QualifiedModuleName QualifiedModuleName => _wrapped.QualifiedModuleName;
+
+ public string AsTypeName => _wrapped.AsTypeName;
+
+ public bool TryValidateEncapsulationAttributes(out string errorMessage)
+ {
+ (bool IsValid, string ErrorMsg) = ConflictFinder?.ValidateEncapsulationAttributes(this) ?? (true, string.Empty);
+ errorMessage = ErrorMsg;
+ return IsValid;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj != null
+ && obj is EncapsulateFieldAsUDTMemberCandidate convertWrapper
+ && convertWrapper.QualifiedModuleName == QualifiedModuleName
+ && convertWrapper.IdentifierName == IdentifierName;
+ }
+
+ public override int GetHashCode() => _hashCode;
+ }
+}
diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs
index 796beb86ff..990d6ac4e2 100644
--- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs
+++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs
@@ -1,11 +1,8 @@
-using Antlr4.Runtime;
-using Rubberduck.Parsing.Grammar;
+using Rubberduck.Parsing.Grammar;
using Rubberduck.Parsing.Symbols;
using Rubberduck.Refactorings.Common;
-using Rubberduck.Refactorings.EncapsulateField.Extensions;
using Rubberduck.VBEditor;
using System;
-using System.Collections.Generic;
namespace Rubberduck.Refactorings.EncapsulateField
{
@@ -21,217 +18,122 @@ public interface IEncapsulateFieldCandidate : IEncapsulateFieldRefactoringElemen
string TargetID { get; }
Declaration Declaration { get; }
bool EncapsulateFlag { get; set; }
- string BackingIdentifier { set; get; }
- string BackingAsTypeName { get; }
+ string BackingIdentifier { get; }
+ Action BackingIdentifierMutator { get; }
string PropertyIdentifier { set; get; }
string PropertyAsTypeName { get; }
- bool CanBeReadWrite { set; get; }
- bool ImplementLet { get; }
- bool ImplementSet { get; }
+ bool CanBeReadWrite { get; }
bool IsReadOnly { set; get; }
- string ParameterName { get; }
- IValidateVBAIdentifiers NameValidator { set; get; }
IEncapsulateFieldConflictFinder ConflictFinder { set; get; }
bool TryValidateEncapsulationAttributes(out string errorMessage);
- string IdentifierForReference(IdentifierReference idRef);
- IEnumerable PropertyAttributeSets { get; }
}
public class EncapsulateFieldCandidate : IEncapsulateFieldCandidate
{
- protected Declaration _target;
- protected QualifiedModuleName _qmn;
- protected readonly string _uniqueID;
- protected int _hashCode;
- private string _identifierName;
+ protected readonly int _hashCode;
protected EncapsulationIdentifiers _fieldAndProperty;
- private string _rhsParameterIdentifierName;
- public EncapsulateFieldCandidate(Declaration declaration, IValidateVBAIdentifiers identifierValidator)
+ public EncapsulateFieldCandidate(Declaration declaration)
{
- _target = declaration;
- NameValidator = identifierValidator;
- _rhsParameterIdentifierName = Resources.Refactorings.Refactorings.CodeBuilder_DefaultPropertyRHSParam;
+ Declaration = declaration;
+ AsTypeName = declaration.AsTypeName;
+
+ _fieldAndProperty = new EncapsulationIdentifiers(declaration.IdentifierName);
+ BackingIdentifierMutator = (value) => _fieldAndProperty.Field = value;
- _fieldAndProperty = new EncapsulationIdentifiers(declaration.IdentifierName, identifierValidator);
IdentifierName = declaration.IdentifierName;
- PropertyAsTypeName = declaration.AsTypeName;
- _qmn = declaration.QualifiedModuleName;
- CanBeReadWrite = true;
+ TargetID = IdentifierName;
- _uniqueID = $"{_qmn.Name}.{declaration.IdentifierName}";
- _hashCode = _uniqueID.GetHashCode();
+ QualifiedModuleName = declaration.QualifiedModuleName;
- ImplementLet = true;
- ImplementSet = false;
- if (_target.IsEnumField() && _target.AsTypeDeclaration.HasPrivateAccessibility())
- {
- //5.3.1 The declared type of a function declaration
- //may not be a private enum name.
- PropertyAsTypeName = Tokens.Long;
- }
- else if (_target.AsTypeName.Equals(Tokens.Variant)
- && !_target.IsArray)
- {
- ImplementSet = true;
- }
- else if (Declaration.IsObject)
- {
- ImplementLet = false;
- ImplementSet = true;
- }
- }
+ //5.3.1 The declared type of a function declaration may not be a private enum.
+ PropertyAsTypeName = declaration.IsEnumField() && declaration.AsTypeDeclaration.HasPrivateAccessibility()
+ ? Tokens.Long
+ : declaration.AsTypeName;
- protected Dictionary IdentifierReplacements { get; } = new Dictionary();
+ CanBeReadWrite = !Declaration.IsArray;
- public Declaration Declaration => _target;
+ _hashCode = $"{QualifiedModuleName.Name}.{declaration.IdentifierName}".GetHashCode();
+ }
- public string AsTypeName => _target.AsTypeName;
+ public Declaration Declaration { get; }
- public virtual string BackingIdentifier
- {
- get => _fieldAndProperty.Field;
- set => _fieldAndProperty.Field = value;
- }
+ public string IdentifierName { get; }
- public string BackingAsTypeName => Declaration.AsTypeName;
+ public string AsTypeName { get; }
- public virtual IValidateVBAIdentifiers NameValidator { set; get; }
+ public bool CanBeReadWrite { get; }
+
+ public virtual bool IsReadOnly { set; get; }
public virtual IEncapsulateFieldConflictFinder ConflictFinder { set; get; }
- public virtual bool TryValidateEncapsulationAttributes(out string errorMessage)
- => ConflictFinder.TryValidateEncapsulationAttributes(this, out errorMessage);
+ public string PropertyAsTypeName { get; set; }
+
+ public QualifiedModuleName QualifiedModuleName { get; }
+
+ public virtual bool TryValidateEncapsulationAttributes(out string errorMessage)
+ {
+ (bool IsValid, string ErrorMsg) = ConflictFinder?.ValidateEncapsulationAttributes(this) ?? (true, string.Empty);
+
+ errorMessage = ErrorMsg;
+ return IsValid;
+ }
- public virtual string TargetID => _target?.IdentifierName ?? IdentifierName;
+ public virtual string TargetID { get; }
protected bool _encapsulateFlag;
public virtual bool EncapsulateFlag
{
set
{
- var valueChanged = _encapsulateFlag != value;
-
- _encapsulateFlag = value;
- if (!_encapsulateFlag)
- {
- PropertyIdentifier = _fieldAndProperty.DefaultPropertyName;
- }
- else if (valueChanged)
+ if (_encapsulateFlag != value)
{
- ConflictFinder.AssignNoConflictIdentifiers(this);
+ _encapsulateFlag = value;
+ if (!_encapsulateFlag)
+ {
+ PropertyIdentifier = _fieldAndProperty.DefaultPropertyName;
+ return;
+ }
+
+ ConflictFinder?.AssignNoConflictIdentifiers(this);
}
}
get => _encapsulateFlag;
}
- public virtual bool IsReadOnly { set; get; }
- public bool CanBeReadWrite { set; get; }
-
- public override bool Equals(object obj)
- {
- return obj != null
- && obj is IEncapsulateFieldCandidate efc
- && $"{efc.QualifiedModuleName.Name}.{efc.IdentifierName}" == _uniqueID;
- }
-
- public override int GetHashCode() => _hashCode;
-
- public override string ToString()
- =>$"({TargetID}){Declaration.ToString()}";
-
- protected string IdentifierInNewProperties
- => BackingIdentifier;
-
- public string IdentifierForReference(IdentifierReference idRef)
- {
- if (idRef.QualifiedModuleName != QualifiedModuleName)
- {
- return PropertyIdentifier;
- }
- return IdentifierForLocalReferences(idRef);
- }
-
- protected virtual string IdentifierForLocalReferences(IdentifierReference idRef)
- => PropertyIdentifier;
-
public string PropertyIdentifier
{
get => _fieldAndProperty.Property;
set
{
- _fieldAndProperty.Property = value;
-
- TryRestoreNewFieldNameAsOriginalFieldIdentifierName();
- }
- }
-
- private void TryRestoreNewFieldNameAsOriginalFieldIdentifierName()
- {
- var canNowUseOriginalFieldName = !_fieldAndProperty.TargetFieldName.IsEquivalentVBAIdentifierTo(_fieldAndProperty.Property)
- && !ConflictFinder.IsConflictingProposedIdentifier(_fieldAndProperty.TargetFieldName, this, DeclarationType.Variable);
-
- if (canNowUseOriginalFieldName)
- {
- _fieldAndProperty.Field = _fieldAndProperty.TargetFieldName;
- return;
- }
-
- if (_fieldAndProperty.Field.IsEquivalentVBAIdentifierTo(_fieldAndProperty.TargetFieldName))
- {
- _fieldAndProperty.Field = _fieldAndProperty.DefaultNewFieldName;
- var isConflictingFieldIdentifier = ConflictFinder.HasConflictingIdentifier(this, DeclarationType.Variable, out _);
- for (var count = 1; count < 10 && isConflictingFieldIdentifier; count++)
+ if (_fieldAndProperty.Property != value)
{
- BackingIdentifier = BackingIdentifier.IncrementEncapsulationIdentifier();
- isConflictingFieldIdentifier = ConflictFinder.HasConflictingIdentifier(this, DeclarationType.Variable, out _);
+ _fieldAndProperty.Property = value;
+
+ //Reset the backing field identifier
+ _fieldAndProperty.Field = _fieldAndProperty.TargetFieldName;
+ ConflictFinder?.AssignNoConflictBackingFieldIdentifier(this);
}
}
}
- public string PropertyAsTypeName { get; set; }
+ public virtual string BackingIdentifier => _fieldAndProperty.Field;
- public QualifiedModuleName QualifiedModuleName => _qmn;
+ public virtual Action BackingIdentifierMutator { get; }
- public string IdentifierName
+ public override bool Equals(object obj)
{
- get => Declaration?.IdentifierName ?? _identifierName;
- set => _identifierName = value;
+ return obj != null
+ && obj is IEncapsulateFieldCandidate efc
+ && efc.QualifiedModuleName == QualifiedModuleName
+ && efc.IdentifierName == IdentifierName;
}
- public virtual string ReferenceQualifier { set; get; }
-
- public string ParameterName => _rhsParameterIdentifierName;
-
- private bool _implLet;
- public bool ImplementLet { get => !IsReadOnly && _implLet; set => _implLet = value; }
-
- private bool _implSet;
- public bool ImplementSet { get => !IsReadOnly && _implSet; set => _implSet = value; }
-
- public EncapsulateFieldStrategy EncapsulateFieldStrategy { set; get; } = EncapsulateFieldStrategy.UseBackingFields;
-
- public virtual IEnumerable PropertyAttributeSets
- => new List() { AsPropertyAttributeSet };
+ public override int GetHashCode() => _hashCode;
- protected virtual PropertyAttributeSet AsPropertyAttributeSet
- {
- get
- {
- return new PropertyAttributeSet()
- {
- PropertyName = PropertyIdentifier,
- BackingField = IdentifierInNewProperties,
- AsTypeName = PropertyAsTypeName,
- ParameterName = ParameterName,
- GenerateLetter = ImplementLet,
- GenerateSetter = ImplementSet,
- UsesSetAssignment = Declaration.IsObject,
- IsUDTProperty = false,
- Declaration = Declaration
- };
- }
- }
+ public override string ToString()
+ => $"({TargetID}){Declaration.ToString()}";
}
}
diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateFactory.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateFactory.cs
new file mode 100644
index 0000000000..8af4875200
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateFactory.cs
@@ -0,0 +1,66 @@
+using Rubberduck.Parsing.Symbols;
+using Rubberduck.Parsing.VBA;
+using Rubberduck.Refactorings.Common;
+using Rubberduck.Refactorings.EncapsulateField;
+using Rubberduck.VBEditor;
+using System;
+using System.Linq;
+
+namespace Rubberduck.Refactorings
+{
+ public interface IEncapsulateFieldCandidateFactory
+ {
+ IEncapsulateFieldCandidate CreateFieldCandidate(Declaration target);
+ IEncapsulateFieldAsUDTMemberCandidate CreateUDTMemberCandidate(IEncapsulateFieldCandidate fieldCandidate, IObjectStateUDT defaultObjectStateField);
+ IObjectStateUDT CreateDefaultObjectStateField(QualifiedModuleName qualifiedModuleName);
+ IObjectStateUDT CreateObjectStateField(IUserDefinedTypeCandidate userDefinedTypeField);
+ }
+
+ public class EncapsulateFieldCandidateFactory : IEncapsulateFieldCandidateFactory
+ {
+ private readonly IDeclarationFinderProvider _declarationFinderProvider;
+
+ public EncapsulateFieldCandidateFactory(IDeclarationFinderProvider declarationFinderProvider)
+ {
+ _declarationFinderProvider = declarationFinderProvider;
+ }
+
+ public IEncapsulateFieldCandidate CreateFieldCandidate(Declaration target)
+ {
+ if (!target.IsUserDefinedType())
+ {
+ return new EncapsulateFieldCandidate(target);
+ }
+
+ var udtField = new UserDefinedTypeCandidate(target) as IUserDefinedTypeCandidate;
+
+ var udtMembers = _declarationFinderProvider.DeclarationFinder
+ .UserDeclarations(DeclarationType.UserDefinedTypeMember)
+ .Where(utm => udtField.Declaration.AsTypeDeclaration == utm.ParentDeclaration);
+
+ foreach (var udtMemberDeclaration in udtMembers)
+ {
+ var candidateUDTMember = new UserDefinedTypeMemberCandidate(CreateFieldCandidate(udtMemberDeclaration), udtField);
+ udtField.AddMember(candidateUDTMember);
+ }
+
+ return udtField;
+ }
+
+ public IEncapsulateFieldAsUDTMemberCandidate CreateUDTMemberCandidate(IEncapsulateFieldCandidate fieldCandidate, IObjectStateUDT defaultObjectStateField)
+ => new EncapsulateFieldAsUDTMemberCandidate(fieldCandidate, defaultObjectStateField);
+
+ public IObjectStateUDT CreateDefaultObjectStateField(QualifiedModuleName qualifiedModuleName)
+ => new ObjectStateFieldCandidate(qualifiedModuleName);
+
+ public IObjectStateUDT CreateObjectStateField(IUserDefinedTypeCandidate userDefinedTypeField)
+ {
+ if ((userDefinedTypeField.Declaration.AsTypeDeclaration?.Accessibility ?? Accessibility.Implicit) != Accessibility.Private)
+ {
+ throw new ArgumentException();
+ }
+
+ return new ObjectStateFieldCandidate(userDefinedTypeField);
+ }
+ }
+}
diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulationIdentifiers.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulationIdentifiers.cs
index bb29bdb253..3edb6503bd 100644
--- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulationIdentifiers.cs
+++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulationIdentifiers.cs
@@ -1,6 +1,8 @@
using Rubberduck.Common;
using System.Collections.Generic;
using Rubberduck.Refactorings.EncapsulateField.Extensions;
+using Rubberduck.Refactorings.Common;
+using Rubberduck.Parsing.Symbols;
namespace Rubberduck.Refactorings.EncapsulateField
{
@@ -9,7 +11,7 @@ public class EncapsulationIdentifiers
private KeyValuePair _fieldAndProperty;
private string _targetIdentifier;
- public EncapsulationIdentifiers(string field, IValidateVBAIdentifiers identifierValidator)
+ public EncapsulationIdentifiers(string field)
{
_targetIdentifier = field;
@@ -18,7 +20,7 @@ public EncapsulationIdentifiers(string field, IValidateVBAIdentifiers identifier
if (field.TryMatchHungarianNotationCriteria(out var nonHungarianName))
{
- if (identifierValidator.IsValidVBAIdentifier(nonHungarianName, out _))
+ if (!VBAIdentifierValidator.TryMatchInvalidIdentifierCriteria(nonHungarianName, DeclarationType.Variable, out _))
{
DefaultPropertyName = nonHungarianName;
DefaultNewFieldName = field;
@@ -27,7 +29,7 @@ public EncapsulationIdentifiers(string field, IValidateVBAIdentifiers identifier
else if (field.StartsWith("m_"))
{
var propertyName = field.Substring(2).CapitalizeFirstLetter();
- if (identifierValidator.IsValidVBAIdentifier(propertyName, out _))
+ if (!VBAIdentifierValidator.TryMatchInvalidIdentifierCriteria(propertyName, DeclarationType.Property, out _))
{
DefaultPropertyName = propertyName;
DefaultNewFieldName = field;
diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ObjectStateFieldCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ObjectStateFieldCandidate.cs
new file mode 100644
index 0000000000..88f5bae33c
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ObjectStateFieldCandidate.cs
@@ -0,0 +1,104 @@
+using Rubberduck.Parsing.Symbols;
+using Rubberduck.Common;
+using Rubberduck.VBEditor;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Rubberduck.Parsing.Grammar;
+
+namespace Rubberduck.Refactorings.EncapsulateField
+{
+ public interface IObjectStateUDT : IEncapsulateFieldRefactoringElement
+ {
+ Declaration Declaration { get; }
+ string TypeIdentifier { set; get; }
+ string FieldIdentifier { set; get; }
+ string FieldDeclarationBlock { get; }
+ bool IsExistingDeclaration { get; }
+ Declaration AsTypeDeclaration { get; }
+ bool IsSelected { set; get; }
+ IReadOnlyCollection ExistingMembers { get; }
+ }
+
+ ///
+ /// ObjectStateUDT is a Private UserDefinedType whose UserDefinedTypeMembers represent
+ /// a consolidated grouping of object state in lieu of (or in addition to) a set of Private fields.
+ ///
+ ///
+ /// Within the EncapsulateField refactoring, the ObjectStateUDT can be an existing
+ /// UserDefinedType or a new UserDefinedType generated by the refactoring.
+ ///
+ public class ObjectStateFieldCandidate : IObjectStateUDT
+ {
+ private static string _defaultNewFieldName = "this";
+ private readonly IUserDefinedTypeCandidate _wrappedUDTField;
+
+ public ObjectStateFieldCandidate(IUserDefinedTypeCandidate udtField)
+ : this(udtField.IdentifierName, udtField.Declaration.AsTypeName)
+ {
+ if (!udtField.TypeDeclarationIsPrivate)
+ {
+ throw new ArgumentException();
+ }
+
+ QualifiedModuleName = udtField.QualifiedModuleName;
+ _wrappedUDTField = udtField;
+ }
+
+ public ObjectStateFieldCandidate(QualifiedModuleName qualifiedModuleName)
+ :this(_defaultNewFieldName, $"T{qualifiedModuleName.ComponentName.CapitalizeFirstLetter()}")
+ {
+ QualifiedModuleName = qualifiedModuleName;
+ }
+
+ private ObjectStateFieldCandidate(string fieldIdentifier, string typeIdentifier)
+ {
+ FieldIdentifier = fieldIdentifier;
+ TypeIdentifier = typeIdentifier;
+ }
+
+ public string IdentifierName => _wrappedUDTField?.IdentifierName ?? FieldIdentifier;
+
+ public Declaration Declaration => _wrappedUDTField?.Declaration;
+
+ public string AsTypeName => _wrappedUDTField?.AsTypeName ?? TypeIdentifier;
+
+ public string FieldDeclarationBlock
+ => $"{Accessibility.Private} {IdentifierName} {Tokens.As} {AsTypeName}";
+
+ private bool _isSelected;
+ public bool IsSelected
+ {
+ set
+ {
+ _isSelected = value;
+ if (_isSelected && IsExistingDeclaration)
+ {
+ _wrappedUDTField.EncapsulateFlag = false;
+ }
+ }
+ get => _isSelected;
+ }
+
+ public IReadOnlyCollection ExistingMembers
+ => _wrappedUDTField?.Members.ToList() ?? new List();
+
+ public QualifiedModuleName QualifiedModuleName { get; }
+
+ public string TypeIdentifier { set; get; }
+
+ public bool IsExistingDeclaration => _wrappedUDTField != null;
+
+ public Declaration AsTypeDeclaration => _wrappedUDTField?.Declaration.AsTypeDeclaration;
+
+ public string FieldIdentifier { set; get; }
+
+ public override bool Equals(object obj)
+ {
+ return (obj is IObjectStateUDT stateUDT && stateUDT.FieldIdentifier == FieldIdentifier)
+ || (obj is IEncapsulateFieldRefactoringElement fd && fd.IdentifierName == IdentifierName);
+ }
+
+ public override int GetHashCode() => $"{QualifiedModuleName.Name}.{FieldIdentifier}".GetHashCode();
+ }
+}
diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs
index b5e523d041..1694544d33 100644
--- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs
+++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs
@@ -1,5 +1,4 @@
-using Rubberduck.Parsing.Grammar;
-using Rubberduck.Parsing.Symbols;
+using Rubberduck.Parsing.Symbols;
using Rubberduck.Refactorings.Common;
using System;
using System.Collections.Generic;
@@ -10,16 +9,17 @@ public interface IUserDefinedTypeCandidate : IEncapsulateFieldCandidate
{
IEnumerable Members { get; }
void AddMember(IUserDefinedTypeMemberCandidate member);
- bool TypeDeclarationIsPrivate { set; get; }
- bool CanBeObjectStateUDT { set; get; }
- bool IsSelectedObjectStateUDT { set; get; }
+ bool TypeDeclarationIsPrivate { get; }
}
public class UserDefinedTypeCandidate : EncapsulateFieldCandidate, IUserDefinedTypeCandidate
{
- public UserDefinedTypeCandidate(Declaration declaration, IValidateVBAIdentifiers identifierValidator)
- : base(declaration, identifierValidator)
+ public UserDefinedTypeCandidate(Declaration declaration)
+ : base(declaration)
{
+ BackingIdentifierMutator = Declaration.AsTypeDeclaration.HasPrivateAccessibility()
+ ? null
+ : base.BackingIdentifierMutator;
}
public void AddMember(IUserDefinedTypeMemberCandidate member)
@@ -30,54 +30,28 @@ public void AddMember(IUserDefinedTypeMemberCandidate member)
private List _udtMembers = new List();
public IEnumerable Members => _udtMembers;
- private bool _isPrivate;
public bool TypeDeclarationIsPrivate
- {
- set => _isPrivate = value;
- get => Declaration.AsTypeDeclaration?.HasPrivateAccessibility() ?? false;
- }
-
- public bool IsSelectedObjectStateUDT { set; get; }
-
- private bool _canBeObjectStateUDT;
- public bool CanBeObjectStateUDT
- {
- set => _canBeObjectStateUDT = value;
- get => _canBeObjectStateUDT;
- }
+ => Declaration.AsTypeDeclaration?.HasPrivateAccessibility() ?? false;
- public override string BackingIdentifier
- {
- get => TypeDeclarationIsPrivate ? _fieldAndProperty.TargetFieldName : _fieldAndProperty.Field;
- set => _fieldAndProperty.Field = value;
- }
+ public override string BackingIdentifier =>
+ BackingIdentifierMutator is null
+ ? _fieldAndProperty.TargetFieldName
+ : _fieldAndProperty.Field;
- private IValidateVBAIdentifiers _namesValidator;
- public override IValidateVBAIdentifiers NameValidator
- {
- set
- {
- _namesValidator = value;
- foreach (var member in Members)
- {
- member.NameValidator = value;
- }
- }
- get => _namesValidator;
- }
+ public override Action BackingIdentifierMutator { get; }
- private IEncapsulateFieldConflictFinder _conflictsValidator;
+ private IEncapsulateFieldConflictFinder _conflictsFinder;
public override IEncapsulateFieldConflictFinder ConflictFinder
{
set
{
- _conflictsValidator = value;
+ _conflictsFinder = value;
foreach (var member in Members)
{
member.ConflictFinder = value;
}
}
- get => _conflictsValidator;
+ get => _conflictsFinder;
}
private bool _isReadOnly;
@@ -98,6 +72,7 @@ public override bool EncapsulateFlag
{
set
{
+ base.EncapsulateFlag = value;
if (TypeDeclarationIsPrivate)
{
foreach (var member in Members)
@@ -105,79 +80,13 @@ public override bool EncapsulateFlag
member.EncapsulateFlag = value;
}
}
- base.EncapsulateFlag = value;
}
get => base.EncapsulateFlag;
}
- protected override string IdentifierForLocalReferences(IdentifierReference idRef)
- {
- if (idRef.Context.Parent.Parent is VBAParser.WithStmtContext wsc)
- {
- return BackingIdentifier;
- }
-
- return TypeDeclarationIsPrivate ? BackingIdentifier : PropertyIdentifier;
- }
-
public override bool Equals(object obj)
- {
- if (obj is IUserDefinedTypeCandidate udt)
- {
- return udt.TargetID.Equals(TargetID);
- }
- return false;
- }
-
- public override int GetHashCode()
- {
- return base.GetHashCode();
- }
-
- public override IEnumerable PropertyAttributeSets
- {
- get
- {
- if (TypeDeclarationIsPrivate)
- {
- var specs = new List();
- foreach (var member in Members)
- {
- var sets = member.PropertyAttributeSets;
- var modifiedSets = new List();
- PropertyAttributeSet newSet;
- foreach (var set in sets)
- {
- newSet = set;
- newSet.BackingField = $"{BackingIdentifier}.{set.BackingField}";
- modifiedSets.Add(newSet);
- }
- specs.AddRange(modifiedSets);
- }
- return specs;
- }
- return new List() { AsPropertyAttributeSet };
- }
- }
-
- protected override PropertyAttributeSet AsPropertyAttributeSet
- {
- get
- {
- return new PropertyAttributeSet()
- {
- PropertyName = PropertyIdentifier,
- BackingField = IdentifierInNewProperties,
- AsTypeName = PropertyAsTypeName,
- ParameterName = ParameterName,
- GenerateLetter = ImplementLet,
- GenerateSetter = ImplementSet,
- UsesSetAssignment = Declaration.IsObject,
- IsUDTProperty = true,
- Declaration = Declaration
- };
- }
- }
+ => (obj is IUserDefinedTypeCandidate udt && udt.TargetID.Equals(TargetID));
+ public override int GetHashCode() => base.GetHashCode();
}
}
diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs
index b28b16c52a..58712073a7 100644
--- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs
+++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs
@@ -1,154 +1,54 @@
-using Rubberduck.Parsing;
-using Rubberduck.Parsing.Grammar;
-using Rubberduck.Parsing.Symbols;
+using Rubberduck.Parsing.Symbols;
using Rubberduck.VBEditor;
using System;
-using System.Collections.Generic;
-using System.Linq;
namespace Rubberduck.Refactorings.EncapsulateField
{
public interface IUserDefinedTypeMemberCandidate : IEncapsulateFieldCandidate
{
IUserDefinedTypeCandidate UDTField { get; }
- PropertyAttributeSet AsPropertyGeneratorSpec { get; }
- IEnumerable FieldContextReferences { get; }
IEncapsulateFieldCandidate WrappedCandidate { get; }
}
public class UserDefinedTypeMemberCandidate : IUserDefinedTypeMemberCandidate
{
- private int _hashCode;
- private readonly string _uniqueID;
- private string _rhsParameterIdentifierName;
+ private readonly int _hashCode;
+
public UserDefinedTypeMemberCandidate(IEncapsulateFieldCandidate candidate, IUserDefinedTypeCandidate udtField)
{
- _wrappedCandidate = candidate;
- _rhsParameterIdentifierName = Resources.Refactorings.Refactorings.CodeBuilder_DefaultPropertyRHSParam;
+ WrappedCandidate = candidate;
UDTField = udtField;
PropertyIdentifier = IdentifierName;
BackingIdentifier = IdentifierName;
- _uniqueID = BuildUniqueID(candidate, UDTField);
- _hashCode = _uniqueID.GetHashCode();
+ _hashCode = TargetID.GetHashCode();
}
- private IEncapsulateFieldCandidate _wrappedCandidate;
-
- public IEncapsulateFieldCandidate WrappedCandidate => _wrappedCandidate;
+ public IEncapsulateFieldCandidate WrappedCandidate { private set; get; }
- public string AsTypeName => _wrappedCandidate.AsTypeName;
-
- public string BackingIdentifier
- {
- get
- {
- return _wrappedCandidate.IdentifierName;
- }
- set { }
- }
-
- public string BackingAsTypeName => Declaration.AsTypeName;
+ public string AsTypeName => WrappedCandidate.AsTypeName;
public IUserDefinedTypeCandidate UDTField { private set; get; }
- public IValidateVBAIdentifiers NameValidator
- {
- set => _wrappedCandidate.NameValidator = value;
- get => _wrappedCandidate.NameValidator;
- }
-
public IEncapsulateFieldConflictFinder ConflictFinder
{
- set => _wrappedCandidate.ConflictFinder = value;
- get => _wrappedCandidate.ConflictFinder;
+ set => WrappedCandidate.ConflictFinder = value;
+ get => WrappedCandidate.ConflictFinder;
}
public string TargetID => $"{UDTField.IdentifierName}.{IdentifierName}";
- public IEnumerable FieldContextReferences
- => GetUDTMemberReferencesForField(this, UDTField);
-
public string IdentifierForReference(IdentifierReference idRef)
=> PropertyIdentifier;
- public PropertyAttributeSet AsPropertyGeneratorSpec
- {
- get
- {
- return new PropertyAttributeSet()
- {
- PropertyName = PropertyIdentifier,
- BackingField = BackingIdentifier,
- AsTypeName = PropertyAsTypeName,
- ParameterName = ParameterName,
- GenerateLetter = ImplementLet,
- GenerateSetter = ImplementSet,
- UsesSetAssignment = Declaration.IsObject,
- IsUDTProperty = Declaration.DeclarationType == DeclarationType.UserDefinedType,
- Declaration = Declaration
- };
- }
- }
-
- public override bool Equals(object obj)
- {
- return obj != null
- && obj is IUserDefinedTypeMemberCandidate udtMember
- && BuildUniqueID(udtMember, udtMember.UDTField) == _uniqueID;
- }
-
- public override int GetHashCode() => _hashCode;
-
public string PropertyIdentifier { set; get; }
- private static string BuildUniqueID(IEncapsulateFieldCandidate candidate, IEncapsulateFieldCandidate field) => $"{candidate.QualifiedModuleName.Name}.{field.IdentifierName}.{candidate.IdentifierName}";
+ public string BackingIdentifier { get; }
- private static IEnumerable GetUDTMemberReferencesForField(IEncapsulateFieldCandidate udtMember, IUserDefinedTypeCandidate field)
- {
- var refs = new List();
- foreach (var idRef in udtMember.Declaration.References)
- {
- if (idRef.Context.TryGetAncestor(out var mac))
- {
- var LHS = mac.children.First();
- switch (LHS)
- {
- case VBAParser.SimpleNameExprContext snec:
- if (snec.GetText().Equals(field.IdentifierName))
- {
- refs.Add(idRef);
- }
- break;
- case VBAParser.MemberAccessExprContext submac:
- if (submac.children.Last() is VBAParser.UnrestrictedIdentifierContext ur && ur.GetText().Equals(field.IdentifierName))
- {
- refs.Add(idRef);
- }
- break;
- case VBAParser.WithMemberAccessExprContext wmac:
- if (wmac.children.Last().GetText().Equals(field.IdentifierName))
- {
- refs.Add(idRef);
- }
- break;
- }
- }
- else if (idRef.Context.TryGetAncestor(out var wmac))
- {
- var wm = wmac.GetAncestor();
- var Lexpr = wm.GetChild();
- if (Lexpr.GetText().Equals(field.IdentifierName))
- {
- refs.Add(idRef);
- }
- }
- }
- return refs;
- }
+ public Action BackingIdentifierMutator { get; } = null;
- public Declaration Declaration => _wrappedCandidate.Declaration;
+ public Declaration Declaration => WrappedCandidate.Declaration;
- public string IdentifierName => _wrappedCandidate.IdentifierName;
+ public string IdentifierName => WrappedCandidate.IdentifierName;
public bool TryValidateEncapsulationAttributes(out string errorMessage)
{
@@ -158,8 +58,8 @@ public bool TryValidateEncapsulationAttributes(out string errorMessage)
public bool IsReadOnly
{
- set => _wrappedCandidate.IsReadOnly = value;
- get => _wrappedCandidate.IsReadOnly;
+ set => WrappedCandidate.IsReadOnly = value;
+ get => WrappedCandidate.IsReadOnly;
}
private bool _encapsulateFlag;
@@ -167,7 +67,7 @@ public bool EncapsulateFlag
{
set
{
- if (_wrappedCandidate is IUserDefinedTypeCandidate udt && udt.TypeDeclarationIsPrivate)
+ if (WrappedCandidate is IUserDefinedTypeCandidate udt && udt.TypeDeclarationIsPrivate)
{
foreach (var member in udt.Members)
{
@@ -175,63 +75,43 @@ public bool EncapsulateFlag
}
return;
}
- var valueChanged = _encapsulateFlag != value;
+ var valueChanged = _encapsulateFlag != value;
_encapsulateFlag = value;
- if (!_encapsulateFlag)
+
+ PropertyIdentifier = WrappedCandidate.PropertyIdentifier;
+
+ if (_encapsulateFlag && valueChanged && ConflictFinder != null)
{
- _wrappedCandidate.EncapsulateFlag = value;
- PropertyIdentifier = _wrappedCandidate.PropertyIdentifier;
+ ConflictFinder.AssignNoConflictIdentifiers(this);
}
- else if (valueChanged)
+
+ if (!_encapsulateFlag)
{
- ConflictFinder.AssignNoConflictIdentifiers(this);
+ WrappedCandidate.EncapsulateFlag = value;
}
+
}
get => _encapsulateFlag;
}
- public bool CanBeReadWrite
- {
- set => _wrappedCandidate.CanBeReadWrite = value;
- get => _wrappedCandidate.CanBeReadWrite;
- }
+ public bool CanBeReadWrite => !Declaration.IsArray;
+
public bool HasValidEncapsulationAttributes => true;
public QualifiedModuleName QualifiedModuleName
- => _wrappedCandidate.QualifiedModuleName;
+ => WrappedCandidate.QualifiedModuleName;
- public string PropertyAsTypeName => _wrappedCandidate.PropertyAsTypeName;
+ public string PropertyAsTypeName => WrappedCandidate.PropertyAsTypeName;
- public string ParameterName => _rhsParameterIdentifierName;
-
- public bool ImplementLet => _wrappedCandidate.ImplementLet;
-
- public bool ImplementSet => _wrappedCandidate.ImplementSet;
-
- public IEnumerable PropertyAttributeSets
+ public override bool Equals(object obj)
{
- get
- {
- if (!(_wrappedCandidate is IUserDefinedTypeCandidate udt))
- {
- return new List() { AsPropertyGeneratorSpec };
- }
-
- var sets = _wrappedCandidate.PropertyAttributeSets;
- if (udt.TypeDeclarationIsPrivate)
- {
- return sets;
- }
- var modifiedSets = new List();
- for(var idx = 0; idx < sets.Count(); idx++)
- {
- var attr = sets.ElementAt(idx);
- attr.BackingField = attr.PropertyName;
- modifiedSets.Add(attr);
- }
- return modifiedSets;
- }
+ return obj != null
+ && obj is IUserDefinedTypeMemberCandidate udtMember
+ && udtMember.QualifiedModuleName == QualifiedModuleName
+ && udtMember.TargetID == TargetID;
}
+
+ public override int GetHashCode() => _hashCode;
}
}
diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldEncapsulationModel.cs b/Rubberduck.Refactorings/EncapsulateField/FieldEncapsulationModel.cs
new file mode 100644
index 0000000000..6372dec9dc
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/FieldEncapsulationModel.cs
@@ -0,0 +1,28 @@
+using Rubberduck.Parsing.Symbols;
+
+namespace Rubberduck.Refactorings.EncapsulateField
+{
+ ///
+ /// FieldEncapsulationModel consolidates attributes necessary for the EncapsulateFieldUseBackingFieldRefactoringAction
+ /// and the EncapsulateFieldUseBackingUDTMemberRefactoringAction.
+ ///
+ ///
+ /// There is no identifier validation or conflict checking performed.
+ /// If the target is a UserDefinedType Field and the UserDefinedType is Private,
+ /// then the propertyIdentifier parameter is ignored and PropertyIdentifiers for each UserDefinedTypeMember
+ /// are generated by the refactoring action.
+ ///
+ public class FieldEncapsulationModel
+ {
+ public FieldEncapsulationModel(VariableDeclaration target, bool isReadOnly = false, string propertyIdentifier = null)
+ {
+ Declaration = target;
+ IsReadOnly = isReadOnly;
+ PropertyIdentifier = propertyIdentifier;
+ }
+
+ public VariableDeclaration Declaration { get; }
+ public string PropertyIdentifier { set; get; }
+ public bool IsReadOnly { set; get; }
+ }
+}
diff --git a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT.cs b/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT.cs
deleted file mode 100644
index f64202ad68..0000000000
--- a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT.cs
+++ /dev/null
@@ -1,170 +0,0 @@
-using Rubberduck.Parsing.Grammar;
-using Rubberduck.Parsing.Symbols;
-using Rubberduck.Common;
-using Rubberduck.SmartIndenter;
-using Rubberduck.VBEditor;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Rubberduck.Refactorings.EncapsulateField.Extensions;
-using Rubberduck.Resources;
-
-namespace Rubberduck.Refactorings.EncapsulateField
-{
- public interface IObjectStateUDT : IEncapsulateFieldRefactoringElement
- {
- string TypeIdentifier { set; get; }
- string FieldIdentifier { set; get; }
- string TypeDeclarationBlock(IIndenter indenter = null);
- string FieldDeclarationBlock { get; }
- void AddMembers(IEnumerable fields);
- bool IsExistingDeclaration { get; }
- Declaration AsTypeDeclaration { get; }
- bool IsSelected { set; get; }
- IEnumerable ExistingMembers { get; }
- }
-
- //ObjectStateUDT can be an existing UDT (Private only) selected by the user, or a
- //newly inserted declaration
- public class ObjectStateUDT : IObjectStateUDT
- {
- private static string _defaultNewFieldName = "this";
- private List _convertedMembers;
-
- private readonly IUserDefinedTypeCandidate _wrappedUDT;
- private int _hashCode;
-
- public ObjectStateUDT(IUserDefinedTypeCandidate udt)
- : this(udt.Declaration.AsTypeName)
- {
- if (!udt.TypeDeclarationIsPrivate)
- {
- throw new ArgumentException();
- }
-
- FieldIdentifier = udt.IdentifierName;
- _wrappedUDT = udt;
- _hashCode = ($"{_qmn.Name}.{_wrappedUDT.IdentifierName}").GetHashCode();
- }
-
- public ObjectStateUDT(QualifiedModuleName qmn)
- :this($"T{qmn.ComponentName.CapitalizeFirstLetter()}")
- {
- QualifiedModuleName = qmn;
- }
-
- private ObjectStateUDT(string typeIdentifier)
- {
- FieldIdentifier = _defaultNewFieldName;
- TypeIdentifier = typeIdentifier;
- _convertedMembers = new List();
- }
-
- public string IdentifierName => _wrappedUDT?.IdentifierName ?? FieldIdentifier;
-
- public string AsTypeName => _wrappedUDT?.AsTypeName ?? TypeIdentifier;
-
- private bool _isSelected;
- public bool IsSelected
- {
- set
- {
- _isSelected = value;
- if (_wrappedUDT != null)
- {
- _wrappedUDT.IsSelectedObjectStateUDT = value;
- }
-
- if (_isSelected && IsExistingDeclaration)
- {
- _wrappedUDT.EncapsulateFlag = false;
- }
- }
- get => _isSelected;
- }
-
- public IEnumerable ExistingMembers
- {
- get
- {
- if (IsExistingDeclaration)
- {
- return _wrappedUDT.Members;
- }
- return Enumerable.Empty();
- }
- }
-
-
- private QualifiedModuleName _qmn;
- public QualifiedModuleName QualifiedModuleName
- {
- set => _qmn = value;
- get => _wrappedUDT?.QualifiedModuleName ?? _qmn;
- }
-
- public string TypeIdentifier { set; get; }
-
- public bool IsExistingDeclaration => _wrappedUDT != null;
-
- public Declaration AsTypeDeclaration => _wrappedUDT?.Declaration.AsTypeDeclaration;
-
- public string FieldIdentifier { set; get; }
-
- public void AddMembers(IEnumerable fields)
- {
- _convertedMembers = new List();
- if (IsExistingDeclaration)
- {
- foreach (var member in _wrappedUDT.Members)
- {
- var convertedMember = new ConvertToUDTMember(member, this) { EncapsulateFlag = false };
- _convertedMembers.Add(convertedMember);
- }
- }
- _convertedMembers.AddRange(fields);
- }
-
- public string FieldDeclarationBlock
- => $"{Accessibility.Private} {IdentifierName} {Tokens.As} {AsTypeName}";
-
- public string TypeDeclarationBlock(IIndenter indenter = null)
- {
- if (indenter != null)
- {
- return string.Join(Environment.NewLine, indenter?.Indent(BlockLines(Accessibility.Private) ?? BlockLines(Accessibility.Private), true));
- }
- return string.Join(Environment.NewLine, BlockLines(Accessibility.Private));
- }
-
- public override bool Equals(object obj)
- {
- if (obj is IObjectStateUDT stateUDT && stateUDT.FieldIdentifier == FieldIdentifier)
- {
- return true;
- }
- if (obj is IEncapsulateFieldRefactoringElement fd && fd.IdentifierName == IdentifierName)
- {
- return true;
- }
- return false;
- }
-
- public override int GetHashCode() => _hashCode;
-
- private IEnumerable BlockLines(Accessibility accessibility)
- {
- var blockLines = new List();
-
- blockLines.Add($"{accessibility.TokenString()} {Tokens.Type} {TypeIdentifier}");
-
- _convertedMembers.ForEach(m => blockLines.Add($"{m.UDTMemberDeclaration}"));
-
- blockLines.Add($"{Tokens.End} {Tokens.Type}");
-
- return blockLines;
- }
- }
-}
diff --git a/Rubberduck.Refactorings/EncapsulateField/PropertyAttributeSetsGenerator.cs b/Rubberduck.Refactorings/EncapsulateField/PropertyAttributeSetsGenerator.cs
new file mode 100644
index 0000000000..7d6e6a14e7
--- /dev/null
+++ b/Rubberduck.Refactorings/EncapsulateField/PropertyAttributeSetsGenerator.cs
@@ -0,0 +1,153 @@
+using Rubberduck.Parsing.Grammar;
+using Rubberduck.Parsing.Symbols;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Rubberduck.Refactorings.EncapsulateField
+{
+ public struct PropertyAttributeSet
+ {
+ public string PropertyName { get; set; }
+ public string BackingField { get; set; }
+ public string AsTypeName { get; set; }
+ public string RHSParameterIdentifier { get; set; }
+ public bool GeneratePropertyLet { get; set; }
+ public bool GeneratePropertySet { get; set; }
+ public bool UsesSetAssignment { get; set; }
+ public bool IsUDTProperty { get; set; }
+ public Declaration Declaration { get; set; }
+ }
+
+ public interface IPropertyAttributeSetsGenerator
+ {
+ IReadOnlyCollection GeneratePropertyAttributeSets(IEncapsulateFieldCandidate candidate);
+ }
+
+ ///
+ /// PropertyAttributeSetsGenerator operates on an IEncapsulateFieldCandidate instance to
+ /// generate a collection of PropertyAttributeSets used by the EncapsulateField refactoring
+ /// actions to generate Property Let/Set/Get code blocks.
+ ///
+ ///
+ /// Typically there is only a single PropertyAttributeSet in the collection.
+ /// In the case of a Private UserDefinedType, there will be a PropertyAttributeSet
+ /// for each UserDefinedTypeMember.
+ ///
+ public class PropertyAttributeSetsGenerator : IPropertyAttributeSetsGenerator
+ {
+ private Func _backingFieldQualifierFunc;
+
+ public PropertyAttributeSetsGenerator()
+ {
+ _backingFieldQualifierFunc = BackingField_BackingFieldQualifier;
+ }
+
+ private static string BackingUDTMember_BackingFieldQualifier(IEncapsulateFieldCandidate candidate, string backingField)
+ => $"{candidate.PropertyIdentifier}.{backingField}";
+
+ private static string BackingField_BackingFieldQualifier(IEncapsulateFieldCandidate candidate, string backingField)
+ => $"{candidate.BackingIdentifier}.{backingField}";
+
+ public IReadOnlyCollection GeneratePropertyAttributeSets(IEncapsulateFieldCandidate candidate)
+ {
+ if (!(candidate is IEncapsulateFieldAsUDTMemberCandidate asUDTCandidate))
+ {
+ _backingFieldQualifierFunc = BackingField_BackingFieldQualifier;
+ return CreatePropertyAttributeSets(candidate).ToList();
+ }
+
+ return GeneratePropertyAttributeSets(asUDTCandidate);
+ }
+
+ private IReadOnlyCollection GeneratePropertyAttributeSets(IEncapsulateFieldAsUDTMemberCandidate asUDTCandidate)
+ {
+ _backingFieldQualifierFunc = BackingUDTMember_BackingFieldQualifier;
+
+ Func QualifyPrivateUDTWrappedBackingField = attributeSet =>
+ {
+ var fields = attributeSet.BackingField.Split(new char[] { '.' });
+
+ return fields.Count() > 1
+ ? $"{asUDTCandidate.ObjectStateUDT.FieldIdentifier}.{attributeSet.BackingField}"
+ : $"{asUDTCandidate.ObjectStateUDT.FieldIdentifier}.{attributeSet.PropertyName}";
+ };
+
+ var propertyAttributeSet = CreatePropertyAttributeSets(asUDTCandidate.WrappedCandidate);
+
+ return QualifyBackingField(propertyAttributeSet, set => QualifyPrivateUDTWrappedBackingField(set)).ToList();
+ }
+
+ private IEnumerable CreatePropertyAttributeSets(IUserDefinedTypeCandidate candidate)
+ {
+
+ if (candidate.TypeDeclarationIsPrivate)
+ {
+ var allPropertyAttributeSets = new List();
+ foreach (var member in candidate.Members)
+ {
+ var propertyAttributeSets = CreatePropertyAttributeSets(member);
+ var modifiedSets = QualifyBackingField(propertyAttributeSets, propertyAttributeSet => _backingFieldQualifierFunc(candidate, propertyAttributeSet.BackingField));
+ allPropertyAttributeSets.AddRange(modifiedSets);
+ }
+ return allPropertyAttributeSets;
+ }
+
+ return new List() { CreatePropertyAttributeSet(candidate) };
+ }
+
+ private IEnumerable CreatePropertyAttributeSets(IUserDefinedTypeMemberCandidate udtMemberCandidate)
+ {
+ if (udtMemberCandidate.WrappedCandidate is IUserDefinedTypeCandidate udtCandidate)
+ {
+ var propertyAttributeSets = CreatePropertyAttributeSets(udtMemberCandidate.WrappedCandidate);
+
+ return udtCandidate.TypeDeclarationIsPrivate
+ ? propertyAttributeSets
+ : QualifyBackingField(propertyAttributeSets, attr => attr.PropertyName);
+ }
+
+ return new List() { CreatePropertyAttributeSet(udtMemberCandidate) };
+ }
+
+ private IEnumerable CreatePropertyAttributeSets(IEncapsulateFieldCandidate candidate)
+ {
+ switch (candidate)
+ {
+ case IUserDefinedTypeCandidate udtCandidate:
+ return CreatePropertyAttributeSets(udtCandidate);
+ case IUserDefinedTypeMemberCandidate udtMemberCandidate:
+ return CreatePropertyAttributeSets(udtMemberCandidate);
+ default:
+ return new List() { CreatePropertyAttributeSet(candidate) };
+ }
+ }
+
+ private IEnumerable QualifyBackingField(IEnumerable propertyAttributeSets, Func backingFieldQualifier)
+ {
+ var modifiedSets = new List();
+ for (var idx = 0; idx < propertyAttributeSets.Count(); idx++)
+ {
+ var propertyAttributeSet = propertyAttributeSets.ElementAt(idx);
+ propertyAttributeSet.BackingField = backingFieldQualifier(propertyAttributeSet);
+ modifiedSets.Add(propertyAttributeSet);
+ }
+ return modifiedSets;
+ }
+
+ private PropertyAttributeSet CreatePropertyAttributeSet(IEncapsulateFieldCandidate candidate)
+ {
+ return new PropertyAttributeSet()
+ {
+ PropertyName = candidate.PropertyIdentifier,
+ BackingField = candidate.BackingIdentifier,
+ AsTypeName = candidate.PropertyAsTypeName,
+ RHSParameterIdentifier = Resources.Refactorings.Refactorings.CodeBuilder_DefaultPropertyRHSParam,
+ GeneratePropertyLet = !candidate.IsReadOnly && !candidate.Declaration.IsObject && !candidate.Declaration.IsArray,
+ GeneratePropertySet = !candidate.IsReadOnly && !candidate.Declaration.IsArray && (candidate.Declaration.IsObject || candidate.Declaration.AsTypeName == Tokens.Variant),
+ UsesSetAssignment = candidate.Declaration.IsObject,
+ Declaration = candidate.Declaration
+ };
+ }
+ }
+}
diff --git a/Rubberduck.Refactorings/EncapsulateField/Validations/ConvertFieldsToUDTMembersStrategyConflictFinder.cs b/Rubberduck.Refactorings/EncapsulateField/Validations/ConvertFieldsToUDTMembersStrategyConflictFinder.cs
deleted file mode 100644
index b6c6b0ee5d..0000000000
--- a/Rubberduck.Refactorings/EncapsulateField/Validations/ConvertFieldsToUDTMembersStrategyConflictFinder.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-using Rubberduck.Parsing.Symbols;
-using Rubberduck.Parsing.VBA;
-using Rubberduck.Refactorings.EncapsulateField.Extensions;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Rubberduck.Refactorings.EncapsulateField
-{
- public class ConvertFieldsToUDTMembersStrategyConflictFinder : EncapsulateFieldConflictFinderBase
- {
- private IEnumerable _objectStateUDTs;
- public ConvertFieldsToUDTMembersStrategyConflictFinder(IDeclarationFinderProvider declarationFinderProvider, IEnumerable