tree-sitter-analyzer 0.7.0__py3-none-any.whl → 0.8.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.
Potentially problematic release.
This version of tree-sitter-analyzer might be problematic. Click here for more details.
- tree_sitter_analyzer/__init__.py +132 -132
- tree_sitter_analyzer/__main__.py +11 -11
- tree_sitter_analyzer/api.py +533 -533
- tree_sitter_analyzer/cli/__init__.py +39 -39
- tree_sitter_analyzer/cli/__main__.py +12 -12
- tree_sitter_analyzer/cli/commands/__init__.py +26 -26
- tree_sitter_analyzer/cli/commands/advanced_command.py +88 -88
- tree_sitter_analyzer/cli/commands/base_command.py +178 -160
- tree_sitter_analyzer/cli/commands/default_command.py +18 -18
- tree_sitter_analyzer/cli/commands/partial_read_command.py +141 -141
- tree_sitter_analyzer/cli/commands/query_command.py +88 -81
- tree_sitter_analyzer/cli/commands/structure_command.py +138 -138
- tree_sitter_analyzer/cli/commands/summary_command.py +101 -101
- tree_sitter_analyzer/cli/commands/table_command.py +235 -235
- tree_sitter_analyzer/cli/info_commands.py +121 -121
- tree_sitter_analyzer/cli_main.py +303 -297
- tree_sitter_analyzer/core/__init__.py +15 -15
- tree_sitter_analyzer/core/analysis_engine.py +580 -555
- tree_sitter_analyzer/core/cache_service.py +320 -320
- tree_sitter_analyzer/core/engine.py +566 -566
- tree_sitter_analyzer/core/parser.py +293 -293
- tree_sitter_analyzer/encoding_utils.py +459 -459
- tree_sitter_analyzer/exceptions.py +406 -337
- tree_sitter_analyzer/file_handler.py +210 -210
- tree_sitter_analyzer/formatters/__init__.py +1 -1
- tree_sitter_analyzer/formatters/base_formatter.py +167 -167
- tree_sitter_analyzer/formatters/formatter_factory.py +78 -78
- tree_sitter_analyzer/interfaces/__init__.py +9 -9
- tree_sitter_analyzer/interfaces/cli.py +528 -528
- tree_sitter_analyzer/interfaces/cli_adapter.py +343 -343
- tree_sitter_analyzer/interfaces/mcp_adapter.py +206 -206
- tree_sitter_analyzer/interfaces/mcp_server.py +425 -405
- tree_sitter_analyzer/languages/__init__.py +10 -10
- tree_sitter_analyzer/languages/javascript_plugin.py +446 -446
- tree_sitter_analyzer/languages/python_plugin.py +755 -755
- tree_sitter_analyzer/mcp/__init__.py +31 -31
- tree_sitter_analyzer/mcp/resources/__init__.py +44 -44
- tree_sitter_analyzer/mcp/resources/code_file_resource.py +209 -209
- tree_sitter_analyzer/mcp/server.py +408 -333
- tree_sitter_analyzer/mcp/tools/__init__.py +30 -30
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +673 -654
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +247 -247
- tree_sitter_analyzer/mcp/tools/base_tool.py +54 -54
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +308 -300
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +379 -362
- tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +559 -543
- tree_sitter_analyzer/mcp/utils/__init__.py +107 -107
- tree_sitter_analyzer/mcp/utils/error_handler.py +549 -549
- tree_sitter_analyzer/output_manager.py +253 -253
- tree_sitter_analyzer/plugins/__init__.py +280 -280
- tree_sitter_analyzer/plugins/base.py +529 -529
- tree_sitter_analyzer/plugins/manager.py +379 -379
- tree_sitter_analyzer/project_detector.py +317 -0
- tree_sitter_analyzer/queries/__init__.py +26 -26
- tree_sitter_analyzer/queries/java.py +391 -391
- tree_sitter_analyzer/queries/javascript.py +148 -148
- tree_sitter_analyzer/queries/python.py +285 -285
- tree_sitter_analyzer/queries/typescript.py +229 -229
- tree_sitter_analyzer/query_loader.py +257 -257
- tree_sitter_analyzer/security/__init__.py +22 -0
- tree_sitter_analyzer/security/boundary_manager.py +237 -0
- tree_sitter_analyzer/security/regex_checker.py +292 -0
- tree_sitter_analyzer/security/validator.py +241 -0
- tree_sitter_analyzer/table_formatter.py +652 -589
- tree_sitter_analyzer/utils.py +277 -277
- {tree_sitter_analyzer-0.7.0.dist-info → tree_sitter_analyzer-0.8.1.dist-info}/METADATA +27 -1
- tree_sitter_analyzer-0.8.1.dist-info/RECORD +77 -0
- tree_sitter_analyzer-0.7.0.dist-info/RECORD +0 -72
- {tree_sitter_analyzer-0.7.0.dist-info → tree_sitter_analyzer-0.8.1.dist-info}/WHEEL +0 -0
- {tree_sitter_analyzer-0.7.0.dist-info → tree_sitter_analyzer-0.8.1.dist-info}/entry_points.txt +0 -0
|
@@ -1,589 +1,652 @@
|
|
|
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 os
|
|
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
|
-
return os.linesep
|
|
30
|
-
|
|
31
|
-
def _convert_to_platform_newlines(self, text: str) -> str:
|
|
32
|
-
"""Convert standard \\n to platform-specific newline characters"""
|
|
33
|
-
if os.linesep != "\n":
|
|
34
|
-
return text.replace("\n", os.linesep)
|
|
35
|
-
return text
|
|
36
|
-
|
|
37
|
-
def format_structure(self, structure_data: dict[str, Any]) -> str:
|
|
38
|
-
"""Format structure data as table"""
|
|
39
|
-
if self.format_type == "full":
|
|
40
|
-
result = self._format_full_table(structure_data)
|
|
41
|
-
elif self.format_type == "compact":
|
|
42
|
-
result = self._format_compact_table(structure_data)
|
|
43
|
-
elif self.format_type == "csv":
|
|
44
|
-
result = self._format_csv(structure_data)
|
|
45
|
-
else:
|
|
46
|
-
raise ValueError(f"Unsupported format type: {self.format_type}")
|
|
47
|
-
|
|
48
|
-
# Finally convert to platform-specific newline characters
|
|
49
|
-
# Skip newline conversion for CSV format (newline control is handled within _format_csv)
|
|
50
|
-
if self.format_type == "csv":
|
|
51
|
-
return result
|
|
52
|
-
|
|
53
|
-
return self._convert_to_platform_newlines(result)
|
|
54
|
-
|
|
55
|
-
def _format_full_table(self, data: dict[str, Any]) -> str:
|
|
56
|
-
"""Full table format - organized by class"""
|
|
57
|
-
lines = []
|
|
58
|
-
|
|
59
|
-
# Header - use filename for multi-class files
|
|
60
|
-
classes = data.get("classes", [])
|
|
61
|
-
if classes is None:
|
|
62
|
-
classes = []
|
|
63
|
-
|
|
64
|
-
#
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
lines.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
lines.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
lines.append("
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
lines
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
if classes
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
lines.append(f"
|
|
346
|
-
lines.append("")
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
lines.append("
|
|
351
|
-
lines.append("
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
"
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
)
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
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 os
|
|
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
|
+
return os.linesep
|
|
30
|
+
|
|
31
|
+
def _convert_to_platform_newlines(self, text: str) -> str:
|
|
32
|
+
"""Convert standard \\n to platform-specific newline characters"""
|
|
33
|
+
if os.linesep != "\n":
|
|
34
|
+
return text.replace("\n", os.linesep)
|
|
35
|
+
return text
|
|
36
|
+
|
|
37
|
+
def format_structure(self, structure_data: dict[str, Any]) -> str:
|
|
38
|
+
"""Format structure data as table"""
|
|
39
|
+
if self.format_type == "full":
|
|
40
|
+
result = self._format_full_table(structure_data)
|
|
41
|
+
elif self.format_type == "compact":
|
|
42
|
+
result = self._format_compact_table(structure_data)
|
|
43
|
+
elif self.format_type == "csv":
|
|
44
|
+
result = self._format_csv(structure_data)
|
|
45
|
+
else:
|
|
46
|
+
raise ValueError(f"Unsupported format type: {self.format_type}")
|
|
47
|
+
|
|
48
|
+
# Finally convert to platform-specific newline characters
|
|
49
|
+
# Skip newline conversion for CSV format (newline control is handled within _format_csv)
|
|
50
|
+
if self.format_type == "csv":
|
|
51
|
+
return result
|
|
52
|
+
|
|
53
|
+
return self._convert_to_platform_newlines(result)
|
|
54
|
+
|
|
55
|
+
def _format_full_table(self, data: dict[str, Any]) -> str:
|
|
56
|
+
"""Full table format - organized by class"""
|
|
57
|
+
lines = []
|
|
58
|
+
|
|
59
|
+
# Header - use package.class format for single class, filename for multi-class files
|
|
60
|
+
classes = data.get("classes", [])
|
|
61
|
+
if classes is None:
|
|
62
|
+
classes = []
|
|
63
|
+
|
|
64
|
+
# Determine header format
|
|
65
|
+
package_name = (data.get("package") or {}).get("name", "")
|
|
66
|
+
if len(classes) == 1:
|
|
67
|
+
# Single class: use package.ClassName format
|
|
68
|
+
class_name = classes[0].get("name", "Unknown")
|
|
69
|
+
if package_name:
|
|
70
|
+
header = f"{package_name}.{class_name}"
|
|
71
|
+
else:
|
|
72
|
+
header = class_name
|
|
73
|
+
else:
|
|
74
|
+
# Multiple classes or no classes: use filename or default
|
|
75
|
+
file_path = data.get("file_path", "")
|
|
76
|
+
if file_path and file_path != "Unknown":
|
|
77
|
+
file_name = file_path.split("/")[-1].split("\\")[-1]
|
|
78
|
+
if file_name.endswith(".java"):
|
|
79
|
+
file_name = file_name[:-5] # Remove .java extension
|
|
80
|
+
elif file_name.endswith(".py"):
|
|
81
|
+
file_name = file_name[:-3] # Remove .py extension
|
|
82
|
+
elif file_name.endswith(".js"):
|
|
83
|
+
file_name = file_name[:-3] # Remove .js extension
|
|
84
|
+
|
|
85
|
+
if package_name and len(classes) == 0:
|
|
86
|
+
# No classes but has package: use package.filename
|
|
87
|
+
header = f"{package_name}.{file_name}"
|
|
88
|
+
else:
|
|
89
|
+
header = file_name
|
|
90
|
+
else:
|
|
91
|
+
# No file path: use default format
|
|
92
|
+
if package_name:
|
|
93
|
+
header = f"{package_name}.Unknown"
|
|
94
|
+
else:
|
|
95
|
+
header = "unknown.Unknown"
|
|
96
|
+
|
|
97
|
+
lines.append(f"# {header}")
|
|
98
|
+
lines.append("")
|
|
99
|
+
|
|
100
|
+
# Package info
|
|
101
|
+
package_name = (data.get("package") or {}).get("name", "")
|
|
102
|
+
if package_name:
|
|
103
|
+
lines.append("## Package")
|
|
104
|
+
lines.append(f"`{package_name}`")
|
|
105
|
+
lines.append("")
|
|
106
|
+
|
|
107
|
+
# Imports
|
|
108
|
+
imports = data.get("imports", [])
|
|
109
|
+
if imports:
|
|
110
|
+
lines.append("## Imports")
|
|
111
|
+
lines.append(f"```{self.language}")
|
|
112
|
+
for imp in imports:
|
|
113
|
+
lines.append(str(imp.get("statement", "")))
|
|
114
|
+
lines.append("```")
|
|
115
|
+
lines.append("")
|
|
116
|
+
|
|
117
|
+
# Class Info section (for single class files or empty data)
|
|
118
|
+
if len(classes) == 1 or len(classes) == 0:
|
|
119
|
+
lines.append("## Class Info")
|
|
120
|
+
lines.append("| Property | Value |")
|
|
121
|
+
lines.append("|----------|-------|")
|
|
122
|
+
|
|
123
|
+
package_name = (data.get("package") or {}).get("name", "unknown")
|
|
124
|
+
|
|
125
|
+
if len(classes) == 1:
|
|
126
|
+
class_info = classes[0]
|
|
127
|
+
lines.append(f"| Package | {package_name} |")
|
|
128
|
+
lines.append(f"| Type | {str(class_info.get('type', 'class'))} |")
|
|
129
|
+
lines.append(f"| Visibility | {str(class_info.get('visibility', 'public'))} |")
|
|
130
|
+
|
|
131
|
+
# Lines
|
|
132
|
+
line_range = class_info.get("line_range", {})
|
|
133
|
+
lines_str = f"{line_range.get('start', 1)}-{line_range.get('end', 50)}"
|
|
134
|
+
lines.append(f"| Lines | {lines_str} |")
|
|
135
|
+
else:
|
|
136
|
+
# Empty data case
|
|
137
|
+
lines.append(f"| Package | {package_name} |")
|
|
138
|
+
lines.append("| Type | class |")
|
|
139
|
+
lines.append("| Visibility | public |")
|
|
140
|
+
lines.append("| Lines | 0-0 |")
|
|
141
|
+
|
|
142
|
+
# Count methods and fields
|
|
143
|
+
all_methods = data.get("methods", []) or []
|
|
144
|
+
all_fields = data.get("fields", []) or []
|
|
145
|
+
lines.append(f"| Total Methods | {len(all_methods)} |")
|
|
146
|
+
lines.append(f"| Total Fields | {len(all_fields)} |")
|
|
147
|
+
lines.append("")
|
|
148
|
+
|
|
149
|
+
# Classes Overview
|
|
150
|
+
if len(classes) > 1:
|
|
151
|
+
lines.append("## Classes Overview")
|
|
152
|
+
lines.append("| Class | Type | Visibility | Lines | Methods | Fields |")
|
|
153
|
+
lines.append("|-------|------|------------|-------|---------|--------|")
|
|
154
|
+
|
|
155
|
+
for class_info in classes:
|
|
156
|
+
name = str(class_info.get("name", "Unknown"))
|
|
157
|
+
class_type = str(class_info.get("type", "class"))
|
|
158
|
+
visibility = str(class_info.get("visibility", "public"))
|
|
159
|
+
line_range = class_info.get("line_range", {})
|
|
160
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
161
|
+
|
|
162
|
+
# Calculate method and field counts for this class
|
|
163
|
+
class_methods = self._get_class_methods(data, line_range)
|
|
164
|
+
class_fields = self._get_class_fields(data, line_range)
|
|
165
|
+
|
|
166
|
+
lines.append(
|
|
167
|
+
f"| {name} | {class_type} | {visibility} | {lines_str} | {len(class_methods)} | {len(class_fields)} |"
|
|
168
|
+
)
|
|
169
|
+
lines.append("")
|
|
170
|
+
|
|
171
|
+
# Detailed class information - organized by class
|
|
172
|
+
for class_info in classes:
|
|
173
|
+
lines.extend(self._format_class_details(class_info, data))
|
|
174
|
+
|
|
175
|
+
# Remove trailing empty lines
|
|
176
|
+
while lines and lines[-1] == "":
|
|
177
|
+
lines.pop()
|
|
178
|
+
|
|
179
|
+
return "\n".join(lines)
|
|
180
|
+
|
|
181
|
+
def _get_class_methods(self, data: dict[str, Any], class_line_range: dict[str, int]) -> list[dict[str, Any]]:
|
|
182
|
+
"""Get methods that belong to a specific class based on line range, excluding nested classes."""
|
|
183
|
+
methods = data.get("methods", [])
|
|
184
|
+
classes = data.get("classes", [])
|
|
185
|
+
class_methods = []
|
|
186
|
+
|
|
187
|
+
# Get nested class ranges to exclude their methods
|
|
188
|
+
nested_class_ranges = []
|
|
189
|
+
for cls in classes:
|
|
190
|
+
cls_range = cls.get("line_range", {})
|
|
191
|
+
cls_start = cls_range.get("start", 0)
|
|
192
|
+
cls_end = cls_range.get("end", 0)
|
|
193
|
+
|
|
194
|
+
# If this class is nested within the current class range
|
|
195
|
+
if (class_line_range.get("start", 0) < cls_start and
|
|
196
|
+
cls_end < class_line_range.get("end", 0)):
|
|
197
|
+
nested_class_ranges.append((cls_start, cls_end))
|
|
198
|
+
|
|
199
|
+
for method in methods:
|
|
200
|
+
method_line = method.get("line_range", {}).get("start", 0)
|
|
201
|
+
|
|
202
|
+
# Check if method is within the class range
|
|
203
|
+
if (class_line_range.get("start", 0) <= method_line <= class_line_range.get("end", 0)):
|
|
204
|
+
# Check if method is NOT within any nested class
|
|
205
|
+
in_nested_class = False
|
|
206
|
+
for nested_start, nested_end in nested_class_ranges:
|
|
207
|
+
if nested_start <= method_line <= nested_end:
|
|
208
|
+
in_nested_class = True
|
|
209
|
+
break
|
|
210
|
+
|
|
211
|
+
if not in_nested_class:
|
|
212
|
+
class_methods.append(method)
|
|
213
|
+
|
|
214
|
+
return class_methods
|
|
215
|
+
|
|
216
|
+
def _get_class_fields(self, data: dict[str, Any], class_line_range: dict[str, int]) -> list[dict[str, Any]]:
|
|
217
|
+
"""Get fields that belong to a specific class based on line range, excluding nested classes."""
|
|
218
|
+
fields = data.get("fields", [])
|
|
219
|
+
classes = data.get("classes", [])
|
|
220
|
+
class_fields = []
|
|
221
|
+
|
|
222
|
+
# Get nested class ranges to exclude their fields
|
|
223
|
+
nested_class_ranges = []
|
|
224
|
+
for cls in classes:
|
|
225
|
+
cls_range = cls.get("line_range", {})
|
|
226
|
+
cls_start = cls_range.get("start", 0)
|
|
227
|
+
cls_end = cls_range.get("end", 0)
|
|
228
|
+
|
|
229
|
+
# If this class is nested within the current class range
|
|
230
|
+
if (class_line_range.get("start", 0) < cls_start and
|
|
231
|
+
cls_end < class_line_range.get("end", 0)):
|
|
232
|
+
nested_class_ranges.append((cls_start, cls_end))
|
|
233
|
+
|
|
234
|
+
for field in fields:
|
|
235
|
+
field_line = field.get("line_range", {}).get("start", 0)
|
|
236
|
+
|
|
237
|
+
# Check if field is within the class range
|
|
238
|
+
if (class_line_range.get("start", 0) <= field_line <= class_line_range.get("end", 0)):
|
|
239
|
+
# Check if field is NOT within any nested class
|
|
240
|
+
in_nested_class = False
|
|
241
|
+
for nested_start, nested_end in nested_class_ranges:
|
|
242
|
+
if nested_start <= field_line <= nested_end:
|
|
243
|
+
in_nested_class = True
|
|
244
|
+
break
|
|
245
|
+
|
|
246
|
+
if not in_nested_class:
|
|
247
|
+
class_fields.append(field)
|
|
248
|
+
|
|
249
|
+
return class_fields
|
|
250
|
+
|
|
251
|
+
def _format_class_details(self, class_info: dict[str, Any], data: dict[str, Any]) -> list[str]:
|
|
252
|
+
"""Format detailed information for a single class."""
|
|
253
|
+
lines = []
|
|
254
|
+
|
|
255
|
+
name = str(class_info.get("name", "Unknown"))
|
|
256
|
+
line_range = class_info.get("line_range", {})
|
|
257
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
258
|
+
|
|
259
|
+
# Class header
|
|
260
|
+
lines.append(f"## {name} ({lines_str})")
|
|
261
|
+
|
|
262
|
+
# Get class-specific methods and fields
|
|
263
|
+
class_methods = self._get_class_methods(data, line_range)
|
|
264
|
+
class_fields = self._get_class_fields(data, line_range)
|
|
265
|
+
|
|
266
|
+
# Fields section
|
|
267
|
+
if class_fields:
|
|
268
|
+
lines.append("### Fields")
|
|
269
|
+
lines.append("| Name | Type | Vis | Modifiers | Line | Doc |")
|
|
270
|
+
lines.append("|------|------|-----|-----------|------|-----|")
|
|
271
|
+
|
|
272
|
+
for field in class_fields:
|
|
273
|
+
name_field = str(field.get("name", ""))
|
|
274
|
+
type_field = str(field.get("type", ""))
|
|
275
|
+
visibility = self._convert_visibility(str(field.get("visibility", "")))
|
|
276
|
+
modifiers = ",".join(field.get("modifiers", []))
|
|
277
|
+
line_num = field.get("line_range", {}).get("start", 0)
|
|
278
|
+
doc = self._extract_doc_summary(str(field.get("javadoc", ""))) if self.include_javadoc else "-"
|
|
279
|
+
|
|
280
|
+
lines.append(f"| {name_field} | {type_field} | {visibility} | {modifiers} | {line_num} | {doc} |")
|
|
281
|
+
lines.append("")
|
|
282
|
+
|
|
283
|
+
# Methods section - separate by type
|
|
284
|
+
constructors = [m for m in class_methods if m.get("is_constructor", False)]
|
|
285
|
+
regular_methods = [m for m in class_methods if not m.get("is_constructor", False)]
|
|
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 = [m for m in regular_methods if m.get("visibility", "") == "public"]
|
|
299
|
+
protected_methods = [m for m in regular_methods if m.get("visibility", "") == "protected"]
|
|
300
|
+
package_methods = [m for m in regular_methods if m.get("visibility", "") == "package"]
|
|
301
|
+
private_methods = [m for m in regular_methods if m.get("visibility", "") == "private"]
|
|
302
|
+
|
|
303
|
+
for method_group, title in [
|
|
304
|
+
(public_methods, "Public Methods"),
|
|
305
|
+
(protected_methods, "Protected Methods"),
|
|
306
|
+
(package_methods, "Package Methods"),
|
|
307
|
+
(private_methods, "Private Methods")
|
|
308
|
+
]:
|
|
309
|
+
if method_group:
|
|
310
|
+
lines.append(f"### {title}")
|
|
311
|
+
lines.append("| Method | Signature | Vis | Lines | Cx | Doc |")
|
|
312
|
+
lines.append("|--------|-----------|-----|-------|----|----|")
|
|
313
|
+
|
|
314
|
+
for method in method_group:
|
|
315
|
+
lines.append(self._format_method_row_detailed(method))
|
|
316
|
+
lines.append("")
|
|
317
|
+
|
|
318
|
+
return lines
|
|
319
|
+
|
|
320
|
+
def _format_method_row_detailed(self, method: dict[str, Any]) -> str:
|
|
321
|
+
"""Format method row for detailed class view."""
|
|
322
|
+
name = str(method.get("name", ""))
|
|
323
|
+
signature = self._create_full_signature(method)
|
|
324
|
+
visibility = self._convert_visibility(str(method.get("visibility", "")))
|
|
325
|
+
line_range = method.get("line_range", {})
|
|
326
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
327
|
+
complexity = method.get("complexity_score", 0)
|
|
328
|
+
doc = self._extract_doc_summary(str(method.get("javadoc", ""))) if self.include_javadoc else "-"
|
|
329
|
+
|
|
330
|
+
return f"| {name} | {signature} | {visibility} | {lines_str} | {complexity} | {doc} |"
|
|
331
|
+
|
|
332
|
+
def _format_traditional_sections(self, data: dict[str, Any]) -> list[str]:
|
|
333
|
+
"""Format traditional sections when no classes are found."""
|
|
334
|
+
lines = []
|
|
335
|
+
|
|
336
|
+
# Traditional class info
|
|
337
|
+
lines.append("## Class Info")
|
|
338
|
+
lines.append("| Property | Value |")
|
|
339
|
+
lines.append("|----------|-------|")
|
|
340
|
+
|
|
341
|
+
package_name = (data.get("package") or {}).get("name", "unknown")
|
|
342
|
+
class_info = data.get("classes", [{}])[0] if data.get("classes") else {}
|
|
343
|
+
stats = data.get("statistics") or {}
|
|
344
|
+
|
|
345
|
+
lines.append(f"| Package | {package_name} |")
|
|
346
|
+
lines.append(f"| Type | {str(class_info.get('type', 'class'))} |")
|
|
347
|
+
lines.append(f"| Visibility | {str(class_info.get('visibility', 'public'))} |")
|
|
348
|
+
lines.append(f"| Lines | {class_info.get('line_range', {}).get('start', 0)}-{class_info.get('line_range', {}).get('end', 0)} |")
|
|
349
|
+
lines.append(f"| Total Methods | {stats.get('method_count', 0)} |")
|
|
350
|
+
lines.append(f"| Total Fields | {stats.get('field_count', 0)} |")
|
|
351
|
+
lines.append("")
|
|
352
|
+
|
|
353
|
+
# Fields
|
|
354
|
+
fields = data.get("fields", [])
|
|
355
|
+
if fields:
|
|
356
|
+
lines.append("## Fields")
|
|
357
|
+
lines.append("| Name | Type | Vis | Modifiers | Line | Doc |")
|
|
358
|
+
lines.append("|------|------|-----|-----------|------|-----|")
|
|
359
|
+
|
|
360
|
+
for field in fields:
|
|
361
|
+
name = str(field.get("name", ""))
|
|
362
|
+
field_type = str(field.get("type", ""))
|
|
363
|
+
visibility = self._convert_visibility(str(field.get("visibility", "")))
|
|
364
|
+
modifiers = ",".join([str(m) for m in field.get("modifiers", [])])
|
|
365
|
+
line = field.get("line_range", {}).get("start", 0)
|
|
366
|
+
doc = self._extract_doc_summary(str(field.get("javadoc", ""))) if self.include_javadoc else "-"
|
|
367
|
+
|
|
368
|
+
lines.append(f"| {name} | {field_type} | {visibility} | {modifiers} | {line} | {doc} |")
|
|
369
|
+
lines.append("")
|
|
370
|
+
|
|
371
|
+
# Methods by type
|
|
372
|
+
methods = data.get("methods", [])
|
|
373
|
+
constructors = [m for m in methods if m.get("is_constructor", False)]
|
|
374
|
+
regular_methods = [m for m in methods if not m.get("is_constructor", False)]
|
|
375
|
+
|
|
376
|
+
# Constructors
|
|
377
|
+
if constructors:
|
|
378
|
+
lines.append("## Constructor")
|
|
379
|
+
lines.append("| Method | Signature | Vis | Lines | Cols | Cx | Doc |")
|
|
380
|
+
lines.append("|--------|-----------|-----|-------|------|----|----|")
|
|
381
|
+
for method in constructors:
|
|
382
|
+
lines.append(self._format_method_row(method))
|
|
383
|
+
lines.append("")
|
|
384
|
+
|
|
385
|
+
# Methods by visibility
|
|
386
|
+
for visibility, title in [("public", "Public Methods"), ("private", "Private Methods")]:
|
|
387
|
+
visibility_methods = [m for m in regular_methods if str(m.get("visibility")) == visibility]
|
|
388
|
+
if visibility_methods:
|
|
389
|
+
lines.append(f"## {title}")
|
|
390
|
+
lines.append("| Method | Signature | Vis | Lines | Cols | Cx | Doc |")
|
|
391
|
+
lines.append("|--------|-----------|-----|-------|------|----|----|")
|
|
392
|
+
for method in visibility_methods:
|
|
393
|
+
lines.append(self._format_method_row(method))
|
|
394
|
+
lines.append("")
|
|
395
|
+
|
|
396
|
+
return lines
|
|
397
|
+
|
|
398
|
+
def _format_compact_table(self, data: dict[str, Any]) -> str:
|
|
399
|
+
"""Compact table format"""
|
|
400
|
+
lines = []
|
|
401
|
+
|
|
402
|
+
# Header
|
|
403
|
+
package_name = (data.get("package") or {}).get("name", "unknown")
|
|
404
|
+
classes = data.get("classes", [])
|
|
405
|
+
if classes is None:
|
|
406
|
+
classes = []
|
|
407
|
+
class_name = classes[0].get("name", "Unknown") if classes else "Unknown"
|
|
408
|
+
lines.append(f"# {package_name}.{class_name}")
|
|
409
|
+
lines.append("")
|
|
410
|
+
|
|
411
|
+
# Basic information
|
|
412
|
+
stats = data.get("statistics") or {}
|
|
413
|
+
lines.append("## Info")
|
|
414
|
+
lines.append("| Property | Value |")
|
|
415
|
+
lines.append("|----------|-------|")
|
|
416
|
+
lines.append(f"| Package | {package_name} |")
|
|
417
|
+
lines.append(f"| Methods | {stats.get('method_count', 0)} |")
|
|
418
|
+
lines.append(f"| Fields | {stats.get('field_count', 0)} |")
|
|
419
|
+
lines.append("")
|
|
420
|
+
|
|
421
|
+
# Methods (simplified version)
|
|
422
|
+
methods = data.get("methods", [])
|
|
423
|
+
if methods is None:
|
|
424
|
+
methods = []
|
|
425
|
+
if methods:
|
|
426
|
+
lines.append("## Methods")
|
|
427
|
+
lines.append("| Method | Sig | V | L | Cx | Doc |")
|
|
428
|
+
lines.append("|--------|-----|---|---|----|----|")
|
|
429
|
+
|
|
430
|
+
for method in methods:
|
|
431
|
+
name = str(method.get("name", ""))
|
|
432
|
+
signature = self._create_compact_signature(method)
|
|
433
|
+
visibility = self._convert_visibility(str(method.get("visibility", "")))
|
|
434
|
+
line_range = method.get("line_range", {})
|
|
435
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
436
|
+
complexity = method.get("complexity_score", 0)
|
|
437
|
+
doc = self._clean_csv_text(
|
|
438
|
+
self._extract_doc_summary(str(method.get("javadoc", "")))
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
lines.append(
|
|
442
|
+
f"| {name} | {signature} | {visibility} | {lines_str} | {complexity} | {doc} |"
|
|
443
|
+
)
|
|
444
|
+
lines.append("")
|
|
445
|
+
|
|
446
|
+
# Remove trailing empty lines
|
|
447
|
+
while lines and lines[-1] == "":
|
|
448
|
+
lines.pop()
|
|
449
|
+
|
|
450
|
+
return "\n".join(lines)
|
|
451
|
+
|
|
452
|
+
def _format_csv(self, data: dict[str, Any]) -> str:
|
|
453
|
+
"""CSV format"""
|
|
454
|
+
output = io.StringIO()
|
|
455
|
+
writer = csv.writer(
|
|
456
|
+
output, lineterminator="\n"
|
|
457
|
+
) # Explicitly specify newline character
|
|
458
|
+
|
|
459
|
+
# Header
|
|
460
|
+
writer.writerow(
|
|
461
|
+
["Type", "Name", "Signature", "Visibility", "Lines", "Complexity", "Doc"]
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
# Fields
|
|
465
|
+
for field in data.get("fields", []):
|
|
466
|
+
writer.writerow(
|
|
467
|
+
[
|
|
468
|
+
"Field",
|
|
469
|
+
str(field.get("name", "")),
|
|
470
|
+
f"{str(field.get('name', ''))}:{str(field.get('type', ''))}",
|
|
471
|
+
str(field.get("visibility", "")),
|
|
472
|
+
f"{field.get('line_range', {}).get('start', 0)}-{field.get('line_range', {}).get('end', 0)}",
|
|
473
|
+
"",
|
|
474
|
+
self._clean_csv_text(
|
|
475
|
+
self._extract_doc_summary(str(field.get("javadoc", "")))
|
|
476
|
+
),
|
|
477
|
+
]
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
# Methods
|
|
481
|
+
for method in data.get("methods", []):
|
|
482
|
+
writer.writerow(
|
|
483
|
+
[
|
|
484
|
+
"Constructor" if method.get("is_constructor", False) else "Method",
|
|
485
|
+
str(method.get("name", "")),
|
|
486
|
+
self._clean_csv_text(self._create_full_signature(method)),
|
|
487
|
+
str(method.get("visibility", "")),
|
|
488
|
+
f"{method.get('line_range', {}).get('start', 0)}-{method.get('line_range', {}).get('end', 0)}",
|
|
489
|
+
method.get("complexity_score", 0),
|
|
490
|
+
self._clean_csv_text(
|
|
491
|
+
self._extract_doc_summary(str(method.get("javadoc", "")))
|
|
492
|
+
),
|
|
493
|
+
]
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
# Completely control CSV output newlines
|
|
497
|
+
csv_content = output.getvalue()
|
|
498
|
+
# Unify all newline patterns and remove trailing newlines
|
|
499
|
+
csv_content = csv_content.replace("\r\n", "\n").replace("\r", "\n")
|
|
500
|
+
csv_content = csv_content.rstrip("\n")
|
|
501
|
+
output.close()
|
|
502
|
+
|
|
503
|
+
return csv_content
|
|
504
|
+
|
|
505
|
+
def _format_method_row(self, method: dict[str, Any]) -> str:
|
|
506
|
+
"""Format method row"""
|
|
507
|
+
name = str(method.get("name", ""))
|
|
508
|
+
signature = self._create_full_signature(method)
|
|
509
|
+
visibility = self._convert_visibility(str(method.get("visibility", "")))
|
|
510
|
+
line_range = method.get("line_range", {})
|
|
511
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
512
|
+
cols_str = (
|
|
513
|
+
"5-6" # Default value (actual implementation should get accurate values)
|
|
514
|
+
)
|
|
515
|
+
complexity = method.get("complexity_score", 0)
|
|
516
|
+
if self.include_javadoc:
|
|
517
|
+
doc = self._clean_csv_text(
|
|
518
|
+
self._extract_doc_summary(str(method.get("javadoc", "")))
|
|
519
|
+
)
|
|
520
|
+
else:
|
|
521
|
+
doc = "-"
|
|
522
|
+
|
|
523
|
+
return f"| {name} | {signature} | {visibility} | {lines_str} | {cols_str} | {complexity} | {doc} |"
|
|
524
|
+
|
|
525
|
+
def _create_full_signature(self, method: dict[str, Any]) -> str:
|
|
526
|
+
"""Create complete method signature"""
|
|
527
|
+
params = method.get("parameters", [])
|
|
528
|
+
param_strs = []
|
|
529
|
+
for param in params:
|
|
530
|
+
param_type = str(param.get("type", "Object"))
|
|
531
|
+
param_name = str(param.get("name", "param"))
|
|
532
|
+
param_strs.append(f"{param_name}:{param_type}")
|
|
533
|
+
|
|
534
|
+
params_str = ", ".join(param_strs)
|
|
535
|
+
return_type = str(method.get("return_type", "void"))
|
|
536
|
+
|
|
537
|
+
modifiers = []
|
|
538
|
+
if method.get("is_static", False):
|
|
539
|
+
modifiers.append("[static]")
|
|
540
|
+
|
|
541
|
+
modifier_str = " ".join(modifiers)
|
|
542
|
+
signature = f"({params_str}):{return_type}"
|
|
543
|
+
|
|
544
|
+
if modifier_str:
|
|
545
|
+
signature += f" {modifier_str}"
|
|
546
|
+
|
|
547
|
+
return signature
|
|
548
|
+
|
|
549
|
+
def _create_compact_signature(self, method: dict[str, Any]) -> str:
|
|
550
|
+
"""Create compact method signature"""
|
|
551
|
+
params = method.get("parameters", [])
|
|
552
|
+
param_types = [self._shorten_type(p.get("type", "O")) for p in params]
|
|
553
|
+
params_str = ",".join(param_types)
|
|
554
|
+
return_type = self._shorten_type(method.get("return_type", "void"))
|
|
555
|
+
|
|
556
|
+
return f"({params_str}):{return_type}"
|
|
557
|
+
|
|
558
|
+
def _shorten_type(self, type_name: Any) -> str:
|
|
559
|
+
"""Shorten type name"""
|
|
560
|
+
if type_name is None:
|
|
561
|
+
return "O"
|
|
562
|
+
|
|
563
|
+
# Convert non-string types to string
|
|
564
|
+
if not isinstance(type_name, str):
|
|
565
|
+
type_name = str(type_name)
|
|
566
|
+
|
|
567
|
+
# At this point, type_name is guaranteed to be a string
|
|
568
|
+
assert isinstance(type_name, str)
|
|
569
|
+
|
|
570
|
+
type_mapping = {
|
|
571
|
+
"String": "S",
|
|
572
|
+
"int": "i",
|
|
573
|
+
"long": "l",
|
|
574
|
+
"double": "d",
|
|
575
|
+
"boolean": "b",
|
|
576
|
+
"void": "void",
|
|
577
|
+
"Object": "O",
|
|
578
|
+
"Exception": "E",
|
|
579
|
+
"SQLException": "SE",
|
|
580
|
+
"IllegalArgumentException": "IAE",
|
|
581
|
+
"RuntimeException": "RE",
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
# Map<String,Object> -> M<S,O>
|
|
585
|
+
if "Map<" in type_name:
|
|
586
|
+
return (
|
|
587
|
+
type_name.replace("Map<", "M<")
|
|
588
|
+
.replace("String", "S")
|
|
589
|
+
.replace("Object", "O")
|
|
590
|
+
)
|
|
591
|
+
|
|
592
|
+
# List<String> -> L<S>
|
|
593
|
+
if "List<" in type_name:
|
|
594
|
+
return type_name.replace("List<", "L<").replace("String", "S")
|
|
595
|
+
|
|
596
|
+
# String[] -> S[]
|
|
597
|
+
if "[]" in type_name:
|
|
598
|
+
base_type = type_name.replace("[]", "")
|
|
599
|
+
if base_type:
|
|
600
|
+
return type_mapping.get(base_type, base_type[0].upper()) + "[]"
|
|
601
|
+
else:
|
|
602
|
+
return "O[]"
|
|
603
|
+
|
|
604
|
+
return type_mapping.get(type_name, type_name)
|
|
605
|
+
|
|
606
|
+
def _convert_visibility(self, visibility: str) -> str:
|
|
607
|
+
"""Convert visibility to symbol"""
|
|
608
|
+
mapping = {"public": "+", "private": "-", "protected": "#", "package": "~"}
|
|
609
|
+
return mapping.get(visibility, visibility)
|
|
610
|
+
|
|
611
|
+
def _extract_doc_summary(self, javadoc: str) -> str:
|
|
612
|
+
"""Extract summary from JavaDoc"""
|
|
613
|
+
if not javadoc:
|
|
614
|
+
return "-"
|
|
615
|
+
|
|
616
|
+
# Remove comment symbols
|
|
617
|
+
clean_doc = (
|
|
618
|
+
javadoc.replace("/**", "").replace("*/", "").replace("*", "").strip()
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
# Get first line (use standard \\n only)
|
|
622
|
+
lines = clean_doc.split("\n")
|
|
623
|
+
first_line = lines[0].strip()
|
|
624
|
+
|
|
625
|
+
# Truncate if too long
|
|
626
|
+
if len(first_line) > 50:
|
|
627
|
+
first_line = first_line[:47] + "..."
|
|
628
|
+
|
|
629
|
+
# Escape characters that cause problems in Markdown tables (use standard \\n only)
|
|
630
|
+
return first_line.replace("|", "\\|").replace("\n", " ")
|
|
631
|
+
|
|
632
|
+
def _clean_csv_text(self, text: str) -> str:
|
|
633
|
+
"""Text cleaning for CSV format"""
|
|
634
|
+
if not text:
|
|
635
|
+
return ""
|
|
636
|
+
|
|
637
|
+
# Replace all newline characters with spaces
|
|
638
|
+
cleaned = text.replace("\r\n", " ").replace("\r", " ").replace("\n", " ")
|
|
639
|
+
# Convert consecutive spaces to single space
|
|
640
|
+
cleaned = " ".join(cleaned.split())
|
|
641
|
+
# Escape characters that cause problems in CSV
|
|
642
|
+
cleaned = cleaned.replace('"', '""') # Escape double quotes
|
|
643
|
+
|
|
644
|
+
return cleaned
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
def create_table_formatter(
|
|
648
|
+
format_type: str, language: str = "java", include_javadoc: bool = False
|
|
649
|
+
) -> "TableFormatter":
|
|
650
|
+
"""Create table formatter (using new factory)"""
|
|
651
|
+
# Create TableFormatter directly (for JavaDoc support)
|
|
652
|
+
return TableFormatter(format_type, language, include_javadoc)
|