tree-sitter-analyzer 0.1.3__py3-none-any.whl → 0.3.0__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.

Potentially problematic release.


This version of tree-sitter-analyzer might be problematic. Click here for more details.

Files changed (79) hide show
  1. tree_sitter_analyzer/__init__.py +133 -121
  2. tree_sitter_analyzer/__main__.py +11 -12
  3. tree_sitter_analyzer/api.py +531 -539
  4. tree_sitter_analyzer/cli/__init__.py +39 -39
  5. tree_sitter_analyzer/cli/__main__.py +12 -13
  6. tree_sitter_analyzer/cli/commands/__init__.py +26 -27
  7. tree_sitter_analyzer/cli/commands/advanced_command.py +88 -88
  8. tree_sitter_analyzer/cli/commands/base_command.py +160 -155
  9. tree_sitter_analyzer/cli/commands/default_command.py +18 -19
  10. tree_sitter_analyzer/cli/commands/partial_read_command.py +141 -133
  11. tree_sitter_analyzer/cli/commands/query_command.py +81 -82
  12. tree_sitter_analyzer/cli/commands/structure_command.py +138 -121
  13. tree_sitter_analyzer/cli/commands/summary_command.py +101 -93
  14. tree_sitter_analyzer/cli/commands/table_command.py +232 -233
  15. tree_sitter_analyzer/cli/info_commands.py +120 -121
  16. tree_sitter_analyzer/cli_main.py +277 -276
  17. tree_sitter_analyzer/core/__init__.py +15 -20
  18. tree_sitter_analyzer/core/analysis_engine.py +591 -574
  19. tree_sitter_analyzer/core/cache_service.py +320 -330
  20. tree_sitter_analyzer/core/engine.py +557 -560
  21. tree_sitter_analyzer/core/parser.py +293 -288
  22. tree_sitter_analyzer/core/query.py +494 -502
  23. tree_sitter_analyzer/encoding_utils.py +458 -460
  24. tree_sitter_analyzer/exceptions.py +337 -340
  25. tree_sitter_analyzer/file_handler.py +217 -222
  26. tree_sitter_analyzer/formatters/__init__.py +1 -1
  27. tree_sitter_analyzer/formatters/base_formatter.py +167 -168
  28. tree_sitter_analyzer/formatters/formatter_factory.py +78 -74
  29. tree_sitter_analyzer/formatters/java_formatter.py +287 -270
  30. tree_sitter_analyzer/formatters/python_formatter.py +255 -235
  31. tree_sitter_analyzer/interfaces/__init__.py +9 -10
  32. tree_sitter_analyzer/interfaces/cli.py +528 -557
  33. tree_sitter_analyzer/interfaces/cli_adapter.py +322 -319
  34. tree_sitter_analyzer/interfaces/mcp_adapter.py +180 -170
  35. tree_sitter_analyzer/interfaces/mcp_server.py +405 -416
  36. tree_sitter_analyzer/java_analyzer.py +218 -219
  37. tree_sitter_analyzer/language_detector.py +398 -400
  38. tree_sitter_analyzer/language_loader.py +224 -228
  39. tree_sitter_analyzer/languages/__init__.py +10 -11
  40. tree_sitter_analyzer/languages/java_plugin.py +1129 -1113
  41. tree_sitter_analyzer/languages/python_plugin.py +737 -712
  42. tree_sitter_analyzer/mcp/__init__.py +31 -32
  43. tree_sitter_analyzer/mcp/resources/__init__.py +44 -47
  44. tree_sitter_analyzer/mcp/resources/code_file_resource.py +212 -213
  45. tree_sitter_analyzer/mcp/resources/project_stats_resource.py +560 -550
  46. tree_sitter_analyzer/mcp/server.py +333 -345
  47. tree_sitter_analyzer/mcp/tools/__init__.py +30 -31
  48. tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +621 -557
  49. tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +242 -245
  50. tree_sitter_analyzer/mcp/tools/base_tool.py +54 -55
  51. tree_sitter_analyzer/mcp/tools/read_partial_tool.py +300 -302
  52. tree_sitter_analyzer/mcp/tools/table_format_tool.py +362 -359
  53. tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +543 -476
  54. tree_sitter_analyzer/mcp/utils/__init__.py +105 -106
  55. tree_sitter_analyzer/mcp/utils/error_handler.py +549 -549
  56. tree_sitter_analyzer/models.py +470 -481
  57. tree_sitter_analyzer/output_manager.py +261 -264
  58. tree_sitter_analyzer/plugins/__init__.py +333 -334
  59. tree_sitter_analyzer/plugins/base.py +477 -446
  60. tree_sitter_analyzer/plugins/java_plugin.py +608 -625
  61. tree_sitter_analyzer/plugins/javascript_plugin.py +446 -439
  62. tree_sitter_analyzer/plugins/manager.py +362 -355
  63. tree_sitter_analyzer/plugins/plugin_loader.py +85 -83
  64. tree_sitter_analyzer/plugins/python_plugin.py +606 -598
  65. tree_sitter_analyzer/plugins/registry.py +374 -366
  66. tree_sitter_analyzer/queries/__init__.py +26 -27
  67. tree_sitter_analyzer/queries/java.py +391 -394
  68. tree_sitter_analyzer/queries/javascript.py +148 -149
  69. tree_sitter_analyzer/queries/python.py +285 -286
  70. tree_sitter_analyzer/queries/typescript.py +229 -230
  71. tree_sitter_analyzer/query_loader.py +254 -260
  72. tree_sitter_analyzer/table_formatter.py +468 -448
  73. tree_sitter_analyzer/utils.py +277 -277
  74. tree_sitter_analyzer-0.3.0.dist-info/METADATA +346 -0
  75. tree_sitter_analyzer-0.3.0.dist-info/RECORD +77 -0
  76. tree_sitter_analyzer-0.1.3.dist-info/METADATA +0 -444
  77. tree_sitter_analyzer-0.1.3.dist-info/RECORD +0 -77
  78. {tree_sitter_analyzer-0.1.3.dist-info → tree_sitter_analyzer-0.3.0.dist-info}/WHEEL +0 -0
  79. {tree_sitter_analyzer-0.1.3.dist-info → tree_sitter_analyzer-0.3.0.dist-info}/entry_points.txt +0 -0
@@ -1,625 +1,608 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- Java Language Plugin
5
-
6
- Provides Java-specific parsing and element extraction functionality.
7
- """
8
-
9
- import re
10
- from typing import TYPE_CHECKING, List, Optional
11
-
12
- if TYPE_CHECKING:
13
- import tree_sitter
14
- from ..core.analysis_engine import AnalysisRequest
15
- from ..models import AnalysisResult
16
-
17
- from ..language_loader import loader
18
- from ..models import (
19
- Class,
20
- Function,
21
- Import,
22
- JavaAnnotation,
23
- JavaClass,
24
- JavaField,
25
- JavaImport,
26
- JavaMethod,
27
- Variable,
28
- )
29
- from ..utils import log_debug, log_error, log_warning
30
- from . import ElementExtractor, LanguagePlugin
31
-
32
-
33
- class JavaElementExtractor(ElementExtractor):
34
- """Advanced Java-specific element extractor with comprehensive analysis"""
35
-
36
- def __init__(self) -> None:
37
- # 分析コンテキスト
38
- self.current_package: str = ""
39
- self.current_file: str = ""
40
- self.source_code: str = ""
41
- self.imports: List[str] = []
42
-
43
- def extract_functions(
44
- self, tree: "tree_sitter.Tree", source_code: str
45
- ) -> List[Function]:
46
- """Extract Java method definitions with comprehensive analysis"""
47
- self.source_code = source_code
48
- functions: List[Function] = []
49
-
50
- # 複数のメソッドパターンを検索
51
- method_queries = [
52
- # 通常のメソッド宣言
53
- """
54
- (method_declaration
55
- (modifiers)? @method.modifiers
56
- type: (_)? @method.return_type
57
- name: (identifier) @method.name
58
- parameters: (formal_parameters) @method.params
59
- (throws)? @method.throws
60
- body: (block)? @method.body) @method.declaration
61
- """,
62
- # コンストラクタ
63
- """
64
- (constructor_declaration
65
- (modifiers)? @constructor.modifiers
66
- name: (identifier) @constructor.name
67
- parameters: (formal_parameters) @constructor.params
68
- (throws)? @constructor.throws
69
- body: (constructor_body) @constructor.body) @constructor.declaration
70
- """,
71
- ]
72
-
73
- try:
74
- language = tree.language if hasattr(tree, "language") else None
75
- if language:
76
- for query_string in method_queries:
77
- query = language.query(query_string)
78
- captures = query.captures(tree.root_node)
79
-
80
- if isinstance(captures, dict):
81
- # メソッド宣言を処理
82
- method_nodes = captures.get("method.declaration", [])
83
- for node in method_nodes:
84
- function = self._extract_detailed_method_info(
85
- node, source_code, False
86
- )
87
- if function:
88
- functions.append(function)
89
-
90
- # コンストラクタを処理
91
- constructor_nodes = captures.get("constructor.declaration", [])
92
- for node in constructor_nodes:
93
- function = self._extract_detailed_method_info(
94
- node, source_code, True
95
- )
96
- if function:
97
- functions.append(function)
98
-
99
- except Exception as e:
100
- log_warning(f"Could not extract Java methods: {e}")
101
-
102
- return functions
103
-
104
- def extract_classes(
105
- self, tree: "tree_sitter.Tree", source_code: str
106
- ) -> List[Class]:
107
- """Extract Java class definitions with comprehensive analysis"""
108
- self.source_code = source_code
109
- classes: List[Class] = []
110
-
111
- # 複数のクラスタイプを検索
112
- class_queries = [
113
- # 通常のクラス
114
- """
115
- (class_declaration
116
- (modifiers)? @class.modifiers
117
- name: (identifier) @class.name
118
- (superclass)? @class.superclass
119
- (super_interfaces)? @class.interfaces
120
- body: (class_body) @class.body) @class.declaration
121
- """,
122
- # インターフェース
123
- """
124
- (interface_declaration
125
- (modifiers)? @interface.modifiers
126
- name: (identifier) @interface.name
127
- (extends_interfaces)? @interface.extends
128
- body: (interface_body) @interface.body) @interface.declaration
129
- """,
130
- # 列挙型
131
- """
132
- (enum_declaration
133
- (modifiers)? @enum.modifiers
134
- name: (identifier) @enum.name
135
- (super_interfaces)? @enum.interfaces
136
- body: (enum_body) @enum.body) @enum.declaration
137
- """,
138
- # アノテーション型
139
- """
140
- (annotation_type_declaration
141
- (modifiers)? @annotation.modifiers
142
- name: (identifier) @annotation.name
143
- body: (annotation_type_body) @annotation.body) @annotation.declaration
144
- """,
145
- ]
146
-
147
- try:
148
- language = tree.language if hasattr(tree, "language") else None
149
- if language:
150
- for query_string in class_queries:
151
- query = language.query(query_string)
152
- captures = query.captures(tree.root_node)
153
-
154
- if isinstance(captures, dict):
155
- # 各タイプのクラスを処理
156
- for key, nodes in captures.items():
157
- if key.endswith(".declaration"):
158
- class_type = key.split(".")[0]
159
- for node in nodes:
160
- cls = self._extract_detailed_class_info(
161
- node, source_code, class_type
162
- )
163
- if cls:
164
- classes.append(cls)
165
-
166
- except Exception as e:
167
- log_warning(f"Could not extract Java classes: {e}")
168
-
169
- return classes
170
-
171
- def extract_variables(
172
- self, tree: "tree_sitter.Tree", source_code: str
173
- ) -> List[Variable]:
174
- """Extract Java field and variable definitions"""
175
- variables: List[Variable] = []
176
-
177
- # Field declarations
178
- query_string = """
179
- (field_declaration
180
- type: (_) @field.type
181
- declarator: (variable_declarator
182
- name: (identifier) @field.name)) @field.declaration
183
- """
184
-
185
- try:
186
- language = tree.language if hasattr(tree, "language") else None
187
- if language:
188
- query = language.query(query_string)
189
- captures = query.captures(tree.root_node)
190
-
191
- if isinstance(captures, dict):
192
- field_nodes = captures.get("field.declaration", [])
193
- for node in field_nodes:
194
- variable = self._extract_field_info(node, source_code)
195
- if variable:
196
- variables.append(variable)
197
-
198
- except Exception as e:
199
- log_warning(f"Could not extract Java fields: {e}")
200
-
201
- return variables
202
-
203
- def extract_imports(
204
- self, tree: "tree_sitter.Tree", source_code: str
205
- ) -> List[Import]:
206
- """Extract Java import statements"""
207
- imports: List[Import] = []
208
-
209
- query_string = """
210
- (import_declaration
211
- (scoped_identifier) @import.name) @import.declaration
212
- """
213
-
214
- try:
215
- language = tree.language if hasattr(tree, "language") else None
216
- if language:
217
- query = language.query(query_string)
218
- captures = query.captures(tree.root_node)
219
-
220
- if isinstance(captures, dict):
221
- import_nodes = captures.get("import.declaration", [])
222
- for node in import_nodes:
223
- imp = self._extract_import_info(node, source_code)
224
- if imp:
225
- imports.append(imp)
226
-
227
- except Exception as e:
228
- log_warning(f"Could not extract Java imports: {e}")
229
-
230
- return imports
231
-
232
- def _extract_detailed_method_info(
233
- self, node: "tree_sitter.Node", source_code: str, is_constructor: bool = False
234
- ) -> Optional[Function]:
235
- """Extract comprehensive method information from AST node"""
236
- try:
237
- # 基本情報の抽出
238
- name = self._extract_name_from_node(node, source_code)
239
- if not name:
240
- return None
241
-
242
- # 詳細情報の抽出
243
- return_type = (
244
- self._extract_return_type_from_node(node, source_code)
245
- if not is_constructor
246
- else "void"
247
- )
248
- parameters = self._extract_parameters_from_node(node, source_code)
249
- modifiers = self._extract_modifiers_from_node(node, source_code)
250
- annotations = self._extract_annotations_from_node(node, source_code)
251
- throws = self._extract_throws_from_node(node, source_code)
252
-
253
- # 可視性の判定
254
- visibility = "public"
255
- if "private" in modifiers:
256
- visibility = "private"
257
- elif "protected" in modifiers:
258
- visibility = "protected"
259
- elif "public" not in modifiers and len(modifiers) > 0:
260
- visibility = "package"
261
-
262
- # メソッドボディの抽出
263
- body = self._extract_method_body(node, source_code)
264
- signature = self._generate_method_signature(
265
- name, return_type, parameters, modifiers
266
- )
267
-
268
- # 複雑度の簡易計算
269
- complexity_score = self._calculate_complexity(body)
270
-
271
- # Function型として返すため、基本的なFunction型を作成
272
- return Function(
273
- name=name,
274
- start_line=node.start_point[0] + 1,
275
- end_line=node.end_point[0] + 1,
276
- raw_text=source_code[node.start_byte : node.end_byte],
277
- language="java",
278
- parameters=parameters,
279
- return_type=return_type,
280
- modifiers=modifiers,
281
- is_static="static" in modifiers,
282
- is_private="private" in modifiers,
283
- is_public="public" in modifiers,
284
- )
285
-
286
- except Exception as e:
287
- log_warning(f"Could not extract detailed method info: {e}")
288
- return None
289
-
290
- def _extract_name_from_node(
291
- self, node: "tree_sitter.Node", source_code: str
292
- ) -> Optional[str]:
293
- """Extract name from AST node"""
294
- for child in node.children:
295
- if child.type == "identifier":
296
- return source_code[child.start_byte : child.end_byte]
297
- return None
298
-
299
- def _extract_return_type_from_node(
300
- self, node: "tree_sitter.Node", source_code: str
301
- ) -> str:
302
- """Extract return type from method node"""
303
- for child in node.children:
304
- if child.type in [
305
- "type_identifier",
306
- "generic_type",
307
- "array_type",
308
- "primitive_type",
309
- ]:
310
- return source_code[child.start_byte : child.end_byte]
311
- return "void"
312
-
313
- def _extract_parameters_from_node(
314
- self, node: "tree_sitter.Node", source_code: str
315
- ) -> List[str]:
316
- """Extract parameters from method node"""
317
- parameters: List[str] = []
318
- for child in node.children:
319
- if child.type == "formal_parameters":
320
- for param_child in child.children:
321
- if param_child.type == "formal_parameter":
322
- param_text = source_code[
323
- param_child.start_byte : param_child.end_byte
324
- ]
325
- parameters.append(param_text)
326
- return parameters
327
-
328
- def _extract_modifiers_from_node(
329
- self, node: "tree_sitter.Node", source_code: str
330
- ) -> List[str]:
331
- """Extract modifiers from node"""
332
- modifiers: List[str] = []
333
- for child in node.children:
334
- if child.type == "modifiers":
335
- modifier_text = source_code[child.start_byte : child.end_byte]
336
- # 簡単な分割で各修飾子を抽出
337
- for modifier in modifier_text.split():
338
- if modifier in [
339
- "public",
340
- "private",
341
- "protected",
342
- "static",
343
- "final",
344
- "abstract",
345
- "synchronized",
346
- ]:
347
- modifiers.append(modifier)
348
- return modifiers
349
-
350
- def _extract_annotations_from_node(
351
- self, node: "tree_sitter.Node", source_code: str
352
- ) -> List[JavaAnnotation]:
353
- """Extract annotations from node (simplified)"""
354
- annotations: List[JavaAnnotation] = []
355
- # より詳細な実装が必要だが、今回は簡略化
356
- return annotations
357
-
358
- def _extract_throws_from_node(
359
- self, node: "tree_sitter.Node", source_code: str
360
- ) -> List[str]:
361
- """Extract throws clause from method node"""
362
- throws: List[str] = []
363
- for child in node.children:
364
- if child.type == "throws":
365
- throws_text = source_code[child.start_byte : child.end_byte]
366
- # "throws" キーワードを除去して例外タイプを抽出
367
- if throws_text.startswith("throws"):
368
- exceptions = throws_text[6:].strip()
369
- throws.extend([ex.strip() for ex in exceptions.split(",")])
370
- return throws
371
-
372
- def _extract_method_body(self, node: "tree_sitter.Node", source_code: str) -> str:
373
- """Extract method body"""
374
- for child in node.children:
375
- if child.type in ["block", "constructor_body"]:
376
- return source_code[child.start_byte : child.end_byte]
377
- return ""
378
-
379
- def _generate_method_signature(
380
- self, name: str, return_type: str, parameters: List[str], modifiers: List[str]
381
- ) -> str:
382
- """Generate method signature"""
383
- modifier_str = " ".join(modifiers) + " " if modifiers else ""
384
- param_str = ", ".join(parameters) if parameters else ""
385
- return f"{modifier_str}{return_type} {name}({param_str})"
386
-
387
- def _calculate_complexity(self, body: str) -> int:
388
- """Calculate cyclomatic complexity (simplified)"""
389
- complexity = 1 # Base complexity
390
- keywords = ["if", "else", "for", "while", "switch", "case", "catch", "&&", "||"]
391
- for keyword in keywords:
392
- complexity += body.count(keyword)
393
- return complexity
394
-
395
- def _extract_detailed_class_info(
396
- self, node: "tree_sitter.Node", source_code: str, class_type: str = "class"
397
- ) -> Optional[Class]:
398
- """Extract comprehensive class information from AST node"""
399
- try:
400
- # 基本情報の抽出
401
- name = self._extract_name_from_node(node, source_code)
402
- if not name:
403
- return None
404
-
405
- # 詳細情報の抽出
406
- modifiers = self._extract_modifiers_from_node(node, source_code)
407
- annotations = self._extract_annotations_from_node(node, source_code)
408
- superclass = self._extract_superclass_from_node(node, source_code)
409
- interfaces = self._extract_interfaces_from_node(node, source_code)
410
-
411
- # 完全修飾名の生成
412
- full_qualified_name = (
413
- f"{self.current_package}.{name}" if self.current_package else name
414
- )
415
-
416
- # 可視性の判定
417
- visibility = "public"
418
- if "private" in modifiers:
419
- visibility = "private"
420
- elif "protected" in modifiers:
421
- visibility = "protected"
422
- elif "public" not in modifiers and len(modifiers) > 0:
423
- visibility = "package"
424
-
425
- # ネストクラスかどうかの判定(簡略化)
426
- is_nested = "." in self.current_package if self.current_package else False
427
-
428
- # Class型として返すため、基本的なClass型を作成
429
- return Class(
430
- name=name,
431
- start_line=node.start_point[0] + 1,
432
- end_line=node.end_point[0] + 1,
433
- raw_text=source_code[node.start_byte : node.end_byte],
434
- language="java",
435
- class_type=class_type,
436
- full_qualified_name=full_qualified_name,
437
- package_name=self.current_package,
438
- superclass=superclass,
439
- interfaces=interfaces,
440
- modifiers=modifiers,
441
- )
442
-
443
- except Exception as e:
444
- log_warning(f"Could not extract detailed class info: {e}")
445
- return None
446
-
447
- def _extract_superclass_from_node(
448
- self, node: "tree_sitter.Node", source_code: str
449
- ) -> Optional[str]:
450
- """Extract superclass from class node"""
451
- for child in node.children:
452
- if child.type == "superclass":
453
- for subchild in child.children:
454
- if subchild.type == "type_identifier":
455
- return source_code[subchild.start_byte : subchild.end_byte]
456
- return None
457
-
458
- def _extract_interfaces_from_node(
459
- self, node: "tree_sitter.Node", source_code: str
460
- ) -> List[str]:
461
- """Extract implemented interfaces from class node"""
462
- interfaces: List[str] = []
463
- for child in node.children:
464
- if child.type in ["super_interfaces", "extends_interfaces"]:
465
- for subchild in child.children:
466
- if subchild.type == "type_identifier":
467
- interfaces.append(
468
- source_code[subchild.start_byte : subchild.end_byte]
469
- )
470
- return interfaces
471
-
472
- def _extract_field_info(
473
- self, node: "tree_sitter.Node", source_code: str
474
- ) -> Optional[Variable]:
475
- """Extract detailed field information from AST node"""
476
- try:
477
- # Check if node has required attributes
478
- if (
479
- not hasattr(node, "start_byte")
480
- or not hasattr(node, "end_byte")
481
- or not hasattr(node, "start_point")
482
- or not hasattr(node, "end_point")
483
- ):
484
- return None
485
- if (
486
- node.start_byte is None
487
- or node.end_byte is None
488
- or node.start_point is None
489
- or node.end_point is None
490
- ):
491
- return None
492
-
493
- # Simple field extraction
494
- field_text = source_code[node.start_byte : node.end_byte]
495
-
496
- # Variable型として返すため、基本的なVariable型を作成
497
- return Variable(
498
- name="field", # Would need more sophisticated parsing
499
- start_line=node.start_point[0] + 1,
500
- end_line=node.end_point[0] + 1,
501
- raw_text=field_text,
502
- language="java",
503
- )
504
-
505
- except Exception as e:
506
- log_warning(f"Could not extract field info: {e}")
507
- return None
508
-
509
- def _extract_import_info(
510
- self, node: "tree_sitter.Node", source_code: str
511
- ) -> Optional[Import]:
512
- """Extract detailed import information from AST node"""
513
- try:
514
- # Check if node has required attributes
515
- if (
516
- not hasattr(node, "start_byte")
517
- or not hasattr(node, "end_byte")
518
- or not hasattr(node, "start_point")
519
- or not hasattr(node, "end_point")
520
- ):
521
- return None
522
- if (
523
- node.start_byte is None
524
- or node.end_byte is None
525
- or node.start_point is None
526
- or node.end_point is None
527
- ):
528
- return None
529
-
530
- import_text = source_code[node.start_byte : node.end_byte]
531
-
532
- # Import型として返すため、基本的なImport型を作成
533
- return Import(
534
- name="import", # Would need more sophisticated parsing
535
- start_line=node.start_point[0] + 1,
536
- end_line=node.end_point[0] + 1,
537
- raw_text=import_text,
538
- language="java",
539
- module_name=import_text,
540
- )
541
-
542
- except Exception as e:
543
- log_warning(f"Could not extract import info: {e}")
544
- return None
545
-
546
-
547
- class JavaPlugin(LanguagePlugin):
548
- """Java language plugin"""
549
-
550
- def __init__(self) -> None:
551
- self._extractor = JavaElementExtractor()
552
- self._language: Optional["tree_sitter.Language"] = None
553
-
554
- @property
555
- def language_name(self) -> str:
556
- return "java"
557
-
558
- @property
559
- def file_extensions(self) -> List[str]:
560
- return [".java", ".jsp", ".jspx"]
561
-
562
- def get_language_name(self) -> str:
563
- """Return the name of the programming language this plugin supports"""
564
- return "java"
565
-
566
- def get_file_extensions(self) -> List[str]:
567
- """Return list of file extensions this plugin supports"""
568
- return [".java", ".jsp", ".jspx"]
569
-
570
- def create_extractor(self) -> ElementExtractor:
571
- """Create and return an element extractor for this language"""
572
- return JavaElementExtractor()
573
-
574
- def get_extractor(self) -> ElementExtractor:
575
- return self._extractor
576
-
577
- def get_tree_sitter_language(self) -> Optional["tree_sitter.Language"]:
578
- """Load and return Java tree-sitter language"""
579
- if self._language is None:
580
- self._language = loader.load_language("java")
581
- return self._language
582
-
583
- async def analyze_file(self, file_path: str, request: 'AnalysisRequest') -> 'AnalysisResult':
584
- """
585
- Javaファイルを解析してAnalysisResultを返す
586
-
587
- Args:
588
- file_path: 解析対象ファイルのパス
589
- request: 解析リクエスト
590
-
591
- Returns:
592
- 解析結果
593
- """
594
- from ..models import AnalysisResult
595
- from ..core.analysis_engine import get_analysis_engine, AnalysisRequest
596
-
597
- try:
598
- # Use UnifiedAnalysisEngine for file analysis
599
- analyzer = get_analysis_engine()
600
-
601
- # Create analysis request and analyze file
602
- request = AnalysisRequest(
603
- file_path=file_path,
604
- language="java",
605
- include_complexity=True,
606
- include_details=True
607
- )
608
- result = await analyzer.analyze(request)
609
-
610
- if not result or not result.success:
611
- return AnalysisResult(
612
- file_path=file_path,
613
- success=False,
614
- error_message=f"Failed to analyze Java file: {file_path}"
615
- )
616
-
617
- return result
618
-
619
- except Exception as e:
620
- log_error(f"Error analyzing Java file {file_path}: {e}")
621
- return AnalysisResult(
622
- file_path=file_path,
623
- success=False,
624
- error_message=str(e)
625
- )
1
+ #!/usr/bin/env python3
2
+ """
3
+ Java Language Plugin
4
+
5
+ Provides Java-specific parsing and element extraction functionality.
6
+ """
7
+
8
+ from typing import TYPE_CHECKING, Optional
9
+
10
+ if TYPE_CHECKING:
11
+ import tree_sitter
12
+
13
+ from ..core.analysis_engine import AnalysisRequest
14
+ from ..models import AnalysisResult
15
+
16
+ from ..language_loader import loader
17
+ from ..models import Class, Function, Import, JavaAnnotation, Variable
18
+ from ..utils import log_error, log_warning
19
+ from . import ElementExtractor, LanguagePlugin
20
+
21
+
22
+ class JavaElementExtractor(ElementExtractor):
23
+ """Advanced Java-specific element extractor with comprehensive analysis"""
24
+
25
+ def __init__(self) -> None:
26
+ # 分析コンテキスト
27
+ self.current_package: str = ""
28
+ self.current_file: str = ""
29
+ self.source_code: str = ""
30
+ self.imports: list[str] = []
31
+
32
+ def extract_functions(
33
+ self, tree: "tree_sitter.Tree", source_code: str
34
+ ) -> list[Function]:
35
+ """Extract Java method definitions with comprehensive analysis"""
36
+ self.source_code = source_code
37
+ functions: list[Function] = []
38
+
39
+ # 複数のメソッドパターンを検索
40
+ method_queries = [
41
+ # 通常のメソッド宣言
42
+ """
43
+ (method_declaration
44
+ (modifiers)? @method.modifiers
45
+ type: (_)? @method.return_type
46
+ name: (identifier) @method.name
47
+ parameters: (formal_parameters) @method.params
48
+ (throws)? @method.throws
49
+ body: (block)? @method.body) @method.declaration
50
+ """,
51
+ # コンストラクタ
52
+ """
53
+ (constructor_declaration
54
+ (modifiers)? @constructor.modifiers
55
+ name: (identifier) @constructor.name
56
+ parameters: (formal_parameters) @constructor.params
57
+ (throws)? @constructor.throws
58
+ body: (constructor_body) @constructor.body) @constructor.declaration
59
+ """,
60
+ ]
61
+
62
+ try:
63
+ language = tree.language if hasattr(tree, "language") else None
64
+ if language:
65
+ for query_string in method_queries:
66
+ query = language.query(query_string)
67
+ captures = query.captures(tree.root_node)
68
+
69
+ if isinstance(captures, dict):
70
+ # メソッド宣言を処理
71
+ method_nodes = captures.get("method.declaration", [])
72
+ for node in method_nodes:
73
+ function = self._extract_detailed_method_info(
74
+ node, source_code, False
75
+ )
76
+ if function:
77
+ functions.append(function)
78
+
79
+ # コンストラクタを処理
80
+ constructor_nodes = captures.get("constructor.declaration", [])
81
+ for node in constructor_nodes:
82
+ function = self._extract_detailed_method_info(
83
+ node, source_code, True
84
+ )
85
+ if function:
86
+ functions.append(function)
87
+
88
+ except Exception as e:
89
+ log_warning(f"Could not extract Java methods: {e}")
90
+
91
+ return functions
92
+
93
+ def extract_classes(
94
+ self, tree: "tree_sitter.Tree", source_code: str
95
+ ) -> list[Class]:
96
+ """Extract Java class definitions with comprehensive analysis"""
97
+ self.source_code = source_code
98
+ classes: list[Class] = []
99
+
100
+ # 複数のクラスタイプを検索
101
+ class_queries = [
102
+ # 通常のクラス
103
+ """
104
+ (class_declaration
105
+ (modifiers)? @class.modifiers
106
+ name: (identifier) @class.name
107
+ (superclass)? @class.superclass
108
+ (super_interfaces)? @class.interfaces
109
+ body: (class_body) @class.body) @class.declaration
110
+ """,
111
+ # インターフェース
112
+ """
113
+ (interface_declaration
114
+ (modifiers)? @interface.modifiers
115
+ name: (identifier) @interface.name
116
+ (extends_interfaces)? @interface.extends
117
+ body: (interface_body) @interface.body) @interface.declaration
118
+ """,
119
+ # 列挙型
120
+ """
121
+ (enum_declaration
122
+ (modifiers)? @enum.modifiers
123
+ name: (identifier) @enum.name
124
+ (super_interfaces)? @enum.interfaces
125
+ body: (enum_body) @enum.body) @enum.declaration
126
+ """,
127
+ # アノテーション型
128
+ """
129
+ (annotation_type_declaration
130
+ (modifiers)? @annotation.modifiers
131
+ name: (identifier) @annotation.name
132
+ body: (annotation_type_body) @annotation.body) @annotation.declaration
133
+ """,
134
+ ]
135
+
136
+ try:
137
+ language = tree.language if hasattr(tree, "language") else None
138
+ if language:
139
+ for query_string in class_queries:
140
+ query = language.query(query_string)
141
+ captures = query.captures(tree.root_node)
142
+
143
+ if isinstance(captures, dict):
144
+ # 各タイプのクラスを処理
145
+ for key, nodes in captures.items():
146
+ if key.endswith(".declaration"):
147
+ class_type = key.split(".")[0]
148
+ for node in nodes:
149
+ cls = self._extract_detailed_class_info(
150
+ node, source_code, class_type
151
+ )
152
+ if cls:
153
+ classes.append(cls)
154
+
155
+ except Exception as e:
156
+ log_warning(f"Could not extract Java classes: {e}")
157
+
158
+ return classes
159
+
160
+ def extract_variables(
161
+ self, tree: "tree_sitter.Tree", source_code: str
162
+ ) -> list[Variable]:
163
+ """Extract Java field and variable definitions"""
164
+ variables: list[Variable] = []
165
+
166
+ # Field declarations
167
+ query_string = """
168
+ (field_declaration
169
+ type: (_) @field.type
170
+ declarator: (variable_declarator
171
+ name: (identifier) @field.name)) @field.declaration
172
+ """
173
+
174
+ try:
175
+ language = tree.language if hasattr(tree, "language") else None
176
+ if language:
177
+ query = language.query(query_string)
178
+ captures = query.captures(tree.root_node)
179
+
180
+ if isinstance(captures, dict):
181
+ field_nodes = captures.get("field.declaration", [])
182
+ for node in field_nodes:
183
+ variable = self._extract_field_info(node, source_code)
184
+ if variable:
185
+ variables.append(variable)
186
+
187
+ except Exception as e:
188
+ log_warning(f"Could not extract Java fields: {e}")
189
+
190
+ return variables
191
+
192
+ def extract_imports(
193
+ self, tree: "tree_sitter.Tree", source_code: str
194
+ ) -> list[Import]:
195
+ """Extract Java import statements"""
196
+ imports: list[Import] = []
197
+
198
+ query_string = """
199
+ (import_declaration
200
+ (scoped_identifier) @import.name) @import.declaration
201
+ """
202
+
203
+ try:
204
+ language = tree.language if hasattr(tree, "language") else None
205
+ if language:
206
+ query = language.query(query_string)
207
+ captures = query.captures(tree.root_node)
208
+
209
+ if isinstance(captures, dict):
210
+ import_nodes = captures.get("import.declaration", [])
211
+ for node in import_nodes:
212
+ imp = self._extract_import_info(node, source_code)
213
+ if imp:
214
+ imports.append(imp)
215
+
216
+ except Exception as e:
217
+ log_warning(f"Could not extract Java imports: {e}")
218
+
219
+ return imports
220
+
221
+ def _extract_detailed_method_info(
222
+ self, node: "tree_sitter.Node", source_code: str, is_constructor: bool = False
223
+ ) -> Function | None:
224
+ """Extract comprehensive method information from AST node"""
225
+ try:
226
+ # 基本情報の抽出
227
+ name = self._extract_name_from_node(node, source_code)
228
+ if not name:
229
+ return None
230
+
231
+ # 詳細情報の抽出
232
+ return_type = (
233
+ self._extract_return_type_from_node(node, source_code)
234
+ if not is_constructor
235
+ else "void"
236
+ )
237
+ parameters = self._extract_parameters_from_node(node, source_code)
238
+ modifiers = self._extract_modifiers_from_node(node, source_code)
239
+ # annotations = self._extract_annotations_from_node(node, source_code) # Not used currently
240
+ # throws = self._extract_throws_from_node(node, source_code) # Not used currently
241
+
242
+ # 可視性の判定
243
+ # visibility = "public"
244
+ # if "private" in modifiers:
245
+ # visibility = "private"
246
+ # elif "protected" in modifiers:
247
+ # visibility = "protected" # Not used currently
248
+ # elif "public" not in modifiers and len(modifiers) > 0:
249
+ # visibility = "package" # Not used currently
250
+
251
+ # メソッドボディの抽出
252
+ # body = self._extract_method_body(node, source_code) # Not used currently
253
+ # signature = self._generate_method_signature(
254
+ # name, return_type, parameters, modifiers
255
+ # ) # Not used currently
256
+
257
+ # 複雑度の簡易計算
258
+ # complexity_score = self._calculate_complexity(body) # Not used currently
259
+
260
+ # Function型として返すため、基本的なFunction型を作成
261
+ return Function(
262
+ name=name,
263
+ start_line=node.start_point[0] + 1,
264
+ end_line=node.end_point[0] + 1,
265
+ raw_text=source_code[node.start_byte : node.end_byte],
266
+ language="java",
267
+ parameters=parameters,
268
+ return_type=return_type,
269
+ modifiers=modifiers,
270
+ is_static="static" in modifiers,
271
+ is_private="private" in modifiers,
272
+ is_public="public" in modifiers,
273
+ )
274
+
275
+ except Exception as e:
276
+ log_warning(f"Could not extract detailed method info: {e}")
277
+ return None
278
+
279
+ def _extract_name_from_node(
280
+ self, node: "tree_sitter.Node", source_code: str
281
+ ) -> str | None:
282
+ """Extract name from AST node"""
283
+ for child in node.children:
284
+ if child.type == "identifier":
285
+ return source_code[child.start_byte : child.end_byte]
286
+ return None
287
+
288
+ def _extract_return_type_from_node(
289
+ self, node: "tree_sitter.Node", source_code: str
290
+ ) -> str:
291
+ """Extract return type from method node"""
292
+ for child in node.children:
293
+ if child.type in [
294
+ "type_identifier",
295
+ "generic_type",
296
+ "array_type",
297
+ "primitive_type",
298
+ ]:
299
+ return source_code[child.start_byte : child.end_byte]
300
+ return "void"
301
+
302
+ def _extract_parameters_from_node(
303
+ self, node: "tree_sitter.Node", source_code: str
304
+ ) -> list[str]:
305
+ """Extract parameters from method node"""
306
+ parameters: list[str] = []
307
+ for child in node.children:
308
+ if child.type == "formal_parameters":
309
+ for param_child in child.children:
310
+ if param_child.type == "formal_parameter":
311
+ param_text = source_code[
312
+ param_child.start_byte : param_child.end_byte
313
+ ]
314
+ parameters.append(param_text)
315
+ return parameters
316
+
317
+ def _extract_modifiers_from_node(
318
+ self, node: "tree_sitter.Node", source_code: str
319
+ ) -> list[str]:
320
+ """Extract modifiers from node"""
321
+ modifiers: list[str] = []
322
+ for child in node.children:
323
+ if child.type == "modifiers":
324
+ modifier_text = source_code[child.start_byte : child.end_byte]
325
+ # 簡単な分割で各修飾子を抽出
326
+ for modifier in modifier_text.split():
327
+ if modifier in [
328
+ "public",
329
+ "private",
330
+ "protected",
331
+ "static",
332
+ "final",
333
+ "abstract",
334
+ "synchronized",
335
+ ]:
336
+ modifiers.append(modifier)
337
+ return modifiers
338
+
339
+ def _extract_annotations_from_node(
340
+ self, node: "tree_sitter.Node", source_code: str
341
+ ) -> list[JavaAnnotation]:
342
+ """Extract annotations from node (simplified)"""
343
+ annotations: list[JavaAnnotation] = []
344
+ # より詳細な実装が必要だが、今回は簡略化
345
+ return annotations
346
+
347
+ def _extract_throws_from_node(
348
+ self, node: "tree_sitter.Node", source_code: str
349
+ ) -> list[str]:
350
+ """Extract throws clause from method node"""
351
+ throws: list[str] = []
352
+ for child in node.children:
353
+ if child.type == "throws":
354
+ throws_text = source_code[child.start_byte : child.end_byte]
355
+ # "throws" キーワードを除去して例外タイプを抽出
356
+ if throws_text.startswith("throws"):
357
+ exceptions = throws_text[6:].strip()
358
+ throws.extend([ex.strip() for ex in exceptions.split(",")])
359
+ return throws
360
+
361
+ def _extract_method_body(self, node: "tree_sitter.Node", source_code: str) -> str:
362
+ """Extract method body"""
363
+ for child in node.children:
364
+ if child.type in ["block", "constructor_body"]:
365
+ return source_code[child.start_byte : child.end_byte]
366
+ return ""
367
+
368
+ def _generate_method_signature(
369
+ self, name: str, return_type: str, parameters: list[str], modifiers: list[str]
370
+ ) -> str:
371
+ """Generate method signature"""
372
+ modifier_str = " ".join(modifiers) + " " if modifiers else ""
373
+ param_str = ", ".join(parameters) if parameters else ""
374
+ return f"{modifier_str}{return_type} {name}({param_str})"
375
+
376
+ def _calculate_complexity(self, body: str) -> int:
377
+ """Calculate cyclomatic complexity (simplified)"""
378
+ complexity = 1 # Base complexity
379
+ keywords = ["if", "else", "for", "while", "switch", "case", "catch", "&&", "||"]
380
+ for keyword in keywords:
381
+ complexity += body.count(keyword)
382
+ return complexity
383
+
384
+ def _extract_detailed_class_info(
385
+ self, node: "tree_sitter.Node", source_code: str, class_type: str = "class"
386
+ ) -> Class | None:
387
+ """Extract comprehensive class information from AST node"""
388
+ try:
389
+ # 基本情報の抽出
390
+ name = self._extract_name_from_node(node, source_code)
391
+ if not name:
392
+ return None
393
+
394
+ # 詳細情報の抽出
395
+ modifiers = self._extract_modifiers_from_node(node, source_code)
396
+ # annotations = self._extract_annotations_from_node(node, source_code) # Not used currently
397
+ superclass = self._extract_superclass_from_node(node, source_code)
398
+ interfaces = self._extract_interfaces_from_node(node, source_code)
399
+
400
+ # 完全修飾名の生成
401
+ full_qualified_name = (
402
+ f"{self.current_package}.{name}" if self.current_package else name
403
+ )
404
+
405
+ # 可視性の判定
406
+ # visibility = "public"
407
+ # if "private" in modifiers:
408
+ # visibility = "private"
409
+ # elif "protected" in modifiers:
410
+ # visibility = "protected" # Not used currently
411
+ # elif "public" not in modifiers and len(modifiers) > 0:
412
+ # visibility = "package" # Not used currently
413
+
414
+ # ネストクラスかどうかの判定(簡略化)
415
+ # is_nested = "." in self.current_package if self.current_package else False # Not used currently
416
+
417
+ # Class型として返すため、基本的なClass型を作成
418
+ return Class(
419
+ name=name,
420
+ start_line=node.start_point[0] + 1,
421
+ end_line=node.end_point[0] + 1,
422
+ raw_text=source_code[node.start_byte : node.end_byte],
423
+ language="java",
424
+ class_type=class_type,
425
+ full_qualified_name=full_qualified_name,
426
+ package_name=self.current_package,
427
+ superclass=superclass,
428
+ interfaces=interfaces,
429
+ modifiers=modifiers,
430
+ )
431
+
432
+ except Exception as e:
433
+ log_warning(f"Could not extract detailed class info: {e}")
434
+ return None
435
+
436
+ def _extract_superclass_from_node(
437
+ self, node: "tree_sitter.Node", source_code: str
438
+ ) -> str | None:
439
+ """Extract superclass from class node"""
440
+ for child in node.children:
441
+ if child.type == "superclass":
442
+ for subchild in child.children:
443
+ if subchild.type == "type_identifier":
444
+ return source_code[subchild.start_byte : subchild.end_byte]
445
+ return None
446
+
447
+ def _extract_interfaces_from_node(
448
+ self, node: "tree_sitter.Node", source_code: str
449
+ ) -> list[str]:
450
+ """Extract implemented interfaces from class node"""
451
+ interfaces: list[str] = []
452
+ for child in node.children:
453
+ if child.type in ["super_interfaces", "extends_interfaces"]:
454
+ for subchild in child.children:
455
+ if subchild.type == "type_identifier":
456
+ interfaces.append(
457
+ source_code[subchild.start_byte : subchild.end_byte]
458
+ )
459
+ return interfaces
460
+
461
+ def _extract_field_info(
462
+ self, node: "tree_sitter.Node", source_code: str
463
+ ) -> Variable | None:
464
+ """Extract detailed field information from AST node"""
465
+ try:
466
+ # Check if node has required attributes
467
+ if (
468
+ not hasattr(node, "start_byte")
469
+ or not hasattr(node, "end_byte")
470
+ or not hasattr(node, "start_point")
471
+ or not hasattr(node, "end_point")
472
+ or node.start_byte is None
473
+ or node.end_byte is None
474
+ or node.start_point is None
475
+ or node.end_point is None
476
+ ):
477
+ return None
478
+
479
+ # Simple field extraction
480
+ field_text = source_code[node.start_byte : node.end_byte]
481
+
482
+ # Variable型として返すため、基本的なVariable型を作成
483
+ return Variable(
484
+ name="field", # Would need more sophisticated parsing
485
+ start_line=node.start_point[0] + 1,
486
+ end_line=node.end_point[0] + 1,
487
+ raw_text=field_text,
488
+ language="java",
489
+ )
490
+
491
+ except Exception as e:
492
+ log_warning(f"Could not extract field info: {e}")
493
+ return None
494
+
495
+ def _extract_import_info(
496
+ self, node: "tree_sitter.Node", source_code: str
497
+ ) -> Import | None:
498
+ """Extract detailed import information from AST node"""
499
+ try:
500
+ # Check if node has required attributes
501
+ if (
502
+ not hasattr(node, "start_byte")
503
+ or not hasattr(node, "end_byte")
504
+ or not hasattr(node, "start_point")
505
+ or not hasattr(node, "end_point")
506
+ or node.start_byte is None
507
+ or node.end_byte is None
508
+ or node.start_point is None
509
+ or node.end_point is None
510
+ ):
511
+ return None
512
+
513
+ import_text = source_code[node.start_byte : node.end_byte]
514
+
515
+ # Import型として返すため、基本的なImport型を作成
516
+ return Import(
517
+ name="import", # Would need more sophisticated parsing
518
+ start_line=node.start_point[0] + 1,
519
+ end_line=node.end_point[0] + 1,
520
+ raw_text=import_text,
521
+ language="java",
522
+ module_name=import_text,
523
+ )
524
+
525
+ except Exception as e:
526
+ log_warning(f"Could not extract import info: {e}")
527
+ return None
528
+
529
+
530
+ class JavaPlugin(LanguagePlugin):
531
+ """Java language plugin"""
532
+
533
+ def __init__(self) -> None:
534
+ self._extractor = JavaElementExtractor()
535
+ self._language: tree_sitter.Language | None = None
536
+
537
+ @property
538
+ def language_name(self) -> str:
539
+ return "java"
540
+
541
+ @property
542
+ def file_extensions(self) -> list[str]:
543
+ return [".java", ".jsp", ".jspx"]
544
+
545
+ def get_language_name(self) -> str:
546
+ """Return the name of the programming language this plugin supports"""
547
+ return "java"
548
+
549
+ def get_file_extensions(self) -> list[str]:
550
+ """Return list of file extensions this plugin supports"""
551
+ return [".java", ".jsp", ".jspx"]
552
+
553
+ def create_extractor(self) -> ElementExtractor:
554
+ """Create and return an element extractor for this language"""
555
+ return JavaElementExtractor()
556
+
557
+ def get_extractor(self) -> ElementExtractor:
558
+ return self._extractor
559
+
560
+ def get_tree_sitter_language(self) -> Optional["tree_sitter.Language"]:
561
+ """Load and return Java tree-sitter language"""
562
+ if self._language is None:
563
+ self._language = loader.load_language("java")
564
+ return self._language
565
+
566
+ async def analyze_file(
567
+ self, file_path: str, request: "AnalysisRequest"
568
+ ) -> "AnalysisResult":
569
+ """
570
+ Javaファイルを解析してAnalysisResultを返す
571
+
572
+ Args:
573
+ file_path: 解析対象ファイルのパス
574
+ request: 解析リクエスト
575
+
576
+ Returns:
577
+ 解析結果
578
+ """
579
+ from ..core.analysis_engine import AnalysisRequest, get_analysis_engine
580
+ from ..models import AnalysisResult
581
+
582
+ try:
583
+ # Use UnifiedAnalysisEngine for file analysis
584
+ analyzer = get_analysis_engine()
585
+
586
+ # Create analysis request and analyze file
587
+ request = AnalysisRequest(
588
+ file_path=file_path,
589
+ language="java",
590
+ include_complexity=True,
591
+ include_details=True,
592
+ )
593
+ result = await analyzer.analyze(request)
594
+
595
+ if not result or not result.success:
596
+ return AnalysisResult(
597
+ file_path=file_path,
598
+ success=False,
599
+ error_message=f"Failed to analyze Java file: {file_path}",
600
+ )
601
+
602
+ return result
603
+
604
+ except Exception as e:
605
+ log_error(f"Error analyzing Java file {file_path}: {e}")
606
+ return AnalysisResult(
607
+ file_path=file_path, success=False, error_message=str(e)
608
+ )