/* * 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 ); } }