def _calculate_class_RFC(self, java_class: AST) -> int: class_declaration = java_class.get_root() assert class_declaration.node_type == ASTNodeType.CLASS_DECLARATION rfc = 0 invoked_methods: Set[_MethodInvocationParams] = set() local_methods_names: Set[str] = set() for method_ast in java_class.get_subtrees( ASTNodeType.METHOD_DECLARATION): method_declaration = method_ast.get_root() local_methods_names.add(method_declaration.name) if "public" in method_declaration.modifiers: rfc += 1 invoked_methods |= self._get_all_method_invocation_params( method_ast) # filter out inherited methods # consider local methods with name not found # among methods names of current class as inherited invoked_methods = { invoked_method for invoked_method in invoked_methods if not invoked_method.isLocal or invoked_method.name in local_methods_names } rfc += len(invoked_methods) return rfc
def extract_method_statements_semantic( method_ast: AST) -> Dict[ASTNode, StatementSemantic]: statement_semantic: Dict[ASTNode, StatementSemantic] = OrderedDict() for statement in method_ast.get_root().body: statement_semantic.update( _extract_statement_semantic(statement, method_ast)) return statement_semantic
def _calcalute_diameter(self, ast: AST) -> int: distant_node_from_root, _ = self._find_distant_node( ast, ast.get_root(), False) # traverse undirected graph, because we need to ba able to traverse from child to parent in general # it is not needed at previous call, because the most distant node of a tree is anyway a child of root # and there is no need to traverse from child to parent, which simply safe time _, diameter = self._find_distant_node(ast, distant_node_from_root, True) return diameter
def _calculate_class_fan_out(self, java_class: AST) -> int: class_declaration = java_class.get_root() assert class_declaration.node_type == ASTNodeType.CLASS_DECLARATION used_classes_names: Set[str] = set() for type_reference in java_class.get_proxy_nodes( ASTNodeType.REFERENCE_TYPE): used_class_name = self._get_class_name_from_type_reference( type_reference) if used_class_name not in FanOut._excluded_class_names: used_classes_names.add(used_class_name) # remove name of the class used_classes_names -= {class_declaration.name} return len(used_classes_names)
def find_patterns(tree: AST, patterns: List[Any]) -> Set[str]: """ Searches all setters in a component :param patterns: list of patterns to check :param tree: ast tree :return: list of method name which are setters """ patterns_method_names: Set[str] = set() for method_declaration in tree.get_root().methods: method_ast = tree.get_subtree(method_declaration) for pattern in patterns: if is_ast_pattern(method_ast, pattern): patterns_method_names.add(method_declaration.name) return patterns_method_names
def _filter_class_methods_and_fields(class_ast: AST, allowed_fields_names: Set[str], allowed_methods_names: Set[str]) -> AST: class_declaration = class_ast.get_root() allowed_nodes = {class_declaration.node_index} for field_declaration in class_declaration.fields: if len(allowed_fields_names & set(field_declaration.names)) != 0: field_ast = class_ast.get_subtree(field_declaration) allowed_nodes.update(node.node_index for node in field_ast) for method_declaration in class_declaration.methods: if method_declaration.name in allowed_methods_names: method_ast = class_ast.get_subtree(method_declaration) allowed_nodes.update(node.node_index for node in method_ast) return AST(class_ast.tree.subgraph(allowed_nodes), class_declaration.node_index)
def _find_fields_usage(method_ast: AST) -> Set[str]: local_variables: Set[str] = set() for variable_declaration in method_ast.get_proxy_nodes( ASTNodeType.LOCAL_VARIABLE_DECLARATION): local_variables.update(variable_declaration.name) method_declaration = method_ast.get_root() for parameter in method_declaration.parameters: local_variables.add(parameter.name) used_fields: Set[str] = set() for member_reference in method_ast.get_proxy_nodes( ASTNodeType.MEMBER_REFERENCE): if member_reference.qualifier is None and \ member_reference.member not in local_variables: used_fields.add(member_reference.member) return used_fields
def _create_usage_graph(class_ast: AST) -> DiGraph: usage_graph = DiGraph() fields_ids: Dict[str, int] = {} methods_ids: Dict[str, int] = {} class_declaration = class_ast.get_root() for field_declaration in class_declaration.fields: # several fields can be declared at one line for field_name in field_declaration.names: fields_ids[field_name] = len(fields_ids) usage_graph.add_node(fields_ids[field_name], type="field", name=field_name) for method_declaration in class_declaration.methods: method_name = method_declaration.name # overloaded methods considered as single node in usage_graph if method_name not in methods_ids: methods_ids[method_name] = len(fields_ids) + 1 + len(methods_ids) usage_graph.add_node(methods_ids[method_name], type="method", name=method_name) for method_declaration in class_declaration.methods: method_ast = class_ast.get_subtree(method_declaration) for invoked_method_name in _find_local_method_invocations(method_ast): if invoked_method_name in methods_ids: usage_graph.add_edge( methods_ids[method_declaration.name], methods_ids[invoked_method_name], ) for used_field_name in _find_fields_usage(method_ast): if used_field_name in fields_ids: usage_graph.add_edge(methods_ids[method_declaration.name], fields_ids[used_field_name]) return usage_graph