Skip to content

Commit ee3d8ea

Browse files
feat: added TokenstreamRewriter
1 parent c843e45 commit ee3d8ea

File tree

3 files changed

+87
-24
lines changed

3 files changed

+87
-24
lines changed

src/migration/related/ApexMigration.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as fs from 'fs';
33
// import { sfcclicommand } from '../../utils/sfcli/commands/sfclicommand';
44
import * as shell from 'shelljs';
55
import { Org } from '@salesforce/core';
6-
import { ApexASTParser, MethodCall } from '../../utils/apex/parser/apexparser';
6+
import { ApexASTParser, InterfaceImplements, MethodCall } from '../../utils/apex/parser/apexparser';
77
import { MigrationResult, RelatedObjectsMigrate } from '../interfaces';
88
import { sfProject } from '../../utils/sfcli/project/sfProject';
99
import { fileutil, File } from '../../utils/file/fileutil';
@@ -39,7 +39,13 @@ export class ApexMigration extends BaseRelatedObjectMigration implements Related
3939

4040
public processApexFile(file: File): void {
4141
const fileContent = fs.readFileSync(file.location, 'utf8');
42-
const interfaces = new Set<string>(['VlocityOpenInterface', 'VlocityOpenInterface2', 'Callable']);
42+
const interfaces: InterfaceImplements[] = [];
43+
interfaces.push(
44+
new InterfaceImplements('VlocityOpenInterface', this.namespace),
45+
new InterfaceImplements('VlocityOpenInterface2', this.namespace),
46+
new InterfaceImplements('Callable')
47+
);
48+
// const interfaces = new Set<string>(['VlocityOpenInterface', 'VlocityOpenInterface2', 'Callable']);
4349
const methodCalls = new Set<MethodCall>();
4450
methodCalls.add(new MethodCall('process', 'DRGlobal', this.namespace));
4551
methodCalls.add(new MethodCall('processObjectsJSON', 'DRGlobal', this.namespace));

src/utils/apex/parser/apexparser.ts

Lines changed: 63 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,22 @@ import {
1414
CompilationUnitContext,
1515
TypeRefContext,
1616
} from '@apexdevtools/apex-parser';
17-
import { CharStreams, Token } from 'antlr4ts';
17+
import { CharStreams, Token, TokenStreamRewriter } from 'antlr4ts';
1818
import { ParseTreeWalker } from 'antlr4ts/tree/ParseTreeWalker';
1919

2020
export class ApexASTParser {
2121
private apexFileContent: string;
22-
private implementsInterface: Map<string, Token> = new Map();
22+
private implementsInterface: Map<InterfaceImplements, Token[]> = new Map();
2323
private methodParameter: Map<string, Token> = new Map();
2424
private namespaceChange: Map<string, Token[]> = new Map();
2525
private namespace: string;
2626
// private callsMethods: Map<string, Token[]>;
27-
private interfaceNames: Set<string>;
27+
private interfaceNames: InterfaceImplements[];
2828
// private className: string;
2929
private astListener: ApexParserListener;
3030
private methodCalls: Set<MethodCall>;
3131

32-
public get implementsInterfaces(): Map<string, Token> {
32+
public get implementsInterfaces(): Map<InterfaceImplements, Token[]> {
3333
return this.implementsInterface;
3434
}
3535

@@ -42,7 +42,7 @@ export class ApexASTParser {
4242

4343
public constructor(
4444
apexFileContent: string,
45-
interfaceNames: Set<string>,
45+
interfaceNames: InterfaceImplements[],
4646
methodCalls: Set<MethodCall>,
4747
namespace: string
4848
) {
@@ -63,6 +63,15 @@ export class ApexASTParser {
6363
return context;
6464
}
6565

66+
public rewrite(): string {
67+
const lexer = new ApexLexer(new CaseInsensitiveInputStream(CharStreams.fromString(this.apexFileContent)));
68+
const tokens = new CommonTokenStream(lexer);
69+
const rewriter = new TokenStreamRewriter(tokens);
70+
const parser = new ApexParser(tokens);
71+
parser.compilationUnit();
72+
return rewriter.getText();
73+
}
74+
6675
private createASTListener(): ApexParserListener {
6776
class ApexMigrationListener implements ApexParserListener {
6877
public constructor(private parser: ApexASTParser) {
@@ -73,15 +82,23 @@ export class ApexASTParser {
7382
if (!interfaceToBeSearched) return;
7483
if (!ctx.typeList() || !ctx.typeList().typeRef()) return;
7584
for (const typeRefContext of ctx.typeList().typeRef())
76-
for (const typeNameContext of typeRefContext.typeName()) {
77-
if (!typeNameContext.id() || !typeNameContext.id().Identifier()) continue;
78-
if (interfaceToBeSearched.has(typeNameContext.id().Identifier().symbol.text)) {
79-
this.parser.implementsInterface.set(
80-
typeNameContext.id().Identifier().symbol.text,
81-
typeNameContext.id().Identifier().symbol
82-
);
83-
}
85+
for (const toSearch of this.parser.interfaceNames) {
86+
const matchingTokens = InterfaceMatcher.getMatchingTokens(toSearch, typeRefContext);
87+
if (matchingTokens.length === 0) continue;
88+
this.parser.implementsInterface.set(toSearch, matchingTokens);
89+
// Logger.logger.info('For interface ${toSearch.name} found tokens ${matchingTokens}');
8490
}
91+
/*
92+
for (const typeNameContext of typeRefContext.typeName()) {
93+
if (!typeNameContext.id() || !typeNameContext.id().Identifier()) continue;
94+
if (interfaceToBeSearched.has(typeNameContext.id().Identifier().symbol.text)) {
95+
this.parser.implementsInterface.set(
96+
typeNameContext.id().Identifier().symbol.text,
97+
typeNameContext.id().Identifier().symbol
98+
);
99+
}
100+
}
101+
*/
85102
}
86103
public enterDotExpression(ctx: DotExpressionContext): void {
87104
// console.log('*********');
@@ -150,3 +167,36 @@ export class MethodCall {
150167
else return `${this.className}.${this.methodName}()`;
151168
}
152169
}
170+
171+
export class InterfaceImplements {
172+
public name: string;
173+
public namespace: string;
174+
175+
public constructor(name: string, namespace?: string) {
176+
this.name = name;
177+
if (namespace) this.namespace = namespace;
178+
}
179+
}
180+
export class InterfaceMatcher {
181+
public static getMatchingTokens(checkFor: InterfaceImplements, ctx: TypeRefContext): Token[] {
182+
const tokens: Token[] = [];
183+
const typeNameContexts = ctx.typeName();
184+
if (!typeNameContexts) return tokens;
185+
if (
186+
!checkFor.namespace &&
187+
typeNameContexts.length === 1 &&
188+
checkFor.name === typeNameContexts[0]?.id()?.Identifier()?.symbol?.text
189+
) {
190+
tokens.push(typeNameContexts[0].id().Identifier().symbol);
191+
} else if (
192+
checkFor.namespace &&
193+
typeNameContexts.length === 2 &&
194+
checkFor.namespace === typeNameContexts[0]?.id()?.Identifier()?.symbol?.text &&
195+
checkFor.name === typeNameContexts[1]?.id()?.Identifier()?.symbol?.text
196+
) {
197+
tokens.push(typeNameContexts[0].id().Identifier().symbol);
198+
tokens.push(typeNameContexts[1].id().Identifier().symbol);
199+
}
200+
return tokens;
201+
}
202+
}

test/utils/apex/parser/apexparser.test.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { expect } from '@salesforce/command/lib/test';
2-
import { ApexASTParser, MethodCall } from '../../../../src/utils/apex/parser/apexparser';
2+
import { ApexASTParser, InterfaceImplements, MethodCall } from '../../../../src/utils/apex/parser/apexparser';
33

44
describe('ApexASTParser', () => {
55
it('should parse the Apex file and collect interface implementations, method calls, and class names', () => {
6-
const apexFileContent = `global with sharing class FormulaParserService implements Callable{
6+
const apexFileContent = `global with sharing class FormulaParserService implements vlocity_ins.VlocityOpenInterface2, Callable
7+
{
78
89
global void justForTest(String kkdbk) {
910
/* Specify Data Mapper extract or transform to call */
@@ -14,21 +15,27 @@ describe('ApexASTParser', () => {
1415
vlocity_ins.DRProcessResult result1 = vlocity_ins.DRGlobal.process(myTransformData, 'DRName');
1516
}
1617
}`;
17-
const callable = 'Callable';
18-
const interfaceName = new Set<string>(['Callable']);
18+
// const vlocityOpenInterface2 = 'vlocity_ins.VlocityOpenInterface2';
19+
const namespace = 'vlocity_ins';
20+
const interfaces: InterfaceImplements[] = [];
21+
const vlocityOpenInterface = new InterfaceImplements('VlocityOpenInterface', namespace);
22+
const vlocityOpenInterface2 = new InterfaceImplements('VlocityOpenInterface2', namespace);
23+
interfaces.push(vlocityOpenInterface, vlocityOpenInterface2, new InterfaceImplements('Callable'));
1924
const methodCalls = new Set<MethodCall>();
20-
methodCalls.add(new MethodCall('process', 'DRGlobal', 'vlocity_ins'));
21-
methodCalls.add(new MethodCall('processObjectsJSON', 'DRGlobal', 'vlocity_ins'));
22-
const apexParser = new ApexASTParser(apexFileContent, interfaceName, methodCalls, 'vlocity_ins');
25+
methodCalls.add(new MethodCall('process', 'DRGlobal', namespace));
26+
methodCalls.add(new MethodCall('processObjectsJSON', 'DRGlobal', namespace));
27+
const apexParser = new ApexASTParser(apexFileContent, interfaces, methodCalls, namespace);
2328
apexParser.parse();
29+
apexParser.rewrite();
2430
const implementsInterface = apexParser.implementsInterfaces;
2531
// const callsMethods = apexParser.getCallsMethods();
2632
// const className = apexParser.getClassName();
33+
// const pos = implementsInterface.get(vlocityOpenInterface2);
2734

2835
// Add your assertions here based on the expected results
2936
// implementsInterface.get(interfaceName);
30-
expect(implementsInterface.get(callable).charPositionInLine).to.be.equal(58);
31-
expect(implementsInterface.get(callable).line).to.be.equal(1);
37+
expect(implementsInterface.get(vlocityOpenInterface2)[0].charPositionInLine).to.be.equal(58);
38+
expect(implementsInterface.get(vlocityOpenInterface2)[1].line).to.be.equal(1);
3239
// expect(callsMethods).to.not.be.empty;
3340
// expect(className).to.equal('YourClass');
3441
});

0 commit comments

Comments
 (0)