From 2f688144c42d7d6e71dace89cb54b8af15c6b1bf Mon Sep 17 00:00:00 2001 From: Oleh Maikovych Date: Tue, 27 Feb 2024 00:39:03 +0200 Subject: [PATCH 1/3] #2192 Adjusted value matcher util and dynamic query builder to handle problem when someone unintentionally enters trailing whitespaces --- .../platform/basic/ValueMatcherUtils.java | 6 +++--- .../review/DynamicQueryBuilder.java | 19 +++++++++++++++---- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/basic/ValueMatcherUtils.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/basic/ValueMatcherUtils.java index a418eefcc2..2c4b14b9ea 100644 --- a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/basic/ValueMatcherUtils.java +++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/basic/ValueMatcherUtils.java @@ -29,11 +29,11 @@ public static > Builder } public static ConditionModel createStrictSearchByKeyCriteriaModel(final Class> entityType, final String searchString) { - ConditionModel keyCriteria = cond().prop(KEY).iLikeWithCast().val(searchString).model(); + ConditionModel keyCriteria = cond().prop(KEY).iLikeWithCast().val(searchString.trim()).model(); if (isCompositeEntity((Class>) entityType)) { for (final String propName : keyPaths(entityType)) { - keyCriteria = cond().condition(keyCriteria).or().prop(propName).iLikeWithCast().val(searchString).model(); + keyCriteria = cond().condition(keyCriteria).or().prop(propName).iLikeWithCast().val(searchString.trim()).model(); } } @@ -41,7 +41,7 @@ public static ConditionModel createStrictSearchByKeyCriteriaModel(final Class crite return prepare(criteria); } + /** + * Creates new array based on the passed list of string. This method also trims every element of the passed list. + * + * @param criteria + * @return + */ + public static String[] prepExectCritValuesForEntityTypedProp(final List criteria) { + return criteria.stream().map(crit -> crit.trim()).toArray(String[]::new); + } + /** * Returns true if the type is supported in dynamic criteria, false otherwise. * @@ -1115,7 +1126,7 @@ private static ConditionModel propertyDescriptorLike(final String propertyNameWi final Map> searchVals = searchValues.stream().collect(groupingBy(str -> str.contains("*"))); final Set>> matchedPropDescriptors = new LinkedHashSet<>(); concat( - searchVals.getOrDefault(false, emptyList()).stream(), + stream(prepExectCritValuesForEntityTypedProp(searchVals.getOrDefault(false, emptyList()))), stream(prepCritValuesForEntityTypedProp(searchVals.getOrDefault(true, emptyList()))) ).forEach(val -> matchedPropDescriptors.addAll(new PojoValueMatcher<>(allPropertyDescriptors, KEY, allPropertyDescriptors.size()).findMatches(val))); return matchedPropDescriptors.isEmpty() @@ -1144,7 +1155,7 @@ private static ConditionModel propertyLike(final String propertyNameWithKey, fin } else if (exactAndWildcardSearchVals.containsKey(false) && !exactAndWildcardSearchVals.containsKey(true)) { // only exact search values are present return cond() // Condition for exact search values; union entities need ".id" to help EQL. - .prop(propertyNameWithoutKey + (isUnionEntityType(propType) ? ".id" : "")).in().model(select(propType).where().prop(KEY).in().values(exactAndWildcardSearchVals.get(false).toArray()).model()).model(); + .prop(propertyNameWithoutKey + (isUnionEntityType(propType) ? ".id" : "")).in().model(select(propType).where().prop(KEY).in().values(prepExectCritValuesForEntityTypedProp(exactAndWildcardSearchVals.get(false))).model()).model(); } else { // only whildcard search values are present return cond() // Condition for wildcard search values. From f1fca8d9ac12210019e4380bc980ac372381802c Mon Sep 17 00:00:00 2001 From: Oleh Maikovych Date: Wed, 28 Feb 2024 00:12:42 +0200 Subject: [PATCH 2/3] #2192 Adjusted unit tests for dynamic query builder and adjusted prepare utility method to control triming of input string. --- .../dynamic/DynamicQueryBuilderSqlTest.java | 147 +++++++++++++++++- .../review/DynamicQueryBuilder.java | 12 +- .../fielden/platform/utils/MiscUtilities.java | 13 +- .../web/utils/EntityResourceUtils.java | 4 +- .../webui/EntityAutocompletionResource.java | 2 +- 5 files changed, 162 insertions(+), 16 deletions(-) diff --git a/platform-dao/src/test/java/ua/com/fielden/platform/dao/dynamic/DynamicQueryBuilderSqlTest.java b/platform-dao/src/test/java/ua/com/fielden/platform/dao/dynamic/DynamicQueryBuilderSqlTest.java index a65f46a218..81bacf534e 100644 --- a/platform-dao/src/test/java/ua/com/fielden/platform/dao/dynamic/DynamicQueryBuilderSqlTest.java +++ b/platform-dao/src/test/java/ua/com/fielden/platform/dao/dynamic/DynamicQueryBuilderSqlTest.java @@ -1,5 +1,6 @@ package ua.com.fielden.platform.dao.dynamic; +import static java.util.Arrays.stream; import static org.junit.Assert.assertEquals; import static ua.com.fielden.platform.entity.query.fluent.EntityQueryUtils.cond; import static ua.com.fielden.platform.entity.query.fluent.EntityQueryUtils.select; @@ -54,6 +55,7 @@ import ua.com.fielden.platform.test.CommonTestEntityModuleWithPropertyFactory; import ua.com.fielden.platform.test.EntityModuleWithPropertyFactory; import ua.com.fielden.platform.types.Money; +import ua.com.fielden.platform.utils.CollectionUtil; import ua.com.fielden.platform.utils.IDates; /** @@ -557,7 +559,27 @@ public void query_composition_for_property_of_type_string_without_wildchards_in_ } @Test - public void query_composition_for_property_of_type_string_with_wildchards_in_crit_value_preserves_the_original_placement_of_wildcards_and_does_not_inject_them_at_the_beginning_and_end() { + public void query_composition_for_property_of_type_string_without_wildcards_and_with_whitespaces_in_crit_value_should_not_trim_whitespaces_and_should_automatically_injects_wildcards_at_the_beginning_and_end() { + //"entityProp.stringProp" + set_up(); + final QueryProperty property = queryProperties.get("stringProp"); + property.setValue(" Some string value "); + + final String cbn = property.getConditionBuildingName(); + + final ICompleted> expected = // + /**/iJoin.where().condition(cond() // + /* */.condition(cond().prop(cbn).isNotNull().and() // + /* */.condition(cond().prop(cbn).iLike().anyOfValues(new Object[] { "% Some string value %" }).model()) // + /* */.model()) // + /**/.model()); // + final ICompleted> actual = createQuery(masterKlass, new ArrayList<>(queryProperties.values()), dates); + + assertEquals("Incorrect query model has been built.", expected.model(), actual.model()); + } + + @Test + public void query_composition_for_property_of_type_string_with_wildcards_in_crit_value_preserves_the_original_placement_of_wildcards_and_does_not_inject_them_at_the_beginning_and_end() { //"entityProp.stringProp" set_up(); final QueryProperty property = queryProperties.get("stringProp"); @@ -578,7 +600,28 @@ public void query_composition_for_property_of_type_string_with_wildchards_in_cri } @Test - public void query_composition_for_property_of_type_string_with_and_without_wildchards_in_crit_values_retain_original_whildcards_and_autoinject_wildcards_at_the_beginning_and_end_for_values_with_no_wildcards() { + public void query_composition_for_property_of_type_string_with_wildcards_and_whitespaces_in_crit_value_should_not_trim_whitespaces_and_should_preserves_the_original_placement_of_wildcards_and_does_not_inject_them_at_the_beginning_and_end() { + //"entityProp.stringProp" + set_up(); + final QueryProperty property = queryProperties.get("stringProp"); + final String critValue = " Some string value* , *Some string value , Some string *values, *Some string value* "; + property.setValue(critValue); + + final String cbn = property.getConditionBuildingName(); + + final ICompleted> expected = // + /**/iJoin.where().condition(cond() // + /* */.condition(cond().prop(cbn).isNotNull().and() // + /* */.condition(cond().prop(cbn).iLike().anyOfValues(critValue.replace("*", "%").split(",")).model()) // + /* */.model()) // + /**/.model()); // + final ICompleted> actual = createQuery(masterKlass, new ArrayList<>(queryProperties.values()), dates); + + assertEquals("Incorrect query model has been built.", expected.model(), actual.model()); + } + + @Test + public void query_composition_for_property_of_type_string_with_and_without_wildcards_in_crit_values_retain_original_whildcards_and_autoinject_wildcards_at_the_beginning_and_end_for_values_with_no_wildcards() { //"entityProp.stringProp" set_up(); final QueryProperty property = queryProperties.get("stringProp"); @@ -597,6 +640,25 @@ public void query_composition_for_property_of_type_string_with_and_without_wildc assertEquals("Incorrect query model has been built.", expected.model(), actual.model()); } + @Test + public void query_composition_for_property_of_type_string_with_and_without_wildhards_and_with_whitespaces_in_crit_values_should_not_trim_whitespaces_and_should_retain_original_whildcards_and_autoinject_wildcards_at_the_beginning_and_end_for_values_with_no_wildcards() { + //"entityProp.stringProp" + set_up(); + final QueryProperty property = queryProperties.get("stringProp"); + property.setValue(" Some string value,*Some string value , Some string *value , *Some string* value"); + + final String cbn = property.getConditionBuildingName(); + + final ICompleted> expected = // + /**/iJoin.where().condition(cond() // + /* */.condition(cond().prop(cbn).isNotNull().and() // + /* */.condition(cond().prop(cbn).iLike().anyOfValues(new String[] {"% Some string value%", "%Some string value ", " Some string %value ", " %Some string% value"}).model()) // + /* */.model()) // + /**/.model()); // + final ICompleted> actual = createQuery(masterKlass, new ArrayList<>(queryProperties.values()), dates); + + assertEquals("Incorrect query model has been built.", expected.model(), actual.model()); + } @Test public void query_composition_for_properties_of_entity_type_with_wildcard_selection_crit_value_uses_iLike_operator() { @@ -619,6 +681,28 @@ public void query_composition_for_properties_of_entity_type_with_wildcard_select assertEquals("Incorrect query model has been built.", expected.model(), actual.model()); } + @Test + public void query_composition_for_selection_crit_of_entity_value_with_wildcards_and_whitespaces_should_trim_whitespaces() { + final String propertyName = "entityProp"; + + set_up(); + final QueryProperty property = queryProperties.get(propertyName); + property.setValue(Arrays.asList(" some val 1* ", " some val 2* ")); + + final String cbn = property.getConditionBuildingName(); + final List critValuesWithWildcard = CollectionUtil.listOf("some val 1*", "some val 2*"); + + final ICompleted> expected = // + /**/iJoin.where().condition(cond() // + /* */.condition(cond().prop(getPropertyNameWithoutKeyPart(cbn)).isNotNull().and() // + /* */.condition(cond().prop(cbn).iLike().anyOfValues(DynamicQueryBuilder.prepCritValuesForEntityTypedProp(critValuesWithWildcard)).model()) // + /* */.model()) // + /**/.model()); // + final ICompleted> actual = createQuery(masterKlass, new ArrayList<>(queryProperties.values()), dates); + + assertEquals("Incorrect query model has been built.", expected.model(), actual.model()); + } + @Test public void query_composition_for_properties_of_entity_type_without_wildcard_selection_crit_value_uses_in_operator_with_subselect() { final String propertyName = "entityProp"; @@ -643,6 +727,30 @@ public void query_composition_for_properties_of_entity_type_without_wildcard_sel assertEquals("Incorrect query model has been built.", expected.model(), actual.model()); } + @Test + public void query_composition_for_selection_crit_of_entity_value_with_whitespaces_should_trim_whitespaces() { + final String propertyName = "entityProp"; + + set_up(); + final QueryProperty property = queryProperties.get(propertyName); + final String[] critValues = new String[] {" some val 1 ", " some val 2 "}; + property.setValue(Arrays.asList(critValues)); + + + final String cbn = property.getConditionBuildingName(); + final String cbnNoKey = cbn.substring(0, cbn.length() - 4); // cut off ".key" from the name + + final ICompleted> expected = // + /**/iJoin.where().condition(cond() // + /* */.condition(cond().prop(getPropertyNameWithoutKeyPart(cbn)).isNotNull().and() // + /* */.condition(cond().prop(cbnNoKey).in().model(select(SlaveEntity.class).where().prop("key").in().values("some val 1", "some val 2").model()).model()) // + /* */.model()) // + /**/.model()); // + final ICompleted> actual = createQuery(masterKlass, new ArrayList<>(queryProperties.values()), dates); + + assertEquals("Incorrect query model has been built.", expected.model(), actual.model()); + } + @Test public void query_composition_for_properties_of_entity_type_with_and_without_wildcard_selection_crit_value_uses_combination_of_in_operator_with_subselect_and_iLike_operator() { final String propertyName = "entityProp"; @@ -678,6 +786,41 @@ public void query_composition_for_properties_of_entity_type_with_and_without_wil assertEquals("Incorrect query model has been built.", expected.model(), actual.model()); } + @Test + public void query_composition_for_selection_crit_of_entity_typed_values_with_some_wildcard_and_whitespaces_also_trims_whitespaces() { + final String propertyName = "entityProp"; + + set_up(); + final QueryProperty property = queryProperties.get(propertyName); + final String[] critValues = new String[] {"some val 1 ", " some val 2* ", " some val 3*", " some val 4",}; + property.setValue(Arrays.asList(critValues)); + + final String[] critValuesWithWildcard = new String[] {"some val 2*", "some val 3*"}; + + final String cbn = property.getConditionBuildingName(); + final String cbnNoKey = cbn.substring(0, cbn.length() - 4); // cut off ".key" from the name + + + final EntityResultQueryModel subSelect = select(SlaveEntity.class).where().prop("key").in().values("some val 1", "some val 4").model(); + final ConditionModel whereCondition = cond() + .condition( + cond().prop(getPropertyNameWithoutKeyPart(cbn)).isNotNull() + .and() + .condition( + cond().prop(cbnNoKey).in().model(subSelect) + .or().prop(cbn).iLike().anyOfValues(prepCritValuesForEntityTypedProp(Arrays.asList(critValuesWithWildcard))) + .model()) + .model()) + .model(); + + final ICompleted> expected = // + /**/iJoin.where().condition(whereCondition); + + final ICompleted> actual = createQuery(masterKlass, new ArrayList<>(queryProperties.values()), dates); + + assertEquals("Incorrect query model has been built.", expected.model(), actual.model()); + } + //////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////// 2. Property level (Negation / Null) ///////////////////// diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/entity_centre/review/DynamicQueryBuilder.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/entity_centre/review/DynamicQueryBuilder.java index fa63f84441..b9b309dc71 100644 --- a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/entity_centre/review/DynamicQueryBuilder.java +++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/entity_centre/review/DynamicQueryBuilder.java @@ -848,9 +848,9 @@ public static String[] prepCritValuesForStringTypedProp(final String criteria) { final String[] crits = criteria.split(","); for (int index = 0; index < crits.length; index++) { if (!crits[index].contains("*")) { - crits[index] = "*" + crits[index].trim() + "*"; + crits[index] = "*" + crits[index] + "*"; } - crits[index] = prepare(crits[index]); + crits[index] = prepare(crits[index], false); } return crits; } @@ -864,9 +864,9 @@ public static String[] prepCritValuesForStringTypedProp(final String criteria) { */ private static String prepCritValuesForSingleStringTypedProp(final String criteria) { if (!criteria.contains("*")) { - return prepare("*" + criteria.trim() + "*"); + return prepare("*" + criteria + "*", false); } - return prepare(criteria); + return prepare(criteria, false); } /** @@ -876,7 +876,7 @@ private static String prepCritValuesForSingleStringTypedProp(final String criter * @return */ public static String[] prepCritValuesForEntityTypedProp(final List criteria) { - return prepare(criteria); + return prepare(criteria, true); } /** @@ -1149,7 +1149,7 @@ private static ConditionModel propertyLike(final String propertyNameWithKey, fin if (exactAndWildcardSearchVals.containsKey(false) && exactAndWildcardSearchVals.containsKey(true)) { // both exact and whildcard search values are present return cond() // Condition for exact search values; union entities need ".id" to help EQL. - .prop(propertyNameWithoutKey + (isUnionEntityType(propType) ? ".id" : "")).in().model(select(propType).where().prop(KEY).in().values(exactAndWildcardSearchVals.get(false).toArray()).model()) + .prop(propertyNameWithoutKey + (isUnionEntityType(propType) ? ".id" : "")).in().model(select(propType).where().prop(KEY).in().values(prepExectCritValuesForEntityTypedProp(exactAndWildcardSearchVals.get(false))).model()) // Condition for wildcard search values. .or().prop(propertyNameWithKey).iLike().anyOfValues(prepCritValuesForEntityTypedProp(exactAndWildcardSearchVals.get(true))).model(); } else if (exactAndWildcardSearchVals.containsKey(false) && !exactAndWildcardSearchVals.containsKey(true)) { // only exact search values are present diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/utils/MiscUtilities.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/utils/MiscUtilities.java index cb3af598f5..47d1e2ce5f 100644 --- a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/utils/MiscUtilities.java +++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/utils/MiscUtilities.java @@ -56,13 +56,14 @@ public String getDescription() { * Creates a new array of values based on the passed list by changing * to %. * * @param criteria + * @param trim - indicates whether elements of criteria list parameter should be trimmed or not. * @return */ - public static String[] prepare(final List criteria) { + public static String[] prepare(final List criteria, final boolean trim) { final List result = new ArrayList<>(); if (criteria != null) { for (final String crit : criteria) { - result.add(prepare(crit)); + result.add(prepare(crit, trim)); } } // eliminate empty or null values @@ -96,13 +97,15 @@ public static boolean valueMatchesPattern(final String value, final String value * Converts auto-completer-like regular expression to normal regular expression (simply replaces all '*' with '%' characters) * * @param autocompleterExp + * @param trim - indicates whether autocompleterExp parameter should be trimmed or not. * @return */ - public static String prepare(final String autocompleterExp) { - if ("*".equals(autocompleterExp.trim())) { + public static String prepare(final String autocompleterExp, final boolean trim) { + if ("*".equals(trim ? autocompleterExp.trim(): autocompleterExp)) { return null; } - return autocompleterExp.replace("*", "%").trim(); + final String processedExpr = autocompleterExp.replace("*", "%"); + return trim ? processedExpr.trim(): processedExpr; } /** diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/web/utils/EntityResourceUtils.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/web/utils/EntityResourceUtils.java index 2295e8a5df..286d8372c0 100644 --- a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/web/utils/EntityResourceUtils.java +++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/web/utils/EntityResourceUtils.java @@ -762,7 +762,7 @@ private static > Object convert(final Class type, public static AbstractEntity findAndFetchBy(final String searchString, final Class> entityType, final Optional optActiveProp, final fetch> fetch, final IEntityDao> companion) { if (isCompositeEntity(entityType)) { //logger.debug(format("KEY-based restoration of value: type [%s] property [%s] propertyType [%s] id [%s] reflectedValue [%s].", type.getSimpleName(), propertyName, entityPropertyType.getSimpleName(), reflectedValueId, reflectedValue)); - final String compositeKeyAsString = MiscUtilities.prepare(prepSearchStringForCompositeKey(entityType, searchString)); + final String compositeKeyAsString = MiscUtilities.prepare(prepSearchStringForCompositeKey(entityType, searchString), true); final EntityResultQueryModel> model = select(entityType).where().prop(KEY).iLike().val(compositeKeyAsString).model().setFilterable(true); final QueryExecutionModel, EntityResultQueryModel>> qem = from(model).with(fetch).model(); try { @@ -798,7 +798,7 @@ public static AbstractEntity findAndFetchBy(final String searchString, final } } else { //logger.debug(format("KEY-based restoration of value: type [%s] property [%s] propertyType [%s] id [%s] reflectedValue [%s].", type.getSimpleName(), propertyName, entityPropertyType.getSimpleName(), reflectedValueId, reflectedValue)); - final String[] keys = MiscUtilities.prepare(Arrays.asList(searchString)); + final String[] keys = MiscUtilities.prepare(Arrays.asList(searchString), true); final String key; if (keys.length > 1) { throw new IllegalArgumentException(format("Value [%s] does not represent a single key value, which is required for coversion to an instance of type [%s].", searchString, entityType.getName())); diff --git a/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/EntityAutocompletionResource.java b/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/EntityAutocompletionResource.java index c8a8d6afd9..049ab5400a 100644 --- a/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/EntityAutocompletionResource.java +++ b/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/EntityAutocompletionResource.java @@ -151,7 +151,7 @@ public Representation post(final Representation envelope) { */ public static T2 prepSearchString(final CentreContextHolder centreContextHolder, final boolean shouldUpperCase) { final String searchStringVal = (String) centreContextHolder.getCustomObject().get("@@searchString"); // custom property inside paramsHolder - final Optional maybeSearchString = ofNullable(prepare(searchStringVal.contains("*") || searchStringVal.contains("%") ? searchStringVal : "*" + searchStringVal + "*")); + final Optional maybeSearchString = ofNullable(prepare(searchStringVal.contains("*") || searchStringVal.contains("%") ? searchStringVal : "*" + searchStringVal + "*", true)); final String searchString = maybeSearchString.map(str -> shouldUpperCase ? str.toUpperCase() : str).orElse("%"); final Optional maybeDataPage = ofNullable((Integer) centreContextHolder.getCustomObject().get("@@dataPage")); final int dataPageNo = maybeDataPage.orElse(1); From d9c25df19affa139fdbfc2dc9954b93abb0f53f4 Mon Sep 17 00:00:00 2001 From: Oleh Maikovych Date: Wed, 28 Feb 2024 16:21:35 +0200 Subject: [PATCH 3/3] #2192 Adjusted entity editor web component and autocompleter resource to trim just search from entity editors. Also adjusted for preparing serach criterias to separate entity editors search criteira from string search criterias. --- .../platform/basic/ValueMatcherUtils.java | 6 ++--- .../review/DynamicQueryBuilder.java | 9 ++++--- .../fielden/platform/utils/MiscUtilities.java | 26 +++++++++++++------ .../web/utils/EntityResourceUtils.java | 10 ++++--- .../webui/EntityAutocompletionResource.java | 2 +- .../platform/web/editors/tg-entity-editor.js | 2 +- 6 files changed, 34 insertions(+), 21 deletions(-) diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/basic/ValueMatcherUtils.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/basic/ValueMatcherUtils.java index 2c4b14b9ea..a418eefcc2 100644 --- a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/basic/ValueMatcherUtils.java +++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/basic/ValueMatcherUtils.java @@ -29,11 +29,11 @@ public static > Builder } public static ConditionModel createStrictSearchByKeyCriteriaModel(final Class> entityType, final String searchString) { - ConditionModel keyCriteria = cond().prop(KEY).iLikeWithCast().val(searchString.trim()).model(); + ConditionModel keyCriteria = cond().prop(KEY).iLikeWithCast().val(searchString).model(); if (isCompositeEntity((Class>) entityType)) { for (final String propName : keyPaths(entityType)) { - keyCriteria = cond().condition(keyCriteria).or().prop(propName).iLikeWithCast().val(searchString.trim()).model(); + keyCriteria = cond().condition(keyCriteria).or().prop(propName).iLikeWithCast().val(searchString).model(); } } @@ -41,7 +41,7 @@ public static ConditionModel createStrictSearchByKeyCriteriaModel(final Class criteria) { - return prepare(criteria, true); + return prepare(criteria); } /** diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/utils/MiscUtilities.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/utils/MiscUtilities.java index 47d1e2ce5f..a7798026f8 100644 --- a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/utils/MiscUtilities.java +++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/utils/MiscUtilities.java @@ -56,14 +56,13 @@ public String getDescription() { * Creates a new array of values based on the passed list by changing * to %. * * @param criteria - * @param trim - indicates whether elements of criteria list parameter should be trimmed or not. * @return */ - public static String[] prepare(final List criteria, final boolean trim) { + public static String[] prepare(final List criteria) { final List result = new ArrayList<>(); if (criteria != null) { for (final String crit : criteria) { - result.add(prepare(crit, trim)); + result.add(prepare(crit)); } } // eliminate empty or null values @@ -97,15 +96,26 @@ public static boolean valueMatchesPattern(final String value, final String value * Converts auto-completer-like regular expression to normal regular expression (simply replaces all '*' with '%' characters) * * @param autocompleterExp - * @param trim - indicates whether autocompleterExp parameter should be trimmed or not. * @return */ - public static String prepare(final String autocompleterExp, final boolean trim) { - if ("*".equals(trim ? autocompleterExp.trim(): autocompleterExp)) { + public static String prepare(final String autocompleterExp) { + if ("*".equals(autocompleterExp.trim())) { return null; } - final String processedExpr = autocompleterExp.replace("*", "%"); - return trim ? processedExpr.trim(): processedExpr; + return autocompleterExp.replace("*", "%").trim(); + } + + /** + * Converts auto-completer-like regular expression for string typed property into normal regular expression (simply replaces all '*' with '%' characters) without trimming it + * + * @param stringExpr - The expression for string property + * @return converted into SQL like regular expression without trimming stringExpr + */ + public static String prepareStringExpression(final String stringExpr) { + if ("*".equals(stringExpr)) { + return null; + } + return stringExpr.replace("*", "%"); } /** diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/web/utils/EntityResourceUtils.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/web/utils/EntityResourceUtils.java index 286d8372c0..0db32c3263 100644 --- a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/web/utils/EntityResourceUtils.java +++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/web/utils/EntityResourceUtils.java @@ -2,6 +2,7 @@ import static java.lang.Class.forName; import static java.lang.String.format; +import static java.util.Arrays.asList; import static java.util.Locale.getDefault; import static java.util.Optional.empty; import static java.util.Optional.of; @@ -24,6 +25,7 @@ import static ua.com.fielden.platform.utils.EntityUtils.isCompositeEntity; import static ua.com.fielden.platform.utils.EntityUtils.isEntityType; import static ua.com.fielden.platform.utils.EntityUtils.isUnionEntityType; +import static ua.com.fielden.platform.utils.MiscUtilities.prepare; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; @@ -258,7 +260,7 @@ public static > M apply(final Map mo */ @SuppressWarnings("unused") private static > void logPropertyApplication(final String actionCaption, final boolean apply, final boolean shortLog, final Class type, final String name, final boolean isEntityStale, final Map valAndOrigVal, final M entity, final String... propertiesToLogArray) { - final Set propertiesToLog = new LinkedHashSet<>(Arrays.asList(propertiesToLogArray)); + final Set propertiesToLog = new LinkedHashSet<>(asList(propertiesToLogArray)); if (propertiesToLog.contains(name)) { final StringBuilder builder = new StringBuilder(actionCaption); builder.append(":\t"); @@ -611,7 +613,7 @@ private static Class determinePropertyType(final Class type, final String } final ParameterizedType parameterizedEntityType = (ParameterizedType) type.getAnnotatedSuperclass().getType(); if (parameterizedEntityType.getActualTypeArguments().length != 1 || !(parameterizedEntityType.getActualTypeArguments()[0] instanceof Class)) { - throw Result.failure(new IllegalStateException(format("The type parameters %s of functional entity %s (for collection modification) is malformed.", Arrays.asList(parameterizedEntityType.getActualTypeArguments()), type.getSimpleName()))); + throw Result.failure(new IllegalStateException(format("The type parameters %s of functional entity %s (for collection modification) is malformed.", asList(parameterizedEntityType.getActualTypeArguments()), type.getSimpleName()))); } propertyType = (Class) parameterizedEntityType.getActualTypeArguments()[0]; } else { @@ -762,7 +764,7 @@ private static > Object convert(final Class type, public static AbstractEntity findAndFetchBy(final String searchString, final Class> entityType, final Optional optActiveProp, final fetch> fetch, final IEntityDao> companion) { if (isCompositeEntity(entityType)) { //logger.debug(format("KEY-based restoration of value: type [%s] property [%s] propertyType [%s] id [%s] reflectedValue [%s].", type.getSimpleName(), propertyName, entityPropertyType.getSimpleName(), reflectedValueId, reflectedValue)); - final String compositeKeyAsString = MiscUtilities.prepare(prepSearchStringForCompositeKey(entityType, searchString), true); + final String compositeKeyAsString = prepare(prepSearchStringForCompositeKey(entityType, searchString)); final EntityResultQueryModel> model = select(entityType).where().prop(KEY).iLike().val(compositeKeyAsString).model().setFilterable(true); final QueryExecutionModel, EntityResultQueryModel>> qem = from(model).with(fetch).model(); try { @@ -798,7 +800,7 @@ public static AbstractEntity findAndFetchBy(final String searchString, final } } else { //logger.debug(format("KEY-based restoration of value: type [%s] property [%s] propertyType [%s] id [%s] reflectedValue [%s].", type.getSimpleName(), propertyName, entityPropertyType.getSimpleName(), reflectedValueId, reflectedValue)); - final String[] keys = MiscUtilities.prepare(Arrays.asList(searchString), true); + final String[] keys = prepare(asList(searchString)); final String key; if (keys.length > 1) { throw new IllegalArgumentException(format("Value [%s] does not represent a single key value, which is required for coversion to an instance of type [%s].", searchString, entityType.getName())); diff --git a/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/EntityAutocompletionResource.java b/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/EntityAutocompletionResource.java index 049ab5400a..7b8e1b69f9 100644 --- a/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/EntityAutocompletionResource.java +++ b/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/EntityAutocompletionResource.java @@ -151,7 +151,7 @@ public Representation post(final Representation envelope) { */ public static T2 prepSearchString(final CentreContextHolder centreContextHolder, final boolean shouldUpperCase) { final String searchStringVal = (String) centreContextHolder.getCustomObject().get("@@searchString"); // custom property inside paramsHolder - final Optional maybeSearchString = ofNullable(prepare(searchStringVal.contains("*") || searchStringVal.contains("%") ? searchStringVal : "*" + searchStringVal + "*", true)); + final Optional maybeSearchString = ofNullable(prepare(searchStringVal.contains("*") || searchStringVal.contains("%") ? searchStringVal.trim() : "*" + searchStringVal.trim() + "*")); final String searchString = maybeSearchString.map(str -> shouldUpperCase ? str.toUpperCase() : str).orElse("%"); final Optional maybeDataPage = ofNullable((Integer) centreContextHolder.getCustomObject().get("@@dataPage")); final int dataPageNo = maybeDataPage.orElse(1); diff --git a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/tg-entity-editor.js b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/tg-entity-editor.js index 72bbf37c07..03a5d94219 100644 --- a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/tg-entity-editor.js +++ b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/tg-entity-editor.js @@ -780,7 +780,7 @@ export class TgEntityEditor extends TgEditor { let inputText = ''; // default value if (this.multi === false) { // assign the actual search string - inputText = ignoreInputText === true ? defaultSearchQuery : this._prepInput(this.decoratedInput().value) || defaultSearchQuery; + inputText = ignoreInputText === true ? defaultSearchQuery : this._prepInput(this.decoratedInput().value.trim()) || defaultSearchQuery; } else { // The following manipulations with indexes are required in case of multi selection // in order to determine what part of the input text should be used for search and