From 11f569ef0a4b8bddde675dca4cbcefcba08a3398 Mon Sep 17 00:00:00 2001 From: Valera V Harseko Date: Sun, 4 Oct 2026 12:31:39 +0300 Subject: [PATCH] [#150] Keep OrFilter.getRight() an OR for three or more sub-filters OrFilter.getRight() wrapped the remaining sub-filters in an AndFilter, so every caller that walks getLeft()/getRight() (ObjectNormalizerFacade, AbstractFilterTranslator, FilterHandlers serialization, the groovy MapFilterVisitor) turned or(a, b, c) into OR(a, AND(b, c)). Fixes #150 --- .../common/objects/filter/OrFilter.java | 2 +- .../common/objects/FilterBuilderTests.java | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/filter/OrFilter.java b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/filter/OrFilter.java index a77d67302..431180d78 100644 --- a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/filter/OrFilter.java +++ b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/filter/OrFilter.java @@ -81,7 +81,7 @@ public Filter getRight() { if (subFilters.size() > 2) { final LinkedList right = new LinkedList(subFilters); right.removeFirst(); - return new AndFilter(right); + return new OrFilter(right); } else if (subFilters.size() == 2 ){ return subFilters.getLast(); } else { diff --git a/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/framework/common/objects/FilterBuilderTests.java b/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/framework/common/objects/FilterBuilderTests.java index dd5d475ae..071d0d9e3 100644 --- a/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/framework/common/objects/FilterBuilderTests.java +++ b/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/framework/common/objects/FilterBuilderTests.java @@ -20,16 +20,21 @@ * "Portions Copyrighted [year] [name of copyright owner]" * ==================== * Portions Copyrighted 2014-2015 ForgeRock AS. + * Portions Copyrighted 2026 3A Systems, LLC */ package org.identityconnectors.framework.common.objects; +import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; +import org.identityconnectors.framework.common.objects.filter.AndFilter; +import org.identityconnectors.framework.common.objects.filter.CompositeFilter; import org.identityconnectors.framework.common.objects.filter.Filter; import org.identityconnectors.framework.common.objects.filter.FilterBuilder; import org.identityconnectors.framework.common.objects.filter.FilterVisitor; import org.identityconnectors.framework.common.objects.filter.FilteredResultsHandlerVisitor; +import org.identityconnectors.framework.common.objects.filter.OrFilter; import org.testng.annotations.Test; public class FilterBuilderTests { @@ -231,6 +236,42 @@ public void orFilter() { assertFalse(filter.accept(null)); } + @Test + public void orFilterRightSideKeepsOrForThreeOrMoreSubFilters() { + final Filter f = FilterBuilder.or(new FalseFilter(), new FalseFilter(), new TrueFilter()); + final Filter right = ((CompositeFilter) f).getRight(); + assertTrue(right instanceof OrFilter, "getRight() of a flat OR must be an OR: " + right); + assertEquals(((OrFilter) right).getFilters().size(), 2); + // Walking getLeft()/getRight() must not change what the filter matches. + assertTrue(new OrFilter(((CompositeFilter) f).getLeft(), right).accept(null)); + + final Filter four = FilterBuilder.or(new FalseFilter(), new FalseFilter(), new FalseFilter(), new TrueFilter()); + assertTrue(rebuild(four).accept(null)); + } + + @Test + public void andFilterRightSideKeepsAndForThreeOrMoreSubFilters() { + final Filter f = FilterBuilder.and(new TrueFilter(), new TrueFilter(), new FalseFilter()); + final Filter right = ((CompositeFilter) f).getRight(); + assertTrue(right instanceof AndFilter, "getRight() of a flat AND must be an AND: " + right); + assertFalse(rebuild(f).accept(null)); + } + + /** + * Rebuilds a composite filter as a binary tree through getLeft()/getRight(), + * the way normalizers, translators and serializers walk it. + */ + private static Filter rebuild(final Filter filter) { + if (filter instanceof AndFilter) { + return new AndFilter(rebuild(((AndFilter) filter).getLeft()), + rebuild(((AndFilter) filter).getRight())); + } else if (filter instanceof OrFilter) { + return new OrFilter(rebuild(((OrFilter) filter).getLeft()), + rebuild(((OrFilter) filter).getRight())); + } + return filter; + } + @Test public void notFilter() { Filter filter = null;