diff --git a/HMCL/src/main/java/moe/mickey/minecraft/skin/fx/SkinCube.java b/HMCL/src/main/java/moe/mickey/minecraft/skin/fx/SkinCube.java index efd000b12..0929156c6 100644 --- a/HMCL/src/main/java/moe/mickey/minecraft/skin/fx/SkinCube.java +++ b/HMCL/src/main/java/moe/mickey/minecraft/skin/fx/SkinCube.java @@ -4,8 +4,6 @@ import javafx.scene.shape.Mesh; import javafx.scene.shape.MeshView; import javafx.scene.shape.TriangleMesh; -import org.apache.commons.lang3.ArrayUtils; - public class SkinCube extends MeshView { public static class Model extends TriangleMesh { @@ -77,13 +75,23 @@ public class SkinCube extends MeshView { }; int[] copy = faces.clone(); - ArrayUtils.reverse(copy); + + for (int i = 0, mid = copy.length >> 1, j = copy.length - 1; i < mid; i++, j--) { + int tmp = copy[i]; + copy[i] = copy[j]; + copy[j] = tmp; + } + for (int i = 0; i < copy.length; i += 2) { int tmp = copy[i]; copy[i] = copy[i + 1]; copy[i + 1] = tmp; } - return ArrayUtils.addAll(faces, copy); + + int[] result = new int[faces.length + copy.length]; + System.arraycopy(faces, 0, result, 0, faces.length); + System.arraycopy(copy, 0, result, faces.length, copy.length); + return result; } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java index f2955c79d..fe70b151f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java @@ -46,9 +46,9 @@ import javafx.scene.text.TextFlow; import javafx.util.Callback; import javafx.util.Duration; import javafx.util.StringConverter; -import org.apache.commons.lang3.mutable.MutableObject; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.ui.construct.JFXHyperlink; +import org.jackhuang.hmcl.util.Holder; import org.jackhuang.hmcl.util.Logging; import org.jackhuang.hmcl.util.ResourceNotFoundError; import org.jackhuang.hmcl.util.io.FileUtils; @@ -721,16 +721,16 @@ public final class FXUtils { } public static Callback, ListCell> jfxListCellFactory(Function graphicBuilder) { - MutableObject lastCell = new MutableObject<>(); + Holder lastCell = new Holder<>(); return view -> new JFXListCell() { @Override public void updateItem(T item, boolean empty) { super.updateItem(item, empty); // https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html - if (this == lastCell.getValue() && !isVisible()) + if (this == lastCell.value && !isVisible()) return; - lastCell.setValue(this); + lastCell.value = this; if (!empty) { setContentDisplay(ContentDisplay.GRAPHIC_ONLY); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LogWindow.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LogWindow.java index 9b9f828e8..a45a77014 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LogWindow.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LogWindow.java @@ -35,9 +35,9 @@ import javafx.scene.control.Label; import javafx.scene.control.*; import javafx.scene.layout.*; import javafx.stage.Stage; -import org.apache.commons.lang3.mutable.MutableObject; import org.jackhuang.hmcl.game.LauncherHelper; import org.jackhuang.hmcl.setting.Theme; +import org.jackhuang.hmcl.util.Holder; import org.jackhuang.hmcl.util.CircularArrayList; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.Log4jLevel; @@ -294,7 +294,7 @@ public final class LogWindow extends Stage { listView.setStyle("-fx-font-family: " + Lang.requireNonNullElse(config().getFontFamily(), FXUtils.DEFAULT_MONOSPACE_FONT) + "; -fx-font-size: " + config().getFontSize() + "px;"); - MutableObject lastCell = new MutableObject<>(); + Holder lastCell = new Holder<>(); listView.setCellFactory(x -> new ListCell() { { getStyleClass().add("log-window-list-cell"); @@ -313,9 +313,9 @@ public final class LogWindow extends Stage { super.updateItem(item, empty); // https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html - if (this == lastCell.getValue() && !isVisible()) + if (this == lastCell.value && !isVisible()) return; - lastCell.setValue(this); + lastCell.value = this; pseudoClassStateChanged(EMPTY, empty); pseudoClassStateChanged(FATAL, !empty && item.level == Log4jLevel.FATAL); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MDListCell.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MDListCell.java index a0246c3e9..fe2f3263f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MDListCell.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MDListCell.java @@ -22,17 +22,17 @@ import javafx.css.PseudoClass; import javafx.scene.control.ListCell; import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; -import org.apache.commons.lang3.mutable.MutableObject; import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.util.Holder; public abstract class MDListCell extends ListCell { private final PseudoClass SELECTED = PseudoClass.getPseudoClass("selected"); private final StackPane container = new StackPane(); private final StackPane root = new StackPane(); - private final MutableObject lastCell; + private final Holder lastCell; - public MDListCell(JFXListView listView, MutableObject lastCell) { + public MDListCell(JFXListView listView, Holder lastCell) { this.lastCell = lastCell; setText(null); @@ -57,9 +57,9 @@ public abstract class MDListCell extends ListCell { // https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html if (lastCell != null) { - if (this == lastCell.getValue() && !isVisible()) + if (this == lastCell.value && !isVisible()) return; - lastCell.setValue(this); + lastCell.value = this; } updateControl(item, empty); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java index 7a44a882c..f69a553a0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java @@ -28,7 +28,6 @@ import javafx.scene.control.Label; import javafx.scene.control.ListCell; import javafx.scene.image.Image; import javafx.scene.layout.*; -import org.apache.commons.lang3.mutable.MutableObject; import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.RemoteVersion; import org.jackhuang.hmcl.download.VersionList; @@ -53,6 +52,7 @@ import org.jackhuang.hmcl.ui.wizard.Navigation; import org.jackhuang.hmcl.ui.wizard.Refreshable; import org.jackhuang.hmcl.ui.wizard.WizardPage; import org.jackhuang.hmcl.util.HMCLService; +import org.jackhuang.hmcl.util.Holder; import org.jackhuang.hmcl.util.i18n.Locales; import java.util.EnumMap; @@ -179,7 +179,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres btnRefresh.setGraphic(wrap(SVG.refresh(Theme.blackFillBinding(), -1, -1))); - MutableObject lastCell = new MutableObject<>(); + Holder lastCell = new Holder<>(); EnumMap icons = new EnumMap<>(VersionIconType.class); list.setCellFactory(listView -> new RemoteVersionListCell(lastCell, icons)); @@ -273,10 +273,10 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres final RipplerContainer ripplerContainer = new RipplerContainer(content); final StackPane pane = new StackPane(); - private final MutableObject lastCell; + private final Holder lastCell; private final EnumMap icons; - RemoteVersionListCell(MutableObject lastCell, EnumMap icons) { + RemoteVersionListCell(Holder lastCell, EnumMap icons) { this.lastCell = lastCell; this.icons = icons; @@ -294,9 +294,9 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres super.updateItem(remoteVersion, empty); // https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html - if (this == lastCell.getValue() && !isVisible()) + if (this == lastCell.value && !isVisible()) return; - lastCell.setValue(this); + lastCell.value = this; if (empty) { setGraphic(null); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SponsorPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SponsorPage.java index ff6859719..07716990b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SponsorPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SponsorPage.java @@ -27,10 +27,10 @@ import javafx.scene.Cursor; import javafx.scene.control.Label; import javafx.scene.layout.*; import javafx.scene.text.TextAlignment; -import org.apache.commons.lang3.mutable.MutableObject; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.util.Holder; import org.jackhuang.hmcl.util.io.HttpRequest; import java.math.BigDecimal; @@ -85,16 +85,16 @@ public class SponsorPage extends StackPane { StackPane pane = new StackPane(); pane.getStyleClass().add("card"); listView = new JFXListView<>(); - MutableObject lastCell = new MutableObject<>(); + Holder lastCell = new Holder<>(); listView.setCellFactory((listView) -> new JFXListCell() { @Override public void updateItem(Sponsor item, boolean empty) { super.updateItem(item, empty); // https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html - if (this == lastCell.getValue() && !isVisible()) + if (this == lastCell.value && !isVisible()) return; - lastCell.setValue(this); + lastCell.value = this; if (!empty) { setText(item.getName()); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java index dc9a1e4b4..5d6d1020d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java @@ -32,7 +32,6 @@ import javafx.scene.image.ImageView; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.StackPane; -import org.apache.commons.lang3.mutable.MutableObject; import org.jackhuang.hmcl.mod.LocalModFile; import org.jackhuang.hmcl.mod.ModManager; import org.jackhuang.hmcl.setting.Theme; @@ -44,6 +43,7 @@ import org.jackhuang.hmcl.ui.SVG; import org.jackhuang.hmcl.ui.animation.ContainerAnimations; import org.jackhuang.hmcl.ui.animation.TransitionPane; import org.jackhuang.hmcl.ui.construct.*; +import org.jackhuang.hmcl.util.Holder; import org.jackhuang.hmcl.util.Lazy; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.i18n.I18n; @@ -164,7 +164,7 @@ class ModListPageSkin extends SkinBase { center.getStyleClass().add("large-spinner-pane"); center.loadingProperty().bind(skinnable.loadingProperty()); - MutableObject lastCell = new MutableObject<>(); + Holder lastCell = new Holder<>(); listView.setCellFactory(x -> new ModInfoListCell(listView, lastCell)); listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); Bindings.bindContent(listView.getItems(), skinnable.getItems()); @@ -371,7 +371,7 @@ class ModListPageSkin extends SkinBase { JFXButton revealButton = new JFXButton(); BooleanProperty booleanProperty; - ModInfoListCell(JFXListView listView, MutableObject lastCell) { + ModInfoListCell(JFXListView listView, Holder lastCell) { super(listView, lastCell); HBox container = new HBox(8); diff --git a/HMCLCore/build.gradle.kts b/HMCLCore/build.gradle.kts index c5c94d641..6b886f6b1 100644 --- a/HMCLCore/build.gradle.kts +++ b/HMCLCore/build.gradle.kts @@ -15,6 +15,5 @@ dependencies { api("com.nqzero:permit-reflect:0.3") api("org.nanohttpd:nanohttpd:2.3.1") api("org.apache.commons:commons-compress:1.21") - api("org.apache.commons:commons-lang3:3.12.0") compileOnlyApi("org.jetbrains:annotations:16.0.3") } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/JavaVersionConstraint.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/JavaVersionConstraint.java index f2e1444c1..53ec61242 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/JavaVersionConstraint.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/JavaVersionConstraint.java @@ -17,50 +17,51 @@ */ package org.jackhuang.hmcl.game; -import org.apache.commons.lang3.Range; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.platform.Architecture; import org.jackhuang.hmcl.util.platform.JavaVersion; import org.jackhuang.hmcl.util.platform.OperatingSystem; import org.jackhuang.hmcl.util.versioning.VersionNumber; +import org.jackhuang.hmcl.util.versioning.VersionRange; import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Objects; import static org.jackhuang.hmcl.download.LibraryAnalyzer.LAUNCH_WRAPPER_MAIN; +import static org.jackhuang.hmcl.util.versioning.VersionRange.*; public enum JavaVersionConstraint { // Minecraft>=1.13 requires Java 8 - VANILLA_JAVA_8(JavaVersionConstraint.RULE_MANDATORY, versionRange("1.13", JavaVersionConstraint.MAX), versionRange("1.8", JavaVersionConstraint.MAX)), + VANILLA_JAVA_8(JavaVersionConstraint.RULE_MANDATORY, atLeast("1.13"), atLeast("1.8")), // Minecraft 1.17 requires Java 16 - VANILLA_JAVA_16(JavaVersionConstraint.RULE_MANDATORY, versionRange("1.17", JavaVersionConstraint.MAX), versionRange("16", JavaVersionConstraint.MAX)), + VANILLA_JAVA_16(JavaVersionConstraint.RULE_MANDATORY, atLeast("1.17"), atLeast("16")), // Minecraft>=1.18 requires Java 17 - VANILLA_JAVA_17(JavaVersionConstraint.RULE_MANDATORY, versionRange("1.18", JavaVersionConstraint.MAX), versionRange("17", JavaVersionConstraint.MAX)), + VANILLA_JAVA_17(JavaVersionConstraint.RULE_MANDATORY, atLeast("1.18"), atLeast("17")), // Minecraft<=1.7.2+Forge requires Java<=7, But LegacyModFixer may fix that problem. So only suggest user using Java 7. - MODDED_JAVA_7(JavaVersionConstraint.RULE_SUGGESTED, versionRange(JavaVersionConstraint.MIN, "1.7.2"), versionRange(JavaVersionConstraint.MIN, "1.7.999")) { + MODDED_JAVA_7(JavaVersionConstraint.RULE_SUGGESTED, atMost("1.7.2"), atMost("1.7.999")) { @Override protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version, @Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) { return version != null && analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE); } }, - MODDED_JAVA_8(JavaVersionConstraint.RULE_SUGGESTED, versionRange("1.7.10", "1.16.999"), versionRange("1.8", "1.8.999")) { + MODDED_JAVA_8(JavaVersionConstraint.RULE_SUGGESTED, between("1.7.10", "1.16.999"), between("1.8", "1.8.999")) { @Override protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version, @Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) { return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE); } }, - MODDED_JAVA_16(JavaVersionConstraint.RULE_SUGGESTED, versionRange("1.17", "1.17.999"), versionRange("16", "16.999")) { + MODDED_JAVA_16(JavaVersionConstraint.RULE_SUGGESTED, between("1.17", "1.17.999"), between("16", "16.999")) { @Override protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version, @Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) { return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE); } }, - MODDED_JAVA_17(JavaVersionConstraint.RULE_SUGGESTED, versionRange("1.18", JavaVersionConstraint.MAX), versionRange("17", "17.999")) { + MODDED_JAVA_17(JavaVersionConstraint.RULE_SUGGESTED, atLeast("1.18"), between("17", "17.999")) { @Override protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version, @Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) { @@ -68,7 +69,7 @@ public enum JavaVersionConstraint { } }, // LaunchWrapper<=1.12 will crash because LaunchWrapper assumes the system class loader is an instance of URLClassLoader (Java 8) - LAUNCH_WRAPPER(JavaVersionConstraint.RULE_MANDATORY, versionRange("0", "1.12.999"), versionRange("0", "1.8.999")) { + LAUNCH_WRAPPER(JavaVersionConstraint.RULE_MANDATORY, atMost("1.12.999"), atMost("1.8.999")) { @Override protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version, @Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) { @@ -80,9 +81,9 @@ public enum JavaVersionConstraint { } }, // Minecraft>=1.13 may crash when generating world on Java [1.8,1.8.0_51) - VANILLA_JAVA_8_51(JavaVersionConstraint.RULE_SUGGESTED, versionRange("1.13", JavaVersionConstraint.MAX), versionRange("1.8.0_51", JavaVersionConstraint.MAX)), + VANILLA_JAVA_8_51(JavaVersionConstraint.RULE_SUGGESTED, atLeast("1.13"), atLeast("1.8.0_51")), // Minecraft with suggested java version recorded in game json is restrictedly constrained. - GAME_JSON(JavaVersionConstraint.RULE_MANDATORY, versionRange(JavaVersionConstraint.MIN, JavaVersionConstraint.MAX), versionRange(JavaVersionConstraint.MIN, JavaVersionConstraint.MAX)) { + GAME_JSON(JavaVersionConstraint.RULE_MANDATORY, VersionRange.all(), VersionRange.all()) { @Override protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version, @Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) { @@ -92,19 +93,19 @@ public enum JavaVersionConstraint { } @Override - public Range getJavaVersionRange(Version version) { + public VersionRange getJavaVersionRange(Version version) { String javaVersion; if (Objects.requireNonNull(version.getJavaVersion()).getMajorVersion() >= 9) { javaVersion = "" + version.getJavaVersion().getMajorVersion(); } else { javaVersion = "1." + version.getJavaVersion().getMajorVersion(); } - return JavaVersionConstraint.versionRange(javaVersion, JavaVersionConstraint.MAX); + return atLeast(javaVersion); } }, // On Linux, JDK 9+ cannot launch Minecraft<=1.12.2, since JDK 9+ does not accept loading native library built in different arch. // For example, JDK 9+ 64-bit cannot load 32-bit lwjgl native library. - VANILLA_LINUX_JAVA_8(JavaVersionConstraint.RULE_MANDATORY, versionRange("0", "1.12.999"), versionRange(JavaVersionConstraint.MIN, "1.8.999")) { + VANILLA_LINUX_JAVA_8(JavaVersionConstraint.RULE_MANDATORY, atMost("1.12.999"), atMost("1.8.999")) { @Override protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version, @Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) { @@ -119,7 +120,7 @@ public enum JavaVersionConstraint { } }, // Minecraft currently does not provide official support for architectures other than x86 and x86-64. - VANILLA_X86(JavaVersionConstraint.RULE_SUGGESTED, versionRange(JavaVersionConstraint.MIN, JavaVersionConstraint.MAX), versionRange(JavaVersionConstraint.MIN, JavaVersionConstraint.MAX)) { + VANILLA_X86(JavaVersionConstraint.RULE_SUGGESTED, VersionRange.all(), VersionRange.all()) { @Override protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version, @Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) { @@ -138,7 +139,7 @@ public enum JavaVersionConstraint { } }, // Minecraft 1.16+Forge with crash because JDK-8273826 - MODLAUNCHER_8(JavaVersionConstraint.RULE_SUGGESTED, versionRange("1.16.3", "1.17.1"), versionRange(JavaVersionConstraint.MIN, JavaVersionConstraint.MAX)) { + MODLAUNCHER_8(JavaVersionConstraint.RULE_SUGGESTED, between("1.16.3", "1.17.1"), VersionRange.all()) { @Override protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version, @Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) { @@ -158,7 +159,7 @@ public enum JavaVersionConstraint { case "1.16.5": return forgePatchVersion.compareTo(VersionNumber.asVersion("36.2.23")) <= 0; case "1.17.1": - return versionRange("37.0.60", "37.0.75").contains(forgePatchVersion); + return between("37.0.60", "37.0.75").contains(forgePatchVersion); default: return false; } @@ -184,10 +185,10 @@ public enum JavaVersionConstraint { };; private final int type; - private final Range gameVersionRange; - private final Range javaVersionRange; + private final VersionRange gameVersionRange; + private final VersionRange javaVersionRange; - JavaVersionConstraint(int type, Range gameVersionRange, Range javaVersionRange) { + JavaVersionConstraint(int type, VersionRange gameVersionRange, VersionRange javaVersionRange) { this.type = type; this.gameVersionRange = gameVersionRange; this.javaVersionRange = javaVersionRange; @@ -197,11 +198,11 @@ public enum JavaVersionConstraint { return type; } - public Range getGameVersionRange() { + public VersionRange getGameVersionRange() { return gameVersionRange; } - public Range getJavaVersionRange(Version version) { + public VersionRange getJavaVersionRange(Version version) { return javaVersionRange; } @@ -224,12 +225,12 @@ public enum JavaVersionConstraint { public static final List ALL = Lang.immutableListOf(values()); public static VersionRanges findSuitableJavaVersionRange(VersionNumber gameVersion, Version version) { - Range mandatoryJavaRange = versionRange(MIN, MAX); - Range suggestedJavaRange = versionRange(MIN, MAX); + VersionRange mandatoryJavaRange = VersionRange.all(); + VersionRange suggestedJavaRange = VersionRange.all(); LibraryAnalyzer analyzer = version != null ? LibraryAnalyzer.analyze(version) : null; for (JavaVersionConstraint java : ALL) { if (java.appliesToVersion(gameVersion, version, null, analyzer)) { - Range javaVersionRange = java.getJavaVersionRange(version); + VersionRange javaVersionRange = java.getJavaVersionRange(version); if (java.type == RULE_MANDATORY) { mandatoryJavaRange = mandatoryJavaRange.intersectionWith(javaVersionRange); suggestedJavaRange = suggestedJavaRange.intersectionWith(javaVersionRange); @@ -297,31 +298,20 @@ public enum JavaVersionConstraint { public static final int RULE_MANDATORY = 1; public static final int RULE_SUGGESTED = 2; - public static final String MIN = "0"; - public static final String MAX = "10000"; + public static final class VersionRanges { + private final VersionRange mandatory; + private final VersionRange suggested; - private static Range versionRange(String fromInclusive, String toExclusive) { - return Range.between(VersionNumber.asVersion(fromInclusive), VersionNumber.asVersion(toExclusive)); - } - - static Range versionIs(String version) { - return Range.is(VersionNumber.asVersion(version)); - } - - public static class VersionRanges { - private final Range mandatory; - private final Range suggested; - - public VersionRanges(Range mandatory, Range suggested) { + public VersionRanges(VersionRange mandatory, VersionRange suggested) { this.mandatory = mandatory; this.suggested = suggested; } - public Range getMandatory() { + public VersionRange getMandatory() { return mandatory; } - public Range getSuggested() { + public VersionRange getSuggested() { return suggested; } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Holder.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Holder.java new file mode 100644 index 000000000..c788336ef --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Holder.java @@ -0,0 +1,28 @@ +package org.jackhuang.hmcl.util; + +import java.util.Objects; + +public final class Holder { + public T value; + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (!(obj instanceof Holder)) + return false; + + return Objects.equals(this.value, ((Holder) obj).value); + } + + @Override + public String toString() { + return "Holder[" + value + "]"; + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/versioning/VersionNumber.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/versioning/VersionNumber.java index 2c5a56a44..badf696ec 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/versioning/VersionNumber.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/versioning/VersionNumber.java @@ -347,6 +347,14 @@ public class VersionNumber implements Comparable { return canonical; } + public VersionNumber min(VersionNumber that) { + return this.compareTo(that) <= 0 ? this : that; + } + + public VersionNumber max(VersionNumber that) { + return this.compareTo(that) >= 0 ? this : that; + } + @Override public boolean equals(Object o) { return o instanceof VersionNumber && canonical.equals(((VersionNumber) o).canonical); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/versioning/VersionRange.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/versioning/VersionRange.java new file mode 100644 index 000000000..1f35d91f8 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/versioning/VersionRange.java @@ -0,0 +1,163 @@ +package org.jackhuang.hmcl.util.versioning; + +import java.util.Objects; + +public final class VersionRange { + private static final VersionRange EMPTY = new VersionRange(null, null); + private static final VersionRange ALL = new VersionRange(null, null); + + public static VersionRange empty() { + return EMPTY; + } + + public static VersionRange all() { + return ALL; + } + + public static VersionRange between(String minimum, String maximum) { + return between(VersionNumber.asVersion(minimum), VersionNumber.asVersion(maximum)); + } + + public static VersionRange between(VersionNumber minimum, VersionNumber maximum) { + assert minimum.compareTo(maximum) <= 0; + return new VersionRange(minimum, maximum); + } + + public static VersionRange atLeast(String minimum) { + return atLeast(VersionNumber.asVersion(minimum)); + } + + public static VersionRange atLeast(VersionNumber minimum) { + assert minimum != null; + return new VersionRange(minimum, null); + } + + public static VersionRange atMost(String maximum) { + return atMost(VersionNumber.asVersion(maximum)); + } + + public static VersionRange atMost(VersionNumber maximum) { + assert maximum != null; + return new VersionRange(null, maximum); + } + + private final VersionNumber minimum; + private final VersionNumber maximum; + + private VersionRange(VersionNumber minimum, VersionNumber maximum) { + this.minimum = minimum; + this.maximum = maximum; + } + + public VersionNumber getMinimum() { + return minimum; + } + + public VersionNumber getMaximum() { + return maximum; + } + + public boolean isEmpty() { + return this == EMPTY; + } + + public boolean isAll() { + return !isEmpty() && minimum == null && maximum == null; + } + + public boolean contains(String versionNumber) { + return contains(VersionNumber.asVersion(versionNumber)); + } + + public boolean contains(VersionNumber versionNumber) { + if (isEmpty()) return false; + if (isAll()) return true; + + return (minimum == null || minimum.compareTo(versionNumber) <= 0) && (maximum == null || maximum.compareTo(versionNumber) >= 0); + } + + public boolean isOverlappedBy(final VersionRange that) { + if (this.isEmpty() || that.isEmpty()) + return false; + + if (this.isAll() || that.isAll()) + return true; + + if (this.minimum == null) + return that.minimum == null || that.minimum.compareTo(this.maximum) <= 0; + + if (this.maximum == null) + return that.maximum == null || that.maximum.compareTo(this.minimum) >= 0; + + return that.contains(minimum) || that.contains(maximum) || (that.minimum != null && contains(that.minimum)); + } + + public VersionRange intersectionWith(VersionRange that) { + if (this.isAll()) + return that; + if (that.isAll()) + return this; + + if (!isOverlappedBy(that)) + return EMPTY; + + VersionNumber newMinimum; + if (this.minimum == null) + newMinimum = that.minimum; + else if (that.minimum == null) + newMinimum = this.minimum; + else + newMinimum = this.minimum.max(that.minimum); + + VersionNumber newMaximum; + if (this.maximum == null) + newMaximum = that.maximum; + else if (that.maximum == null) + newMaximum = this.maximum; + else + newMaximum = this.maximum.min(that.maximum); + + return new VersionRange(newMinimum, newMaximum); + } + + @Override + public int hashCode() { + if (isEmpty()) + return 1121763849; // Magic Number + if (isAll()) + return -475303149; // Magic Number + + return Objects.hash(minimum) ^ Objects.hash(maximum); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!(obj instanceof VersionRange)) + return false; + + VersionRange that = (VersionRange) obj; + + return this.isEmpty() == that.isEmpty() && this.isAll() == that.isAll() + && Objects.equals(this.minimum, that.minimum) + && Objects.equals(this.maximum, that.maximum); + } + + @Override + public String toString() { + if (isEmpty()) + return "EMPTY"; + + if (isAll()) + return "ALL"; + + if (minimum == null) + return "At most " + maximum; + + if (maximum == null) + return "At least " + minimum; + + return "[" + minimum + ".." + maximum + "]"; + } +} diff --git a/HMCLCore/src/test/java/org/jackhuang/hmcl/game/JavaVersionConstraintTest.java b/HMCLCore/src/test/java/org/jackhuang/hmcl/game/JavaVersionConstraintTest.java index f1ed6f778..6bc3ce585 100644 --- a/HMCLCore/src/test/java/org/jackhuang/hmcl/game/JavaVersionConstraintTest.java +++ b/HMCLCore/src/test/java/org/jackhuang/hmcl/game/JavaVersionConstraintTest.java @@ -17,8 +17,8 @@ */ package org.jackhuang.hmcl.game; -import org.apache.commons.lang3.Range; import org.jackhuang.hmcl.util.versioning.VersionNumber; +import org.jackhuang.hmcl.util.versioning.VersionRange; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -32,8 +32,6 @@ public class JavaVersionConstraintTest { null ); - assertEquals( - Range.between(VersionNumber.asVersion("16"), VersionNumber.asVersion(JavaVersionConstraint.MAX)), - range.getMandatory()); + assertEquals(VersionRange.atLeast("16"), range.getMandatory()); } } diff --git a/HMCLCore/src/test/java/org/jackhuang/hmcl/util/VersionNumberTest.java b/HMCLCore/src/test/java/org/jackhuang/hmcl/util/versioning/VersionNumberTest.java similarity index 97% rename from HMCLCore/src/test/java/org/jackhuang/hmcl/util/VersionNumberTest.java rename to HMCLCore/src/test/java/org/jackhuang/hmcl/util/versioning/VersionNumberTest.java index b51ffbdc5..24c1aa649 100644 --- a/HMCLCore/src/test/java/org/jackhuang/hmcl/util/VersionNumberTest.java +++ b/HMCLCore/src/test/java/org/jackhuang/hmcl/util/versioning/VersionNumberTest.java @@ -15,9 +15,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.util; +package org.jackhuang.hmcl.util.versioning; -import org.jackhuang.hmcl.util.versioning.VersionNumber; import org.junit.jupiter.api.Test; import java.util.Arrays; diff --git a/HMCLCore/src/test/java/org/jackhuang/hmcl/util/versioning/VersionRangeTest.java b/HMCLCore/src/test/java/org/jackhuang/hmcl/util/versioning/VersionRangeTest.java new file mode 100644 index 000000000..0697cb0cc --- /dev/null +++ b/HMCLCore/src/test/java/org/jackhuang/hmcl/util/versioning/VersionRangeTest.java @@ -0,0 +1,119 @@ +package org.jackhuang.hmcl.util.versioning; + +import org.junit.jupiter.api.Test; + +import static org.jackhuang.hmcl.util.versioning.VersionRange.*; +import static org.junit.jupiter.api.Assertions.*; + +public class VersionRangeTest { + + @Test + public void testContains() { + assertTrue(between("10", "20").contains("10")); + assertTrue(between("10", "20").contains("15")); + assertTrue(between("10", "20").contains("20")); + assertFalse(between("10", "20").contains("5")); + assertFalse(between("10", "20").contains("25")); + + assertTrue(between("10", "10").contains("10")); + assertFalse(between("10", "10").contains("5")); + assertFalse(between("10", "10").contains("15")); + + assertTrue(atLeast("10").contains("10")); + assertTrue(atLeast("10").contains("20")); + assertFalse(atLeast("10").contains("5")); + + assertTrue(atMost("10").contains("10")); + assertTrue(atMost("10").contains("5")); + assertFalse(atMost("10").contains("20")); + + assertFalse(empty().contains("0")); + assertFalse(empty().contains("10")); + + assertTrue(all().contains("0")); + assertTrue(all().contains("10")); + } + + private static void assertIsOverlappedBy(boolean value, VersionRange range1, VersionRange range2) { + assertEquals(value, range1.isOverlappedBy(range2)); + assertEquals(value, range2.isOverlappedBy(range1)); + } + + @Test + public void testIsOverlappedBy() { + assertIsOverlappedBy(true, all(), all()); + assertIsOverlappedBy(false, all(), empty()); + assertIsOverlappedBy(false, empty(), empty()); + + assertIsOverlappedBy(true, all(), between("10", "20")); + assertIsOverlappedBy(true, all(), atLeast("10")); + assertIsOverlappedBy(true, all(), atMost("10")); + + assertIsOverlappedBy(false, empty(), between("10", "20")); + assertIsOverlappedBy(false, empty(), atLeast("10")); + assertIsOverlappedBy(false, empty(), atMost("10")); + + assertIsOverlappedBy(true, between("10", "20"), between("10", "20")); + assertIsOverlappedBy(true, between("10", "20"), between("5", "20")); + assertIsOverlappedBy(true, between("10", "20"), between("5", "15")); + assertIsOverlappedBy(true, between("10", "20"), between("5", "10")); + assertIsOverlappedBy(false, between("10", "20"), between("5", "5")); + assertIsOverlappedBy(true, between("10", "20"), between("10", "30")); + assertIsOverlappedBy(true, between("10", "20"), between("15", "30")); + assertIsOverlappedBy(true, between("10", "20"), between("20", "30")); + assertIsOverlappedBy(false, between("10", "20"), between("21", "30")); + assertIsOverlappedBy(true, between("10", "20"), atLeast("5")); + assertIsOverlappedBy(true, between("10", "20"), atLeast("10")); + assertIsOverlappedBy(true, between("10", "20"), atLeast("15")); + assertIsOverlappedBy(true, between("10", "20"), atLeast("20")); + assertIsOverlappedBy(false, between("10", "20"), atLeast("25")); + + assertIsOverlappedBy(true, atLeast("10"), atLeast("10")); + assertIsOverlappedBy(true, atLeast("10"), atLeast("20")); + assertIsOverlappedBy(true, atLeast("10"), atLeast("5")); + assertIsOverlappedBy(true, atLeast("10"), atMost("10")); + assertIsOverlappedBy(true, atLeast("10"), atMost("20")); + assertIsOverlappedBy(false, atLeast("10"), atMost("5")); + } + + private static void assertIntersectionWith(VersionRange range1, VersionRange range2, VersionRange result) { + assertEquals(result, range1.intersectionWith(range2)); + assertEquals(result, range2.intersectionWith(range1)); + } + + @Test + public void testIntersectionWith() { + assertIntersectionWith(all(), all(), all()); + assertIntersectionWith(all(), empty(), empty()); + assertIntersectionWith(all(), between("10", "20"), between("10", "20")); + assertIntersectionWith(all(), atLeast("10"), atLeast("10")); + assertIntersectionWith(all(), atMost("10"), atMost("10")); + + assertIntersectionWith(empty(), empty(), empty()); + assertIntersectionWith(empty(), between("10", "20"), empty()); + assertIntersectionWith(empty(), atLeast("10"), empty()); + assertIntersectionWith(empty(), atMost("10"), empty()); + + assertIntersectionWith(between("10", "20"), between("10", "20"), between("10", "20")); + assertIntersectionWith(between("10", "20"), between("5", "20"), between("10", "20")); + assertIntersectionWith(between("10", "20"), between("10", "25"), between("10", "20")); + assertIntersectionWith(between("10", "20"), between("5", "25"), between("10", "20")); + assertIntersectionWith(between("10", "20"), between("15", "20"), between("15", "20")); + assertIntersectionWith(between("10", "20"), between("10", "15"), between("10", "15")); + assertIntersectionWith(between("10", "20"), between("14", "16"), between("14", "16")); + assertIntersectionWith(between("10", "20"), atLeast("5"), between("10", "20")); + assertIntersectionWith(between("10", "20"), atLeast("10"), between("10", "20")); + assertIntersectionWith(between("10", "20"), atLeast("15"), between("15", "20")); + assertIntersectionWith(between("10", "20"), atLeast("20"), between("20", "20")); + assertIntersectionWith(between("10", "20"), atLeast("25"), empty()); + assertIntersectionWith(between("10", "20"), atMost("25"), between("10", "20")); + assertIntersectionWith(between("10", "20"), atMost("20"), between("10", "20")); + assertIntersectionWith(between("10", "20"), atMost("15"), between("10", "15")); + assertIntersectionWith(between("10", "20"), atMost("10"), between("10", "10")); + assertIntersectionWith(between("10", "20"), atMost("5"), empty()); + + assertIntersectionWith(atLeast("10"), atMost("10"), between("10", "10")); + assertIntersectionWith(atLeast("10"), atMost("20"), between("10", "20")); + assertIntersectionWith(atLeast("10"), atMost("5"), empty()); + } +}