reproto 0.1.1__py3-none-any.whl → 0.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- .git/COMMIT_EDITMSG +29 -1
- .git/index +0 -0
- .git/logs/HEAD +4 -0
- .git/logs/refs/heads/iyue +4 -0
- .git/logs/refs/remotes/gitlab/iyue +4 -0
- .git/logs/refs/remotes/origin/iyue +4 -0
- .git/objects/00/81a7e1ec3251cc192ed2b73d5be897593fb872 +0 -0
- .git/objects/00/885cebf557ff261574deb93cb96449d06db01c +0 -0
- .git/objects/02/33f5b60263e5dc6d041c0b223a0724eb650faa +0 -0
- .git/objects/03/3fab23ccc74b707a31f6f37dc5df42c100aac0 +0 -0
- .git/objects/07/557fbd1d149ce51af8e98e8ca8590757a89dfa +0 -0
- .git/objects/07/b71df33935c14ddf99e3480c04eca4685028bc +0 -0
- .git/objects/0b/e20ea749c6ae1075ae98f9426e00b0a4a235af +0 -0
- .git/objects/12/f9cc4ad0b4f0af4f7bae379f281b2cebe7cc7f +0 -0
- .git/objects/13/5f50fea51c72f977f48d06a7584aba0f61e260 +0 -0
- .git/objects/1b/97584ebada3e93d7a2cfa4a1aae0b79c414d20 +0 -0
- .git/objects/1b/f0c1ad9586578e8332d061f7648dcb041ec063 +0 -0
- .git/objects/1e/46816d16e7c9db7575f1964403c3daa105be5c +0 -0
- .git/objects/24/d78e796570a8572a03bc1dd26608a7cfb506f8 +2 -0
- .git/objects/27/24208ca2442e8ab9b453d0eb230fa41243b270 +0 -0
- .git/objects/2a/fe93d8bcbeab9e136d8b6766604c32b3610314 +0 -0
- .git/objects/2d/2b812ca27c477f9e1b2c2706a5eb795ffcf8eb +0 -0
- .git/objects/2d/8a0da260a710010ae62be134ac1cea6ceecfd1 +0 -0
- .git/objects/2d/e8ecbb5ab5de1a032bef3f4606ce5fa7c6c4e8 +0 -0
- .git/objects/30/9347e5681d80bd3c7949882e27090dd9070d16 +0 -0
- .git/objects/33/633c9df669ff8cf38638717937a54990814268 +0 -0
- .git/objects/39/993e3600bf4ab82aa361b738ee97a108787450 +0 -0
- .git/objects/3b/ab663710fd6b43d9372313fced9043c4cb07dd +0 -0
- .git/objects/3e/b3f1273caf6814dfa69325ccbd9fd1340cf20a +0 -0
- .git/objects/3f/b8830f516342a0ae1cb7c34b65016827cb9570 +4 -0
- .git/objects/41/f40c22247de377be99e30784229f3f128508a2 +0 -0
- .git/objects/45/fbc774dedb61c7c205ea732f59a8dca8d13555 +3 -0
- .git/objects/48/cb11e75518a53be14146018214110986fade67 +0 -0
- .git/objects/4f/c6dc41f9c0a1a8e0eedd3ba49c43d78d0dbaba +0 -0
- .git/objects/59/4c23f158ccbd0a4288f9ea046d06160195afbf +0 -0
- .git/objects/5b/d4d87753b79e9157817f0c2e6964a731052854 +0 -0
- .git/objects/60/5cb6fd6a9f8894ad4d43a9b8e4785c1b3b0e17 +1 -0
- .git/objects/60/f61a0ea50091eac8d344c86597375cbdfc2785 +0 -0
- .git/objects/63/ddda2a403efaab3f4c6597b3a73a7b1147adb5 +0 -0
- .git/objects/65/0189fe083bd711e45d463b229a72be619abad2 +0 -0
- .git/objects/66/663db35bfec8ef5f1a5b1c840fde1bb62a0eb8 +0 -0
- .git/objects/66/6c5c5fc30435228116fa08c9d821bebaaa8926 +0 -0
- .git/objects/7c/ef0adfb28fd774bc78061c6f088e1ef9b050f6 +0 -0
- .git/objects/7d/dc129188a10c68ab756ef2cacb292c76920403 +0 -0
- .git/objects/80/17038e0f7818a44a742f77c86f4f88ed768fcd +0 -0
- .git/objects/87/c7db6c91c17a2df84b56d30bd24a0f6b9dbdd9 +0 -0
- .git/objects/8a/0ed0ed8886fbc823e8d2258fa6d18699e94e25 +0 -0
- .git/objects/8d/d857b3d0ab3f5cd2e9173d532ef86e30df8eda +0 -0
- .git/objects/92/984cf67b2c25d435468a8218daa26ba0466054 +0 -0
- .git/objects/93/140b54b1fb9116ee214afee8abf2c72a232487 +0 -0
- .git/objects/93/68f2c32f83054ab072b7c9686d8baa0bad7f12 +4 -0
- .git/objects/9a/5ad062be9f6e001f4237a598a08981aba731e6 +0 -0
- .git/objects/9e/23448ac58f907d9d123c32bdccedbb3d6741b5 +0 -0
- .git/objects/a0/d192999af7e2cbfa6a9ccd04d720a04e5a06d5 +0 -0
- .git/objects/a1/655e0cb323c300562f97dcc67d5a446908c8ec +0 -0
- .git/objects/a5/38cc82cef7c49500d3522220f0f60a9ebc1ae6 +0 -0
- .git/objects/a9/41063a7ce89c353fa24378ec7c3f12f08f9df8 +0 -0
- .git/objects/a9/cc7923c34a4c97c5711d6309672f41d46c612a +0 -0
- .git/objects/ac/5c983d949d8c928bb022badf801e45e75e785e +0 -0
- .git/objects/af/c9cc15629847447063e86a82b8b56abb4fc08f +0 -0
- .git/objects/b0/82ca2c1b5a03edff25da3c2b2b573d049877e9 +0 -0
- .git/objects/b1/db1c131cf32916028342c0037ce8eb57a8eb26 +0 -0
- .git/objects/b2/8334b94392b8af397a05ed702690fa6c9ab1ca +0 -0
- .git/objects/b8/7c89dcfce9e244ff5ef6a4bd394de12e8c8092 +0 -0
- .git/objects/bc/e98bdb71c8681acb460195fdcbbe5d36290976 +0 -0
- .git/objects/c1/87d5e047eca86cfd8d444be2987aaa3f62c4d6 +0 -0
- .git/objects/c4/c2da96b0bb8db2acb0e6615cf340c7e51af26b +0 -0
- .git/objects/c5/13a96e7584636b20b12280c029750d5bc3da1e +0 -0
- .git/objects/c7/c34283697bd3cce07db53953eda25ee7cc371e +0 -0
- .git/objects/c9/d60d922a04b87587cd67b0abf9fe5a7b7b76cd +0 -0
- .git/objects/d2/69b1676dbf32f76a7c405d0b4ea6a70ac3a626 +0 -0
- .git/objects/d3/5a918b1d9125ad35d60e08b181323df3246f1a +0 -0
- .git/objects/d8/eaf86669fbfd10497570c1784db1ed2696b588 +0 -0
- .git/objects/d9/3bd435c8c7ad4efb83dff04d5450fabb9e3faf +0 -0
- .git/objects/d9/90e6d553577d37ebce8b28b3015ecbde038b42 +0 -0
- .git/objects/da/13cc15bcd8ee39c81f36dee7f179a569ecab0b +0 -0
- .git/objects/e3/27755808d88c7ae5c06c229cf18bd0519646df +0 -0
- .git/objects/e4/4c1d8a90207ac082d8ab7ff0db66708e2ebc31 +0 -0
- .git/objects/e5/83e7c40be934d16a1fa2e973487b395d930f42 +0 -0
- .git/objects/ed/1ae867d5e63195845afc58d88c38ecbdea97df +0 -0
- .git/objects/ef/f44e5099da27f7fb1ef14bb34902ccf4250b89 +0 -0
- .git/objects/f5/1be495b96272fa2e47f30071aed35ac1f0dd2c +0 -0
- .git/objects/f8/ed595d25bd9d500e765a792c513878f7ddb1f7 +0 -0
- .git/objects/fd/0bc07dc3c95e6168ab6d367d9eca139ac1e539 +0 -0
- .git/refs/heads/iyue +1 -1
- .git/refs/remotes/gitlab/iyue +1 -1
- .git/refs/remotes/origin/iyue +1 -1
- .gitignore +2 -1
- README.md +104 -190
- core/__init__.py +23 -0
- core/info_decoder.py +520 -10
- core/reconstructor.py +159 -21
- generation/__init__.py +17 -0
- generation/proto_generator.py +62 -16
- include/google/protobuf/any.proto +162 -0
- include/google/protobuf/api.proto +207 -0
- include/google/protobuf/compiler/plugin.proto +180 -0
- include/google/protobuf/cpp_features.proto +67 -0
- include/google/protobuf/descriptor.proto +1417 -0
- include/google/protobuf/duration.proto +115 -0
- include/google/protobuf/empty.proto +51 -0
- include/google/protobuf/field_mask.proto +245 -0
- include/google/protobuf/go_features.proto +80 -0
- include/google/protobuf/java_features.proto +130 -0
- include/google/protobuf/source_context.proto +48 -0
- include/google/protobuf/struct.proto +95 -0
- include/google/protobuf/timestamp.proto +144 -0
- include/google/protobuf/type.proto +193 -0
- include/google/protobuf/wrappers.proto +157 -0
- main.py +53 -56
- models/__init__.py +31 -24
- parsing/__init__.py +22 -0
- parsing/enum_parser.py +10 -2
- parsing/java_parser.py +302 -13
- pyproject.toml +1 -1
- reproto-0.1.3.dist-info/METADATA +209 -0
- {reproto-0.1.1.dist-info → reproto-0.1.3.dist-info}/RECORD +125 -31
- utils/__init__.py +40 -0
- utils/builtin_proto.py +269 -0
- utils/file_cache.py +8 -1
- utils/report_utils.py +71 -0
- utils/type_index.py +8 -1
- utils/type_utils.py +39 -6
- core/bytecode_parser.py +0 -274
- reproto-0.1.1.dist-info/METADATA +0 -295
- {reproto-0.1.1.dist-info → reproto-0.1.3.dist-info}/WHEEL +0 -0
- {reproto-0.1.1.dist-info → reproto-0.1.3.dist-info}/entry_points.txt +0 -0
core/reconstructor.py
CHANGED
@@ -15,14 +15,31 @@ from pathlib import Path
|
|
15
15
|
from collections import deque
|
16
16
|
from typing import Set, Dict, List, Optional, Tuple
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
from
|
22
|
-
from
|
23
|
-
from
|
24
|
-
from
|
25
|
-
from
|
18
|
+
# 智能导入:同时支持相对导入(包环境)和绝对导入(开发环境)
|
19
|
+
try:
|
20
|
+
# 相对导入(包环境)
|
21
|
+
from ..parsing.java_parser import JavaParser
|
22
|
+
from ..parsing.enum_parser import EnumParser
|
23
|
+
from .info_decoder import InfoDecoder
|
24
|
+
from ..generation.proto_generator import ProtoGenerator
|
25
|
+
from ..models.message_definition import MessageDefinition, EnumDefinition, EnumValueDefinition
|
26
|
+
from ..utils.logger import get_logger
|
27
|
+
from ..utils.file_cache import get_file_cache
|
28
|
+
from ..utils.type_utils import type_mapper, naming_converter, TypeMapper, NamingConverter
|
29
|
+
from ..utils.builtin_proto import get_builtin_manager
|
30
|
+
from ..utils.type_index import get_type_index
|
31
|
+
except ImportError:
|
32
|
+
# 绝对导入(开发环境)
|
33
|
+
from parsing.java_parser import JavaParser
|
34
|
+
from parsing.enum_parser import EnumParser
|
35
|
+
from core.info_decoder import InfoDecoder
|
36
|
+
from generation.proto_generator import ProtoGenerator
|
37
|
+
from models.message_definition import MessageDefinition, EnumDefinition, EnumValueDefinition
|
38
|
+
from utils.logger import get_logger
|
39
|
+
from utils.file_cache import get_file_cache
|
40
|
+
from utils.type_utils import type_mapper, naming_converter, TypeMapper, NamingConverter
|
41
|
+
from utils.builtin_proto import get_builtin_manager
|
42
|
+
from utils.type_index import get_type_index
|
26
43
|
|
27
44
|
|
28
45
|
class JavaSourceAnalyzer:
|
@@ -267,6 +284,43 @@ class JavaSourceAnalyzer:
|
|
267
284
|
|
268
285
|
return None
|
269
286
|
|
287
|
+
def _extract_constant_value(self, constant_name: str) -> Optional[int]:
|
288
|
+
"""
|
289
|
+
从Java源码中提取常量值
|
290
|
+
|
291
|
+
Args:
|
292
|
+
constant_name: 常量名(如 "SKIP_RECOVERY_FIELD_NUMBER")
|
293
|
+
|
294
|
+
Returns:
|
295
|
+
常量值,如果找不到则返回None
|
296
|
+
"""
|
297
|
+
if not self._current_class_content:
|
298
|
+
return None
|
299
|
+
|
300
|
+
# 查找常量声明模式
|
301
|
+
patterns = [
|
302
|
+
# public static final int SKIP_RECOVERY_FIELD_NUMBER = 5;
|
303
|
+
rf'public\s+static\s+final\s+int\s+{re.escape(constant_name)}\s*=\s*(\d+)\s*;',
|
304
|
+
# private static final int SKIP_RECOVERY_FIELD_NUMBER = 5;
|
305
|
+
rf'private\s+static\s+final\s+int\s+{re.escape(constant_name)}\s*=\s*(\d+)\s*;',
|
306
|
+
# static final int SKIP_RECOVERY_FIELD_NUMBER = 5;
|
307
|
+
rf'static\s+final\s+int\s+{re.escape(constant_name)}\s*=\s*(\d+)\s*;',
|
308
|
+
# final int SKIP_RECOVERY_FIELD_NUMBER = 5;
|
309
|
+
rf'final\s+int\s+{re.escape(constant_name)}\s*=\s*(\d+)\s*;',
|
310
|
+
# int SKIP_RECOVERY_FIELD_NUMBER = 5;
|
311
|
+
rf'int\s+{re.escape(constant_name)}\s*=\s*(\d+)\s*;',
|
312
|
+
]
|
313
|
+
|
314
|
+
for pattern in patterns:
|
315
|
+
matches = re.findall(pattern, self._current_class_content)
|
316
|
+
if matches:
|
317
|
+
try:
|
318
|
+
return int(matches[0])
|
319
|
+
except ValueError:
|
320
|
+
continue
|
321
|
+
|
322
|
+
return None
|
323
|
+
|
270
324
|
def _load_class_content(self, class_name: str) -> Optional[str]:
|
271
325
|
"""加载类的源码内容(使用缓存优化)"""
|
272
326
|
try:
|
@@ -321,6 +375,9 @@ class ProtoReconstructor:
|
|
321
375
|
self.java_source_analyzer = JavaSourceAnalyzer(sources_dir)
|
322
376
|
self.info_decoder.java_source_analyzer = self.java_source_analyzer
|
323
377
|
|
378
|
+
# 初始化内置proto管理器
|
379
|
+
self.builtin_manager = get_builtin_manager(output_dir=str(output_dir))
|
380
|
+
|
324
381
|
# 🚀 性能优化:移除未使用的索引系统,简化代码
|
325
382
|
# 索引系统在实际使用中被基础类型检测绕过,且构建耗时
|
326
383
|
# 改为使用直接的文件路径构造和智能包名推断
|
@@ -363,7 +420,6 @@ class ProtoReconstructor:
|
|
363
420
|
self._generate_all_proto_files()
|
364
421
|
|
365
422
|
# 5. 输出性能统计信息
|
366
|
-
from utils.file_cache import get_file_cache
|
367
423
|
file_cache = get_file_cache()
|
368
424
|
file_cache.print_stats()
|
369
425
|
|
@@ -460,7 +516,20 @@ class ProtoReconstructor:
|
|
460
516
|
return
|
461
517
|
|
462
518
|
# 3. 尝试解析为消息类
|
463
|
-
|
519
|
+
# 特殊处理:如果是内部类且找到的是外部类文件,需要从外部类中提取内部类信息
|
520
|
+
if '$' in class_name and java_file_path.stem != class_name.split('.')[-1]:
|
521
|
+
# 这是内部类,但找到的是外部类文件
|
522
|
+
inner_class_name = class_name.split('$')[-1] # 获取内部类名
|
523
|
+
info_string, objects_array = self.java_parser.parse_inner_class_from_file(
|
524
|
+
java_file_path, inner_class_name
|
525
|
+
)
|
526
|
+
# 为内部类创建虚拟的Java文件路径,用于字段标签提取
|
527
|
+
virtual_java_file_path = java_file_path.parent / f"{java_file_path.stem}${inner_class_name}.java"
|
528
|
+
else:
|
529
|
+
# 普通类或独立的内部类文件
|
530
|
+
info_string, objects_array = self.java_parser.parse_java_file(java_file_path)
|
531
|
+
virtual_java_file_path = java_file_path
|
532
|
+
|
464
533
|
if not info_string:
|
465
534
|
error_msg = "无法从Java文件中提取protobuf信息"
|
466
535
|
self.failed_classes[class_name] = error_msg
|
@@ -469,7 +538,7 @@ class ProtoReconstructor:
|
|
469
538
|
|
470
539
|
# 4. 解码字节码为消息定义
|
471
540
|
message_def = self.info_decoder.decode_message_info(
|
472
|
-
class_name, info_string, objects_array,
|
541
|
+
class_name, info_string, objects_array, virtual_java_file_path
|
473
542
|
)
|
474
543
|
|
475
544
|
if message_def:
|
@@ -478,6 +547,16 @@ class ProtoReconstructor:
|
|
478
547
|
|
479
548
|
# 5. 发现并添加依赖类到队列
|
480
549
|
self._discover_dependencies(message_def)
|
550
|
+
|
551
|
+
# 6. 处理InfoDecoder发现的依赖类(如oneof中的类引用)
|
552
|
+
discovered_deps = self.info_decoder.get_discovered_dependencies()
|
553
|
+
for dep_class in discovered_deps:
|
554
|
+
if dep_class not in self.processed_classes and dep_class not in self.pending_classes:
|
555
|
+
self.pending_classes.append(dep_class)
|
556
|
+
self.logger.info(f" 🔗 发现oneof依赖: {dep_class}")
|
557
|
+
|
558
|
+
# 清理InfoDecoder的依赖记录,为下次解析做准备
|
559
|
+
self.info_decoder.discovered_dependencies = []
|
481
560
|
else:
|
482
561
|
error_msg = "字节码解码失败,可能不是protobuf消息类"
|
483
562
|
self.failed_classes[class_name] = error_msg
|
@@ -503,11 +582,24 @@ class ProtoReconstructor:
|
|
503
582
|
message_def: 消息定义对象
|
504
583
|
"""
|
505
584
|
dependencies = self._extract_dependencies(message_def)
|
585
|
+
builtin_count = 0
|
586
|
+
|
506
587
|
for dep in dependencies:
|
507
|
-
|
588
|
+
# 检查是否为内置类型
|
589
|
+
if self.builtin_manager.is_builtin_type(dep):
|
590
|
+
# 处理内置类型依赖
|
591
|
+
if self.builtin_manager.ensure_builtin_proto_file(dep):
|
592
|
+
self.logger.info(f" 📦 处理内置依赖: {dep}")
|
593
|
+
builtin_count += 1
|
594
|
+
else:
|
595
|
+
self.logger.error(f" ❌ 内置依赖处理失败: {dep}")
|
596
|
+
elif dep not in self.processed_classes:
|
508
597
|
self.pending_classes.append(dep)
|
509
598
|
self.logger.info(f" 🔗 发现依赖: {dep}")
|
510
599
|
|
600
|
+
if builtin_count > 0:
|
601
|
+
self.logger.info(f" 📊 处理了 {builtin_count} 个内置依赖")
|
602
|
+
|
511
603
|
# 处理枚举依赖
|
512
604
|
self.logger.info(f" 🔍 开始处理枚举依赖...")
|
513
605
|
enum_count = 0
|
@@ -930,20 +1022,46 @@ class ProtoReconstructor:
|
|
930
1022
|
if full_path.exists():
|
931
1023
|
return full_path
|
932
1024
|
|
933
|
-
# 🚀 优化2
|
1025
|
+
# 🚀 优化2:处理内部类,正确的查找顺序
|
934
1026
|
if '$' in class_name:
|
935
|
-
# 内部类处理:com.example.Models$Inner -> com/example/Models.java
|
936
1027
|
last_dot_index = class_name.rfind('.')
|
937
1028
|
if last_dot_index != -1:
|
938
1029
|
package_path = class_name[:last_dot_index].replace('.', '/')
|
939
1030
|
class_part = class_name[last_dot_index + 1:]
|
940
1031
|
|
941
|
-
#
|
1032
|
+
# 方式1:优先查找主类文件 - 内部类通常定义在主类中
|
1033
|
+
# 如:com.example.Service$InnerClass -> 在 Service$CompleteOnboardingRequest.java 中查找
|
1034
|
+
# 这里需要找到包含这个内部类的主类文件
|
1035
|
+
outer_class_prefix = class_part.split('$')[0] # Service
|
1036
|
+
|
1037
|
+
# 在同一包下查找所有以外部类名开头的文件,并检查是否包含目标内部类
|
1038
|
+
inner_class_name = class_part.split('$')[-1] # 获取内部类名,如SkipRecovery
|
1039
|
+
package_dir = self.sources_dir / package_path
|
1040
|
+
if package_dir.exists():
|
1041
|
+
for java_file in package_dir.glob(f"{outer_class_prefix}$*.java"):
|
1042
|
+
self.logger.debug(f" 📁 检查主类文件: {java_file}")
|
1043
|
+
# 检查这个文件是否包含目标内部类
|
1044
|
+
if self._file_contains_inner_class(java_file, inner_class_name):
|
1045
|
+
self.logger.debug(f" ✅ 找到包含内部类 {inner_class_name} 的文件: {java_file}")
|
1046
|
+
return java_file
|
1047
|
+
|
1048
|
+
# 方式2:查找独立的内部类文件
|
1049
|
+
# 如:com.example.Service$InnerClass -> com/example/Service$InnerClass.java
|
1050
|
+
inner_class_file_path = f"{package_path}/{class_part}.java"
|
1051
|
+
inner_class_full_path = self.sources_dir / inner_class_file_path
|
1052
|
+
|
1053
|
+
if inner_class_full_path.exists():
|
1054
|
+
self.logger.debug(f" 📁 找到独立内部类文件: {inner_class_full_path}")
|
1055
|
+
return inner_class_full_path
|
1056
|
+
|
1057
|
+
# 方式3:传统风格 - 内部类在外部类文件中(外部类本身)
|
1058
|
+
# 如:com.example.Service$InnerClass -> com/example/Service.java
|
942
1059
|
outer_class = class_part.split('$')[0]
|
943
1060
|
outer_class_file_path = f"{package_path}/{outer_class}.java"
|
944
1061
|
outer_class_full_path = self.sources_dir / outer_class_file_path
|
945
1062
|
|
946
1063
|
if outer_class_full_path.exists():
|
1064
|
+
self.logger.debug(f" 📁 找到外部类文件: {outer_class_full_path}")
|
947
1065
|
return outer_class_full_path
|
948
1066
|
|
949
1067
|
# 🚀 优化3:简化文件查找逻辑,移除索引依赖
|
@@ -970,6 +1088,32 @@ class ProtoReconstructor:
|
|
970
1088
|
|
971
1089
|
return None
|
972
1090
|
|
1091
|
+
def _file_contains_inner_class(self, java_file_path: Path, inner_class_name: str) -> bool:
|
1092
|
+
"""
|
1093
|
+
检查Java文件是否包含指定的内部类定义
|
1094
|
+
|
1095
|
+
Args:
|
1096
|
+
java_file_path: Java文件路径
|
1097
|
+
inner_class_name: 内部类名(如"SkipRecovery")
|
1098
|
+
|
1099
|
+
Returns:
|
1100
|
+
是否包含该内部类
|
1101
|
+
"""
|
1102
|
+
try:
|
1103
|
+
content = java_file_path.read_text(encoding='utf-8')
|
1104
|
+
# 查找内部类定义
|
1105
|
+
class_pattern = rf'public\s+static\s+final\s+class\s+{re.escape(inner_class_name)}\s+extends\s+'
|
1106
|
+
if re.search(class_pattern, content):
|
1107
|
+
return True
|
1108
|
+
|
1109
|
+
# 尝试更宽松的匹配
|
1110
|
+
class_pattern = rf'class\s+{re.escape(inner_class_name)}\s+extends\s+'
|
1111
|
+
return re.search(class_pattern, content) is not None
|
1112
|
+
|
1113
|
+
except Exception as e:
|
1114
|
+
self.logger.debug(f" ❌ 检查文件 {java_file_path} 时出错: {e}")
|
1115
|
+
return False
|
1116
|
+
|
973
1117
|
def _infer_full_class_name(self, simple_name: str, current_package: str) -> Optional[str]:
|
974
1118
|
"""
|
975
1119
|
推断简单类名的完整包名(通用算法,适用于任何应用)
|
@@ -1140,8 +1284,6 @@ class ProtoReconstructor:
|
|
1140
1284
|
是否为基础字段类型
|
1141
1285
|
"""
|
1142
1286
|
# 🚀 性能优化:使用缓存的类型检查器,避免重复计算
|
1143
|
-
from utils.type_utils import TypeMapper
|
1144
|
-
|
1145
1287
|
# 直接使用统一的基础类型检查,无需额外逻辑
|
1146
1288
|
return TypeMapper.is_java_basic_type(type_name)
|
1147
1289
|
|
@@ -1224,8 +1366,6 @@ class ProtoReconstructor:
|
|
1224
1366
|
实际的完整类型名
|
1225
1367
|
"""
|
1226
1368
|
# 🚀 优化:使用统一的类型检查器
|
1227
|
-
from utils.type_utils import TypeMapper
|
1228
|
-
|
1229
1369
|
if TypeMapper.is_java_basic_type(inferred_type):
|
1230
1370
|
self.logger.debug(f" 跳过基础类型: {inferred_type}")
|
1231
1371
|
return None
|
@@ -1236,7 +1376,6 @@ class ProtoReconstructor:
|
|
1236
1376
|
|
1237
1377
|
try:
|
1238
1378
|
# 使用索引系统进行快速查找,避免文件IO
|
1239
|
-
from utils.type_index import get_type_index
|
1240
1379
|
type_index = get_type_index(self.sources_dir)
|
1241
1380
|
|
1242
1381
|
# 构造可能的完整类名
|
@@ -1394,5 +1533,4 @@ class ProtoReconstructor:
|
|
1394
1533
|
蛇形命名字符串
|
1395
1534
|
"""
|
1396
1535
|
# 🚀 优化:使用统一的命名转换器,避免重复实现
|
1397
|
-
from utils.type_utils import NamingConverter
|
1398
1536
|
return NamingConverter.to_snake_case(camel_str)
|
generation/__init__.py
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
"""
|
2
|
+
代码生成模块
|
3
|
+
|
4
|
+
负责将解析得到的消息定义转换为Protobuf .proto文件
|
5
|
+
"""
|
6
|
+
|
7
|
+
# 智能导入:同时支持相对导入(包环境)和绝对导入(开发环境)
|
8
|
+
try:
|
9
|
+
# 相对导入(包环境)
|
10
|
+
from .proto_generator import ProtoGenerator
|
11
|
+
except ImportError:
|
12
|
+
# 绝对导入(开发环境)
|
13
|
+
from generation.proto_generator import ProtoGenerator
|
14
|
+
|
15
|
+
__all__ = [
|
16
|
+
'ProtoGenerator'
|
17
|
+
]
|
1
18
|
|
2
19
|
|
3
20
|
|
generation/proto_generator.py
CHANGED
@@ -10,8 +10,17 @@ Author: AI Assistant
|
|
10
10
|
|
11
11
|
import re
|
12
12
|
from typing import Dict, Set, List, Union
|
13
|
-
|
14
|
-
|
13
|
+
|
14
|
+
# 智能导入:同时支持相对导入(包环境)和绝对导入(开发环境)
|
15
|
+
try:
|
16
|
+
# 相对导入(包环境)
|
17
|
+
from ..models.message_definition import MessageDefinition, FieldDefinition, EnumDefinition, EnumValueDefinition
|
18
|
+
from ..utils.type_utils import type_mapper, naming_converter, field_name_processor
|
19
|
+
from ..utils.logger import get_logger
|
20
|
+
except ImportError:
|
21
|
+
# 绝对导入(开发环境)
|
22
|
+
from models.message_definition import MessageDefinition, FieldDefinition, EnumDefinition, EnumValueDefinition
|
23
|
+
from utils.type_utils import type_mapper, naming_converter, field_name_processor
|
15
24
|
|
16
25
|
|
17
26
|
class ProtoGenerator:
|
@@ -70,7 +79,6 @@ class ProtoGenerator:
|
|
70
79
|
return '\n'.join(lines)
|
71
80
|
|
72
81
|
except Exception as e:
|
73
|
-
from utils.logger import get_logger
|
74
82
|
logger = get_logger("proto_generator")
|
75
83
|
logger.error(f"❌ 生成proto文件失败 {message_def.name if message_def else 'Unknown'}: {e}")
|
76
84
|
raise
|
@@ -118,7 +126,6 @@ class ProtoGenerator:
|
|
118
126
|
return '\n'.join(lines)
|
119
127
|
|
120
128
|
except Exception as e:
|
121
|
-
from utils.logger import get_logger
|
122
129
|
logger = get_logger("proto_generator")
|
123
130
|
logger.error(f"❌ 生成枚举proto文件失败 {enum_def.name if enum_def else 'Unknown'}: {e}")
|
124
131
|
raise
|
@@ -190,7 +197,16 @@ class ProtoGenerator:
|
|
190
197
|
Returns:
|
191
198
|
消息定义的行列表
|
192
199
|
"""
|
193
|
-
|
200
|
+
# 清理消息名称中的$符号
|
201
|
+
clean_name = naming_converter.clean_proto_name(message_def.name)
|
202
|
+
|
203
|
+
lines = []
|
204
|
+
|
205
|
+
# 如果是内部类、匿名类或包含$的特殊类,添加原始Java类名注释
|
206
|
+
if message_def.full_name and ('$' in message_def.full_name or 'Anonymous' in message_def.full_name):
|
207
|
+
lines.append(f'// 原始Java类: {message_def.full_name}')
|
208
|
+
|
209
|
+
lines.append(f'message {clean_name} {{')
|
194
210
|
|
195
211
|
# 生成oneof字段(oneof字段内部也按tag排序)
|
196
212
|
for oneof in message_def.oneofs:
|
@@ -214,7 +230,16 @@ class ProtoGenerator:
|
|
214
230
|
Returns:
|
215
231
|
枚举定义的行列表
|
216
232
|
"""
|
217
|
-
|
233
|
+
# 清理枚举名称中的$符号
|
234
|
+
clean_name = naming_converter.clean_proto_name(enum_def.name)
|
235
|
+
|
236
|
+
lines = []
|
237
|
+
|
238
|
+
# 如果是内部类、匿名类或包含$的特殊enum,添加原始Java类名注释
|
239
|
+
if enum_def.full_name and ('$' in enum_def.full_name or 'Anonymous' in enum_def.full_name):
|
240
|
+
lines.append(f'// 原始Java类: {enum_def.full_name}')
|
241
|
+
|
242
|
+
lines.append(f'enum {clean_name} {{')
|
218
243
|
|
219
244
|
# 生成枚举值(按value排序)
|
220
245
|
sorted_values = sorted(enum_def.values, key=lambda enum_value: enum_value.value)
|
@@ -272,7 +297,6 @@ class ProtoGenerator:
|
|
272
297
|
return f' {field_type} {field.name} = {field.tag};'
|
273
298
|
|
274
299
|
except Exception as e:
|
275
|
-
from utils.logger import get_logger
|
276
300
|
logger = get_logger("proto_generator")
|
277
301
|
logger.error(f"❌ 生成字段定义失败 {field.name if field else 'Unknown'}: {e}")
|
278
302
|
raise
|
@@ -335,6 +359,16 @@ class ProtoGenerator:
|
|
335
359
|
if field.type_name in basic_proto_types:
|
336
360
|
return None
|
337
361
|
|
362
|
+
# 检查是否为Google Protobuf内置类型
|
363
|
+
try:
|
364
|
+
from utils.builtin_proto import get_builtin_manager
|
365
|
+
builtin_manager = get_builtin_manager()
|
366
|
+
if builtin_manager.is_builtin_type(field.type_name):
|
367
|
+
return builtin_manager.get_import_path(field.type_name)
|
368
|
+
except (ImportError, ValueError):
|
369
|
+
# 如果内置管理器不可用,继续使用原有逻辑
|
370
|
+
pass
|
371
|
+
|
338
372
|
# 处理map类型:map<string, Contact> -> 提取值类型Contact
|
339
373
|
if field.type_name.startswith('map<'):
|
340
374
|
# 解析map类型:map<key_type, value_type>
|
@@ -366,6 +400,10 @@ class ProtoGenerator:
|
|
366
400
|
# 生成枚举文件的导入路径
|
367
401
|
return self._class_name_to_import_path(enum_full_name)
|
368
402
|
|
403
|
+
# 优先使用字段定义中保存的完整类名信息(用于内部类等特殊情况)
|
404
|
+
if hasattr(field, 'full_class_name') and field.full_class_name:
|
405
|
+
return self._class_name_to_import_path(field.full_class_name)
|
406
|
+
|
369
407
|
# 解析完整类名(消息类型)
|
370
408
|
full_class_name = self._resolve_full_class_name(field.type_name, current_package, all_messages)
|
371
409
|
if full_class_name:
|
@@ -418,9 +456,12 @@ class ProtoGenerator:
|
|
418
456
|
Returns:
|
419
457
|
proto导入路径
|
420
458
|
"""
|
421
|
-
# com.example.
|
459
|
+
# com.example.Service$SkipRecovery -> com/example/service_skip_recovery.proto
|
460
|
+
# 注意:这里要使用完整的类名(包含$符号)来生成文件名,与实际文件名保持一致
|
422
461
|
parts = class_name.split('.')
|
423
|
-
|
462
|
+
# 使用完整的类名部分(可能包含$)来生成proto文件名
|
463
|
+
class_part = parts[-1]
|
464
|
+
proto_name = self._to_snake_case(class_part) + '.proto'
|
424
465
|
package_path = '/'.join(parts[:-1])
|
425
466
|
return f"{package_path}/{proto_name}"
|
426
467
|
|
@@ -464,20 +505,25 @@ class ProtoGenerator:
|
|
464
505
|
if field.type_name == 'message':
|
465
506
|
return self._generate_message_type_name(field.name)
|
466
507
|
|
467
|
-
#
|
508
|
+
# 检查是否为枚举类型,如果是则使用清理后的枚举名
|
468
509
|
if all_enums:
|
469
510
|
for enum_full_name, enum_def in all_enums.items():
|
470
511
|
enum_class_name = enum_full_name.split('.')[-1] # 获取类名部分
|
471
512
|
if field.type_name == enum_class_name:
|
472
|
-
#
|
473
|
-
return enum_def.name
|
513
|
+
# 使用清理后的枚举名,去掉$符号
|
514
|
+
return naming_converter.clean_proto_name(enum_def.name)
|
474
515
|
|
475
|
-
#
|
516
|
+
# 已知的具体类型名,处理Google Protobuf类型和普通类型
|
476
517
|
if '.' in field.type_name:
|
477
|
-
#
|
478
|
-
|
518
|
+
# 如果是Google Protobuf类型,保持完整的类型名
|
519
|
+
if field.type_name.startswith('google.protobuf.'):
|
520
|
+
return field.type_name
|
521
|
+
# 其他类型提取简单类型名,并清理$符号
|
522
|
+
simple_name = field.type_name.split('.')[-1]
|
523
|
+
return naming_converter.clean_proto_name(simple_name)
|
479
524
|
else:
|
480
|
-
|
525
|
+
# 清理简单类型名中的$符号
|
526
|
+
return naming_converter.clean_proto_name(field.type_name)
|
481
527
|
|
482
528
|
def _get_basic_proto_type(self, type_name: str) -> str:
|
483
529
|
"""
|
@@ -0,0 +1,162 @@
|
|
1
|
+
// Protocol Buffers - Google's data interchange format
|
2
|
+
// Copyright 2008 Google Inc. All rights reserved.
|
3
|
+
// https://developers.google.com/protocol-buffers/
|
4
|
+
//
|
5
|
+
// Redistribution and use in source and binary forms, with or without
|
6
|
+
// modification, are permitted provided that the following conditions are
|
7
|
+
// met:
|
8
|
+
//
|
9
|
+
// * Redistributions of source code must retain the above copyright
|
10
|
+
// notice, this list of conditions and the following disclaimer.
|
11
|
+
// * Redistributions in binary form must reproduce the above
|
12
|
+
// copyright notice, this list of conditions and the following disclaimer
|
13
|
+
// in the documentation and/or other materials provided with the
|
14
|
+
// distribution.
|
15
|
+
// * Neither the name of Google Inc. nor the names of its
|
16
|
+
// contributors may be used to endorse or promote products derived from
|
17
|
+
// this software without specific prior written permission.
|
18
|
+
//
|
19
|
+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
20
|
+
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
21
|
+
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
22
|
+
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
23
|
+
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
24
|
+
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
25
|
+
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
26
|
+
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
27
|
+
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
28
|
+
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
29
|
+
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
30
|
+
|
31
|
+
syntax = "proto3";
|
32
|
+
|
33
|
+
package google.protobuf;
|
34
|
+
|
35
|
+
option go_package = "google.golang.org/protobuf/types/known/anypb";
|
36
|
+
option java_package = "com.google.protobuf";
|
37
|
+
option java_outer_classname = "AnyProto";
|
38
|
+
option java_multiple_files = true;
|
39
|
+
option objc_class_prefix = "GPB";
|
40
|
+
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
|
41
|
+
|
42
|
+
// `Any` contains an arbitrary serialized protocol buffer message along with a
|
43
|
+
// URL that describes the type of the serialized message.
|
44
|
+
//
|
45
|
+
// Protobuf library provides support to pack/unpack Any values in the form
|
46
|
+
// of utility functions or additional generated methods of the Any type.
|
47
|
+
//
|
48
|
+
// Example 1: Pack and unpack a message in C++.
|
49
|
+
//
|
50
|
+
// Foo foo = ...;
|
51
|
+
// Any any;
|
52
|
+
// any.PackFrom(foo);
|
53
|
+
// ...
|
54
|
+
// if (any.UnpackTo(&foo)) {
|
55
|
+
// ...
|
56
|
+
// }
|
57
|
+
//
|
58
|
+
// Example 2: Pack and unpack a message in Java.
|
59
|
+
//
|
60
|
+
// Foo foo = ...;
|
61
|
+
// Any any = Any.pack(foo);
|
62
|
+
// ...
|
63
|
+
// if (any.is(Foo.class)) {
|
64
|
+
// foo = any.unpack(Foo.class);
|
65
|
+
// }
|
66
|
+
// // or ...
|
67
|
+
// if (any.isSameTypeAs(Foo.getDefaultInstance())) {
|
68
|
+
// foo = any.unpack(Foo.getDefaultInstance());
|
69
|
+
// }
|
70
|
+
//
|
71
|
+
// Example 3: Pack and unpack a message in Python.
|
72
|
+
//
|
73
|
+
// foo = Foo(...)
|
74
|
+
// any = Any()
|
75
|
+
// any.Pack(foo)
|
76
|
+
// ...
|
77
|
+
// if any.Is(Foo.DESCRIPTOR):
|
78
|
+
// any.Unpack(foo)
|
79
|
+
// ...
|
80
|
+
//
|
81
|
+
// Example 4: Pack and unpack a message in Go
|
82
|
+
//
|
83
|
+
// foo := &pb.Foo{...}
|
84
|
+
// any, err := anypb.New(foo)
|
85
|
+
// if err != nil {
|
86
|
+
// ...
|
87
|
+
// }
|
88
|
+
// ...
|
89
|
+
// foo := &pb.Foo{}
|
90
|
+
// if err := any.UnmarshalTo(foo); err != nil {
|
91
|
+
// ...
|
92
|
+
// }
|
93
|
+
//
|
94
|
+
// The pack methods provided by protobuf library will by default use
|
95
|
+
// 'type.googleapis.com/full.type.name' as the type URL and the unpack
|
96
|
+
// methods only use the fully qualified type name after the last '/'
|
97
|
+
// in the type URL, for example "foo.bar.com/x/y.z" will yield type
|
98
|
+
// name "y.z".
|
99
|
+
//
|
100
|
+
// JSON
|
101
|
+
// ====
|
102
|
+
// The JSON representation of an `Any` value uses the regular
|
103
|
+
// representation of the deserialized, embedded message, with an
|
104
|
+
// additional field `@type` which contains the type URL. Example:
|
105
|
+
//
|
106
|
+
// package google.profile;
|
107
|
+
// message Person {
|
108
|
+
// string first_name = 1;
|
109
|
+
// string last_name = 2;
|
110
|
+
// }
|
111
|
+
//
|
112
|
+
// {
|
113
|
+
// "@type": "type.googleapis.com/google.profile.Person",
|
114
|
+
// "firstName": <string>,
|
115
|
+
// "lastName": <string>
|
116
|
+
// }
|
117
|
+
//
|
118
|
+
// If the embedded message type is well-known and has a custom JSON
|
119
|
+
// representation, that representation will be embedded adding a field
|
120
|
+
// `value` which holds the custom JSON in addition to the `@type`
|
121
|
+
// field. Example (for message [google.protobuf.Duration][]):
|
122
|
+
//
|
123
|
+
// {
|
124
|
+
// "@type": "type.googleapis.com/google.protobuf.Duration",
|
125
|
+
// "value": "1.212s"
|
126
|
+
// }
|
127
|
+
//
|
128
|
+
message Any {
|
129
|
+
// A URL/resource name that uniquely identifies the type of the serialized
|
130
|
+
// protocol buffer message. This string must contain at least
|
131
|
+
// one "/" character. The last segment of the URL's path must represent
|
132
|
+
// the fully qualified name of the type (as in
|
133
|
+
// `path/google.protobuf.Duration`). The name should be in a canonical form
|
134
|
+
// (e.g., leading "." is not accepted).
|
135
|
+
//
|
136
|
+
// In practice, teams usually precompile into the binary all types that they
|
137
|
+
// expect it to use in the context of Any. However, for URLs which use the
|
138
|
+
// scheme `http`, `https`, or no scheme, one can optionally set up a type
|
139
|
+
// server that maps type URLs to message definitions as follows:
|
140
|
+
//
|
141
|
+
// * If no scheme is provided, `https` is assumed.
|
142
|
+
// * An HTTP GET on the URL must yield a [google.protobuf.Type][]
|
143
|
+
// value in binary format, or produce an error.
|
144
|
+
// * Applications are allowed to cache lookup results based on the
|
145
|
+
// URL, or have them precompiled into a binary to avoid any
|
146
|
+
// lookup. Therefore, binary compatibility needs to be preserved
|
147
|
+
// on changes to types. (Use versioned type names to manage
|
148
|
+
// breaking changes.)
|
149
|
+
//
|
150
|
+
// Note: this functionality is not currently available in the official
|
151
|
+
// protobuf release, and it is not used for type URLs beginning with
|
152
|
+
// type.googleapis.com. As of May 2023, there are no widely used type server
|
153
|
+
// implementations and no plans to implement one.
|
154
|
+
//
|
155
|
+
// Schemes other than `http`, `https` (or the empty scheme) might be
|
156
|
+
// used with implementation specific semantics.
|
157
|
+
//
|
158
|
+
string type_url = 1;
|
159
|
+
|
160
|
+
// Must be a valid serialized protocol buffer of the above specified type.
|
161
|
+
bytes value = 2;
|
162
|
+
}
|