mirror of
https://github.com/AlmostReliable/almostunified.git
synced 2024-11-28 18:45:48 -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 net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class RecipeTransformer {
|
public class RecipeTransformer {
|
||||||
|
|
||||||
|
@ -29,23 +33,59 @@ public class RecipeTransformer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public RecipeTransformationResult transformRecipes(Map<ResourceLocation, JsonElement> recipes) {
|
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();
|
RecipeTransformationResult recipeTransformationResult = new RecipeTransformationResult();
|
||||||
|
|
||||||
for (var entry : recipes.entrySet()) {
|
rawRecipesByType.forEach((type, rawRecipes) -> {
|
||||||
if (!hasValidType(entry.getValue().getAsJsonObject())) {
|
for (int curIndex = 0; curIndex < rawRecipes.size(); curIndex++) {
|
||||||
continue;
|
RawRecipe curRecipe = rawRecipes.get(curIndex);
|
||||||
}
|
JsonObject result = transformRecipe(curRecipe.getId(), curRecipe.getOriginal());
|
||||||
|
|
||||||
if (entry.getValue() instanceof JsonObject json) {
|
|
||||||
JsonObject result = transformRecipe(entry.getKey(), json);
|
|
||||||
recipeTransformationResult.track(entry.getKey(), json, result);
|
|
||||||
if (result != null) {
|
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();
|
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;
|
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.ReplacementMap;
|
||||||
import com.almostreliable.unified.utils.TagMapTests;
|
import com.almostreliable.unified.utils.TagMapTests;
|
||||||
import com.almostreliable.unified.utils.UnifyTag;
|
import com.almostreliable.unified.utils.UnifyTag;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
@ -71,4 +73,15 @@ public class TestUtils {
|
||||||
consumer.accept(factory);
|
consumer.accept(factory);
|
||||||
return new RecipeTransformer(factory, map);
|
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