Simplify RSQL fields (#2416)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-05-23 11:17:54 +03:00
committed by GitHub
parent 108f03848f
commit 8184aad13c
12 changed files with 64 additions and 175 deletions

View File

@@ -42,11 +42,6 @@ public enum ActionFields implements RsqlQueryField, FieldValueConverter<ActionFi
private final String jpaEntityFieldName; private final String jpaEntityFieldName;
private final List<String> subEntityAttributes; private final List<String> subEntityAttributes;
ActionFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityAttributes = Collections.emptyList();
}
ActionFields(final String jpaEntityFieldName, final String... subEntityAttributes) { ActionFields(final String jpaEntityFieldName, final String... subEntityAttributes) {
this.jpaEntityFieldName = jpaEntityFieldName; this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityAttributes = List.of(subEntityAttributes); this.subEntityAttributes = List.of(subEntityAttributes);

View File

@@ -34,33 +34,19 @@ public enum DistributionSetFields implements RsqlQueryField {
COMPLETE("complete"), COMPLETE("complete"),
MODULE("modules", SoftwareModuleFields.ID.getJpaEntityFieldName(), SoftwareModuleFields.NAME.getJpaEntityFieldName()), MODULE("modules", SoftwareModuleFields.ID.getJpaEntityFieldName(), SoftwareModuleFields.NAME.getJpaEntityFieldName()),
TAG("tags", "name"), TAG("tags", "name"),
METADATA("metadata", new SimpleImmutableEntry<>("key", "value")), METADATA("metadata"),
VALID("valid"); VALID("valid");
private final String jpaEntityFieldName; private final String jpaEntityFieldName;
private final List<String> subEntityAttributes; private final List<String> subEntityAttributes;
private final Entry<String, String> subEntityMapTuple;
DistributionSetFields(final String jpaEntityFieldName) {
this(jpaEntityFieldName, Collections.emptyList(), null);
}
DistributionSetFields(final String jpaEntityFieldName, final String... subEntityAttributes) { DistributionSetFields(final String jpaEntityFieldName, final String... subEntityAttributes) {
this(jpaEntityFieldName, List.of(subEntityAttributes), null);
}
DistributionSetFields(final String jpaEntityFieldName, final Entry<String, String> subEntityMapTuple) {
this(jpaEntityFieldName, Collections.emptyList(), subEntityMapTuple);
}
DistributionSetFields(final String jpaEntityFieldName, List<String> subEntityAttributes, final Entry<String, String> subEntityMapTuple) {
this.jpaEntityFieldName = jpaEntityFieldName; this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityMapTuple = subEntityMapTuple; this.subEntityAttributes = List.of(subEntityAttributes);
this.subEntityAttributes = subEntityAttributes;
} }
@Override @Override
public Optional<Entry<String, String>> getSubEntityMapTuple() { public boolean isMap() {
return Optional.ofNullable(subEntityMapTuple); return this == METADATA;
} }
} }

View File

@@ -27,17 +27,13 @@ public enum DistributionSetTagFields implements RsqlQueryField {
NAME(TagFields.NAME.getJpaEntityFieldName()), NAME(TagFields.NAME.getJpaEntityFieldName()),
DESCRIPTION(TagFields.DESCRIPTION.getJpaEntityFieldName()), DESCRIPTION(TagFields.DESCRIPTION.getJpaEntityFieldName()),
COLOUR(TagFields.COLOUR.getJpaEntityFieldName()), COLOUR(TagFields.COLOUR.getJpaEntityFieldName()),
DISTRIBUTIONSET("assignedToDistributionSet", DISTRIBUTIONSET(
"assignedToDistributionSet",
DistributionSetFields.ID.getJpaEntityFieldName(), DistributionSetFields.NAME.getJpaEntityFieldName()); DistributionSetFields.ID.getJpaEntityFieldName(), DistributionSetFields.NAME.getJpaEntityFieldName());
private final String jpaEntityFieldName; private final String jpaEntityFieldName;
private final List<String> subEntityAttributes; private final List<String> subEntityAttributes;
DistributionSetTagFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityAttributes = Collections.emptyList();
}
DistributionSetTagFields(final String jpaEntityFieldName, final String... subEntityAttributes) { DistributionSetTagFields(final String jpaEntityFieldName, final String... subEntityAttributes) {
this.jpaEntityFieldName = jpaEntityFieldName; this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityAttributes = List.of(subEntityAttributes); this.subEntityAttributes = List.of(subEntityAttributes);

View File

@@ -13,8 +13,7 @@ package org.eclipse.hawkbit.repository;
* A value convert which converts given string based values into an object which * A value convert which converts given string based values into an object which
* can be used for building generic queries. Mapping external API values e.g. * can be used for building generic queries. Mapping external API values e.g.
* REST API to inside representation on database. E.g. mapping 'pending' or * REST API to inside representation on database. E.g. mapping 'pending' or
* 'finished' values in rest queries to Action#isActive boolean * 'finished' values in rest queries to Action#isActive boolean value.
* value.
* *
* @param <T> the enum parameter * @param <T> the enum parameter
*/ */

View File

@@ -24,18 +24,15 @@ public enum RolloutFields implements RsqlQueryField {
NAME("name"), NAME("name"),
DESCRIPTION("description"), DESCRIPTION("description"),
STATUS("status"), STATUS("status"),
DISTRIBUTIONSET("distributionSet", DistributionSetFields.ID.getJpaEntityFieldName(), DISTRIBUTIONSET(
"distributionSet",
DistributionSetFields.ID.getJpaEntityFieldName(),
DistributionSetFields.NAME.getJpaEntityFieldName(), DistributionSetFields.VERSION.getJpaEntityFieldName(), DistributionSetFields.NAME.getJpaEntityFieldName(), DistributionSetFields.VERSION.getJpaEntityFieldName(),
DistributionSetFields.TYPE.getJpaEntityFieldName()); DistributionSetFields.TYPE.getJpaEntityFieldName());
private final String jpaEntityFieldName; private final String jpaEntityFieldName;
private final List<String> subEntityAttributes; private final List<String> subEntityAttributes;
RolloutFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityAttributes = Collections.emptyList();
}
RolloutFields(final String jpaEntityFieldName, final String... subEntityAttributes) { RolloutFields(final String jpaEntityFieldName, final String... subEntityAttributes) {
this.jpaEntityFieldName = jpaEntityFieldName; this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityAttributes = List.of(subEntityAttributes); this.subEntityAttributes = List.of(subEntityAttributes);

View File

@@ -34,30 +34,6 @@ public interface RsqlQueryField {
@NotNull @NotNull
String getJpaEntityFieldName(); String getJpaEntityFieldName();
/**
* Contains the sub entity the given field.
*
* @param propertyField the given field
* @return <code>true</code> contains <code>false</code> contains not
*/
default boolean containsSubEntityAttribute(final String propertyField) {
final List<String> subEntityAttributes = getSubEntityAttributes();
if (subEntityAttributes.contains(propertyField)) {
return true;
}
for (final String attribute : subEntityAttributes) {
final String[] graph = attribute.split(SUB_ATTRIBUTE_SPLIT_REGEX);
for (final String subAttribute : graph) {
if (subAttribute.equalsIgnoreCase(propertyField)) {
return true;
}
}
}
return false;
}
/** /**
* @return all sub entities attributes. * @return all sub entities attributes.
*/ */
@@ -65,22 +41,6 @@ public interface RsqlQueryField {
return Collections.emptyList(); return Collections.emptyList();
} }
/**
* @return a key/value tuple of a sub entity.
*/
default Optional<Entry<String, String>> getSubEntityMapTuple() {
return Optional.empty();
}
/**
* Is the entity field a {@link Map} consisting of key-value pairs.
*
* @return <code>true</code> is a map <code>false</code> is not a map
*/
default boolean isMap() {
return getSubEntityMapTuple().isPresent();
}
/** /**
* Returns the name of the field, that identifies the entity. * Returns the name of the field, that identifies the entity.
* *
@@ -89,4 +49,13 @@ public interface RsqlQueryField {
default String identifierFieldName() { default String identifierFieldName() {
return "id"; return "id";
} }
/**
* Is the entity field a {@link Map} consisting of key-value pairs.
*
* @return <code>true</code> is a map <code>false</code> is not a map
*/
default boolean isMap() {
return false;
}
} }

View File

@@ -45,25 +45,10 @@ public enum TargetFields implements RsqlQueryField {
private final String jpaEntityFieldName; private final String jpaEntityFieldName;
private final List<String> subEntityAttributes; private final List<String> subEntityAttributes;
private final Entry<String, String> subEntityMapTuple;
TargetFields(final String jpaEntityFieldName) {
this(jpaEntityFieldName, Collections.emptyList(), null);
}
TargetFields(final String jpaEntityFieldName, final String... subEntityAttributes) { TargetFields(final String jpaEntityFieldName, final String... subEntityAttributes) {
this(jpaEntityFieldName, List.of(subEntityAttributes), null);
}
TargetFields(final String jpaEntityFieldName, final List<String> subEntityAttributes, final Entry<String, String> subEntityMapTuple) {
this.jpaEntityFieldName = jpaEntityFieldName; this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityAttributes = subEntityAttributes; this.subEntityAttributes = List.of(subEntityAttributes);
this.subEntityMapTuple = subEntityMapTuple;
}
@Override
public Optional<Entry<String, String>> getSubEntityMapTuple() {
return Optional.ofNullable(subEntityMapTuple);
} }
@Override @Override

View File

@@ -25,7 +25,7 @@ public enum TargetFilterQueryFields implements RsqlQueryField {
AUTOASSIGNDISTRIBUTIONSET("autoAssignDistributionSet", "name", "version"); AUTOASSIGNDISTRIBUTIONSET("autoAssignDistributionSet", "name", "version");
private final String jpaEntityFieldName; private final String jpaEntityFieldName;
private List<String> subEntityAttributes; private final List<String> subEntityAttributes;
TargetFilterQueryFields(final String jpaEntityFieldName) { TargetFilterQueryFields(final String jpaEntityFieldName) {
this(jpaEntityFieldName, Collections.emptyList()); this(jpaEntityFieldName, Collections.emptyList());

View File

@@ -36,12 +36,6 @@ class FileNameFieldsTest {
assertThat(matchingClasses).isNotEmpty(); assertThat(matchingClasses).isNotEmpty();
matchingClasses.forEach(providerClass -> { matchingClasses.forEach(providerClass -> {
assertThat(providerClass.getEnumConstants()).isNotEmpty(); assertThat(providerClass.getEnumConstants()).isNotEmpty();
for (final RsqlQueryField provider : providerClass.getEnumConstants()) {
if (provider.isMap() && !provider.getSubEntityAttributes().isEmpty()) {
throw new UnsupportedOperationException(
"Currently sub-entity attributes for maps are not supported, alternatively you could use the key/value tuple, defined by SimpleImmutableEntry class");
}
}
}); });
} }
} }

View File

@@ -60,7 +60,7 @@ public abstract class AbstractRSQLVisitor<A extends Enum<A> & RsqlQueryField> {
for (int i = 1; i < split.length; i++) { for (int i = 1; i < split.length; i++) {
split[i] = getFormattedSubEntityAttribute(enumValue, split[i]); split[i] = getFormattedSubEntityAttribute(enumValue, split[i]);
if (!enumValue.containsSubEntityAttribute(split[i])) { if (!containsSubEntityAttribute(enumValue, split[i])) {
if (i != split.length - 1 || !enumValue.isMap()) { if (i != split.length - 1 || !enumValue.isMap()) {
throw createRSQLParameterUnsupportedException(node, null); throw createRSQLParameterUnsupportedException(node, null);
} // otherwise - the key of map is not in the sub entity attributes } // otherwise - the key of map is not in the sub entity attributes
@@ -86,6 +86,32 @@ public abstract class AbstractRSQLVisitor<A extends Enum<A> & RsqlQueryField> {
} }
} }
/**
* Contains the sub entity the given field.
*
* @param propertyField the given field
* @return <code>true</code> contains <code>false</code> contains not
*/
private boolean containsSubEntityAttribute(final A enumField, final String propertyField) {
final List<String> subEntityAttributes = enumField.getSubEntityAttributes();
if (subEntityAttributes.contains(propertyField)) {
return true;
}
for (final String attribute : subEntityAttributes) {
final String[] graph = attribute.split(RsqlQueryField.SUB_ATTRIBUTE_SPLIT_REGEX);
for (final String subAttribute : graph) {
if (subAttribute.equalsIgnoreCase(propertyField)) {
return true;
}
}
}
return false;
}
/** /**
* @param node current processing node * @param node current processing node
* @param rootException in case there is a cause otherwise {@code null} * @param rootException in case there is a cause otherwise {@code null}

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2021 Bosch.IO GmbH and others * Copyright (c) 2025 Contributors to the Eclipse Foundation
* *
* This program and the accompanying materials are made * This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0 * available under the terms of the Eclipse Public License 2.0
@@ -16,7 +16,6 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@@ -344,9 +343,8 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
final Predicate mapPredicate = mapToMapPredicate(fieldPath, queryPath); final Predicate mapPredicate = mapToMapPredicate(fieldPath, queryPath);
final Predicate valuePredicate = addOperatorPredicate(node, getMapValueFieldPath(queryPath.getEnumValue(), fieldPath),
transformedValues, value, queryPath);
final Predicate valuePredicate = addOperatorPredicate(node, fieldPath, transformedValues, value, queryPath);
return toSingleList(mapPredicate != null ? cb.and(mapPredicate, valuePredicate) : valuePredicate); return toSingleList(mapPredicate != null ? cb.and(mapPredicate, valuePredicate) : valuePredicate);
} }
@@ -398,15 +396,6 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
expressionToCompare -> in(expressionToCompare, transformedValues)); expressionToCompare -> in(expressionToCompare, transformedValues));
} }
private Path<Object> getMapValueFieldPath(final A enumField, final Path<Object> fieldPath) {
final String valueFieldNameFromSubEntity = enumField.getSubEntityMapTuple().map(Entry::getValue).orElse(null);
if (!enumField.isMap() || valueFieldNameFromSubEntity == null) {
return fieldPath;
}
return fieldPath.get(valueFieldNameFromSubEntity);
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Predicate mapToMapPredicate(final Path<Object> fieldPath, final QueryPath queryPath) { private Predicate mapToMapPredicate(final Path<Object> fieldPath, final QueryPath queryPath) {
if (!queryPath.getEnumValue().isMap()) { if (!queryPath.getEnumValue().isMap()) {
@@ -415,16 +404,8 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
final String[] graph = queryPath.getJpaPath(); final String[] graph = queryPath.getJpaPath();
final String keyValue = graph[graph.length - 1]; final String keyValue = graph[graph.length - 1];
if (fieldPath instanceof MapJoin) { // Currently we support only string key. So below cast is safe.
// Currently we support only string key. So below cast is safe. return equal((Expression<String>) (((MapJoin<?, ?, ?>) fieldPath).key()), keyValue);
return equal((Expression<String>) (((MapJoin<?, ?, ?>) fieldPath).key()), keyValue);
}
final String keyFieldName = queryPath.getEnumValue().getSubEntityMapTuple().map(Entry::getKey)
.orElseThrow(() -> new UnsupportedOperationException(
"For the fields, defined as Map, only Map java type or tuple in the form of " +
"SimpleImmutableEntry are allowed. Neither of those could be found!"));
return equal(fieldPath.get(keyFieldName), keyValue);
} }
private Predicate getEqualToPredicate(final Object transformedValue, final Path<Object> fieldPath) { private Predicate getEqualToPredicate(final Object transformedValue, final Path<Object> fieldPath) {
@@ -514,17 +495,12 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
private Expression<String> getExpressionToCompare(final Path innerFieldPath, final A enumField) { private Expression<String> getExpressionToCompare(final Path innerFieldPath, final A enumField) {
if (!enumField.isMap()) { if (enumField.isMap()){
return pathOfString(innerFieldPath);
}
if (innerFieldPath instanceof MapJoin) {
// Currently we support only string key. So below cast is safe. // Currently we support only string key. So below cast is safe.
return (Expression<String>) (((MapJoin<?, ?, ?>) pathOfString(innerFieldPath)).value()); return (Expression<String>) (((MapJoin<?, ?, ?>) pathOfString(innerFieldPath)).value());
} else {
return pathOfString(innerFieldPath);
} }
final String valueFieldName = enumField.getSubEntityMapTuple().map(Entry::getValue)
.orElseThrow(() -> new UnsupportedOperationException(
"For the fields, defined as Map, only Map java type or tuple in the form of SimpleImmutableEntry are allowed. Neither of those could be found!"));
return pathOfString(innerFieldPath).get(valueFieldName);
} }
private String toSQL(final String transformedValue) { private String toSQL(final String transformedValue) {

View File

@@ -9,14 +9,12 @@
*/ */
package org.eclipse.hawkbit.repository.jpa.rsql; package org.eclipse.hawkbit.repository.jpa.rsql;
import java.util.AbstractMap;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function; import java.util.function.Function;
import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaBuilder;
@@ -125,7 +123,7 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
if (values.get(0) == null) { if (values.get(0) == null) {
// IS operator for maps and null value is treated as doesn't exist correspondingly // IS operator for maps and null value is treated as doesn't exist correspondingly
((PluralJoin<?, ?, ?>) fieldPath).on(toMapEntryKeyPredicate(queryPath, fieldPath)); ((PluralJoin<?, ?, ?>) fieldPath).on(toMapEntryKeyPredicate(queryPath, fieldPath));
return cb.isNull(getValueFieldPath(queryPath, fieldPath)); return cb.isNull(fieldPath);
} }
} else if (node.getOperator() == RSQLUtility.NOT) { } else if (node.getOperator() == RSQLUtility.NOT) {
if (values.size() != 1) { if (values.size() != 1) {
@@ -133,13 +131,12 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
} }
// NOT operator for maps and null value is treated as does exist correspondingly // NOT operator for maps and null value is treated as does exist correspondingly
((PluralJoin<?, ?, ?>) fieldPath).on(toMapEntryKeyPredicate(queryPath, fieldPath)); ((PluralJoin<?, ?, ?>) fieldPath).on(toMapEntryKeyPredicate(queryPath, fieldPath));
final Path<?> valueFieldPath = getValueFieldPath(queryPath, fieldPath);
if (values.get(0) == null) { if (values.get(0) == null) {
// special handling of "exists" // special handling of "exists"
return cb.isNotNull(valueFieldPath); return cb.isNotNull(fieldPath);
} else { } else {
// special handling or "not equal" or null (same as != but with possible optimized join - no subquery) // special handling or "not equal" or null (same as != but with possible optimized join - no subquery)
return toNotEqualToPredicate(queryPath, valueFieldPath, values.get(0)); return toNotEqualToPredicate(queryPath, fieldPath, values.get(0));
} }
} }
mapEntryKeyPredicate = toMapEntryKeyPredicate(queryPath, fieldPath); mapEntryKeyPredicate = toMapEntryKeyPredicate(queryPath, fieldPath);
@@ -147,38 +144,15 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
mapEntryKeyPredicate = null; mapEntryKeyPredicate = null;
} }
final Predicate valuePredicate = toOperatorAndValuePredicate(node, queryPath, getValueFieldPath(queryPath, fieldPath), values); final Predicate valuePredicate = toOperatorAndValuePredicate(node, queryPath, fieldPath, values);
return mapEntryKeyPredicate == null ? valuePredicate : cb.and(mapEntryKeyPredicate, valuePredicate); return mapEntryKeyPredicate == null ? valuePredicate : cb.and(mapEntryKeyPredicate, valuePredicate);
} }
@SuppressWarnings("unchecked")
private Predicate toMapEntryKeyPredicate(final QueryPath queryPath, final Path<?> fieldPath) { private Predicate toMapEntryKeyPredicate(final QueryPath queryPath, final Path<?> fieldPath) {
final String[] graph = queryPath.getJpaPath(); final String[] graph = queryPath.getJpaPath();
return equal(mapEntryKeyPath(queryPath, fieldPath), graph[graph.length - 1]); return equal((Path<String>) ((MapJoin<?, ?, ?>) fieldPath).key(), graph[graph.length - 1]);
}
@SuppressWarnings("unchecked")
private Path<String> mapEntryKeyPath(final QueryPath queryPath, final Path<?> fieldPath) {
if (fieldPath instanceof MapJoin) {
// Currently we support only string key. So below cast is safe.
return (Path<String>) ((MapJoin<?, ?, ?>) fieldPath).key();
}
return fieldPath.get(queryPath.getEnumValue().getSubEntityMapTuple()
.map(Entry::getKey)
.orElseThrow(() -> new UnsupportedOperationException(String.format(
"For the fields, defined as Map, only %s java type or tuple in the form of %s are allowed!",
Map.class.getName(), AbstractMap.SimpleImmutableEntry.class.getName()))));
}
private Path<?> getValueFieldPath(final QueryPath queryPath, final Path<?> fieldPath) {
final A enumField = queryPath.getEnumValue();
if (enumField.isMap()) {
final Path<?> mapValuePath = enumField.getSubEntityMapTuple().map(Entry::getValue).map(fieldPath::get).orElse(null);
return mapValuePath == null ? fieldPath : mapValuePath;
} else {
return fieldPath;
}
} }
private Predicate toOperatorAndValuePredicate( private Predicate toOperatorAndValuePredicate(
@@ -359,20 +333,12 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
private Path<String> getExpressionToCompare(final A enumField, final Path fieldPath) { private Path<String> getExpressionToCompare(final A enumField, final Path fieldPath) {
if (!enumField.isMap()) { if (enumField.isMap()) {
return pathOfString(fieldPath);
}
if (fieldPath instanceof MapJoin) {
// Currently we support only string key. So below cast is safe. // Currently we support only string key. So below cast is safe.
return (Path<String>) (((MapJoin<?, ?, ?>) fieldPath).value()); return (Path<String>) (((MapJoin<?, ?, ?>) fieldPath).value());
} else {
return pathOfString(fieldPath);
} }
return enumField.getSubEntityMapTuple()
.map(Entry::getValue)
.map(fieldPath::<String>get)
.orElseThrow(() ->
new UnsupportedOperationException(
"For the fields, defined as Map, only Map java type or tuple in the form of SimpleImmutableEntry are allowed." +
" Neither of those could be found!"));
} }
// result is String, enum value, boolean or null // result is String, enum value, boolean or null