This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a commit to branch 4_0_X
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/4_0_X by this push:
     new b35f8309de [SYNCOPE-1882] Fixing multivalue dropdown attribute check + 
SearchClausePanel improvements
b35f8309de is described below

commit b35f8309de7227fd53211b1c23564c0774eaf017
Author: Francesco Chicchiriccò <[email protected]>
AuthorDate: Thu May 15 11:14:40 2025 +0200

    [SYNCOPE-1882] Fixing multivalue dropdown attribute check + 
SearchClausePanel improvements
---
 .../commons/markup/html/form/AjaxPalettePanel.java |   8 +-
 .../client/ui/commons/panels/SyncopeFormPanel.java |   2 +-
 .../console/panels/search/AbstractSearchPanel.java |   7 +-
 .../console/panels/search/SearchClausePanel.java   | 723 ++++++++++-----------
 .../client/console/rest/SchemaRestClient.java      |  26 +-
 .../client/console/wizards/any/PlainAttrs.java     |   4 +-
 .../client/enduser/panels/any/PlainAttrs.java      |  22 +-
 pom.xml                                            |   4 +-
 8 files changed, 390 insertions(+), 406 deletions(-)

diff --git 
a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AjaxPalettePanel.java
 
b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AjaxPalettePanel.java
index eccd2d51a4..723d813209 100644
--- 
a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AjaxPalettePanel.java
+++ 
b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AjaxPalettePanel.java
@@ -158,11 +158,9 @@ public class AjaxPalettePanel<T extends Serializable> 
extends AbstractFieldPanel
 
                         List<String> ids = 
builder.idExtractor.apply(getValue()).toList();
                         List<T> unselected = new ArrayList<>(choices.size());
-                        choices.forEach(choice -> {
-                            if (!ids.contains(renderer.getIdValue(choice, 0))) 
{
-                                unselected.add(choice);
-                            }
-                        });
+                        choices.stream().
+                                filter(choice -> 
!ids.contains(renderer.getIdValue(choice, 0))).
+                                forEach(unselected::add);
 
                         return unselected;
                     }
diff --git 
a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/panels/SyncopeFormPanel.java
 
b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/panels/SyncopeFormPanel.java
index cf8d4a6b25..386436fbd8 100644
--- 
a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/panels/SyncopeFormPanel.java
+++ 
b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/panels/SyncopeFormPanel.java
@@ -165,7 +165,7 @@ public class SyncopeFormPanel<F extends SyncopeForm> 
extends Panel {
                                 public List<String> getObject() {
                                     return 
Optional.ofNullable(prop.getValue()).
                                             map(v -> List.of(v.split(";"))).
-                                        orElseGet(List::of);
+                                            orElseGet(List::of);
                                 }
 
                                 @Override
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.java
index e3185493bf..c399c170ef 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.java
@@ -247,9 +247,10 @@ public abstract class AbstractSearchPanel extends Panel {
             protected Map<String, PlainSchemaTO> load() {
                 Map<String, PlainSchemaTO> dSchemaNames = new HashMap<>();
                 SearchableFields.get(typeKind.getTOClass()).forEach((key, 
type) -> {
-                    PlainSchemaTO plain = new PlainSchemaTO();
-                    plain.setType(type);
-                    dSchemaNames.put(key, plain);
+                    PlainSchemaTO plainSchema = new PlainSchemaTO();
+                    plainSchema.setType(type);
+                    plainSchema.setKey(key);
+                    dSchemaNames.put(key, plainSchema);
                 });
                 return dSchemaNames;
             }
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClausePanel.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClausePanel.java
index b68f0168ef..96262d306c 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClausePanel.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClausePanel.java
@@ -22,7 +22,6 @@ import 
de.agilecoders.wicket.extensions.markup.html.bootstrap.form.checkbox.boot
 import 
de.agilecoders.wicket.extensions.markup.html.bootstrap.form.checkbox.bootstraptoggle.BootstrapToggleConfig;
 import java.io.Serializable;
 import java.text.ParseException;
-import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
 import java.util.Locale;
@@ -121,18 +120,223 @@ public class SearchClausePanel extends 
FieldPanel<SearchClause> {
             return List.of();
         }
 
-        default void setFieldAccess(
-                FieldPanel<String> value,
+        default void adjust(
+                Type type,
                 AjaxTextFieldPanel property,
+                FieldPanel<Comparator> comparator,
+                FieldPanel<?> value,
                 LoadableDetachableModel<List<Pair<String, String>>> 
properties) {
 
             value.setEnabled(true);
-            value.setModelObject(StringUtils.EMPTY);
+            value.setModelObject(null);
             property.setEnabled(true);
 
             // reload properties list
             properties.detach();
-            
property.setChoices(properties.getObject().stream().map(Pair::getKey).collect(Collectors.toList()));
+            
property.setChoices(properties.getObject().stream().map(Pair::getKey).toList());
+        }
+    }
+
+    protected class ComparatorRenderer implements 
IChoiceRenderer<SearchClause.Comparator> {
+
+        private static final long serialVersionUID = -9086043750227867686L;
+
+        @Override
+        public Object getDisplayValue(final SearchClause.Comparator object) {
+            if (clause == null || clause.getObject() == null || 
clause.getObject().getType() == null) {
+                return object.toString();
+            }
+
+            String display;
+
+            switch (clause.getObject().getType()) {
+                case ATTRIBUTE:
+                    switch (object) {
+                        case IS_NULL:
+                            display = "NULL";
+                            break;
+
+                        case IS_NOT_NULL:
+                            display = "NOT NULL";
+                            break;
+
+                        case EQUALS:
+                            display = "==";
+                            break;
+
+                        case NOT_EQUALS:
+                            display = "!=";
+                            break;
+
+                        case LESS_THAN:
+                            display = "<";
+                            break;
+
+                        case LESS_OR_EQUALS:
+                            display = "<=";
+                            break;
+
+                        case GREATER_THAN:
+                            display = ">";
+                            break;
+
+                        case GREATER_OR_EQUALS:
+                            display = ">=";
+                            break;
+
+                        default:
+                            display = StringUtils.EMPTY;
+                    }
+                    break;
+
+                case GROUP_MEMBERSHIP:
+                    switch (object) {
+                        case EQUALS:
+                            display = "IN";
+                            break;
+
+                        case NOT_EQUALS:
+                            display = "NOT IN";
+                            break;
+
+                        default:
+                            display = StringUtils.EMPTY;
+                    }
+                    break;
+
+                case GROUP_MEMBER:
+                    switch (object) {
+                        case EQUALS:
+                            display = "WITH";
+                            break;
+
+                        case NOT_EQUALS:
+                            display = "WITHOUT";
+                            break;
+
+                        default:
+                            display = StringUtils.EMPTY;
+                    }
+                    break;
+
+                case AUX_CLASS:
+                case ROLE_MEMBERSHIP:
+                case RESOURCE:
+                    switch (object) {
+                        case EQUALS:
+                            display = "HAS";
+                            break;
+
+                        case NOT_EQUALS:
+                            display = "HAS NOT";
+                            break;
+
+                        default:
+                            display = StringUtils.EMPTY;
+                    }
+                    break;
+
+                case RELATIONSHIP:
+                    switch (object) {
+                        case IS_NOT_NULL:
+                            display = "EXIST";
+                            break;
+
+                        case IS_NULL:
+                            display = "NOT EXIST";
+                            break;
+
+                        case EQUALS:
+                            display = "WITH";
+                            break;
+
+                        case NOT_EQUALS:
+                            display = "WITHOUT";
+                            break;
+
+                        default:
+                            display = StringUtils.EMPTY;
+                    }
+                    break;
+
+                case CUSTOM:
+                    display = customizer.comparatorDisplayValue(object);
+                    break;
+
+                default:
+                    display = object.toString();
+            }
+            return display;
+        }
+
+        @Override
+        public String getIdValue(final SearchClause.Comparator object, final 
int index) {
+            return getDisplayValue(object).toString();
+        }
+
+        @Override
+        public SearchClause.Comparator getObject(
+                final String id, final IModel<? extends List<? extends 
SearchClause.Comparator>> choices) {
+
+            if (id == null) {
+                return SearchClause.Comparator.EQUALS;
+            }
+
+            final SearchClause.Comparator comparator;
+            switch (id) {
+                case "HAS":
+                case "IN":
+                case "WITH":
+                    comparator = SearchClause.Comparator.EQUALS;
+                    break;
+
+                case "HAS NOT":
+                case "NOT IN":
+                case "WITHOUT":
+                    comparator = SearchClause.Comparator.NOT_EQUALS;
+                    break;
+
+                case "NULL":
+                case "NOT EXIST":
+                    comparator = SearchClause.Comparator.IS_NULL;
+                    break;
+
+                case "NOT NULL":
+                case "EXIST":
+                    comparator = SearchClause.Comparator.IS_NOT_NULL;
+                    break;
+
+                case "==":
+                    comparator = SearchClause.Comparator.EQUALS;
+                    break;
+
+                case "!=":
+                    comparator = SearchClause.Comparator.NOT_EQUALS;
+                    break;
+
+                case "<":
+                    comparator = SearchClause.Comparator.LESS_THAN;
+                    break;
+
+                case "<=":
+                    comparator = SearchClause.Comparator.LESS_OR_EQUALS;
+                    break;
+
+                case ">":
+                    comparator = SearchClause.Comparator.GREATER_THAN;
+                    break;
+
+                case ">=":
+                    comparator = SearchClause.Comparator.GREATER_OR_EQUALS;
+                    break;
+
+                default:
+                    // EQUALS to be used as default value
+                    comparator = 
customizer.comparatorGetObject(id).orElse(SearchClause.Comparator.EQUALS);
+                    break;
+            }
+
+            return comparator;
         }
     }
 
@@ -174,8 +378,7 @@ public class SearchClausePanel extends 
FieldPanel<SearchClause> {
 
     protected IEventSink resultContainer;
 
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    private FieldPanel value;
+    private FieldPanel<?> value;
 
     public SearchClausePanel(
             final String id,
@@ -193,7 +396,7 @@ public class SearchClausePanel extends 
FieldPanel<SearchClause> {
 
         super(id, name, clause);
 
-        this.clause = clause == null ? new Model<>(null) : clause;
+        this.clause = Optional.ofNullable(clause).orElseGet(() -> new 
Model<>(null));
 
         this.required = required;
         this.types = types;
@@ -211,11 +414,9 @@ public class SearchClausePanel extends 
FieldPanel<SearchClause> {
 
             @Override
             public void onClick(final AjaxRequestTarget target) {
-                if (resultContainer == null) {
-                    send(SearchClausePanel.this, Broadcast.BUBBLE, new 
SearchEvent(target));
-                } else {
-                    send(resultContainer, Broadcast.EXACT, new 
SearchEvent(target));
-                }
+                Optional.ofNullable(resultContainer).ifPresentOrElse(
+                        container -> send(container, Broadcast.EXACT, new 
SearchEvent(target)),
+                        () -> send(SearchClausePanel.this, Broadcast.BUBBLE, 
new SearchEvent(target)));
             }
         };
 
@@ -281,7 +482,7 @@ public class SearchClausePanel extends 
FieldPanel<SearchClause> {
                 }
 
                 switch (field.getModel().getObject().getType()) {
-                    case ATTRIBUTE:
+                    case ATTRIBUTE -> {
                         Locale locale = 
SyncopeConsoleSession.get().getLocale();
                         List<Pair<String, String>> names = 
dnames.getObject().entrySet().stream().
                                 map(item -> Pair.of(
@@ -293,42 +494,49 @@ public class SearchClausePanel extends 
FieldPanel<SearchClause> {
                                     map(item -> Pair.of(
                                     item.getKey(),
                                     
Optional.ofNullable(item.getValue().getLabel(locale)).orElseGet(item::getKey))).
-                                toList());
+                                    toList());
                         }
                         return names.stream().
                                 sorted(java.util.Comparator.comparing(name -> 
name.getValue().toLowerCase())).
                                 collect(Collectors.toList());
+                    }
 
-                    case GROUP_MEMBERSHIP:
-                        return groupInfo.getLeft().getObject().stream().
-                                map(item -> Pair.of(item, 
item)).collect(Collectors.toList());
+                    case GROUP_MEMBERSHIP -> {
+                        return 
groupInfo.getLeft().getObject().stream().map(item -> Pair.of(item, 
item)).toList();
+                    }
 
-                    case ROLE_MEMBERSHIP:
+                    case ROLE_MEMBERSHIP -> {
                         return Optional.ofNullable(roleNames).
                                 map(r -> 
r.getObject().stream().sorted().map(item -> Pair.of(item, item)).
                                 collect(Collectors.toList())).
-                            orElseGet(List::of);
+                                orElseGet(List::of);
+                    }
 
-                    case AUX_CLASS:
+                    case AUX_CLASS -> {
                         return auxClassNames.getObject().stream().sorted().
-                                map(item -> Pair.of(item, 
item)).collect(Collectors.toList());
+                                map(item -> Pair.of(item, item)).toList();
+                    }
 
-                    case RESOURCE:
+                    case RESOURCE -> {
                         return resourceNames.getObject().stream().sorted().
-                                map(item -> Pair.of(item, 
item)).collect(Collectors.toList());
+                                map(item -> Pair.of(item, item)).toList();
+                    }
 
-                    case RELATIONSHIP:
-                        return 
relationshipTypeRestClient.list().stream().sorted().
+                    case RELATIONSHIP -> {
+                        return relationshipTypeRestClient.list().stream().
                                 map(item -> Pair.of(item.getKey(), 
item.getKey())).
-                                collect(Collectors.toList());
+                                sorted().toList();
+                    }
 
-                    case CUSTOM:
+                    case CUSTOM -> {
                         return customizer.properties().stream().
                                 map(item -> Pair.of(item, item)).
                                 collect(Collectors.toList());
+                    }
 
-                    default:
+                    default -> {
                         return List.of();
+                    }
                 }
             }
         };
@@ -386,7 +594,6 @@ public class SearchClausePanel extends 
FieldPanel<SearchClause> {
     }
 
     @Override
-    @SuppressWarnings({ "rawtypes", "unchecked" })
     public FieldPanel<SearchClause> settingsDependingComponents() {
         SearchClause searchClause = this.clause.getObject();
 
@@ -448,8 +655,8 @@ public class SearchClausePanel extends 
FieldPanel<SearchClause> {
             operatorContainer.add(searchButtonFragment);
         }
 
-        AjaxTextFieldPanel property = new AjaxTextFieldPanel("property", 
"property",
-                new PropertyModel<>(searchClause, "property"), true) {
+        AjaxTextFieldPanel property = new AjaxTextFieldPanel(
+                "property", "property", new PropertyModel<>(searchClause, 
"property")) {
 
             private static final long serialVersionUID = -7157802546272668001L;
 
@@ -479,10 +686,8 @@ public class SearchClausePanel extends 
FieldPanel<SearchClause> {
                 });
             }
         };
-
-        property.hideLabel().setOutputMarkupId(true).setEnabled(true);
-        
property.setChoices(properties.getObject().stream().map(Pair::getValue).collect(Collectors.toList()));
-        field.add(property);
+        
property.setChoices(properties.getObject().stream().map(Pair::getValue).toList());
+        
field.add(property.hideLabel().setOutputMarkupId(true).setEnabled(true));
 
         property.getField().add(PREVENT_DEFAULT_RETURN);
         property.getField().add(new 
IndicatorAjaxEventBehavior(Constants.ON_KEYUP) {
@@ -496,8 +701,7 @@ public class SearchClausePanel extends 
FieldPanel<SearchClause> {
 
                     String[] inputAsArray = 
property.getField().getInputAsArray();
                     if (ArrayUtils.isEmpty(inputAsArray)) {
-                        
property.setChoices(properties.getObject().stream().map(Pair::getKey)
-                                .collect(Collectors.toList()));
+                        
property.setChoices(properties.getObject().stream().map(Pair::getKey).toList());
                     } else if (groupInfo.getRight().getObject() > 
Constants.MAX_GROUP_LIST_SIZE) {
                         String inputValue = inputAsArray.length > 1 && 
inputAsArray[1] != null
                                 ? inputAsArray[1]
@@ -516,7 +720,7 @@ public class SearchClausePanel extends 
FieldPanel<SearchClause> {
                                 1,
                                 Constants.MAX_GROUP_LIST_SIZE,
                                 new SortParam<>(Constants.NAME_FIELD_NAME, 
true),
-                                
null).stream().map(GroupTO::getName).collect(Collectors.toList()));
+                                null).stream().map(GroupTO::getName).toList());
                     }
                 }
             }
@@ -548,21 +752,25 @@ public class SearchClausePanel extends 
FieldPanel<SearchClause> {
         comparator.setChoices(comparators);
         comparator.setNullValid(false).hideLabel().setOutputMarkupId(true);
         comparator.setRequired(required);
-        comparator.setChoiceRenderer(getComparatorRender(field.getModel()));
+        comparator.setChoiceRenderer(new ComparatorRenderer());
         field.add(comparator);
 
-        renderSearchValueField(searchClause, property);
+        value = buildValue(searchClause, property);
         field.addOrReplace(value);
 
+        adjust(searchClause.getType(), property, comparator);
+
         property.getField().add(new 
IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
 
             private static final long serialVersionUID = -1107858522700306810L;
 
             @Override
             protected void onUpdate(final AjaxRequestTarget target) {
-                renderSearchValueField(searchClause, property);
+                value = buildValue(searchClause, property);
                 field.addOrReplace(value);
                 target.add(value);
+
+                adjust(searchClause.getType(), property, comparator);
             }
         });
 
@@ -584,7 +792,7 @@ public class SearchClausePanel extends 
FieldPanel<SearchClause> {
                 }
                 SearchClausePanel.this.clause.setObject(searchClause);
 
-                setFieldAccess(searchClause.getType(), property, comparator, 
value);
+                adjust(searchClause.getType(), property, comparator);
 
                 // reset property value in case and just in case of change of 
type
                 property.setModelObject(StringUtils.EMPTY);
@@ -608,7 +816,7 @@ public class SearchClausePanel extends 
FieldPanel<SearchClause> {
                     if (comparator.getModelObject() == 
SearchClause.Comparator.IS_NULL
                             || comparator.getModelObject() == 
SearchClause.Comparator.IS_NOT_NULL) {
 
-                        value.setModelObject(StringUtils.EMPTY);
+                        value.setModelObject(null);
                         value.setEnabled(false);
                     } else {
                         value.setEnabled(true);
@@ -629,373 +837,154 @@ public class SearchClausePanel extends 
FieldPanel<SearchClause> {
             }
         });
 
-        setFieldAccess(searchClause.getType(), property, comparator, value);
-
         return this;
     }
 
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    private void setFieldAccess(
-            final Type type,
-            final AjaxTextFieldPanel property,
-            final FieldPanel<Comparator> comparator,
-            final FieldPanel value) {
-
-        if (type != null) {
-            property.setEnabled(true);
-            comparator.setEnabled(true);
-            value.setEnabled(true);
-
-            switch (type) {
-                case ATTRIBUTE:
-                    if (!comparator.isEnabled()) {
-                        comparator.setEnabled(true);
-                        comparator.setRequired(true);
-                    }
-
-                    if (comparator.getModelObject() == 
SearchClause.Comparator.IS_NULL
-                            || comparator.getModelObject() == 
SearchClause.Comparator.IS_NOT_NULL) {
-                        value.setEnabled(false);
-                        value.setModelObject(StringUtils.EMPTY);
-                    }
-
-                    // reload properties list
-                    properties.detach();
-                    property.setChoices(
-                            properties.getObject().stream().
-                                    
map(Pair::getValue).collect(Collectors.toList()));
-                    break;
-
-                case ROLE_MEMBERSHIP:
-                    value.setEnabled(false);
-                    value.setModelObject(StringUtils.EMPTY);
-
-                    // reload properties list
-                    properties.detach();
-                    
property.setChoices(properties.getObject().stream().map(Pair::getKey).collect(Collectors.toList()));
-                    break;
-
-                case GROUP_MEMBERSHIP:
-                    value.setEnabled(false);
-                    value.setModelObject(StringUtils.EMPTY);
-
-                    // reload properties list
-                    properties.detach();
-                    
property.setChoices(properties.getObject().stream().map(Pair::getKey).collect(Collectors.toList()));
-                    break;
-
-                case GROUP_MEMBER:
-                    value.setEnabled(true);
-                    property.setEnabled(false);
-                    property.setModelObject(StringUtils.EMPTY);
-                    break;
-
-                case AUX_CLASS:
-                case RESOURCE:
-                    value.setEnabled(false);
-                    value.setModelObject(StringUtils.EMPTY);
-
-                    // reload properties list
-                    properties.detach();
-                    
property.setChoices(properties.getObject().stream().map(Pair::getKey).collect(Collectors.toList()));
-                    break;
-
-                case RELATIONSHIP:
-                    value.setEnabled(true);
-                    value.setModelObject(StringUtils.EMPTY);
-                    property.setEnabled(true);
-
-                    // reload properties list
-                    properties.detach();
-                    
property.setChoices(properties.getObject().stream().map(Pair::getKey).collect(Collectors.toList()));
-                    break;
-
-                case CUSTOM:
-                    customizer.setFieldAccess(value, property, properties);
-                    break;
-
-                default:
-                    break;
-            }
+    @SuppressWarnings("unchecked")
+    protected void adjust(final Type type, final AjaxTextFieldPanel property, 
final FieldPanel<Comparator> comparator) {
+        if (type == null) {
+            return;
         }
-    }
-
-    private IChoiceRenderer<SearchClause.Comparator> getComparatorRender(final 
IModel<SearchClause> clause) {
-        return new IChoiceRenderer<>() {
 
-            private static final long serialVersionUID = -9086043750227867686L;
+        property.setEnabled(true);
+        comparator.setEnabled(true);
+        value.setEnabled(true);
 
-            @Override
-            public Object getDisplayValue(final SearchClause.Comparator 
object) {
-                if (clause == null || clause.getObject() == null || 
clause.getObject().getType() == null) {
-                    return object.toString();
+        switch (type) {
+            case ATTRIBUTE -> {
+                if (!comparator.isEnabled()) {
+                    comparator.setEnabled(true);
+                    comparator.setRequired(true);
                 }
 
-                String display;
-
-                switch (clause.getObject().getType()) {
-                    case ATTRIBUTE:
-                        switch (object) {
-                            case IS_NULL:
-                                display = "NULL";
-                                break;
-
-                            case IS_NOT_NULL:
-                                display = "NOT NULL";
-                                break;
-
-                            case EQUALS:
-                                display = "==";
-                                break;
-
-                            case NOT_EQUALS:
-                                display = "!=";
-                                break;
-
-                            case LESS_THAN:
-                                display = "<";
-                                break;
-
-                            case LESS_OR_EQUALS:
-                                display = "<=";
-                                break;
-
-                            case GREATER_THAN:
-                                display = ">";
-                                break;
-
-                            case GREATER_OR_EQUALS:
-                                display = ">=";
-                                break;
-
-                            default:
-                                display = StringUtils.EMPTY;
-                        }
-                        break;
-
-                    case GROUP_MEMBERSHIP:
-                        switch (object) {
-                            case EQUALS:
-                                display = "IN";
-                                break;
-
-                            case NOT_EQUALS:
-                                display = "NOT IN";
-                                break;
-
-                            default:
-                                display = StringUtils.EMPTY;
-                        }
-                        break;
-
-                    case GROUP_MEMBER:
-                        switch (object) {
-                            case EQUALS:
-                                display = "WITH";
-                                break;
-
-                            case NOT_EQUALS:
-                                display = "WITHOUT";
-                                break;
-
-                            default:
-                                display = StringUtils.EMPTY;
-                        }
-                        break;
-
-                    case AUX_CLASS:
-                    case ROLE_MEMBERSHIP:
-                    case RESOURCE:
-                        switch (object) {
-                            case EQUALS:
-                                display = "HAS";
-                                break;
-
-                            case NOT_EQUALS:
-                                display = "HAS NOT";
-                                break;
-
-                            default:
-                                display = StringUtils.EMPTY;
-                        }
-                        break;
-
-                    case RELATIONSHIP:
-                        switch (object) {
-                            case IS_NOT_NULL:
-                                display = "EXIST";
-                                break;
+                if (comparator.getModelObject() == 
SearchClause.Comparator.IS_NULL
+                        || comparator.getModelObject() == 
SearchClause.Comparator.IS_NOT_NULL) {
+                    value.setEnabled(false);
+                    value.setModelObject(null);
+                }
 
-                            case IS_NULL:
-                                display = "NOT EXIST";
-                                break;
+                // reload properties list
+                properties.detach();
+                
property.setChoices(properties.getObject().stream().map(Pair::getValue).toList());
+            }
 
-                            case EQUALS:
-                                display = "WITH";
-                                break;
+            case ROLE_MEMBERSHIP -> {
+                value.setEnabled(false);
+                value.setModelObject(null);
 
-                            case NOT_EQUALS:
-                                display = "WITHOUT";
-                                break;
+                // reload properties list
+                properties.detach();
+                
property.setChoices(properties.getObject().stream().map(Pair::getKey).toList());
+            }
 
-                            default:
-                                display = StringUtils.EMPTY;
-                        }
-                        break;
+            case GROUP_MEMBERSHIP -> {
+                value.setEnabled(false);
+                value.setModelObject(null);
 
-                    case CUSTOM:
-                        display = customizer.comparatorDisplayValue(object);
-                        break;
-
-                    default:
-                        display = object.toString();
-                }
-                return display;
+                // reload properties list
+                properties.detach();
+                
property.setChoices(properties.getObject().stream().map(Pair::getKey).toList());
             }
 
-            @Override
-            public String getIdValue(final SearchClause.Comparator object, 
final int index) {
-                return getDisplayValue(object).toString();
+            case GROUP_MEMBER -> {
+                value.setEnabled(true);
+                property.setEnabled(false);
+                property.setModelObject(StringUtils.EMPTY);
             }
 
-            @Override
-            public SearchClause.Comparator getObject(
-                    final String id, final IModel<? extends List<? extends 
SearchClause.Comparator>> choices) {
+            case AUX_CLASS, RESOURCE -> {
+                value.setEnabled(false);
+                value.setModelObject(null);
 
-                if (id == null) {
-                    return SearchClause.Comparator.EQUALS;
-                }
+                // reload properties list
+                properties.detach();
+                
property.setChoices(properties.getObject().stream().map(Pair::getKey).toList());
+            }
 
-                final SearchClause.Comparator comparator;
-                switch (id) {
-                    case "HAS":
-                    case "IN":
-                    case "WITH":
-                        comparator = SearchClause.Comparator.EQUALS;
-                        break;
-
-                    case "HAS NOT":
-                    case "NOT IN":
-                    case "WITHOUT":
-                        comparator = SearchClause.Comparator.NOT_EQUALS;
-                        break;
-
-                    case "NULL":
-                    case "NOT EXIST":
-                        comparator = SearchClause.Comparator.IS_NULL;
-                        break;
-
-                    case "NOT NULL":
-                    case "EXIST":
-                        comparator = SearchClause.Comparator.IS_NOT_NULL;
-                        break;
-
-                    case "==":
-                        comparator = SearchClause.Comparator.EQUALS;
-                        break;
-
-                    case "!=":
-                        comparator = SearchClause.Comparator.NOT_EQUALS;
-                        break;
-
-                    case "<":
-                        comparator = SearchClause.Comparator.LESS_THAN;
-                        break;
-
-                    case "<=":
-                        comparator = SearchClause.Comparator.LESS_OR_EQUALS;
-                        break;
-
-                    case ">":
-                        comparator = SearchClause.Comparator.GREATER_THAN;
-                        break;
-
-                    case ">=":
-                        comparator = SearchClause.Comparator.GREATER_OR_EQUALS;
-                        break;
+            case RELATIONSHIP -> {
+                value.setEnabled(true);
+                value.setModelObject(null);
+                property.setEnabled(true);
 
-                    default:
-                        // EQUALS to be used as default value
-                        comparator = 
customizer.comparatorGetObject(id).orElse(SearchClause.Comparator.EQUALS);
-                        break;
-                }
+                // reload properties list
+                properties.detach();
+                
property.setChoices(properties.getObject().stream().map(Pair::getKey).toList());
+            }
+
+            case CUSTOM ->
+                customizer.adjust(type, property, comparator, value, 
properties);
 
-                return comparator;
+            default -> {
             }
-        };
+        }
     }
 
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    private void renderSearchValueField(
-            final SearchClause searchClause,
-            final AjaxTextFieldPanel property) {
-
-        PlainSchemaTO plainSchema = 
anames.getObject().get(property.getModelObject());
-        if (plainSchema == null) {
-            PlainSchemaTO defaultPlainTO = new PlainSchemaTO();
-            defaultPlainTO.setType(AttrSchemaType.String);
-            plainSchema = property.getModelObject() == null ? defaultPlainTO
-                    : 
dnames.getObject().getOrDefault(property.getModelObject(), defaultPlainTO);
-        }
+    @SuppressWarnings("unchecked")
+    protected FieldPanel<?> buildValue(final SearchClause searchClause, final 
AjaxTextFieldPanel property) {
+        PlainSchemaTO plainSchema = 
Optional.ofNullable(anames.getObject().get(property.getModelObject())).
+                orElseGet(() -> {
+                    PlainSchemaTO defaultPlainTO = new PlainSchemaTO();
+                    defaultPlainTO.setType(AttrSchemaType.String);
+                    return Optional.ofNullable(property.getModelObject()).
+                            map(k -> dnames.getObject().getOrDefault(k, 
defaultPlainTO)).
+                            orElse(defaultPlainTO);
+                });
 
+        FieldPanel<?> result;
         switch (plainSchema.getType()) {
             case Boolean:
-                value = new AjaxTextFieldPanel(
+                result = new AjaxTextFieldPanel(
                         "value",
                         "value",
                         new PropertyModel<>(searchClause, "value"),
                         true);
-                ((AjaxTextFieldPanel) value).setChoices(Arrays.asList("true", 
"false"));
+                ((AjaxTextFieldPanel) result).setChoices(List.of("true", 
"false"));
 
                 break;
 
             case Date:
-                FastDateFormat fdf = plainSchema.getConversionPattern() == null
+                FastDateFormat formatter = 
StringUtils.isBlank(plainSchema.getConversionPattern())
                         ? 
DateFormatUtils.ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT
                         : 
FastDateFormat.getInstance(plainSchema.getConversionPattern());
 
-                value = new AjaxDateTimeFieldPanel(
+                result = new AjaxDateTimeFieldPanel(
                         "value",
                         "value",
-                        new PropertyModel(searchClause, "value") {
+                        new PropertyModel<>(searchClause, "value") {
 
-                    private static final long serialVersionUID = 
1177692285167186690L;
+                    private static final long serialVersionUID = 
-3743432456095828573L;
 
                     @Override
-                    public Object getObject() {
-                        String date = (String) super.getObject();
+                    public Date getObject() {
                         try {
-                            return date != null ? fdf.parse(date) : null;
-                        } catch (ParseException ex) {
-                            LOG.error("Date parse error {}", date, ex);
+                            return StringUtils.isBlank(searchClause.getValue())
+                                    ? null
+                                    : formatter.parse(searchClause.getValue());
+                        } catch (ParseException e) {
+                            LOG.error("Unparsable date: {}", 
searchClause.getValue(), e);
+                            return null;
                         }
-                        return null;
                     }
 
                     @Override
-                    public void setObject(final Object object) {
-                        if (object instanceof Date) {
-                            String valueDate = fdf.format(object);
-                            super.setObject(valueDate);
-                        } else {
-                            super.setObject(object);
-                        }
+                    public void setObject(final Date object) {
+                        Optional.ofNullable(object).ifPresent(date -> 
searchClause.setValue(formatter.format(date)));
                     }
                 }, 
DateFormatUtils.ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT);
                 break;
 
             case Enum:
-                value = new AjaxDropDownChoicePanel<>(
+                result = new AjaxDropDownChoicePanel<>(
                         "value",
                         "value",
-                        new PropertyModel(searchClause, "value"),
+                        new PropertyModel<>(searchClause, "value"),
                         true);
-                ((AjaxDropDownChoicePanel<String>) value).setChoices(
+                ((AjaxDropDownChoicePanel<String>) result).setChoices(
                         
plainSchema.getEnumValues().keySet().stream().sorted().toList());
 
                 if (!plainSchema.getEnumValues().isEmpty()) {
                     Map<String, String> valueMap = plainSchema.getEnumValues();
-                    ((AjaxDropDownChoicePanel) value).setChoiceRenderer(new 
IChoiceRenderer<String>() {
+                    ((AjaxDropDownChoicePanel) result).setChoiceRenderer(new 
IChoiceRenderer<String>() {
 
                         private static final long serialVersionUID = 
-3724971416312135885L;
 
@@ -1019,44 +1008,44 @@ public class SearchClausePanel extends 
FieldPanel<SearchClause> {
                 break;
 
             case Long:
-                value = new 
AjaxNumberFieldPanel.Builder<Long>().enableOnChange().build(
+                result = new 
AjaxNumberFieldPanel.Builder<Long>().enableOnChange().build(
                         "value",
                         "Value",
                         Long.class,
-                        new PropertyModel(searchClause, "value"));
+                        new PropertyModel<>(searchClause, "value"));
 
-                value.add(new AttributeModifier("class", "field value 
search-spinner"));
+                result.add(new AttributeModifier("class", "field value 
search-spinner"));
                 break;
 
             case Double:
-                value = new 
AjaxNumberFieldPanel.Builder<Double>().enableOnChange().step(0.1).build(
+                result = new 
AjaxNumberFieldPanel.Builder<Double>().enableOnChange().step(0.1).build(
                         "value",
                         "value",
                         Double.class,
-                        new PropertyModel(searchClause, "value"));
-                value.add(new AttributeModifier("class", "field value 
search-spinner"));
+                        new PropertyModel<>(searchClause, "value"));
+                result.add(new AttributeModifier("class", "field value 
search-spinner"));
                 break;
 
             default:
-                value = new AjaxTextFieldPanel(
+                result = new AjaxTextFieldPanel(
                         "value", "value", new PropertyModel<>(searchClause, 
"value"), true);
                 break;
         }
 
-        value.hideLabel().setOutputMarkupId(true);
-        value.getField().add(PREVENT_DEFAULT_RETURN);
-        value.getField().add(new 
IndicatorAjaxEventBehavior(Constants.ON_KEYDOWN) {
+        result.hideLabel().setOutputMarkupId(true);
+        result.getField().add(PREVENT_DEFAULT_RETURN);
+        result.getField().add(new 
IndicatorAjaxEventBehavior(Constants.ON_KEYDOWN) {
 
             private static final long serialVersionUID = -7133385027739964990L;
 
             @Override
             protected void onEvent(final AjaxRequestTarget target) {
                 target.focusComponent(null);
-                value.getField().inputChanged();
-                value.getField().validate();
-                if (value.getField().isValid()) {
-                    value.getField().valid();
-                    value.getField().updateModel();
+                result.getField().inputChanged();
+                result.getField().validate();
+                if (result.getField().isValid()) {
+                    result.getField().valid();
+                    result.getField().updateModel();
                 }
             }
 
@@ -1066,6 +1055,8 @@ public class SearchClausePanel extends 
FieldPanel<SearchClause> {
                 AJAX_SUBMIT_ON_RETURN.accept(attributes);
             }
         });
+
+        return result;
     }
 
     @Override
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/SchemaRestClient.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/SchemaRestClient.java
index 8609cd22b6..8ec815ad15 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/SchemaRestClient.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/SchemaRestClient.java
@@ -20,11 +20,11 @@ package org.apache.syncope.client.console.rest;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 import java.util.stream.Collectors;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.AnyTO;
-import org.apache.syncope.common.lib.to.AnyTypeTO;
 import org.apache.syncope.common.lib.to.RealmTO;
 import org.apache.syncope.common.lib.to.SchemaTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
@@ -40,25 +40,17 @@ public class SchemaRestClient extends BaseRestClient {
 
     private static final long serialVersionUID = -2479730152700312373L;
 
-    public <T extends SchemaTO> List<T> getSchemas(
-            final SchemaType schemaType, final AnyTypeKind kind) {
-
-        AnyTypeService client = getService(AnyTypeService.class);
-
+    public <T extends SchemaTO> List<T> getSchemas(final SchemaType 
schemaType, final AnyTypeKind kind) {
         List<String> classes = new ArrayList<>();
 
         switch (kind) {
-            case USER:
-            case GROUP:
-                AnyTypeTO type = client.read(kind.name());
-                if (type != null) {
-                    classes.addAll(type.getClasses());
-                }
-                break;
-
-            default:
-                getService(AnyTypeService.class).list().stream().filter(
-                        anyType -> anyType.getKind() != AnyTypeKind.USER && 
anyType.getKind() != AnyTypeKind.GROUP).
+            case USER, GROUP ->
+                
Optional.ofNullable(getService(AnyTypeService.class).read(kind.name())).
+                        ifPresent(anyType -> 
classes.addAll(anyType.getClasses()));
+
+            default ->
+                getService(AnyTypeService.class).list().stream().
+                        filter(anyType -> anyType.getKind() == 
AnyTypeKind.ANY_OBJECT).
                         forEach(anyType -> 
classes.addAll(anyType.getClasses()));
         }
 
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/PlainAttrs.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/PlainAttrs.java
index 334f28c229..af58c4ec97 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/PlainAttrs.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/PlainAttrs.java
@@ -151,7 +151,7 @@ public class PlainAttrs extends 
AbstractAttrs<PlainSchemaTO> {
             Attr attr = new Attr();
             attr.setSchema(schema.getKey());
             if (attrMap.get(schema.getKey()) == null || 
attrMap.get(schema.getKey()).getValues().isEmpty()) {
-                if (schema.getType() != AttrSchemaType.Dropdown && 
!schema.isMultivalue()) {
+                if (schema.getType() != AttrSchemaType.Dropdown || 
!schema.isMultivalue()) {
                     attr.getValues().add(StringUtils.EMPTY);
                 }
             } else {
@@ -175,7 +175,7 @@ public class PlainAttrs extends 
AbstractAttrs<PlainSchemaTO> {
             Attr attr = new Attr();
             attr.setSchema(schema.getKey());
             if (attrMap.get(schema.getKey()) == null || 
attrMap.get(schema.getKey()).getValues().isEmpty()) {
-                if (schema.getType() != AttrSchemaType.Dropdown && 
!schema.isMultivalue()) {
+                if (schema.getType() != AttrSchemaType.Dropdown || 
!schema.isMultivalue()) {
                     attr.getValues().add(StringUtils.EMPTY);
                 }
             } else {
diff --git 
a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/PlainAttrs.java
 
b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/PlainAttrs.java
index dd715ffd94..45ef210a09 100644
--- 
a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/PlainAttrs.java
+++ 
b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/PlainAttrs.java
@@ -134,20 +134,20 @@ public class PlainAttrs extends 
AbstractAttrs<PlainSchemaTO> {
 
     @Override
     protected void setAttrs() {
-        List<Attr> plainAttrs = new ArrayList<>();
-
         Map<String, Attr> attrMap = 
EntityTOUtils.buildAttrMap(userTO.getPlainAttrs());
 
-        plainAttrs.addAll(schemas.values().stream().map(schema -> {
-            Attr attrTO = new Attr();
-            attrTO.setSchema(schema.getKey());
+        List<Attr> plainAttrs = schemas.values().stream().map(schema -> {
+            Attr attr = new Attr();
+            attr.setSchema(schema.getKey());
             if (attrMap.get(schema.getKey()) == null || 
attrMap.get(schema.getKey()).getValues().isEmpty()) {
-                attrTO.getValues().add("");
+                if (schema.getType() != AttrSchemaType.Dropdown || 
!schema.isMultivalue()) {
+                    attr.getValues().add(StringUtils.EMPTY);
+                }
             } else {
-                attrTO = attrMap.get(schema.getKey());
+                attr = attrMap.get(schema.getKey());
             }
-            return attrTO;
-        }).toList());
+            return attr;
+        }).toList();
 
         userTO.getPlainAttrs().clear();
         userTO.getPlainAttrs().addAll(plainAttrs);
@@ -163,7 +163,9 @@ public class PlainAttrs extends 
AbstractAttrs<PlainSchemaTO> {
             Attr attr = new Attr();
             attr.setSchema(schema.getKey());
             if (attrMap.get(schema.getKey()) == null || 
attrMap.get(schema.getKey()).getValues().isEmpty()) {
-                attr.getValues().add(StringUtils.EMPTY);
+                if (schema.getType() != AttrSchemaType.Dropdown || 
!schema.isMultivalue()) {
+                    attr.getValues().add(StringUtils.EMPTY);
+                }
             } else {
                 
attr.getValues().addAll(attrMap.get(schema.getKey()).getValues());
             }
diff --git a/pom.xml b/pom.xml
index d10bb6f6e8..236f8f95e8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -446,7 +446,7 @@ under the License.
     <cas.version>7.2.2</cas.version>
     <cas-client.version>4.0.4</cas-client.version>
 
-    <swagger-core.version>2.2.31</swagger-core.version>
+    <swagger-core.version>2.2.32</swagger-core.version>
     <swagger-ui.version>5.21.0</swagger-ui.version>
 
     <jquery-slimscroll.version>1.3.8</jquery-slimscroll.version>
@@ -506,7 +506,7 @@ under the License.
     <cargo.deployable.ping.timeout>60000</cargo.deployable.ping.timeout>
 
     <tomcat.version>10.1.41</tomcat.version>
-    <wildfly.version>36.0.0.Final</wildfly.version>
+    <wildfly.version>36.0.1.Final</wildfly.version>
     <payara.version>6.2025.4</payara.version>
     <jakarta.faces.version>4.1.3</jakarta.faces.version>
 

Reply via email to