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.
Files changed (127) hide show
  1. .git/COMMIT_EDITMSG +29 -1
  2. .git/index +0 -0
  3. .git/logs/HEAD +4 -0
  4. .git/logs/refs/heads/iyue +4 -0
  5. .git/logs/refs/remotes/gitlab/iyue +4 -0
  6. .git/logs/refs/remotes/origin/iyue +4 -0
  7. .git/objects/00/81a7e1ec3251cc192ed2b73d5be897593fb872 +0 -0
  8. .git/objects/00/885cebf557ff261574deb93cb96449d06db01c +0 -0
  9. .git/objects/02/33f5b60263e5dc6d041c0b223a0724eb650faa +0 -0
  10. .git/objects/03/3fab23ccc74b707a31f6f37dc5df42c100aac0 +0 -0
  11. .git/objects/07/557fbd1d149ce51af8e98e8ca8590757a89dfa +0 -0
  12. .git/objects/07/b71df33935c14ddf99e3480c04eca4685028bc +0 -0
  13. .git/objects/0b/e20ea749c6ae1075ae98f9426e00b0a4a235af +0 -0
  14. .git/objects/12/f9cc4ad0b4f0af4f7bae379f281b2cebe7cc7f +0 -0
  15. .git/objects/13/5f50fea51c72f977f48d06a7584aba0f61e260 +0 -0
  16. .git/objects/1b/97584ebada3e93d7a2cfa4a1aae0b79c414d20 +0 -0
  17. .git/objects/1b/f0c1ad9586578e8332d061f7648dcb041ec063 +0 -0
  18. .git/objects/1e/46816d16e7c9db7575f1964403c3daa105be5c +0 -0
  19. .git/objects/24/d78e796570a8572a03bc1dd26608a7cfb506f8 +2 -0
  20. .git/objects/27/24208ca2442e8ab9b453d0eb230fa41243b270 +0 -0
  21. .git/objects/2a/fe93d8bcbeab9e136d8b6766604c32b3610314 +0 -0
  22. .git/objects/2d/2b812ca27c477f9e1b2c2706a5eb795ffcf8eb +0 -0
  23. .git/objects/2d/8a0da260a710010ae62be134ac1cea6ceecfd1 +0 -0
  24. .git/objects/2d/e8ecbb5ab5de1a032bef3f4606ce5fa7c6c4e8 +0 -0
  25. .git/objects/30/9347e5681d80bd3c7949882e27090dd9070d16 +0 -0
  26. .git/objects/33/633c9df669ff8cf38638717937a54990814268 +0 -0
  27. .git/objects/39/993e3600bf4ab82aa361b738ee97a108787450 +0 -0
  28. .git/objects/3b/ab663710fd6b43d9372313fced9043c4cb07dd +0 -0
  29. .git/objects/3e/b3f1273caf6814dfa69325ccbd9fd1340cf20a +0 -0
  30. .git/objects/3f/b8830f516342a0ae1cb7c34b65016827cb9570 +4 -0
  31. .git/objects/41/f40c22247de377be99e30784229f3f128508a2 +0 -0
  32. .git/objects/45/fbc774dedb61c7c205ea732f59a8dca8d13555 +3 -0
  33. .git/objects/48/cb11e75518a53be14146018214110986fade67 +0 -0
  34. .git/objects/4f/c6dc41f9c0a1a8e0eedd3ba49c43d78d0dbaba +0 -0
  35. .git/objects/59/4c23f158ccbd0a4288f9ea046d06160195afbf +0 -0
  36. .git/objects/5b/d4d87753b79e9157817f0c2e6964a731052854 +0 -0
  37. .git/objects/60/5cb6fd6a9f8894ad4d43a9b8e4785c1b3b0e17 +1 -0
  38. .git/objects/60/f61a0ea50091eac8d344c86597375cbdfc2785 +0 -0
  39. .git/objects/63/ddda2a403efaab3f4c6597b3a73a7b1147adb5 +0 -0
  40. .git/objects/65/0189fe083bd711e45d463b229a72be619abad2 +0 -0
  41. .git/objects/66/663db35bfec8ef5f1a5b1c840fde1bb62a0eb8 +0 -0
  42. .git/objects/66/6c5c5fc30435228116fa08c9d821bebaaa8926 +0 -0
  43. .git/objects/7c/ef0adfb28fd774bc78061c6f088e1ef9b050f6 +0 -0
  44. .git/objects/7d/dc129188a10c68ab756ef2cacb292c76920403 +0 -0
  45. .git/objects/80/17038e0f7818a44a742f77c86f4f88ed768fcd +0 -0
  46. .git/objects/87/c7db6c91c17a2df84b56d30bd24a0f6b9dbdd9 +0 -0
  47. .git/objects/8a/0ed0ed8886fbc823e8d2258fa6d18699e94e25 +0 -0
  48. .git/objects/8d/d857b3d0ab3f5cd2e9173d532ef86e30df8eda +0 -0
  49. .git/objects/92/984cf67b2c25d435468a8218daa26ba0466054 +0 -0
  50. .git/objects/93/140b54b1fb9116ee214afee8abf2c72a232487 +0 -0
  51. .git/objects/93/68f2c32f83054ab072b7c9686d8baa0bad7f12 +4 -0
  52. .git/objects/9a/5ad062be9f6e001f4237a598a08981aba731e6 +0 -0
  53. .git/objects/9e/23448ac58f907d9d123c32bdccedbb3d6741b5 +0 -0
  54. .git/objects/a0/d192999af7e2cbfa6a9ccd04d720a04e5a06d5 +0 -0
  55. .git/objects/a1/655e0cb323c300562f97dcc67d5a446908c8ec +0 -0
  56. .git/objects/a5/38cc82cef7c49500d3522220f0f60a9ebc1ae6 +0 -0
  57. .git/objects/a9/41063a7ce89c353fa24378ec7c3f12f08f9df8 +0 -0
  58. .git/objects/a9/cc7923c34a4c97c5711d6309672f41d46c612a +0 -0
  59. .git/objects/ac/5c983d949d8c928bb022badf801e45e75e785e +0 -0
  60. .git/objects/af/c9cc15629847447063e86a82b8b56abb4fc08f +0 -0
  61. .git/objects/b0/82ca2c1b5a03edff25da3c2b2b573d049877e9 +0 -0
  62. .git/objects/b1/db1c131cf32916028342c0037ce8eb57a8eb26 +0 -0
  63. .git/objects/b2/8334b94392b8af397a05ed702690fa6c9ab1ca +0 -0
  64. .git/objects/b8/7c89dcfce9e244ff5ef6a4bd394de12e8c8092 +0 -0
  65. .git/objects/bc/e98bdb71c8681acb460195fdcbbe5d36290976 +0 -0
  66. .git/objects/c1/87d5e047eca86cfd8d444be2987aaa3f62c4d6 +0 -0
  67. .git/objects/c4/c2da96b0bb8db2acb0e6615cf340c7e51af26b +0 -0
  68. .git/objects/c5/13a96e7584636b20b12280c029750d5bc3da1e +0 -0
  69. .git/objects/c7/c34283697bd3cce07db53953eda25ee7cc371e +0 -0
  70. .git/objects/c9/d60d922a04b87587cd67b0abf9fe5a7b7b76cd +0 -0
  71. .git/objects/d2/69b1676dbf32f76a7c405d0b4ea6a70ac3a626 +0 -0
  72. .git/objects/d3/5a918b1d9125ad35d60e08b181323df3246f1a +0 -0
  73. .git/objects/d8/eaf86669fbfd10497570c1784db1ed2696b588 +0 -0
  74. .git/objects/d9/3bd435c8c7ad4efb83dff04d5450fabb9e3faf +0 -0
  75. .git/objects/d9/90e6d553577d37ebce8b28b3015ecbde038b42 +0 -0
  76. .git/objects/da/13cc15bcd8ee39c81f36dee7f179a569ecab0b +0 -0
  77. .git/objects/e3/27755808d88c7ae5c06c229cf18bd0519646df +0 -0
  78. .git/objects/e4/4c1d8a90207ac082d8ab7ff0db66708e2ebc31 +0 -0
  79. .git/objects/e5/83e7c40be934d16a1fa2e973487b395d930f42 +0 -0
  80. .git/objects/ed/1ae867d5e63195845afc58d88c38ecbdea97df +0 -0
  81. .git/objects/ef/f44e5099da27f7fb1ef14bb34902ccf4250b89 +0 -0
  82. .git/objects/f5/1be495b96272fa2e47f30071aed35ac1f0dd2c +0 -0
  83. .git/objects/f8/ed595d25bd9d500e765a792c513878f7ddb1f7 +0 -0
  84. .git/objects/fd/0bc07dc3c95e6168ab6d367d9eca139ac1e539 +0 -0
  85. .git/refs/heads/iyue +1 -1
  86. .git/refs/remotes/gitlab/iyue +1 -1
  87. .git/refs/remotes/origin/iyue +1 -1
  88. .gitignore +2 -1
  89. README.md +104 -190
  90. core/__init__.py +23 -0
  91. core/info_decoder.py +520 -10
  92. core/reconstructor.py +159 -21
  93. generation/__init__.py +17 -0
  94. generation/proto_generator.py +62 -16
  95. include/google/protobuf/any.proto +162 -0
  96. include/google/protobuf/api.proto +207 -0
  97. include/google/protobuf/compiler/plugin.proto +180 -0
  98. include/google/protobuf/cpp_features.proto +67 -0
  99. include/google/protobuf/descriptor.proto +1417 -0
  100. include/google/protobuf/duration.proto +115 -0
  101. include/google/protobuf/empty.proto +51 -0
  102. include/google/protobuf/field_mask.proto +245 -0
  103. include/google/protobuf/go_features.proto +80 -0
  104. include/google/protobuf/java_features.proto +130 -0
  105. include/google/protobuf/source_context.proto +48 -0
  106. include/google/protobuf/struct.proto +95 -0
  107. include/google/protobuf/timestamp.proto +144 -0
  108. include/google/protobuf/type.proto +193 -0
  109. include/google/protobuf/wrappers.proto +157 -0
  110. main.py +53 -56
  111. models/__init__.py +31 -24
  112. parsing/__init__.py +22 -0
  113. parsing/enum_parser.py +10 -2
  114. parsing/java_parser.py +302 -13
  115. pyproject.toml +1 -1
  116. reproto-0.1.3.dist-info/METADATA +209 -0
  117. {reproto-0.1.1.dist-info → reproto-0.1.3.dist-info}/RECORD +125 -31
  118. utils/__init__.py +40 -0
  119. utils/builtin_proto.py +269 -0
  120. utils/file_cache.py +8 -1
  121. utils/report_utils.py +71 -0
  122. utils/type_index.py +8 -1
  123. utils/type_utils.py +39 -6
  124. core/bytecode_parser.py +0 -274
  125. reproto-0.1.1.dist-info/METADATA +0 -295
  126. {reproto-0.1.1.dist-info → reproto-0.1.3.dist-info}/WHEEL +0 -0
  127. {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
- from parsing.java_parser import JavaParser
19
- from parsing.enum_parser import EnumParser
20
- from core.info_decoder import InfoDecoder
21
- from generation.proto_generator import ProtoGenerator
22
- from models.message_definition import MessageDefinition, EnumDefinition, EnumValueDefinition
23
- from utils.logger import get_logger
24
- from utils.file_cache import get_file_cache
25
- from utils.type_utils import type_mapper, naming_converter
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
- info_string, objects_array = self.java_parser.parse_java_file(java_file_path)
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, java_file_path
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
- if dep not in self.processed_classes:
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
 
@@ -10,8 +10,17 @@ Author: AI Assistant
10
10
 
11
11
  import re
12
12
  from typing import Dict, Set, List, Union
13
- from models.message_definition import MessageDefinition, FieldDefinition, EnumDefinition, EnumValueDefinition
14
- from utils.type_utils import type_mapper, naming_converter, field_name_processor
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
- lines = [f'message {message_def.name} {{']
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
- lines = [f'enum {enum_def.name} {{']
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.Model -> com/example/model.proto
459
+ # com.example.Service$SkipRecovery -> com/example/service_skip_recovery.proto
460
+ # 注意:这里要使用完整的类名(包含$符号)来生成文件名,与实际文件名保持一致
422
461
  parts = class_name.split('.')
423
- proto_name = self._to_snake_case(parts[-1]) + '.proto'
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
- return field.type_name.split('.')[-1]
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
- return field.type_name
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
+ }