弃用 commons-lang (#2061)

* Remove ArrayUtils

* Remove MutableObject

* VersionRange

* Replace Range with VersionRange

* fix checkstyle

* Update

* update

* update

* fix checkstyle

* fix checkstyle

* update
This commit is contained in:
Glavo
2023-02-08 19:57:52 +08:00
committed by GitHub
parent 90c261ddd1
commit 7e522ff136
15 changed files with 391 additions and 79 deletions

View File

@@ -4,8 +4,6 @@ import javafx.scene.shape.Mesh;
import javafx.scene.shape.MeshView; import javafx.scene.shape.MeshView;
import javafx.scene.shape.TriangleMesh; import javafx.scene.shape.TriangleMesh;
import org.apache.commons.lang3.ArrayUtils;
public class SkinCube extends MeshView { public class SkinCube extends MeshView {
public static class Model extends TriangleMesh { public static class Model extends TriangleMesh {
@@ -77,13 +75,23 @@ public class SkinCube extends MeshView {
}; };
int[] copy = faces.clone(); 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) { for (int i = 0; i < copy.length; i += 2) {
int tmp = copy[i]; int tmp = copy[i];
copy[i] = copy[i + 1]; copy[i] = copy[i + 1];
copy[i + 1] = tmp; 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;
} }
} }

View File

@@ -46,9 +46,9 @@ import javafx.scene.text.TextFlow;
import javafx.util.Callback; import javafx.util.Callback;
import javafx.util.Duration; import javafx.util.Duration;
import javafx.util.StringConverter; import javafx.util.StringConverter;
import org.apache.commons.lang3.mutable.MutableObject;
import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.ui.construct.JFXHyperlink; import org.jackhuang.hmcl.ui.construct.JFXHyperlink;
import org.jackhuang.hmcl.util.Holder;
import org.jackhuang.hmcl.util.Logging; import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.ResourceNotFoundError; import org.jackhuang.hmcl.util.ResourceNotFoundError;
import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.FileUtils;
@@ -721,16 +721,16 @@ public final class FXUtils {
} }
public static <T> Callback<ListView<T>, ListCell<T>> jfxListCellFactory(Function<T, Node> graphicBuilder) { public static <T> Callback<ListView<T>, ListCell<T>> jfxListCellFactory(Function<T, Node> graphicBuilder) {
MutableObject<Object> lastCell = new MutableObject<>(); Holder<Object> lastCell = new Holder<>();
return view -> new JFXListCell<T>() { return view -> new JFXListCell<T>() {
@Override @Override
public void updateItem(T item, boolean empty) { public void updateItem(T item, boolean empty) {
super.updateItem(item, empty); super.updateItem(item, empty);
// https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html // https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html
if (this == lastCell.getValue() && !isVisible()) if (this == lastCell.value && !isVisible())
return; return;
lastCell.setValue(this); lastCell.value = this;
if (!empty) { if (!empty) {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY); setContentDisplay(ContentDisplay.GRAPHIC_ONLY);

View File

@@ -35,9 +35,9 @@ import javafx.scene.control.Label;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.layout.*; import javafx.scene.layout.*;
import javafx.stage.Stage; import javafx.stage.Stage;
import org.apache.commons.lang3.mutable.MutableObject;
import org.jackhuang.hmcl.game.LauncherHelper; import org.jackhuang.hmcl.game.LauncherHelper;
import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.util.Holder;
import org.jackhuang.hmcl.util.CircularArrayList; import org.jackhuang.hmcl.util.CircularArrayList;
import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.Log4jLevel; 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) listView.setStyle("-fx-font-family: " + Lang.requireNonNullElse(config().getFontFamily(), FXUtils.DEFAULT_MONOSPACE_FONT)
+ "; -fx-font-size: " + config().getFontSize() + "px;"); + "; -fx-font-size: " + config().getFontSize() + "px;");
MutableObject<Object> lastCell = new MutableObject<>(); Holder<Object> lastCell = new Holder<>();
listView.setCellFactory(x -> new ListCell<Log>() { listView.setCellFactory(x -> new ListCell<Log>() {
{ {
getStyleClass().add("log-window-list-cell"); getStyleClass().add("log-window-list-cell");
@@ -313,9 +313,9 @@ public final class LogWindow extends Stage {
super.updateItem(item, empty); super.updateItem(item, empty);
// https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html // https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html
if (this == lastCell.getValue() && !isVisible()) if (this == lastCell.value && !isVisible())
return; return;
lastCell.setValue(this); lastCell.value = this;
pseudoClassStateChanged(EMPTY, empty); pseudoClassStateChanged(EMPTY, empty);
pseudoClassStateChanged(FATAL, !empty && item.level == Log4jLevel.FATAL); pseudoClassStateChanged(FATAL, !empty && item.level == Log4jLevel.FATAL);

View File

@@ -22,17 +22,17 @@ import javafx.css.PseudoClass;
import javafx.scene.control.ListCell; import javafx.scene.control.ListCell;
import javafx.scene.layout.Region; import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import org.apache.commons.lang3.mutable.MutableObject;
import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.util.Holder;
public abstract class MDListCell<T> extends ListCell<T> { public abstract class MDListCell<T> extends ListCell<T> {
private final PseudoClass SELECTED = PseudoClass.getPseudoClass("selected"); private final PseudoClass SELECTED = PseudoClass.getPseudoClass("selected");
private final StackPane container = new StackPane(); private final StackPane container = new StackPane();
private final StackPane root = new StackPane(); private final StackPane root = new StackPane();
private final MutableObject<Object> lastCell; private final Holder<Object> lastCell;
public MDListCell(JFXListView<T> listView, MutableObject<Object> lastCell) { public MDListCell(JFXListView<T> listView, Holder<Object> lastCell) {
this.lastCell = lastCell; this.lastCell = lastCell;
setText(null); setText(null);
@@ -57,9 +57,9 @@ public abstract class MDListCell<T> extends ListCell<T> {
// https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html // https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html
if (lastCell != null) { if (lastCell != null) {
if (this == lastCell.getValue() && !isVisible()) if (this == lastCell.value && !isVisible())
return; return;
lastCell.setValue(this); lastCell.value = this;
} }
updateControl(item, empty); updateControl(item, empty);

View File

@@ -28,7 +28,6 @@ import javafx.scene.control.Label;
import javafx.scene.control.ListCell; import javafx.scene.control.ListCell;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.layout.*; import javafx.scene.layout.*;
import org.apache.commons.lang3.mutable.MutableObject;
import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion; import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList; 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.Refreshable;
import org.jackhuang.hmcl.ui.wizard.WizardPage; import org.jackhuang.hmcl.ui.wizard.WizardPage;
import org.jackhuang.hmcl.util.HMCLService; import org.jackhuang.hmcl.util.HMCLService;
import org.jackhuang.hmcl.util.Holder;
import org.jackhuang.hmcl.util.i18n.Locales; import org.jackhuang.hmcl.util.i18n.Locales;
import java.util.EnumMap; 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))); btnRefresh.setGraphic(wrap(SVG.refresh(Theme.blackFillBinding(), -1, -1)));
MutableObject<RemoteVersionListCell> lastCell = new MutableObject<>(); Holder<RemoteVersionListCell> lastCell = new Holder<>();
EnumMap<VersionIconType, Image> icons = new EnumMap<>(VersionIconType.class); EnumMap<VersionIconType, Image> icons = new EnumMap<>(VersionIconType.class);
list.setCellFactory(listView -> new RemoteVersionListCell(lastCell, icons)); 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 RipplerContainer ripplerContainer = new RipplerContainer(content);
final StackPane pane = new StackPane(); final StackPane pane = new StackPane();
private final MutableObject<RemoteVersionListCell> lastCell; private final Holder<RemoteVersionListCell> lastCell;
private final EnumMap<VersionIconType, Image> icons; private final EnumMap<VersionIconType, Image> icons;
RemoteVersionListCell(MutableObject<RemoteVersionListCell> lastCell, EnumMap<VersionIconType, Image> icons) { RemoteVersionListCell(Holder<RemoteVersionListCell> lastCell, EnumMap<VersionIconType, Image> icons) {
this.lastCell = lastCell; this.lastCell = lastCell;
this.icons = icons; this.icons = icons;
@@ -294,9 +294,9 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
super.updateItem(remoteVersion, empty); super.updateItem(remoteVersion, empty);
// https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html // https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html
if (this == lastCell.getValue() && !isVisible()) if (this == lastCell.value && !isVisible())
return; return;
lastCell.setValue(this); lastCell.value = this;
if (empty) { if (empty) {
setGraphic(null); setGraphic(null);

View File

@@ -27,10 +27,10 @@ import javafx.scene.Cursor;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.layout.*; import javafx.scene.layout.*;
import javafx.scene.text.TextAlignment; import javafx.scene.text.TextAlignment;
import org.apache.commons.lang3.mutable.MutableObject;
import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.util.Holder;
import org.jackhuang.hmcl.util.io.HttpRequest; import org.jackhuang.hmcl.util.io.HttpRequest;
import java.math.BigDecimal; import java.math.BigDecimal;
@@ -85,16 +85,16 @@ public class SponsorPage extends StackPane {
StackPane pane = new StackPane(); StackPane pane = new StackPane();
pane.getStyleClass().add("card"); pane.getStyleClass().add("card");
listView = new JFXListView<>(); listView = new JFXListView<>();
MutableObject<Object> lastCell = new MutableObject<>(); Holder<Object> lastCell = new Holder<>();
listView.setCellFactory((listView) -> new JFXListCell<Sponsor>() { listView.setCellFactory((listView) -> new JFXListCell<Sponsor>() {
@Override @Override
public void updateItem(Sponsor item, boolean empty) { public void updateItem(Sponsor item, boolean empty) {
super.updateItem(item, empty); super.updateItem(item, empty);
// https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html // https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html
if (this == lastCell.getValue() && !isVisible()) if (this == lastCell.value && !isVisible())
return; return;
lastCell.setValue(this); lastCell.value = this;
if (!empty) { if (!empty) {
setText(item.getName()); setText(item.getName());

View File

@@ -32,7 +32,6 @@ import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority; import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import org.apache.commons.lang3.mutable.MutableObject;
import org.jackhuang.hmcl.mod.LocalModFile; import org.jackhuang.hmcl.mod.LocalModFile;
import org.jackhuang.hmcl.mod.ModManager; import org.jackhuang.hmcl.mod.ModManager;
import org.jackhuang.hmcl.setting.Theme; 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.ContainerAnimations;
import org.jackhuang.hmcl.ui.animation.TransitionPane; import org.jackhuang.hmcl.ui.animation.TransitionPane;
import org.jackhuang.hmcl.ui.construct.*; import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.util.Holder;
import org.jackhuang.hmcl.util.Lazy; import org.jackhuang.hmcl.util.Lazy;
import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.i18n.I18n; import org.jackhuang.hmcl.util.i18n.I18n;
@@ -164,7 +164,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
center.getStyleClass().add("large-spinner-pane"); center.getStyleClass().add("large-spinner-pane");
center.loadingProperty().bind(skinnable.loadingProperty()); center.loadingProperty().bind(skinnable.loadingProperty());
MutableObject<Object> lastCell = new MutableObject<>(); Holder<Object> lastCell = new Holder<>();
listView.setCellFactory(x -> new ModInfoListCell(listView, lastCell)); listView.setCellFactory(x -> new ModInfoListCell(listView, lastCell));
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
Bindings.bindContent(listView.getItems(), skinnable.getItems()); Bindings.bindContent(listView.getItems(), skinnable.getItems());
@@ -371,7 +371,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
JFXButton revealButton = new JFXButton(); JFXButton revealButton = new JFXButton();
BooleanProperty booleanProperty; BooleanProperty booleanProperty;
ModInfoListCell(JFXListView<ModInfoObject> listView, MutableObject<Object> lastCell) { ModInfoListCell(JFXListView<ModInfoObject> listView, Holder<Object> lastCell) {
super(listView, lastCell); super(listView, lastCell);
HBox container = new HBox(8); HBox container = new HBox(8);

View File

@@ -15,6 +15,5 @@ dependencies {
api("com.nqzero:permit-reflect:0.3") api("com.nqzero:permit-reflect:0.3")
api("org.nanohttpd:nanohttpd:2.3.1") api("org.nanohttpd:nanohttpd:2.3.1")
api("org.apache.commons:commons-compress:1.21") api("org.apache.commons:commons-compress:1.21")
api("org.apache.commons:commons-lang3:3.12.0")
compileOnlyApi("org.jetbrains:annotations:16.0.3") compileOnlyApi("org.jetbrains:annotations:16.0.3")
} }

View File

@@ -17,50 +17,51 @@
*/ */
package org.jackhuang.hmcl.game; package org.jackhuang.hmcl.game;
import org.apache.commons.lang3.Range;
import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.platform.Architecture; import org.jackhuang.hmcl.util.platform.Architecture;
import org.jackhuang.hmcl.util.platform.JavaVersion; import org.jackhuang.hmcl.util.platform.JavaVersion;
import org.jackhuang.hmcl.util.platform.OperatingSystem; import org.jackhuang.hmcl.util.platform.OperatingSystem;
import org.jackhuang.hmcl.util.versioning.VersionNumber; import org.jackhuang.hmcl.util.versioning.VersionNumber;
import org.jackhuang.hmcl.util.versioning.VersionRange;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LAUNCH_WRAPPER_MAIN; import static org.jackhuang.hmcl.download.LibraryAnalyzer.LAUNCH_WRAPPER_MAIN;
import static org.jackhuang.hmcl.util.versioning.VersionRange.*;
public enum JavaVersionConstraint { public enum JavaVersionConstraint {
// Minecraft>=1.13 requires Java 8 // 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 // 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 // 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. // 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 @Override
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version, protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) { @Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
return version != null && analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE); 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 @Override
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version, protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) { @Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE); 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 @Override
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version, protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) { @Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE); 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 @Override
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version, protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) { @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) // 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 @Override
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version, protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) { @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) // 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. // 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 @Override
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version, protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) { @Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
@@ -92,19 +93,19 @@ public enum JavaVersionConstraint {
} }
@Override @Override
public Range<VersionNumber> getJavaVersionRange(Version version) { public VersionRange getJavaVersionRange(Version version) {
String javaVersion; String javaVersion;
if (Objects.requireNonNull(version.getJavaVersion()).getMajorVersion() >= 9) { if (Objects.requireNonNull(version.getJavaVersion()).getMajorVersion() >= 9) {
javaVersion = "" + version.getJavaVersion().getMajorVersion(); javaVersion = "" + version.getJavaVersion().getMajorVersion();
} else { } else {
javaVersion = "1." + version.getJavaVersion().getMajorVersion(); 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. // 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. // 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 @Override
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version, protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) { @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. // 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 @Override
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version, protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) { @Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
@@ -138,7 +139,7 @@ public enum JavaVersionConstraint {
} }
}, },
// Minecraft 1.16+Forge with crash because JDK-8273826 // 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 @Override
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version, protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) { @Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
@@ -158,7 +159,7 @@ public enum JavaVersionConstraint {
case "1.16.5": case "1.16.5":
return forgePatchVersion.compareTo(VersionNumber.asVersion("36.2.23")) <= 0; return forgePatchVersion.compareTo(VersionNumber.asVersion("36.2.23")) <= 0;
case "1.17.1": 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: default:
return false; return false;
} }
@@ -184,10 +185,10 @@ public enum JavaVersionConstraint {
};; };;
private final int type; private final int type;
private final Range<VersionNumber> gameVersionRange; private final VersionRange gameVersionRange;
private final Range<VersionNumber> javaVersionRange; private final VersionRange javaVersionRange;
JavaVersionConstraint(int type, Range<VersionNumber> gameVersionRange, Range<VersionNumber> javaVersionRange) { JavaVersionConstraint(int type, VersionRange gameVersionRange, VersionRange javaVersionRange) {
this.type = type; this.type = type;
this.gameVersionRange = gameVersionRange; this.gameVersionRange = gameVersionRange;
this.javaVersionRange = javaVersionRange; this.javaVersionRange = javaVersionRange;
@@ -197,11 +198,11 @@ public enum JavaVersionConstraint {
return type; return type;
} }
public Range<VersionNumber> getGameVersionRange() { public VersionRange getGameVersionRange() {
return gameVersionRange; return gameVersionRange;
} }
public Range<VersionNumber> getJavaVersionRange(Version version) { public VersionRange getJavaVersionRange(Version version) {
return javaVersionRange; return javaVersionRange;
} }
@@ -224,12 +225,12 @@ public enum JavaVersionConstraint {
public static final List<JavaVersionConstraint> ALL = Lang.immutableListOf(values()); public static final List<JavaVersionConstraint> ALL = Lang.immutableListOf(values());
public static VersionRanges findSuitableJavaVersionRange(VersionNumber gameVersion, Version version) { public static VersionRanges findSuitableJavaVersionRange(VersionNumber gameVersion, Version version) {
Range<VersionNumber> mandatoryJavaRange = versionRange(MIN, MAX); VersionRange mandatoryJavaRange = VersionRange.all();
Range<VersionNumber> suggestedJavaRange = versionRange(MIN, MAX); VersionRange suggestedJavaRange = VersionRange.all();
LibraryAnalyzer analyzer = version != null ? LibraryAnalyzer.analyze(version) : null; LibraryAnalyzer analyzer = version != null ? LibraryAnalyzer.analyze(version) : null;
for (JavaVersionConstraint java : ALL) { for (JavaVersionConstraint java : ALL) {
if (java.appliesToVersion(gameVersion, version, null, analyzer)) { if (java.appliesToVersion(gameVersion, version, null, analyzer)) {
Range<VersionNumber> javaVersionRange = java.getJavaVersionRange(version); VersionRange javaVersionRange = java.getJavaVersionRange(version);
if (java.type == RULE_MANDATORY) { if (java.type == RULE_MANDATORY) {
mandatoryJavaRange = mandatoryJavaRange.intersectionWith(javaVersionRange); mandatoryJavaRange = mandatoryJavaRange.intersectionWith(javaVersionRange);
suggestedJavaRange = suggestedJavaRange.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_MANDATORY = 1;
public static final int RULE_SUGGESTED = 2; public static final int RULE_SUGGESTED = 2;
public static final String MIN = "0"; public static final class VersionRanges {
public static final String MAX = "10000"; private final VersionRange mandatory;
private final VersionRange suggested;
private static Range<VersionNumber> versionRange(String fromInclusive, String toExclusive) { public VersionRanges(VersionRange mandatory, VersionRange suggested) {
return Range.between(VersionNumber.asVersion(fromInclusive), VersionNumber.asVersion(toExclusive));
}
static Range<VersionNumber> versionIs(String version) {
return Range.is(VersionNumber.asVersion(version));
}
public static class VersionRanges {
private final Range<VersionNumber> mandatory;
private final Range<VersionNumber> suggested;
public VersionRanges(Range<VersionNumber> mandatory, Range<VersionNumber> suggested) {
this.mandatory = mandatory; this.mandatory = mandatory;
this.suggested = suggested; this.suggested = suggested;
} }
public Range<VersionNumber> getMandatory() { public VersionRange getMandatory() {
return mandatory; return mandatory;
} }
public Range<VersionNumber> getSuggested() { public VersionRange getSuggested() {
return suggested; return suggested;
} }
} }

View File

@@ -0,0 +1,28 @@
package org.jackhuang.hmcl.util;
import java.util.Objects;
public final class Holder<T> {
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 + "]";
}
}

View File

@@ -347,6 +347,14 @@ public class VersionNumber implements Comparable<VersionNumber> {
return canonical; 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 @Override
public boolean equals(Object o) { public boolean equals(Object o) {
return o instanceof VersionNumber && canonical.equals(((VersionNumber) o).canonical); return o instanceof VersionNumber && canonical.equals(((VersionNumber) o).canonical);

View File

@@ -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 + "]";
}
}

View File

@@ -17,8 +17,8 @@
*/ */
package org.jackhuang.hmcl.game; package org.jackhuang.hmcl.game;
import org.apache.commons.lang3.Range;
import org.jackhuang.hmcl.util.versioning.VersionNumber; import org.jackhuang.hmcl.util.versioning.VersionNumber;
import org.jackhuang.hmcl.util.versioning.VersionRange;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
@@ -32,8 +32,6 @@ public class JavaVersionConstraintTest {
null null
); );
assertEquals( assertEquals(VersionRange.atLeast("16"), range.getMandatory());
Range.between(VersionNumber.asVersion("16"), VersionNumber.asVersion(JavaVersionConstraint.MAX)),
range.getMandatory());
} }
} }

View File

@@ -15,9 +15,8 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
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 org.junit.jupiter.api.Test;
import java.util.Arrays; import java.util.Arrays;

View File

@@ -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());
}
}