java代码绘制类图,python脚本实现解析代码为plantUML

内容完全由AI编写,仅经过简单筛选和不严谨的测试。
…省略AI思考过程…
…省略开始前的废话和失败尝试…
安装依赖

pip install javalang

脚本正文

import os
import javalang
import argparse
from typing import List, Dict, Optional, Set
from collections import defaultdict


class JavaToPlantUMLConverter:
    def __init__(self, root_dir: str, output_file: str = "output.puml"):
        self.root_dir = root_dir
        self.output_file = output_file
        self.declared_types: List[Dict] = []  # 所有解析到的类/接口信息

    @staticmethod
    def extract_types(root) -> Set[str]:
        """从节点树中提取所有涉及的类名(过滤Java内置类型)"""
        types = set()
        if not root:
            return types
        
        # 基本类型和常见Java内置类列表(过滤不显示依赖)
        basic_types = {
            # 基础类型及包装类
            'int', 'long', 'float', 'double', 'boolean', 'char', 'byte', 'short', 'void',
            'Integer', 'Long', 'Double', 'Float', 'Boolean', 'Byte', 'Short', 'Character', 'Void',
            # 核心类库
            'Object', 'String', 'Thread', 'Class', 'System', 'Runtime', 
            'Exception', 'Throwable', 'Error', 'StackTraceElement',
            # 集合框架
            'List', 'Map', 'Set', 'Collection', 'ArrayList', 'HashMap', 'HashSet',
            'LinkedList', 'TreeMap', 'TreeSet', 'Iterator', 'Iterable',
            # IO相关
            'File', 'InputStream', 'OutputStream', 'Reader', 'Writer',
            'FileInputStream', 'FileOutputStream', 'BufferedReader',
            # 其他常用类
            'URL', 'Connection', 'ThreadPoolExecutor', 'LocalDate', 'LocalTime', 'LocalDateTime',
            'BigInteger', 'BigDecimal', 'StringBuilder', 'StringBuffer',
            'DataLine', 'SourceDataLine', 'AudioInputStream', 'AudioSystem', 'FloatControl',
            None
        }

        # 处理方法抛出的异常类型
        if isinstance(root, javalang.tree.MethodDeclaration) and root.throws:
            for exception in root.throws:
                if exception not in basic_types:
                    types.add(exception)

        # 处理引用类型(含泛型)
        if isinstance(root, javalang.tree.ReferenceType):
            # 提取泛型参数类型
            if root.arguments:
                for arg in root.arguments:
                    types.update(JavaToPlantUMLConverter.extract_types(arg))
            # 提取主类型
            if root.name not in basic_types:
                types.add(root.name)

        # 处理类型参数和基础类型
        elif isinstance(root, (javalang.tree.TypeParameter, javalang.tree.Type)):
            if hasattr(root, 'name') and root.name not in basic_types:
                types.add(root.name)

        # 递归处理节点的子元素
        elif isinstance(root, javalang.ast.Node):
            for child in root.children:
                child_types = JavaToPlantUMLConverter.extract_types(child)
                types.update(child_types)

        # 处理列表/元组类型的子元素
        elif isinstance(root, (list, tuple)):
            for item in root:
                item_types = JavaToPlantUMLConverter.extract_types(item)
                types.update(item_types)

        return types

    def find_java_files(self) -> List[str]:
        """查找指定目录下的所有Java文件"""
        java_files = []
        for root, _, files in os.walk(self.root_dir):
            for file in files:
                if file.endswith(".java"):
                    java_files.append(os.path.join(root, file))
        return java_files

    def parse_java_file(self, file_path: str) -> Optional[List[Dict]]:
        """解析单个Java文件,返回所有类/接口声明信息"""
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                code = f.read()

            tree = javalang.parse.parse(code)
            package_name = tree.package.name if tree.package else ""
            declared_types = []

            # 处理文件中所有类/接口声明
            for type_declaration in tree.types:
                if isinstance(type_declaration, (javalang.tree.ClassDeclaration, javalang.tree.InterfaceDeclaration)):
                    dec_type = {
                        'name': type_declaration.name,
                        'package': package_name,
                        'full_name': f"{package_name}.{type_declaration.name}" if package_name else type_declaration.name,
                        'is_interface': isinstance(type_declaration, javalang.tree.InterfaceDeclaration),
                        'extends': [],
                        'implements': [],
                        'fields': [],
                        'methods': [],
                        'raw_dependencies': set()
                    }

                    # 处理继承关系
                    if hasattr(type_declaration, "extends") and type_declaration.extends:
                        for ext in type_declaration.extends:
                            ext_types = JavaToPlantUMLConverter.extract_types(ext)
                            dec_type['extends'].extend(ext_types)
                            dec_type['raw_dependencies'].update(ext_types)

                    # 处理实现关系
                    if hasattr(type_declaration, "implements") and type_declaration.implements:
                        for impl in type_declaration.implements:
                            impl_types = JavaToPlantUMLConverter.extract_types(impl)
                            dec_type['implements'].extend(impl_types)
                            dec_type['raw_dependencies'].update(impl_types)

                    # 处理字段
                    if hasattr(type_declaration, "fields") and type_declaration.fields:
                        for field in type_declaration.fields:
                            field_types = JavaToPlantUMLConverter.extract_types(field.type)
                            dec_type['raw_dependencies'].update(field_types)
                            for declarator in field.declarators:
                                dec_type['fields'].append({
                                    'name': declarator.name,
                                    'type': field.type.name,
                                    'modifiers': field.modifiers
                                })

                    # 处理方法
                    if hasattr(type_declaration, "methods") and type_declaration.methods:
                        for method in type_declaration.methods:
                            method_types = JavaToPlantUMLConverter.extract_types(method)
                            dec_type['raw_dependencies'].update(method_types)
                            dec_type['methods'].append({
                                'name': method.name,
                                'return_type': method.return_type.name if method.return_type else None,
                                'parameters': method.parameters,
                                'modifiers': method.modifiers,
                                'throws': method.throws
                            })

                    declared_types.append(dec_type)

            return declared_types

        except Exception as e:
            print(f"解析文件失败: {file_path}")
            print(f"错误信息: {str(e)}")
            return None

    def resolve_extends(self) -> Dict[str, Set[str]]:
        """解析继承关系(类->父类,接口->父接口)"""
        extends = defaultdict(set)
        simple_to_full = defaultdict(list)

        # 建立简单类名到全类名的映射
        for dec_type in self.declared_types:
            simple_to_full[dec_type['name']].append(dec_type['full_name'])

        # 解析继承关系
        for dec_type in self.declared_types:
            for parent_simple in dec_type['extends']:
                possible_full = simple_to_full.get(parent_simple, [])
                if not possible_full:
                    continue
                # 优先匹配同包类
                same_package = [fn for fn in possible_full if fn.startswith(dec_type['package'])]
                target_full = same_package[0] if same_package else possible_full[0]
                if target_full != dec_type['full_name']:
                    extends[dec_type['full_name']].add(target_full)

        return extends

    def resolve_implements(self) -> Dict[str, Set[str]]:
        """解析实现关系(类->接口)"""
        implements = defaultdict(set)
        simple_to_full = defaultdict(list)

        for dec_type in self.declared_types:
            simple_to_full[dec_type['name']].append(dec_type['full_name'])

        for dec_type in self.declared_types:
            if dec_type['is_interface']:
                continue  # 接口不实现其他类型
            for iface_simple in dec_type['implements']:
                possible_full = simple_to_full.get(iface_simple, [])
                if not possible_full:
                    continue
                same_package = [fn for fn in possible_full if fn.startswith(dec_type['package'])]
                target_full = same_package[0] if same_package else possible_full[0]
                if target_full != dec_type['full_name']:
                    implements[dec_type['full_name']].add(target_full)

        return implements

    def resolve_dependencies(self) -> Dict[str, Set[str]]:
        """解析依赖关系(字段/参数/返回值引用)"""
        dependencies = defaultdict(set)
        simple_to_full = defaultdict(list)

        for dec_type in self.declared_types:
            simple_to_full[dec_type['name']].append(dec_type['full_name'])

        for dec_type in self.declared_types:
            # 排除继承和实现关系(已单独处理)
            filtered_deps = dec_type['raw_dependencies'] - set(dec_type['extends']) - set(dec_type['implements'])
            for dep_simple in filtered_deps:
                possible_full = simple_to_full.get(dep_simple, [])
                if not possible_full:
                    continue
                same_package = [fn for fn in possible_full if fn.startswith(dec_type['package'])]
                target_full = same_package[0] if same_package else possible_full[0]
                if target_full != dec_type['full_name']:
                    dependencies[dec_type['full_name']].add(target_full)

        return dependencies

    @staticmethod
    def modifier_to_plantuml(modifiers: List[str]) -> str:
        """将Java修饰符转换为PlantUML符号"""
        if 'private' in modifiers:
            return "-"
        elif 'protected' in modifiers:
            return "#"
        elif 'public' in modifiers:
            return "+"
        else:
            return "~"  # 包私有

    def generate_plantuml(self) -> str:
        """生成完整的PlantUML代码"""
        plantuml = "@startuml\n"
        plantuml += "skinparam monochrome true\n"
        plantuml += "skinparam classAttributeIconSize 0\n"
        plantuml += "skinparam roundCorner 6\n"  # 美化:圆角矩形

        # 1. 定义所有类和接口
        for dec_type in self.declared_types:
            if dec_type['is_interface']:
                plantuml += f"interface {dec_type['full_name']} {{\n"
            else:
                plantuml += f"class {dec_type['full_name']} {{\n"

            # 字段定义
            for field in dec_type['fields']:
                modifier = self.modifier_to_plantuml(field['modifiers'])
                plantuml += f"\t{modifier}{field['name']}: {field['type']}\n"

            # 字段和方法之间空行分隔
            if dec_type['fields'] and dec_type['methods']:
                plantuml += "\n"

            # 方法定义
            for method in dec_type['methods']:
                modifier = self.modifier_to_plantuml(method['modifiers'])
                # 处理参数
                params = []
                for param in method['parameters']:
                    params.append(f"{param.type.name} {param.name}")
                params_str = ", ".join(params)
                # 处理返回值
                return_type = method['return_type'] or 'void'
                plantuml += f"\t{modifier}{method['name']}({params_str}): {return_type}\n"

            plantuml += "}\n\n"

        # 2. 实现关系(接口 <|.. 类)
        implements = self.resolve_implements()
        for impl_class, interfaces in implements.items():
            for iface in interfaces:
                plantuml += f"{iface} <|.. {impl_class}\n"

        # 3. 继承关系(父类 <|-- 子类)
        extends = self.resolve_extends()
        for sub_class, super_classes in extends.items():
            for super_class in super_classes:
                plantuml += f"{super_class} <|-- {sub_class}\n"

        # 4. 依赖关系(被依赖类 <.. 依赖类)
        dependencies = self.resolve_dependencies()
        for dep_class, target_classes in dependencies.items():
            for target in target_classes:
                plantuml += f"{target} <.. {dep_class}\n"

        plantuml += "@enduml"
        return plantuml

    def process(self):
        """主处理流程:解析文件 -> 生成PlantUML"""
        # 查找并解析所有Java文件
        java_files = self.find_java_files()
        print(f"找到 {len(java_files)} 个Java文件")

        for file in java_files:
            types = self.parse_java_file(file)
            if types:
                self.declared_types.extend(types)
                print(f"已解析文件: {file}(包含 {len(types)} 个类/接口)")

        if not self.declared_types:
            print("未解析到任何类或接口,无法生成PlantUML")
            return

        # 生成并保存PlantUML代码
        plantuml_code = self.generate_plantuml()
        with open(self.output_file, 'w', encoding='utf-8') as f:
            f.write(plantuml_code)

        print(f"\nPlantUML文件已生成: {os.path.abspath(self.output_file)}")
        print(f"包含 {len(self.declared_types)} 个类/接口,可使用PlantUML工具渲染")


def main():
    # 解析命令行参数
    parser = argparse.ArgumentParser(description="将Java代码转换为PlantUML类图(支持依赖关系解析)")
    parser.add_argument("input_dir", help="Java源代码目录")
    parser.add_argument("-o", "--output", default="output.puml", help="输出的PlantUML文件名(默认:output.puml)")
    args = parser.parse_args()

    # 验证输入目录
    if not os.path.isdir(args.input_dir):
        print(f"错误:输入路径 '{args.input_dir}' 不是有效的目录")
        return

    # 执行转换
    converter = JavaToPlantUMLConverter(args.input_dir, args.output)
    converter.process()


if __name__ == "__main__":
    main()

正文结束,然后是废话。

主要步骤:

遍历指定目录下的所有Java文件。

使用javalang解析每个Java文件,提取类/接口的声明信息,包括名称、包、字段、方法、继承和实现关系。

从字段、方法参数、返回值等中提取依赖的类型。

解析类之间的继承关系、实现关系和依赖关系。

生成PlantUML代码,包括类/接口的定义、字段、方法以及关系。

…省略AI的的更多废话…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值