Implemented a recursive equality checker for JsonElement. Compensates for MongoDB rounding issues, non deterministic map entry order etc. Also reduces CPU load with 30-40%.
This commit is contained in:
parent
b50c8cd048
commit
6d57d6f51c
@ -139,7 +139,7 @@ public class DriverMongo extends DriverAbstract
|
||||
Long mtime = ((Number)raw.removeField(MTIME_FIELD)).longValue();
|
||||
raw.removeField(ID_FIELD);
|
||||
|
||||
JsonElement element = MongoGsonConverter.mongo2GsonObject(raw);
|
||||
JsonElement element = GsonMongoConverter.mongo2GsonObject(raw);
|
||||
|
||||
return new SimpleEntry<JsonElement, Long>(element, mtime);
|
||||
}
|
||||
@ -149,7 +149,7 @@ public class DriverMongo extends DriverAbstract
|
||||
{
|
||||
DBCollection dbcoll = fixColl(coll);
|
||||
|
||||
BasicDBObject dbo = MongoGsonConverter.gson2MongoObject(data);
|
||||
BasicDBObject dbo = GsonMongoConverter.gson2MongoObject(data);
|
||||
Long mtime = System.currentTimeMillis();
|
||||
dbo.put(MTIME_FIELD, mtime);
|
||||
dbo.put(ID_FIELD, id);
|
||||
|
177
src/com/massivecraft/mcore/store/GsonEqualsChecker.java
Normal file
177
src/com/massivecraft/mcore/store/GsonEqualsChecker.java
Normal file
@ -0,0 +1,177 @@
|
||||
package com.massivecraft.mcore.store;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import com.massivecraft.mcore.xlib.gson.JsonArray;
|
||||
import com.massivecraft.mcore.xlib.gson.JsonElement;
|
||||
import com.massivecraft.mcore.xlib.gson.JsonNull;
|
||||
import com.massivecraft.mcore.xlib.gson.JsonObject;
|
||||
import com.massivecraft.mcore.xlib.gson.JsonPrimitive;
|
||||
import com.massivecraft.mcore.xlib.gson.internal.LazilyParsedNumber;
|
||||
|
||||
public class GsonEqualsChecker
|
||||
{
|
||||
// The argument one must be JsonElement, and can not be null.
|
||||
// The argument twoObject may be anything, even null.
|
||||
public static boolean equals(JsonElement one, Object twoObject)
|
||||
{
|
||||
// Null check (one can't ever be null)
|
||||
if (twoObject == null) return false;
|
||||
|
||||
// Object identity speedup
|
||||
if (one == twoObject) return true;
|
||||
|
||||
// Type-Switch
|
||||
if (one.isJsonObject())
|
||||
{
|
||||
// JsonObject
|
||||
return objectEquals((JsonObject)one, twoObject);
|
||||
}
|
||||
else if (one.isJsonArray())
|
||||
{
|
||||
// JsonArray
|
||||
return arrayEquals((JsonArray)one, twoObject);
|
||||
}
|
||||
else if (one.isJsonPrimitive())
|
||||
{
|
||||
// JsonPrimitive
|
||||
return primitiveEquals((JsonPrimitive)one, twoObject);
|
||||
}
|
||||
else if (one.isJsonNull())
|
||||
{
|
||||
// JsonNull
|
||||
return nullEquals((JsonNull)one, twoObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ???
|
||||
throw new IllegalArgumentException("Unsupported value type for: " + one);
|
||||
}
|
||||
}
|
||||
|
||||
// The argument one must be JsonObject, and can not be null.
|
||||
// The argument twoObject may be anything, even null.
|
||||
public static boolean objectEquals(JsonObject one, Object twoObject)
|
||||
{
|
||||
// Null check (one can't ever be null)
|
||||
if (twoObject == null) return false;
|
||||
|
||||
// Object identity speedup
|
||||
if (one == twoObject) return true;
|
||||
|
||||
// twoObject must be JsonObject
|
||||
if (!(twoObject instanceof JsonObject)) return false;
|
||||
|
||||
// Cast to JsonObject
|
||||
JsonObject two = (JsonObject)twoObject;
|
||||
|
||||
// Size must be the same
|
||||
if (one.entrySet().size() != two.entrySet().size()) return false;
|
||||
|
||||
// And each entry must exist and be the same
|
||||
for (Entry<String, JsonElement> entry : one.entrySet())
|
||||
{
|
||||
if (!equals(entry.getValue(), two.get(entry.getKey()))) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// The argument one must be JsonArray, and can not be null.
|
||||
// The argument twoObject may be anything, even null.
|
||||
public static boolean arrayEquals(JsonArray one, Object twoObject)
|
||||
{
|
||||
// Null check (one can't ever be null)
|
||||
if (twoObject == null) return false;
|
||||
|
||||
// Object identity speedup
|
||||
if (one == twoObject) return true;
|
||||
|
||||
// twoObject must be JsonArray
|
||||
if (!(twoObject instanceof JsonArray)) return false;
|
||||
|
||||
// Cast to JsonArray
|
||||
JsonArray two = (JsonArray)twoObject;
|
||||
|
||||
// Size must be the same
|
||||
int size = one.size();
|
||||
if (two.size() != size) return false;
|
||||
|
||||
// And each element index must be the same
|
||||
for (int i = 0; i < size ; i++)
|
||||
{
|
||||
if (!equals(one.get(i), two.get(i))) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// The argument one must be JsonPrimitive, and can not be null.
|
||||
// The argument twoObject may be anything, even null.
|
||||
public static boolean primitiveEquals(JsonPrimitive one, Object twoObject)
|
||||
{
|
||||
// Null check (one can't ever be null)
|
||||
if (twoObject == null) return false;
|
||||
|
||||
// Object identity speedup
|
||||
if (one == twoObject) return true;
|
||||
|
||||
// if twoObject is JsonObject or JsonArray we are not equal.
|
||||
if (!(twoObject instanceof JsonPrimitive)) return false;
|
||||
|
||||
// Cast to JsonPrimitive
|
||||
JsonPrimitive two = (JsonPrimitive)twoObject;
|
||||
|
||||
// Boolean check
|
||||
if (one.isBoolean())
|
||||
{
|
||||
return one.getAsBoolean() == two.getAsBoolean();
|
||||
}
|
||||
|
||||
// Number check
|
||||
if (one.isNumber())
|
||||
{
|
||||
Number oneNumber = one.getAsNumber();
|
||||
Number twoNumber = two.getAsNumber();
|
||||
|
||||
boolean floating;
|
||||
if (oneNumber instanceof LazilyParsedNumber)
|
||||
{
|
||||
floating = StringUtils.contains(oneNumber.toString(), '.');
|
||||
}
|
||||
else
|
||||
{
|
||||
floating = (oneNumber instanceof Double || oneNumber instanceof Float);
|
||||
}
|
||||
|
||||
if (floating)
|
||||
{
|
||||
// Our epsilon is pretty big in order to see float and double as the same.
|
||||
return Math.abs(oneNumber.doubleValue() - twoNumber.doubleValue()) < 0.0001D;
|
||||
}
|
||||
else
|
||||
{
|
||||
return oneNumber.longValue() == twoNumber.longValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// String check
|
||||
if (one.isString())
|
||||
{
|
||||
return one.getAsString().equals(two.getAsString());
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unsupported value type for: " + one);
|
||||
}
|
||||
|
||||
// The argument one must be JsonNull, and can not be null.
|
||||
// The argument twoObject may be anything, even null.
|
||||
public static boolean nullEquals(JsonNull one, Object twoObject)
|
||||
{
|
||||
// Null check (one can't ever be null)
|
||||
if (twoObject == null) return false;
|
||||
|
||||
return twoObject == JsonNull.INSTANCE;
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ import com.massivecraft.mcore.xlib.mongodb.BasicDBList;
|
||||
import com.massivecraft.mcore.xlib.mongodb.BasicDBObject;
|
||||
import com.massivecraft.mcore.xlib.mongodb.DBObject;
|
||||
|
||||
public final class MongoGsonConverter
|
||||
public final class GsonMongoConverter
|
||||
{
|
||||
// -------------------------------------------- //
|
||||
// CONSTANTS
|
@ -52,7 +52,7 @@ public class MStore
|
||||
if (one == null) return two == null;
|
||||
if (two == null) return one == null;
|
||||
|
||||
return one.toString().equals(two.toString());
|
||||
return GsonEqualsChecker.equals(one, two);
|
||||
}
|
||||
|
||||
// -------------------------------------------- //
|
||||
|
Loading…
Reference in New Issue
Block a user