tree-sitter-analyzer 1.9.17.1__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.
- tree_sitter_analyzer/__init__.py +132 -0
- tree_sitter_analyzer/__main__.py +11 -0
- tree_sitter_analyzer/api.py +853 -0
- tree_sitter_analyzer/cli/__init__.py +39 -0
- tree_sitter_analyzer/cli/__main__.py +12 -0
- tree_sitter_analyzer/cli/argument_validator.py +89 -0
- tree_sitter_analyzer/cli/commands/__init__.py +26 -0
- tree_sitter_analyzer/cli/commands/advanced_command.py +226 -0
- tree_sitter_analyzer/cli/commands/base_command.py +181 -0
- tree_sitter_analyzer/cli/commands/default_command.py +18 -0
- tree_sitter_analyzer/cli/commands/find_and_grep_cli.py +188 -0
- tree_sitter_analyzer/cli/commands/list_files_cli.py +133 -0
- tree_sitter_analyzer/cli/commands/partial_read_command.py +139 -0
- tree_sitter_analyzer/cli/commands/query_command.py +109 -0
- tree_sitter_analyzer/cli/commands/search_content_cli.py +161 -0
- tree_sitter_analyzer/cli/commands/structure_command.py +156 -0
- tree_sitter_analyzer/cli/commands/summary_command.py +116 -0
- tree_sitter_analyzer/cli/commands/table_command.py +414 -0
- tree_sitter_analyzer/cli/info_commands.py +124 -0
- tree_sitter_analyzer/cli_main.py +472 -0
- tree_sitter_analyzer/constants.py +85 -0
- tree_sitter_analyzer/core/__init__.py +15 -0
- tree_sitter_analyzer/core/analysis_engine.py +580 -0
- tree_sitter_analyzer/core/cache_service.py +333 -0
- tree_sitter_analyzer/core/engine.py +585 -0
- tree_sitter_analyzer/core/parser.py +293 -0
- tree_sitter_analyzer/core/query.py +605 -0
- tree_sitter_analyzer/core/query_filter.py +200 -0
- tree_sitter_analyzer/core/query_service.py +340 -0
- tree_sitter_analyzer/encoding_utils.py +530 -0
- tree_sitter_analyzer/exceptions.py +747 -0
- tree_sitter_analyzer/file_handler.py +246 -0
- tree_sitter_analyzer/formatters/__init__.py +1 -0
- tree_sitter_analyzer/formatters/base_formatter.py +201 -0
- tree_sitter_analyzer/formatters/csharp_formatter.py +367 -0
- tree_sitter_analyzer/formatters/formatter_config.py +197 -0
- tree_sitter_analyzer/formatters/formatter_factory.py +84 -0
- tree_sitter_analyzer/formatters/formatter_registry.py +377 -0
- tree_sitter_analyzer/formatters/formatter_selector.py +96 -0
- tree_sitter_analyzer/formatters/go_formatter.py +368 -0
- tree_sitter_analyzer/formatters/html_formatter.py +498 -0
- tree_sitter_analyzer/formatters/java_formatter.py +423 -0
- tree_sitter_analyzer/formatters/javascript_formatter.py +611 -0
- tree_sitter_analyzer/formatters/kotlin_formatter.py +268 -0
- tree_sitter_analyzer/formatters/language_formatter_factory.py +123 -0
- tree_sitter_analyzer/formatters/legacy_formatter_adapters.py +228 -0
- tree_sitter_analyzer/formatters/markdown_formatter.py +725 -0
- tree_sitter_analyzer/formatters/php_formatter.py +301 -0
- tree_sitter_analyzer/formatters/python_formatter.py +830 -0
- tree_sitter_analyzer/formatters/ruby_formatter.py +278 -0
- tree_sitter_analyzer/formatters/rust_formatter.py +233 -0
- tree_sitter_analyzer/formatters/sql_formatter_wrapper.py +689 -0
- tree_sitter_analyzer/formatters/sql_formatters.py +536 -0
- tree_sitter_analyzer/formatters/typescript_formatter.py +543 -0
- tree_sitter_analyzer/formatters/yaml_formatter.py +462 -0
- tree_sitter_analyzer/interfaces/__init__.py +9 -0
- tree_sitter_analyzer/interfaces/cli.py +535 -0
- tree_sitter_analyzer/interfaces/cli_adapter.py +359 -0
- tree_sitter_analyzer/interfaces/mcp_adapter.py +224 -0
- tree_sitter_analyzer/interfaces/mcp_server.py +428 -0
- tree_sitter_analyzer/language_detector.py +553 -0
- tree_sitter_analyzer/language_loader.py +271 -0
- tree_sitter_analyzer/languages/__init__.py +10 -0
- tree_sitter_analyzer/languages/csharp_plugin.py +1076 -0
- tree_sitter_analyzer/languages/css_plugin.py +449 -0
- tree_sitter_analyzer/languages/go_plugin.py +836 -0
- tree_sitter_analyzer/languages/html_plugin.py +496 -0
- tree_sitter_analyzer/languages/java_plugin.py +1299 -0
- tree_sitter_analyzer/languages/javascript_plugin.py +1622 -0
- tree_sitter_analyzer/languages/kotlin_plugin.py +656 -0
- tree_sitter_analyzer/languages/markdown_plugin.py +1928 -0
- tree_sitter_analyzer/languages/php_plugin.py +862 -0
- tree_sitter_analyzer/languages/python_plugin.py +1636 -0
- tree_sitter_analyzer/languages/ruby_plugin.py +757 -0
- tree_sitter_analyzer/languages/rust_plugin.py +673 -0
- tree_sitter_analyzer/languages/sql_plugin.py +2444 -0
- tree_sitter_analyzer/languages/typescript_plugin.py +1892 -0
- tree_sitter_analyzer/languages/yaml_plugin.py +695 -0
- tree_sitter_analyzer/legacy_table_formatter.py +860 -0
- tree_sitter_analyzer/mcp/__init__.py +34 -0
- tree_sitter_analyzer/mcp/resources/__init__.py +43 -0
- tree_sitter_analyzer/mcp/resources/code_file_resource.py +208 -0
- tree_sitter_analyzer/mcp/resources/project_stats_resource.py +586 -0
- tree_sitter_analyzer/mcp/server.py +869 -0
- tree_sitter_analyzer/mcp/tools/__init__.py +28 -0
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +779 -0
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +291 -0
- tree_sitter_analyzer/mcp/tools/base_tool.py +139 -0
- tree_sitter_analyzer/mcp/tools/fd_rg_utils.py +816 -0
- tree_sitter_analyzer/mcp/tools/find_and_grep_tool.py +686 -0
- tree_sitter_analyzer/mcp/tools/list_files_tool.py +413 -0
- tree_sitter_analyzer/mcp/tools/output_format_validator.py +148 -0
- tree_sitter_analyzer/mcp/tools/query_tool.py +443 -0
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +464 -0
- tree_sitter_analyzer/mcp/tools/search_content_tool.py +836 -0
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +572 -0
- tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +653 -0
- tree_sitter_analyzer/mcp/utils/__init__.py +113 -0
- tree_sitter_analyzer/mcp/utils/error_handler.py +569 -0
- tree_sitter_analyzer/mcp/utils/file_output_factory.py +217 -0
- tree_sitter_analyzer/mcp/utils/file_output_manager.py +322 -0
- tree_sitter_analyzer/mcp/utils/gitignore_detector.py +358 -0
- tree_sitter_analyzer/mcp/utils/path_resolver.py +414 -0
- tree_sitter_analyzer/mcp/utils/search_cache.py +343 -0
- tree_sitter_analyzer/models.py +840 -0
- tree_sitter_analyzer/mypy_current_errors.txt +2 -0
- tree_sitter_analyzer/output_manager.py +255 -0
- tree_sitter_analyzer/platform_compat/__init__.py +3 -0
- tree_sitter_analyzer/platform_compat/adapter.py +324 -0
- tree_sitter_analyzer/platform_compat/compare.py +224 -0
- tree_sitter_analyzer/platform_compat/detector.py +67 -0
- tree_sitter_analyzer/platform_compat/fixtures.py +228 -0
- tree_sitter_analyzer/platform_compat/profiles.py +217 -0
- tree_sitter_analyzer/platform_compat/record.py +55 -0
- tree_sitter_analyzer/platform_compat/recorder.py +155 -0
- tree_sitter_analyzer/platform_compat/report.py +92 -0
- tree_sitter_analyzer/plugins/__init__.py +280 -0
- tree_sitter_analyzer/plugins/base.py +647 -0
- tree_sitter_analyzer/plugins/manager.py +384 -0
- tree_sitter_analyzer/project_detector.py +328 -0
- tree_sitter_analyzer/queries/__init__.py +27 -0
- tree_sitter_analyzer/queries/csharp.py +216 -0
- tree_sitter_analyzer/queries/css.py +615 -0
- tree_sitter_analyzer/queries/go.py +275 -0
- tree_sitter_analyzer/queries/html.py +543 -0
- tree_sitter_analyzer/queries/java.py +402 -0
- tree_sitter_analyzer/queries/javascript.py +724 -0
- tree_sitter_analyzer/queries/kotlin.py +192 -0
- tree_sitter_analyzer/queries/markdown.py +258 -0
- tree_sitter_analyzer/queries/php.py +95 -0
- tree_sitter_analyzer/queries/python.py +859 -0
- tree_sitter_analyzer/queries/ruby.py +92 -0
- tree_sitter_analyzer/queries/rust.py +223 -0
- tree_sitter_analyzer/queries/sql.py +555 -0
- tree_sitter_analyzer/queries/typescript.py +871 -0
- tree_sitter_analyzer/queries/yaml.py +236 -0
- tree_sitter_analyzer/query_loader.py +272 -0
- tree_sitter_analyzer/security/__init__.py +22 -0
- tree_sitter_analyzer/security/boundary_manager.py +277 -0
- tree_sitter_analyzer/security/regex_checker.py +297 -0
- tree_sitter_analyzer/security/validator.py +599 -0
- tree_sitter_analyzer/table_formatter.py +782 -0
- tree_sitter_analyzer/utils/__init__.py +53 -0
- tree_sitter_analyzer/utils/logging.py +433 -0
- tree_sitter_analyzer/utils/tree_sitter_compat.py +289 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/METADATA +485 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/RECORD +149 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/WHEEL +4 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/entry_points.txt +25 -0
|
@@ -0,0 +1,860 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Legacy Table Formatter for Tree-sitter Analyzer
|
|
4
|
+
|
|
5
|
+
This module provides the restored v1.6.1.4 TableFormatter implementation
|
|
6
|
+
to ensure backward compatibility for analyze_code_structure tool.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import csv
|
|
10
|
+
import io
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class LegacyTableFormatter:
|
|
15
|
+
"""
|
|
16
|
+
Legacy table formatter for code analysis results.
|
|
17
|
+
|
|
18
|
+
This class restores the exact v1.6.1.4 behavior for the analyze_code_structure
|
|
19
|
+
tool, ensuring backward compatibility and specification compliance.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
format_type: str = "full",
|
|
25
|
+
language: str = "java",
|
|
26
|
+
include_javadoc: bool = False,
|
|
27
|
+
):
|
|
28
|
+
"""
|
|
29
|
+
Initialize the legacy table formatter.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
format_type: Format type (full, compact, csv)
|
|
33
|
+
language: Programming language for syntax highlighting
|
|
34
|
+
include_javadoc: Whether to include JavaDoc/documentation
|
|
35
|
+
"""
|
|
36
|
+
self.format_type = format_type
|
|
37
|
+
self.language = language
|
|
38
|
+
self.include_javadoc = include_javadoc
|
|
39
|
+
|
|
40
|
+
def _get_platform_newline(self) -> str:
|
|
41
|
+
"""Get platform-specific newline character"""
|
|
42
|
+
import os
|
|
43
|
+
|
|
44
|
+
return "\r\n" if os.name == "nt" else "\n" # Windows uses \r\n, others use \n
|
|
45
|
+
|
|
46
|
+
def _convert_to_platform_newlines(self, text: str) -> str:
|
|
47
|
+
"""Convert standard \\n to platform-specific newline characters"""
|
|
48
|
+
platform_newline = self._get_platform_newline()
|
|
49
|
+
if platform_newline != "\n":
|
|
50
|
+
return text.replace("\n", platform_newline)
|
|
51
|
+
return text
|
|
52
|
+
|
|
53
|
+
def format_structure(self, structure_data: dict[str, Any]) -> str:
|
|
54
|
+
"""
|
|
55
|
+
Format structure data as table.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
structure_data: Dictionary containing analysis results
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Formatted string in the specified format
|
|
62
|
+
|
|
63
|
+
Raises:
|
|
64
|
+
ValueError: If format_type is not supported
|
|
65
|
+
"""
|
|
66
|
+
if self.format_type == "full":
|
|
67
|
+
result = self._format_full_table(structure_data)
|
|
68
|
+
elif self.format_type == "compact":
|
|
69
|
+
result = self._format_compact_table(structure_data)
|
|
70
|
+
elif self.format_type == "csv":
|
|
71
|
+
result = self._format_csv(structure_data)
|
|
72
|
+
else:
|
|
73
|
+
raise ValueError(f"Unsupported format type: {self.format_type}")
|
|
74
|
+
|
|
75
|
+
# Convert to platform-specific newline characters
|
|
76
|
+
# Skip newline conversion for CSV format
|
|
77
|
+
if self.format_type in ["csv"]:
|
|
78
|
+
return result
|
|
79
|
+
|
|
80
|
+
return self._convert_to_platform_newlines(result)
|
|
81
|
+
|
|
82
|
+
def _format_full_table(self, data: dict[str, Any]) -> str:
|
|
83
|
+
"""Full table format - compliant with format specification"""
|
|
84
|
+
lines = []
|
|
85
|
+
|
|
86
|
+
# Header - use package.class format for single class
|
|
87
|
+
classes = data.get("classes", [])
|
|
88
|
+
if classes is None:
|
|
89
|
+
classes = []
|
|
90
|
+
|
|
91
|
+
# Determine header format
|
|
92
|
+
package_name = (data.get("package") or {}).get("name", "")
|
|
93
|
+
if len(classes) == 1:
|
|
94
|
+
# Single class: use package.ClassName format
|
|
95
|
+
class_name = classes[0].get("name", "Unknown")
|
|
96
|
+
if package_name:
|
|
97
|
+
header = f"{package_name}.{class_name}"
|
|
98
|
+
else:
|
|
99
|
+
header = class_name
|
|
100
|
+
else:
|
|
101
|
+
# Multiple classes or no classes: use filename or default
|
|
102
|
+
file_path = data.get("file_path", "")
|
|
103
|
+
if file_path and file_path != "Unknown":
|
|
104
|
+
file_name = file_path.split("/")[-1].split("\\")[-1]
|
|
105
|
+
if file_name.endswith(".java"):
|
|
106
|
+
file_name = file_name[:-5] # Remove .java extension
|
|
107
|
+
elif file_name.endswith(".py"):
|
|
108
|
+
file_name = file_name[:-3] # Remove .py extension
|
|
109
|
+
elif file_name.endswith(".js"):
|
|
110
|
+
file_name = file_name[:-3] # Remove .js extension
|
|
111
|
+
|
|
112
|
+
if package_name and len(classes) == 0:
|
|
113
|
+
# No classes but has package: use package.filename
|
|
114
|
+
header = f"{package_name}.{file_name}"
|
|
115
|
+
else:
|
|
116
|
+
header = file_name
|
|
117
|
+
else:
|
|
118
|
+
# No file path: use default format
|
|
119
|
+
if package_name:
|
|
120
|
+
header = f"{package_name}.Unknown"
|
|
121
|
+
else:
|
|
122
|
+
header = "unknown.Unknown"
|
|
123
|
+
|
|
124
|
+
lines.append(f"# {header}")
|
|
125
|
+
lines.append("")
|
|
126
|
+
|
|
127
|
+
# Get package name once for use throughout
|
|
128
|
+
package_name = (data.get("package") or {}).get("name", "")
|
|
129
|
+
|
|
130
|
+
# Package section (if package exists)
|
|
131
|
+
if package_name and package_name != "unknown":
|
|
132
|
+
lines.append("## Package")
|
|
133
|
+
lines.append(f"`{package_name}`")
|
|
134
|
+
lines.append("")
|
|
135
|
+
|
|
136
|
+
# Imports section (should appear before class info)
|
|
137
|
+
imports = data.get("imports", [])
|
|
138
|
+
if imports:
|
|
139
|
+
lines.append("## Imports")
|
|
140
|
+
lines.append(f"```{self.language}")
|
|
141
|
+
for imp in imports:
|
|
142
|
+
statement = str(imp.get("statement", ""))
|
|
143
|
+
lines.append(statement)
|
|
144
|
+
lines.append("```")
|
|
145
|
+
lines.append("")
|
|
146
|
+
|
|
147
|
+
# Class Info section (required by specification)
|
|
148
|
+
lines.append("## Class Info")
|
|
149
|
+
lines.append("| Property | Value |")
|
|
150
|
+
lines.append("|----------|-------|")
|
|
151
|
+
|
|
152
|
+
# Use package_name or default to "unknown" for display
|
|
153
|
+
display_package = package_name if package_name else "unknown"
|
|
154
|
+
|
|
155
|
+
if len(classes) >= 1:
|
|
156
|
+
class_info = classes[0]
|
|
157
|
+
class_name = str(class_info.get("name", "Unknown"))
|
|
158
|
+
lines.append(f"| Name | {class_name} |")
|
|
159
|
+
lines.append(f"| Package | {display_package} |")
|
|
160
|
+
lines.append(f"| Type | {str(class_info.get('type', 'class'))} |")
|
|
161
|
+
lines.append(f"| Access | {str(class_info.get('visibility', 'public'))} |")
|
|
162
|
+
|
|
163
|
+
# Lines
|
|
164
|
+
line_range = class_info.get("line_range", {})
|
|
165
|
+
lines_str = f"{line_range.get('start', 1)}-{line_range.get('end', 50)}"
|
|
166
|
+
|
|
167
|
+
# Add optional fields
|
|
168
|
+
extends = class_info.get("extends")
|
|
169
|
+
if extends:
|
|
170
|
+
lines.append(f"| Extends | {extends} |")
|
|
171
|
+
|
|
172
|
+
implements = class_info.get("implements", [])
|
|
173
|
+
if implements:
|
|
174
|
+
lines.append(f"| Implements | {', '.join(implements)} |")
|
|
175
|
+
else:
|
|
176
|
+
# Empty data case
|
|
177
|
+
lines.append("| Name | Unknown |")
|
|
178
|
+
lines.append(f"| Package | {display_package} |")
|
|
179
|
+
lines.append("| Type | class |")
|
|
180
|
+
lines.append("| Access | public |")
|
|
181
|
+
|
|
182
|
+
lines.append("")
|
|
183
|
+
|
|
184
|
+
# Check if we have multiple classes to organize by class
|
|
185
|
+
all_methods = data.get("methods", []) or []
|
|
186
|
+
all_fields = data.get("fields", []) or []
|
|
187
|
+
|
|
188
|
+
if len(classes) > 1:
|
|
189
|
+
# Multiple classes: add Classes Overview section
|
|
190
|
+
lines.append("## Classes Overview")
|
|
191
|
+
lines.append("| Class | Type | Visibility | Lines | Methods | Fields |")
|
|
192
|
+
lines.append("|-------|------|------------|-------|---------|--------|")
|
|
193
|
+
|
|
194
|
+
for class_info in classes:
|
|
195
|
+
class_name = str(class_info.get("name", "Unknown"))
|
|
196
|
+
class_type = str(class_info.get("type", "class"))
|
|
197
|
+
visibility = str(class_info.get("visibility", "public"))
|
|
198
|
+
line_range = class_info.get("line_range", {})
|
|
199
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
200
|
+
|
|
201
|
+
# Count methods and fields for this class
|
|
202
|
+
class_methods = self._get_class_methods(data, line_range)
|
|
203
|
+
class_fields = self._get_class_fields(data, line_range)
|
|
204
|
+
|
|
205
|
+
lines.append(
|
|
206
|
+
f"| {class_name} | {class_type} | {visibility} | {lines_str} | {len(class_methods)} | {len(class_fields)} |"
|
|
207
|
+
)
|
|
208
|
+
lines.append("")
|
|
209
|
+
|
|
210
|
+
# Multiple classes: organize methods and fields by class
|
|
211
|
+
for class_info in classes:
|
|
212
|
+
class_name = str(class_info.get("name", "Unknown"))
|
|
213
|
+
line_range = class_info.get("line_range", {})
|
|
214
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
215
|
+
|
|
216
|
+
lines.append(f"## {class_name} ({lines_str})")
|
|
217
|
+
|
|
218
|
+
# Get methods for this class
|
|
219
|
+
class_methods = self._get_class_methods(data, line_range)
|
|
220
|
+
|
|
221
|
+
if class_methods:
|
|
222
|
+
lines.append("### Methods")
|
|
223
|
+
lines.append("| Name | Return Type | Parameters | Access | Line |")
|
|
224
|
+
lines.append("|------|-------------|------------|--------|------|")
|
|
225
|
+
|
|
226
|
+
for method in class_methods:
|
|
227
|
+
name = str(method.get("name", ""))
|
|
228
|
+
# Constructors don't have return types
|
|
229
|
+
is_constructor = method.get("is_constructor", False)
|
|
230
|
+
return_type = (
|
|
231
|
+
"-"
|
|
232
|
+
if is_constructor
|
|
233
|
+
else str(method.get("return_type", "void"))
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
# Format parameters as "type1 param1, type2 param2"
|
|
237
|
+
params = method.get("parameters", [])
|
|
238
|
+
param_strs = []
|
|
239
|
+
for param in params:
|
|
240
|
+
if isinstance(param, dict):
|
|
241
|
+
param_type = str(param.get("type", "Object"))
|
|
242
|
+
param_name = str(param.get("name", "param"))
|
|
243
|
+
param_strs.append(f"{param_type} {param_name}")
|
|
244
|
+
elif isinstance(param, str):
|
|
245
|
+
param_strs.append(param)
|
|
246
|
+
else:
|
|
247
|
+
param_strs.append(str(param))
|
|
248
|
+
params_str = ", ".join(param_strs)
|
|
249
|
+
|
|
250
|
+
access = str(method.get("visibility", "public"))
|
|
251
|
+
line_num = method.get("line_range", {}).get("start", 0)
|
|
252
|
+
|
|
253
|
+
lines.append(
|
|
254
|
+
f"| {name} | {return_type} | {params_str} | {access} | {line_num} |"
|
|
255
|
+
)
|
|
256
|
+
lines.append("")
|
|
257
|
+
|
|
258
|
+
# Get fields for this class
|
|
259
|
+
class_fields = self._get_class_fields(data, line_range)
|
|
260
|
+
|
|
261
|
+
if class_fields:
|
|
262
|
+
lines.append("### Fields")
|
|
263
|
+
lines.append("| Name | Type | Access | Static | Final | Line |")
|
|
264
|
+
lines.append("|------|------|--------|--------|-------|------|")
|
|
265
|
+
|
|
266
|
+
for field in class_fields:
|
|
267
|
+
name = str(field.get("name", ""))
|
|
268
|
+
field_type = str(field.get("type", "Object"))
|
|
269
|
+
access = str(field.get("visibility", "private"))
|
|
270
|
+
|
|
271
|
+
# Check modifiers for static and final
|
|
272
|
+
modifiers = field.get("modifiers", [])
|
|
273
|
+
is_static = "static" in modifiers or field.get(
|
|
274
|
+
"is_static", False
|
|
275
|
+
)
|
|
276
|
+
is_final = "final" in modifiers or field.get("is_final", False)
|
|
277
|
+
|
|
278
|
+
static_str = "true" if is_static else "false"
|
|
279
|
+
final_str = "true" if is_final else "false"
|
|
280
|
+
|
|
281
|
+
line_num = field.get("line_range", {}).get("start", 0)
|
|
282
|
+
|
|
283
|
+
lines.append(
|
|
284
|
+
f"| {name} | {field_type} | {access} | {static_str} | {final_str} | {line_num} |"
|
|
285
|
+
)
|
|
286
|
+
lines.append("")
|
|
287
|
+
else:
|
|
288
|
+
# Single class or no classes: use original format
|
|
289
|
+
# Methods section (required by specification)
|
|
290
|
+
lines.append("## Methods")
|
|
291
|
+
if all_methods:
|
|
292
|
+
lines.append("| Name | Return Type | Parameters | Access | Line |")
|
|
293
|
+
lines.append("|------|-------------|------------|--------|------|")
|
|
294
|
+
|
|
295
|
+
for method in all_methods:
|
|
296
|
+
name = str(method.get("name", ""))
|
|
297
|
+
# Constructors don't have return types
|
|
298
|
+
is_constructor = method.get("is_constructor", False)
|
|
299
|
+
return_type = (
|
|
300
|
+
"-"
|
|
301
|
+
if is_constructor
|
|
302
|
+
else str(method.get("return_type", "void"))
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
# Format parameters as "type1 param1, type2 param2"
|
|
306
|
+
params = method.get("parameters", [])
|
|
307
|
+
param_strs = []
|
|
308
|
+
for param in params:
|
|
309
|
+
if isinstance(param, dict):
|
|
310
|
+
param_type = str(param.get("type", "Object"))
|
|
311
|
+
param_name = str(param.get("name", "param"))
|
|
312
|
+
param_strs.append(f"{param_type} {param_name}")
|
|
313
|
+
elif isinstance(param, str):
|
|
314
|
+
param_strs.append(param)
|
|
315
|
+
else:
|
|
316
|
+
param_strs.append(str(param))
|
|
317
|
+
params_str = ", ".join(param_strs)
|
|
318
|
+
|
|
319
|
+
access = str(method.get("visibility", "public"))
|
|
320
|
+
line_num = method.get("line_range", {}).get("start", 0)
|
|
321
|
+
|
|
322
|
+
lines.append(
|
|
323
|
+
f"| {name} | {return_type} | {params_str} | {access} | {line_num} |"
|
|
324
|
+
)
|
|
325
|
+
else:
|
|
326
|
+
lines.append("| Name | Return Type | Parameters | Access | Line |")
|
|
327
|
+
lines.append("|------|-------------|------------|--------|------|")
|
|
328
|
+
lines.append("")
|
|
329
|
+
|
|
330
|
+
# Fields section (required by specification)
|
|
331
|
+
lines.append("## Fields")
|
|
332
|
+
if all_fields:
|
|
333
|
+
lines.append("| Name | Type | Access | Static | Final | Line |")
|
|
334
|
+
lines.append("|------|------|--------|--------|-------|------|")
|
|
335
|
+
|
|
336
|
+
for field in all_fields:
|
|
337
|
+
name = str(field.get("name", ""))
|
|
338
|
+
field_type = str(field.get("type", "Object"))
|
|
339
|
+
access = str(field.get("visibility", "private"))
|
|
340
|
+
|
|
341
|
+
# Check modifiers for static and final
|
|
342
|
+
modifiers = field.get("modifiers", [])
|
|
343
|
+
is_static = "static" in modifiers or field.get("is_static", False)
|
|
344
|
+
is_final = "final" in modifiers or field.get("is_final", False)
|
|
345
|
+
|
|
346
|
+
static_str = "true" if is_static else "false"
|
|
347
|
+
final_str = "true" if is_final else "false"
|
|
348
|
+
|
|
349
|
+
line_num = field.get("line_range", {}).get("start", 0)
|
|
350
|
+
|
|
351
|
+
lines.append(
|
|
352
|
+
f"| {name} | {field_type} | {access} | {static_str} | {final_str} | {line_num} |"
|
|
353
|
+
)
|
|
354
|
+
else:
|
|
355
|
+
lines.append("| Name | Type | Access | Static | Final | Line |")
|
|
356
|
+
lines.append("|------|------|--------|--------|-------|------|")
|
|
357
|
+
lines.append("")
|
|
358
|
+
|
|
359
|
+
# Remove trailing empty lines
|
|
360
|
+
while lines and lines[-1] == "":
|
|
361
|
+
lines.pop()
|
|
362
|
+
|
|
363
|
+
return "\n".join(lines)
|
|
364
|
+
|
|
365
|
+
def _get_class_methods(
|
|
366
|
+
self, data: dict[str, Any], class_line_range: dict[str, int]
|
|
367
|
+
) -> list[dict[str, Any]]:
|
|
368
|
+
"""Get methods that belong to a specific class based on line range, excluding nested classes."""
|
|
369
|
+
methods = data.get("methods", [])
|
|
370
|
+
classes = data.get("classes", [])
|
|
371
|
+
class_methods = []
|
|
372
|
+
|
|
373
|
+
# Get nested class ranges to exclude their methods
|
|
374
|
+
nested_class_ranges = []
|
|
375
|
+
for cls in classes:
|
|
376
|
+
cls_range = cls.get("line_range", {})
|
|
377
|
+
cls_start = cls_range.get("start", 0)
|
|
378
|
+
cls_end = cls_range.get("end", 0)
|
|
379
|
+
|
|
380
|
+
# If this class is nested within the current class range
|
|
381
|
+
if class_line_range.get(
|
|
382
|
+
"start", 0
|
|
383
|
+
) < cls_start and cls_end < class_line_range.get("end", 0):
|
|
384
|
+
nested_class_ranges.append((cls_start, cls_end))
|
|
385
|
+
|
|
386
|
+
for method in methods:
|
|
387
|
+
method_line = method.get("line_range", {}).get("start", 0)
|
|
388
|
+
|
|
389
|
+
# Check if method is within the class range
|
|
390
|
+
if (
|
|
391
|
+
class_line_range.get("start", 0)
|
|
392
|
+
<= method_line
|
|
393
|
+
<= class_line_range.get("end", 0)
|
|
394
|
+
):
|
|
395
|
+
# Check if method is NOT within any nested class
|
|
396
|
+
in_nested_class = False
|
|
397
|
+
for nested_start, nested_end in nested_class_ranges:
|
|
398
|
+
if nested_start <= method_line <= nested_end:
|
|
399
|
+
in_nested_class = True
|
|
400
|
+
break
|
|
401
|
+
|
|
402
|
+
if not in_nested_class:
|
|
403
|
+
class_methods.append(method)
|
|
404
|
+
|
|
405
|
+
return class_methods
|
|
406
|
+
|
|
407
|
+
def _get_class_fields(
|
|
408
|
+
self, data: dict[str, Any], class_line_range: dict[str, int]
|
|
409
|
+
) -> list[dict[str, Any]]:
|
|
410
|
+
"""Get fields that belong to a specific class based on line range, excluding nested classes."""
|
|
411
|
+
fields = data.get("fields", [])
|
|
412
|
+
classes = data.get("classes", [])
|
|
413
|
+
class_fields = []
|
|
414
|
+
|
|
415
|
+
# Get nested class ranges to exclude their fields
|
|
416
|
+
nested_class_ranges = []
|
|
417
|
+
for cls in classes:
|
|
418
|
+
cls_range = cls.get("line_range", {})
|
|
419
|
+
cls_start = cls_range.get("start", 0)
|
|
420
|
+
cls_end = cls_range.get("end", 0)
|
|
421
|
+
|
|
422
|
+
# If this class is nested within the current class range
|
|
423
|
+
if class_line_range.get(
|
|
424
|
+
"start", 0
|
|
425
|
+
) < cls_start and cls_end < class_line_range.get("end", 0):
|
|
426
|
+
nested_class_ranges.append((cls_start, cls_end))
|
|
427
|
+
|
|
428
|
+
for field in fields:
|
|
429
|
+
field_line = field.get("line_range", {}).get("start", 0)
|
|
430
|
+
|
|
431
|
+
# Check if field is within the class range
|
|
432
|
+
if (
|
|
433
|
+
class_line_range.get("start", 0)
|
|
434
|
+
<= field_line
|
|
435
|
+
<= class_line_range.get("end", 0)
|
|
436
|
+
):
|
|
437
|
+
# Check if field is NOT within any nested class
|
|
438
|
+
in_nested_class = False
|
|
439
|
+
for nested_start, nested_end in nested_class_ranges:
|
|
440
|
+
if nested_start <= field_line <= nested_end:
|
|
441
|
+
in_nested_class = True
|
|
442
|
+
break
|
|
443
|
+
|
|
444
|
+
if not in_nested_class:
|
|
445
|
+
class_fields.append(field)
|
|
446
|
+
|
|
447
|
+
return class_fields
|
|
448
|
+
|
|
449
|
+
def _format_class_details(
|
|
450
|
+
self, class_info: dict[str, Any], data: dict[str, Any]
|
|
451
|
+
) -> list[str]:
|
|
452
|
+
"""Format detailed information for a single class."""
|
|
453
|
+
lines = []
|
|
454
|
+
|
|
455
|
+
name = str(class_info.get("name", "Unknown"))
|
|
456
|
+
line_range = class_info.get("line_range", {})
|
|
457
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
458
|
+
|
|
459
|
+
# Class header
|
|
460
|
+
lines.append(f"## {name} ({lines_str})")
|
|
461
|
+
|
|
462
|
+
# Get class-specific methods and fields
|
|
463
|
+
class_methods = self._get_class_methods(data, line_range)
|
|
464
|
+
class_fields = self._get_class_fields(data, line_range)
|
|
465
|
+
|
|
466
|
+
# Fields section
|
|
467
|
+
if class_fields:
|
|
468
|
+
lines.append("### Fields")
|
|
469
|
+
lines.append("| Name | Type | Vis | Modifiers | Line | Doc |")
|
|
470
|
+
lines.append("|------|------|-----|-----------|------|-----|")
|
|
471
|
+
|
|
472
|
+
for field in class_fields:
|
|
473
|
+
name_field = str(field.get("name", ""))
|
|
474
|
+
type_field = str(field.get("type", ""))
|
|
475
|
+
visibility = self._convert_visibility(str(field.get("visibility", "")))
|
|
476
|
+
modifiers = ",".join(field.get("modifiers", []))
|
|
477
|
+
line_num = field.get("line_range", {}).get("start", 0)
|
|
478
|
+
doc = (
|
|
479
|
+
self._extract_doc_summary(str(field.get("javadoc", "")))
|
|
480
|
+
if self.include_javadoc
|
|
481
|
+
else "-"
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
lines.append(
|
|
485
|
+
f"| {name_field} | {type_field} | {visibility} | {modifiers} | {line_num} | {doc} |"
|
|
486
|
+
)
|
|
487
|
+
lines.append("")
|
|
488
|
+
|
|
489
|
+
# Methods section - separate by type
|
|
490
|
+
constructors = [m for m in class_methods if m.get("is_constructor", False)]
|
|
491
|
+
regular_methods = [
|
|
492
|
+
m for m in class_methods if not m.get("is_constructor", False)
|
|
493
|
+
]
|
|
494
|
+
|
|
495
|
+
# Constructors
|
|
496
|
+
if constructors:
|
|
497
|
+
lines.append("### Constructors")
|
|
498
|
+
lines.append("| Constructor | Signature | Vis | Lines | Cx | Doc |")
|
|
499
|
+
lines.append("|-------------|-----------|-----|-------|----|----|")
|
|
500
|
+
|
|
501
|
+
for method in constructors:
|
|
502
|
+
lines.append(self._format_method_row_detailed(method))
|
|
503
|
+
lines.append("")
|
|
504
|
+
|
|
505
|
+
# Methods grouped by visibility
|
|
506
|
+
public_methods = [
|
|
507
|
+
m for m in regular_methods if m.get("visibility", "") == "public"
|
|
508
|
+
]
|
|
509
|
+
protected_methods = [
|
|
510
|
+
m for m in regular_methods if m.get("visibility", "") == "protected"
|
|
511
|
+
]
|
|
512
|
+
package_methods = [
|
|
513
|
+
m for m in regular_methods if m.get("visibility", "") == "package"
|
|
514
|
+
]
|
|
515
|
+
private_methods = [
|
|
516
|
+
m for m in regular_methods if m.get("visibility", "") == "private"
|
|
517
|
+
]
|
|
518
|
+
|
|
519
|
+
for method_group, title in [
|
|
520
|
+
(public_methods, "Public Methods"),
|
|
521
|
+
(protected_methods, "Protected Methods"),
|
|
522
|
+
(package_methods, "Package Methods"),
|
|
523
|
+
(private_methods, "Private Methods"),
|
|
524
|
+
]:
|
|
525
|
+
if method_group:
|
|
526
|
+
lines.append(f"### {title}")
|
|
527
|
+
lines.append("| Method | Signature | Vis | Lines | Cx | Doc |")
|
|
528
|
+
lines.append("|--------|-----------|-----|-------|----|----|")
|
|
529
|
+
|
|
530
|
+
for method in method_group:
|
|
531
|
+
lines.append(self._format_method_row_detailed(method))
|
|
532
|
+
lines.append("")
|
|
533
|
+
|
|
534
|
+
return lines
|
|
535
|
+
|
|
536
|
+
def _format_method_row_detailed(self, method: dict[str, Any]) -> str:
|
|
537
|
+
"""Format method row for detailed class view."""
|
|
538
|
+
name = str(method.get("name", ""))
|
|
539
|
+
signature = self._create_full_signature(method)
|
|
540
|
+
visibility = self._convert_visibility(str(method.get("visibility", "")))
|
|
541
|
+
line_range = method.get("line_range", {})
|
|
542
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
543
|
+
complexity = method.get("complexity_score", 0)
|
|
544
|
+
doc = (
|
|
545
|
+
self._extract_doc_summary(str(method.get("javadoc", "")))
|
|
546
|
+
if self.include_javadoc
|
|
547
|
+
else "-"
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
return f"| {name} | {signature} | {visibility} | {lines_str} | {complexity} | {doc} |"
|
|
551
|
+
|
|
552
|
+
def _format_compact_table(self, data: dict[str, Any]) -> str:
|
|
553
|
+
"""Compact table format - compliant with format specification"""
|
|
554
|
+
lines = []
|
|
555
|
+
|
|
556
|
+
# Header - just class name (no package in compact format)
|
|
557
|
+
classes = data.get("classes", [])
|
|
558
|
+
if classes is None:
|
|
559
|
+
classes = []
|
|
560
|
+
class_name = classes[0].get("name", "Unknown") if classes else "Unknown"
|
|
561
|
+
lines.append(f"# {class_name}")
|
|
562
|
+
lines.append("")
|
|
563
|
+
|
|
564
|
+
# Info section (required by specification)
|
|
565
|
+
classes = data.get("classes", [])
|
|
566
|
+
class_type = classes[0].get("type", "class") if classes else "class"
|
|
567
|
+
methods = data.get("methods", []) or []
|
|
568
|
+
fields = data.get("fields", []) or []
|
|
569
|
+
|
|
570
|
+
lines.append("## Info")
|
|
571
|
+
lines.append("| Property | Value |")
|
|
572
|
+
lines.append("|----------|-------|")
|
|
573
|
+
lines.append(f"| Type | {class_type} |")
|
|
574
|
+
lines.append(f"| Methods | {len(methods)} |")
|
|
575
|
+
lines.append(f"| Fields | {len(fields)} |")
|
|
576
|
+
lines.append("")
|
|
577
|
+
|
|
578
|
+
# Methods section (simplified, required by specification)
|
|
579
|
+
lines.append("## Methods")
|
|
580
|
+
if methods:
|
|
581
|
+
lines.append("| Name | Return Type | Access | Line |")
|
|
582
|
+
lines.append("|------|-------------|--------|------|")
|
|
583
|
+
|
|
584
|
+
for method in methods:
|
|
585
|
+
name = str(method.get("name", ""))
|
|
586
|
+
return_type = str(method.get("return_type", "void"))
|
|
587
|
+
access = str(method.get("visibility", "public"))
|
|
588
|
+
line_num = method.get("line_range", {}).get("start", 0)
|
|
589
|
+
|
|
590
|
+
lines.append(f"| {name} | {return_type} | {access} | {line_num} |")
|
|
591
|
+
else:
|
|
592
|
+
lines.append("| Name | Return Type | Access | Line |")
|
|
593
|
+
lines.append("|------|-------------|--------|------|")
|
|
594
|
+
lines.append("")
|
|
595
|
+
|
|
596
|
+
# Fields section (simplified, required by specification)
|
|
597
|
+
lines.append("## Fields")
|
|
598
|
+
if fields:
|
|
599
|
+
lines.append("| Name | Type | Access | Line |")
|
|
600
|
+
lines.append("|------|------|--------|------|")
|
|
601
|
+
|
|
602
|
+
for field in fields:
|
|
603
|
+
name = str(field.get("name", ""))
|
|
604
|
+
field_type = str(field.get("type", "Object"))
|
|
605
|
+
access = str(field.get("visibility", "private"))
|
|
606
|
+
line_num = field.get("line_range", {}).get("start", 0)
|
|
607
|
+
|
|
608
|
+
lines.append(f"| {name} | {field_type} | {access} | {line_num} |")
|
|
609
|
+
else:
|
|
610
|
+
lines.append("| Name | Type | Access | Line |")
|
|
611
|
+
lines.append("|------|------|--------|------|")
|
|
612
|
+
lines.append("")
|
|
613
|
+
|
|
614
|
+
# Remove trailing empty lines
|
|
615
|
+
while lines and lines[-1] == "":
|
|
616
|
+
lines.pop()
|
|
617
|
+
|
|
618
|
+
return "\n".join(lines)
|
|
619
|
+
|
|
620
|
+
def _format_csv(self, data: dict[str, Any]) -> str:
|
|
621
|
+
"""CSV format - compliant with format specification"""
|
|
622
|
+
output = io.StringIO()
|
|
623
|
+
writer = csv.writer(
|
|
624
|
+
output, lineterminator="\n"
|
|
625
|
+
) # Explicitly specify newline character
|
|
626
|
+
|
|
627
|
+
# Header - specification compliant
|
|
628
|
+
writer.writerow(
|
|
629
|
+
[
|
|
630
|
+
"Type",
|
|
631
|
+
"Name",
|
|
632
|
+
"ReturnType",
|
|
633
|
+
"Parameters",
|
|
634
|
+
"Access",
|
|
635
|
+
"Static",
|
|
636
|
+
"Final",
|
|
637
|
+
"Line",
|
|
638
|
+
]
|
|
639
|
+
)
|
|
640
|
+
|
|
641
|
+
# Class row
|
|
642
|
+
classes = data.get("classes", [])
|
|
643
|
+
if classes:
|
|
644
|
+
for cls in classes:
|
|
645
|
+
writer.writerow(
|
|
646
|
+
[
|
|
647
|
+
str(cls.get("type", "class")),
|
|
648
|
+
str(cls.get("name", "Unknown")),
|
|
649
|
+
"", # No return type for class
|
|
650
|
+
"", # No parameters for class
|
|
651
|
+
str(cls.get("visibility", "public")),
|
|
652
|
+
"false", # Classes are not static
|
|
653
|
+
"true" if "final" in cls.get("modifiers", []) else "false",
|
|
654
|
+
cls.get("line_range", {}).get("start", 0),
|
|
655
|
+
]
|
|
656
|
+
)
|
|
657
|
+
|
|
658
|
+
# Method rows
|
|
659
|
+
for method in data.get("methods", []):
|
|
660
|
+
# Format parameters as "param1:type1;param2:type2"
|
|
661
|
+
params = method.get("parameters", [])
|
|
662
|
+
param_strs = []
|
|
663
|
+
for param in params:
|
|
664
|
+
if isinstance(param, dict):
|
|
665
|
+
param_type = str(param.get("type", "Object"))
|
|
666
|
+
param_name = str(param.get("name", "param"))
|
|
667
|
+
param_strs.append(f"{param_name}:{param_type}")
|
|
668
|
+
elif isinstance(param, str):
|
|
669
|
+
# Handle "type param" format - convert to "param:type"
|
|
670
|
+
parts = param.strip().split()
|
|
671
|
+
if len(parts) >= 2:
|
|
672
|
+
# Everything except last part is type, last part is name
|
|
673
|
+
param_type = " ".join(parts[:-1])
|
|
674
|
+
param_name = parts[-1]
|
|
675
|
+
param_strs.append(f"{param_name}:{param_type}")
|
|
676
|
+
else:
|
|
677
|
+
# Fallback for single-part parameters
|
|
678
|
+
param_strs.append(param)
|
|
679
|
+
else:
|
|
680
|
+
param_strs.append(str(param))
|
|
681
|
+
params_str = ";".join(param_strs)
|
|
682
|
+
|
|
683
|
+
# Check modifiers for static and final
|
|
684
|
+
modifiers = method.get("modifiers", [])
|
|
685
|
+
is_static = "static" in modifiers or method.get("is_static", False)
|
|
686
|
+
is_final = "final" in modifiers or method.get("is_final", False)
|
|
687
|
+
|
|
688
|
+
writer.writerow(
|
|
689
|
+
[
|
|
690
|
+
"constructor" if method.get("is_constructor", False) else "method",
|
|
691
|
+
str(method.get("name", "")),
|
|
692
|
+
str(method.get("return_type", "void")),
|
|
693
|
+
params_str,
|
|
694
|
+
str(method.get("visibility", "public")),
|
|
695
|
+
"true" if is_static else "false",
|
|
696
|
+
"true" if is_final else "false",
|
|
697
|
+
method.get("line_range", {}).get("start", 0),
|
|
698
|
+
]
|
|
699
|
+
)
|
|
700
|
+
|
|
701
|
+
# Field rows
|
|
702
|
+
for field in data.get("fields", []):
|
|
703
|
+
# Check modifiers for static and final
|
|
704
|
+
modifiers = field.get("modifiers", [])
|
|
705
|
+
is_static = "static" in modifiers or field.get("is_static", False)
|
|
706
|
+
is_final = "final" in modifiers or field.get("is_final", False)
|
|
707
|
+
|
|
708
|
+
writer.writerow(
|
|
709
|
+
[
|
|
710
|
+
"field",
|
|
711
|
+
str(field.get("name", "")),
|
|
712
|
+
str(field.get("type", "Object")),
|
|
713
|
+
"", # No parameters for fields
|
|
714
|
+
str(field.get("visibility", "private")),
|
|
715
|
+
"true" if is_static else "false",
|
|
716
|
+
"true" if is_final else "false",
|
|
717
|
+
field.get("line_range", {}).get("start", 0),
|
|
718
|
+
]
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
# Control CSV output newlines
|
|
722
|
+
csv_content = output.getvalue()
|
|
723
|
+
# Unify all newline patterns and remove trailing newlines
|
|
724
|
+
csv_content = csv_content.replace("\r\n", "\n").replace("\r", "\n")
|
|
725
|
+
csv_content = csv_content.rstrip("\n")
|
|
726
|
+
output.close()
|
|
727
|
+
|
|
728
|
+
return csv_content
|
|
729
|
+
|
|
730
|
+
def _create_full_signature(self, method: dict[str, Any]) -> str:
|
|
731
|
+
"""Create complete method signature"""
|
|
732
|
+
params = method.get("parameters", [])
|
|
733
|
+
param_strs = []
|
|
734
|
+
for param in params:
|
|
735
|
+
# Handle both dict and string parameters
|
|
736
|
+
if isinstance(param, dict):
|
|
737
|
+
param_type = str(param.get("type", "Object"))
|
|
738
|
+
param_name = str(param.get("name", "param"))
|
|
739
|
+
param_strs.append(f"{param_name}:{param_type}")
|
|
740
|
+
elif isinstance(param, str):
|
|
741
|
+
# If parameter is already a string, use it directly
|
|
742
|
+
param_strs.append(param)
|
|
743
|
+
else:
|
|
744
|
+
# Fallback for other types
|
|
745
|
+
param_strs.append(str(param))
|
|
746
|
+
|
|
747
|
+
params_str = ",".join(param_strs) # Remove space after comma
|
|
748
|
+
return_type = str(method.get("return_type", "void"))
|
|
749
|
+
|
|
750
|
+
modifiers = []
|
|
751
|
+
if method.get("is_static", False):
|
|
752
|
+
modifiers.append("[static]")
|
|
753
|
+
|
|
754
|
+
modifier_str = " ".join(modifiers)
|
|
755
|
+
signature = f"({params_str}):{return_type}"
|
|
756
|
+
|
|
757
|
+
if modifier_str:
|
|
758
|
+
signature += f" {modifier_str}"
|
|
759
|
+
|
|
760
|
+
return signature
|
|
761
|
+
|
|
762
|
+
def _create_compact_signature(self, method: dict[str, Any]) -> str:
|
|
763
|
+
"""Create compact method signature"""
|
|
764
|
+
params = method.get("parameters", [])
|
|
765
|
+
param_types = []
|
|
766
|
+
for p in params:
|
|
767
|
+
if isinstance(p, dict):
|
|
768
|
+
param_types.append(self._shorten_type(p.get("type", "O")))
|
|
769
|
+
elif isinstance(p, str):
|
|
770
|
+
# If parameter is already a string, shorten it directly
|
|
771
|
+
param_types.append(self._shorten_type(p))
|
|
772
|
+
else:
|
|
773
|
+
# Fallback for other types
|
|
774
|
+
param_types.append(self._shorten_type(str(p)))
|
|
775
|
+
|
|
776
|
+
params_str = ",".join(param_types)
|
|
777
|
+
return_type = self._shorten_type(method.get("return_type", "void"))
|
|
778
|
+
|
|
779
|
+
return f"({params_str}):{return_type}"
|
|
780
|
+
|
|
781
|
+
def _shorten_type(self, type_name: Any) -> str:
|
|
782
|
+
"""Shorten type name"""
|
|
783
|
+
if type_name is None:
|
|
784
|
+
return "O"
|
|
785
|
+
|
|
786
|
+
# Convert non-string types to string
|
|
787
|
+
if not isinstance(type_name, str):
|
|
788
|
+
type_name = str(type_name)
|
|
789
|
+
|
|
790
|
+
type_mapping = {
|
|
791
|
+
"String": "S",
|
|
792
|
+
"int": "i",
|
|
793
|
+
"long": "l",
|
|
794
|
+
"double": "d",
|
|
795
|
+
"boolean": "b",
|
|
796
|
+
"void": "void",
|
|
797
|
+
"Object": "O",
|
|
798
|
+
"Exception": "E",
|
|
799
|
+
"SQLException": "SE",
|
|
800
|
+
"IllegalArgumentException": "IAE",
|
|
801
|
+
"RuntimeException": "RE",
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
# Map<String,Object> -> M<S,O>
|
|
805
|
+
if "Map<" in type_name:
|
|
806
|
+
return str(
|
|
807
|
+
type_name.replace("Map<", "M<")
|
|
808
|
+
.replace("String", "S")
|
|
809
|
+
.replace("Object", "O")
|
|
810
|
+
)
|
|
811
|
+
|
|
812
|
+
# List<String> -> L<S>
|
|
813
|
+
if "List<" in type_name:
|
|
814
|
+
return str(type_name.replace("List<", "L<").replace("String", "S"))
|
|
815
|
+
|
|
816
|
+
# String[] -> S[]
|
|
817
|
+
if "[]" in type_name:
|
|
818
|
+
base_type = type_name.replace("[]", "")
|
|
819
|
+
if base_type:
|
|
820
|
+
return str(type_mapping.get(base_type, base_type[0].upper())) + "[]"
|
|
821
|
+
else:
|
|
822
|
+
return "O[]"
|
|
823
|
+
|
|
824
|
+
return str(type_mapping.get(type_name, type_name))
|
|
825
|
+
|
|
826
|
+
def _convert_visibility(self, visibility: str) -> str:
|
|
827
|
+
"""Convert visibility to symbol"""
|
|
828
|
+
mapping = {"public": "+", "private": "-", "protected": "#", "package": "~"}
|
|
829
|
+
return mapping.get(visibility, visibility)
|
|
830
|
+
|
|
831
|
+
def _extract_doc_summary(self, javadoc: str) -> str:
|
|
832
|
+
"""Extract summary from JavaDoc"""
|
|
833
|
+
if not javadoc:
|
|
834
|
+
return "-"
|
|
835
|
+
|
|
836
|
+
# Remove comment symbols
|
|
837
|
+
clean_doc = (
|
|
838
|
+
javadoc.replace("/**", "").replace("*/", "").replace("*", "").strip()
|
|
839
|
+
)
|
|
840
|
+
|
|
841
|
+
# Get first sentence
|
|
842
|
+
if clean_doc:
|
|
843
|
+
sentences = clean_doc.split(".")
|
|
844
|
+
if sentences:
|
|
845
|
+
return sentences[0].strip()
|
|
846
|
+
|
|
847
|
+
return "-"
|
|
848
|
+
|
|
849
|
+
def _clean_csv_text(self, text: str) -> str:
|
|
850
|
+
"""Clean text for CSV output"""
|
|
851
|
+
if not text or text == "-":
|
|
852
|
+
return "-"
|
|
853
|
+
|
|
854
|
+
# Remove newlines and extra whitespace
|
|
855
|
+
cleaned = " ".join(text.split())
|
|
856
|
+
|
|
857
|
+
# Escape quotes for CSV
|
|
858
|
+
cleaned = cleaned.replace('"', '""')
|
|
859
|
+
|
|
860
|
+
return cleaned
|