ContextSetJsonSerializer output should be sorted

This commit is contained in:
Luck 2020-08-14 16:25:55 +01:00
parent e2b575dd24
commit 4d5a24f2c4
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
7 changed files with 61 additions and 46 deletions

View File

@ -68,11 +68,11 @@ public class ContextsFile {
} }
if (data.has("static-contexts")) { if (data.has("static-contexts")) {
this.staticContexts = ContextSetJsonSerializer.deserializeContextSet(data.get("static-contexts").getAsJsonObject()).immutableCopy(); this.staticContexts = ContextSetJsonSerializer.deserialize(data.get("static-contexts").getAsJsonObject()).immutableCopy();
} }
if (data.has("default-contexts")) { if (data.has("default-contexts")) {
this.defaultContexts = ContextSetJsonSerializer.deserializeContextSet(data.get("default-contexts").getAsJsonObject()).immutableCopy(); this.defaultContexts = ContextSetJsonSerializer.deserialize(data.get("default-contexts").getAsJsonObject()).immutableCopy();
} }
} catch (IOException e) { } catch (IOException e) {
@ -85,8 +85,8 @@ public class ContextsFile {
try (BufferedWriter writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) { try (BufferedWriter writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) {
JsonObject data = new JsonObject(); JsonObject data = new JsonObject();
data.add("static-contexts", ContextSetJsonSerializer.serializeContextSet(this.staticContexts)); data.add("static-contexts", ContextSetJsonSerializer.serialize(this.staticContexts));
data.add("default-contexts", ContextSetJsonSerializer.serializeContextSet(this.defaultContexts)); data.add("default-contexts", ContextSetJsonSerializer.serialize(this.defaultContexts));
new GsonBuilder().setPrettyPrinting().create().toJson(data, writer); new GsonBuilder().setPrettyPrinting().create().toJson(data, writer);
writer.flush(); writer.flush();

View File

@ -39,73 +39,88 @@ import net.luckperms.api.context.ContextSet;
import net.luckperms.api.context.MutableContextSet; import net.luckperms.api.context.MutableContextSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
/**
* Serializes and deserializes {@link ContextSet}s to and from JSON.
*
* <p>The entries within the serialized output are sorted, this ensures that any two invocations
* of {@link #serialize(ContextSet)} with the same {@link ContextSet} will produce
* the same exact JSON string.</p>
*/
public final class ContextSetJsonSerializer { public final class ContextSetJsonSerializer {
private ContextSetJsonSerializer() {} private ContextSetJsonSerializer() {}
public static JsonObject serializeContextSet(ContextSet contextSet) { public static JsonObject serialize(ContextSet contextSet) {
JsonObject data = new JsonObject(); JsonObject output = new JsonObject();
Map<String, Set<String>> map = contextSet.toMap();
for (Map.Entry<String, Set<String>> entry : map.entrySet()) { List<Map.Entry<String, Set<String>>> entries = new ArrayList<>(contextSet.toMap().entrySet());
List<String> values = new ArrayList<>(entry.getValue()); entries.sort(Map.Entry.comparingByKey()); // sort - consistent output order
int size = values.size();
if (size == 1) { for (Map.Entry<String, Set<String>> entry : entries) {
data.addProperty(entry.getKey(), values.get(0)); String[] values = entry.getValue().toArray(new String[0]);
} else if (size > 1) { switch (values.length) {
JsonArray arr = new JsonArray(); case 0:
for (String s : values) { break;
arr.add(new JsonPrimitive(s)); case 1:
} output.addProperty(entry.getKey(), values[0]);
data.add(entry.getKey(), arr); break;
default:
Arrays.sort(values); // sort - consistent output order
JsonArray arr = new JsonArray();
for (String value : values) {
arr.add(new JsonPrimitive(value));
}
output.add(entry.getKey(), arr);
break;
} }
} }
return data; return output;
} }
public static ContextSet deserializeContextSet(Gson gson, String json) { public static ContextSet deserialize(Gson gson, String input) {
Objects.requireNonNull(json, "json"); Objects.requireNonNull(input, "input");
if (json.equals("{}")) { if (input.equals("{}")) {
return ImmutableContextSetImpl.EMPTY; return ImmutableContextSetImpl.EMPTY;
} }
JsonObject context = gson.fromJson(json, JsonObject.class); JsonObject jsonObject = gson.fromJson(input, JsonObject.class);
if (context == null) { if (jsonObject == null) {
return ImmutableContextSetImpl.EMPTY; return ImmutableContextSetImpl.EMPTY;
} }
return deserializeContextSet(context); return deserialize(jsonObject);
} }
public static ContextSet deserializeContextSet(JsonElement element) { public static ContextSet deserialize(JsonElement element) {
Preconditions.checkArgument(element.isJsonObject()); Preconditions.checkArgument(element.isJsonObject());
JsonObject data = element.getAsJsonObject(); JsonObject jsonObject = element.getAsJsonObject();
if (data.entrySet().isEmpty()) { if (jsonObject.size() == 0) {
return ImmutableContextSetImpl.EMPTY; return ImmutableContextSetImpl.EMPTY;
} }
MutableContextSet map = new MutableContextSetImpl(); MutableContextSet contextSet = new MutableContextSetImpl();
for (Map.Entry<String, JsonElement> e : data.entrySet()) { for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
String k = e.getKey(); String k = entry.getKey();
JsonElement v = e.getValue(); JsonElement v = entry.getValue();
if (v.isJsonArray()) { if (v.isJsonArray()) {
JsonArray values = v.getAsJsonArray(); JsonArray values = v.getAsJsonArray();
for (JsonElement value : values) { for (JsonElement value : values) {
map.add(k, value.getAsString()); contextSet.add(k, value.getAsString());
} }
} else { } else {
map.add(k, v.getAsString()); contextSet.add(k, v.getAsString());
} }
} }
return map; return contextSet;
} }
} }

View File

@ -61,7 +61,7 @@ public class NodeJsonSerializer {
} }
if (!node.getContexts().isEmpty()) { if (!node.getContexts().isEmpty()) {
attributes.add("context", ContextSetJsonSerializer.serializeContextSet(node.getContexts())); attributes.add("context", ContextSetJsonSerializer.serialize(node.getContexts()));
} }
arr.add(attributes); arr.add(attributes);
@ -84,7 +84,7 @@ public class NodeJsonSerializer {
} }
if (attributes.has("context")) { if (attributes.has("context")) {
builder.context(ContextSetJsonSerializer.deserializeContextSet(attributes.get("context"))); builder.context(ContextSetJsonSerializer.deserialize(attributes.get("context")));
} }
nodes.add(builder.build()); nodes.add(builder.build());

View File

@ -93,7 +93,7 @@ public final class SqlNode {
world = "global"; world = "global";
} }
return new SqlNode(permission, value, server, world, expiry, ContextSetJsonSerializer.deserializeContextSet(GsonProvider.normal(), contexts).immutableCopy(), sqlId); return new SqlNode(permission, value, server, world, expiry, ContextSetJsonSerializer.deserialize(GsonProvider.normal(), contexts).immutableCopy(), sqlId);
} }
private final String permission; private final String permission;

View File

@ -759,7 +759,7 @@ public class SqlStorage implements StorageImplementation {
ps.setString(4, nd.getServer()); ps.setString(4, nd.getServer());
ps.setString(5, nd.getWorld()); ps.setString(5, nd.getWorld());
ps.setLong(6, nd.getExpiry()); ps.setLong(6, nd.getExpiry());
ps.setString(7, GsonProvider.normal().toJson(ContextSetJsonSerializer.serializeContextSet(nd.getContexts()))); ps.setString(7, GsonProvider.normal().toJson(ContextSetJsonSerializer.serialize(nd.getContexts())));
} }
private static Set<SqlNode> getMissingFromRemote(Set<SqlNode> local, Set<SqlNode> remote) { private static Set<SqlNode> getMissingFromRemote(Set<SqlNode> local, Set<SqlNode> remote) {

View File

@ -127,7 +127,7 @@ public final class WebEditor {
}) })
) )
.consume(o -> { .consume(o -> {
o.add("potentialContexts", ContextSetJsonSerializer.serializeContextSet(potentialContexts.build())); o.add("potentialContexts", ContextSetJsonSerializer.serialize(potentialContexts.build()));
}) })
.toJson(); .toJson();
} }

View File

@ -122,7 +122,7 @@ public class SubjectDataContainer {
JsonObject context = section.get("context").getAsJsonObject(); JsonObject context = section.get("context").getAsJsonObject();
JsonObject data = section.get("data").getAsJsonObject(); JsonObject data = section.get("data").getAsJsonObject();
ImmutableContextSet contextSet = ContextSetJsonSerializer.deserializeContextSet(context).immutableCopy(); ImmutableContextSet contextSet = ContextSetJsonSerializer.deserialize(context).immutableCopy();
ImmutableMap.Builder<String, Boolean> perms = ImmutableMap.builder(); ImmutableMap.Builder<String, Boolean> perms = ImmutableMap.builder();
for (Map.Entry<String, JsonElement> perm : data.entrySet()) { for (Map.Entry<String, JsonElement> perm : data.entrySet()) {
perms.put(perm.getKey(), perm.getValue().getAsBoolean()); perms.put(perm.getKey(), perm.getValue().getAsBoolean());
@ -145,7 +145,7 @@ public class SubjectDataContainer {
JsonObject context = section.get("context").getAsJsonObject(); JsonObject context = section.get("context").getAsJsonObject();
JsonObject data = section.get("data").getAsJsonObject(); JsonObject data = section.get("data").getAsJsonObject();
ImmutableContextSet contextSet = ContextSetJsonSerializer.deserializeContextSet(context).immutableCopy(); ImmutableContextSet contextSet = ContextSetJsonSerializer.deserialize(context).immutableCopy();
ImmutableMap.Builder<String, String> opts = ImmutableMap.builder(); ImmutableMap.Builder<String, String> opts = ImmutableMap.builder();
for (Map.Entry<String, JsonElement> opt : data.entrySet()) { for (Map.Entry<String, JsonElement> opt : data.entrySet()) {
opts.put(opt.getKey(), opt.getValue().getAsString()); opts.put(opt.getKey(), opt.getValue().getAsString());
@ -168,7 +168,7 @@ public class SubjectDataContainer {
JsonObject context = section.get("context").getAsJsonObject(); JsonObject context = section.get("context").getAsJsonObject();
JsonArray data = section.get("data").getAsJsonArray(); JsonArray data = section.get("data").getAsJsonArray();
ImmutableContextSet contextSet = ContextSetJsonSerializer.deserializeContextSet(context).immutableCopy(); ImmutableContextSet contextSet = ContextSetJsonSerializer.deserialize(context).immutableCopy();
ImmutableList.Builder<LPSubjectReference> pars = ImmutableList.builder(); ImmutableList.Builder<LPSubjectReference> pars = ImmutableList.builder();
for (JsonElement p : data) { for (JsonElement p : data) {
if (!p.isJsonObject()) { if (!p.isJsonObject()) {
@ -203,7 +203,7 @@ public class SubjectDataContainer {
} }
JsonObject section = new JsonObject(); JsonObject section = new JsonObject();
section.add("context", ContextSetJsonSerializer.serializeContextSet(e.getKey())); section.add("context", ContextSetJsonSerializer.serialize(e.getKey()));
JsonObject data = new JsonObject(); JsonObject data = new JsonObject();
@ -227,7 +227,7 @@ public class SubjectDataContainer {
} }
JsonObject section = new JsonObject(); JsonObject section = new JsonObject();
section.add("context", ContextSetJsonSerializer.serializeContextSet(e.getKey())); section.add("context", ContextSetJsonSerializer.serialize(e.getKey()));
JsonObject data = new JsonObject(); JsonObject data = new JsonObject();
@ -251,7 +251,7 @@ public class SubjectDataContainer {
} }
JsonObject section = new JsonObject(); JsonObject section = new JsonObject();
section.add("context", ContextSetJsonSerializer.serializeContextSet(e.getKey())); section.add("context", ContextSetJsonSerializer.serialize(e.getKey()));
JsonArray data = new JsonArray(); JsonArray data = new JsonArray();
for (LPSubjectReference ref : e.getValue()) { for (LPSubjectReference ref : e.getValue()) {