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')