/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.elasticsearch.schema.impl.json;
import java.util.Map;
import java.util.Objects;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
/**
* Determines whether two {@link JsonElement}s should be considered equivalent.
*
* @author Yoann Rodiere
*/
public class AnalysisJsonElementEquivalence {
private final AnalysisJsonElementEquivalence nestedEquivalence;
public AnalysisJsonElementEquivalence() {
this.nestedEquivalence = this; // Use the same equivalence for array items and object properties
}
public AnalysisJsonElementEquivalence(AnalysisJsonElementEquivalence itemEquivalence) {
this.nestedEquivalence = itemEquivalence; // Use the given equivalence for array items and object properties
}
/**
* Determines whether two {@link JsonElement}s should be considered equivalent.
* @param left An element whose equivalence to {@code right} will be tested.
* @param right An element whose equivalence to {@code left} will be tested.
* @return {@code true} if {@code left} and {@code right} are equivalent, {@code false} otherwise.
*/
public boolean isEquivalent(JsonElement left, JsonElement right) {
if ( left == null || right == null ) {
return left == right;
}
else {
if ( left.isJsonPrimitive() && right.isJsonPrimitive() ) {
return isPrimitiveEquivalent( left.getAsJsonPrimitive(), right.getAsJsonPrimitive() );
}
else if ( left.isJsonArray() && right.isJsonArray() ) {
return isArrayEquivalent( left.getAsJsonArray(), right.getAsJsonArray() );
}
else if ( left.isJsonObject() && right.isJsonObject() ) {
return isObjectEquivalent( left.getAsJsonObject(), right.getAsJsonObject() );
}
else {
return isElementEquivalent( left, right );
}
}
}
/*
* Compares the string representation of primitives.
*
* This is necessary when validating analysis settings for two reasons:
*
* 1. When we translate Lucene analyzer definitions, we only use
* string parameters, even for integer values, because strings
* are what we get from users and we don't have extensive knowledge
* of the parameter types (which would enable us to convert them
* to the right type).
* 2. Regardless of the item above, when we retrieve settings
* from Elasticsearch, it only returns strings, probably because
* the values are stored as strings. Thus we must also handle the
* case where we initially set an integer value but Elasticsearch
* shows it as a string.
*/
protected boolean isPrimitiveEquivalent(JsonPrimitive left, JsonPrimitive right) {
return Objects.equals( left.getAsString(), right.getAsString() );
}
protected boolean isArrayEquivalent(JsonArray left, JsonArray right) {
if ( left == null || right == null ) {
return left == right;
}
if ( left.size() != right.size() ) {
return false;
}
int size = left.size();
for ( int i = 0 ; i < size ; ++i ) {
if ( !isNestedEquivalent( left.get( i ), right.get( i ) ) ) {
return false;
}
}
return true;
}
protected boolean isObjectEquivalent(JsonObject left, JsonObject right) {
for ( Map.Entry<String, JsonElement> leftEntry : left.entrySet() ) {
String propertyName = leftEntry.getKey();
JsonElement leftValue = leftEntry.getValue();
JsonElement rightValue = right.get( propertyName );
if ( !isNestedEquivalent( leftValue, rightValue ) ) {
return false;
}
}
// Also check for properties that are only in "right"
for ( Map.Entry<String, JsonElement> rightEntry : right.entrySet() ) {
String propertyName = rightEntry.getKey();
if ( !left.has( propertyName ) ) {
JsonElement leftValue = null;
JsonElement rightValue = rightEntry.getValue();
// Let the equivalence decide whether null can be equivalent to something
if ( !isNestedEquivalent( leftValue, rightValue ) ) {
return false;
}
}
}
return true;
}
protected boolean isNestedEquivalent(JsonElement left, JsonElement right) {
return nestedEquivalence.isEquivalent( left, right );
}
/*
* Compare two elements that either aren't of the same type or are both JsonNull.
*/
protected boolean isElementEquivalent(JsonElement left, JsonElement right) {
return Objects.equals( left, right );
}
}