diff --git a/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/operations/ObjectNormalizerFacade.java b/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/operations/ObjectNormalizerFacade.java index e5765f3e..da474b29 100644 --- a/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/operations/ObjectNormalizerFacade.java +++ b/OpenICF-java-framework/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/operations/ObjectNormalizerFacade.java @@ -20,6 +20,7 @@ * "Portions Copyrighted [year] [name of copyright owner]" * ==================== * Portions Copyrighted 2010-2013 ForgeRock AS. + * Portions Copyrighted 2026 3A Systems, LLC */ package org.identityconnectors.framework.impl.api.local.operations; @@ -34,12 +35,12 @@ import org.identityconnectors.framework.common.objects.SyncDelta; import org.identityconnectors.framework.common.objects.SyncDeltaBuilder; import org.identityconnectors.framework.common.objects.filter.AndFilter; -import org.identityconnectors.framework.common.objects.filter.AttributeFilter; import org.identityconnectors.framework.common.objects.filter.ContainsAllValuesFilter; import org.identityconnectors.framework.common.objects.filter.ContainsFilter; import org.identityconnectors.framework.common.objects.filter.EndsWithFilter; import org.identityconnectors.framework.common.objects.filter.EqualsFilter; import org.identityconnectors.framework.common.objects.filter.Filter; +import org.identityconnectors.framework.common.objects.filter.FilterVisitor; import org.identityconnectors.framework.common.objects.filter.GreaterThanFilter; import org.identityconnectors.framework.common.objects.filter.GreaterThanOrEqualFilter; import org.identityconnectors.framework.common.objects.filter.LessThanFilter; @@ -146,47 +147,79 @@ public SyncDelta normalizeSyncDelta(SyncDelta delta) { * @return The normalized filter. */ public Filter normalizeFilter(Filter filter) { - if (filter instanceof ContainsFilter) { - AttributeFilter afilter = (AttributeFilter) filter; - return new ContainsFilter(normalizeAttribute(afilter.getAttribute())); - } else if (filter instanceof EndsWithFilter) { - AttributeFilter afilter = (AttributeFilter) filter; - return new EndsWithFilter(normalizeAttribute(afilter.getAttribute())); - } else if (filter instanceof EqualsFilter) { - AttributeFilter afilter = (AttributeFilter) filter; - return new EqualsFilter(normalizeAttribute(afilter.getAttribute())); - } else if (filter instanceof GreaterThanFilter) { - AttributeFilter afilter = (AttributeFilter) filter; - return new GreaterThanFilter(normalizeAttribute(afilter.getAttribute())); - } else if (filter instanceof GreaterThanOrEqualFilter) { - AttributeFilter afilter = (AttributeFilter) filter; - return new GreaterThanOrEqualFilter(normalizeAttribute(afilter.getAttribute())); - } else if (filter instanceof LessThanFilter) { - AttributeFilter afilter = (AttributeFilter) filter; - return new LessThanFilter(normalizeAttribute(afilter.getAttribute())); - } else if (filter instanceof LessThanOrEqualFilter) { - AttributeFilter afilter = (AttributeFilter) filter; - return new LessThanOrEqualFilter(normalizeAttribute(afilter.getAttribute())); - } else if (filter instanceof StartsWithFilter) { - AttributeFilter afilter = (AttributeFilter) filter; - return new StartsWithFilter(normalizeAttribute(afilter.getAttribute())); - } else if (filter instanceof ContainsAllValuesFilter) { - AttributeFilter afilter = (AttributeFilter) filter; - return new ContainsAllValuesFilter(normalizeAttribute(afilter.getAttribute())); - } else if (filter instanceof NotFilter) { - NotFilter notFilter = (NotFilter) filter; - return new NotFilter(normalizeFilter(notFilter.getFilter())); - } else if (filter instanceof AndFilter) { - AndFilter andFilter = (AndFilter) filter; - return new AndFilter(normalizeFilter(andFilter.getLeft()), normalizeFilter(andFilter - .getRight())); - } else if (filter instanceof OrFilter) { - OrFilter orFilter = (OrFilter) filter; - return new OrFilter(normalizeFilter(orFilter.getLeft()), normalizeFilter(orFilter + if (filter == null) { + return null; + } + return filter.accept(NormalizingFilterVisitor.INSTANCE, this); + } + + /** + * Applies {@link #normalizeAttribute(Attribute)} to every attribute + * referenced by a filter, recursing through composite (AND/OR/NOT) + * filters. Filter kinds with no attribute of their own (e.g. presence, + * extended) are returned unchanged. + */ + private static final class NormalizingFilterVisitor implements + FilterVisitor { + + static final NormalizingFilterVisitor INSTANCE = new NormalizingFilterVisitor(); + + public Filter visitAndFilter(ObjectNormalizerFacade p, AndFilter filter) { + return new AndFilter(p.normalizeFilter(filter.getLeft()), p.normalizeFilter(filter .getRight())); - } else { + } + + public Filter visitContainsFilter(ObjectNormalizerFacade p, ContainsFilter filter) { + return new ContainsFilter(p.normalizeAttribute(filter.getAttribute())); + } + + public Filter visitContainsAllValuesFilter(ObjectNormalizerFacade p, + ContainsAllValuesFilter filter) { + return new ContainsAllValuesFilter(p.normalizeAttribute(filter.getAttribute())); + } + + public Filter visitEqualsFilter(ObjectNormalizerFacade p, EqualsFilter filter) { + return new EqualsFilter(p.normalizeAttribute(filter.getAttribute())); + } + + public Filter visitExtendedFilter(ObjectNormalizerFacade p, Filter filter) { return filter; } + + public Filter visitGreaterThanFilter(ObjectNormalizerFacade p, GreaterThanFilter filter) { + return new GreaterThanFilter(p.normalizeAttribute(filter.getAttribute())); + } + + public Filter visitGreaterThanOrEqualFilter(ObjectNormalizerFacade p, + GreaterThanOrEqualFilter filter) { + return new GreaterThanOrEqualFilter(p.normalizeAttribute(filter.getAttribute())); + } + + public Filter visitLessThanFilter(ObjectNormalizerFacade p, LessThanFilter filter) { + return new LessThanFilter(p.normalizeAttribute(filter.getAttribute())); + } + + public Filter visitLessThanOrEqualFilter(ObjectNormalizerFacade p, + LessThanOrEqualFilter filter) { + return new LessThanOrEqualFilter(p.normalizeAttribute(filter.getAttribute())); + } + + public Filter visitNotFilter(ObjectNormalizerFacade p, NotFilter filter) { + return new NotFilter(p.normalizeFilter(filter.getFilter())); + } + + public Filter visitOrFilter(ObjectNormalizerFacade p, OrFilter filter) { + return new OrFilter(p.normalizeFilter(filter.getLeft()), p.normalizeFilter(filter + .getRight())); + } + + public Filter visitStartsWithFilter(ObjectNormalizerFacade p, StartsWithFilter filter) { + return new StartsWithFilter(p.normalizeAttribute(filter.getAttribute())); + } + + public Filter visitEndsWithFilter(ObjectNormalizerFacade p, EndsWithFilter filter) { + return new EndsWithFilter(p.normalizeAttribute(filter.getAttribute())); + } } } diff --git a/OpenICF-java-framework/connector-framework-internal/src/test/java/org/identityconnectors/framework/impl/api/local/operations/ObjectNormalizerFacadeTests.java b/OpenICF-java-framework/connector-framework-internal/src/test/java/org/identityconnectors/framework/impl/api/local/operations/ObjectNormalizerFacadeTests.java index 86846612..4f73504f 100644 --- a/OpenICF-java-framework/connector-framework-internal/src/test/java/org/identityconnectors/framework/impl/api/local/operations/ObjectNormalizerFacadeTests.java +++ b/OpenICF-java-framework/connector-framework-internal/src/test/java/org/identityconnectors/framework/impl/api/local/operations/ObjectNormalizerFacadeTests.java @@ -19,6 +19,7 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * "Portions Copyrighted 2026 3A Systems, LLC" */ package org.identityconnectors.framework.impl.api.local.operations; @@ -162,6 +163,17 @@ public void testNot() { assertNormalizedFilter(expected, filter); } + @Test + public void testPresenceFilterPassedThroughUnchanged() { + Filter filter = FilterBuilder.present("foo"); + assertNormalizedFilter(filter, filter); + } + + @Test + public void testNullFilterReturnsNull() { + assertEquals(createTestNormalizer().normalizeFilter(null), null); + } + @Test public void testContainsAllValues() { Filter expected = FilterBuilder.containsAllValues(createNormalizedTestAttribute()); diff --git a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/filter/AbstractFilterTranslator.java b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/filter/AbstractFilterTranslator.java index 1ce21d98..193468d9 100644 --- a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/filter/AbstractFilterTranslator.java +++ b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/filter/AbstractFilterTranslator.java @@ -409,28 +409,76 @@ private T createLeafExpression(Filter filter) { * @return The expression or null (for everything) */ private T createLeafExpression(Filter filter, boolean not) { - if (filter instanceof ContainsFilter) { - return createContainsExpression((ContainsFilter) filter, not); - } else if (filter instanceof EndsWithFilter) { - return createEndsWithExpression((EndsWithFilter) filter, not); - } else if (filter instanceof EqualsFilter) { - return createEqualsExpression((EqualsFilter) filter, not); - } else if (filter instanceof GreaterThanFilter) { - return createGreaterThanExpression((GreaterThanFilter) filter, not); - } else if (filter instanceof GreaterThanOrEqualFilter) { - return createGreaterThanOrEqualExpression((GreaterThanOrEqualFilter) filter, not); - } else if (filter instanceof LessThanFilter) { - return createLessThanExpression((LessThanFilter) filter, not); - } else if (filter instanceof LessThanOrEqualFilter) { - return createLessThanOrEqualExpression((LessThanOrEqualFilter) filter, not); - } else if (filter instanceof StartsWithFilter) { - return createStartsWithExpression((StartsWithFilter) filter, not); - } else if (filter instanceof ContainsAllValuesFilter) { - return createContainsAllValuesExpression((ContainsAllValuesFilter) filter, not); - } else { + if (filter == null) { + return null; + } + return filter.accept(new LeafExpressionVisitor(), not); + } + + /** + * Dispatches a leaf filter to its corresponding createXxxExpression + * override. AND/OR/NOT filters never reach here (see + * {@link #createLeafExpression(Filter)}, which already unwraps a + * top-level NOT); those and any other unrecognized filter kind return + * {@code null}, matching the previous instanceof-chain's fallback. + */ + private final class LeafExpressionVisitor implements FilterVisitor { + + public T visitAndFilter(Boolean not, AndFilter filter) { + // unrecognized expression - nothing we can do + return null; + } + + public T visitContainsFilter(Boolean not, ContainsFilter filter) { + return createContainsExpression(filter, not); + } + + public T visitContainsAllValuesFilter(Boolean not, ContainsAllValuesFilter filter) { + return createContainsAllValuesExpression(filter, not); + } + + public T visitEqualsFilter(Boolean not, EqualsFilter filter) { + return createEqualsExpression(filter, not); + } + + public T visitExtendedFilter(Boolean not, Filter filter) { // unrecognized expression - nothing we can do return null; } + + public T visitGreaterThanFilter(Boolean not, GreaterThanFilter filter) { + return createGreaterThanExpression(filter, not); + } + + public T visitGreaterThanOrEqualFilter(Boolean not, GreaterThanOrEqualFilter filter) { + return createGreaterThanOrEqualExpression(filter, not); + } + + public T visitLessThanFilter(Boolean not, LessThanFilter filter) { + return createLessThanExpression(filter, not); + } + + public T visitLessThanOrEqualFilter(Boolean not, LessThanOrEqualFilter filter) { + return createLessThanOrEqualExpression(filter, not); + } + + public T visitNotFilter(Boolean not, NotFilter filter) { + // unrecognized expression - nothing we can do + return null; + } + + public T visitOrFilter(Boolean not, OrFilter filter) { + // unrecognized expression - nothing we can do + return null; + } + + public T visitStartsWithFilter(Boolean not, StartsWithFilter filter) { + return createStartsWithExpression(filter, not); + } + + public T visitEndsWithFilter(Boolean not, EndsWithFilter filter) { + return createEndsWithExpression(filter, not); + } } /**