Skip to content

Commit 26a86e8

Browse files
CsCherrYYjdneo
andauthored
refactor: Error reporting and null check in export jar (#402)
Co-authored-by: Sheng Chen <[email protected]>
1 parent 76692ab commit 26a86e8

File tree

7 files changed

+221
-103
lines changed

7 files changed

+221
-103
lines changed

src/exportJarSteps/ExportJarTaskProvider.ts

Lines changed: 94 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@ import { languageServerApiManager } from "../languageServerApi/languageServerApi
1717
import { Settings } from "../settings";
1818
import { IUriData, Trie, TrieNode } from "../views/nodeCache/Trie";
1919
import { IClasspathResult } from "./GenerateJarExecutor";
20+
import { IExportJarStepExecutor } from "./IExportJarStepExecutor";
2021
import { IClasspath, IStepMetadata } from "./IStepMetadata";
2122
import { IMainClassInfo } from "./ResolveMainClassExecutor";
22-
import { ExportJarConstants, ExportJarStep, failMessage, getExtensionApi, stepMap, successMessage, toPosixPath, toWinPath } from "./utility";
23+
import {
24+
ExportJarConstants, ExportJarMessages, ExportJarStep, failMessage, getExtensionApi,
25+
resetStepMetadata, stepMap, successMessage, toPosixPath, toWinPath,
26+
} from "./utility";
2327

2428
interface IExportJarTaskDefinition extends TaskDefinition {
2529
label?: string;
@@ -38,23 +42,33 @@ export async function executeExportJarTask(node?: INodeData): Promise<void> {
3842
const stepMetadata: IStepMetadata = {
3943
entry: node,
4044
steps: [],
45+
projectList: [],
46+
elements: [],
47+
classpaths: [],
4148
};
4249
try {
43-
await stepMap.get(ExportJarStep.ResolveJavaProject).execute(stepMetadata);
50+
const resolveJavaProjectExecutor: IExportJarStepExecutor | undefined = stepMap.get(ExportJarStep.ResolveJavaProject);
51+
if (!resolveJavaProjectExecutor) {
52+
throw new Error(ExportJarMessages.stepErrorMessage(ExportJarMessages.StepAction.FINDEXECUTOR, ExportJarStep.ResolveJavaProject));
53+
}
54+
await resolveJavaProjectExecutor.execute(stepMetadata);
55+
tasks.executeTask(ExportJarTaskProvider.getTask(stepMetadata));
4456
} catch (err) {
4557
if (err) {
4658
failMessage(`${err}`);
4759
}
4860
isExportingJar = false;
4961
return;
5062
}
51-
tasks.executeTask(ExportJarTaskProvider.getTask(stepMetadata));
5263
}
5364
export class ExportJarTaskProvider implements TaskProvider {
5465

5566
public static exportJarType: string = "java";
5667

5768
public static getTask(stepMetadata: IStepMetadata): Task {
69+
if (!stepMetadata.workspaceFolder) {
70+
throw new Error(ExportJarMessages.fieldUndefinedMessage(ExportJarMessages.Field.WORKSPACEFOLDER, ExportJarStep.ResolveTask));
71+
}
5872
const defaultDefinition: IExportJarTaskDefinition = {
5973
type: ExportJarTaskProvider.exportJarType,
6074
label: `${ExportJarTaskProvider.exportJarType}: exportjar:default`,
@@ -82,19 +96,25 @@ export class ExportJarTaskProvider implements TaskProvider {
8296
workspaceFolder: folder,
8397
projectList: await Jdtls.getProjects(folder.uri.toString()),
8498
steps: [],
99+
elements: [],
100+
classpaths: [],
85101
};
86102
return new ExportJarTaskTerminal(resolvedDefinition, stepMetadata);
87103
}));
88104
resolvedTask.presentationOptions.reveal = TaskRevealKind.Never;
89105
return resolvedTask;
90106
}
91107

92-
public async provideTasks(): Promise<Task[]> {
108+
public async provideTasks(): Promise<Task[] | undefined> {
109+
const folders: readonly WorkspaceFolder[] = workspace.workspaceFolders || [];
110+
if (_.isEmpty(folders)) {
111+
return undefined;
112+
}
93113
if (!_.isEmpty(this.tasks)) {
94114
return this.tasks;
95115
}
96116
this.tasks = [];
97-
for (const folder of workspace.workspaceFolders) {
117+
for (const folder of folders) {
98118
const projectList: INodeData[] = await Jdtls.getProjects(folder.uri.toString());
99119
const elementList: string[] = [];
100120
if (_.isEmpty(projectList)) {
@@ -123,6 +143,8 @@ export class ExportJarTaskProvider implements TaskProvider {
123143
workspaceFolder: folder,
124144
projectList: await Jdtls.getProjects(folder.uri.toString()),
125145
steps: [],
146+
elements: [],
147+
classpaths: [],
126148
};
127149
return new ExportJarTaskTerminal(resolvedDefinition, stepMetadata);
128150
}), undefined);
@@ -147,11 +169,15 @@ class ExportJarTaskTerminal implements Pseudoterminal {
147169
this.stepMetadata = stepMetadata;
148170
this.stepMetadata.mainClass = exportJarTaskDefinition.mainClass;
149171
this.stepMetadata.outputPath = exportJarTaskDefinition.targetPath;
150-
this.stepMetadata.elements = exportJarTaskDefinition.elements;
172+
this.stepMetadata.elements = exportJarTaskDefinition.elements || [];
151173
}
152174

153175
public async open(_initialDimensions: TerminalDimensions | undefined): Promise<void> {
176+
let exportResult: boolean | undefined;
154177
try {
178+
if (!this.stepMetadata.workspaceFolder) {
179+
throw new Error(ExportJarMessages.fieldUndefinedMessage(ExportJarMessages.Field.WORKSPACEFOLDER, ExportJarStep.ResolveTask));
180+
}
155181
if (this.stepMetadata.outputPath === undefined) {
156182
// TODO: get resolved path from setting configuration.java.project.exportJar.targetPath.
157183
// For the tasks whose targetPath is undefined, the user will select the output location manually.
@@ -170,12 +196,20 @@ class ExportJarTaskTerminal implements Pseudoterminal {
170196
this.stepMetadata.classpaths = await this.resolveClasspaths(outputFolderMap,
171197
artifactMap, testOutputFolderMap, testArtifactMap);
172198
}
173-
await this.createJarFile(this.stepMetadata);
199+
exportResult = await this.createJarFile(this.stepMetadata);
174200
} catch (err) {
175201
if (err) {
176202
failMessage(`${err}`);
177203
}
178204
} finally {
205+
isExportingJar = false;
206+
if (exportResult === true) {
207+
successMessage(this.stepMetadata.outputPath);
208+
} else if (exportResult === false) {
209+
// We call `executeExportJarTask()` with the same entry here
210+
// to help the user reselect the Java project.
211+
executeExportJarTask(this.stepMetadata.entry);
212+
}
179213
this.closeEmitter.fire();
180214
}
181215
}
@@ -184,28 +218,46 @@ class ExportJarTaskTerminal implements Pseudoterminal {
184218

185219
}
186220

187-
private async createJarFile(stepMetadata: IStepMetadata): Promise<void> {
221+
private async createJarFile(stepMetadata: IStepMetadata): Promise<boolean> {
188222
let step: ExportJarStep = ExportJarStep.ResolveJavaProject;
223+
let previousStep: ExportJarStep | undefined;
224+
let executor: IExportJarStepExecutor | undefined;
189225
while (step !== ExportJarStep.Finish) {
190-
try {
191-
step = await stepMap.get(step).execute(stepMetadata);
192-
if (step === ExportJarStep.ResolveJavaProject) {
193-
// If the user comes back to the step resolving Java project, we need to finish
194-
// the current task and start a new task related to the new Java project.
195-
isExportingJar = false;
196-
executeExportJarTask(stepMetadata.entry);
197-
return;
226+
executor = stepMap.get(step);
227+
if (!executor) {
228+
throw new Error(ExportJarMessages.stepErrorMessage(ExportJarMessages.StepAction.FINDEXECUTOR, step));
229+
}
230+
if (!await executor.execute(stepMetadata)) {
231+
// Go back
232+
previousStep = stepMetadata.steps.pop();
233+
if (!previousStep) {
234+
throw new Error(ExportJarMessages.stepErrorMessage(ExportJarMessages.StepAction.GOBACK, step));
198235
}
199-
} catch (err) {
200-
if (err) {
201-
failMessage(`${err}`);
236+
resetStepMetadata(previousStep, stepMetadata);
237+
step = previousStep;
238+
} else {
239+
// Go ahead
240+
switch (step) {
241+
case ExportJarStep.ResolveJavaProject:
242+
step = ExportJarStep.ResolveMainClass;
243+
break;
244+
case ExportJarStep.ResolveMainClass:
245+
step = ExportJarStep.GenerateJar;
246+
break;
247+
case ExportJarStep.GenerateJar:
248+
step = ExportJarStep.Finish;
249+
break;
250+
default:
251+
throw new Error(ExportJarMessages.stepErrorMessage(ExportJarMessages.StepAction.GOAHEAD, step));
202252
}
203-
isExportingJar = false;
204-
return;
253+
}
254+
if (step === ExportJarStep.ResolveJavaProject) {
255+
// It's possible for a user who comes back to the step selecting the Java project to change the workspace.
256+
// Since a specific task corresponds to a specific workspace, we return "false" as a mark.
257+
return false;
205258
}
206259
}
207-
isExportingJar = false;
208-
successMessage(stepMetadata.outputPath);
260+
return true;
209261
}
210262

211263
private async setClasspathMap(project: INodeData, classpathScope: string,
@@ -236,7 +288,10 @@ class ExportJarTaskTerminal implements Pseudoterminal {
236288
if (element.length === 0) {
237289
continue;
238290
}
239-
const matchResult = element.match(regExp);
291+
const matchResult: RegExpMatchArray | null = element.match(regExp);
292+
if (matchResult === null) {
293+
continue;
294+
}
240295
if (_.isEmpty(matchResult) || matchResult.length <= 2) {
241296
if (extname(element) === ".jar") {
242297
artifacts.push(this.toAbsolutePosixPath(element));
@@ -245,7 +300,7 @@ class ExportJarTaskTerminal implements Pseudoterminal {
245300
}
246301
continue;
247302
}
248-
const projectName: string = (matchResult[2] === undefined) ? undefined : matchResult[2].substring(1);
303+
const projectName: string | undefined = matchResult[2]?.substring(1);
249304
switch (matchResult[1]) {
250305
case ExportJarConstants.DEPENDENCIES:
251306
artifacts = artifacts.concat(this.getJarElementsFromClasspathMapping(matchResult, artifactMap, projectName));
@@ -278,8 +333,9 @@ class ExportJarTaskTerminal implements Pseudoterminal {
278333
}
279334
const sources: IClasspath[] = [];
280335
for (const glob of await globby(globPatterns)) {
281-
const tireNode: TrieNode<IUriData> = trie.find(Uri.file(platform() === "win32" ? toWinPath(glob) : glob).fsPath, /* returnEarly = */true);
282-
if (tireNode === undefined) {
336+
const tireNode: TrieNode<IUriData> | undefined = trie.find(
337+
Uri.file(platform() === "win32" ? toWinPath(glob) : glob).fsPath, /* returnEarly = */true);
338+
if (!tireNode?.value?.uri) {
283339
continue;
284340
}
285341
let fsPath = Uri.parse(tireNode.value.uri).fsPath;
@@ -309,8 +365,15 @@ class ExportJarTaskTerminal implements Pseudoterminal {
309365
private getJarElementsFromClasspathMapping(matchResult: RegExpMatchArray, rawClasspathEntries: Map<string, string[]>,
310366
projectName: string | undefined): string[] {
311367
const result: string[] = [];
368+
if (!matchResult.input) {
369+
return result;
370+
}
312371
if (projectName !== undefined) {
313-
for (const classpath of rawClasspathEntries.get(projectName)) {
372+
const entries: string[] = rawClasspathEntries.get(projectName) || [];
373+
if (_.isEmpty(entries)) {
374+
return result;
375+
}
376+
for (const classpath of entries) {
314377
result.push(this.toAbsolutePosixPath(matchResult.input.replace(matchResult[0], classpath)));
315378
}
316379
} else {
@@ -324,6 +387,9 @@ class ExportJarTaskTerminal implements Pseudoterminal {
324387
}
325388

326389
private toAbsolutePosixPath(path: string): string {
390+
if (!this.stepMetadata.workspaceFolder) {
391+
throw new Error(ExportJarMessages.fieldUndefinedMessage(ExportJarMessages.Field.WORKSPACEFOLDER, ExportJarStep.ResolveTask));
392+
}
327393
const negative: boolean = (path[0] === "!");
328394
let positivePath: string = negative ? path.substring(1) : path;
329395
if (!isAbsolute(positivePath)) {

0 commit comments

Comments
 (0)