mirror of
https://github.com/AlmostReliable/almostunified.git
synced 2024-11-28 10:35:38 -05:00
Create JsonCompare
This commit is contained in:
parent
f1f8a3121d
commit
004b84290a
5 changed files with 330 additions and 10 deletions
|
@ -0,0 +1,65 @@
|
|||
package com.almostreliable.unified.recipe;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Objects;
|
||||
|
||||
public class RawRecipe {
|
||||
private final ResourceLocation id;
|
||||
private final ResourceLocation type;
|
||||
private final JsonObject originalRecipe;
|
||||
private boolean isDuplicate = false;
|
||||
@Nullable private JsonObject transformedRecipe;
|
||||
|
||||
|
||||
public RawRecipe(ResourceLocation id, JsonObject originalRecipe) {
|
||||
this.id = id;
|
||||
this.originalRecipe = originalRecipe;
|
||||
|
||||
try {
|
||||
this.type = ResourceLocation.tryParse(originalRecipe.get("type").getAsString());
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("Could not detect recipe type");
|
||||
}
|
||||
}
|
||||
|
||||
public ResourceLocation getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public ResourceLocation getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public JsonObject getOriginal() {
|
||||
return originalRecipe;
|
||||
}
|
||||
|
||||
public void markAsDuplicate() {
|
||||
isDuplicate = true;
|
||||
}
|
||||
|
||||
public boolean isDuplicate() {
|
||||
return isDuplicate;
|
||||
}
|
||||
|
||||
public void setTransformed(JsonObject transformedRecipe) {
|
||||
Objects.requireNonNull(transformedRecipe);
|
||||
if(isTransformed()) {
|
||||
throw new IllegalStateException("Recipe already transformed");
|
||||
}
|
||||
|
||||
this.transformedRecipe = transformedRecipe;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JsonObject getTransformed() {
|
||||
return transformedRecipe;
|
||||
}
|
||||
|
||||
public boolean isTransformed() {
|
||||
return transformedRecipe != null;
|
||||
}
|
||||
}
|
|
@ -9,7 +9,11 @@ import com.google.gson.JsonPrimitive;
|
|||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class RecipeTransformer {
|
||||
|
||||
|
@ -29,23 +33,59 @@ public class RecipeTransformer {
|
|||
}
|
||||
|
||||
public RecipeTransformationResult transformRecipes(Map<ResourceLocation, JsonElement> recipes) {
|
||||
ConcurrentMap<ResourceLocation, List<RawRecipe>> rawRecipesByType = recipes
|
||||
.entrySet()
|
||||
.parallelStream()
|
||||
.filter(entry -> entry.getValue().isJsonObject() && hasValidType(entry.getValue().getAsJsonObject()))
|
||||
.map(entry -> new RawRecipe(entry.getKey(), entry.getValue().getAsJsonObject()))
|
||||
.collect(Collectors.groupingByConcurrent(RawRecipe::getType));
|
||||
|
||||
RecipeTransformationResult recipeTransformationResult = new RecipeTransformationResult();
|
||||
|
||||
for (var entry : recipes.entrySet()) {
|
||||
if (!hasValidType(entry.getValue().getAsJsonObject())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry.getValue() instanceof JsonObject json) {
|
||||
JsonObject result = transformRecipe(entry.getKey(), json);
|
||||
recipeTransformationResult.track(entry.getKey(), json, result);
|
||||
rawRecipesByType.forEach((type, rawRecipes) -> {
|
||||
for (int curIndex = 0; curIndex < rawRecipes.size(); curIndex++) {
|
||||
RawRecipe curRecipe = rawRecipes.get(curIndex);
|
||||
JsonObject result = transformRecipe(curRecipe.getId(), curRecipe.getOriginal());
|
||||
if (result != null) {
|
||||
entry.setValue(result);
|
||||
recipeTransformationResult.track(curRecipe.getId(), curRecipe.getOriginal(), result); // TODO remove
|
||||
|
||||
curRecipe.setTransformed(result);
|
||||
|
||||
for (int compareIndex = 0; compareIndex < curIndex; compareIndex++) { // TODO extract
|
||||
RawRecipe toCompare = rawRecipes.get(compareIndex);
|
||||
if(toCompare.isDuplicate()) continue;
|
||||
JsonObject json = toCompare.isTransformed() ? toCompare.getTransformed() : toCompare.getOriginal();
|
||||
if(result.equals(json)) { // TODO replace with cool equal which has additional compare rules
|
||||
toCompare.markAsDuplicate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Map<ResourceLocation, List<RawRecipe>> duplicates = new HashMap<>();
|
||||
rawRecipesByType.forEach((type, rawRecipes) -> {
|
||||
List<RawRecipe> duplicatesForType = rawRecipes.stream().filter(RawRecipe::isDuplicate).collect(Collectors.toList());
|
||||
if(!duplicatesForType.isEmpty()) {
|
||||
duplicates.put(type, duplicatesForType);
|
||||
}
|
||||
});
|
||||
|
||||
recipeTransformationResult.end();
|
||||
|
||||
// for (var entry : recipes.entrySet()) {
|
||||
// if (!hasValidType(entry.getValue().getAsJsonObject())) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// if (entry.getValue() instanceof JsonObject json) {
|
||||
// JsonObject result = transformRecipe(entry.getKey(), json);
|
||||
// recipeTransformationResult.track(entry.getKey(), json, result);
|
||||
// if (result != null) {
|
||||
// entry.setValue(result);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return recipeTransformationResult;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
package com.almostreliable.unified.utils;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class JsonCompare {
|
||||
|
||||
public static int compare(JsonObject first, JsonObject second, LinkedHashMap<String, Rule> rules) {
|
||||
for (var entry : rules.entrySet()) {
|
||||
JsonElement fElement = first.get(entry.getKey());
|
||||
JsonElement sElement = second.get(entry.getKey());
|
||||
|
||||
if (fElement == null && sElement == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int compareIndex = entry.getValue().compare(fElement, sElement);
|
||||
if (compareIndex != 0) {
|
||||
return compareIndex;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static JsonObject compare(LinkedHashMap<String, Rule> rules, JsonObject... jsonObjects) {
|
||||
List<JsonObject> unsorted = Arrays.asList(jsonObjects);
|
||||
unsorted.sort((f, s) -> compare(f, s, rules));
|
||||
return unsorted.get(0);
|
||||
}
|
||||
|
||||
public static boolean matches(JsonObject first, JsonObject second, Collection<String> ignoredProperties) {
|
||||
List<String> firstValidKeys = first
|
||||
.keySet()
|
||||
.stream()
|
||||
.filter(key -> !ignoredProperties.contains(key))
|
||||
.toList();
|
||||
List<String> secondValidKeys = second
|
||||
.keySet()
|
||||
.stream()
|
||||
.filter(key -> !ignoredProperties.contains(key))
|
||||
.toList();
|
||||
|
||||
if (firstValidKeys.size() != secondValidKeys.size()) return false;
|
||||
|
||||
for (String firstKey : firstValidKeys) {
|
||||
if (!first.get(firstKey).equals(second.get(firstKey))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Rule {
|
||||
/**
|
||||
* Compare two JsonElements. The caller must ensure that at least one element is not null.
|
||||
*/
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
int compare(@Nullable JsonElement first, @Nullable JsonElement second);
|
||||
}
|
||||
|
||||
public static class LowerRule implements Rule {
|
||||
@Override
|
||||
public int compare(@Nullable JsonElement first, @Nullable JsonElement second) {
|
||||
double firstValue = first instanceof JsonPrimitive fp ? fp.getAsDouble() : 0;
|
||||
double secondValue = second instanceof JsonPrimitive sp ? sp.getAsDouble() : 0;
|
||||
return Double.compare(firstValue, secondValue);
|
||||
}
|
||||
}
|
||||
|
||||
public static class HigherRule implements Rule {
|
||||
@Override
|
||||
public int compare(@Nullable JsonElement first, @Nullable JsonElement second) {
|
||||
double firstValue = first instanceof JsonPrimitive fp ? fp.getAsDouble() : 0;
|
||||
double secondValue = second instanceof JsonPrimitive sp ? sp.getAsDouble() : 0;
|
||||
return Double.compare(secondValue, firstValue);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,8 @@ import com.almostreliable.unified.recipe.handler.RecipeHandlerFactory;
|
|||
import com.almostreliable.unified.utils.ReplacementMap;
|
||||
import com.almostreliable.unified.utils.TagMapTests;
|
||||
import com.almostreliable.unified.utils.UnifyTag;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
@ -71,4 +73,15 @@ public class TestUtils {
|
|||
consumer.accept(factory);
|
||||
return new RecipeTransformer(factory, map);
|
||||
}
|
||||
|
||||
public static JsonObject json(String json) {
|
||||
return new Gson().fromJson(json, JsonObject.class);
|
||||
}
|
||||
|
||||
public static JsonObject json(String json, Consumer<JsonObject> consumer) {
|
||||
Gson gson = new Gson();
|
||||
JsonObject obj = gson.fromJson(json, JsonObject.class);
|
||||
consumer.accept(obj);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
package com.almostreliable.unified.util;
|
||||
|
||||
import com.almostreliable.unified.TestUtils;
|
||||
import com.almostreliable.unified.utils.JsonCompare;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class JsonCompareTest {
|
||||
public static String recipe = """
|
||||
{
|
||||
"type": "minecraft:smelting",
|
||||
"group": "coal",
|
||||
"ingredient": {
|
||||
"item": "minecraft:coal_ore"
|
||||
},
|
||||
"result": "minecraft:coal",
|
||||
"experience": 0.1,
|
||||
"cookingtime": 200
|
||||
}
|
||||
""";
|
||||
|
||||
@Test
|
||||
public void simpleCompareFirst() {
|
||||
JsonObject first = TestUtils.json(recipe, j -> j.addProperty("experience", 0.2));
|
||||
JsonObject second = TestUtils.json(recipe); // 0.1 experience
|
||||
|
||||
LinkedHashMap<String, JsonCompare.Rule> rules = new LinkedHashMap<>();
|
||||
rules.put("experience", new JsonCompare.LowerRule());
|
||||
JsonObject result = JsonCompare.compare(rules, first, second);
|
||||
assertEquals(second, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleCompareSecond() {
|
||||
JsonObject first = TestUtils.json(recipe, j -> j.addProperty("experience", 0.05));
|
||||
JsonObject second = TestUtils.json(recipe); // 0.1 experience
|
||||
|
||||
LinkedHashMap<String, JsonCompare.Rule> rules = new LinkedHashMap<>();
|
||||
rules.put("experience", new JsonCompare.LowerRule());
|
||||
JsonObject result = JsonCompare.compare(rules, first, second);
|
||||
assertEquals(first, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareHigherWins() {
|
||||
JsonObject first = TestUtils.json(recipe, j -> j.addProperty("experience", 0.05));
|
||||
JsonObject second = TestUtils.json(recipe); // 0.1 experience // 0.1 experience
|
||||
|
||||
LinkedHashMap<String, JsonCompare.Rule> rules = new LinkedHashMap<>();
|
||||
rules.put("experience", new JsonCompare.HigherRule());
|
||||
JsonObject result = JsonCompare.compare(rules, first, second);
|
||||
assertEquals(second, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareMulti() {
|
||||
JsonObject a = TestUtils.json(recipe, j -> {
|
||||
j.addProperty("experience", 0.1);
|
||||
j.addProperty("cookingtime", 100);
|
||||
});
|
||||
JsonObject b = TestUtils.json(recipe, j -> j.addProperty("experience", 0.1));
|
||||
JsonObject c = TestUtils.json(recipe, j -> {
|
||||
j.addProperty("experience", 0.1);
|
||||
j.addProperty("cookingtime", 50);
|
||||
});
|
||||
JsonObject d = TestUtils.json(recipe, j -> j.addProperty("experience", 0.2));
|
||||
JsonObject e = TestUtils.json(recipe, j -> j.addProperty("experience", 0.2));
|
||||
JsonObject f = TestUtils.json(recipe, j -> j.addProperty("experience", 0.1));
|
||||
JsonObject g = TestUtils.json(recipe, j -> {
|
||||
j.addProperty("experience", 0.2);
|
||||
j.addProperty("cookingtime", 100);
|
||||
});
|
||||
|
||||
LinkedHashMap<String, JsonCompare.Rule> rules = new LinkedHashMap<>();
|
||||
rules.put("experience", new JsonCompare.HigherRule());
|
||||
rules.put("cookingtime", new JsonCompare.LowerRule());
|
||||
|
||||
List<JsonObject> list = Arrays.asList(a, b, c, d, e, f, g);
|
||||
list.sort((first, second) -> JsonCompare.compare(first, second, rules));
|
||||
List<JsonObject> results = Arrays.asList(g, d, e, c, a, b, f);
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
assertEquals(results.get(i), list.get(i), "Failed at index " + i);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleMatch() {
|
||||
JsonObject first = TestUtils.json(recipe);
|
||||
JsonObject second = TestUtils.json(recipe);
|
||||
boolean matches = JsonCompare.matches(first, second, List.of());
|
||||
assertTrue(matches);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noMatch() {
|
||||
JsonObject first = TestUtils.json(recipe, j -> j.addProperty("experience", 100));
|
||||
JsonObject second = TestUtils.json(recipe);
|
||||
boolean matches = JsonCompare.matches(first, second, List.of());
|
||||
assertFalse(matches);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchBecauseIgnore() {
|
||||
JsonObject first = TestUtils.json(recipe, j -> j.addProperty("experience", 100));
|
||||
JsonObject second = TestUtils.json(recipe);
|
||||
boolean matches = JsonCompare.matches(first, second, List.of("experience"));
|
||||
assertTrue(matches);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue