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,782 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Table Formatter for Tree-sitter Analyzer
|
|
4
|
+
|
|
5
|
+
Provides table-formatted output for Java code analysis results.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import csv
|
|
9
|
+
import io
|
|
10
|
+
import json
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TableFormatter:
|
|
15
|
+
"""Table formatter for code analysis results"""
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
format_type: str = "full",
|
|
20
|
+
language: str = "java",
|
|
21
|
+
include_javadoc: bool = False,
|
|
22
|
+
):
|
|
23
|
+
self.format_type = format_type
|
|
24
|
+
self.language = language
|
|
25
|
+
self.include_javadoc = include_javadoc
|
|
26
|
+
|
|
27
|
+
def _get_platform_newline(self) -> str:
|
|
28
|
+
"""Get platform-specific newline character"""
|
|
29
|
+
import os
|
|
30
|
+
|
|
31
|
+
return "\r\n" if os.name == "nt" else "\n" # Windows uses \r\n, others use \n
|
|
32
|
+
|
|
33
|
+
def _convert_to_platform_newlines(self, text: str) -> str:
|
|
34
|
+
"""Convert standard \\n to platform-specific newline characters"""
|
|
35
|
+
platform_newline = self._get_platform_newline()
|
|
36
|
+
if platform_newline != "\n":
|
|
37
|
+
return text.replace("\n", platform_newline)
|
|
38
|
+
return text
|
|
39
|
+
|
|
40
|
+
def format_structure(self, structure_data: dict[str, Any]) -> str:
|
|
41
|
+
"""Format structure data as table"""
|
|
42
|
+
if self.format_type == "full":
|
|
43
|
+
result = self._format_full_table(structure_data)
|
|
44
|
+
elif self.format_type == "compact":
|
|
45
|
+
result = self._format_compact_table(structure_data)
|
|
46
|
+
elif self.format_type == "csv":
|
|
47
|
+
result = self._format_csv(structure_data)
|
|
48
|
+
elif self.format_type == "json":
|
|
49
|
+
result = self._format_json(structure_data)
|
|
50
|
+
else:
|
|
51
|
+
raise ValueError(f"Unsupported format type: {self.format_type}")
|
|
52
|
+
|
|
53
|
+
# Finally convert to platform-specific newline characters
|
|
54
|
+
# Skip newline conversion for CSV and JSON formats
|
|
55
|
+
if self.format_type in ["csv", "json"]:
|
|
56
|
+
return result
|
|
57
|
+
|
|
58
|
+
return self._convert_to_platform_newlines(result)
|
|
59
|
+
|
|
60
|
+
def _format_full_table(self, data: dict[str, Any]) -> str:
|
|
61
|
+
"""Full table format - organized by class"""
|
|
62
|
+
lines = []
|
|
63
|
+
|
|
64
|
+
# Header - use language-specific title generation
|
|
65
|
+
title = self._generate_title(data)
|
|
66
|
+
lines.append(f"# {title}")
|
|
67
|
+
lines.append("")
|
|
68
|
+
|
|
69
|
+
# Get classes for later use
|
|
70
|
+
classes = data.get("classes", [])
|
|
71
|
+
if classes is None:
|
|
72
|
+
classes = []
|
|
73
|
+
|
|
74
|
+
# Package info
|
|
75
|
+
package_name = (data.get("package") or {}).get("name", "unknown")
|
|
76
|
+
if package_name and package_name != "unknown":
|
|
77
|
+
lines.append("## Package")
|
|
78
|
+
lines.append(f"`{package_name}`")
|
|
79
|
+
lines.append("")
|
|
80
|
+
|
|
81
|
+
# Imports
|
|
82
|
+
imports = data.get("imports", [])
|
|
83
|
+
if imports:
|
|
84
|
+
lines.append("## Imports")
|
|
85
|
+
lines.append(f"```{self.language}")
|
|
86
|
+
for imp in imports:
|
|
87
|
+
lines.append(str(imp.get("statement", "")))
|
|
88
|
+
lines.append("```")
|
|
89
|
+
lines.append("")
|
|
90
|
+
|
|
91
|
+
# Class Info section (for single class files or empty data)
|
|
92
|
+
if len(classes) == 1 or len(classes) == 0:
|
|
93
|
+
lines.append("## Class Info")
|
|
94
|
+
lines.append("| Property | Value |")
|
|
95
|
+
lines.append("|----------|-------|")
|
|
96
|
+
|
|
97
|
+
# Re-use package_name from above instead of re-fetching
|
|
98
|
+
|
|
99
|
+
if len(classes) == 1:
|
|
100
|
+
class_info = classes[0]
|
|
101
|
+
lines.append(f"| Package | {package_name} |")
|
|
102
|
+
lines.append(f"| Type | {str(class_info.get('type', 'class'))} |")
|
|
103
|
+
lines.append(
|
|
104
|
+
f"| Visibility | {str(class_info.get('visibility', 'public'))} |"
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# Lines
|
|
108
|
+
line_range = class_info.get("line_range", {})
|
|
109
|
+
lines_str = f"{line_range.get('start', 1)}-{line_range.get('end', 50)}"
|
|
110
|
+
lines.append(f"| Lines | {lines_str} |")
|
|
111
|
+
else:
|
|
112
|
+
# Empty data case
|
|
113
|
+
lines.append(f"| Package | {package_name} |")
|
|
114
|
+
lines.append("| Type | class |")
|
|
115
|
+
lines.append("| Visibility | public |")
|
|
116
|
+
lines.append("| Lines | 0-0 |")
|
|
117
|
+
|
|
118
|
+
# Count methods and fields
|
|
119
|
+
all_methods = data.get("methods", []) or []
|
|
120
|
+
all_fields = data.get("fields", []) or []
|
|
121
|
+
lines.append(f"| Total Methods | {len(all_methods)} |")
|
|
122
|
+
lines.append(f"| Total Fields | {len(all_fields)} |")
|
|
123
|
+
lines.append("")
|
|
124
|
+
|
|
125
|
+
# Classes Overview
|
|
126
|
+
if len(classes) > 1:
|
|
127
|
+
lines.append("## Classes Overview")
|
|
128
|
+
lines.append("| Class | Type | Visibility | Lines | Methods | Fields |")
|
|
129
|
+
lines.append("|-------|------|------------|-------|---------|--------|")
|
|
130
|
+
|
|
131
|
+
for class_info in classes:
|
|
132
|
+
name = str(class_info.get("name", "Unknown"))
|
|
133
|
+
class_type = str(class_info.get("type", "class"))
|
|
134
|
+
visibility = str(class_info.get("visibility", "public"))
|
|
135
|
+
line_range = class_info.get("line_range", {})
|
|
136
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
137
|
+
|
|
138
|
+
# Calculate method and field counts for this class
|
|
139
|
+
class_methods = self._get_class_methods(data, line_range)
|
|
140
|
+
class_fields = self._get_class_fields(data, line_range)
|
|
141
|
+
|
|
142
|
+
lines.append(
|
|
143
|
+
f"| {name} | {class_type} | {visibility} | {lines_str} | {len(class_methods)} | {len(class_fields)} |"
|
|
144
|
+
)
|
|
145
|
+
lines.append("")
|
|
146
|
+
|
|
147
|
+
# Detailed class information - organized by class
|
|
148
|
+
for class_info in classes:
|
|
149
|
+
lines.extend(self._format_class_details(class_info, data))
|
|
150
|
+
|
|
151
|
+
# Remove trailing empty lines
|
|
152
|
+
while lines and lines[-1] == "":
|
|
153
|
+
lines.pop()
|
|
154
|
+
|
|
155
|
+
return "\n".join(lines)
|
|
156
|
+
|
|
157
|
+
def _get_class_methods(
|
|
158
|
+
self, data: dict[str, Any], class_line_range: dict[str, int]
|
|
159
|
+
) -> list[dict[str, Any]]:
|
|
160
|
+
"""Get methods that belong to a specific class based on line range, excluding nested classes."""
|
|
161
|
+
methods = data.get("methods", [])
|
|
162
|
+
classes = data.get("classes", [])
|
|
163
|
+
class_methods = []
|
|
164
|
+
|
|
165
|
+
# Get nested class ranges to exclude their methods
|
|
166
|
+
nested_class_ranges = []
|
|
167
|
+
for cls in classes:
|
|
168
|
+
cls_range = cls.get("line_range", {})
|
|
169
|
+
cls_start = cls_range.get("start", 0)
|
|
170
|
+
cls_end = cls_range.get("end", 0)
|
|
171
|
+
|
|
172
|
+
# If this class is nested within the current class range
|
|
173
|
+
if class_line_range.get(
|
|
174
|
+
"start", 0
|
|
175
|
+
) < cls_start and cls_end < class_line_range.get("end", 0):
|
|
176
|
+
nested_class_ranges.append((cls_start, cls_end))
|
|
177
|
+
|
|
178
|
+
for method in methods:
|
|
179
|
+
method_line = method.get("line_range", {}).get("start", 0)
|
|
180
|
+
|
|
181
|
+
# Check if method is within the class range
|
|
182
|
+
if (
|
|
183
|
+
class_line_range.get("start", 0)
|
|
184
|
+
<= method_line
|
|
185
|
+
<= class_line_range.get("end", 0)
|
|
186
|
+
):
|
|
187
|
+
# Check if method is NOT within any nested class
|
|
188
|
+
in_nested_class = False
|
|
189
|
+
for nested_start, nested_end in nested_class_ranges:
|
|
190
|
+
if nested_start <= method_line <= nested_end:
|
|
191
|
+
in_nested_class = True
|
|
192
|
+
break
|
|
193
|
+
|
|
194
|
+
if not in_nested_class:
|
|
195
|
+
class_methods.append(method)
|
|
196
|
+
|
|
197
|
+
return class_methods
|
|
198
|
+
|
|
199
|
+
def _get_class_fields(
|
|
200
|
+
self, data: dict[str, Any], class_line_range: dict[str, int]
|
|
201
|
+
) -> list[dict[str, Any]]:
|
|
202
|
+
"""Get fields that belong to a specific class based on line range, excluding nested classes."""
|
|
203
|
+
fields = data.get("fields", [])
|
|
204
|
+
classes = data.get("classes", [])
|
|
205
|
+
class_fields = []
|
|
206
|
+
|
|
207
|
+
# Get nested class ranges to exclude their fields
|
|
208
|
+
nested_class_ranges = []
|
|
209
|
+
for cls in classes:
|
|
210
|
+
cls_range = cls.get("line_range", {})
|
|
211
|
+
cls_start = cls_range.get("start", 0)
|
|
212
|
+
cls_end = cls_range.get("end", 0)
|
|
213
|
+
|
|
214
|
+
# If this class is nested within the current class range
|
|
215
|
+
if class_line_range.get(
|
|
216
|
+
"start", 0
|
|
217
|
+
) < cls_start and cls_end < class_line_range.get("end", 0):
|
|
218
|
+
nested_class_ranges.append((cls_start, cls_end))
|
|
219
|
+
|
|
220
|
+
for field in fields:
|
|
221
|
+
field_line = field.get("line_range", {}).get("start", 0)
|
|
222
|
+
|
|
223
|
+
# Check if field is within the class range
|
|
224
|
+
if (
|
|
225
|
+
class_line_range.get("start", 0)
|
|
226
|
+
<= field_line
|
|
227
|
+
<= class_line_range.get("end", 0)
|
|
228
|
+
):
|
|
229
|
+
# Check if field is NOT within any nested class
|
|
230
|
+
in_nested_class = False
|
|
231
|
+
for nested_start, nested_end in nested_class_ranges:
|
|
232
|
+
if nested_start <= field_line <= nested_end:
|
|
233
|
+
in_nested_class = True
|
|
234
|
+
break
|
|
235
|
+
|
|
236
|
+
if not in_nested_class:
|
|
237
|
+
class_fields.append(field)
|
|
238
|
+
|
|
239
|
+
return class_fields
|
|
240
|
+
|
|
241
|
+
def _format_class_details(
|
|
242
|
+
self, class_info: dict[str, Any], data: dict[str, Any]
|
|
243
|
+
) -> list[str]:
|
|
244
|
+
"""Format detailed information for a single class."""
|
|
245
|
+
lines = []
|
|
246
|
+
|
|
247
|
+
name = str(class_info.get("name", "Unknown"))
|
|
248
|
+
line_range = class_info.get("line_range", {})
|
|
249
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
250
|
+
|
|
251
|
+
# Class header
|
|
252
|
+
lines.append(f"## {name} ({lines_str})")
|
|
253
|
+
|
|
254
|
+
# Get class-specific methods and fields
|
|
255
|
+
class_methods = self._get_class_methods(data, line_range)
|
|
256
|
+
class_fields = self._get_class_fields(data, line_range)
|
|
257
|
+
|
|
258
|
+
# Fields section
|
|
259
|
+
if class_fields:
|
|
260
|
+
lines.append("### Fields")
|
|
261
|
+
lines.append("| Name | Type | Vis | Modifiers | Line | Doc |")
|
|
262
|
+
lines.append("|------|------|-----|-----------|------|-----|")
|
|
263
|
+
|
|
264
|
+
for field in class_fields:
|
|
265
|
+
name_field = str(field.get("name", ""))
|
|
266
|
+
type_field = str(field.get("type", ""))
|
|
267
|
+
visibility = self._convert_visibility(str(field.get("visibility", "")))
|
|
268
|
+
modifiers = ",".join(field.get("modifiers", []))
|
|
269
|
+
line_num = field.get("line_range", {}).get("start", 0)
|
|
270
|
+
doc = (
|
|
271
|
+
self._extract_doc_summary(str(field.get("javadoc", "")))
|
|
272
|
+
if self.include_javadoc
|
|
273
|
+
else "-"
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
lines.append(
|
|
277
|
+
f"| {name_field} | {type_field} | {visibility} | {modifiers} | {line_num} | {doc} |"
|
|
278
|
+
)
|
|
279
|
+
lines.append("")
|
|
280
|
+
|
|
281
|
+
# Methods section - separate by type
|
|
282
|
+
constructors = [m for m in class_methods if m.get("is_constructor", False)]
|
|
283
|
+
regular_methods = [
|
|
284
|
+
m for m in class_methods if not m.get("is_constructor", False)
|
|
285
|
+
]
|
|
286
|
+
|
|
287
|
+
# Constructors
|
|
288
|
+
if constructors:
|
|
289
|
+
lines.append("### Constructors")
|
|
290
|
+
lines.append("| Constructor | Signature | Vis | Lines | Cx | Doc |")
|
|
291
|
+
lines.append("|-------------|-----------|-----|-------|----|----|")
|
|
292
|
+
|
|
293
|
+
for method in constructors:
|
|
294
|
+
lines.append(self._format_method_row_detailed(method))
|
|
295
|
+
lines.append("")
|
|
296
|
+
|
|
297
|
+
# Methods grouped by visibility
|
|
298
|
+
public_methods = [
|
|
299
|
+
m for m in regular_methods if m.get("visibility", "") == "public"
|
|
300
|
+
]
|
|
301
|
+
protected_methods = [
|
|
302
|
+
m for m in regular_methods if m.get("visibility", "") == "protected"
|
|
303
|
+
]
|
|
304
|
+
package_methods = [
|
|
305
|
+
m for m in regular_methods if m.get("visibility", "") == "package"
|
|
306
|
+
]
|
|
307
|
+
private_methods = [
|
|
308
|
+
m for m in regular_methods if m.get("visibility", "") == "private"
|
|
309
|
+
]
|
|
310
|
+
|
|
311
|
+
for method_group, title in [
|
|
312
|
+
(public_methods, "Public Methods"),
|
|
313
|
+
(protected_methods, "Protected Methods"),
|
|
314
|
+
(package_methods, "Package Methods"),
|
|
315
|
+
(private_methods, "Private Methods"),
|
|
316
|
+
]:
|
|
317
|
+
if method_group:
|
|
318
|
+
lines.append(f"### {title}")
|
|
319
|
+
lines.append("| Method | Signature | Vis | Lines | Cx | Doc |")
|
|
320
|
+
lines.append("|--------|-----------|-----|-------|----|----|")
|
|
321
|
+
|
|
322
|
+
for method in method_group:
|
|
323
|
+
lines.append(self._format_method_row_detailed(method))
|
|
324
|
+
lines.append("")
|
|
325
|
+
|
|
326
|
+
return lines
|
|
327
|
+
|
|
328
|
+
def _format_method_row_detailed(self, method: dict[str, Any]) -> str:
|
|
329
|
+
"""Format method row for detailed class view."""
|
|
330
|
+
name = str(method.get("name", ""))
|
|
331
|
+
signature = self._create_full_signature(method)
|
|
332
|
+
visibility = self._convert_visibility(str(method.get("visibility", "")))
|
|
333
|
+
line_range = method.get("line_range", {})
|
|
334
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
335
|
+
complexity = method.get("complexity_score", 0)
|
|
336
|
+
doc = (
|
|
337
|
+
self._extract_doc_summary(str(method.get("javadoc", "")))
|
|
338
|
+
if self.include_javadoc
|
|
339
|
+
else "-"
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
return f"| {name} | {signature} | {visibility} | {lines_str} | {complexity} | {doc} |"
|
|
343
|
+
|
|
344
|
+
def _format_traditional_sections(self, data: dict[str, Any]) -> list[str]:
|
|
345
|
+
"""Format traditional sections when no classes are found."""
|
|
346
|
+
lines = []
|
|
347
|
+
|
|
348
|
+
# Traditional class info
|
|
349
|
+
lines.append("## Class Info")
|
|
350
|
+
lines.append("| Property | Value |")
|
|
351
|
+
lines.append("|----------|-------|")
|
|
352
|
+
|
|
353
|
+
package_name = (data.get("package") or {}).get("name", "unknown")
|
|
354
|
+
class_info = data.get("classes", [{}])[0] if data.get("classes") else {}
|
|
355
|
+
stats = data.get("statistics") or {}
|
|
356
|
+
|
|
357
|
+
lines.append(f"| Package | {package_name} |")
|
|
358
|
+
lines.append(f"| Type | {str(class_info.get('type', 'class'))} |")
|
|
359
|
+
lines.append(f"| Visibility | {str(class_info.get('visibility', 'public'))} |")
|
|
360
|
+
lines.append(
|
|
361
|
+
f"| Lines | {class_info.get('line_range', {}).get('start', 0)}-{class_info.get('line_range', {}).get('end', 0)} |"
|
|
362
|
+
)
|
|
363
|
+
lines.append(f"| Total Methods | {stats.get('method_count', 0)} |")
|
|
364
|
+
lines.append(f"| Total Fields | {stats.get('field_count', 0)} |")
|
|
365
|
+
lines.append("")
|
|
366
|
+
|
|
367
|
+
# Fields
|
|
368
|
+
fields = data.get("fields", [])
|
|
369
|
+
if fields:
|
|
370
|
+
lines.append("## Fields")
|
|
371
|
+
lines.append("| Name | Type | Vis | Modifiers | Line | Doc |")
|
|
372
|
+
lines.append("|------|------|-----|-----------|------|-----|")
|
|
373
|
+
|
|
374
|
+
for field in fields:
|
|
375
|
+
name = str(field.get("name", ""))
|
|
376
|
+
field_type = str(field.get("type", ""))
|
|
377
|
+
visibility = self._convert_visibility(str(field.get("visibility", "")))
|
|
378
|
+
modifiers = ",".join([str(m) for m in field.get("modifiers", [])])
|
|
379
|
+
line = field.get("line_range", {}).get("start", 0)
|
|
380
|
+
doc = (
|
|
381
|
+
self._extract_doc_summary(str(field.get("javadoc", "")))
|
|
382
|
+
if self.include_javadoc
|
|
383
|
+
else "-"
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
lines.append(
|
|
387
|
+
f"| {name} | {field_type} | {visibility} | {modifiers} | {line} | {doc} |"
|
|
388
|
+
)
|
|
389
|
+
lines.append("")
|
|
390
|
+
|
|
391
|
+
# Methods by type
|
|
392
|
+
methods = data.get("methods", [])
|
|
393
|
+
constructors = [m for m in methods if m.get("is_constructor", False)]
|
|
394
|
+
regular_methods = [m for m in methods if not m.get("is_constructor", False)]
|
|
395
|
+
|
|
396
|
+
# Constructors
|
|
397
|
+
if constructors:
|
|
398
|
+
lines.append("## Constructor")
|
|
399
|
+
lines.append("| Method | Signature | Vis | Lines | Cols | Cx | Doc |")
|
|
400
|
+
lines.append("|--------|-----------|-----|-------|------|----|----|")
|
|
401
|
+
for method in constructors:
|
|
402
|
+
lines.append(self._format_method_row(method))
|
|
403
|
+
lines.append("")
|
|
404
|
+
|
|
405
|
+
# Methods by visibility
|
|
406
|
+
for visibility, title in [
|
|
407
|
+
("public", "Public Methods"),
|
|
408
|
+
("private", "Private Methods"),
|
|
409
|
+
]:
|
|
410
|
+
visibility_methods = [
|
|
411
|
+
m for m in regular_methods if str(m.get("visibility")) == visibility
|
|
412
|
+
]
|
|
413
|
+
if visibility_methods:
|
|
414
|
+
lines.append(f"## {title}")
|
|
415
|
+
lines.append("| Method | Signature | Vis | Lines | Cols | Cx | Doc |")
|
|
416
|
+
lines.append("|--------|-----------|-----|-------|------|----|----|")
|
|
417
|
+
for method in visibility_methods:
|
|
418
|
+
lines.append(self._format_method_row(method))
|
|
419
|
+
lines.append("")
|
|
420
|
+
|
|
421
|
+
return lines
|
|
422
|
+
|
|
423
|
+
def _generate_title(self, data: dict[str, Any]) -> str:
|
|
424
|
+
"""
|
|
425
|
+
Generate document title based on language and structure.
|
|
426
|
+
|
|
427
|
+
Args:
|
|
428
|
+
data: Analysis result dictionary containing classes, package, file_path
|
|
429
|
+
|
|
430
|
+
Returns:
|
|
431
|
+
Formatted title string (without leading "# ")
|
|
432
|
+
"""
|
|
433
|
+
language = self.language.lower()
|
|
434
|
+
package_name = (data.get("package") or {}).get("name", "")
|
|
435
|
+
classes = data.get("classes", []) or []
|
|
436
|
+
file_path = data.get("file_path", "")
|
|
437
|
+
|
|
438
|
+
# Extract filename without extension
|
|
439
|
+
filename = self._extract_filename(file_path)
|
|
440
|
+
|
|
441
|
+
if language == "java":
|
|
442
|
+
return self._generate_java_title(package_name, classes, filename)
|
|
443
|
+
elif language == "python":
|
|
444
|
+
return self._generate_python_title(filename)
|
|
445
|
+
elif language in ["javascript", "typescript", "js", "ts"]:
|
|
446
|
+
return self._generate_js_ts_title(classes, filename)
|
|
447
|
+
else:
|
|
448
|
+
# Default fallback
|
|
449
|
+
return self._generate_default_title(package_name, classes, filename)
|
|
450
|
+
|
|
451
|
+
def _generate_java_title(
|
|
452
|
+
self, package_name: str, classes: list, filename: str
|
|
453
|
+
) -> str:
|
|
454
|
+
"""Generate title for Java files."""
|
|
455
|
+
if len(classes) == 1:
|
|
456
|
+
# Single class: use package.ClassName format
|
|
457
|
+
class_name = classes[0].get("name", "Unknown")
|
|
458
|
+
if package_name and package_name != "unknown":
|
|
459
|
+
return f"{package_name}.{class_name}"
|
|
460
|
+
return class_name
|
|
461
|
+
else:
|
|
462
|
+
# Multiple classes or no classes: use package.filename format
|
|
463
|
+
if package_name and package_name != "unknown":
|
|
464
|
+
return f"{package_name}.{filename}"
|
|
465
|
+
return filename
|
|
466
|
+
|
|
467
|
+
def _generate_python_title(self, filename: str) -> str:
|
|
468
|
+
"""Generate title for Python files."""
|
|
469
|
+
return f"Module: {filename}"
|
|
470
|
+
|
|
471
|
+
def _generate_js_ts_title(self, classes: list, filename: str) -> str:
|
|
472
|
+
"""Generate title for JavaScript/TypeScript files."""
|
|
473
|
+
if classes:
|
|
474
|
+
# Use primary (first) class name
|
|
475
|
+
return classes[0].get("name", filename)
|
|
476
|
+
return filename
|
|
477
|
+
|
|
478
|
+
def _generate_default_title(
|
|
479
|
+
self, package_name: str, classes: list, filename: str
|
|
480
|
+
) -> str:
|
|
481
|
+
"""Generate default title for unsupported languages."""
|
|
482
|
+
if len(classes) == 1:
|
|
483
|
+
class_name = classes[0].get("name", "Unknown")
|
|
484
|
+
if package_name and package_name != "unknown":
|
|
485
|
+
return f"{package_name}.{class_name}"
|
|
486
|
+
return class_name
|
|
487
|
+
return filename
|
|
488
|
+
|
|
489
|
+
def _extract_filename(self, file_path: str) -> str:
|
|
490
|
+
"""Extract filename without extension from file path."""
|
|
491
|
+
if not file_path or file_path == "Unknown":
|
|
492
|
+
return "unknown"
|
|
493
|
+
|
|
494
|
+
# Get basename
|
|
495
|
+
filename = file_path.split("/")[-1].split("\\")[-1]
|
|
496
|
+
|
|
497
|
+
# Remove common extensions
|
|
498
|
+
for ext in [".java", ".py", ".js", ".ts", ".tsx", ".jsx"]:
|
|
499
|
+
if filename.endswith(ext):
|
|
500
|
+
filename = filename[: -len(ext)]
|
|
501
|
+
break
|
|
502
|
+
|
|
503
|
+
return filename or "unknown"
|
|
504
|
+
|
|
505
|
+
def _format_compact_table(self, data: dict[str, Any]) -> str:
|
|
506
|
+
"""Compact table format"""
|
|
507
|
+
lines = []
|
|
508
|
+
|
|
509
|
+
# Header - use language-specific title generation
|
|
510
|
+
title = self._generate_title(data)
|
|
511
|
+
lines.append(f"# {title}")
|
|
512
|
+
lines.append("")
|
|
513
|
+
|
|
514
|
+
# Basic information
|
|
515
|
+
stats = data.get("statistics") or {}
|
|
516
|
+
package_name = (data.get("package") or {}).get("name", "unknown")
|
|
517
|
+
lines.append("## Info")
|
|
518
|
+
lines.append("| Property | Value |")
|
|
519
|
+
lines.append("|----------|-------|")
|
|
520
|
+
lines.append(f"| Package | {package_name} |")
|
|
521
|
+
lines.append(f"| Methods | {stats.get('method_count', 0)} |")
|
|
522
|
+
lines.append(f"| Fields | {stats.get('field_count', 0)} |")
|
|
523
|
+
lines.append("")
|
|
524
|
+
|
|
525
|
+
# Methods (simplified version)
|
|
526
|
+
methods = data.get("methods", [])
|
|
527
|
+
if methods is None:
|
|
528
|
+
methods = []
|
|
529
|
+
if methods:
|
|
530
|
+
lines.append("## Methods")
|
|
531
|
+
lines.append("| Method | Sig | V | L | Cx | Doc |")
|
|
532
|
+
lines.append("|--------|-----|---|---|----|----|")
|
|
533
|
+
|
|
534
|
+
for method in methods:
|
|
535
|
+
name = str(method.get("name", ""))
|
|
536
|
+
signature = self._create_compact_signature(method)
|
|
537
|
+
visibility = self._convert_visibility(str(method.get("visibility", "")))
|
|
538
|
+
line_range = method.get("line_range", {})
|
|
539
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
540
|
+
complexity = method.get("complexity_score", 0)
|
|
541
|
+
doc = self._clean_csv_text(
|
|
542
|
+
self._extract_doc_summary(str(method.get("javadoc", "")))
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
lines.append(
|
|
546
|
+
f"| {name} | {signature} | {visibility} | {lines_str} | {complexity} | {doc} |"
|
|
547
|
+
)
|
|
548
|
+
lines.append("")
|
|
549
|
+
|
|
550
|
+
# Remove trailing empty lines
|
|
551
|
+
while lines and lines[-1] == "":
|
|
552
|
+
lines.pop()
|
|
553
|
+
|
|
554
|
+
return "\n".join(lines)
|
|
555
|
+
|
|
556
|
+
def _format_csv(self, data: dict[str, Any]) -> str:
|
|
557
|
+
"""CSV format"""
|
|
558
|
+
output = io.StringIO()
|
|
559
|
+
writer = csv.writer(
|
|
560
|
+
output, lineterminator="\n"
|
|
561
|
+
) # Explicitly specify newline character
|
|
562
|
+
|
|
563
|
+
# Header
|
|
564
|
+
writer.writerow(
|
|
565
|
+
["Type", "Name", "Signature", "Visibility", "Lines", "Complexity", "Doc"]
|
|
566
|
+
)
|
|
567
|
+
|
|
568
|
+
# Fields
|
|
569
|
+
for field in data.get("fields", []):
|
|
570
|
+
writer.writerow(
|
|
571
|
+
[
|
|
572
|
+
"Field",
|
|
573
|
+
str(field.get("name", "")),
|
|
574
|
+
f"{str(field.get('name', ''))}:{str(field.get('type', ''))}",
|
|
575
|
+
str(field.get("visibility", "")),
|
|
576
|
+
f"{field.get('line_range', {}).get('start', 0)}-{field.get('line_range', {}).get('end', 0)}",
|
|
577
|
+
"",
|
|
578
|
+
self._clean_csv_text(
|
|
579
|
+
self._extract_doc_summary(str(field.get("javadoc", "")))
|
|
580
|
+
),
|
|
581
|
+
]
|
|
582
|
+
)
|
|
583
|
+
|
|
584
|
+
# Methods
|
|
585
|
+
for method in data.get("methods", []):
|
|
586
|
+
writer.writerow(
|
|
587
|
+
[
|
|
588
|
+
"Constructor" if method.get("is_constructor", False) else "Method",
|
|
589
|
+
str(method.get("name", "")),
|
|
590
|
+
self._clean_csv_text(self._create_full_signature(method)),
|
|
591
|
+
str(method.get("visibility", "")),
|
|
592
|
+
f"{method.get('line_range', {}).get('start', 0)}-{method.get('line_range', {}).get('end', 0)}",
|
|
593
|
+
method.get("complexity_score", 0),
|
|
594
|
+
self._clean_csv_text(
|
|
595
|
+
self._extract_doc_summary(str(method.get("javadoc", "")))
|
|
596
|
+
),
|
|
597
|
+
]
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
# Completely control CSV output newlines
|
|
601
|
+
csv_content = output.getvalue()
|
|
602
|
+
# Unify all newline patterns and remove trailing newlines
|
|
603
|
+
csv_content = csv_content.replace("\r\n", "\n").replace("\r", "\n")
|
|
604
|
+
csv_content = csv_content.rstrip("\n")
|
|
605
|
+
output.close()
|
|
606
|
+
|
|
607
|
+
return csv_content
|
|
608
|
+
|
|
609
|
+
def _format_json(self, data: dict[str, Any]) -> str:
|
|
610
|
+
"""JSON format"""
|
|
611
|
+
# Create a clean JSON structure with all the analysis data
|
|
612
|
+
json_data = {
|
|
613
|
+
"file_info": {
|
|
614
|
+
"file_path": data.get("file_path", ""),
|
|
615
|
+
"language": data.get("language", ""),
|
|
616
|
+
"package": data.get("package"),
|
|
617
|
+
},
|
|
618
|
+
"statistics": data.get("statistics", {}),
|
|
619
|
+
"elements": {
|
|
620
|
+
"classes": data.get("classes", []),
|
|
621
|
+
"methods": data.get("methods", []),
|
|
622
|
+
"fields": data.get("fields", []),
|
|
623
|
+
"imports": data.get("imports", []),
|
|
624
|
+
},
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
# Return formatted JSON with proper indentation
|
|
628
|
+
return json.dumps(json_data, indent=2, ensure_ascii=False)
|
|
629
|
+
|
|
630
|
+
def _format_method_row(self, method: dict[str, Any]) -> str:
|
|
631
|
+
"""Format method row"""
|
|
632
|
+
name = str(method.get("name", ""))
|
|
633
|
+
signature = self._create_full_signature(method)
|
|
634
|
+
visibility = self._convert_visibility(str(method.get("visibility", "")))
|
|
635
|
+
line_range = method.get("line_range", {})
|
|
636
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
637
|
+
cols_str = (
|
|
638
|
+
"5-6" # Default value (actual implementation should get accurate values)
|
|
639
|
+
)
|
|
640
|
+
complexity = method.get("complexity_score", 0)
|
|
641
|
+
if self.include_javadoc:
|
|
642
|
+
doc = self._clean_csv_text(
|
|
643
|
+
self._extract_doc_summary(str(method.get("javadoc", "")))
|
|
644
|
+
)
|
|
645
|
+
else:
|
|
646
|
+
doc = "-"
|
|
647
|
+
|
|
648
|
+
return f"| {name} | {signature} | {visibility} | {lines_str} | {cols_str} | {complexity} | {doc} |"
|
|
649
|
+
|
|
650
|
+
def _create_full_signature(self, method: dict[str, Any]) -> str:
|
|
651
|
+
"""Create complete method signature"""
|
|
652
|
+
params = method.get("parameters", [])
|
|
653
|
+
param_strs = []
|
|
654
|
+
for param in params:
|
|
655
|
+
param_type = str(param.get("type", "Object"))
|
|
656
|
+
param_name = str(param.get("name", "param"))
|
|
657
|
+
param_strs.append(f"{param_name}:{param_type}")
|
|
658
|
+
|
|
659
|
+
params_str = ", ".join(param_strs)
|
|
660
|
+
return_type = str(method.get("return_type", "void"))
|
|
661
|
+
|
|
662
|
+
modifiers = []
|
|
663
|
+
if method.get("is_static", False):
|
|
664
|
+
modifiers.append("[static]")
|
|
665
|
+
|
|
666
|
+
modifier_str = " ".join(modifiers)
|
|
667
|
+
signature = f"({params_str}):{return_type}"
|
|
668
|
+
|
|
669
|
+
if modifier_str:
|
|
670
|
+
signature += f" {modifier_str}"
|
|
671
|
+
|
|
672
|
+
return signature
|
|
673
|
+
|
|
674
|
+
def _create_compact_signature(self, method: dict[str, Any]) -> str:
|
|
675
|
+
"""Create compact method signature"""
|
|
676
|
+
params = method.get("parameters", [])
|
|
677
|
+
param_types = [self._shorten_type(p.get("type", "O")) for p in params]
|
|
678
|
+
params_str = ",".join(param_types)
|
|
679
|
+
return_type = self._shorten_type(method.get("return_type", "void"))
|
|
680
|
+
|
|
681
|
+
return f"({params_str}):{return_type}"
|
|
682
|
+
|
|
683
|
+
def _shorten_type(self, type_name: Any) -> str:
|
|
684
|
+
"""Shorten type name"""
|
|
685
|
+
if type_name is None:
|
|
686
|
+
return "O"
|
|
687
|
+
|
|
688
|
+
# Convert non-string types to string
|
|
689
|
+
if not isinstance(type_name, str):
|
|
690
|
+
type_name = str(type_name)
|
|
691
|
+
|
|
692
|
+
# At this point, type_name is guaranteed to be a string
|
|
693
|
+
# Defensive check (avoid using assert for runtime safety and security checks)
|
|
694
|
+
if not isinstance(type_name, str):
|
|
695
|
+
try:
|
|
696
|
+
type_name = str(type_name)
|
|
697
|
+
except Exception:
|
|
698
|
+
type_name = "O"
|
|
699
|
+
|
|
700
|
+
type_mapping = {
|
|
701
|
+
"String": "S",
|
|
702
|
+
"int": "i",
|
|
703
|
+
"long": "l",
|
|
704
|
+
"double": "d",
|
|
705
|
+
"boolean": "b",
|
|
706
|
+
"void": "void",
|
|
707
|
+
"Object": "O",
|
|
708
|
+
"Exception": "E",
|
|
709
|
+
"SQLException": "SE",
|
|
710
|
+
"IllegalArgumentException": "IAE",
|
|
711
|
+
"RuntimeException": "RE",
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
# Map<String,Object> -> M<S,O>
|
|
715
|
+
if "Map<" in type_name:
|
|
716
|
+
return str(
|
|
717
|
+
type_name.replace("Map<", "M<")
|
|
718
|
+
.replace("String", "S")
|
|
719
|
+
.replace("Object", "O")
|
|
720
|
+
)
|
|
721
|
+
|
|
722
|
+
# List<String> -> L<S>
|
|
723
|
+
if "List<" in type_name:
|
|
724
|
+
return str(type_name.replace("List<", "L<").replace("String", "S"))
|
|
725
|
+
|
|
726
|
+
# String[] -> S[]
|
|
727
|
+
if "[]" in type_name:
|
|
728
|
+
base_type = type_name.replace("[]", "")
|
|
729
|
+
if base_type:
|
|
730
|
+
return str(type_mapping.get(base_type, base_type[0].upper())) + "[]"
|
|
731
|
+
else:
|
|
732
|
+
return "O[]"
|
|
733
|
+
|
|
734
|
+
return str(type_mapping.get(type_name, type_name))
|
|
735
|
+
|
|
736
|
+
def _convert_visibility(self, visibility: str) -> str:
|
|
737
|
+
"""Convert visibility to symbol"""
|
|
738
|
+
mapping = {"public": "+", "private": "-", "protected": "#", "package": "~"}
|
|
739
|
+
return mapping.get(visibility, visibility)
|
|
740
|
+
|
|
741
|
+
def _extract_doc_summary(self, javadoc: str) -> str:
|
|
742
|
+
"""Extract summary from JavaDoc"""
|
|
743
|
+
if not javadoc:
|
|
744
|
+
return "-"
|
|
745
|
+
|
|
746
|
+
# Remove comment symbols
|
|
747
|
+
clean_doc = (
|
|
748
|
+
javadoc.replace("/**", "").replace("*/", "").replace("*", "").strip()
|
|
749
|
+
)
|
|
750
|
+
|
|
751
|
+
# Get first line (use standard \\n only)
|
|
752
|
+
lines = clean_doc.split("\n")
|
|
753
|
+
first_line = lines[0].strip()
|
|
754
|
+
|
|
755
|
+
# Truncate if too long
|
|
756
|
+
if len(first_line) > 50:
|
|
757
|
+
first_line = first_line[:47] + "..."
|
|
758
|
+
|
|
759
|
+
# Escape characters that cause problems in Markdown tables (use standard \\n only)
|
|
760
|
+
return first_line.replace("|", "\\|").replace("\n", " ")
|
|
761
|
+
|
|
762
|
+
def _clean_csv_text(self, text: str) -> str:
|
|
763
|
+
"""Text cleaning for CSV format"""
|
|
764
|
+
if not text:
|
|
765
|
+
return ""
|
|
766
|
+
|
|
767
|
+
# Replace all newline characters with spaces
|
|
768
|
+
cleaned = text.replace("\r\n", " ").replace("\r", " ").replace("\n", " ")
|
|
769
|
+
# Convert consecutive spaces to single space
|
|
770
|
+
cleaned = " ".join(cleaned.split())
|
|
771
|
+
# Escape characters that cause problems in CSV
|
|
772
|
+
cleaned = cleaned.replace('"', '""') # Escape double quotes
|
|
773
|
+
|
|
774
|
+
return cleaned
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
def create_table_formatter(
|
|
778
|
+
format_type: str, language: str = "java", include_javadoc: bool = False
|
|
779
|
+
) -> "TableFormatter":
|
|
780
|
+
"""Create table formatter (using new factory)"""
|
|
781
|
+
# Create TableFormatter directly (for JavaDoc support)
|
|
782
|
+
return TableFormatter(format_type, language, include_javadoc)
|