149 lines
5.4 KiB
Python
149 lines
5.4 KiB
Python
from __future__ import annotations
|
|
|
|
import xml.etree.ElementTree as ET
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from depaudit.parsers import Parser, ParsedManifest, Dependency
|
|
|
|
|
|
class JavaParser(Parser):
|
|
language = "java"
|
|
|
|
def can_parse(self, file_path: Path) -> bool:
|
|
return file_path.name in ("pom.xml", "build.gradle", "build.gradle.kts")
|
|
|
|
def parse(self, file_path: Path) -> ParsedManifest:
|
|
manifest = ParsedManifest(
|
|
language=self.language,
|
|
file_path=file_path,
|
|
)
|
|
|
|
if file_path.name == "pom.xml":
|
|
self._parse_pom_xml(file_path, manifest)
|
|
elif file_path.name.startswith("build.gradle"):
|
|
self._parse_gradle(file_path, manifest)
|
|
|
|
return manifest
|
|
|
|
def _parse_pom_xml(self, file_path: Path, manifest: ParsedManifest) -> None:
|
|
tree = ET.parse(file_path)
|
|
root = tree.getroot()
|
|
ns = {"maven": "http://maven.apache.org/POM/4.0.0"}
|
|
|
|
artifact_id = root.find("maven:artifactId", ns)
|
|
if artifact_id is None:
|
|
artifact_id = root.find("artifactId")
|
|
|
|
group_id = root.find("maven:groupId", ns)
|
|
if group_id is None:
|
|
group_id = root.find("groupId")
|
|
|
|
version = root.find("maven:version", ns)
|
|
if version is None:
|
|
version = root.find("version")
|
|
|
|
if group_id is not None and artifact_id is not None:
|
|
manifest.project_name = group_id.text + "/" + artifact_id.text
|
|
else:
|
|
manifest.project_name = None
|
|
manifest.project_version = version.text if version is not None else None
|
|
|
|
self._parse_dependencies(root, ns, file_path, manifest)
|
|
self._parse_dependency_management(root, ns, file_path, manifest)
|
|
|
|
def _parse_dependencies(
|
|
self, root: ET.Element, ns: dict[str, str], file_path: Path, manifest: ParsedManifest
|
|
) -> None:
|
|
dependencies_elem = root.find("maven:dependencies", ns)
|
|
if dependencies_elem is None:
|
|
dependencies_elem = root.find("dependencies")
|
|
|
|
if dependencies_elem is None:
|
|
return
|
|
|
|
for dep in dependencies_elem.findall("maven:dependency", ns):
|
|
group_id = dep.find("maven:groupId", ns)
|
|
if group_id is None:
|
|
group_id = dep.find("groupId")
|
|
artifact_id = dep.find("maven:artifactId", ns)
|
|
if artifact_id is None:
|
|
artifact_id = dep.find("artifactId")
|
|
version = dep.find("maven:version", ns)
|
|
if version is None:
|
|
version = dep.find("version")
|
|
scope = dep.find("maven:scope", ns)
|
|
if scope is None:
|
|
scope = dep.find("scope")
|
|
|
|
if group_id is not None and artifact_id is not None:
|
|
name = str(group_id.text) + ":" + str(artifact_id.text)
|
|
ver = version.text if version is not None else ""
|
|
dev = scope is not None and scope.text in ["test", "provided"]
|
|
manifest.dependencies.append(
|
|
self._create_dependency(file_path, name, ver, dev=dev)
|
|
)
|
|
|
|
def _parse_dependency_management(
|
|
self, root: ET.Element, ns: dict[str, str], file_path: Path, manifest: ParsedManifest
|
|
) -> None:
|
|
dm_elem = root.find("maven:dependencyManagement", ns)
|
|
if dm_elem is None:
|
|
dm_elem = root.find("dependencyManagement")
|
|
|
|
if dm_elem is None:
|
|
return
|
|
|
|
deps_elem = dm_elem.find("maven:dependencies", ns)
|
|
if deps_elem is None:
|
|
deps_elem = dm_elem.find("dependencies")
|
|
|
|
if deps_elem is None:
|
|
return
|
|
|
|
for dep in deps_elem.findall("maven:dependency", ns):
|
|
group_id = dep.find("maven:groupId", ns)
|
|
artifact_id = dep.find("maven:artifactId", ns)
|
|
version = dep.find("maven:version", ns)
|
|
|
|
if group_id is not None and artifact_id is not None:
|
|
name = str(group_id.text) + ":" + str(artifact_id.text)
|
|
ver = version.text if version is not None else ""
|
|
manifest.dependencies.append(
|
|
self._create_dependency(file_path, name, ver)
|
|
)
|
|
|
|
def _parse_gradle(self, file_path: Path, manifest: ParsedManifest) -> None:
|
|
import re
|
|
content = file_path.read_text(encoding="utf-8")
|
|
|
|
name_match = self._extract_gradle_value(content, "name")
|
|
if name_match:
|
|
manifest.project_name = name_match
|
|
|
|
version_match = self._extract_gradle_value(content, "version")
|
|
if version_match:
|
|
manifest.project_version = version_match
|
|
|
|
dep_pattern = r"(?:implementation|api|compileOnly|runtimeOnly|testImplementation|testCompileOnly)\s*[\(\"']?\s*([^\s:\'\")]+)\s*:\s*([^\s:\'\")]+)\s*:\s*([^\s:\'\")]+)"
|
|
for match in re.finditer(dep_pattern, content):
|
|
group = match.group(1)
|
|
name = match.group(2)
|
|
version = match.group(4)
|
|
full_name = group + ":" + name
|
|
manifest.dependencies.append(
|
|
self._create_dependency(file_path, full_name, version)
|
|
)
|
|
|
|
def _extract_gradle_value(self, content: str, key: str) -> str | None:
|
|
import re
|
|
patterns = [
|
|
key + "\s*=\s*["']([^"\']+)["']",
|
|
key + "\s*=\s*([^\s]+)",
|
|
]
|
|
for pattern in patterns:
|
|
match = re.search(pattern, content)
|
|
if match:
|
|
return match.group(1).strip()
|
|
return None
|