Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -146,47 +147,79 @@
* @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<Filter, ObjectNormalizerFacade> {

static final NormalizingFilterVisitor INSTANCE = new NormalizingFilterVisitor();

public Filter visitAndFilter(ObjectNormalizerFacade p, AndFilter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<Filter,ObjectNormalizerFacade>.visitAndFilter
; it is advisable to add an Override annotation.
return new AndFilter(p.normalizeFilter(filter.getLeft()), p.normalizeFilter(filter
.getRight()));
} else {
}

public Filter visitContainsFilter(ObjectNormalizerFacade p, ContainsFilter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<Filter,ObjectNormalizerFacade>.visitContainsFilter
; it is advisable to add an Override annotation.
return new ContainsFilter(p.normalizeAttribute(filter.getAttribute()));
}

public Filter visitContainsAllValuesFilter(ObjectNormalizerFacade p,

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<Filter,ObjectNormalizerFacade>.visitContainsAllValuesFilter
; it is advisable to add an Override annotation.
ContainsAllValuesFilter filter) {
return new ContainsAllValuesFilter(p.normalizeAttribute(filter.getAttribute()));
}

public Filter visitEqualsFilter(ObjectNormalizerFacade p, EqualsFilter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<Filter,ObjectNormalizerFacade>.visitEqualsFilter
; it is advisable to add an Override annotation.
return new EqualsFilter(p.normalizeAttribute(filter.getAttribute()));
}

public Filter visitExtendedFilter(ObjectNormalizerFacade p, Filter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<Filter,ObjectNormalizerFacade>.visitExtendedFilter
; it is advisable to add an Override annotation.
return filter;
}

public Filter visitGreaterThanFilter(ObjectNormalizerFacade p, GreaterThanFilter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<Filter,ObjectNormalizerFacade>.visitGreaterThanFilter
; it is advisable to add an Override annotation.
return new GreaterThanFilter(p.normalizeAttribute(filter.getAttribute()));
}

public Filter visitGreaterThanOrEqualFilter(ObjectNormalizerFacade p,

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<Filter,ObjectNormalizerFacade>.visitGreaterThanOrEqualFilter
; it is advisable to add an Override annotation.
GreaterThanOrEqualFilter filter) {
return new GreaterThanOrEqualFilter(p.normalizeAttribute(filter.getAttribute()));
}

public Filter visitLessThanFilter(ObjectNormalizerFacade p, LessThanFilter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<Filter,ObjectNormalizerFacade>.visitLessThanFilter
; it is advisable to add an Override annotation.
return new LessThanFilter(p.normalizeAttribute(filter.getAttribute()));
}

public Filter visitLessThanOrEqualFilter(ObjectNormalizerFacade p,

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<Filter,ObjectNormalizerFacade>.visitLessThanOrEqualFilter
; it is advisable to add an Override annotation.
LessThanOrEqualFilter filter) {
return new LessThanOrEqualFilter(p.normalizeAttribute(filter.getAttribute()));
}

public Filter visitNotFilter(ObjectNormalizerFacade p, NotFilter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<Filter,ObjectNormalizerFacade>.visitNotFilter
; it is advisable to add an Override annotation.
return new NotFilter(p.normalizeFilter(filter.getFilter()));
}

public Filter visitOrFilter(ObjectNormalizerFacade p, OrFilter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<Filter,ObjectNormalizerFacade>.visitOrFilter
; it is advisable to add an Override annotation.
return new OrFilter(p.normalizeFilter(filter.getLeft()), p.normalizeFilter(filter
.getRight()));
}

public Filter visitStartsWithFilter(ObjectNormalizerFacade p, StartsWithFilter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<Filter,ObjectNormalizerFacade>.visitStartsWithFilter
; it is advisable to add an Override annotation.
return new StartsWithFilter(p.normalizeAttribute(filter.getAttribute()));
}

public Filter visitEndsWithFilter(ObjectNormalizerFacade p, EndsWithFilter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<Filter,ObjectNormalizerFacade>.visitEndsWithFilter
; it is advisable to add an Override annotation.
return new EndsWithFilter(p.normalizeAttribute(filter.getAttribute()));
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,28 +409,76 @@
* @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<T, Boolean> {

public T visitAndFilter(Boolean not, AndFilter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<T,Boolean>.visitAndFilter
; it is advisable to add an Override annotation.
// unrecognized expression - nothing we can do
return null;
}

public T visitContainsFilter(Boolean not, ContainsFilter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<T,Boolean>.visitContainsFilter
; it is advisable to add an Override annotation.
return createContainsExpression(filter, not);
}

public T visitContainsAllValuesFilter(Boolean not, ContainsAllValuesFilter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<T,Boolean>.visitContainsAllValuesFilter
; it is advisable to add an Override annotation.
return createContainsAllValuesExpression(filter, not);
}

public T visitEqualsFilter(Boolean not, EqualsFilter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<T,Boolean>.visitEqualsFilter
; it is advisable to add an Override annotation.
return createEqualsExpression(filter, not);
}

public T visitExtendedFilter(Boolean not, Filter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<T,Boolean>.visitExtendedFilter
; it is advisable to add an Override annotation.
// unrecognized expression - nothing we can do
return null;
}

public T visitGreaterThanFilter(Boolean not, GreaterThanFilter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<T,Boolean>.visitGreaterThanFilter
; it is advisable to add an Override annotation.
return createGreaterThanExpression(filter, not);
}

public T visitGreaterThanOrEqualFilter(Boolean not, GreaterThanOrEqualFilter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<T,Boolean>.visitGreaterThanOrEqualFilter
; it is advisable to add an Override annotation.
return createGreaterThanOrEqualExpression(filter, not);
}

public T visitLessThanFilter(Boolean not, LessThanFilter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<T,Boolean>.visitLessThanFilter
; it is advisable to add an Override annotation.
return createLessThanExpression(filter, not);
}

public T visitLessThanOrEqualFilter(Boolean not, LessThanOrEqualFilter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<T,Boolean>.visitLessThanOrEqualFilter
; it is advisable to add an Override annotation.
return createLessThanOrEqualExpression(filter, not);
}

public T visitNotFilter(Boolean not, NotFilter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<T,Boolean>.visitNotFilter
; it is advisable to add an Override annotation.
// unrecognized expression - nothing we can do
return null;
}

public T visitOrFilter(Boolean not, OrFilter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<T,Boolean>.visitOrFilter
; it is advisable to add an Override annotation.
// unrecognized expression - nothing we can do
return null;
}

public T visitStartsWithFilter(Boolean not, StartsWithFilter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<T,Boolean>.visitStartsWithFilter
; it is advisable to add an Override annotation.
return createStartsWithExpression(filter, not);
}

public T visitEndsWithFilter(Boolean not, EndsWithFilter filter) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
FilterVisitor<T,Boolean>.visitEndsWithFilter
; it is advisable to add an Override annotation.
return createEndsWithExpression(filter, not);
}
}

/**
Expand Down
Loading