linguclaw 0.4.0
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.
- package/LICENSE +21 -0
- package/README.md +161 -0
- package/dist/agent-system.d.ts +196 -0
- package/dist/agent-system.d.ts.map +1 -0
- package/dist/agent-system.js +738 -0
- package/dist/agent-system.js.map +1 -0
- package/dist/alphabeta.d.ts +54 -0
- package/dist/alphabeta.d.ts.map +1 -0
- package/dist/alphabeta.js +193 -0
- package/dist/alphabeta.js.map +1 -0
- package/dist/browser.d.ts +62 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +224 -0
- package/dist/browser.js.map +1 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +565 -0
- package/dist/cli.js.map +1 -0
- package/dist/code-parser.d.ts +39 -0
- package/dist/code-parser.d.ts.map +1 -0
- package/dist/code-parser.js +385 -0
- package/dist/code-parser.js.map +1 -0
- package/dist/config.d.ts +66 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +232 -0
- package/dist/config.js.map +1 -0
- package/dist/core/engine.d.ts +359 -0
- package/dist/core/engine.d.ts.map +1 -0
- package/dist/core/engine.js +127 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/daemon.d.ts +29 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +212 -0
- package/dist/daemon.js.map +1 -0
- package/dist/email-receiver.d.ts +63 -0
- package/dist/email-receiver.d.ts.map +1 -0
- package/dist/email-receiver.js +553 -0
- package/dist/email-receiver.js.map +1 -0
- package/dist/git-integration.d.ts +180 -0
- package/dist/git-integration.d.ts.map +1 -0
- package/dist/git-integration.js +850 -0
- package/dist/git-integration.js.map +1 -0
- package/dist/inbox.d.ts +84 -0
- package/dist/inbox.d.ts.map +1 -0
- package/dist/inbox.js +198 -0
- package/dist/inbox.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/languages/cpp.d.ts +51 -0
- package/dist/languages/cpp.d.ts.map +1 -0
- package/dist/languages/cpp.js +930 -0
- package/dist/languages/cpp.js.map +1 -0
- package/dist/languages/csharp.d.ts +79 -0
- package/dist/languages/csharp.d.ts.map +1 -0
- package/dist/languages/csharp.js +1776 -0
- package/dist/languages/csharp.js.map +1 -0
- package/dist/languages/go.d.ts +50 -0
- package/dist/languages/go.d.ts.map +1 -0
- package/dist/languages/go.js +882 -0
- package/dist/languages/go.js.map +1 -0
- package/dist/languages/java.d.ts +47 -0
- package/dist/languages/java.d.ts.map +1 -0
- package/dist/languages/java.js +649 -0
- package/dist/languages/java.js.map +1 -0
- package/dist/languages/python.d.ts +47 -0
- package/dist/languages/python.d.ts.map +1 -0
- package/dist/languages/python.js +655 -0
- package/dist/languages/python.js.map +1 -0
- package/dist/languages/rust.d.ts +61 -0
- package/dist/languages/rust.d.ts.map +1 -0
- package/dist/languages/rust.js +1064 -0
- package/dist/languages/rust.js.map +1 -0
- package/dist/logger.d.ts +20 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +133 -0
- package/dist/logger.js.map +1 -0
- package/dist/longterm-memory.d.ts +47 -0
- package/dist/longterm-memory.d.ts.map +1 -0
- package/dist/longterm-memory.js +300 -0
- package/dist/longterm-memory.js.map +1 -0
- package/dist/memory.d.ts +42 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +274 -0
- package/dist/memory.js.map +1 -0
- package/dist/messaging.d.ts +103 -0
- package/dist/messaging.d.ts.map +1 -0
- package/dist/messaging.js +645 -0
- package/dist/messaging.js.map +1 -0
- package/dist/multi-provider.d.ts +69 -0
- package/dist/multi-provider.d.ts.map +1 -0
- package/dist/multi-provider.js +484 -0
- package/dist/multi-provider.js.map +1 -0
- package/dist/orchestrator.d.ts +65 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +441 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/plugins.d.ts +52 -0
- package/dist/plugins.d.ts.map +1 -0
- package/dist/plugins.js +215 -0
- package/dist/plugins.js.map +1 -0
- package/dist/prism-orchestrator.d.ts +26 -0
- package/dist/prism-orchestrator.d.ts.map +1 -0
- package/dist/prism-orchestrator.js +191 -0
- package/dist/prism-orchestrator.js.map +1 -0
- package/dist/prism.d.ts +46 -0
- package/dist/prism.d.ts.map +1 -0
- package/dist/prism.js +188 -0
- package/dist/prism.js.map +1 -0
- package/dist/privacy.d.ts +23 -0
- package/dist/privacy.d.ts.map +1 -0
- package/dist/privacy.js +220 -0
- package/dist/privacy.js.map +1 -0
- package/dist/proactive.d.ts +30 -0
- package/dist/proactive.d.ts.map +1 -0
- package/dist/proactive.js +260 -0
- package/dist/proactive.js.map +1 -0
- package/dist/refactoring-engine.d.ts +100 -0
- package/dist/refactoring-engine.d.ts.map +1 -0
- package/dist/refactoring-engine.js +717 -0
- package/dist/refactoring-engine.js.map +1 -0
- package/dist/resilience.d.ts +43 -0
- package/dist/resilience.d.ts.map +1 -0
- package/dist/resilience.js +200 -0
- package/dist/resilience.js.map +1 -0
- package/dist/safety.d.ts +40 -0
- package/dist/safety.d.ts.map +1 -0
- package/dist/safety.js +133 -0
- package/dist/safety.js.map +1 -0
- package/dist/sandbox.d.ts +33 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +173 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/scheduler.d.ts +72 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +374 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/semantic-memory.d.ts +70 -0
- package/dist/semantic-memory.d.ts.map +1 -0
- package/dist/semantic-memory.js +430 -0
- package/dist/semantic-memory.js.map +1 -0
- package/dist/skills.d.ts +97 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +575 -0
- package/dist/skills.js.map +1 -0
- package/dist/static/dashboard.html +853 -0
- package/dist/static/hub.html +772 -0
- package/dist/static/index.html +818 -0
- package/dist/static/logo.svg +24 -0
- package/dist/static/workflow-editor.html +913 -0
- package/dist/tools.d.ts +67 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +303 -0
- package/dist/tools.js.map +1 -0
- package/dist/types.d.ts +295 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +90 -0
- package/dist/types.js.map +1 -0
- package/dist/web.d.ts +76 -0
- package/dist/web.d.ts.map +1 -0
- package/dist/web.js +2139 -0
- package/dist/web.js.map +1 -0
- package/dist/workflow-engine.d.ts +114 -0
- package/dist/workflow-engine.d.ts.map +1 -0
- package/dist/workflow-engine.js +855 -0
- package/dist/workflow-engine.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,649 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Java Language Support for LinguClaw
|
|
4
|
+
* Advanced Java parser with class hierarchy and annotation analysis
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.JavaLanguageSupport = exports.JavaAnalyzer = exports.JavaParser = void 0;
|
|
8
|
+
class JavaParser {
|
|
9
|
+
source = '';
|
|
10
|
+
lines = [];
|
|
11
|
+
parse(source, filePath) {
|
|
12
|
+
this.source = source;
|
|
13
|
+
this.lines = source.split('\n');
|
|
14
|
+
try {
|
|
15
|
+
const ast = this.parseJava(source, filePath);
|
|
16
|
+
return {
|
|
17
|
+
ast,
|
|
18
|
+
errors: this.findSyntaxErrors(),
|
|
19
|
+
warnings: [],
|
|
20
|
+
tokens: [],
|
|
21
|
+
comments: this.extractComments(),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
return this.fallbackParse(filePath);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
parseJava(source, filePath) {
|
|
29
|
+
const root = {
|
|
30
|
+
type: 'CompilationUnit',
|
|
31
|
+
id: `${filePath}:0:0`,
|
|
32
|
+
location: this.createLocation(filePath, 0, 0, this.lines.length, 0),
|
|
33
|
+
children: [],
|
|
34
|
+
metadata: { language: 'java', package: '' },
|
|
35
|
+
};
|
|
36
|
+
// Extract package declaration
|
|
37
|
+
const packageMatch = source.match(/package\s+([\w.]+);/);
|
|
38
|
+
if (packageMatch) {
|
|
39
|
+
root.metadata.package = packageMatch[1];
|
|
40
|
+
root.children.push({
|
|
41
|
+
type: 'PackageDeclaration',
|
|
42
|
+
id: `${filePath}:1:0`,
|
|
43
|
+
location: this.createLocation(filePath, 1, 0, 1, packageMatch[0].length),
|
|
44
|
+
children: [],
|
|
45
|
+
metadata: { name: packageMatch[1] },
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
// Parse imports
|
|
49
|
+
const imports = this.parseImports(source, filePath);
|
|
50
|
+
root.children.push(...imports);
|
|
51
|
+
// Parse type declarations (classes, interfaces, enums, records)
|
|
52
|
+
const types = this.parseTypeDeclarations(source, filePath);
|
|
53
|
+
root.children.push(...types);
|
|
54
|
+
return root;
|
|
55
|
+
}
|
|
56
|
+
parseImports(source, filePath) {
|
|
57
|
+
const imports = [];
|
|
58
|
+
const importRegex = /import\s+(static\s+)?([\w.*]+);/g;
|
|
59
|
+
let match;
|
|
60
|
+
while ((match = importRegex.exec(source)) !== null) {
|
|
61
|
+
const isStatic = !!match[1];
|
|
62
|
+
const importPath = match[2];
|
|
63
|
+
const isWildcard = importPath.endsWith('.*');
|
|
64
|
+
imports.push({
|
|
65
|
+
type: 'ImportDeclaration',
|
|
66
|
+
id: `${filePath}:0:${match.index}`,
|
|
67
|
+
location: this.createLocation(filePath, 0, match.index, 0, match.index + match[0].length),
|
|
68
|
+
children: [],
|
|
69
|
+
metadata: {
|
|
70
|
+
path: importPath,
|
|
71
|
+
isStatic,
|
|
72
|
+
isWildcard,
|
|
73
|
+
isJavaStandard: importPath.startsWith('java.') || importPath.startsWith('javax.'),
|
|
74
|
+
isThirdParty: !importPath.startsWith('java.') &&
|
|
75
|
+
!importPath.startsWith('javax.') &&
|
|
76
|
+
!importPath.startsWith(this.getPackageName(source)),
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return imports;
|
|
81
|
+
}
|
|
82
|
+
parseTypeDeclarations(source, filePath) {
|
|
83
|
+
const types = [];
|
|
84
|
+
// Match class, interface, enum, record, annotation declarations
|
|
85
|
+
const typeRegex = /(public\s+|private\s+|protected\s+)?(abstract\s+|final\s+|sealed\s+|non-sealed\s+)?(static\s+)?(class|interface|enum|record|@interface)\s+(\w+)(?:<([^>]+)>)?(?:\s+(?:extends|implements)\s+([^{]+))?/g;
|
|
86
|
+
let match;
|
|
87
|
+
while ((match = typeRegex.exec(source)) !== null) {
|
|
88
|
+
const visibility = match[1]?.trim() || 'package-private';
|
|
89
|
+
const modifiers = match[2]?.trim() || '';
|
|
90
|
+
const isStatic = !!match[3];
|
|
91
|
+
const typeKind = match[4];
|
|
92
|
+
const typeName = match[5];
|
|
93
|
+
const generics = match[6];
|
|
94
|
+
const extendsOrImplements = match[7]?.trim();
|
|
95
|
+
// Find the body
|
|
96
|
+
const bodyStart = source.indexOf('{', match.index);
|
|
97
|
+
if (bodyStart === -1)
|
|
98
|
+
continue;
|
|
99
|
+
const bodyEnd = this.findMatchingBrace(source, bodyStart);
|
|
100
|
+
const body = source.substring(bodyStart + 1, bodyEnd);
|
|
101
|
+
const typeNode = {
|
|
102
|
+
type: this.mapTypeKind(typeKind),
|
|
103
|
+
id: `${filePath}:0:${match.index}`,
|
|
104
|
+
location: this.createLocation(filePath, 0, match.index, 0, bodyEnd + 1),
|
|
105
|
+
children: this.parseClassBody(body, filePath),
|
|
106
|
+
metadata: {
|
|
107
|
+
name: typeName,
|
|
108
|
+
visibility,
|
|
109
|
+
isAbstract: modifiers.includes('abstract'),
|
|
110
|
+
isFinal: modifiers.includes('final'),
|
|
111
|
+
isSealed: modifiers.includes('sealed'),
|
|
112
|
+
isStatic,
|
|
113
|
+
isRecord: typeKind === 'record',
|
|
114
|
+
isAnnotation: typeKind === '@interface',
|
|
115
|
+
generics: generics ? generics.split(',').map(g => g.trim()) : [],
|
|
116
|
+
extends: this.parseExtends(extendsOrImplements),
|
|
117
|
+
implements: this.parseImplements(extendsOrImplements),
|
|
118
|
+
annotations: this.extractAnnotations(source, match.index),
|
|
119
|
+
javadoc: this.extractJavadoc(source, match.index),
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
types.push(typeNode);
|
|
123
|
+
}
|
|
124
|
+
return types;
|
|
125
|
+
}
|
|
126
|
+
parseClassBody(body, filePath) {
|
|
127
|
+
const members = [];
|
|
128
|
+
const lines = body.split('\n');
|
|
129
|
+
// Parse fields
|
|
130
|
+
const fieldRegex = /(private\s+|public\s+|protected\s+)?(static\s+)?(final\s+)?(transient\s+)?(volatile\s+)?([\w<>,\s\[\]]+?)\s+(\w+)\s*(?:=\s*([^;]+))?;/g;
|
|
131
|
+
let match;
|
|
132
|
+
while ((match = fieldRegex.exec(body)) !== null) {
|
|
133
|
+
members.push({
|
|
134
|
+
type: 'FieldDeclaration',
|
|
135
|
+
id: `${filePath}:0:${match.index}`,
|
|
136
|
+
location: this.createLocation(filePath, 0, match.index, 0, match.index + match[0].length),
|
|
137
|
+
children: [],
|
|
138
|
+
metadata: {
|
|
139
|
+
name: match[7],
|
|
140
|
+
type: match[6].trim(),
|
|
141
|
+
visibility: match[1]?.trim() || 'package-private',
|
|
142
|
+
isStatic: !!match[2],
|
|
143
|
+
isFinal: !!match[3],
|
|
144
|
+
isTransient: !!match[4],
|
|
145
|
+
isVolatile: !!match[5],
|
|
146
|
+
initializer: match[8]?.trim(),
|
|
147
|
+
annotations: this.extractAnnotations(body, match.index),
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
// Parse methods
|
|
152
|
+
const methodRegex = /(private\s+|public\s+|protected\s+)?(static\s+)?(abstract\s+)?(final\s+)?(synchronized\s+)?(native\s+)?(strictfp\s+)?(<[^>]+>\s+)?([\w<>,\s\[\]]+?)\s+(\w+)\s*\(([^)]*)\)\s*(?:throws\s+([^{]+))?/g;
|
|
153
|
+
while ((match = methodRegex.exec(body)) !== null) {
|
|
154
|
+
const visibility = match[1]?.trim() || 'package-private';
|
|
155
|
+
const isStatic = !!match[2];
|
|
156
|
+
const isAbstract = !!match[3];
|
|
157
|
+
const isFinal = !!match[4];
|
|
158
|
+
const isSynchronized = !!match[5];
|
|
159
|
+
const isNative = !!match[6];
|
|
160
|
+
const generics = match[8];
|
|
161
|
+
const returnType = match[9].trim();
|
|
162
|
+
const methodName = match[10];
|
|
163
|
+
const params = match[11];
|
|
164
|
+
const throwsClause = match[12]?.trim();
|
|
165
|
+
// Find method body
|
|
166
|
+
const afterSignature = body.indexOf('(', match.index) + params.length + 1;
|
|
167
|
+
let bodyStart = body.indexOf('{', afterSignature);
|
|
168
|
+
let hasBody = bodyStart !== -1;
|
|
169
|
+
let bodyEnd = hasBody ? this.findMatchingBrace(body, bodyStart) : afterSignature;
|
|
170
|
+
const methodNode = {
|
|
171
|
+
type: 'MethodDeclaration',
|
|
172
|
+
id: `${filePath}:0:${match.index}`,
|
|
173
|
+
location: this.createLocation(filePath, 0, match.index, 0, hasBody ? bodyEnd + 1 : afterSignature + 1),
|
|
174
|
+
children: hasBody ? this.parseMethodBody(body.substring(bodyStart + 1, bodyEnd), filePath) : [],
|
|
175
|
+
metadata: {
|
|
176
|
+
name: methodName,
|
|
177
|
+
returnType,
|
|
178
|
+
visibility,
|
|
179
|
+
isStatic,
|
|
180
|
+
isAbstract,
|
|
181
|
+
isFinal,
|
|
182
|
+
isSynchronized,
|
|
183
|
+
isNative,
|
|
184
|
+
hasBody,
|
|
185
|
+
isConstructor: methodName === this.getEnclosingClassName(body, match.index),
|
|
186
|
+
generics: generics ? generics.replace(/[<>]/g, '').split(',').map(g => g.trim()) : [],
|
|
187
|
+
parameters: this.parseJavaParameters(params),
|
|
188
|
+
throws: throwsClause ? throwsClause.split(',').map(t => t.trim()) : [],
|
|
189
|
+
annotations: this.extractAnnotations(body, match.index),
|
|
190
|
+
javadoc: this.extractJavadoc(body, match.index),
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
members.push(methodNode);
|
|
194
|
+
}
|
|
195
|
+
// Parse inner classes
|
|
196
|
+
const innerClassRegex = /class\s+(\w+)/g;
|
|
197
|
+
while ((match = innerClassRegex.exec(body)) !== null) {
|
|
198
|
+
// Skip if it's part of a method (heuristic)
|
|
199
|
+
const precedingText = body.substring(0, match.index);
|
|
200
|
+
const openBraces = (precedingText.match(/{/g) || []).length;
|
|
201
|
+
const closeBraces = (precedingText.match(/}/g) || []).length;
|
|
202
|
+
if (openBraces === closeBraces) {
|
|
203
|
+
// Top-level in class body
|
|
204
|
+
const innerBodyStart = body.indexOf('{', match.index);
|
|
205
|
+
const innerBodyEnd = this.findMatchingBrace(body, innerBodyStart);
|
|
206
|
+
members.push({
|
|
207
|
+
type: 'ClassDeclaration',
|
|
208
|
+
id: `${filePath}:0:${match.index}`,
|
|
209
|
+
location: this.createLocation(filePath, 0, match.index, 0, innerBodyEnd + 1),
|
|
210
|
+
children: [],
|
|
211
|
+
metadata: {
|
|
212
|
+
name: match[1],
|
|
213
|
+
isInnerClass: true,
|
|
214
|
+
isStatic: precedingText.substring(match.index - 20, match.index).includes('static'),
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return members;
|
|
220
|
+
}
|
|
221
|
+
parseMethodBody(body, filePath) {
|
|
222
|
+
const statements = [];
|
|
223
|
+
// Variable declarations
|
|
224
|
+
const varRegex = /([\w<>,\s\[\]]+?)\s+(\w+)\s*=\s*([^;]+);/g;
|
|
225
|
+
let match;
|
|
226
|
+
while ((match = varRegex.exec(body)) !== null) {
|
|
227
|
+
statements.push({
|
|
228
|
+
type: 'VariableDeclaration',
|
|
229
|
+
id: `${filePath}:0:${match.index}`,
|
|
230
|
+
location: this.createLocation(filePath, 0, match.index, 0, match.index + match[0].length),
|
|
231
|
+
children: [],
|
|
232
|
+
metadata: {
|
|
233
|
+
type: match[1].trim(),
|
|
234
|
+
name: match[2],
|
|
235
|
+
initializer: match[3].trim(),
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
// Method invocations (for call graph)
|
|
240
|
+
const invocationRegex = /(\w+(?:\.\w+)*)\s*\(/g;
|
|
241
|
+
while ((match = invocationRegex.exec(body)) !== null) {
|
|
242
|
+
const preceding = body.substring(Math.max(0, match.index - 20), match.index);
|
|
243
|
+
// Skip if it's a control structure
|
|
244
|
+
if (!/(if|while|for|switch|catch|synchronized)\s*$/.test(preceding)) {
|
|
245
|
+
statements.push({
|
|
246
|
+
type: 'MethodInvocation',
|
|
247
|
+
id: `${filePath}:0:${match.index}`,
|
|
248
|
+
location: this.createLocation(filePath, 0, match.index, 0, match.index + match[0].length),
|
|
249
|
+
children: [],
|
|
250
|
+
metadata: {
|
|
251
|
+
method: match[1],
|
|
252
|
+
isQualified: match[1].includes('.'),
|
|
253
|
+
object: match[1].includes('.') ? match[1].split('.')[0] : 'this',
|
|
254
|
+
methodName: match[1].includes('.') ? match[1].split('.').pop() : match[1],
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// Exception handling
|
|
260
|
+
const tryCatchRegex = /try\s*\{/g;
|
|
261
|
+
while ((match = tryCatchRegex.exec(body)) !== null) {
|
|
262
|
+
statements.push({
|
|
263
|
+
type: 'TryStatement',
|
|
264
|
+
id: `${filePath}:0:${match.index}`,
|
|
265
|
+
location: this.createLocation(filePath, 0, match.index, 0, match.index + 3),
|
|
266
|
+
children: [],
|
|
267
|
+
metadata: {},
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
return statements;
|
|
271
|
+
}
|
|
272
|
+
parseJavaParameters(params) {
|
|
273
|
+
if (!params.trim())
|
|
274
|
+
return [];
|
|
275
|
+
const result = [];
|
|
276
|
+
const paramList = this.splitParams(params);
|
|
277
|
+
for (const param of paramList) {
|
|
278
|
+
const trimmed = param.trim();
|
|
279
|
+
if (!trimmed)
|
|
280
|
+
continue;
|
|
281
|
+
const isFinal = trimmed.startsWith('final ');
|
|
282
|
+
const withoutFinal = isFinal ? trimmed.substring(6) : trimmed;
|
|
283
|
+
const isVarArgs = withoutFinal.includes('...');
|
|
284
|
+
// Split type and name
|
|
285
|
+
const parts = withoutFinal.replace('...', '').trim().split(/\s+/);
|
|
286
|
+
if (parts.length >= 2) {
|
|
287
|
+
const type = parts.slice(0, -1).join(' ');
|
|
288
|
+
const name = parts[parts.length - 1];
|
|
289
|
+
result.push({
|
|
290
|
+
name,
|
|
291
|
+
type: isVarArgs ? type + '...' : type,
|
|
292
|
+
isVarArgs,
|
|
293
|
+
isFinal,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return result;
|
|
298
|
+
}
|
|
299
|
+
parseExtends(extendsOrImplements) {
|
|
300
|
+
if (!extendsOrImplements)
|
|
301
|
+
return undefined;
|
|
302
|
+
const match = extendsOrImplements.match(/extends\s+(\w+)/);
|
|
303
|
+
return match ? match[1] : undefined;
|
|
304
|
+
}
|
|
305
|
+
parseImplements(extendsOrImplements) {
|
|
306
|
+
if (!extendsOrImplements)
|
|
307
|
+
return [];
|
|
308
|
+
const match = extendsOrImplements.match(/implements\s+(.+)$/);
|
|
309
|
+
if (match) {
|
|
310
|
+
return match[1].split(',').map(i => i.trim());
|
|
311
|
+
}
|
|
312
|
+
return [];
|
|
313
|
+
}
|
|
314
|
+
extractAnnotations(source, position) {
|
|
315
|
+
const annotations = [];
|
|
316
|
+
const preceding = source.substring(0, position);
|
|
317
|
+
const annotationRegex = /@(\w+)(?:\(([^)]*)\))?/g;
|
|
318
|
+
let match;
|
|
319
|
+
// Find annotations in the last few lines before position
|
|
320
|
+
const relevantText = preceding.substring(Math.max(0, preceding.length - 500));
|
|
321
|
+
while ((match = annotationRegex.exec(relevantText)) !== null) {
|
|
322
|
+
annotations.push(match[1]);
|
|
323
|
+
}
|
|
324
|
+
return annotations;
|
|
325
|
+
}
|
|
326
|
+
extractJavadoc(source, position) {
|
|
327
|
+
const preceding = source.substring(0, position);
|
|
328
|
+
const javadocMatch = preceding.match(/\/\*\*([\s\S]*?)\*\/\s*$/);
|
|
329
|
+
if (javadocMatch) {
|
|
330
|
+
return javadocMatch[1].replace(/^\s*\*\s?/gm, '').trim();
|
|
331
|
+
}
|
|
332
|
+
return undefined;
|
|
333
|
+
}
|
|
334
|
+
mapTypeKind(kind) {
|
|
335
|
+
const map = {
|
|
336
|
+
'class': 'ClassDeclaration',
|
|
337
|
+
'interface': 'InterfaceDeclaration',
|
|
338
|
+
'enum': 'EnumDeclaration',
|
|
339
|
+
'record': 'RecordDeclaration',
|
|
340
|
+
'@interface': 'AnnotationDeclaration',
|
|
341
|
+
};
|
|
342
|
+
return map[kind] || 'TypeDeclaration';
|
|
343
|
+
}
|
|
344
|
+
findMatchingBrace(source, startPos) {
|
|
345
|
+
let depth = 1;
|
|
346
|
+
for (let i = startPos + 1; i < source.length; i++) {
|
|
347
|
+
if (source[i] === '{')
|
|
348
|
+
depth++;
|
|
349
|
+
if (source[i] === '}')
|
|
350
|
+
depth--;
|
|
351
|
+
if (depth === 0)
|
|
352
|
+
return i;
|
|
353
|
+
}
|
|
354
|
+
return source.length - 1;
|
|
355
|
+
}
|
|
356
|
+
splitParams(params) {
|
|
357
|
+
const result = [];
|
|
358
|
+
let current = '';
|
|
359
|
+
let depth = 0;
|
|
360
|
+
for (const char of params) {
|
|
361
|
+
if (char === '<' || char === '(')
|
|
362
|
+
depth++;
|
|
363
|
+
if (char === '>' || char === ')')
|
|
364
|
+
depth--;
|
|
365
|
+
if (char === ',' && depth === 0) {
|
|
366
|
+
result.push(current.trim());
|
|
367
|
+
current = '';
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
current += char;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (current.trim()) {
|
|
374
|
+
result.push(current.trim());
|
|
375
|
+
}
|
|
376
|
+
return result;
|
|
377
|
+
}
|
|
378
|
+
getPackageName(source) {
|
|
379
|
+
const match = source.match(/package\s+([\w.]+);/);
|
|
380
|
+
return match ? match[1] : '';
|
|
381
|
+
}
|
|
382
|
+
getEnclosingClassName(source, position) {
|
|
383
|
+
const preceding = source.substring(0, position);
|
|
384
|
+
const classMatch = preceding.match(/class\s+(\w+)/g);
|
|
385
|
+
if (classMatch) {
|
|
386
|
+
const lastMatch = classMatch[classMatch.length - 1];
|
|
387
|
+
const nameMatch = lastMatch.match(/class\s+(\w+)/);
|
|
388
|
+
return nameMatch ? nameMatch[1] : null;
|
|
389
|
+
}
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
createLocation(file, startLine, startCol, endLine, endCol) {
|
|
393
|
+
return {
|
|
394
|
+
file,
|
|
395
|
+
startLine,
|
|
396
|
+
startColumn: startCol,
|
|
397
|
+
endLine,
|
|
398
|
+
endColumn: endCol,
|
|
399
|
+
byteOffset: 0,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
findSyntaxErrors() {
|
|
403
|
+
const errors = [];
|
|
404
|
+
let braceCount = 0;
|
|
405
|
+
let parenCount = 0;
|
|
406
|
+
for (let i = 0; i < this.lines.length; i++) {
|
|
407
|
+
const line = this.lines[i];
|
|
408
|
+
for (const char of line) {
|
|
409
|
+
if (char === '{')
|
|
410
|
+
braceCount++;
|
|
411
|
+
if (char === '}')
|
|
412
|
+
braceCount--;
|
|
413
|
+
if (char === '(')
|
|
414
|
+
parenCount++;
|
|
415
|
+
if (char === ')')
|
|
416
|
+
parenCount--;
|
|
417
|
+
}
|
|
418
|
+
if (braceCount < 0) {
|
|
419
|
+
errors.push({ message: 'Unexpected }', line: i + 1, severity: 'error' });
|
|
420
|
+
braceCount = 0;
|
|
421
|
+
}
|
|
422
|
+
if (parenCount < 0) {
|
|
423
|
+
errors.push({ message: 'Unexpected )', line: i + 1, severity: 'error' });
|
|
424
|
+
parenCount = 0;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if (braceCount > 0) {
|
|
428
|
+
errors.push({ message: `Unclosed braces: ${braceCount}`, line: this.lines.length, severity: 'error' });
|
|
429
|
+
}
|
|
430
|
+
return errors;
|
|
431
|
+
}
|
|
432
|
+
extractComments() {
|
|
433
|
+
const comments = [];
|
|
434
|
+
for (let i = 0; i < this.lines.length; i++) {
|
|
435
|
+
const line = this.lines[i];
|
|
436
|
+
// Single line comments
|
|
437
|
+
const singleIdx = line.indexOf('//');
|
|
438
|
+
if (singleIdx !== -1) {
|
|
439
|
+
comments.push({
|
|
440
|
+
type: 'Line',
|
|
441
|
+
value: line.substring(singleIdx + 2).trim(),
|
|
442
|
+
line: i + 1,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
// Block comments
|
|
447
|
+
const blockRegex = /\/\*[\s\S]*?\*\//g;
|
|
448
|
+
let match;
|
|
449
|
+
while ((match = blockRegex.exec(this.source)) !== null) {
|
|
450
|
+
const lineNum = this.source.substring(0, match.index).split('\n').length;
|
|
451
|
+
comments.push({
|
|
452
|
+
type: 'Block',
|
|
453
|
+
value: match[0].substring(2, match[0].length - 2).trim(),
|
|
454
|
+
line: lineNum,
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
return comments;
|
|
458
|
+
}
|
|
459
|
+
fallbackParse(filePath) {
|
|
460
|
+
return {
|
|
461
|
+
ast: this.parseJava(this.source, filePath),
|
|
462
|
+
errors: this.findSyntaxErrors(),
|
|
463
|
+
warnings: [{ message: 'Using fallback parser' }],
|
|
464
|
+
tokens: [],
|
|
465
|
+
comments: this.extractComments(),
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
exports.JavaParser = JavaParser;
|
|
470
|
+
class JavaAnalyzer {
|
|
471
|
+
analyze(ast, context) {
|
|
472
|
+
const symbolTable = this.buildSymbolTable(ast);
|
|
473
|
+
const classHierarchy = this.analyzeClassHierarchy(ast);
|
|
474
|
+
const springAnalysis = this.analyzeSpringAnnotations(ast);
|
|
475
|
+
const securityIssues = this.findSecurityIssues(ast);
|
|
476
|
+
return {
|
|
477
|
+
symbols: symbolTable,
|
|
478
|
+
callGraph: this.buildCallGraph(ast),
|
|
479
|
+
dataFlow: { definitions: new Map(), uses: new Map(), taintedSources: [], sinks: [] },
|
|
480
|
+
controlFlow: { nodes: [], edges: [], loops: [], branches: [] },
|
|
481
|
+
typeInference: new Map(),
|
|
482
|
+
metrics: this.calculateMetrics(ast),
|
|
483
|
+
suggestions: [
|
|
484
|
+
...springAnalysis.suggestions,
|
|
485
|
+
...securityIssues.map(i => ({
|
|
486
|
+
type: 'security',
|
|
487
|
+
severity: i.severity,
|
|
488
|
+
message: i.description,
|
|
489
|
+
remediation: i.remediation,
|
|
490
|
+
})),
|
|
491
|
+
],
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
buildSymbolTable(ast) {
|
|
495
|
+
return {
|
|
496
|
+
variables: new Map(),
|
|
497
|
+
functions: new Map(),
|
|
498
|
+
classes: new Map(),
|
|
499
|
+
modules: new Map(),
|
|
500
|
+
imports: [],
|
|
501
|
+
exports: [],
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
analyzeClassHierarchy(ast) {
|
|
505
|
+
const classes = [];
|
|
506
|
+
const traverse = (node) => {
|
|
507
|
+
if (node.type === 'ClassDeclaration') {
|
|
508
|
+
classes.push({
|
|
509
|
+
name: node.metadata.name,
|
|
510
|
+
extends: node.metadata.extends,
|
|
511
|
+
implements: node.metadata.implements,
|
|
512
|
+
isAbstract: node.metadata.isAbstract,
|
|
513
|
+
isFinal: node.metadata.isFinal,
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
node.children.forEach(traverse);
|
|
517
|
+
};
|
|
518
|
+
traverse(ast);
|
|
519
|
+
return { classes, inheritanceDepth: this.calculateInheritanceDepth(classes) };
|
|
520
|
+
}
|
|
521
|
+
calculateInheritanceDepth(classes) {
|
|
522
|
+
let maxDepth = 0;
|
|
523
|
+
const getDepth = (className, visited = new Set()) => {
|
|
524
|
+
if (visited.has(className))
|
|
525
|
+
return 0; // Circular
|
|
526
|
+
visited.add(className);
|
|
527
|
+
const cls = classes.find(c => c.name === className);
|
|
528
|
+
if (!cls || !cls.extends)
|
|
529
|
+
return 0;
|
|
530
|
+
return 1 + getDepth(cls.extends, visited);
|
|
531
|
+
};
|
|
532
|
+
for (const cls of classes) {
|
|
533
|
+
maxDepth = Math.max(maxDepth, getDepth(cls.name));
|
|
534
|
+
}
|
|
535
|
+
return maxDepth;
|
|
536
|
+
}
|
|
537
|
+
analyzeSpringAnnotations(ast) {
|
|
538
|
+
const suggestions = [];
|
|
539
|
+
let controllerCount = 0;
|
|
540
|
+
let serviceCount = 0;
|
|
541
|
+
let repositoryCount = 0;
|
|
542
|
+
const traverse = (node) => {
|
|
543
|
+
const annotations = node.metadata.annotations || [];
|
|
544
|
+
if (annotations.includes('Controller') || annotations.includes('RestController')) {
|
|
545
|
+
controllerCount++;
|
|
546
|
+
}
|
|
547
|
+
if (annotations.includes('Service'))
|
|
548
|
+
serviceCount++;
|
|
549
|
+
if (annotations.includes('Repository'))
|
|
550
|
+
repositoryCount++;
|
|
551
|
+
// Check for proper transaction management
|
|
552
|
+
if (annotations.includes('Service') && !annotations.includes('Transactional')) {
|
|
553
|
+
suggestions.push({
|
|
554
|
+
type: 'best-practice',
|
|
555
|
+
severity: 'info',
|
|
556
|
+
message: `Service class ${node.metadata.name} missing @Transactional`,
|
|
557
|
+
remediation: 'Consider adding @Transactional for database operations',
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
node.children.forEach(traverse);
|
|
561
|
+
};
|
|
562
|
+
traverse(ast);
|
|
563
|
+
// Architecture validation
|
|
564
|
+
if (controllerCount > 0 && serviceCount === 0) {
|
|
565
|
+
suggestions.push({
|
|
566
|
+
type: 'architecture',
|
|
567
|
+
severity: 'warning',
|
|
568
|
+
message: 'Controllers without Service layer detected',
|
|
569
|
+
remediation: 'Add Service layer for business logic separation',
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
return { suggestions };
|
|
573
|
+
}
|
|
574
|
+
findSecurityIssues(ast) {
|
|
575
|
+
const issues = [];
|
|
576
|
+
const traverse = (node) => {
|
|
577
|
+
// Check for SQL injection in string concatenation
|
|
578
|
+
if (node.type === 'MethodInvocation') {
|
|
579
|
+
const method = node.metadata.method;
|
|
580
|
+
if (method?.includes('createQuery') || method?.includes('prepareStatement')) {
|
|
581
|
+
// Check for string concatenation in parameters
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
// Check for insecure deserialization
|
|
585
|
+
const annotations = node.metadata.annotations || [];
|
|
586
|
+
if (annotations.includes('RequestMapping') || annotations.includes('GetMapping')) {
|
|
587
|
+
const params = node.metadata.parameters || [];
|
|
588
|
+
for (const param of params) {
|
|
589
|
+
if (!param.annotations?.includes('Valid')) {
|
|
590
|
+
issues.push({
|
|
591
|
+
id: 'JAVA001',
|
|
592
|
+
severity: 'medium',
|
|
593
|
+
category: 'broken-authentication',
|
|
594
|
+
location: node.location,
|
|
595
|
+
description: `Parameter ${param.name} in API endpoint lacks validation`,
|
|
596
|
+
remediation: 'Add @Valid annotation and validation constraints',
|
|
597
|
+
falsePositiveLikelihood: 0.3,
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
// Check for hardcoded credentials
|
|
603
|
+
if (node.type === 'FieldDeclaration') {
|
|
604
|
+
const name = node.metadata.name?.toLowerCase();
|
|
605
|
+
if (name?.includes('password') || name?.includes('secret') || name?.includes('key')) {
|
|
606
|
+
if (node.metadata.initializer) {
|
|
607
|
+
issues.push({
|
|
608
|
+
id: 'JAVA002',
|
|
609
|
+
severity: 'critical',
|
|
610
|
+
category: 'sensitive-data-exposure',
|
|
611
|
+
location: node.location,
|
|
612
|
+
description: `Potential hardcoded credential: ${node.metadata.name}`,
|
|
613
|
+
remediation: 'Use externalized configuration (e.g., Spring @Value)',
|
|
614
|
+
falsePositiveLikelihood: 0.4,
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
node.children.forEach(traverse);
|
|
620
|
+
};
|
|
621
|
+
traverse(ast);
|
|
622
|
+
return issues;
|
|
623
|
+
}
|
|
624
|
+
buildCallGraph(ast) {
|
|
625
|
+
return { nodes: [], edges: [], entryPoints: [], deadCode: [] };
|
|
626
|
+
}
|
|
627
|
+
calculateMetrics(ast) {
|
|
628
|
+
return {
|
|
629
|
+
linesOfCode: 0,
|
|
630
|
+
logicalLines: 0,
|
|
631
|
+
commentLines: 0,
|
|
632
|
+
blankLines: 0,
|
|
633
|
+
cyclomaticComplexity: 0,
|
|
634
|
+
cognitiveComplexity: 0,
|
|
635
|
+
halsteadMetrics: { operators: 0, operands: 0, uniqueOperators: 0, uniqueOperands: 0, volume: 0, difficulty: 0, effort: 0, timeToProgram: 0, bugsDelivered: 0 },
|
|
636
|
+
maintainabilityIndex: 0,
|
|
637
|
+
duplicateRate: 0,
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
exports.JavaAnalyzer = JavaAnalyzer;
|
|
642
|
+
exports.JavaLanguageSupport = {
|
|
643
|
+
id: 'java',
|
|
644
|
+
name: 'Java',
|
|
645
|
+
extensions: ['.java'],
|
|
646
|
+
parser: new JavaParser(),
|
|
647
|
+
analyzer: new JavaAnalyzer(),
|
|
648
|
+
};
|
|
649
|
+
//# sourceMappingURL=java.js.map
|