内容完全由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的的更多废话…
696

被折叠的 条评论
为什么被折叠?



