Skip to content

Commit 42c9e2e

Browse files
feat: parse POM deps/java.version
1 parent 7767d65 commit 42c9e2e

File tree

5 files changed

+173
-15
lines changed

5 files changed

+173
-15
lines changed

package-lock.json

Lines changed: 4 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1091,10 +1091,11 @@
10911091
"fmtr": "^1.1.4",
10921092
"fs-extra": "^10.1.0",
10931093
"globby": "^13.1.3",
1094+
"htmlparser2": "^8.0.2",
10941095
"lodash": "^4.17.21",
10951096
"minimatch": "^5.1.6",
10961097
"semver": "^7.3.8",
10971098
"vscode-extension-telemetry-wrapper": "^0.14.0",
10981099
"vscode-tas-client": "^0.1.75"
10991100
}
1100-
}
1101+
}

src/upgrade/lexerUtils.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
import { Document, isTag, Node, NodeWithChildren, Element, isText } from "domhandler";
4+
import * as hp from "htmlparser2";
5+
6+
export type ElementNode = Node;
7+
export enum XmlTagName {
8+
GroupId = "groupId",
9+
ArtifactId = "artifactId",
10+
Version = "version",
11+
Dependencies = "dependencies",
12+
DependencyManagement = "dependencyManagement",
13+
Exclusions = "exclusions",
14+
Plugins = "plugins",
15+
Plugin = "plugin",
16+
Project = "project",
17+
Dependency = "dependency",
18+
Properties = "properties",
19+
Packaging = "packaging",
20+
Module = "module",
21+
Modules = "modules",
22+
Parent = "parent",
23+
RelativePath = "relativePath"
24+
}
25+
26+
export function parseDocument(text: string): Document {
27+
return hp.parseDocument(text, {
28+
withEndIndices: true,
29+
withStartIndices: true,
30+
lowerCaseTags: false,
31+
xmlMode: true,
32+
});
33+
}
34+
35+
export function getChildrenByTags(parentElement: NodeWithChildren, tags: string[]): Element[] {
36+
const ret: Element[] = [];
37+
for (const child of parentElement.children) {
38+
if (isTag(child) && tags.includes(child.tagName)) {
39+
ret.push(child);
40+
}
41+
}
42+
return ret;
43+
}
44+
45+
export function getExactlyOneChildByTag(parentElement: NodeWithChildren, tag: string): Element | undefined {
46+
const items = getChildrenByTags(parentElement, [tag]);
47+
if (items.length !== 1) {
48+
return undefined;
49+
}
50+
return items[0];
51+
}
52+
53+
export function getTextFromNode(node: Node | undefined | null, fallbackValue = "") {
54+
return node && isText(node) ? node.data : fallbackValue;
55+
}

src/upgrade/pomDataManager.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
import { Range, type TextDocument, Uri, workspace } from "vscode";
5+
import type { Element } from "domhandler";
6+
import { getChildrenByTags, getExactlyOneChildByTag, getTextFromNode, parseDocument, XmlTagName } from "./lexerUtils";
7+
import { buildPackageId, normalizePath } from "./utility";
8+
import { Dependency } from "./type";
9+
import { Upgrade } from "../constants";
10+
11+
function getRangeOfNode(node: Element, vscodeDocument: TextDocument): Range | undefined {
12+
if (!node.startIndex || !node.endIndex) {
13+
return;
14+
}
15+
return new Range(
16+
vscodeDocument.positionAt(node.startIndex),
17+
vscodeDocument.positionAt(node.endIndex + 1),
18+
)
19+
}
20+
21+
function readDependencyNode(node: Element, vscodeDocument: TextDocument): Dependency | undefined {
22+
const range = getRangeOfNode(node, vscodeDocument);
23+
if (!range) {
24+
return undefined;
25+
}
26+
const groupId = getExactlyOneChildByTag(node, XmlTagName.GroupId);
27+
const artifactId = getExactlyOneChildByTag(node, XmlTagName.ArtifactId);
28+
const version = getExactlyOneChildByTag(node, XmlTagName.Version);
29+
if (!groupId || !artifactId) {
30+
return undefined;
31+
}
32+
const groupIdText = getTextFromNode(groupId.children[0]);
33+
const artifactIdText = getTextFromNode(artifactId.children[0]);
34+
const versionText = version ? getTextFromNode(version.children[0]) : undefined;
35+
return {
36+
packageId: buildPackageId(groupIdText, artifactIdText),
37+
version: versionText,
38+
location: range,
39+
}
40+
}
41+
42+
43+
44+
class PomDataManager {
45+
private pomMap: Record< /* pomPath */ string, Record<string /* packageId */, Range>> = {};
46+
47+
public async parsePom(pomPath: string) {
48+
const normalizedPath = normalizePath(pomPath);
49+
const pomDocument = await workspace.openTextDocument(Uri.parse(normalizedPath));
50+
const documentText = pomDocument.getText();
51+
const xmlDocument = await parseDocument(documentText);
52+
this.pomMap[normalizedPath] = {};
53+
54+
const projectNode = getExactlyOneChildByTag(xmlDocument, XmlTagName.Project);
55+
if (!projectNode) {
56+
return;
57+
}
58+
59+
const dependenciesNode = getExactlyOneChildByTag(projectNode, XmlTagName.Dependencies);
60+
const dependencyManagementNode = getExactlyOneChildByTag(projectNode, XmlTagName.DependencyManagement);
61+
const pomMap: Record<string /* packageId */, Range> = {};
62+
63+
if (dependencyManagementNode) {
64+
const deps = getChildrenByTags(dependencyManagementNode, [XmlTagName.Dependency]);
65+
deps.forEach((item) => {
66+
const dep = readDependencyNode(item, pomDocument);
67+
if (dep && (!pomMap[dep.packageId] || dep.version)) {
68+
pomMap[dep.packageId] = dep.location;
69+
}
70+
})
71+
}
72+
if (dependenciesNode) {
73+
const deps = getChildrenByTags(dependenciesNode, [XmlTagName.Dependency]);
74+
deps.forEach((item) => {
75+
const dep = readDependencyNode(item, pomDocument);
76+
if (dep && (!pomMap[dep.packageId] || dep.version)) {
77+
pomMap[dep.packageId] = dep.location;
78+
}
79+
})
80+
}
81+
82+
const propertiesNode = getExactlyOneChildByTag(projectNode, XmlTagName.Properties);
83+
if (propertiesNode) {
84+
const javaVersionNode = getExactlyOneChildByTag(propertiesNode, "java.version");
85+
if (javaVersionNode) {
86+
const range = getRangeOfNode(javaVersionNode, pomDocument);
87+
if (range) {
88+
pomMap[buildPackageId(Upgrade.DIAGNOSTICS_GROUP_ID_FOR_JAVA_ENGINE, "*")] = range;
89+
}
90+
}
91+
}
92+
93+
this.pomMap[normalizedPath] = pomMap;
94+
}
95+
96+
public getPomRange(pomPath: string, packageId: string): Range | undefined {
97+
return this.pomMap[pomPath]?.[packageId];
98+
}
99+
100+
101+
}
102+
103+
const pomDataManager = new PomDataManager();
104+
export default pomDataManager;

src/upgrade/type.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

4+
import type { Range } from "vscode";
5+
46
export type MementoItem<T> = {
57
lastUpdatedTs: number;
68
data: T;
@@ -24,4 +26,10 @@ export type UpgradeIssue = {
2426
suggestedVersion?: string;
2527
}
2628

29+
export type Dependency = {
30+
packageId: string;
31+
location: Range;
32+
version?: string;
33+
}
34+
2735
export type FileIssues = Record</* packageId */string, UpgradeIssue>;

0 commit comments

Comments
 (0)