Inceptum/common/src/main/java/io/gitlab/jfronny/inceptum/common/api/MavenApi.java

176 lines
8.3 KiB
Java

package io.gitlab.jfronny.inceptum.common.api;
import io.gitlab.jfronny.commons.HttpUtils;
import io.gitlab.jfronny.inceptum.common.Net;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.common.model.maven.*;
import org.jetbrains.annotations.Nullable;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import javax.xml.parsers.*;
import javax.xml.stream.XMLStreamException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.util.*;
public class MavenApi {
private static final DocumentBuilder FACTORY;
private static final Set<String> RUNTIME_SCOPES = Set.of("compile", "runtime");
static {
try {
FACTORY = DocumentBuilderFactory.newInstance().newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new RuntimeException("Could not create document builder", e);
}
}
public static Path downloadLibrary(String repo, ArtifactMeta meta) throws IOException, URISyntaxException {
Path res = meta.localPath;
Net.downloadFile(Utils.join("/", repo, meta.getJarPath(true)), res);
return res;
}
public static Pom getPom(String repo, ArtifactMeta meta) throws IOException, SAXException, URISyntaxException, XMLStreamException {
try (InputStream is = HttpUtils.get(Utils.join("/", repo, meta.pomPath)).sendInputStream()) {
Document doc = FACTORY.parse(is);
doc.documentElement.normalize();
if (!"project".equals(doc.documentElement.nodeName)) throw new IOException("Illegal document name");
String modelVersion = null;
String groupId = null;
String artifactId = null;
String version = null;
String packaging = null;
List<MavenDependency> dependencies = null;
String classifier = null;
for (Node node : doc.documentElement.childNodes) {
switch (node.nodeName) {
case "modelVersion" -> modelVersion = node.textContent;
case "parent" -> {
// Dirty hack to get slf4j working: simply assume the groupId and version of the parent is also the groupId of this
if (groupId == null) {
for (Node child : node.childNodes) {
switch (child.nodeName) {
case "groupId" -> {
if (groupId == null) {
groupId = child.textContent;
}
}
case "version" -> {
if (version == null) {
version = child.textContent;
}
}
}
}
}
}
case "groupId" -> groupId = node.textContent;
case "artifactId" -> {
artifactId = node.textContent;
}
case "version" -> {
version = node.textContent;
}
case "packaging" -> packaging = node.textContent;
case "dependencies" -> {
dependencies = new LinkedList<>();
for (Node dep : node.childNodes) {
MavenDependency resolved = parseDependency(dep);
if (resolved != null) {
dependencies.add(resolved);
}
}
}
case "classifier" -> classifier = node.textContent;
default -> {}
}
}
if (modelVersion == null) throw new IOException("Pom lacks modelVersion");
if (groupId == null) throw new IOException("Pom lacks groupId");
if (artifactId == null) throw new IOException("Pom lacks artifactId");
if (version == null) throw new IOException("Pom lacks version");
return new Pom(modelVersion, groupId, artifactId, version, classifier, null, packaging, dependencies);
}
}
private static @Nullable MavenDependency parseDependency(Node doc) throws IOException {
String groupId = null;
String artifactId = null;
String version = null;
String scope = null;
for (Node node : doc.childNodes) {
switch (node.nodeName) {
case "groupId" -> groupId = node.textContent;
case "artifactId" -> artifactId = node.textContent;
case "version" -> version = node.textContent;
case "scope" -> {
scope = node.textContent;
if (!RUNTIME_SCOPES.contains(scope)) return null;
}
case "optional" -> {
if (node.textContent.equals("true")) return null;
}
}
}
if (groupId == null) throw new IOException("Pom lacks groupId");
if (artifactId == null) throw new IOException("Pom lacks artifactId");
if (version == null) {
if (groupId.equals("org.lwjgl")) {
// Lwjgl uses a shared bom for versions which I don't want to support
// The required modules are explicit dependencies of launcher-imgui anyway
return null;
}
throw new IOException("Dependency " + groupId + ":" + artifactId + " lacks version");
}
if (scope == null) throw new IOException("Pom lacks scope");
return new MavenDependency(groupId, artifactId, version, scope);
}
public static ArtifactMeta getMetadata(String repo, String artifact) throws IOException, SAXException, URISyntaxException {
ArtifactMeta sourceMeta = ArtifactMeta.parse(artifact);
try (InputStream is = HttpUtils.get(Utils.join("/", repo, sourceMeta.metadataPath)).sendInputStream()) {
Document doc = FACTORY.parse(is);
doc.documentElement.normalize();
if (!"metadata".equals(doc.documentElement.nodeName)) throw new IOException("Illegal document name");
String groupId = null;
String artifactId = null;
String version = null;
String snapshotVersion = null;
for (Node node : doc.documentElement.childNodes) {
switch (node.nodeName) {
case "groupId" -> groupId = node.textContent;
case "artifactId" -> artifactId = node.textContent;
case "version" -> version = node.textContent;
case "versioning" -> {
for (Node node1 : node.childNodes) {
if (node1.nodeName.equals("snapshot")) {
String timestamp = null;
String buildNumber = null;
for (Node node2 : node1.childNodes) {
switch (node2.nodeName) {
case "timestamp" -> timestamp = node2.textContent;
case "buildNumber" -> buildNumber = node2.textContent;
default -> {}
}
}
if (timestamp == null) throw new IOException("Pom snapshots lack timestamp");
if (buildNumber == null) throw new IOException("Pom snapshots lack buildNumber");
snapshotVersion = timestamp + '-' + buildNumber;
}
}
}
default -> {}
}
}
if (groupId == null) throw new IOException("Pom lacks groupId");
if (artifactId == null) throw new IOException("Pom lacks artifactId");
if (version == null) throw new IOException("Pom lacks version");
return new ArtifactMeta(groupId, artifactId, version, sourceMeta.classifier, snapshotVersion);
}
}
}