mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-02-05 16:44:47 +08:00
Fix #467
This commit is contained in:
parent
08be1896ee
commit
749ecfe37f
@ -296,7 +296,7 @@ public final class LauncherHelper {
|
||||
}
|
||||
|
||||
// Minecraft 1.13 may crash when generating world on Java 8 earlier than 1.8.0_51
|
||||
VersionNumber JAVA_8 = VersionNumber.asVersion("1.8.0.51");
|
||||
VersionNumber JAVA_8 = VersionNumber.asVersion("1.8.0_51");
|
||||
if (!flag && gameVersion.compareTo(VersionNumber.asVersion("1.13")) >= 0 && java.getParsedVersion() == JavaVersion.JAVA_8 && java.getVersionNumber().compareTo(JAVA_8) < 0) {
|
||||
Optional<JavaVersion> java8 = JavaVersion.getJavas().stream()
|
||||
.filter(javaVersion -> javaVersion.getVersionNumber().compareTo(JAVA_8) >= 0)
|
||||
|
@ -34,6 +34,7 @@ import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
import javafx.util.Duration;
|
||||
import org.jackhuang.hmcl.game.HMCLGameRepository;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.setting.Profiles;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
@ -49,6 +50,7 @@ import org.jackhuang.hmcl.upgrade.UpdateHandler;
|
||||
import org.jackhuang.hmcl.util.javafx.MultiStepBinding;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -130,7 +132,7 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
HMCLGameRepository repository = profile.getRepository();
|
||||
List<Node> children = repository.getVersions().parallelStream()
|
||||
.filter(version -> !version.isHidden())
|
||||
.sorted((a, b) -> VersionNumber.COMPARATOR.compare(VersionNumber.asVersion(a.getId()), VersionNumber.asVersion(b.getId())))
|
||||
.sorted(Comparator.comparing(Version::getReleaseTime).thenComparing(a -> VersionNumber.asVersion(a.getId())))
|
||||
.map(version -> {
|
||||
StackPane pane = new StackPane();
|
||||
GameItem item = new GameItem(profile, version.getId());
|
||||
|
@ -25,9 +25,9 @@ import javafx.scene.control.Control;
|
||||
import javafx.scene.control.Skin;
|
||||
import javafx.scene.control.ToggleGroup;
|
||||
import org.jackhuang.hmcl.event.EventBus;
|
||||
import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
|
||||
import org.jackhuang.hmcl.event.RefreshingVersionsEvent;
|
||||
import org.jackhuang.hmcl.game.HMCLGameRepository;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.setting.Profiles;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
@ -37,6 +37,7 @@ import org.jackhuang.hmcl.ui.download.DownloadWizardProvider;
|
||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -65,7 +66,7 @@ public class GameList extends Control implements DecoratorPage {
|
||||
toggleGroup.getProperties().put("ReferenceHolder", listenerHolder);
|
||||
List<GameListItem> children = repository.getVersions().parallelStream()
|
||||
.filter(version -> !version.isHidden())
|
||||
.sorted((a, b) -> VersionNumber.COMPARATOR.compare(VersionNumber.asVersion(a.getId()), VersionNumber.asVersion(b.getId())))
|
||||
.sorted(Comparator.comparing(Version::getReleaseTime).thenComparing(a -> VersionNumber.asVersion(a.getId())))
|
||||
.map(version -> new GameListItem(toggleGroup, profile, version.getId()))
|
||||
.collect(Collectors.toList());
|
||||
JFXUtilities.runInFX(() -> {
|
||||
|
@ -11,7 +11,6 @@ import javafx.stage.FileChooser;
|
||||
import org.jackhuang.hmcl.game.World;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.wizard.SinglePageWizardProvider;
|
||||
import org.jackhuang.hmcl.util.versioning.IntVersionNumber;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
|
||||
import java.io.File;
|
||||
@ -67,7 +66,7 @@ public class WorldListItem extends Control {
|
||||
|
||||
public void manageDatapacks() {
|
||||
if (world.getGameVersion() == null || // old game will not write game version to level.dat
|
||||
(IntVersionNumber.isIntVersionNumber(world.getGameVersion()) // we don't parse snapshot version
|
||||
(VersionNumber.isIntVersionNumber(world.getGameVersion()) // we don't parse snapshot version
|
||||
&& VersionNumber.asVersion(world.getGameVersion()).compareTo(VersionNumber.asVersion("1.13")) < 0)) {
|
||||
Controllers.dialog(i18n("world.datapack.1_13"));
|
||||
return;
|
||||
|
@ -66,9 +66,7 @@ public final class ForgeVersionList extends VersionList<ForgeRemoteVersion> {
|
||||
versions.clear();
|
||||
|
||||
for (Map.Entry<String, int[]> entry : root.getGameVersions().entrySet()) {
|
||||
Optional<String> gameVersion = VersionNumber.parseVersion(entry.getKey());
|
||||
if (!gameVersion.isPresent())
|
||||
continue;
|
||||
String gameVersion = VersionNumber.normalize(entry.getKey());
|
||||
for (int v : entry.getValue()) {
|
||||
ForgeVersion version = root.getNumber().get(v);
|
||||
if (version == null)
|
||||
@ -84,7 +82,7 @@ public final class ForgeVersionList extends VersionList<ForgeRemoteVersion> {
|
||||
|
||||
if (jar == null)
|
||||
continue;
|
||||
versions.put(gameVersion.get(), new ForgeRemoteVersion(
|
||||
versions.put(gameVersion, new ForgeRemoteVersion(
|
||||
version.getGameVersion(), version.getVersion(), jar
|
||||
));
|
||||
}
|
||||
|
@ -71,11 +71,10 @@ public final class LiteLoaderBMCLVersionList extends VersionList<LiteLoaderRemot
|
||||
for (Map.Entry<String, LiteLoaderGameVersions> entry : root.getVersions().entrySet()) {
|
||||
String gameVersion = entry.getKey();
|
||||
LiteLoaderGameVersions liteLoader = entry.getValue();
|
||||
Optional<String> gg = VersionNumber.parseVersion(gameVersion);
|
||||
if (!gg.isPresent())
|
||||
continue;
|
||||
doBranch(gg.get(), gameVersion, liteLoader.getRepoitory(), liteLoader.getArtifacts(), false);
|
||||
doBranch(gg.get(), gameVersion, liteLoader.getRepoitory(), liteLoader.getSnapshots(), true);
|
||||
|
||||
String gg = VersionNumber.normalize(gameVersion);
|
||||
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getArtifacts(), false);
|
||||
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getSnapshots(), true);
|
||||
}
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
|
@ -71,11 +71,10 @@ public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVer
|
||||
for (Map.Entry<String, LiteLoaderGameVersions> entry : root.getVersions().entrySet()) {
|
||||
String gameVersion = entry.getKey();
|
||||
LiteLoaderGameVersions liteLoader = entry.getValue();
|
||||
Optional<String> gg = VersionNumber.parseVersion(gameVersion);
|
||||
if (!gg.isPresent())
|
||||
continue;
|
||||
doBranch(gg.get(), gameVersion, liteLoader.getRepoitory(), liteLoader.getArtifacts(), false);
|
||||
doBranch(gg.get(), gameVersion, liteLoader.getRepoitory(), liteLoader.getSnapshots(), true);
|
||||
|
||||
String gg = VersionNumber.normalize(gameVersion);
|
||||
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getArtifacts(), false);
|
||||
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getSnapshots(), true);
|
||||
}
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
|
@ -70,8 +70,9 @@ public final class OptiFineBMCLVersionList extends VersionList<OptiFineRemoteVer
|
||||
|
||||
if (StringUtils.isBlank(element.getGameVersion()))
|
||||
continue;
|
||||
VersionNumber.parseVersion(element.getGameVersion())
|
||||
.ifPresent(gameVersion -> versions.put(gameVersion, new OptiFineRemoteVersion(gameVersion, version, () -> mirror, isPre)));
|
||||
|
||||
String gameVersion = VersionNumber.normalize(element.getGameVersion());
|
||||
versions.put(gameVersion, new OptiFineRemoteVersion(gameVersion, version, () -> mirror, isPre));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -41,10 +41,12 @@ public final class ModManager {
|
||||
File modsDirectory = new File(repository.getRunDirectory(id), "mods");
|
||||
Consumer<File> puter = modFile -> Lang.ignoringException(() -> modCache.put(id, ModInfo.fromFile(modFile)));
|
||||
Optional.ofNullable(modsDirectory.listFiles()).map(Arrays::stream).ifPresent(files -> files.forEach(modFile -> {
|
||||
if (modFile.isDirectory() && VersionNumber.parseVersion(modFile.getName()).isPresent())
|
||||
if (modFile.isDirectory() && VersionNumber.isIntVersionNumber(modFile.getName())) {
|
||||
// If the folder name is game version, forge will search mod in this subdirectory
|
||||
Optional.ofNullable(modFile.listFiles()).map(Arrays::stream).ifPresent(x -> x.forEach(puter));
|
||||
else
|
||||
} else {
|
||||
puter.accept(modFile);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -70,7 +70,7 @@ public final class JavaVersion {
|
||||
}
|
||||
|
||||
public VersionNumber getVersionNumber() {
|
||||
return VersionNumber.asVersion(longVersion.replace('_', '.'));
|
||||
return VersionNumber.asVersion(longVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.versioning;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* If a version string contains '-', a {@link ComposedVersionNumber}
|
||||
* will be generated.
|
||||
*
|
||||
* Formats like 1.7.10-OptiFine, 1.12.2-Forge
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class ComposedVersionNumber extends VersionNumber {
|
||||
List<VersionNumber> composed;
|
||||
|
||||
public static boolean isComposedVersionNumber(String version) {
|
||||
return version.contains("-");
|
||||
}
|
||||
|
||||
ComposedVersionNumber(String version) {
|
||||
composed = Arrays.stream(version.split("-"))
|
||||
.map(VersionNumber::asVersion)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return composed.stream().map(VersionNumber::toString).collect(Collectors.joining("-"));
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2018 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.versioning;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
|
||||
/**
|
||||
* If a version string formats x.x.x.x, a {@code IntVersionNumber}
|
||||
* will be generated.
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class IntVersionNumber extends VersionNumber {
|
||||
|
||||
final List<Integer> version;
|
||||
|
||||
public static boolean isIntVersionNumber(String version) {
|
||||
if (version.chars().noneMatch(ch -> ch != '.' && (ch < '0' || ch > '9'))
|
||||
&& !version.contains("..") && StringUtils.isNotBlank(version)) {
|
||||
String[] arr = version.split("\\.");
|
||||
for (String str : arr)
|
||||
if (str.length() > 9)
|
||||
// Numbers which are larger than 1e9 cannot be stored as integer.
|
||||
return false;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
IntVersionNumber(String version) {
|
||||
if (!isIntVersionNumber(version))
|
||||
throw new IllegalArgumentException("The version " + version + " is malformed, only dots and digits are allowed.");
|
||||
|
||||
List<Integer> versions = Arrays.stream(version.split("\\."))
|
||||
.map(Integer::parseInt)
|
||||
.collect(Collectors.toList());
|
||||
while (!versions.isEmpty() && versions.get(versions.size() - 1) == 0)
|
||||
versions.remove(versions.size() - 1);
|
||||
|
||||
this.version = versions;
|
||||
}
|
||||
|
||||
public int get(int index) {
|
||||
return version.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return version.stream().map(Object::toString).collect(Collectors.joining("."));
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2018 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.versioning;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* If a version string contains alphabets, a {@code StringVersionNumber}
|
||||
* will be constructed.
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class StringVersionNumber extends VersionNumber {
|
||||
|
||||
private final String version;
|
||||
|
||||
StringVersionNumber(String version) {
|
||||
Objects.requireNonNull(version);
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return version;
|
||||
}
|
||||
|
||||
}
|
@ -1,108 +1,339 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2018 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.versioning;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* The formatted version number represents a version string.
|
||||
*
|
||||
* @author huangyuhui
|
||||
* Copied from org.apache.maven.artifact.versioning.ComparableVersion
|
||||
* Apache License 2.0
|
||||
*/
|
||||
public abstract class VersionNumber implements Comparable<VersionNumber> {
|
||||
public class VersionNumber implements Comparable<VersionNumber> {
|
||||
|
||||
public static VersionNumber asVersion(String version) {
|
||||
Objects.requireNonNull(version);
|
||||
if (ComposedVersionNumber.isComposedVersionNumber(version))
|
||||
return new ComposedVersionNumber(version);
|
||||
else if (IntVersionNumber.isIntVersionNumber(version))
|
||||
return new IntVersionNumber(version);
|
||||
else
|
||||
return new StringVersionNumber(version);
|
||||
return new VersionNumber(version);
|
||||
}
|
||||
|
||||
public static Optional<String> parseVersion(String str) {
|
||||
if (IntVersionNumber.isIntVersionNumber(str))
|
||||
return Optional.of(new IntVersionNumber(str).toString());
|
||||
else
|
||||
return Optional.empty();
|
||||
public static String normalize(String str) {
|
||||
return new VersionNumber(str).getCanonical();
|
||||
}
|
||||
|
||||
public static boolean isIntVersionNumber(String version) {
|
||||
if (version.chars().noneMatch(ch -> ch != '.' && (ch < '0' || ch > '9'))
|
||||
&& !version.contains("..") && StringUtils.isNotBlank(version)) {
|
||||
String[] arr = version.split("\\.");
|
||||
for (String str : arr)
|
||||
if (str.length() > 9)
|
||||
// Numbers which are larger than 1e9 cannot be stored as integer.
|
||||
return false;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private String value;
|
||||
private String canonical;
|
||||
private ListItem items;
|
||||
|
||||
private interface Item {
|
||||
int INTEGER_ITEM = 0;
|
||||
int STRING_ITEM = 1;
|
||||
int LIST_ITEM = 2;
|
||||
|
||||
int compareTo(Item item);
|
||||
|
||||
int getType();
|
||||
|
||||
boolean isNull();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a numeric item in the version item list.
|
||||
*/
|
||||
private static class IntegerItem
|
||||
implements Item {
|
||||
private final BigInteger value;
|
||||
|
||||
public static final IntegerItem ZERO = new IntegerItem();
|
||||
|
||||
private IntegerItem() {
|
||||
this.value = BigInteger.ZERO;
|
||||
}
|
||||
|
||||
IntegerItem(String str) {
|
||||
this.value = new BigInteger(str);
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return INTEGER_ITEM;
|
||||
}
|
||||
|
||||
public boolean isNull() {
|
||||
return BigInteger.ZERO.equals(value);
|
||||
}
|
||||
|
||||
public int compareTo(Item item) {
|
||||
if (item == null) {
|
||||
return BigInteger.ZERO.equals(value) ? 0 : 1; // 1.0 == 1, 1.1 > 1
|
||||
}
|
||||
|
||||
switch (item.getType()) {
|
||||
case INTEGER_ITEM:
|
||||
return value.compareTo(((IntegerItem) item).value);
|
||||
|
||||
case STRING_ITEM:
|
||||
return 1; // 1.1 > 1-sp
|
||||
|
||||
case LIST_ITEM:
|
||||
return 1; // 1.1 > 1-1
|
||||
|
||||
default:
|
||||
throw new RuntimeException("invalid item: " + item.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a string in the version item list, usually a qualifier.
|
||||
*/
|
||||
private static class StringItem
|
||||
implements Item {
|
||||
private String value;
|
||||
|
||||
StringItem(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return STRING_ITEM;
|
||||
}
|
||||
|
||||
public boolean isNull() {
|
||||
return value.isEmpty();
|
||||
}
|
||||
|
||||
public int compareTo(Item item) {
|
||||
if (item == null) {
|
||||
// 1-string > 1
|
||||
return 1;
|
||||
}
|
||||
switch (item.getType()) {
|
||||
case INTEGER_ITEM:
|
||||
return -1; // 1.any < 1.1 ?
|
||||
|
||||
case STRING_ITEM:
|
||||
return value.compareTo(((StringItem) item).value);
|
||||
|
||||
case LIST_ITEM:
|
||||
return -1; // 1.any < 1-1
|
||||
|
||||
default:
|
||||
throw new RuntimeException("invalid item: " + item.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a version list item. This class is used both for the global item list and for sub-lists (which start
|
||||
* with '-(number)' in the version specification).
|
||||
*/
|
||||
private static class ListItem
|
||||
extends ArrayList<Item>
|
||||
implements Item {
|
||||
Character separator;
|
||||
|
||||
public ListItem() {}
|
||||
|
||||
public ListItem(char separator) {
|
||||
this.separator = separator;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return LIST_ITEM;
|
||||
}
|
||||
|
||||
public boolean isNull() {
|
||||
return (size() == 0);
|
||||
}
|
||||
|
||||
void normalize() {
|
||||
for (int i = size() - 1; i >= 0; i--) {
|
||||
Item lastItem = get(i);
|
||||
|
||||
if (lastItem.isNull()) {
|
||||
// remove null trailing items: 0, "", empty list
|
||||
remove(i);
|
||||
} else if (!(lastItem instanceof ListItem)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int compareTo(Item item) {
|
||||
if (item == null) {
|
||||
if (size() == 0) {
|
||||
return 0; // 1-0 = 1- (normalize) = 1
|
||||
}
|
||||
Item first = get(0);
|
||||
return first.compareTo(null);
|
||||
}
|
||||
switch (item.getType()) {
|
||||
case INTEGER_ITEM:
|
||||
return -1; // 1-1 < 1.0.x
|
||||
|
||||
case STRING_ITEM:
|
||||
return 1; // 1-1 > 1-sp
|
||||
|
||||
case LIST_ITEM:
|
||||
Iterator<Item> left = iterator();
|
||||
Iterator<Item> right = ((ListItem) item).iterator();
|
||||
|
||||
while (left.hasNext() || right.hasNext()) {
|
||||
Item l = left.hasNext() ? left.next() : null;
|
||||
Item r = right.hasNext() ? right.next() : null;
|
||||
|
||||
// if this is shorter, then invert the compare and mul with -1
|
||||
int result = l == null ? (r == null ? 0 : -1 * r.compareTo(l)) : l.compareTo(r);
|
||||
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
default:
|
||||
throw new RuntimeException("invalid item: " + item.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
for (Item item : this) {
|
||||
if (buffer.length() > 0) {
|
||||
if (!(item instanceof ListItem))
|
||||
buffer.append('.');
|
||||
}
|
||||
buffer.append(item);
|
||||
}
|
||||
if (separator != null)
|
||||
return separator + buffer.toString();
|
||||
else
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public VersionNumber(String version) {
|
||||
parseVersion(version);
|
||||
}
|
||||
|
||||
private void parseVersion(String version) {
|
||||
this.value = version;
|
||||
|
||||
items = new ListItem();
|
||||
|
||||
version = version.toLowerCase(Locale.ENGLISH);
|
||||
|
||||
ListItem list = items;
|
||||
|
||||
Stack<Item> stack = new Stack<>();
|
||||
stack.push(list);
|
||||
|
||||
boolean isDigit = false;
|
||||
|
||||
int startIndex = 0;
|
||||
|
||||
for (int i = 0; i < version.length(); i++) {
|
||||
char c = version.charAt(i);
|
||||
|
||||
if (c == '.') {
|
||||
if (i == startIndex) {
|
||||
list.add(IntegerItem.ZERO);
|
||||
} else {
|
||||
list.add(parseItem(version.substring(startIndex, i)));
|
||||
}
|
||||
startIndex = i + 1;
|
||||
} else if ("!\"#$%&'()*+,-/:;<=>?@[\\]^_`{|}~".indexOf(c) != -1) {
|
||||
if (i == startIndex) {
|
||||
list.add(IntegerItem.ZERO);
|
||||
} else {
|
||||
list.add(parseItem(version.substring(startIndex, i)));
|
||||
}
|
||||
startIndex = i + 1;
|
||||
|
||||
list.add(list = new ListItem(c));
|
||||
stack.push(list);
|
||||
} else if (Character.isDigit(c)) {
|
||||
if (!isDigit && i > startIndex) {
|
||||
list.add(parseItem(version.substring(startIndex, i)));
|
||||
startIndex = i;
|
||||
|
||||
list.add(list = new ListItem());
|
||||
stack.push(list);
|
||||
}
|
||||
|
||||
isDigit = true;
|
||||
} else {
|
||||
if (isDigit && i > startIndex) {
|
||||
list.add(parseItem(version.substring(startIndex, i)));
|
||||
startIndex = i;
|
||||
|
||||
list.add(list = new ListItem());
|
||||
stack.push(list);
|
||||
}
|
||||
|
||||
isDigit = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (version.length() > startIndex) {
|
||||
list.add(parseItem(version.substring(startIndex)));
|
||||
}
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
list = (ListItem) stack.pop();
|
||||
list.normalize();
|
||||
}
|
||||
|
||||
canonical = items.toString();
|
||||
}
|
||||
|
||||
private static Item parseItem(String buf) {
|
||||
return buf.chars().allMatch(Character::isDigit) ? new IntegerItem(buf) : new StringItem(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(VersionNumber o) {
|
||||
return COMPARATOR.compare(this, o);
|
||||
return items.compareTo(o.items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object another) {
|
||||
return another instanceof VersionNumber && this.toString().equals(another.toString());
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getCanonical() {
|
||||
return canonical;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof VersionNumber && canonical.equals(((VersionNumber) o).canonical);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return toString().hashCode();
|
||||
return canonical.hashCode();
|
||||
}
|
||||
|
||||
private static <T extends Comparable<T>> int compareTo(List<T> a, List<T> b) {
|
||||
int i;
|
||||
for (i = 0; i < a.size() && i < b.size(); ++i) {
|
||||
int res = a.get(i).compareTo(b.get(i));
|
||||
if (res != 0)
|
||||
return res;
|
||||
}
|
||||
if (i < a.size()) return 1;
|
||||
else if (i < b.size()) return -1;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
public static final Comparator<VersionNumber> COMPARATOR = new Comparator<VersionNumber>() {
|
||||
@Override
|
||||
public int compare(VersionNumber a, VersionNumber b) {
|
||||
if (a == null || b == null)
|
||||
return 0;
|
||||
else {
|
||||
if (a instanceof ComposedVersionNumber) {
|
||||
if (b instanceof ComposedVersionNumber)
|
||||
return compareTo(((ComposedVersionNumber) a).composed, ((ComposedVersionNumber) b).composed);
|
||||
else
|
||||
return compare(((ComposedVersionNumber) a).composed.get(0), b);
|
||||
} else if (a instanceof IntVersionNumber) {
|
||||
if (b instanceof ComposedVersionNumber)
|
||||
return -compare(b, a);
|
||||
else if (b instanceof IntVersionNumber)
|
||||
return compareTo(((IntVersionNumber) a).version, ((IntVersionNumber) b).version);
|
||||
else if (b instanceof StringVersionNumber)
|
||||
return a.toString().compareTo(b.toString());
|
||||
} else if (a instanceof StringVersionNumber) {
|
||||
if (b instanceof ComposedVersionNumber)
|
||||
return -compare(b, a);
|
||||
else if (b instanceof StringVersionNumber)
|
||||
return a.toString().compareTo(b.toString());
|
||||
else if (b instanceof IntVersionNumber)
|
||||
return a.toString().compareTo(b.toString());
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unrecognized VersionNumber " + a + " and " + b);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
@ -0,0 +1,98 @@
|
||||
package org.jackhuang.hmcl.util;
|
||||
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class VersionNumberTest {
|
||||
|
||||
@Test
|
||||
public void testCanonical() {
|
||||
VersionNumber u, v;
|
||||
|
||||
v = VersionNumber.asVersion("3.2.0.0");
|
||||
Assert.assertEquals("3.2", v.getCanonical());
|
||||
|
||||
v = VersionNumber.asVersion("3.2.0.0-5");
|
||||
Assert.assertEquals("3.2-5", v.getCanonical());
|
||||
|
||||
v = VersionNumber.asVersion("3.2.0.0-0");
|
||||
Assert.assertEquals("3.2", v.getCanonical());
|
||||
|
||||
v = VersionNumber.asVersion("3.2--------");
|
||||
Assert.assertEquals("3.2", v.getCanonical());
|
||||
|
||||
v = VersionNumber.asVersion("1.7.2$%%^@&snapshot-3.1.1");
|
||||
Assert.assertEquals("1.7.2$%%^@&snapshot-3.1.1", v.getCanonical());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComparator() {
|
||||
VersionNumber u, v;
|
||||
|
||||
u = VersionNumber.asVersion("1.7.10forge1614_FTBInfinity");
|
||||
v = VersionNumber.asVersion("1.12.2");
|
||||
Assert.assertTrue(u.compareTo(v) < 0);
|
||||
|
||||
u = VersionNumber.asVersion("1.8.0_51");
|
||||
v = VersionNumber.asVersion("1.8.0.51");
|
||||
Assert.assertTrue(u.compareTo(v) < 0);
|
||||
|
||||
u = VersionNumber.asVersion("1.8.0_151");
|
||||
v = VersionNumber.asVersion("1.8.0_77");
|
||||
Assert.assertTrue(u.compareTo(v) > 0);
|
||||
|
||||
u = VersionNumber.asVersion("1.6.0_22");
|
||||
v = VersionNumber.asVersion("1.8.0_11");
|
||||
Assert.assertTrue(u.compareTo(v) < 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSorting() {
|
||||
List<String> input = Arrays.asList(
|
||||
"1.10",
|
||||
"1.10.2",
|
||||
"1.10.2-All the Mods",
|
||||
"1.10.2-AOE",
|
||||
"1.10.2-AOE-1.1.5",
|
||||
"1.10.2-forge2511-Age_of_Progression",
|
||||
"1.10.2-forge2511-AOE-1.1.2",
|
||||
"1.10.2-forge2511-ATM-E",
|
||||
"1.10.2-forge2511-simple_life_2",
|
||||
"1.10.2-forge2511_bxztest",
|
||||
"1.10.2-forge2511_Farming_Valley",
|
||||
"1.10.2-forge2511简单生活BXZ",
|
||||
"1.10.2-FTB_Beyond",
|
||||
"1.10.2-LiteLoader1.10.2",
|
||||
"1.12.2",
|
||||
"1.12.2_Modern_Skyblock-3.4.2",
|
||||
"1.13.1",
|
||||
"1.6.4",
|
||||
"1.6.4-Forge9.11.1.1345",
|
||||
"1.7.10",
|
||||
"1.7.10-1614",
|
||||
"1.7.10-1614-test",
|
||||
"1.7.10-F1614-L",
|
||||
"1.7.10-FL1614_04",
|
||||
"1.7.10-Forge10.13.4.1614-1.7.10",
|
||||
"1.7.10-Forge1614",
|
||||
"1.7.10-Forge1614.1",
|
||||
"1.7.10Agrarian_Skies_2",
|
||||
"1.7.10forge1614test",
|
||||
"1.7.10forge1614_ATlauncher",
|
||||
"1.7.10forge1614_FTBInfinity",
|
||||
"1.7.10Forge1614_FTBInfinity-2.6.0",
|
||||
"1.7.10Forge1614_FTBInfinity-3.0.1",
|
||||
"1.7.10forge1614_FTBInfinity_server",
|
||||
"1.8",
|
||||
"1.8-forge1577",
|
||||
"1.8.9",
|
||||
"1.8.9-forge1902",
|
||||
"1.9");
|
||||
input.sort(Comparator.comparing(VersionNumber::asVersion));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user