diff --git a/core/src/main/java/org/springframework/ldap/filter/ProximityFilter.java b/core/src/main/java/org/springframework/ldap/filter/ProximityFilter.java new file mode 100644 index 000000000..dc34924d5 --- /dev/null +++ b/core/src/main/java/org/springframework/ldap/filter/ProximityFilter.java @@ -0,0 +1,46 @@ +/* + * Copyright 2005-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ldap.filter; + +import org.springframework.ldap.support.LdapEncoder; + +/** + * A filter for 'proximity'. The following code: + * + *
+ * ProximityFilter filter = new ProximityFilter("cn", "Some CN"); + * System.out.println(filter.encode()); + *+ * + * would result in: + * + *
+ * (cn~=Some CN) + *+ * + * @author Moritz Kobel + * @since 3.3 + */ +public final class ProximityFilter extends CompareFilter { + + private static final String PROXIMITY_SIGN = "~="; + + public ProximityFilter(String attribute, String value) { + super(attribute, PROXIMITY_SIGN, value, LdapEncoder.filterEncode(value)); + } + +} diff --git a/core/src/main/java/org/springframework/ldap/query/ConditionCriteria.java b/core/src/main/java/org/springframework/ldap/query/ConditionCriteria.java index bd5c026e7..c8329716c 100644 --- a/core/src/main/java/org/springframework/ldap/query/ConditionCriteria.java +++ b/core/src/main/java/org/springframework/ldap/query/ConditionCriteria.java @@ -105,4 +105,17 @@ public interface ConditionCriteria { */ ConditionCriteria not(); + /** + * Appends an {@link org.springframework.ldap.filter.ProximityFilter}. + * @param value the value to compare with. + * @return an ContainerCriteria instance that can be used to continue append more + * criteria or as the LdapQuery instance to be used as instance to e.g. + * {@link org.springframework.ldap.core.LdapOperations#search(LdapQuery, org.springframework.ldap.core.ContextMapper)}. + * @since 3.3 + * @see org.springframework.ldap.filter.EqualsFilter + */ + default ContainerCriteria near(String value) { + throw new UnsupportedOperationException(); + } + } diff --git a/core/src/main/java/org/springframework/ldap/query/DefaultConditionCriteria.java b/core/src/main/java/org/springframework/ldap/query/DefaultConditionCriteria.java index 722878901..a3d922b94 100644 --- a/core/src/main/java/org/springframework/ldap/query/DefaultConditionCriteria.java +++ b/core/src/main/java/org/springframework/ldap/query/DefaultConditionCriteria.java @@ -23,6 +23,7 @@ import org.springframework.ldap.filter.LikeFilter; import org.springframework.ldap.filter.NotFilter; import org.springframework.ldap.filter.PresentFilter; +import org.springframework.ldap.filter.ProximityFilter; import org.springframework.ldap.filter.WhitespaceWildcardsFilter; /** @@ -72,6 +73,11 @@ public ContainerCriteria isPresent() { return appendToParent(new PresentFilter(this.attribute)); } + @Override + public ContainerCriteria near(String value) { + return appendToParent(new ProximityFilter(this.attribute, value)); + } + private ContainerCriteria appendToParent(Filter filter) { return this.parent.append(negateIfApplicable(filter)); } diff --git a/core/src/test/java/org/springframework/ldap/filter/ProximityFilterTests.java b/core/src/test/java/org/springframework/ldap/filter/ProximityFilterTests.java new file mode 100644 index 000000000..b1842f76e --- /dev/null +++ b/core/src/test/java/org/springframework/ldap/filter/ProximityFilterTests.java @@ -0,0 +1,40 @@ +/* + * Copyright 2005-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ldap.filter; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Moritz Kobel + */ +public class ProximityFilterTests { + + @Test + public void testEncode() { + + CompareFilter eqq = new ProximityFilter("foo", "*bar(fie)"); + + StringBuffer buff = new StringBuffer(); + eqq.encode(buff); + + assertThat(buff.toString()).isEqualTo("(foo~=\\2abar\\28fie\\29)"); + + } + +} diff --git a/core/src/test/java/org/springframework/ldap/query/LdapQueryBuilderTests.java b/core/src/test/java/org/springframework/ldap/query/LdapQueryBuilderTests.java index 0f1c38437..543ec6b2a 100644 --- a/core/src/test/java/org/springframework/ldap/query/LdapQueryBuilderTests.java +++ b/core/src/test/java/org/springframework/ldap/query/LdapQueryBuilderTests.java @@ -73,6 +73,13 @@ public void buildPresent() { assertThat(result.filter().encode()).isEqualTo("(cn=*)"); } + @Test + public void buildProximity() { + LdapQuery result = LdapQueryBuilder.query().where("cn").near("John Doe"); + + assertThat(result.filter().encode()).isEqualTo("(cn~=John Doe)"); + } + @Test public void buildHardcodedFilter() { LdapQuery result = LdapQueryBuilder.query().filter("(cn=Person*)");