Skip to content

Pull-up method

Introduction

The module implements pull-up method refactoring operation.

Pre-conditions:

Todo: Add pre-conditions

Post-conditions:

Todo: Add post-conditions

PullUpMethodRefactoringListener (JavaParserLabeledListener)

To implement pull-up method refactoring based on its actors.

Source code in codart\refactorings\pullup_method.py
class PullUpMethodRefactoringListener(JavaParserLabeledListener):
    """

    To implement pull-up method refactoring based on its actors.

    """

    def __init__(self, common_token_stream: CommonTokenStream = None, destination_class: str = None,
                 children_class: list = None, moved_methods=None, method_text: str = None):
        """


        """

        if method_text is None:
            self.mothod_text = []
        else:
            self.method_text = method_text

        if moved_methods is None:
            self.moved_methods = []
        else:
            self.moved_methods = moved_methods
        if children_class is None:
            self.children_class = []
        else:
            self.children_class = children_class
        if common_token_stream is None:
            raise ValueError('common_token_stream is None')
        else:
            self.token_stream_rewriter = TokenStreamRewriter(common_token_stream)

        if destination_class is None:
            raise ValueError("source_class is None")
        else:
            self.destination_class = destination_class

        self.is_children_class = False
        self.detected_field = None
        self.detected_method = None
        self.TAB = "\t"
        self.NEW_LINE = "\n"
        self.code = ""
        self.tempdeclarationcode = ""

    def enterMethodDeclaration(self, ctx: JavaParserLabeled.MethodDeclarationContext):

        if self.is_children_class:

            method_identifier = ctx.IDENTIFIER().getText()
            if self.moved_methods == method_identifier:
                methodDefctx = ctx.parentCtx.parentCtx
                start_index = methodDefctx.start.tokenIndex
                stop_index = methodDefctx.stop.tokenIndex
                self.method_text = self.token_stream_rewriter.getText(
                    program_name=self.token_stream_rewriter.DEFAULT_PROGRAM_NAME,
                    start=start_index,
                    stop=stop_index)

                self.token_stream_rewriter.delete(
                    program_name=self.token_stream_rewriter.DEFAULT_PROGRAM_NAME,
                    from_idx=methodDefctx.start.tokenIndex,
                    to_idx=methodDefctx.stop.tokenIndex
                )
        else:
            return None

    def enterClassDeclaration(self, ctx: JavaParserLabeled.ClassDeclarationContext):
        class_identifier = ctx.IDENTIFIER().getText()
        if class_identifier in self.children_class:
            self.is_children_class = True

        else:
            # Enter another class
            self.is_children_class = False

    def enterClassBody(self, ctx: JavaParserLabeled.ClassBodyContext):
        classDecctx = ctx.parentCtx
        if hasattr(classDecctx, "IDENTIFIER"):
            class_identifier = classDecctx.IDENTIFIER().getText()

            if class_identifier in self.destination_class:
                self.token_stream_rewriter.replaceRange(
                    from_idx=ctx.start.tokenIndex + 1,
                    to_idx=ctx.start.tokenIndex + 1,
                    text="\n" + self.method_text + "\n"
                )

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

    def exitCompilationUnit(self, ctx: JavaParserLabeled.CompilationUnitContext):

        self.token_stream_rewriter.insertAfter(
            index=ctx.stop.tokenIndex,
            text=self.code
        )

__init__(self, common_token_stream=None, destination_class=None, children_class=None, moved_methods=None, method_text=None) special

Source code in codart\refactorings\pullup_method.py
def __init__(self, common_token_stream: CommonTokenStream = None, destination_class: str = None,
             children_class: list = None, moved_methods=None, method_text: str = None):
    """


    """

    if method_text is None:
        self.mothod_text = []
    else:
        self.method_text = method_text

    if moved_methods is None:
        self.moved_methods = []
    else:
        self.moved_methods = moved_methods
    if children_class is None:
        self.children_class = []
    else:
        self.children_class = children_class
    if common_token_stream is None:
        raise ValueError('common_token_stream is None')
    else:
        self.token_stream_rewriter = TokenStreamRewriter(common_token_stream)

    if destination_class is None:
        raise ValueError("source_class is None")
    else:
        self.destination_class = destination_class

    self.is_children_class = False
    self.detected_field = None
    self.detected_method = None
    self.TAB = "\t"
    self.NEW_LINE = "\n"
    self.code = ""
    self.tempdeclarationcode = ""

main(udb_path, children_classes, method_name, *args, **kwargs)

Source code in codart\refactorings\pullup_method.py
def main(udb_path: str, children_classes: list, method_name: str, *args, **kwargs):
    """


    """

    if len(children_classes) <= 1:
        logger.error("len(children_classes) should be gte 2")
        return False

    # Initialize with understand
    destination_class = ""
    fileslist_to_be_rafeactored = set()
    fileslist_to_be_propagate = set()
    propagation_classes = set()

    db = und.open(udb_path)
    try:
        method_ents = [db.lookup(i + "." + method_name, "method")[0] for i in children_classes]
    except IndexError:
        # print([db.lookup(i + "." + method_name, "method") for i in children_classes])
        logger.error(f"Method {method_name} does not exists in all children_classes.")
        db.close()
        return False

    # Get method text
    method_text = method_ents[0].contents().strip()

    for method_ent in method_ents:
        if method_ent.contents().strip() != method_text:
            logger.error("Method content is different.")
            db.close()
            return False

        for ref in method_ent.refs("Use,Call"):
            if ref.ent().parent() is not None:
                if ref.ent().parent().simplename() in children_classes:
                    logger.error("Method has internal dependencies.")
                    db.close()
                    return False

    for mth in db.ents("Java Method"):
        for child in children_classes:
            if mth.longname().endswith(child + "." + method_name):
                fileslist_to_be_rafeactored.add(mth.parent().parent().longname())
                for fth in mth.parent().refs("Extend"):
                    destination_class = fth.ent().longname()
                    fileslist_to_be_rafeactored.add(fth.ent().parent().longname())
                for ref in mth.refs("Java Callby"):
                    propagation_classes.add(ref.ent().parent().longname())
                    fileslist_to_be_propagate.add(ref.ent().parent().parent().longname())

    db.close()

    # print("=========================================")
    # print("fileslist_to_be_propagate :", fileslist_to_be_propagate)
    # print("propagation_classes : ", propagation_classes)
    # print("fileslist_to_be_rafeactored :", fileslist_to_be_rafeactored)
    # print("father class :", destination_class)

    fileslist_to_be_rafeactored = list(fileslist_to_be_rafeactored)
    fileslist_to_be_propagate = list(fileslist_to_be_propagate)
    propagation_class = list(propagation_classes)

    # refactored start
    for file in fileslist_to_be_rafeactored:
        try:
            stream = FileStream(file, encoding='utf-8', errors='ignore')
        except:
            continue
        lexer = JavaLexer(stream)
        token_stream = CommonTokenStream(lexer)
        parser = JavaParserLabeled(token_stream)
        parser.getTokenStream()
        parse_tree = parser.compilationUnit()
        my_listener_refactor = PullUpMethodRefactoringListener(common_token_stream=token_stream,
                                                               destination_class=destination_class,
                                                               children_class=children_classes,
                                                               moved_methods=method_name,
                                                               method_text=method_text)
        walker = ParseTreeWalker()
        walker.walk(t=parse_tree, listener=my_listener_refactor)

        with open(file, mode='w', encoding='utf-8', newline='') as f:
            f.write(my_listener_refactor.token_stream_rewriter.getDefaultText())
    # end refactoring

    # beginning of propagate
    for file in fileslist_to_be_propagate:
        if not os.path.exists(file):
            continue
        stream = FileStream(file, encoding='utf-8', errors='ignore')
        lexer = JavaLexer(stream)
        token_stream = CommonTokenStream(lexer)
        parser = JavaParserLabeled(token_stream)
        parser.getTokenStream()
        parse_tree = parser.compilationUnit()
        my_listener_propagate = PropagationPullUpMethodRefactoringListener(token_stream_rewriter=token_stream,
                                                                           old_class_name=children_classes,
                                                                           new_class_name=destination_class,
                                                                           propagated_class_name=propagation_class)
        walker = ParseTreeWalker()
        walker.walk(t=parse_tree, listener=my_listener_propagate)

        with open(file, mode='w', encoding='utf8', errors='ignore', newline='') as f:
            f.write(my_listener_propagate.token_stream_rewriter.getDefaultText())
    # end of propagate

    return True