From 4793d7aefee30617031f2a79e1dba19211221611 Mon Sep 17 00:00:00 2001 From: JeremyStarTM Date: Sat, 29 Jun 2024 22:16:18 +0200 Subject: [PATCH] Add probably broken dependency resolver --- .../exceptions/DependencyCycleException.java | 40 +++++ .../UnexpectedThrowableException.java | 68 ++++++++ .../UnmetDependenciesException.java | 53 ++++++ .../DependencyResolvedDependencyVector.java | 51 ++++++ .../base/types/DependencyVector.java | 120 ++++++++++++++ .../base/utility/DependencyResolver.java | 152 ++++++++++++++++++ 6 files changed, 484 insertions(+) create mode 100644 base/src/main/java/de/staropensource/sosengine/base/exceptions/DependencyCycleException.java create mode 100644 base/src/main/java/de/staropensource/sosengine/base/exceptions/UnexpectedThrowableException.java create mode 100644 base/src/main/java/de/staropensource/sosengine/base/exceptions/UnmetDependenciesException.java create mode 100644 base/src/main/java/de/staropensource/sosengine/base/types/DependencyResolvedDependencyVector.java create mode 100644 base/src/main/java/de/staropensource/sosengine/base/types/DependencyVector.java create mode 100644 base/src/main/java/de/staropensource/sosengine/base/utility/DependencyResolver.java diff --git a/base/src/main/java/de/staropensource/sosengine/base/exceptions/DependencyCycleException.java b/base/src/main/java/de/staropensource/sosengine/base/exceptions/DependencyCycleException.java new file mode 100644 index 00000000..ee2f6b2c --- /dev/null +++ b/base/src/main/java/de/staropensource/sosengine/base/exceptions/DependencyCycleException.java @@ -0,0 +1,40 @@ +/* + * STAROPENSOURCE ENGINE SOURCE FILE + * Copyright (c) 2024 The StarOpenSource Engine Contributors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.staropensource.sosengine.base.exceptions; + +import org.jetbrains.annotations.NotNull; + +/** + * Represents an exception caused by a dependency cycle. + * + * @since 1-alpha1 + */ +@SuppressWarnings({ "unused" }) +public class DependencyCycleException extends Exception { + /** + * Constructor. + * + * @param path cycle path + * @since 1-alpha1 + */ + public DependencyCycleException(@NotNull String path) { + super("Dependency cycle detected: " + path); + } +} diff --git a/base/src/main/java/de/staropensource/sosengine/base/exceptions/UnexpectedThrowableException.java b/base/src/main/java/de/staropensource/sosengine/base/exceptions/UnexpectedThrowableException.java new file mode 100644 index 00000000..14a08f57 --- /dev/null +++ b/base/src/main/java/de/staropensource/sosengine/base/exceptions/UnexpectedThrowableException.java @@ -0,0 +1,68 @@ +/* + * STAROPENSOURCE ENGINE SOURCE FILE + * Copyright (c) 2024 The StarOpenSource Engine Contributors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.staropensource.sosengine.base.exceptions; + +import lombok.Getter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Represents an exception caused by some throwable. + * Basic wrapper for every throwable there is. + * + * @since 1-alpha1 + */ +@SuppressWarnings({ "unused", "JavadocDeclaration", "JavadocBlankLines" }) +@Getter +public class UnexpectedThrowableException extends Exception { + /** + * Contains the throwable supplied to the constructor. + * + * @since 1-alpha1 + * + * -- GETTER -- + * Returns the throwable supplied by the constructor. + * + * @return throwable + * @since 1-alpha1 + */ + @Nullable + Throwable throwable; + + /** + * Constructor. + * + * @param throwable throwable + * @param message message + */ + public UnexpectedThrowableException(@NotNull Throwable throwable, @NotNull String message) { + super(message); + this.throwable = throwable; + } + + /** + * Constructor. + * + * @param throwable throwable + */ + public UnexpectedThrowableException(@NotNull Throwable throwable) { + this.throwable = throwable; + } +} diff --git a/base/src/main/java/de/staropensource/sosengine/base/exceptions/UnmetDependenciesException.java b/base/src/main/java/de/staropensource/sosengine/base/exceptions/UnmetDependenciesException.java new file mode 100644 index 00000000..d08035d1 --- /dev/null +++ b/base/src/main/java/de/staropensource/sosengine/base/exceptions/UnmetDependenciesException.java @@ -0,0 +1,53 @@ +/* + * STAROPENSOURCE ENGINE SOURCE FILE + * Copyright (c) 2024 The StarOpenSource Engine Contributors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.staropensource.sosengine.base.exceptions; + +import de.staropensource.sosengine.base.types.DependencyVector; +import lombok.Getter; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +/** + * Represents an exception caused by unmet dependencies. + * + * @since 1-alpha1 + */ +@SuppressWarnings({ "unused", "JavadocDeclaration", "JavadocBlankLines" }) +@Getter +public class UnmetDependenciesException extends Exception { + /** + * Contains the unmet dependencies list supplied to the constructor. + * + * @since 1-alpha1 + * + * -- GETTER -- + * Returns the unmet dependencies list supplied to the constructor. + * + * @return unmet dependencies list + * @since 1-alpha1 + */ + @NotNull + private final Map<@NotNull DependencyVector, @NotNull String> unmetDependencies; + + public UnmetDependenciesException(@NotNull Map<@NotNull DependencyVector, @NotNull String> unmetDependencies) { + this.unmetDependencies = unmetDependencies; + } +} diff --git a/base/src/main/java/de/staropensource/sosengine/base/types/DependencyResolvedDependencyVector.java b/base/src/main/java/de/staropensource/sosengine/base/types/DependencyResolvedDependencyVector.java new file mode 100644 index 00000000..1fb8a6a4 --- /dev/null +++ b/base/src/main/java/de/staropensource/sosengine/base/types/DependencyResolvedDependencyVector.java @@ -0,0 +1,51 @@ +/* + * STAROPENSOURCE ENGINE SOURCE FILE + * Copyright (c) 2024 The StarOpenSource Engine Contributors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.staropensource.sosengine.base.types; + +import lombok.Getter; + +import java.util.List; + +/** + * Represents a dependency vector with resolved dependencies, used for dependency management. + * + * @since 1-alpha1 + */ +@SuppressWarnings({ "unused", "JavadocDeclaration", "JavadocBlankLines" }) +@Getter +public class DependencyResolvedDependencyVector extends DependencyVector { + /** + * Contains all resolved dependencies. + * + * @since v1-alpha1 + * + * -- GETTER -- + * Returns all resolved dependencies. + * + * @return resolved dependencies + * @since 1-alpha1 + */ + private final List resolvedDependencies; + + public DependencyResolvedDependencyVector(DependencyVector vector, List resolvedDependencies) { + super(vector.getIdentifier(), vector.getVersioningSystem(), vector.getVersion(), vector.getDependencies()); + this.resolvedDependencies = resolvedDependencies; + } +} diff --git a/base/src/main/java/de/staropensource/sosengine/base/types/DependencyVector.java b/base/src/main/java/de/staropensource/sosengine/base/types/DependencyVector.java new file mode 100644 index 00000000..fb6f9252 --- /dev/null +++ b/base/src/main/java/de/staropensource/sosengine/base/types/DependencyVector.java @@ -0,0 +1,120 @@ +/* + * STAROPENSOURCE ENGINE SOURCE FILE + * Copyright (c) 2024 The StarOpenSource Engine Contributors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.staropensource.sosengine.base.types; + +import lombok.Getter; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a dependency vector, used for dependency management. + * + * @since 1-alpha1 + */ +@SuppressWarnings({ "unused", "JavadocDeclaration", "JavadocBlankLines" }) +@Getter +public class DependencyVector { + /** + * Contains the identifier of this vector. + * + * @since 1-alpha1 + * + * -- GETTER -- + * Returns the vector identifier. + * + * @return vector identifier + * @since 1-alpha1 + */ + @NotNull + private final String identifier; + + /** + * Determines which versioning system this vector uses. + * + * @since 1-alpha1 + * + * -- GETTER -- + * Returns the versioning system this vector uses. + * + * @return versioning system in use + * @since 1-alpha1 + */ + @NotNull + private final Class versioningSystem; + + /** + * Contains the version of this vector. + * + * @since 1-alpha1 + * + * -- GETTER -- + * Returns the vector version. + * + * @return vector version + * @since 1-alpha1 + */ + @NotNull + private final String version; + + /** + * Contains all vectors this vector hard-depends on. + * + * @since 1-alpha1 + * + * -- GETTER -- + * Returns all vectors this vector hard-depends on. + * + * @return hard dependencies + * @since 1-alpha1 + */ + @NotNull + private final List<@NotNull String> dependencies; + + /** + * Constructor. + * + * @param identifier identifier + * @param versioningSystem versioning system to use + * @param version version + * @param dependencies dependencies in the usual format ({@code dependency1}, {@code dependency2=5.1}, {@code dependency3>3.1}, {@code dependency<6.1}) + */ + public DependencyVector(@NotNull String identifier, @NotNull Class versioningSystem, @NotNull String version, @NotNull List<@NotNull String> dependencies) { + this.identifier = identifier; + this.versioningSystem = versioningSystem; + this.version = version; + this.dependencies = dependencies; + } + + /** + * Constructor. + * + * @param identifier identifier + * @param versioningSystem versioning system to use + * @param version version + */ + public DependencyVector(@NotNull String identifier, @NotNull Class versioningSystem, @NotNull String version) { + this.identifier = identifier; + this.versioningSystem = versioningSystem; + this.version = version; + this.dependencies = new ArrayList<>(); + } +} diff --git a/base/src/main/java/de/staropensource/sosengine/base/utility/DependencyResolver.java b/base/src/main/java/de/staropensource/sosengine/base/utility/DependencyResolver.java new file mode 100644 index 00000000..527f1415 --- /dev/null +++ b/base/src/main/java/de/staropensource/sosengine/base/utility/DependencyResolver.java @@ -0,0 +1,152 @@ +/* + * STAROPENSOURCE ENGINE SOURCE FILE + * Copyright (c) 2024 The StarOpenSource Engine Contributors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.staropensource.sosengine.base.utility; + +import de.staropensource.sosengine.base.exceptions.UnexpectedThrowableException; +import de.staropensource.sosengine.base.exceptions.UnmetDependenciesException; +import de.staropensource.sosengine.base.types.DependencyResolvedDependencyVector; +import de.staropensource.sosengine.base.types.DependencyVector; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Resolves dependency vectors. + * + * @since 1-alpha1 + */ +@SuppressWarnings({ "unused" }) +public class DependencyResolver { + /** + * A list of {@link DependencyVector}s. + * + * @since 1-alpha1 + */ + List vectors = new ArrayList<>(); + + /** + * Constructor. + */ + public DependencyResolver() {} + + /** + * Adds a dependency vector. + * + * @param vector dependency vector to add + * @since 1-alpha1 + */ + public void addVector(@NotNull DependencyVector vector) { + vectors.add(vector); + } + + /** + * Resolves all dependency vectors. + * Throws an exception when detecting an unmet dependency or a dependency cycle. + * + * @since 1-alpha1 + */ + public void resolve() throws UnmetDependenciesException, UnexpectedThrowableException { + List resolvedDependencyVectors = new ArrayList<>(); + Map unmetDependencies = new HashMap<>(); + + try { + for (DependencyVector vector : vectors) + for (String dependency : vector.getDependencies()) { + int match = 0; + + if (dependency.contains("=")) { + String dependencyIdentifier = dependency.substring(0, dependency.indexOf("=")); + String dependencyVersion = dependency.substring(dependency.indexOf("=")); + + for (DependencyVector vectorCheck : vectors) + if (vectorCheck.getIdentifier().equals(dependency)) { + if (vectorCheck.getVersioningSystem().getDeclaredConstructor().newInstance(dependencyVersion).compare(vectorCheck.getVersioningSystem().getDeclaredConstructor().newInstance(vectorCheck.getVersion())) == 1) + match = -1; + else + match = 2; + + break; + } + + if (match == 0) + unmetDependencies.put(vector, "Depends on '" + dependencyIdentifier + "', which is missing"); + else { + unmetDependencies.put(vector, "Depends exactly on '" + dependency + "', which is not installed"); + } + } else if (dependency.contains("<")) { + String dependencyIdentifier = dependency.substring(0, dependency.indexOf("<")); + String dependencyVersion = dependency.substring(dependency.indexOf("<")); + + for (DependencyVector vectorCheck : vectors) + if (vectorCheck.getIdentifier().equals(dependency)) { + if (vectorCheck.getVersioningSystem().getDeclaredConstructor().newInstance(dependencyVersion).compare(vectorCheck.getVersioningSystem().getDeclaredConstructor().newInstance(vectorCheck.getVersion())) == 0) + match = -1; + else + match = 2; + + break; + } + + if (match == 0) + unmetDependencies.put(vector, "Depends on '" + dependencyIdentifier + "', which is missing"); + else { + unmetDependencies.put(vector, "Depends at maximum on '" + dependency + "', which is not installed"); + } + } else if (dependency.contains(">")) { + String dependencyIdentifier = dependency.substring(0, dependency.indexOf(">")); + String dependencyVersion = dependency.substring(dependency.indexOf(">")); + + for (DependencyVector vectorCheck : vectors) + if (vectorCheck.getIdentifier().equals(dependency)) { + if (vectorCheck.getVersioningSystem().getDeclaredConstructor().newInstance(dependencyVersion).compare(vectorCheck.getVersioningSystem().getDeclaredConstructor().newInstance(vectorCheck.getVersion())) == 2) + match = -1; + else + match = 2; + + break; + } + + if (match == 0) + unmetDependencies.put(vector, "Depends on '" + dependencyIdentifier + "', which is missing"); + else { + unmetDependencies.put(vector, "Depends at minimum on '" + dependency + "', which is not installed"); + } + } else { + for (DependencyVector vectorCheck : vectors) + if (vectorCheck.getIdentifier().equals(dependency)) { + match = -1; + break; + } + + if (match == 0) + unmetDependencies.put(vector, "Depends on any version of '" + dependency + "', which is missing"); + } + } + } catch (Exception exception) { + throw new UnexpectedThrowableException(exception); + } + + if (!unmetDependencies.isEmpty()) + throw new UnmetDependenciesException(unmetDependencies); + } +}