Skip to content

Encapsulate field

Introduction

The module implements encapsulate field refactoring in response to Deficient Encapsulation design smell.

References

[1] G. Suryanarayana, G. Samarthyam, and T. Sharma, Refactoring for software design smells: managing technical debt, 1st ed. San Francisco, CA, USA: Morgan Kaufmann Publishers Inc., 2014.

EncapsulateFiledRefactoringListener (JavaParserLabeledListener)

To implement encapsulate field refactoring.

Makes a public field private and provide accessors and mutator methods.

Source code in codart\refactorings\encapsulate_field.py
class EncapsulateFiledRefactoringListener(JavaParserLabeledListener):
    """

    To implement encapsulate field refactoring.

    Makes a public field private and provide accessors and mutator methods.

    """

    def __init__(self, common_token_stream: CommonTokenStream = None,
                 package_name: str = None,
                 source_class_name: str = None,
                 field_identifier: str = None):
        """

        Args:

            common_token_stream (CommonTokenStream): contains the program tokens

            package_name (str): The enclosing package of the field

            source_class_name (str): The enclosing class of the field

            field_identifier (str): The field name to be encapsulated

        Returns:

            object (DecreaseMethodVisibilityListener): An instance of EncapsulateFiledRefactoringListener

        """

        self.token_stream = common_token_stream
        if package_name is None:
            self.package_name = ''
        else:
            self.package_name = package_name
        self.source_class_name = source_class_name
        self.field_identifier = field_identifier
        self.getter_exist = False
        self.setter_exist = False
        self.in_source_class = False
        self.in_selected_package = True if self.package_name == '' else False

        # Move all the tokens in the source code in a buffer, token_stream_rewriter.
        if common_token_stream is not None:
            self.token_stream_rewriter = \
                TokenStreamRewriter(common_token_stream)
        else:
            raise TypeError('common_token_stream is None')

    def enterPackageDeclaration(self, ctx: JavaParserLabeled.PackageDeclarationContext):
        if self.package_name == ctx.qualifiedName().getText():
            self.in_selected_package = True
        else:
            self.in_selected_package = False

    def enterClassDeclaration(self, ctx: JavaParserLabeled.ClassDeclarationContext):
        if ctx.IDENTIFIER().getText() == self.source_class_name:
            self.in_source_class = True

    def exitClassDeclaration(self, ctx: JavaParserLabeled.ClassDeclarationContext):
        self.in_source_class = False

    def exitFieldDeclaration(self, ctx: JavaParserLabeled.FieldDeclarationContext):
        if self.in_source_class and self.in_selected_package:
            if ctx.variableDeclarators().variableDeclarator(
                    0).variableDeclaratorId().getText() == self.field_identifier:
                if not ctx.parentCtx.parentCtx.modifier(0):
                    self.token_stream_rewriter.insertBeforeIndex(
                        index=ctx.typeType().stop.tokenIndex,
                        text='private ')

                elif ctx.parentCtx.parentCtx.modifier(0).getText() == 'public':
                    self.token_stream_rewriter.replaceRange(
                        from_idx=ctx.parentCtx.parentCtx.modifier(0).start.tokenIndex,
                        to_idx=ctx.parentCtx.parentCtx.modifier(0).stop.tokenIndex,
                        text='private')
                else:
                    return

                for c in ctx.parentCtx.parentCtx.parentCtx.classBodyDeclaration():
                    try:
                        print('method name: ' + c.memberDeclaration()
                              .methodDeclaration().IDENTIFIER().getText())

                        if c.memberDeclaration().methodDeclaration().IDENTIFIER() \
                                .getText() == 'get' + str.capitalize(
                            self.field_identifier):
                            self.getter_exist = True

                        if c.memberDeclaration().methodDeclaration().IDENTIFIER() \
                                .getText() == 'set' + str.capitalize(
                            self.field_identifier):
                            self.setter_exist = True

                    except:
                        logger.error("not method !!!")

                logger.debug("setter find: " + str(self.setter_exist))
                logger.debug("getter find: " + str(self.getter_exist))

                # generate accessor and mutator methods
                # Accessor body
                new_code = ''
                if not self.getter_exist:
                    new_code = '\n\t// new getter method\n\t'
                    new_code += 'public ' + ctx.typeType().getText() + \
                                ' get' + str.capitalize(self.field_identifier)
                    new_code += '() { \n\t\treturn this.' + self.field_identifier \
                                + ';' + '\n\t}\n'

                # Mutator body
                if not self.setter_exist:
                    new_code += '\n\t// new setter method\n\t'
                    new_code += 'public void set' + str.capitalize(self.field_identifier)
                    new_code += '(' + ctx.typeType().getText() + ' ' \
                                + self.field_identifier + ') { \n\t\t'
                    new_code += 'this.' + self.field_identifier + ' = ' \
                                + self.field_identifier + ';' + '\n\t}\n'
                self.token_stream_rewriter.insertAfter(ctx.stop.tokenIndex, new_code)

                hidden = self.token_stream.getHiddenTokensToRight(ctx.stop.tokenIndex)
                # self.token_stream_rewriter.replaceRange(from_idx=hidden[0].tokenIndex,
                #                                         to_idx=hidden[-1].tokenIndex,
                #                                         text='\n\t/*End of accessor and mutator methods!*/\n\n')

    def exitExpression21(self, ctx: JavaParserLabeled.Expression21Context):
        if self.in_source_class and self.in_selected_package:
            if ctx.expression(0).getText() == self.field_identifier or \
                    ctx.expression(0).getText() == 'this.' + self.field_identifier:
                expr_code = self.token_stream_rewriter.getText(
                    program_name=self.token_stream_rewriter.DEFAULT_PROGRAM_NAME,
                    start=ctx.expression(1).start.tokenIndex,
                    stop=ctx.expression(1).stop.tokenIndex)
                new_code = 'this.set' + str.capitalize(self.field_identifier) + '(' + expr_code + ')'
                self.token_stream_rewriter.replaceRange(ctx.start.tokenIndex, ctx.stop.tokenIndex, new_code)

    def exitExpression0(self, ctx: JavaParserLabeled.Expression0Context):
        if self.in_source_class and self.in_selected_package:
            try:
                if ctx.parentCtx.getChild(1).getText() in ('=', '+=', '-=',
                                                           '*=', '/=', '&=',
                                                           '|=', '^=', '>>=',
                                                           '>>>=', '<<=', '%=') and \
                        ctx.parentCtx.getChild(0) == ctx:
                    return
            except:
                pass
            if ctx.getText() == self.field_identifier:
                new_code = 'this.get' + str.capitalize(self.field_identifier) + '()'
                self.token_stream_rewriter.replaceRange(ctx.start.tokenIndex,
                                                        ctx.stop.tokenIndex,
                                                        new_code)

    def exitExpression1(self, ctx: JavaParserLabeled.Expression1Context):
        if self.in_source_class and self.in_selected_package:
            try:
                if ctx.parentCtx.getChild(1).getText() in ('=', '+=', '-=',
                                                           '*=', '/=', '&=',
                                                           '|=', '^=', '>>=',
                                                           '>>>=', '<<=', '%=') and \
                        ctx.parentCtx.getChild(0) == ctx:
                    return
            except:
                pass
            if ctx.getText() == 'this.' + self.field_identifier:
                new_code = 'this.get' + str.capitalize(self.field_identifier) + '()'
                self.token_stream_rewriter.replaceRange(ctx.start.tokenIndex,
                                                        ctx.stop.tokenIndex,
                                                        new_code)

    def exitCompilationUnit(self, ctx: JavaParserLabeled.CompilationUnitContext):
        try:
            hidden = self.token_stream.getHiddenTokensToLeft(ctx.start.tokenIndex)
            self.token_stream_rewriter.replaceRange(from_idx=hidden[0].tokenIndex,
                                                    to_idx=hidden[-1].tokenIndex,
                                                    text='/*After refactoring (Refactored version)*/\n')
        except:
            pass

__init__(self, common_token_stream=None, package_name=None, source_class_name=None, field_identifier=None) special

Parameters:

Name Type Description Default
common_token_stream CommonTokenStream

contains the program tokens

None
package_name str

The enclosing package of the field

None
source_class_name str

The enclosing class of the field

None
field_identifier str

The field name to be encapsulated

None

Returns:

Type Description
object (DecreaseMethodVisibilityListener)

An instance of EncapsulateFiledRefactoringListener

Source code in codart\refactorings\encapsulate_field.py
def __init__(self, common_token_stream: CommonTokenStream = None,
             package_name: str = None,
             source_class_name: str = None,
             field_identifier: str = None):
    """

    Args:

        common_token_stream (CommonTokenStream): contains the program tokens

        package_name (str): The enclosing package of the field

        source_class_name (str): The enclosing class of the field

        field_identifier (str): The field name to be encapsulated

    Returns:

        object (DecreaseMethodVisibilityListener): An instance of EncapsulateFiledRefactoringListener

    """

    self.token_stream = common_token_stream
    if package_name is None:
        self.package_name = ''
    else:
        self.package_name = package_name
    self.source_class_name = source_class_name
    self.field_identifier = field_identifier
    self.getter_exist = False
    self.setter_exist = False
    self.in_source_class = False
    self.in_selected_package = True if self.package_name == '' else False

    # Move all the tokens in the source code in a buffer, token_stream_rewriter.
    if common_token_stream is not None:
        self.token_stream_rewriter = \
            TokenStreamRewriter(common_token_stream)
    else:
        raise TypeError('common_token_stream is None')