Move field
Introduction
The module implements Move Field refactoring operation
Pre and post-conditions
Pre-conditions:
Todo: Add pre-conditions
Post-conditions:
Todo: Add post-conditions
CutFieldListener (JavaParserLabeledListener)
Source code in codart\refactorings\move_field.py
class CutFieldListener(JavaParserLabeledListener):
"""
"""
def __init__(self, class_name: str, instance_name: str, field_name: str, is_static: bool, import_statement: str,
rewriter: TokenStreamRewriter):
"""
"""
self.class_name = class_name
self.field_name = field_name
self.is_static = is_static
self.import_statement = import_statement
self.rewriter = rewriter
self.instance_name = instance_name
self.instance_name = class_name.lower() + "ByCodArt"
self.is_member = False
self.do_delete = False
self.field_text = ""
def exitPackageDeclaration(self, ctx: JavaParserLabeled.PackageDeclarationContext):
if self.import_statement:
self.rewriter.insertAfterToken(
token=ctx.stop,
text=self.import_statement,
program_name=self.rewriter.DEFAULT_PROGRAM_NAME
)
self.import_statement = None
def enterMemberDeclaration2(self, ctx: JavaParserLabeled.MemberDeclaration2Context):
self.is_member = True
def exitMemberDeclaration2(self, ctx: JavaParserLabeled.MemberDeclaration2Context):
self.is_member = False
def enterVariableDeclaratorId(self, ctx: JavaParserLabeled.VariableDeclaratorIdContext):
if self.is_member and ctx.IDENTIFIER().getText() == self.field_name:
self.do_delete = True
def exitClassBodyDeclaration2(self, ctx: JavaParserLabeled.ClassBodyDeclaration2Context):
if self.do_delete:
self.field_text = self.rewriter.getText(
program_name=self.rewriter.DEFAULT_PROGRAM_NAME,
start=ctx.start.tokenIndex,
stop=ctx.stop.tokenIndex
)
if self.is_static:
replace_text = f"public static {self.class_name} {self.instance_name} = new {self.class_name}();"
else:
replace_text = f"public {self.class_name} {self.instance_name} = new {self.class_name}();"
self.rewriter.replace(
program_name=self.rewriter.DEFAULT_PROGRAM_NAME,
from_idx=ctx.start.tokenIndex,
to_idx=ctx.stop.tokenIndex,
text=replace_text
)
self.do_delete = False
__init__(self, class_name, instance_name, field_name, is_static, import_statement, rewriter)
special
Source code in codart\refactorings\move_field.py
def __init__(self, class_name: str, instance_name: str, field_name: str, is_static: bool, import_statement: str,
rewriter: TokenStreamRewriter):
"""
"""
self.class_name = class_name
self.field_name = field_name
self.is_static = is_static
self.import_statement = import_statement
self.rewriter = rewriter
self.instance_name = instance_name
self.instance_name = class_name.lower() + "ByCodArt"
self.is_member = False
self.do_delete = False
self.field_text = ""
main(source_class, source_package, target_class, target_package, field_name, udb_path, *args, **kwargs)
Move filed main API
Source code in codart\refactorings\move_field.py
def main(source_class: str, source_package: str, target_class: str, target_package: str, field_name: str,
udb_path: str, *args, **kwargs):
"""
Move filed main API
"""
import_statement = None
if source_package != target_package:
import_statement = f"\nimport {target_package}.{target_class};"
instance_name = target_class.lower() + "ByCodArt"
db = und.open(udb_path)
# Check if field is static
field_ent = db.lookup(f"{source_package}.{source_class}.{field_name}", "Variable")
if len(field_ent) == 0:
logger.error(f"Entity not found with query: {source_package}.{source_class}.{field_name}.")
db.close()
return False
if source_package == target_package and source_class == target_class:
logger.error("Can not move to self.")
db.close()
return False
field_ent = field_ent[0]
is_static = field_ent.kindname() == STATIC
if is_static:
logger.warning("Field is static!")
# Find usages
usages = {}
for ref in field_ent.refs("Setby, Useby"):
file = ref.file().longname()
if file in usages:
usages[file].append(ref.line())
else:
usages[file] = [ref.line(), ]
try:
src_class_file = db.lookup(f"{source_package}.{source_class}.java")[0].longname()
target_class_file = db.lookup(f"{target_package}.{target_class}.java")[0].longname()
except IndexError:
logger.error("This is a nested class.")
logger.info(f"{source_package}.{source_class}.java")
logger.info(f"{target_package}.{target_class}.java")
db.close()
return False
db.close()
# Check if there is an cycle
listener = parse_and_walk(
file_path=target_class_file,
listener_class=CheckCycleListener,
class_name=source_class,
)
if not listener.is_valid:
logger.error(f"Can not move field because there is a cycle between {source_class}, {target_class}")
# db.close()
return False
# Propagate Changes
for file in usages.keys():
parse_and_walk(
file_path=file,
listener_class=PropagateListener,
has_write=True,
field_name=field_name,
new_name=f"{instance_name}.{field_name}",
lines=usages[file],
)
# Do the cut and paste!
# Cut
listener = parse_and_walk(
file_path=src_class_file,
listener_class=CutFieldListener,
has_write=True,
class_name=target_class,
instance_name=instance_name,
field_name=field_name,
is_static=is_static,
import_statement=import_statement
)
field_text = listener.field_text
# Paste
parse_and_walk(
file_path=target_class_file,
listener_class=PasteFieldListener,
has_write=True,
field_text=field_text,
)
# db.close()
return True