203 lines
9.4 KiB
Java
203 lines
9.4 KiB
Java
package io.gitlab.jfronny.inceptum.common.api;
|
|
|
|
import io.gitlab.jfronny.commons.http.client.HttpClient;
|
|
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.getLocalPath();
|
|
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 = HttpClient.get(Utils.join("/", repo, meta.getPomPath())).sendInputStream()) {
|
|
Document doc = FACTORY.parse(is);
|
|
doc.getDocumentElement().normalize();
|
|
if (!"project".equals(doc.getDocumentElement().getNodeName())) 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 : children(doc.getDocumentElement())) {
|
|
switch (node.getNodeName()) {
|
|
case "modelVersion" -> modelVersion = node.getTextContent();
|
|
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 : children(node)) {
|
|
switch (child.getNodeName()) {
|
|
case "groupId" -> {
|
|
if (groupId == null) {
|
|
groupId = child.getTextContent();
|
|
}
|
|
}
|
|
case "version" -> {
|
|
if (version == null) {
|
|
version = child.getTextContent();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
case "groupId" -> groupId = node.getTextContent();
|
|
case "artifactId" -> {
|
|
artifactId = node.getTextContent();
|
|
}
|
|
case "version" -> {
|
|
version = node.getTextContent();
|
|
}
|
|
case "packaging" -> packaging = node.getTextContent();
|
|
case "dependencies" -> {
|
|
dependencies = new LinkedList<>();
|
|
for (Node dep : children(node)) {
|
|
MavenDependency resolved = parseDependency(dep);
|
|
if (resolved != null) {
|
|
dependencies.add(resolved);
|
|
}
|
|
}
|
|
}
|
|
case "classifier" -> classifier = node.getTextContent();
|
|
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 : children(doc)) {
|
|
switch (node.getNodeName()) {
|
|
case "groupId" -> groupId = node.getTextContent();
|
|
case "artifactId" -> artifactId = node.getTextContent();
|
|
case "version" -> version = node.getTextContent();
|
|
case "scope" -> {
|
|
scope = node.getTextContent();
|
|
if (!RUNTIME_SCOPES.contains(scope)) return null;
|
|
}
|
|
case "optional" -> {
|
|
if (node.getTextContent().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 = HttpClient.get(Utils.join("/", repo, sourceMeta.getMetadataPath())).sendInputStream()) {
|
|
Document doc = FACTORY.parse(is);
|
|
doc.getDocumentElement().normalize();
|
|
if (!"metadata".equals(doc.getDocumentElement().getNodeName())) throw new IOException("Illegal document name");
|
|
String groupId = null;
|
|
String artifactId = null;
|
|
String version = null;
|
|
String snapshotVersion = null;
|
|
for (Node node : children(doc.getDocumentElement())) {
|
|
switch (node.getNodeName()) {
|
|
case "groupId" -> groupId = node.getTextContent();
|
|
case "artifactId" -> artifactId = node.getTextContent();
|
|
case "version" -> version = node.getTextContent();
|
|
case "versioning" -> {
|
|
for (Node node1 : children(node)) {
|
|
if (node1.getNodeName().equals("snapshot")) {
|
|
String timestamp = null;
|
|
String buildNumber = null;
|
|
for (Node node2 : children(node1)) {
|
|
switch (node2.getNodeName()) {
|
|
case "timestamp" -> timestamp = node2.getTextContent();
|
|
case "buildNumber" -> buildNumber = node2.getTextContent();
|
|
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);
|
|
}
|
|
}
|
|
|
|
private static Iterable<Node> children(Node node) {
|
|
return () -> new Iterator<Node>() {
|
|
NodeList children = node.getChildNodes();
|
|
int index = 0;
|
|
|
|
@Override
|
|
public boolean hasNext() {
|
|
while (index < children.getLength() && isWhitespace(children.item(index))) {
|
|
index++;
|
|
}
|
|
return index < children.getLength();
|
|
}
|
|
|
|
@Override
|
|
public Node next() {
|
|
if (!hasNext()) throw new NoSuchElementException();
|
|
return children.item(index++);
|
|
}
|
|
};
|
|
}
|
|
|
|
private static boolean isWhitespace(Node node) {
|
|
if (node.getNodeType() == Node.TEXT_NODE && node.getTextContent().isBlank()) return true;
|
|
if (node.getNodeType() == Node.COMMENT_NODE) return true;
|
|
return false;
|
|
}
|
|
}
|