使用 TypeToken 替代 Type (#3320)

This commit is contained in:
Glavo
2024-10-11 20:50:58 +08:00
committed by GitHub
parent 541f2e2619
commit ef9159666c
34 changed files with 165 additions and 139 deletions

View File

@@ -17,7 +17,6 @@
*/
package org.jackhuang.hmcl.auth.offline;
import com.google.gson.reflect.TypeToken;
import org.glavo.png.javafx.PNGJavaFXUtils;
import org.jackhuang.hmcl.auth.yggdrasil.GameProfile;
import org.jackhuang.hmcl.auth.yggdrasil.TextureModel;
@@ -38,6 +37,7 @@ import java.util.stream.Stream;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.jackhuang.hmcl.util.Lang.mapOf;
import static org.jackhuang.hmcl.util.Pair.pair;
import static org.jackhuang.hmcl.util.gson.JsonUtils.listTypeOf;
public class YggdrasilServer extends HttpServer {
@@ -81,8 +81,7 @@ public class YggdrasilServer extends HttpServer {
}
private Response profiles(Request request) throws IOException {
List<String> names = JsonUtils.fromNonNullJsonFully(request.getSession().getInputStream(), new TypeToken<List<String>>() {
}.getType());
List<String> names = JsonUtils.fromNonNullJsonFully(request.getSession().getInputStream(), listTypeOf(String.class));
return ok(names.stream().distinct()
.map(this::findCharacterByName)
.flatMap(Lang::toStream)

View File

@@ -17,7 +17,6 @@
*/
package org.jackhuang.hmcl.download.fabric;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.util.gson.JsonUtils;
@@ -25,13 +24,13 @@ import org.jackhuang.hmcl.util.io.NetworkUtils;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import static org.jackhuang.hmcl.util.Lang.wrap;
import static org.jackhuang.hmcl.util.gson.JsonUtils.listTypeOf;
public final class FabricVersionList extends VersionList<FabricRemoteVersion> {
private final DownloadProvider downloadProvider;
@@ -69,8 +68,8 @@ public final class FabricVersionList extends VersionList<FabricRemoteVersion> {
private List<String> getGameVersions(String metaUrl) throws IOException {
String json = NetworkUtils.doGet(downloadProvider.injectURLWithCandidates(metaUrl));
return JsonUtils.GSON.<ArrayList<GameVersion>>fromJson(json, new TypeToken<ArrayList<GameVersion>>() {
}.getType()).stream().map(GameVersion::getVersion).collect(Collectors.toList());
return JsonUtils.GSON.fromJson(json, listTypeOf(GameVersion.class))
.stream().map(GameVersion::getVersion).collect(Collectors.toList());
}
private static String getLaunchMetaUrl(String gameVersion, String loaderVersion) {

View File

@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.download.forge;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.Lang;
@@ -40,6 +39,7 @@ import java.util.concurrent.CompletableFuture;
import static org.jackhuang.hmcl.util.Lang.mapOf;
import static org.jackhuang.hmcl.util.Lang.wrap;
import static org.jackhuang.hmcl.util.Pair.pair;
import static org.jackhuang.hmcl.util.gson.JsonUtils.listTypeOf;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
public final class ForgeBMCLVersionList extends VersionList<ForgeRemoteVersion> {
@@ -87,11 +87,9 @@ public final class ForgeBMCLVersionList extends VersionList<ForgeRemoteVersion>
String lookupVersion = toLookupVersion(gameVersion);
return CompletableFuture.completedFuture(null)
.thenApplyAsync(wrap(unused -> HttpRequest.GET(apiRoot + "/forge/minecraft/" + lookupVersion).<List<ForgeVersion>>getJson(new TypeToken<List<ForgeVersion>>() {
}.getType())))
.thenApplyAsync(wrap(unused -> HttpRequest.GET(apiRoot + "/forge/minecraft/" + lookupVersion).getJson(listTypeOf(ForgeVersion.class))))
.thenAcceptAsync(forgeVersions -> {
lock.writeLock().lock();
try {
versions.clear(gameVersion);
if (forgeVersions == null) return;

View File

@@ -19,7 +19,6 @@ package org.jackhuang.hmcl.download.java.mojang;
import com.google.gson.*;
import com.google.gson.annotations.JsonAdapter;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.game.DownloadInfo;
import org.jackhuang.hmcl.util.Immutable;
@@ -27,6 +26,9 @@ import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import static org.jackhuang.hmcl.util.gson.JsonUtils.listTypeOf;
import static org.jackhuang.hmcl.util.gson.JsonUtils.mapTypeOf;
@JsonAdapter(MojangJavaDownloads.Adapter.class)
public class MojangJavaDownloads {
@@ -49,8 +51,7 @@ public class MojangJavaDownloads {
@Override
public MojangJavaDownloads deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return new MojangJavaDownloads(context.deserialize(json, new TypeToken<Map<String, Map<String, List<JavaDownload>>>>() {
}.getType()));
return new MojangJavaDownloads(context.deserialize(json, mapTypeOf(String.class, mapTypeOf(String.class, listTypeOf(JavaDownload.class))).getType()));
}
}

View File

@@ -19,18 +19,17 @@ package org.jackhuang.hmcl.download.neoforge;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.gson.Validation;
import org.jackhuang.hmcl.util.io.HttpRequest;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import static org.jackhuang.hmcl.util.Lang.wrap;
import static org.jackhuang.hmcl.util.gson.JsonUtils.listTypeOf;
public final class NeoForgeBMCLVersionList extends VersionList<NeoForgeRemoteVersion> {
private final String apiRoot;
@@ -68,8 +67,7 @@ public final class NeoForgeBMCLVersionList extends VersionList<NeoForgeRemoteVer
@Override
public CompletableFuture<?> refreshAsync(String gameVersion) {
return CompletableFuture.completedFuture((Void) null)
.thenApplyAsync(wrap(unused -> HttpRequest.GET(apiRoot + "/neoforge/list/" + gameVersion).<List<NeoForgeVersion>>getJson(new TypeToken<List<NeoForgeVersion>>() {
}.getType())))
.thenApplyAsync(wrap(unused -> HttpRequest.GET(apiRoot + "/neoforge/list/" + gameVersion).getJson(listTypeOf(NeoForgeVersion.class))))
.thenAcceptAsync(neoForgeVersions -> {
lock.writeLock().lock();

View File

@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.download.optifine;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.io.HttpRequest;
@@ -26,10 +25,11 @@ import org.jackhuang.hmcl.util.versioning.VersionNumber;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import static org.jackhuang.hmcl.util.gson.JsonUtils.listTypeOf;
/**
* @author huangyuhui
*/
@@ -72,8 +72,7 @@ public final class OptiFineBMCLVersionList extends VersionList<OptiFineRemoteVer
@Override
public CompletableFuture<?> refreshAsync() {
return HttpRequest.GET(apiRoot + "/optifine/versionlist").<List<OptiFineVersion>>getJsonAsync(new TypeToken<List<OptiFineVersion>>() {
}.getType()).thenAcceptAsync(root -> {
return HttpRequest.GET(apiRoot + "/optifine/versionlist").getJsonAsync(listTypeOf(OptiFineVersion.class)).thenAcceptAsync(root -> {
lock.writeLock().lock();
try {

View File

@@ -17,7 +17,6 @@
*/
package org.jackhuang.hmcl.download.quilt;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.util.gson.JsonUtils;
@@ -25,13 +24,13 @@ import org.jackhuang.hmcl.util.io.NetworkUtils;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import static org.jackhuang.hmcl.util.Lang.wrap;
import static org.jackhuang.hmcl.util.gson.JsonUtils.listTypeOf;
public final class QuiltVersionList extends VersionList<QuiltRemoteVersion> {
private final DownloadProvider downloadProvider;
@@ -69,8 +68,8 @@ public final class QuiltVersionList extends VersionList<QuiltRemoteVersion> {
private List<String> getGameVersions(String metaUrl) throws IOException {
String json = NetworkUtils.doGet(downloadProvider.injectURLWithCandidates(metaUrl));
return JsonUtils.GSON.<ArrayList<GameVersion>>fromJson(json, new TypeToken<ArrayList<GameVersion>>() {
}.getType()).stream().map(GameVersion::getVersion).collect(Collectors.toList());
return JsonUtils.GSON.fromJson(json, listTypeOf(GameVersion.class))
.stream().map(GameVersion::getVersion).collect(Collectors.toList());
}
private static String getLaunchMetaUrl(String gameVersion, String loaderVersion) {

View File

@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.game;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.download.MaintainTask;
import org.jackhuang.hmcl.download.game.VersionJsonSaveTask;
import org.jackhuang.hmcl.event.*;
@@ -499,18 +498,16 @@ public class DefaultGameRepository implements GameRepository {
* read modpack configuration for a version.
*
* @param version version installed as modpack
* @param <M> manifest type of ModpackConfiguration
* @return modpack configuration object, or null if this version is not a modpack.
* @throws VersionNotFoundException if version does not exist.
* @throws IOException if an i/o error occurs.
*/
@Nullable
public <M> ModpackConfiguration<M> readModpackConfiguration(String version) throws IOException, VersionNotFoundException {
public ModpackConfiguration<?> readModpackConfiguration(String version) throws IOException, VersionNotFoundException {
if (!hasVersion(version)) throw new VersionNotFoundException(version);
File file = getModpackConfiguration(version);
if (!file.exists()) return null;
return JsonUtils.GSON.fromJson(FileUtils.readText(file), new TypeToken<ModpackConfiguration<M>>() {
}.getType());
return JsonUtils.GSON.fromJson(FileUtils.readText(file), ModpackConfiguration.class);
}
public boolean isModpack(String version) {

View File

@@ -19,13 +19,14 @@ package org.jackhuang.hmcl.game;
import com.google.gson.*;
import com.google.gson.annotations.JsonAdapter;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.util.Immutable;
import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Collectors;
import static org.jackhuang.hmcl.util.gson.JsonUtils.listTypeOf;
/**
*
* @author huangyuhui
@@ -86,8 +87,7 @@ public class RuledArgument implements Argument {
public RuledArgument deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject obj = json.getAsJsonObject();
List<CompatibilityRule> rules = context.deserialize(obj.get("rules"), new TypeToken<List<CompatibilityRule>>() {
}.getType());
List<CompatibilityRule> rules = context.deserialize(obj.get("rules"), listTypeOf(CompatibilityRule.class).getType());
JsonElement valuesElement;
if (obj.has("values")) {
@@ -102,8 +102,7 @@ public class RuledArgument implements Argument {
if (valuesElement.isJsonPrimitive()) {
values = Collections.singletonList(valuesElement.getAsString());
} else {
values = context.deserialize(valuesElement, new TypeToken<List<String>>() {
}.getType());
values = context.deserialize(valuesElement, listTypeOf(String.class).getType());
}
return new RuledArgument(rules, values);

View File

@@ -18,6 +18,7 @@
package org.jackhuang.hmcl.mod;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.gson.Validation;
import org.jetbrains.annotations.Nullable;
@@ -29,6 +30,11 @@ import java.util.List;
@Immutable
public final class ModpackConfiguration<T> implements Validation {
@SuppressWarnings("unchecked")
public static <T> TypeToken<ModpackConfiguration<T>> typeOf(Class<T> clazz) {
return (TypeToken<ModpackConfiguration<T>>) TypeToken.getParameterized(ModpackConfiguration.class, clazz);
}
private final T manifest;
private final String type;
private final String name;

View File

@@ -38,6 +38,7 @@ import java.util.stream.Stream;
import static org.jackhuang.hmcl.util.Lang.mapOf;
import static org.jackhuang.hmcl.util.Pair.pair;
import static org.jackhuang.hmcl.util.gson.JsonUtils.listTypeOf;
public final class CurseForgeRemoteModRepository implements RemoteModRepository {
@@ -113,8 +114,7 @@ public final class CurseForgeRemoteModRepository implements RemoteModRepository
pair("index", Integer.toString(pageOffset * pageSize)),
pair("pageSize", Integer.toString(pageSize)))
.header("X-API-KEY", apiKey)
.getJson(new TypeToken<Response<List<CurseAddon>>>() {
}.getType());
.getJson(Response.typeOf(listTypeOf(CurseAddon.class)));
if (searchFilter.isEmpty()) {
return new SearchResult(response.getData().stream().map(CurseAddon::toMod), calculateTotalPages(response, pageSize));
}
@@ -163,8 +163,7 @@ public final class CurseForgeRemoteModRepository implements RemoteModRepository
Response<FingerprintMatchesResult> response = HttpRequest.POST(PREFIX + "/v1/fingerprints/432")
.json(mapOf(pair("fingerprints", Collections.singletonList(hash))))
.header("X-API-KEY", apiKey)
.getJson(new TypeToken<Response<FingerprintMatchesResult>>() {
}.getType());
.getJson(Response.typeOf(FingerprintMatchesResult.class));
if (response.getData().getExactMatches() == null || response.getData().getExactMatches().isEmpty()) {
return Optional.empty();
@@ -177,8 +176,7 @@ public final class CurseForgeRemoteModRepository implements RemoteModRepository
public RemoteMod getModById(String id) throws IOException {
Response<CurseAddon> response = HttpRequest.GET(PREFIX + "/v1/mods/" + id)
.header("X-API-KEY", apiKey)
.getJson(new TypeToken<Response<CurseAddon>>() {
}.getType());
.getJson(Response.typeOf(CurseAddon.class));
return response.data.toMod();
}
@@ -186,8 +184,7 @@ public final class CurseForgeRemoteModRepository implements RemoteModRepository
public RemoteMod.File getModFile(String modId, String fileId) throws IOException {
Response<CurseAddon.LatestFile> response = HttpRequest.GET(String.format("%s/v1/mods/%s/files/%s", PREFIX, modId, fileId))
.header("X-API-KEY", apiKey)
.getJson(new TypeToken<Response<CurseAddon.LatestFile>>() {
}.getType());
.getJson(Response.typeOf(CurseAddon.LatestFile.class));
return response.getData().toVersion().getFile();
}
@@ -196,16 +193,14 @@ public final class CurseForgeRemoteModRepository implements RemoteModRepository
Response<List<CurseAddon.LatestFile>> response = HttpRequest.GET(PREFIX + "/v1/mods/" + id + "/files",
pair("pageSize", "10000"))
.header("X-API-KEY", apiKey)
.getJson(new TypeToken<Response<List<CurseAddon.LatestFile>>>() {
}.getType());
.getJson(Response.typeOf(listTypeOf(CurseAddon.LatestFile.class)));
return response.getData().stream().map(CurseAddon.LatestFile::toVersion);
}
public List<CurseAddon.Category> getCategoriesImpl() throws IOException {
Response<List<CurseAddon.Category>> categories = HttpRequest.GET(PREFIX + "/v1/categories", pair("gameId", "432"))
.header("X-API-KEY", apiKey)
.getJson(new TypeToken<Response<List<CurseAddon.Category>>>() {
}.getType());
.getJson(Response.typeOf(listTypeOf(CurseAddon.Category.class)));
return reorganizeCategories(categories.getData(), section);
}
@@ -284,6 +279,17 @@ public final class CurseForgeRemoteModRepository implements RemoteModRepository
}
public static class Response<T> {
@SuppressWarnings("unchecked")
public static <T> TypeToken<Response<T>> typeOf(Class<T> responseType) {
return (TypeToken<Response<T>>) TypeToken.getParameterized(Response.class, responseType);
}
@SuppressWarnings("unchecked")
public static <T> TypeToken<Response<T>> typeOf(TypeToken<T> responseType) {
return (TypeToken<Response<T>>) TypeToken.getParameterized(Response.class, responseType.getType());
}
private final T data;
private final Pagination pagination;

View File

@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.mod.curse;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.GameBuilder;
import org.jackhuang.hmcl.game.DefaultGameRepository;
@@ -99,8 +98,7 @@ public final class CurseInstallTask extends Task<Void> {
ModpackConfiguration<CurseManifest> config = null;
try {
if (json.exists()) {
config = JsonUtils.GSON.fromJson(FileUtils.readText(json), new TypeToken<ModpackConfiguration<CurseManifest>>() {
}.getType());
config = JsonUtils.GSON.fromJson(FileUtils.readText(json), ModpackConfiguration.typeOf(CurseManifest.class));
if (!CurseModpackProvider.INSTANCE.getName().equals(config.getType()))
throw new IllegalArgumentException("Version " + name + " is not a Curse modpack. Cannot update this version.");

View File

@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.mod.mcbbs;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.game.DefaultGameRepository;
import org.jackhuang.hmcl.mod.ModManager;
@@ -88,8 +87,7 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask<Void> {
if (configuration == null) {
// Load configuration from disk
try {
configuration = JsonUtils.fromNonNullJson(FileUtils.readText(configurationFile), new TypeToken<ModpackConfiguration<McbbsModpackManifest>>() {
}.getType());
configuration = JsonUtils.fromNonNullJson(FileUtils.readText(configurationFile), ModpackConfiguration.typeOf(McbbsModpackManifest.class));
} catch (IOException | JsonParseException e) {
throw new IOException("Malformed modpack configuration");
}

View File

@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.mod.mcbbs;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.GameBuilder;
import org.jackhuang.hmcl.game.DefaultGameRepository;
@@ -80,8 +79,7 @@ public class McbbsModpackLocalInstallTask extends Task<Void> {
ModpackConfiguration<McbbsModpackManifest> config = null;
try {
if (json.exists()) {
config = JsonUtils.GSON.fromJson(FileUtils.readText(json), new TypeToken<ModpackConfiguration<McbbsModpackManifest>>() {
}.getType());
config = JsonUtils.GSON.fromJson(FileUtils.readText(json), ModpackConfiguration.typeOf(McbbsModpackManifest.class));
if (!McbbsModpackProvider.INSTANCE.getName().equals(config.getType()))
throw new IllegalArgumentException("Version " + name + " is not a Mcbbs modpack. Cannot update this version.");

View File

@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.mod.mcbbs;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
@@ -56,8 +55,7 @@ public final class McbbsModpackProvider implements ModpackProvider {
@Override
public void injectLaunchOptions(String modpackConfigurationJson, LaunchOptions.Builder builder) {
ModpackConfiguration<McbbsModpackManifest> config = JsonUtils.GSON.fromJson(modpackConfigurationJson, new TypeToken<ModpackConfiguration<McbbsModpackManifest>>() {
}.getType());
ModpackConfiguration<McbbsModpackManifest> config = JsonUtils.GSON.fromJson(modpackConfigurationJson, ModpackConfiguration.typeOf(McbbsModpackManifest.class));
if (!getName().equals(config.getType())) {
throw new IllegalArgumentException("Incorrect manifest type, actual=" + config.getType() + ", expected=" + getName());

View File

@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.mod.mcbbs;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.GameBuilder;
import org.jackhuang.hmcl.game.DefaultGameRepository;
@@ -66,8 +65,7 @@ public class McbbsModpackRemoteInstallTask extends Task<Void> {
ModpackConfiguration<McbbsModpackManifest> config = null;
try {
if (json.exists()) {
config = JsonUtils.GSON.fromJson(FileUtils.readText(json), new TypeToken<ModpackConfiguration<McbbsModpackManifest>>() {
}.getType());
config = JsonUtils.GSON.fromJson(FileUtils.readText(json), ModpackConfiguration.typeOf(McbbsModpackManifest.class));
if (!MODPACK_TYPE.equals(config.getType()))
throw new IllegalArgumentException("Version " + name + " is not a Mcbbs modpack. Cannot update this version.");

View File

@@ -19,7 +19,6 @@ package org.jackhuang.hmcl.mod.modinfo;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.mod.LocalModFile;
import org.jackhuang.hmcl.mod.ModLoaderType;
import org.jackhuang.hmcl.mod.ModManager;
@@ -34,6 +33,8 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import static org.jackhuang.hmcl.util.gson.JsonUtils.listTypeOf;
/**
*
* @author huangyuhui
@@ -125,9 +126,7 @@ public final class ForgeOldModMetadata {
Path mcmod = fs.getPath("mcmod.info");
if (Files.notExists(mcmod))
throw new IOException("File " + modFile + " is not a Forge mod.");
List<ForgeOldModMetadata> modList = JsonUtils.GSON.fromJson(FileUtils.readText(mcmod),
new TypeToken<List<ForgeOldModMetadata>>() {
}.getType());
List<ForgeOldModMetadata> modList = JsonUtils.GSON.fromJson(FileUtils.readText(mcmod), listTypeOf(ForgeOldModMetadata.class));
if (modList == null || modList.isEmpty())
throw new IOException("Mod " + modFile + " `mcmod.info` is malformed..");
ForgeOldModMetadata metadata = modList.get(0);

View File

@@ -18,12 +18,10 @@
package org.jackhuang.hmcl.mod.modrinth;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.GameBuilder;
import org.jackhuang.hmcl.game.DefaultGameRepository;
import org.jackhuang.hmcl.mod.*;
import org.jackhuang.hmcl.mod.curse.CurseManifest;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
@@ -95,8 +93,7 @@ public class ModrinthInstallTask extends Task<Void> {
ModpackConfiguration<ModrinthManifest> config = null;
try {
if (json.exists()) {
config = JsonUtils.GSON.fromJson(FileUtils.readText(json), new TypeToken<ModpackConfiguration<CurseManifest>>() {
}.getType());
config = JsonUtils.GSON.fromJson(FileUtils.readText(json), ModpackConfiguration.typeOf(ModrinthManifest.class));
if (!ModrinthModpackProvider.INSTANCE.getName().equals(config.getType()))
throw new IllegalArgumentException("Version " + name + " is not a Modrinth modpack. Cannot update this version.");

View File

@@ -39,6 +39,7 @@ import java.util.stream.Stream;
import static org.jackhuang.hmcl.util.Lang.mapOf;
import static org.jackhuang.hmcl.util.Pair.pair;
import static org.jackhuang.hmcl.util.gson.JsonUtils.listTypeOf;
public final class ModrinthRemoteModRepository implements RemoteModRepository {
public static final ModrinthRemoteModRepository MODS = new ModrinthRemoteModRepository("mod");
@@ -93,8 +94,7 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
pair("index", convertSortType(sort))
);
Response<ProjectSearchResult> response = HttpRequest.GET(NetworkUtils.withQuery(PREFIX + "/v2/search", query))
.getJson(new TypeToken<Response<ProjectSearchResult>>() {
}.getType());
.getJson(Response.typeOf(ProjectSearchResult.class));
return new SearchResult(response.getHits().stream().map(ProjectSearchResult::toMod), (int) Math.ceil((double) response.totalHits / pageSize));
}
@@ -132,13 +132,12 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
public Stream<RemoteMod.Version> getRemoteVersionsById(String id) throws IOException {
id = StringUtils.removePrefix(id, "local-");
List<ProjectVersion> versions = HttpRequest.GET(PREFIX + "/v2/project/" + id + "/version")
.getJson(new TypeToken<List<ProjectVersion>>() {
}.getType());
.getJson(listTypeOf(ProjectVersion.class));
return versions.stream().map(ProjectVersion::toVersion).flatMap(Lang::toStream);
}
public List<Category> getCategoriesImpl() throws IOException {
List<Category> categories = HttpRequest.GET(PREFIX + "/v2/tag/category").getJson(new TypeToken<List<Category>>() {}.getType());
List<Category> categories = HttpRequest.GET(PREFIX + "/v2/tag/category").getJson(listTypeOf(Category.class));
return categories.stream().filter(category -> category.getProjectType().equals(projectType)).collect(Collectors.toList());
}
@@ -696,6 +695,12 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
}
public static class Response<T> {
@SuppressWarnings("unchecked")
public static <T> TypeToken<Response<T>> typeOf(Class<T> responseType) {
return (TypeToken<Response<T>>) TypeToken.getParameterized(Response.class, responseType);
}
private final int offset;
private final int limit;

View File

@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.mod.multimc;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.GameBuilder;
import org.jackhuang.hmcl.game.Arguments;
@@ -128,8 +127,7 @@ public final class MultiMCModpackInstallTask extends Task<Void> {
ModpackConfiguration<MultiMCInstanceConfiguration> config = null;
try {
if (json.exists()) {
config = JsonUtils.GSON.fromJson(FileUtils.readText(json), new TypeToken<ModpackConfiguration<MultiMCInstanceConfiguration>>() {
}.getType());
config = JsonUtils.GSON.fromJson(FileUtils.readText(json), ModpackConfiguration.typeOf(MultiMCInstanceConfiguration.class));
if (!MultiMCModpackProvider.INSTANCE.getName().equals(config.getType()))
throw new IllegalArgumentException("Version " + name + " is not a MultiMC modpack. Cannot update this version.");

View File

@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.mod.server;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.GameBuilder;
import org.jackhuang.hmcl.game.DefaultGameRepository;
@@ -66,8 +65,7 @@ public class ServerModpackCompletionTask extends Task<Void> {
try {
File manifestFile = repository.getModpackConfiguration(version);
if (manifestFile.exists()) {
this.manifest = JsonUtils.GSON.fromJson(FileUtils.readText(manifestFile), new TypeToken<ModpackConfiguration<ServerModpackManifest>>() {
}.getType());
this.manifest = JsonUtils.GSON.fromJson(FileUtils.readText(manifestFile), ModpackConfiguration.typeOf(ServerModpackManifest.class));
}
} catch (Exception e) {
LOG.warning("Unable to read Server modpack manifest.json", e);

View File

@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.mod.server;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.GameBuilder;
import org.jackhuang.hmcl.game.DefaultGameRepository;
@@ -72,8 +71,7 @@ public class ServerModpackLocalInstallTask extends Task<Void> {
ModpackConfiguration<ServerModpackManifest> config = null;
try {
if (json.exists()) {
config = JsonUtils.GSON.fromJson(FileUtils.readText(json), new TypeToken<ModpackConfiguration<ServerModpackManifest>>() {
}.getType());
config = JsonUtils.GSON.fromJson(FileUtils.readText(json), ModpackConfiguration.typeOf(ServerModpackManifest.class));
if (!ServerModpackProvider.INSTANCE.getName().equals(config.getType()))
throw new IllegalArgumentException("Version " + name + " is not a Server modpack. Cannot update this version.");

View File

@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.mod.server;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.GameBuilder;
import org.jackhuang.hmcl.game.DefaultGameRepository;
@@ -63,11 +62,10 @@ public class ServerModpackRemoteInstallTask extends Task<Void> {
repository.removeVersionFromDisk(name);
});
ModpackConfiguration<ServerModpackManifest> config = null;
ModpackConfiguration<ServerModpackManifest> config;
try {
if (json.exists()) {
config = JsonUtils.GSON.fromJson(FileUtils.readText(json), new TypeToken<ModpackConfiguration<ServerModpackManifest>>() {
}.getType());
config = JsonUtils.GSON.fromJson(FileUtils.readText(json), ModpackConfiguration.typeOf(ServerModpackManifest.class));
if (!MODPACK_TYPE.equals(config.getType()))
throw new IllegalArgumentException("Version " + name + " is not a Server modpack. Cannot update this version.");

View File

@@ -19,9 +19,7 @@ package org.jackhuang.hmcl.util;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.io.IOUtils;
@@ -46,6 +44,7 @@ import java.util.function.BiFunction;
import java.util.stream.Stream;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.jackhuang.hmcl.util.gson.JsonUtils.*;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
public class CacheRepository {
@@ -68,7 +67,7 @@ public class CacheRepository {
}
if (Files.isRegularFile(indexFile)) {
ETagIndex raw = JsonUtils.GSON.fromJson(FileUtils.readText(indexFile), ETagIndex.class);
ETagIndex raw = GSON.fromJson(FileUtils.readText(indexFile), ETagIndex.class);
if (raw == null)
index = new HashMap<>();
else
@@ -289,10 +288,10 @@ public class CacheRepository {
try (FileChannel channel = FileChannel.open(indexFile, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
FileLock lock = channel.lock();
try {
ETagIndex indexOnDisk = JsonUtils.fromMaybeMalformedJson(new String(IOUtils.readFullyWithoutClosing(Channels.newInputStream(channel)), UTF_8), ETagIndex.class);
ETagIndex indexOnDisk = fromMaybeMalformedJson(new String(IOUtils.readFullyWithoutClosing(Channels.newInputStream(channel)), UTF_8), ETagIndex.class);
Map<String, ETagItem> newIndex = joinETagIndexes(indexOnDisk == null ? null : indexOnDisk.eTag, index.values());
channel.truncate(0);
ByteBuffer writeTo = ByteBuffer.wrap(JsonUtils.GSON.toJson(new ETagIndex(newIndex.values())).getBytes(UTF_8));
ByteBuffer writeTo = ByteBuffer.wrap(GSON.toJson(new ETagIndex(newIndex.values())).getBytes(UTF_8));
while (writeTo.hasRemaining()) {
if (channel.write(writeTo) == 0) {
throw new IOException("No value is written");
@@ -412,8 +411,7 @@ public class CacheRepository {
try {
indexFile = cacheDirectory.resolve(name + ".json");
if (Files.isRegularFile(indexFile)) {
joinEntries(JsonUtils.fromNonNullJson(FileUtils.readText(indexFile), new TypeToken<Map<String, Object>>() {
}.getType()));
joinEntries(fromNonNullJson(FileUtils.readText(indexFile), mapTypeOf(String.class, Object.class)));
}
} catch (IOException | JsonParseException e) {
LOG.warning("Unable to read storage {" + name + "} file");
@@ -426,12 +424,11 @@ public class CacheRepository {
try (FileChannel channel = FileChannel.open(indexFile, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
FileLock lock = channel.lock();
try {
Map<String, Object> indexOnDisk = JsonUtils.fromMaybeMalformedJson(new String(IOUtils.readFullyWithoutClosing(Channels.newInputStream(channel)), UTF_8), new TypeToken<Map<String, Object>>() {
}.getType());
Map<String, Object> indexOnDisk = fromMaybeMalformedJson(new String(IOUtils.readFullyWithoutClosing(Channels.newInputStream(channel)), UTF_8), mapTypeOf(String.class, Object.class));
if (indexOnDisk == null) indexOnDisk = new HashMap<>();
indexOnDisk.putAll(storage);
channel.truncate(0);
channel.write(ByteBuffer.wrap(JsonUtils.GSON.toJson(storage).getBytes(UTF_8)));
channel.write(ByteBuffer.wrap(GSON.toJson(storage).getBytes(UTF_8)));
this.storage = indexOnDisk;
} finally {
lock.release();

View File

@@ -27,14 +27,16 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* @author yushijinhun
*/
@SuppressWarnings("unchecked")
public final class JsonUtils {
public static final Gson GSON = defaultGsonBuilder().create();
@@ -48,13 +50,29 @@ public final class JsonUtils {
private JsonUtils() {
}
public static <T> TypeToken<List<T>> listTypeOf(Class<T> elementType) {
return (TypeToken<List<T>>) TypeToken.getParameterized(List.class, elementType);
}
public static <T> TypeToken<List<T>> listTypeOf(TypeToken<T> elementType) {
return (TypeToken<List<T>>) TypeToken.getParameterized(List.class, elementType.getType());
}
public static <K, V> TypeToken<Map<K, V>> mapTypeOf(Class<K> keyType, Class<V> valueType) {
return (TypeToken<Map<K, V>>) TypeToken.getParameterized(Map.class, keyType, valueType);
}
public static <K, V> TypeToken<Map<K, V>> mapTypeOf(Class<K> keyType, TypeToken<V> valueType) {
return (TypeToken<Map<K, V>>) TypeToken.getParameterized(Map.class, keyType, valueType.getType());
}
public static <T> T fromJsonFully(InputStream json, Class<T> classOfT) throws IOException, JsonParseException {
try (InputStreamReader reader = new InputStreamReader(json, StandardCharsets.UTF_8)) {
return GSON.fromJson(reader, classOfT);
}
}
public static <T> T fromJsonFully(InputStream json, Type type) throws IOException, JsonParseException {
public static <T> T fromJsonFully(InputStream json, TypeToken<T> type) throws IOException, JsonParseException {
try (InputStreamReader reader = new InputStreamReader(json, StandardCharsets.UTF_8)) {
return GSON.fromJson(reader, type);
}
@@ -67,13 +85,6 @@ public final class JsonUtils {
return parsed;
}
public static <T> T fromNonNullJson(String json, Type type) throws JsonParseException {
T parsed = GSON.fromJson(json, type);
if (parsed == null)
throw new JsonParseException("Json object cannot be null.");
return parsed;
}
public static <T> T fromNonNullJson(String json, TypeToken<T> type) throws JsonParseException {
T parsed = GSON.fromJson(json, type);
if (parsed == null)
@@ -90,7 +101,7 @@ public final class JsonUtils {
}
}
public static <T> T fromNonNullJsonFully(InputStream json, Type type) throws IOException, JsonParseException {
public static <T> T fromNonNullJsonFully(InputStream json, TypeToken<T> type) throws IOException, JsonParseException {
try (InputStreamReader reader = new InputStreamReader(json, StandardCharsets.UTF_8)) {
T parsed = GSON.fromJson(reader, type);
if (parsed == null)
@@ -107,7 +118,7 @@ public final class JsonUtils {
}
}
public static <T> T fromMaybeMalformedJson(String json, Type type) throws JsonParseException {
public static <T> T fromMaybeMalformedJson(String json, TypeToken<T> type) throws JsonParseException {
try {
return GSON.fromJson(json, type);
} catch (JsonSyntaxException e) {

View File

@@ -18,6 +18,7 @@
package org.jackhuang.hmcl.util.io;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.util.Pair;
import org.jackhuang.hmcl.util.function.ExceptionalBiConsumer;
@@ -26,7 +27,6 @@ import org.jackhuang.hmcl.util.gson.JsonUtils;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
@@ -101,7 +101,7 @@ public abstract class HttpRequest {
return JsonUtils.fromNonNullJson(getString(), typeOfT);
}
public <T> T getJson(Type type) throws IOException, JsonParseException {
public <T> T getJson(TypeToken<T> type) throws IOException, JsonParseException {
return JsonUtils.fromNonNullJson(getString(), type);
}
@@ -109,7 +109,7 @@ public abstract class HttpRequest {
return getStringAsync().thenApplyAsync(jsonString -> JsonUtils.fromNonNullJson(jsonString, typeOfT));
}
public <T> CompletableFuture<T> getJsonAsync(Type type) {
public <T> CompletableFuture<T> getJsonAsync(TypeToken<T> type) {
return getStringAsync().thenApplyAsync(jsonString -> JsonUtils.fromNonNullJson(jsonString, type));
}

View File

@@ -0,0 +1,42 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2024 huangyuhui <huanghongxun2008@126.com> and contributors
*
* 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 <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.util.gson;
import com.google.gson.reflect.TypeToken;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import static org.jackhuang.hmcl.util.gson.JsonUtils.listTypeOf;
import static org.jackhuang.hmcl.util.gson.JsonUtils.mapTypeOf;
import static org.junit.jupiter.api.Assertions.*;
/**
* @author Glavo
*/
public class JsonUtilsTest {
@Test
public void testGetTypeToken() {
assertEquals(new TypeToken<List<Object>>(){}, listTypeOf(Object.class));
assertEquals(new TypeToken<List<String>>(){}, listTypeOf(String.class));
assertEquals(new TypeToken<List<Map<String, Integer>>>(){}, listTypeOf(mapTypeOf(String.class, Integer.class)));
assertEquals(new TypeToken<List<Map<String, List<Integer>>>>(){}, listTypeOf(mapTypeOf(String.class, listTypeOf(Integer.class))));
}
}