diff --git a/core/build.gradle b/core/build.gradle
index 0de33d5b..c84e40b0 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -9,8 +9,7 @@ idea.module.excludeDirs = [
file('build/libs')]
dependencies {
- compile "commons-logging:commons-logging:$commonsLoggingVersion",
- "org.springframework:spring-core:$springVersion",
+ compile "org.springframework:spring-core:$springVersion",
"org.springframework:spring-beans:$springVersion",
"org.springframework:spring-tx:$springVersion",
"org.springframework.data:spring-data-commons:$springDataVersion"
diff --git a/core/src/main/java/org/springframework/ldap/control/AbstractRequestControlDirContextProcessor.java b/core/src/main/java/org/springframework/ldap/control/AbstractRequestControlDirContextProcessor.java
index 230807d3..73985109 100644
--- a/core/src/main/java/org/springframework/ldap/control/AbstractRequestControlDirContextProcessor.java
+++ b/core/src/main/java/org/springframework/ldap/control/AbstractRequestControlDirContextProcessor.java
@@ -1,122 +1,122 @@
-/*
- * Copyright 2005-2010 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
- *
- * http://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.control;
-
-import javax.naming.NamingException;
-import javax.naming.directory.DirContext;
-import javax.naming.ldap.Control;
-import javax.naming.ldap.LdapContext;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.ldap.core.DirContextProcessor;
-
-/**
- * Abstract superclass with responsibility to apply a single RequestControl on
- * an LdapContext, preserving any existing controls. Subclasses should implement
- * {@link DirContextProcessor#postProcess(DirContext)} and template method
- * {@link #createRequestControl()}.
- *
- * @author Mattias Hellborg Arthursson
- * @author Ulrik Sandberg
- */
-public abstract class AbstractRequestControlDirContextProcessor implements DirContextProcessor {
- protected Log log = LogFactory.getLog(AbstractRequestControlDirContextProcessor.class);
-
- private boolean replaceSameControlEnabled = true;
-
- /**
- * If there already exists a request control of the same class as the one
- * created by {@link #createRequestControl()} in the context, the new
- * control can either replace the existing one (default behavior) or be
- * added.
- *
- * @return true if an already existing control will be replaced
- */
- public boolean isReplaceSameControlEnabled() {
- return replaceSameControlEnabled;
- }
-
- /**
- * If there already exists a request control of the same class as the one
- * created by {@link #createRequestControl()} in the context, the new
- * control can either replace the existing one (default behavior) or be
- * added.
- *
- * @param replaceSameControlEnabled true if an already
- * existing control should be replaced
- */
- public void setReplaceSameControlEnabled(boolean replaceSameControlEnabled) {
- this.replaceSameControlEnabled = replaceSameControlEnabled;
- }
-
- /**
- * Get the existing RequestControls from the LdapContext, call
- * {@link #createRequestControl()} to get a new instance, build a new array
- * of Controls and set it on the LdapContext.
- *
- * The {@link Control} feature is specific for LDAP v3 and thus applies only
- * to {@link LdapContext}. However, the generic DirContextProcessor
- * mechanism used for calling preProcess and
- * postProcess uses DirContext, since it also works for LDAP
- * v2. This is the reason that DirContext has to be cast to a LdapContext.
- *
- * @param ctx an LdapContext instance.
- * @throws NamingException
- * @throws IllegalArgumentException if the supplied DirContext is not an
- * LdapContext.
- */
- public void preProcess(DirContext ctx) throws NamingException {
- LdapContext ldapContext;
- if (ctx instanceof LdapContext) {
- ldapContext = (LdapContext) ctx;
- }
- else {
- throw new IllegalArgumentException("Request Control operations require LDAPv3 - "
- + "Context must be of type LdapContext");
- }
-
- Control[] requestControls = ldapContext.getRequestControls();
- if (requestControls == null) {
- requestControls = new Control[0];
- }
- Control newControl = createRequestControl();
-
- Control[] newControls = new Control[requestControls.length + 1];
- for (int i = 0; i < requestControls.length; i++) {
- if (replaceSameControlEnabled && requestControls[i].getClass() == newControl.getClass()) {
- log.debug("Replacing already existing control in context: " + newControl);
- requestControls[i] = newControl;
- ldapContext.setRequestControls(requestControls);
- return;
- }
- newControls[i] = requestControls[i];
- }
-
- // Add the new Control at the end of the array.
- newControls[newControls.length - 1] = newControl;
-
- ldapContext.setRequestControls(newControls);
- }
-
- /**
- * Create an instance of the appropriate RequestControl.
- *
- * @return the new instance.
- */
- public abstract Control createRequestControl();
-}
+/*
+ * Copyright 2005-2013 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
+ *
+ * http://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.control;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.ldap.core.DirContextProcessor;
+
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.naming.ldap.Control;
+import javax.naming.ldap.LdapContext;
+
+/**
+ * Abstract superclass with responsibility to apply a single RequestControl on
+ * an LdapContext, preserving any existing controls. Subclasses should implement
+ * {@link DirContextProcessor#postProcess(DirContext)} and template method
+ * {@link #createRequestControl()}.
+ *
+ * @author Mattias Hellborg Arthursson
+ * @author Ulrik Sandberg
+ */
+public abstract class AbstractRequestControlDirContextProcessor implements DirContextProcessor {
+ protected Logger log = LoggerFactory.getLogger(AbstractRequestControlDirContextProcessor.class);
+
+ private boolean replaceSameControlEnabled = true;
+
+ /**
+ * If there already exists a request control of the same class as the one
+ * created by {@link #createRequestControl()} in the context, the new
+ * control can either replace the existing one (default behavior) or be
+ * added.
+ *
+ * @return true if an already existing control will be replaced
+ */
+ public boolean isReplaceSameControlEnabled() {
+ return replaceSameControlEnabled;
+ }
+
+ /**
+ * If there already exists a request control of the same class as the one
+ * created by {@link #createRequestControl()} in the context, the new
+ * control can either replace the existing one (default behavior) or be
+ * added.
+ *
+ * @param replaceSameControlEnabled true if an already
+ * existing control should be replaced
+ */
+ public void setReplaceSameControlEnabled(boolean replaceSameControlEnabled) {
+ this.replaceSameControlEnabled = replaceSameControlEnabled;
+ }
+
+ /**
+ * Get the existing RequestControls from the LdapContext, call
+ * {@link #createRequestControl()} to get a new instance, build a new array
+ * of Controls and set it on the LdapContext.
+ *
+ * The {@link Control} feature is specific for LDAP v3 and thus applies only
+ * to {@link LdapContext}. However, the generic DirContextProcessor
+ * mechanism used for calling preProcess and
+ * postProcess uses DirContext, since it also works for LDAP
+ * v2. This is the reason that DirContext has to be cast to a LdapContext.
+ *
+ * @param ctx an LdapContext instance.
+ * @throws NamingException
+ * @throws IllegalArgumentException if the supplied DirContext is not an
+ * LdapContext.
+ */
+ public void preProcess(DirContext ctx) throws NamingException {
+ LdapContext ldapContext;
+ if (ctx instanceof LdapContext) {
+ ldapContext = (LdapContext) ctx;
+ }
+ else {
+ throw new IllegalArgumentException("Request Control operations require LDAPv3 - "
+ + "Context must be of type LdapContext");
+ }
+
+ Control[] requestControls = ldapContext.getRequestControls();
+ if (requestControls == null) {
+ requestControls = new Control[0];
+ }
+ Control newControl = createRequestControl();
+
+ Control[] newControls = new Control[requestControls.length + 1];
+ for (int i = 0; i < requestControls.length; i++) {
+ if (replaceSameControlEnabled && requestControls[i].getClass() == newControl.getClass()) {
+ log.debug("Replacing already existing control in context: " + newControl);
+ requestControls[i] = newControl;
+ ldapContext.setRequestControls(requestControls);
+ return;
+ }
+ newControls[i] = requestControls[i];
+ }
+
+ // Add the new Control at the end of the array.
+ newControls[newControls.length - 1] = newControl;
+
+ ldapContext.setRequestControls(newControls);
+ }
+
+ /**
+ * Create an instance of the appropriate RequestControl.
+ *
+ * @return the new instance.
+ */
+ public abstract Control createRequestControl();
+}
diff --git a/core/src/main/java/org/springframework/ldap/control/PagedResultsRequestControl.java b/core/src/main/java/org/springframework/ldap/control/PagedResultsRequestControl.java
index 5b6bc393..4ebcf39c 100644
--- a/core/src/main/java/org/springframework/ldap/control/PagedResultsRequestControl.java
+++ b/core/src/main/java/org/springframework/ldap/control/PagedResultsRequestControl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2005-2010 the original author or authors.
+ * Copyright 2005-2013 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.
@@ -213,7 +213,7 @@ public class PagedResultsRequestControl extends AbstractRequestControlDirContext
}
}
- log.fatal("No matching response control found for paged results - looking for '" + responseControlClass);
+ log.error("No matching response control found for paged results - looking for '{}", responseControlClass);
}
private Object invokeMethod(String method, Class clazz, Object control) {
diff --git a/core/src/main/java/org/springframework/ldap/core/DirContextAdapter.java b/core/src/main/java/org/springframework/ldap/core/DirContextAdapter.java
index 03bdb5fc..228f6c83 100644
--- a/core/src/main/java/org/springframework/ldap/core/DirContextAdapter.java
+++ b/core/src/main/java/org/springframework/ldap/core/DirContextAdapter.java
@@ -15,8 +15,8 @@
*/
package org.springframework.ldap.core;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.ldap.NoSuchAttributeException;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.util.ObjectUtils;
@@ -88,7 +88,7 @@ public class DirContextAdapter implements DirContextOperations {
private static final boolean ORDER_DOESNT_MATTER = false;
- private static Log log = LogFactory.getLog(DirContextAdapter.class);
+ private static Logger log = LoggerFactory.getLogger(DirContextAdapter.class);
private final Attributes originalAttrs;
diff --git a/core/src/main/java/org/springframework/ldap/core/DistinguishedName.java b/core/src/main/java/org/springframework/ldap/core/DistinguishedName.java
index 63ebfc1b..0082169c 100644
--- a/core/src/main/java/org/springframework/ldap/core/DistinguishedName.java
+++ b/core/src/main/java/org/springframework/ldap/core/DistinguishedName.java
@@ -1,857 +1,857 @@
-/*
- * Copyright 2005-2013 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
- *
- * http://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.core;
-
-import org.springframework.util.ObjectUtils;
-import org.springframework.util.StringUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.ldap.BadLdapGrammarException;
-import org.springframework.ldap.support.LdapUtils;
-import org.springframework.ldap.support.ListComparator;
-import org.springframework.util.Assert;
-
-import javax.naming.CompositeName;
-import javax.naming.InvalidNameException;
-import javax.naming.Name;
-import javax.naming.ldap.Rdn;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.ListIterator;
-
-/**
- * Default implementation of a {@link Name} corresponding to an LDAP path. A
- * Distinguished Name manipulation implementation is included in JDK1.5
- * (LdapName), but not in prior releases.
- *
- * A DistinguishedName is particularly useful when building or
- * modifying an LDAP path dynamically, as escaping will be taken care of.
- *
- * A path is split into several names. The {@link Name} interface specifies that
- * the most significant part be in position 0.
- *
- * Example: - * - *
- * Name instances, and consequently DistinguishedName
- * instances are naturally mutable, which is useful when constructing
- * DistinguishedNames. Example:
- *
- *
- * DistinguishedName path = new DistinguishedName("dc=jayway,dc=se");
- * path.add("ou", "People");
- * path.add("uid", "adam.skogman");
- * String dn = path.toString();
- *
- *
- * will render uid=adam.skogman,ou=People,dc=jayway,dc=se.
- *
- *
- * NOTE: The fact that DistinguishedName instances are mutable needs to
- * be taken into careful account, as this means that they may be modified
- * involuntarily. This means that whenever a DistinguishedName
- * instance is kept for reference (e.g. for identification of a domain entry) or
- * as a constant, you should consider getting an immutable copy of the instance
- * using {@link #immutableDistinguishedName()} or
- * {@link #immutableDistinguishedName(String)}.
- *
- * NB:As of version 1.3 the default toString representation of
- * DistinguishedName now defaults to a compact one, without spaces between the
- * respective RDNs. For backward compatibility, set the
- * {@link #SPACED_DN_FORMAT_PROPERTY} ({@value #SPACED_DN_FORMAT_PROPERTY}) to
- * true.
- * @author Adam Skogman
- * @author Mattias Hellborg Arthursson
- *
- * @deprecated As of 2.0 it is recommended to use {@link javax.naming.ldap.LdapName} along with
- * utility methods in {@link LdapUtils} instead.
- * @see javax.naming.ldap.LdapName
- * @see LdapUtils#newLdapName(javax.naming.Name)
- * @see LdapUtils#newLdapName(String)
- * @see org.springframework.ldap.support.LdapUtils#emptyLdapName()
- */
-public class DistinguishedName implements Name {
- /**
- * System property that will be inspected to determine whether
- * {@link #toString()} will format the DN with spaces after each comma or
- * use a more compact representation, i.e.:
- * uid=adam.skogman, ou=People, dc=jayway, dc=se rather than
- * uid=adam.skogman,ou=People,dc=jayway,dc=se. A value other
- * than null or blank will trigger the spaced format. Default is the compact
- * representation.
- *
- * Valid values are: - *
- * Valid values are: - *
DistinguishedName from a String.
- *
- * @param path a String corresponding to a (syntactically) valid LDAP path.
- */
- public DistinguishedName(String path) {
- if (!StringUtils.hasText(path)) {
- names = new LinkedList();
- }
- else {
- parse(path);
- }
- }
-
- /**
- * Construct a new DistinguishedName from the supplied
- * List of {@link LdapRdn} objects.
- *
- * @param list the components that this instance will consist of.
- */
- public DistinguishedName(List list) {
- this.names = list;
- }
-
- /**
- * Construct a new DistinguishedName from the supplied
- * {@link Name}. The parts of the supplied {@link Name} must be
- * syntactically correct {@link LdapRdn}s.
- *
- * @param name the {@link Name} to construct a new
- * DistinguishedName from.
- */
- public DistinguishedName(Name name) {
- Assert.notNull(name, "name cannot be null");
- if (name instanceof CompositeName) {
- parse(LdapUtils.convertCompositeNameToString((CompositeName) name));
- return;
- }
- names = new LinkedList();
- for (int i = 0; i < name.size(); i++) {
- names.add(new LdapRdn(name.get(i)));
- }
- }
-
- /**
- * Parse the supplied String and make this instance represent the
- * corresponding distinguished name.
- *
- * @param path the LDAP path to parse.
- */
- protected void parse(String path) {
- DnParser parser = DefaultDnParserFactory.createDnParser(unmangleCompositeName(path));
- DistinguishedName dn;
- try {
- dn = parser.dn();
- }
- catch (ParseException e) {
- throw new BadLdapGrammarException("Failed to parse DN", e);
- }
- catch (TokenMgrError e) {
- throw new BadLdapGrammarException("Failed to parse DN", e);
- }
- this.names = dn.names;
- }
-
- /**
- * If path is surrounded by quotes, strip them. JNDI considers forward slash
- * ('/') special, but LDAP doesn't. {@link CompositeName#toString()} tends
- * to mangle a {@link Name} with a slash by surrounding it with quotes
- * ('"').
- *
- * @param path Path to check and possibly strip.
- * @return A String with the possibly stripped path.
- */
- private String unmangleCompositeName(String path) {
- String tempPath;
- // Check if CompositeName has mangled the name with quotes
- if (path.startsWith("\"") && path.endsWith("\"")) {
- tempPath = path.substring(1, path.length() - 1);
- }
- else {
- tempPath = path;
- }
-
- tempPath = StringUtils.replace(tempPath, MANGLED_DOUBLE_QUOTES, PROPER_DOUBLE_QUOTES);
- return tempPath;
- }
-
- /**
- * Get the {@link LdapRdn} at a specified position.
- *
- * @param index the {@link LdapRdn} to retrieve.
- * @return the {@link LdapRdn} at the requested position.
- */
- public LdapRdn getLdapRdn(int index) {
- return (LdapRdn) names.get(index);
- }
-
- /**
- * Get the {@link LdapRdn} with the specified key. If there are several
- * {@link Rdn}s with the same key, the first one found (in order of
- * significance) will be returned.
- *
- * @param key Attribute name of the {@link LdapRdn} to retrieve.
- * @return the {@link LdapRdn} with the requested key.
- * @throws IllegalArgumentException if no Rdn matches the given key.
- */
- public LdapRdn getLdapRdn(String key) {
- for (Iterator iter = names.iterator(); iter.hasNext();) {
- LdapRdn rdn = (LdapRdn) iter.next();
- if (ObjectUtils.nullSafeEquals(rdn.getKey(), key)) {
- return rdn;
- }
- }
-
- throw new IllegalArgumentException("No Rdn with the requested key: '" + key + "'");
- }
-
- /**
- * Get the value of the {@link LdapRdnComponent} with the specified key
- * (Attribute value). If there are several Rdns with the same key, the value
- * of the first one found (in order of significance) will be returned.
- *
- * @param key Attribute name of the {@link LdapRdn} to retrieve.
- * @return the value.
- * @throws IllegalArgumentException if no Rdn matches the given key.
- */
- public String getValue(String key) {
- return getLdapRdn(key).getValue();
- }
-
- /**
- * Get the name List.
- *
- * @return the list of {@link LdapRdn}s that this
- * DistinguishedName consists of.
- */
- public List getNames() {
- return names;
- }
-
- /**
- * Get the String representation of this DistinguishedName.
- * Depending on the setting of property
- * org.springframework.ldap.core.spacedDnFormat a space will be
- * added after each comma, to make the result more readable. Default is
- * compact representation, i.e. without any spaces.
- *
- * @return a syntactically correct, properly escaped String representation
- * of the DistinguishedName.
- * @see #SPACED_DN_FORMAT_PROPERTY
- */
- public String toString() {
- String spacedFormatting = System.getProperty(SPACED_DN_FORMAT_PROPERTY);
- if (!StringUtils.hasText(spacedFormatting)) {
- return format(COMPACT);
- }
- else {
- return format(NON_COMPACT);
- }
- }
-
- /**
- * Get the compact String representation of this
- * DistinguishedName. Add no space after each comma, to make it
- * compact.
- *
- * @return a syntactically correct, properly escaped String representation
- * of the DistinguishedName.
- */
- public String toCompactString() {
- return format(COMPACT);
- }
-
- /**
- * Builds a complete LDAP path, ldap encoded, useful as a DN.
- *
- * Always uses lowercase, always separates with ", " i.e. comma and a space.
- *
- * @return the LDAP path.
- */
- public String encode() {
- return format(NON_COMPACT);
- }
-
- private String format(boolean compact) {
- // empty path
- if (names.size() == 0)
- return "";
-
- StringBuffer buffer = new StringBuffer(256);
-
- ListIterator i = names.listIterator(names.size());
- while (i.hasPrevious()) {
- LdapRdn rdn = (LdapRdn) i.previous();
- buffer.append(rdn.getLdapEncoded());
-
- // add comma, except in last iteration
- if (i.hasPrevious()) {
- if (compact) {
- buffer.append(",");
- }
- else {
- buffer.append(", ");
-
- }
- }
- }
-
- return buffer.toString();
- }
-
- /**
- * Builds a complete LDAP path, ldap and url encoded. Separates only with
- * ",".
- *
- * @return the LDAP path, for use in an url.
- */
- public String toUrl() {
- StringBuffer buffer = new StringBuffer(256);
-
- for (int i = names.size() - 1; i >= 0; i--) {
- LdapRdn n = (LdapRdn) names.get(i);
- buffer.append(n.encodeUrl());
- if (i > 0) {
- buffer.append(",");
- }
- }
- return buffer.toString();
- }
-
- /**
- * Determines if this DistinguishedName path contains another
- * path.
- *
- * @param path the path to check.
- * @return true if the supplied path is conained in this
- * instance, false otherwise.
- */
- public boolean contains(DistinguishedName path) {
-
- List shortlist = path.getNames();
-
- // this path must be at least as long
- if (getNames().size() < shortlist.size())
- return false;
-
- // must have names
- if (shortlist.size() == 0)
- return false;
-
- Iterator longiter = getNames().iterator();
- Iterator shortiter = shortlist.iterator();
-
- LdapRdn longname = (LdapRdn) longiter.next();
- LdapRdn shortname = (LdapRdn) shortiter.next();
-
- // find first match
- while (!longname.equals(shortname) && longiter.hasNext()) {
- longname = (LdapRdn) longiter.next();
- }
-
- // Done?
- if (!shortiter.hasNext() && longname.equals(shortname))
- return true;
- if (!longiter.hasNext())
- return false;
-
- // compare
- while (longname.equals(shortname) && longiter.hasNext() && shortiter.hasNext()) {
- longname = (LdapRdn) longiter.next();
- shortname = (LdapRdn) shortiter.next();
- }
-
- // Done
- if (!shortiter.hasNext() && longname.equals(shortname))
- return true;
- else
- return false;
-
- }
-
- /**
- * Add an LDAP path last in this DistinguishedName. E.g.:
- *
- *
- * DistinguishedName name1 = new DistinguishedName("c=SE, dc=jayway, dc=se");
- * DistinguishedName name2 = new DistinguishedName("ou=people");
- * name1.append(name2);
- *
- *
- * will result in ou=people, c=SE, dc=jayway, dc=se
- *
- * @param path the path to append.
- * @return this instance.
- */
- public DistinguishedName append(DistinguishedName path) {
- getNames().addAll(path.getNames());
- return this;
- }
-
- /**
- * Append a new {@link LdapRdn} using the supplied key and value.
- *
- * @param key the key of the {@link LdapRdn}.
- * @param value the value of the {@link LdapRdn}.
- * @return this instance.
- */
- public DistinguishedName append(String key, String value) {
- add(key, value);
- return this;
- }
-
- /**
- * Add an LDAP path first in this DistinguishedName. E.g.:
- *
- *
- * DistinguishedName name1 = new DistinguishedName("ou=people");
- * DistinguishedName name2 = new DistinguishedName("c=SE, dc=jayway, dc=se");
- * name1.prepend(name2);
- *
- *
- * will result in ou=people, c=SE, dc=jayway, dc=se
- *
- * @param path the path to prepend.
- */
- public void prepend(DistinguishedName path) {
- ListIterator i = path.getNames().listIterator(path.getNames().size());
- while (i.hasPrevious()) {
- names.add(0, i.previous());
- }
- }
-
- /**
- * Remove the first part of this DistinguishedName.
- *
- * @return the removed entry.
- */
- public LdapRdn removeFirst() {
- return (LdapRdn) names.remove(0);
- }
-
- /**
- * Remove the supplied path from the beginning of this
- * DistinguishedName if this instance starts with
- * path. Useful for stripping base path suffix from a
- * DistinguishedName.
- *
- * @param path the path to remove from the beginning of this instance.
- */
- public void removeFirst(Name path) {
- if (path != null && this.startsWith(path)) {
- for (int i = 0; i < path.size(); i++)
- this.removeFirst();
- }
- }
-
- /**
- * @see java.lang.Object#clone()
- */
- public Object clone() {
- try {
- DistinguishedName result = (DistinguishedName) super.clone();
- result.names = new LinkedList(names);
- return result;
- }
- catch (CloneNotSupportedException e) {
- log.fatal("CloneNotSupported thrown from superclass - this should not happen");
- throw new RuntimeException("Fatal error in clone", e);
- }
- }
-
- /**
- * @see java.lang.Object#equals(java.lang.Object)
- */
- public boolean equals(Object obj) {
- // A subclass with identical values should NOT be considered equal.
- // EqualsBuilder in commons-lang cannot handle subclasses correctly.
- if (obj == null || obj.getClass() != this.getClass()) {
- return false;
- }
-
- DistinguishedName name = (DistinguishedName) obj;
-
- // compare the lists
- return getNames().equals(name.getNames());
- }
-
- /**
- * @see java.lang.Object#hashCode()
- */
- public int hashCode() {
- return this.getClass().hashCode() ^ getNames().hashCode();
- }
-
- /**
- * Compare this instance to another object. Note that the comparison is done
- * in order of significance, so the most significant Rdn is compared first,
- * then the second and so on.
- *
- * @see javax.naming.Name#compareTo(java.lang.Object)
- */
- public int compareTo(Object obj) {
- DistinguishedName that = (DistinguishedName) obj;
- ListComparator comparator = new ListComparator();
- return comparator.compare(this.names, that.names);
- }
-
- public int size() {
- return names.size();
- }
-
- public boolean isEmpty() {
- return names.size() == 0;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see javax.naming.Name#getAll()
- */
- public Enumeration getAll() {
- LinkedList strings = new LinkedList();
- for (Iterator iter = names.iterator(); iter.hasNext();) {
- LdapRdn rdn = (LdapRdn) iter.next();
- strings.add(rdn.getLdapEncoded());
- }
-
- return Collections.enumeration(strings);
- }
-
- /*
- * (non-Javadoc)
- *
- * @see javax.naming.Name#get(int)
- */
- public String get(int index) {
- LdapRdn rdn = (LdapRdn) names.get(index);
- return rdn.getLdapEncoded();
- }
-
- /*
- * (non-Javadoc)
- *
- * @see javax.naming.Name#getPrefix(int)
- */
- public Name getPrefix(int index) {
- LinkedList newNames = new LinkedList();
- for (int i = 0; i < index; i++) {
- newNames.add(names.get(i));
- }
-
- return new DistinguishedName(newNames);
- }
-
- /*
- * (non-Javadoc)
- *
- * @see javax.naming.Name#getSuffix(int)
- */
- public Name getSuffix(int index) {
- if (index > names.size()) {
- throw new ArrayIndexOutOfBoundsException();
- }
-
- LinkedList newNames = new LinkedList();
- for (int i = index; i < names.size(); i++) {
- newNames.add(names.get(i));
- }
-
- return new DistinguishedName(newNames);
- }
-
- /*
- * (non-Javadoc)
- *
- * @see javax.naming.Name#startsWith(javax.naming.Name)
- */
- public boolean startsWith(Name name) {
- if (name.size() == 0) {
- return false;
- }
-
- DistinguishedName start = null;
- if (name instanceof DistinguishedName) {
- start = (DistinguishedName) name;
- }
- else {
- return false;
- }
-
- if (start.size() > this.size()) {
- return false;
- }
-
- Iterator longiter = names.iterator();
- Iterator shortiter = start.getNames().iterator();
-
- while (shortiter.hasNext()) {
- Object longname = longiter.next();
- Object shortname = shortiter.next();
-
- if (!longname.equals(shortname)) {
- return false;
- }
- }
-
- // All names in shortiter matched.
- return true;
- }
-
- /**
- * Determines if this DistinguishedName ends with a certian
- * path.
- *
- * If the argument path is empty (no names in path) this method will return
- * false.
- *
- * @param name The suffix to check for.
- *
- */
- public boolean endsWith(Name name) {
- DistinguishedName path = null;
- if (name instanceof DistinguishedName) {
- path = (DistinguishedName) name;
- }
- else {
- return false;
- }
-
- List shortlist = path.getNames();
-
- // this path must be at least as long
- if (getNames().size() < shortlist.size())
- return false;
-
- // must have names
- if (shortlist.size() == 0)
- return false;
-
- ListIterator longiter = getNames().listIterator(getNames().size());
- ListIterator shortiter = shortlist.listIterator(shortlist.size());
-
- while (shortiter.hasPrevious()) {
- LdapRdn longname = (LdapRdn) longiter.previous();
- LdapRdn shortname = (LdapRdn) shortiter.previous();
-
- if (!longname.equals(shortname))
- return false;
- }
-
- // if short list ended, all were equal
- return true;
-
- }
-
- /*
- * (non-Javadoc)
- *
- * @see javax.naming.Name#addAll(javax.naming.Name)
- */
- public Name addAll(Name name) throws InvalidNameException {
- return addAll(names.size(), name);
- }
-
- /*
- * (non-Javadoc)
- *
- * @see javax.naming.Name#addAll(int, javax.naming.Name)
- */
- public Name addAll(int arg0, Name name) throws InvalidNameException {
- DistinguishedName distinguishedName = null;
- try {
- distinguishedName = (DistinguishedName) name;
- }
- catch (ClassCastException e) {
- throw new InvalidNameException("Invalid name type");
- }
-
- names.addAll(arg0, distinguishedName.getNames());
- return this;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see javax.naming.Name#add(java.lang.String)
- */
- public Name add(String string) throws InvalidNameException {
- return add(names.size(), string);
- }
-
- /*
- * (non-Javadoc)
- *
- * @see javax.naming.Name#add(int, java.lang.String)
- */
- public Name add(int index, String string) throws InvalidNameException {
- try {
- names.add(index, new LdapRdn(string));
- }
- catch (BadLdapGrammarException e) {
- throw new InvalidNameException("Failed to parse rdn '" + string + "'");
- }
- return this;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see javax.naming.Name#remove(int)
- */
- public Object remove(int arg0) throws InvalidNameException {
- LdapRdn rdn = (LdapRdn) names.remove(arg0);
- return rdn.getLdapEncoded();
- }
-
- /**
- * Remove the last part of this DistinguishedName.
- *
- * @return the removed {@link LdapRdn}.
- */
- public LdapRdn removeLast() {
- return (LdapRdn) names.remove(names.size() - 1);
- }
-
- /**
- * Add a new {@link LdapRdn} using the supplied key and value.
- *
- * @param key the key of the {@link LdapRdn}.
- * @param value the value of the {@link LdapRdn}.
- */
- public void add(String key, String value) {
- names.add(new LdapRdn(key, value));
- }
-
- /**
- * Add the supplied {@link LdapRdn} last in the list of Rdns.
- *
- * @param rdn the {@link LdapRdn} to add.
- */
- public void add(LdapRdn rdn) {
- names.add(rdn);
- }
-
- /**
- * Add the supplied {@link LdapRdn} att the specified index.
- *
- * @param idx the index at which to add the LdapRdn.
- * @param rdn the LdapRdn to add.
- */
- public void add(int idx, LdapRdn rdn) {
- names.add(idx, rdn);
- }
-
- /**
- * Return an immutable copy of this instance. It will not be possible to add
- * or remove any Rdns to or from the returned instance, and the respective
- * Rdns will also be immutable in turn.
- *
- * @return a copy of this instance backed by an immutable list.
- * @since 1.2
- */
- public DistinguishedName immutableDistinguishedName() {
- List listWithImmutableRdns = new ArrayList(names.size());
- for (Iterator iterator = names.iterator(); iterator.hasNext();) {
- LdapRdn rdn = (LdapRdn) iterator.next();
- listWithImmutableRdns.add(rdn.immutableLdapRdn());
- }
-
- return new DistinguishedName(Collections.unmodifiableList(listWithImmutableRdns));
- }
-
- /**
- * Create an immutable DistinguishedName instance, suitable as a constant.
- *
- * @param dnString the DN string to parse.
- * @return an immutable DistinguishedName corresponding to the supplied DN
- * string.
- * @since 1.3
- */
- public static final DistinguishedName immutableDistinguishedName(String dnString) {
- return new DistinguishedName(dnString).immutableDistinguishedName();
- }
-}
+/*
+ * Copyright 2005-2013 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
+ *
+ * http://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.core;
+
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.ldap.BadLdapGrammarException;
+import org.springframework.ldap.support.LdapUtils;
+import org.springframework.ldap.support.ListComparator;
+import org.springframework.util.Assert;
+
+import javax.naming.CompositeName;
+import javax.naming.InvalidNameException;
+import javax.naming.Name;
+import javax.naming.ldap.Rdn;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * Default implementation of a {@link Name} corresponding to an LDAP path. A
+ * Distinguished Name manipulation implementation is included in JDK1.5
+ * (LdapName), but not in prior releases.
+ *
+ * A DistinguishedName is particularly useful when building or
+ * modifying an LDAP path dynamically, as escaping will be taken care of.
+ *
+ * A path is split into several names. The {@link Name} interface specifies that
+ * the most significant part be in position 0.
+ * + * Example: + * + *
+ * Name instances, and consequently DistinguishedName
+ * instances are naturally mutable, which is useful when constructing
+ * DistinguishedNames. Example:
+ *
+ *
+ * DistinguishedName path = new DistinguishedName("dc=jayway,dc=se");
+ * path.add("ou", "People");
+ * path.add("uid", "adam.skogman");
+ * String dn = path.toString();
+ *
+ *
+ * will render uid=adam.skogman,ou=People,dc=jayway,dc=se.
+ *
+ *
+ * NOTE: The fact that DistinguishedName instances are mutable needs to
+ * be taken into careful account, as this means that they may be modified
+ * involuntarily. This means that whenever a DistinguishedName
+ * instance is kept for reference (e.g. for identification of a domain entry) or
+ * as a constant, you should consider getting an immutable copy of the instance
+ * using {@link #immutableDistinguishedName()} or
+ * {@link #immutableDistinguishedName(String)}.
+ *
+ * NB:As of version 1.3 the default toString representation of
+ * DistinguishedName now defaults to a compact one, without spaces between the
+ * respective RDNs. For backward compatibility, set the
+ * {@link #SPACED_DN_FORMAT_PROPERTY} ({@value #SPACED_DN_FORMAT_PROPERTY}) to
+ * true.
+ * @author Adam Skogman
+ * @author Mattias Hellborg Arthursson
+ *
+ * @deprecated As of 2.0 it is recommended to use {@link javax.naming.ldap.LdapName} along with
+ * utility methods in {@link LdapUtils} instead.
+ * @see javax.naming.ldap.LdapName
+ * @see LdapUtils#newLdapName(javax.naming.Name)
+ * @see LdapUtils#newLdapName(String)
+ * @see org.springframework.ldap.support.LdapUtils#emptyLdapName()
+ */
+public class DistinguishedName implements Name {
+ /**
+ * System property that will be inspected to determine whether
+ * {@link #toString()} will format the DN with spaces after each comma or
+ * use a more compact representation, i.e.:
+ * uid=adam.skogman, ou=People, dc=jayway, dc=se rather than
+ * uid=adam.skogman,ou=People,dc=jayway,dc=se. A value other
+ * than null or blank will trigger the spaced format. Default is the compact
+ * representation.
+ *
+ * Valid values are: + *
+ * Valid values are: + *
DistinguishedName from a String.
+ *
+ * @param path a String corresponding to a (syntactically) valid LDAP path.
+ */
+ public DistinguishedName(String path) {
+ if (!StringUtils.hasText(path)) {
+ names = new LinkedList();
+ }
+ else {
+ parse(path);
+ }
+ }
+
+ /**
+ * Construct a new DistinguishedName from the supplied
+ * List of {@link LdapRdn} objects.
+ *
+ * @param list the components that this instance will consist of.
+ */
+ public DistinguishedName(List list) {
+ this.names = list;
+ }
+
+ /**
+ * Construct a new DistinguishedName from the supplied
+ * {@link Name}. The parts of the supplied {@link Name} must be
+ * syntactically correct {@link LdapRdn}s.
+ *
+ * @param name the {@link Name} to construct a new
+ * DistinguishedName from.
+ */
+ public DistinguishedName(Name name) {
+ Assert.notNull(name, "name cannot be null");
+ if (name instanceof CompositeName) {
+ parse(LdapUtils.convertCompositeNameToString((CompositeName) name));
+ return;
+ }
+ names = new LinkedList();
+ for (int i = 0; i < name.size(); i++) {
+ names.add(new LdapRdn(name.get(i)));
+ }
+ }
+
+ /**
+ * Parse the supplied String and make this instance represent the
+ * corresponding distinguished name.
+ *
+ * @param path the LDAP path to parse.
+ */
+ protected void parse(String path) {
+ DnParser parser = DefaultDnParserFactory.createDnParser(unmangleCompositeName(path));
+ DistinguishedName dn;
+ try {
+ dn = parser.dn();
+ }
+ catch (ParseException e) {
+ throw new BadLdapGrammarException("Failed to parse DN", e);
+ }
+ catch (TokenMgrError e) {
+ throw new BadLdapGrammarException("Failed to parse DN", e);
+ }
+ this.names = dn.names;
+ }
+
+ /**
+ * If path is surrounded by quotes, strip them. JNDI considers forward slash
+ * ('/') special, but LDAP doesn't. {@link CompositeName#toString()} tends
+ * to mangle a {@link Name} with a slash by surrounding it with quotes
+ * ('"').
+ *
+ * @param path Path to check and possibly strip.
+ * @return A String with the possibly stripped path.
+ */
+ private String unmangleCompositeName(String path) {
+ String tempPath;
+ // Check if CompositeName has mangled the name with quotes
+ if (path.startsWith("\"") && path.endsWith("\"")) {
+ tempPath = path.substring(1, path.length() - 1);
+ }
+ else {
+ tempPath = path;
+ }
+
+ tempPath = StringUtils.replace(tempPath, MANGLED_DOUBLE_QUOTES, PROPER_DOUBLE_QUOTES);
+ return tempPath;
+ }
+
+ /**
+ * Get the {@link LdapRdn} at a specified position.
+ *
+ * @param index the {@link LdapRdn} to retrieve.
+ * @return the {@link LdapRdn} at the requested position.
+ */
+ public LdapRdn getLdapRdn(int index) {
+ return (LdapRdn) names.get(index);
+ }
+
+ /**
+ * Get the {@link LdapRdn} with the specified key. If there are several
+ * {@link Rdn}s with the same key, the first one found (in order of
+ * significance) will be returned.
+ *
+ * @param key Attribute name of the {@link LdapRdn} to retrieve.
+ * @return the {@link LdapRdn} with the requested key.
+ * @throws IllegalArgumentException if no Rdn matches the given key.
+ */
+ public LdapRdn getLdapRdn(String key) {
+ for (Iterator iter = names.iterator(); iter.hasNext();) {
+ LdapRdn rdn = (LdapRdn) iter.next();
+ if (ObjectUtils.nullSafeEquals(rdn.getKey(), key)) {
+ return rdn;
+ }
+ }
+
+ throw new IllegalArgumentException("No Rdn with the requested key: '" + key + "'");
+ }
+
+ /**
+ * Get the value of the {@link LdapRdnComponent} with the specified key
+ * (Attribute value). If there are several Rdns with the same key, the value
+ * of the first one found (in order of significance) will be returned.
+ *
+ * @param key Attribute name of the {@link LdapRdn} to retrieve.
+ * @return the value.
+ * @throws IllegalArgumentException if no Rdn matches the given key.
+ */
+ public String getValue(String key) {
+ return getLdapRdn(key).getValue();
+ }
+
+ /**
+ * Get the name List.
+ *
+ * @return the list of {@link LdapRdn}s that this
+ * DistinguishedName consists of.
+ */
+ public List getNames() {
+ return names;
+ }
+
+ /**
+ * Get the String representation of this DistinguishedName.
+ * Depending on the setting of property
+ * org.springframework.ldap.core.spacedDnFormat a space will be
+ * added after each comma, to make the result more readable. Default is
+ * compact representation, i.e. without any spaces.
+ *
+ * @return a syntactically correct, properly escaped String representation
+ * of the DistinguishedName.
+ * @see #SPACED_DN_FORMAT_PROPERTY
+ */
+ public String toString() {
+ String spacedFormatting = System.getProperty(SPACED_DN_FORMAT_PROPERTY);
+ if (!StringUtils.hasText(spacedFormatting)) {
+ return format(COMPACT);
+ }
+ else {
+ return format(NON_COMPACT);
+ }
+ }
+
+ /**
+ * Get the compact String representation of this
+ * DistinguishedName. Add no space after each comma, to make it
+ * compact.
+ *
+ * @return a syntactically correct, properly escaped String representation
+ * of the DistinguishedName.
+ */
+ public String toCompactString() {
+ return format(COMPACT);
+ }
+
+ /**
+ * Builds a complete LDAP path, ldap encoded, useful as a DN.
+ *
+ * Always uses lowercase, always separates with ", " i.e. comma and a space.
+ *
+ * @return the LDAP path.
+ */
+ public String encode() {
+ return format(NON_COMPACT);
+ }
+
+ private String format(boolean compact) {
+ // empty path
+ if (names.size() == 0)
+ return "";
+
+ StringBuffer buffer = new StringBuffer(256);
+
+ ListIterator i = names.listIterator(names.size());
+ while (i.hasPrevious()) {
+ LdapRdn rdn = (LdapRdn) i.previous();
+ buffer.append(rdn.getLdapEncoded());
+
+ // add comma, except in last iteration
+ if (i.hasPrevious()) {
+ if (compact) {
+ buffer.append(",");
+ }
+ else {
+ buffer.append(", ");
+
+ }
+ }
+ }
+
+ return buffer.toString();
+ }
+
+ /**
+ * Builds a complete LDAP path, ldap and url encoded. Separates only with
+ * ",".
+ *
+ * @return the LDAP path, for use in an url.
+ */
+ public String toUrl() {
+ StringBuffer buffer = new StringBuffer(256);
+
+ for (int i = names.size() - 1; i >= 0; i--) {
+ LdapRdn n = (LdapRdn) names.get(i);
+ buffer.append(n.encodeUrl());
+ if (i > 0) {
+ buffer.append(",");
+ }
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Determines if this DistinguishedName path contains another
+ * path.
+ *
+ * @param path the path to check.
+ * @return true if the supplied path is conained in this
+ * instance, false otherwise.
+ */
+ public boolean contains(DistinguishedName path) {
+
+ List shortlist = path.getNames();
+
+ // this path must be at least as long
+ if (getNames().size() < shortlist.size())
+ return false;
+
+ // must have names
+ if (shortlist.size() == 0)
+ return false;
+
+ Iterator longiter = getNames().iterator();
+ Iterator shortiter = shortlist.iterator();
+
+ LdapRdn longname = (LdapRdn) longiter.next();
+ LdapRdn shortname = (LdapRdn) shortiter.next();
+
+ // find first match
+ while (!longname.equals(shortname) && longiter.hasNext()) {
+ longname = (LdapRdn) longiter.next();
+ }
+
+ // Done?
+ if (!shortiter.hasNext() && longname.equals(shortname))
+ return true;
+ if (!longiter.hasNext())
+ return false;
+
+ // compare
+ while (longname.equals(shortname) && longiter.hasNext() && shortiter.hasNext()) {
+ longname = (LdapRdn) longiter.next();
+ shortname = (LdapRdn) shortiter.next();
+ }
+
+ // Done
+ if (!shortiter.hasNext() && longname.equals(shortname))
+ return true;
+ else
+ return false;
+
+ }
+
+ /**
+ * Add an LDAP path last in this DistinguishedName. E.g.:
+ *
+ *
+ * DistinguishedName name1 = new DistinguishedName("c=SE, dc=jayway, dc=se");
+ * DistinguishedName name2 = new DistinguishedName("ou=people");
+ * name1.append(name2);
+ *
+ *
+ * will result in ou=people, c=SE, dc=jayway, dc=se
+ *
+ * @param path the path to append.
+ * @return this instance.
+ */
+ public DistinguishedName append(DistinguishedName path) {
+ getNames().addAll(path.getNames());
+ return this;
+ }
+
+ /**
+ * Append a new {@link LdapRdn} using the supplied key and value.
+ *
+ * @param key the key of the {@link LdapRdn}.
+ * @param value the value of the {@link LdapRdn}.
+ * @return this instance.
+ */
+ public DistinguishedName append(String key, String value) {
+ add(key, value);
+ return this;
+ }
+
+ /**
+ * Add an LDAP path first in this DistinguishedName. E.g.:
+ *
+ *
+ * DistinguishedName name1 = new DistinguishedName("ou=people");
+ * DistinguishedName name2 = new DistinguishedName("c=SE, dc=jayway, dc=se");
+ * name1.prepend(name2);
+ *
+ *
+ * will result in ou=people, c=SE, dc=jayway, dc=se
+ *
+ * @param path the path to prepend.
+ */
+ public void prepend(DistinguishedName path) {
+ ListIterator i = path.getNames().listIterator(path.getNames().size());
+ while (i.hasPrevious()) {
+ names.add(0, i.previous());
+ }
+ }
+
+ /**
+ * Remove the first part of this DistinguishedName.
+ *
+ * @return the removed entry.
+ */
+ public LdapRdn removeFirst() {
+ return (LdapRdn) names.remove(0);
+ }
+
+ /**
+ * Remove the supplied path from the beginning of this
+ * DistinguishedName if this instance starts with
+ * path. Useful for stripping base path suffix from a
+ * DistinguishedName.
+ *
+ * @param path the path to remove from the beginning of this instance.
+ */
+ public void removeFirst(Name path) {
+ if (path != null && this.startsWith(path)) {
+ for (int i = 0; i < path.size(); i++)
+ this.removeFirst();
+ }
+ }
+
+ /**
+ * @see java.lang.Object#clone()
+ */
+ public Object clone() {
+ try {
+ DistinguishedName result = (DistinguishedName) super.clone();
+ result.names = new LinkedList(names);
+ return result;
+ }
+ catch (CloneNotSupportedException e) {
+ log.error("CloneNotSupported thrown from superclass - this should not happen");
+ throw new RuntimeException("Fatal error in clone", e);
+ }
+ }
+
+ /**
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object obj) {
+ // A subclass with identical values should NOT be considered equal.
+ // EqualsBuilder in commons-lang cannot handle subclasses correctly.
+ if (obj == null || obj.getClass() != this.getClass()) {
+ return false;
+ }
+
+ DistinguishedName name = (DistinguishedName) obj;
+
+ // compare the lists
+ return getNames().equals(name.getNames());
+ }
+
+ /**
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode() {
+ return this.getClass().hashCode() ^ getNames().hashCode();
+ }
+
+ /**
+ * Compare this instance to another object. Note that the comparison is done
+ * in order of significance, so the most significant Rdn is compared first,
+ * then the second and so on.
+ *
+ * @see javax.naming.Name#compareTo(java.lang.Object)
+ */
+ public int compareTo(Object obj) {
+ DistinguishedName that = (DistinguishedName) obj;
+ ListComparator comparator = new ListComparator();
+ return comparator.compare(this.names, that.names);
+ }
+
+ public int size() {
+ return names.size();
+ }
+
+ public boolean isEmpty() {
+ return names.size() == 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.naming.Name#getAll()
+ */
+ public Enumeration getAll() {
+ LinkedList strings = new LinkedList();
+ for (Iterator iter = names.iterator(); iter.hasNext();) {
+ LdapRdn rdn = (LdapRdn) iter.next();
+ strings.add(rdn.getLdapEncoded());
+ }
+
+ return Collections.enumeration(strings);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.naming.Name#get(int)
+ */
+ public String get(int index) {
+ LdapRdn rdn = (LdapRdn) names.get(index);
+ return rdn.getLdapEncoded();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.naming.Name#getPrefix(int)
+ */
+ public Name getPrefix(int index) {
+ LinkedList newNames = new LinkedList();
+ for (int i = 0; i < index; i++) {
+ newNames.add(names.get(i));
+ }
+
+ return new DistinguishedName(newNames);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.naming.Name#getSuffix(int)
+ */
+ public Name getSuffix(int index) {
+ if (index > names.size()) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ LinkedList newNames = new LinkedList();
+ for (int i = index; i < names.size(); i++) {
+ newNames.add(names.get(i));
+ }
+
+ return new DistinguishedName(newNames);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.naming.Name#startsWith(javax.naming.Name)
+ */
+ public boolean startsWith(Name name) {
+ if (name.size() == 0) {
+ return false;
+ }
+
+ DistinguishedName start = null;
+ if (name instanceof DistinguishedName) {
+ start = (DistinguishedName) name;
+ }
+ else {
+ return false;
+ }
+
+ if (start.size() > this.size()) {
+ return false;
+ }
+
+ Iterator longiter = names.iterator();
+ Iterator shortiter = start.getNames().iterator();
+
+ while (shortiter.hasNext()) {
+ Object longname = longiter.next();
+ Object shortname = shortiter.next();
+
+ if (!longname.equals(shortname)) {
+ return false;
+ }
+ }
+
+ // All names in shortiter matched.
+ return true;
+ }
+
+ /**
+ * Determines if this DistinguishedName ends with a certian
+ * path.
+ *
+ * If the argument path is empty (no names in path) this method will return
+ * false.
+ *
+ * @param name The suffix to check for.
+ *
+ */
+ public boolean endsWith(Name name) {
+ DistinguishedName path = null;
+ if (name instanceof DistinguishedName) {
+ path = (DistinguishedName) name;
+ }
+ else {
+ return false;
+ }
+
+ List shortlist = path.getNames();
+
+ // this path must be at least as long
+ if (getNames().size() < shortlist.size())
+ return false;
+
+ // must have names
+ if (shortlist.size() == 0)
+ return false;
+
+ ListIterator longiter = getNames().listIterator(getNames().size());
+ ListIterator shortiter = shortlist.listIterator(shortlist.size());
+
+ while (shortiter.hasPrevious()) {
+ LdapRdn longname = (LdapRdn) longiter.previous();
+ LdapRdn shortname = (LdapRdn) shortiter.previous();
+
+ if (!longname.equals(shortname))
+ return false;
+ }
+
+ // if short list ended, all were equal
+ return true;
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.naming.Name#addAll(javax.naming.Name)
+ */
+ public Name addAll(Name name) throws InvalidNameException {
+ return addAll(names.size(), name);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.naming.Name#addAll(int, javax.naming.Name)
+ */
+ public Name addAll(int arg0, Name name) throws InvalidNameException {
+ DistinguishedName distinguishedName = null;
+ try {
+ distinguishedName = (DistinguishedName) name;
+ }
+ catch (ClassCastException e) {
+ throw new InvalidNameException("Invalid name type");
+ }
+
+ names.addAll(arg0, distinguishedName.getNames());
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.naming.Name#add(java.lang.String)
+ */
+ public Name add(String string) throws InvalidNameException {
+ return add(names.size(), string);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.naming.Name#add(int, java.lang.String)
+ */
+ public Name add(int index, String string) throws InvalidNameException {
+ try {
+ names.add(index, new LdapRdn(string));
+ }
+ catch (BadLdapGrammarException e) {
+ throw new InvalidNameException("Failed to parse rdn '" + string + "'");
+ }
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.naming.Name#remove(int)
+ */
+ public Object remove(int arg0) throws InvalidNameException {
+ LdapRdn rdn = (LdapRdn) names.remove(arg0);
+ return rdn.getLdapEncoded();
+ }
+
+ /**
+ * Remove the last part of this DistinguishedName.
+ *
+ * @return the removed {@link LdapRdn}.
+ */
+ public LdapRdn removeLast() {
+ return (LdapRdn) names.remove(names.size() - 1);
+ }
+
+ /**
+ * Add a new {@link LdapRdn} using the supplied key and value.
+ *
+ * @param key the key of the {@link LdapRdn}.
+ * @param value the value of the {@link LdapRdn}.
+ */
+ public void add(String key, String value) {
+ names.add(new LdapRdn(key, value));
+ }
+
+ /**
+ * Add the supplied {@link LdapRdn} last in the list of Rdns.
+ *
+ * @param rdn the {@link LdapRdn} to add.
+ */
+ public void add(LdapRdn rdn) {
+ names.add(rdn);
+ }
+
+ /**
+ * Add the supplied {@link LdapRdn} att the specified index.
+ *
+ * @param idx the index at which to add the LdapRdn.
+ * @param rdn the LdapRdn to add.
+ */
+ public void add(int idx, LdapRdn rdn) {
+ names.add(idx, rdn);
+ }
+
+ /**
+ * Return an immutable copy of this instance. It will not be possible to add
+ * or remove any Rdns to or from the returned instance, and the respective
+ * Rdns will also be immutable in turn.
+ *
+ * @return a copy of this instance backed by an immutable list.
+ * @since 1.2
+ */
+ public DistinguishedName immutableDistinguishedName() {
+ List listWithImmutableRdns = new ArrayList(names.size());
+ for (Iterator iterator = names.iterator(); iterator.hasNext();) {
+ LdapRdn rdn = (LdapRdn) iterator.next();
+ listWithImmutableRdns.add(rdn.immutableLdapRdn());
+ }
+
+ return new DistinguishedName(Collections.unmodifiableList(listWithImmutableRdns));
+ }
+
+ /**
+ * Create an immutable DistinguishedName instance, suitable as a constant.
+ *
+ * @param dnString the DN string to parse.
+ * @return an immutable DistinguishedName corresponding to the supplied DN
+ * string.
+ * @since 1.3
+ */
+ public static final DistinguishedName immutableDistinguishedName(String dnString) {
+ return new DistinguishedName(dnString).immutableDistinguishedName();
+ }
+}
diff --git a/core/src/main/java/org/springframework/ldap/core/LdapRdnComponent.java b/core/src/main/java/org/springframework/ldap/core/LdapRdnComponent.java
index 941e361e..fc7aa884 100644
--- a/core/src/main/java/org/springframework/ldap/core/LdapRdnComponent.java
+++ b/core/src/main/java/org/springframework/ldap/core/LdapRdnComponent.java
@@ -1,264 +1,264 @@
-/*
- * Copyright 2005-2013 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
- *
- * http://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.core;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.ldap.support.LdapEncoder;
-import org.springframework.util.Assert;
-import org.springframework.util.StringUtils;
-
-import java.io.Serializable;
-import java.net.URI;
-import java.net.URISyntaxException;
-
-/**
- * Represents part of an LdapRdn. As specified in RFC2253 an LdapRdn may be
- * composed of several attributes, separated by "+". An
- * LdapRdnComponent represents one of these attributes.
- *
- * @author Mattias Hellborg Arthursson
- * @deprecated {@link DistinguishedName} and associated classes are deprecated as of 2.0.
- */
-public class LdapRdnComponent implements Comparable, Serializable {
- private static final long serialVersionUID = -3296747972616243038L;
-
- private static final Log log = LogFactory.getLog(LdapRdnComponent.class);
-
- public static final boolean DONT_DECODE_VALUE = false;
-
- private String key;
-
- private String value;
-
- /**
- * Constructs an LdapRdnComponent without decoding the value.
- *
- * @param key the Attribute name.
- * @param value the Attribute value.
- */
- public LdapRdnComponent(String key, String value) {
- this(key, value, DONT_DECODE_VALUE);
- }
-
- /**
- * Constructs an LdapRdnComponent, optionally decoding the value.
- *
- * Depending on the value of the "key case fold" System property, the keys
- * will be lowercased, uppercased, or preserve their original case. Default
- * is to convert them to lowercase.
- *
- * @param key the Attribute name.
- * @param value the Attribute value.
- * @param decodeValue if true the value is decoded (typically
- * used when a DN is parsed from a String), otherwise the value is used as
- * specified.
- * @see DistinguishedName#KEY_CASE_FOLD_PROPERTY
- */
- public LdapRdnComponent(String key, String value, boolean decodeValue) {
- Assert.hasText(key, "Key must not be empty");
- Assert.hasText(value, "Value must not be empty");
-
- String caseFold = System.getProperty(DistinguishedName.KEY_CASE_FOLD_PROPERTY);
- if (!StringUtils.hasText(caseFold) || caseFold.equals(DistinguishedName.KEY_CASE_FOLD_LOWER)) {
- this.key = key.toLowerCase();
- } else if (caseFold.equals(DistinguishedName.KEY_CASE_FOLD_UPPER)) {
- this.key = key.toUpperCase();
- } else if (caseFold.equals(DistinguishedName.KEY_CASE_FOLD_NONE)) {
- this.key = key;
- } else {
- log
- .warn("\"" + caseFold + "\" invalid property value for " + DistinguishedName.KEY_CASE_FOLD_PROPERTY
- + "; expected \"" + DistinguishedName.KEY_CASE_FOLD_LOWER + "\", \""
- + DistinguishedName.KEY_CASE_FOLD_UPPER + "\", or \""
- + DistinguishedName.KEY_CASE_FOLD_NONE + "\"");
- this.key = key.toLowerCase();
- }
- if (decodeValue) {
- this.value = LdapEncoder.nameDecode(value);
- }
- else {
- this.value = value;
- }
- }
-
- /**
- * Get the key (Attribute name) of this component.
- *
- * @return the key.
- */
- public String getKey() {
- return key;
- }
-
- /**
- * Set the key (Attribute name) of this component.
- *
- * @param key the key.
- * @deprecated Using this method changes the internal state of surrounding
- * DistinguishedName instance. This should be avoided.
- */
- public void setKey(String key) {
- Assert.hasText(key, "Key must not be empty");
- this.key = key;
- }
-
- /**
- * Get the (Attribute) value of this component.
- *
- * @return the value.
- */
- public String getValue() {
- return value;
- }
-
- /**
- * Set the (Attribute) value of this component.
- *
- * @param value the value.
- * @deprecated Using this method changes the internal state of surrounding
- * DistinguishedName instance. This should be avoided.
- */
- public void setValue(String value) {
- Assert.hasText(value, "Value must not be empty");
- this.value = value;
- }
-
- /**
- * Encode key and value to ldap.
- *
- * @return Properly ldap escaped rdn.
- */
- protected String encodeLdap() {
- StringBuffer buff = new StringBuffer(key.length() + value.length() * 2);
-
- buff.append(key);
- buff.append('=');
- buff.append(LdapEncoder.nameEncode(value));
-
- return buff.toString();
- }
-
- /*
- * (non-Javadoc)
- *
- * @see java.lang.Object#toString()
- */
- public String toString() {
- return getLdapEncoded();
- }
-
- /**
- * @return The LdapRdn as a string where the value is LDAP-encoded.
- */
- public String getLdapEncoded() {
- return encodeLdap();
- }
-
- /**
- * Get a String representation of this instance for use in URLs.
- *
- * @return a properly URL encoded representation of this instancs.
- */
- public String encodeUrl() {
- // Use the URI class to properly URL encode the value.
- try {
- URI valueUri = new URI(null, null, value, null);
- return key + "=" + valueUri.toString();
- }
- catch (URISyntaxException e) {
- // This should really never happen...
- return key + "=" + "value";
- }
- }
-
- /*
- * (non-Javadoc)
- *
- * @see java.lang.Object#hashCode()
- */
- public int hashCode() {
- return key.toUpperCase().hashCode() ^ value.toUpperCase().hashCode();
- }
-
- /*
- * (non-Javadoc)
- *
- * @see java.lang.Object#equals(java.lang.Object)
- */
- public boolean equals(Object obj) {
- // Slightly more lenient equals comparison here to enable immutable
- // instances to equal mutable ones.
- if (obj != null && obj instanceof LdapRdnComponent) {
- LdapRdnComponent that = (LdapRdnComponent) obj;
- // It's safe to compare directly against key and value,
- // because they are validated not to be null on instance creation.
- return this.key.equalsIgnoreCase(that.key)
- && this.value.equalsIgnoreCase(that.value);
-
- }
- else {
- return false;
- }
- }
-
- /**
- * Compare this instance to the supplied object.
- *
- * @param obj the object to compare to.
- * @throws ClassCastException if the object is not possible to cast to an
- * LdapRdnComponent.
- */
- public int compareTo(Object obj) {
- LdapRdnComponent that = (LdapRdnComponent) obj;
-
- // It's safe to compare directly against key and value,
- // because they are validated not to be null on instance creation.
- int keyCompare = this.key.toLowerCase().compareTo(that.key.toLowerCase());
- if(keyCompare == 0) {
- return this.value.toLowerCase().compareTo(that.value.toLowerCase());
- } else {
- return keyCompare;
- }
- }
-
- /**
- * Create an immutable copy of this instance. It will not be possible to
- * modify the key or the value of the returned instance.
- *
- * @return an immutable copy of this instance.
- * @since 1.3
- */
- public LdapRdnComponent immutableLdapRdnComponent() {
- return new ImmutableLdapRdnComponent(key, value);
- }
-
- private static class ImmutableLdapRdnComponent extends LdapRdnComponent {
- private static final long serialVersionUID = -7099970046426346567L;
-
- public ImmutableLdapRdnComponent(String key, String value) {
- super(key, value);
- }
-
- public void setKey(String key) {
- throw new UnsupportedOperationException("SetValue not supported for this immutable LdapRdnComponent");
- }
-
- public void setValue(String value) {
- throw new UnsupportedOperationException("SetKey not supported for this immutable LdapRdnComponent");
- }
- }
-}
+/*
+ * Copyright 2005-2013 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
+ *
+ * http://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.core;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.ldap.support.LdapEncoder;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+import java.io.Serializable;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * Represents part of an LdapRdn. As specified in RFC2253 an LdapRdn may be
+ * composed of several attributes, separated by "+". An
+ * LdapRdnComponent represents one of these attributes.
+ *
+ * @author Mattias Hellborg Arthursson
+ * @deprecated {@link DistinguishedName} and associated classes are deprecated as of 2.0.
+ */
+public class LdapRdnComponent implements Comparable, Serializable {
+ private static final long serialVersionUID = -3296747972616243038L;
+
+ private static final Logger log = LoggerFactory.getLogger(LdapRdnComponent.class);
+
+ public static final boolean DONT_DECODE_VALUE = false;
+
+ private String key;
+
+ private String value;
+
+ /**
+ * Constructs an LdapRdnComponent without decoding the value.
+ *
+ * @param key the Attribute name.
+ * @param value the Attribute value.
+ */
+ public LdapRdnComponent(String key, String value) {
+ this(key, value, DONT_DECODE_VALUE);
+ }
+
+ /**
+ * Constructs an LdapRdnComponent, optionally decoding the value.
+ *
+ * Depending on the value of the "key case fold" System property, the keys
+ * will be lowercased, uppercased, or preserve their original case. Default
+ * is to convert them to lowercase.
+ *
+ * @param key the Attribute name.
+ * @param value the Attribute value.
+ * @param decodeValue if true the value is decoded (typically
+ * used when a DN is parsed from a String), otherwise the value is used as
+ * specified.
+ * @see DistinguishedName#KEY_CASE_FOLD_PROPERTY
+ */
+ public LdapRdnComponent(String key, String value, boolean decodeValue) {
+ Assert.hasText(key, "Key must not be empty");
+ Assert.hasText(value, "Value must not be empty");
+
+ String caseFold = System.getProperty(DistinguishedName.KEY_CASE_FOLD_PROPERTY);
+ if (!StringUtils.hasText(caseFold) || caseFold.equals(DistinguishedName.KEY_CASE_FOLD_LOWER)) {
+ this.key = key.toLowerCase();
+ } else if (caseFold.equals(DistinguishedName.KEY_CASE_FOLD_UPPER)) {
+ this.key = key.toUpperCase();
+ } else if (caseFold.equals(DistinguishedName.KEY_CASE_FOLD_NONE)) {
+ this.key = key;
+ } else {
+ log
+ .warn("\"" + caseFold + "\" invalid property value for " + DistinguishedName.KEY_CASE_FOLD_PROPERTY
+ + "; expected \"" + DistinguishedName.KEY_CASE_FOLD_LOWER + "\", \""
+ + DistinguishedName.KEY_CASE_FOLD_UPPER + "\", or \""
+ + DistinguishedName.KEY_CASE_FOLD_NONE + "\"");
+ this.key = key.toLowerCase();
+ }
+ if (decodeValue) {
+ this.value = LdapEncoder.nameDecode(value);
+ }
+ else {
+ this.value = value;
+ }
+ }
+
+ /**
+ * Get the key (Attribute name) of this component.
+ *
+ * @return the key.
+ */
+ public String getKey() {
+ return key;
+ }
+
+ /**
+ * Set the key (Attribute name) of this component.
+ *
+ * @param key the key.
+ * @deprecated Using this method changes the internal state of surrounding
+ * DistinguishedName instance. This should be avoided.
+ */
+ public void setKey(String key) {
+ Assert.hasText(key, "Key must not be empty");
+ this.key = key;
+ }
+
+ /**
+ * Get the (Attribute) value of this component.
+ *
+ * @return the value.
+ */
+ public String getValue() {
+ return value;
+ }
+
+ /**
+ * Set the (Attribute) value of this component.
+ *
+ * @param value the value.
+ * @deprecated Using this method changes the internal state of surrounding
+ * DistinguishedName instance. This should be avoided.
+ */
+ public void setValue(String value) {
+ Assert.hasText(value, "Value must not be empty");
+ this.value = value;
+ }
+
+ /**
+ * Encode key and value to ldap.
+ *
+ * @return Properly ldap escaped rdn.
+ */
+ protected String encodeLdap() {
+ StringBuffer buff = new StringBuffer(key.length() + value.length() * 2);
+
+ buff.append(key);
+ buff.append('=');
+ buff.append(LdapEncoder.nameEncode(value));
+
+ return buff.toString();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ return getLdapEncoded();
+ }
+
+ /**
+ * @return The LdapRdn as a string where the value is LDAP-encoded.
+ */
+ public String getLdapEncoded() {
+ return encodeLdap();
+ }
+
+ /**
+ * Get a String representation of this instance for use in URLs.
+ *
+ * @return a properly URL encoded representation of this instancs.
+ */
+ public String encodeUrl() {
+ // Use the URI class to properly URL encode the value.
+ try {
+ URI valueUri = new URI(null, null, value, null);
+ return key + "=" + valueUri.toString();
+ }
+ catch (URISyntaxException e) {
+ // This should really never happen...
+ return key + "=" + "value";
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode() {
+ return key.toUpperCase().hashCode() ^ value.toUpperCase().hashCode();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object obj) {
+ // Slightly more lenient equals comparison here to enable immutable
+ // instances to equal mutable ones.
+ if (obj != null && obj instanceof LdapRdnComponent) {
+ LdapRdnComponent that = (LdapRdnComponent) obj;
+ // It's safe to compare directly against key and value,
+ // because they are validated not to be null on instance creation.
+ return this.key.equalsIgnoreCase(that.key)
+ && this.value.equalsIgnoreCase(that.value);
+
+ }
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * Compare this instance to the supplied object.
+ *
+ * @param obj the object to compare to.
+ * @throws ClassCastException if the object is not possible to cast to an
+ * LdapRdnComponent.
+ */
+ public int compareTo(Object obj) {
+ LdapRdnComponent that = (LdapRdnComponent) obj;
+
+ // It's safe to compare directly against key and value,
+ // because they are validated not to be null on instance creation.
+ int keyCompare = this.key.toLowerCase().compareTo(that.key.toLowerCase());
+ if(keyCompare == 0) {
+ return this.value.toLowerCase().compareTo(that.value.toLowerCase());
+ } else {
+ return keyCompare;
+ }
+ }
+
+ /**
+ * Create an immutable copy of this instance. It will not be possible to
+ * modify the key or the value of the returned instance.
+ *
+ * @return an immutable copy of this instance.
+ * @since 1.3
+ */
+ public LdapRdnComponent immutableLdapRdnComponent() {
+ return new ImmutableLdapRdnComponent(key, value);
+ }
+
+ private static class ImmutableLdapRdnComponent extends LdapRdnComponent {
+ private static final long serialVersionUID = -7099970046426346567L;
+
+ public ImmutableLdapRdnComponent(String key, String value) {
+ super(key, value);
+ }
+
+ public void setKey(String key) {
+ throw new UnsupportedOperationException("SetValue not supported for this immutable LdapRdnComponent");
+ }
+
+ public void setValue(String value) {
+ throw new UnsupportedOperationException("SetKey not supported for this immutable LdapRdnComponent");
+ }
+ }
+}
diff --git a/core/src/main/java/org/springframework/ldap/core/LdapTemplate.java b/core/src/main/java/org/springframework/ldap/core/LdapTemplate.java
index af84a063..1501947b 100644
--- a/core/src/main/java/org/springframework/ldap/core/LdapTemplate.java
+++ b/core/src/main/java/org/springframework/ldap/core/LdapTemplate.java
@@ -1,1910 +1,1910 @@
-/*
- * Copyright 2005-2013 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
- *
- * http://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.core;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.dao.EmptyResultDataAccessException;
-import org.springframework.dao.IncorrectResultSizeDataAccessException;
-import org.springframework.ldap.AuthenticationException;
-import org.springframework.ldap.NamingException;
-import org.springframework.ldap.UncategorizedLdapException;
-import org.springframework.ldap.filter.Filter;
-import org.springframework.ldap.odm.core.ObjectDirectoryMapper;
-import org.springframework.ldap.odm.core.OdmException;
-import org.springframework.ldap.odm.core.impl.DefaultObjectDirectoryMapper;
-import org.springframework.ldap.query.LdapQuery;
-import org.springframework.ldap.support.LdapUtils;
-import org.springframework.util.Assert;
-
-import javax.naming.Binding;
-import javax.naming.Name;
-import javax.naming.NameClassPair;
-import javax.naming.NameNotFoundException;
-import javax.naming.NamingEnumeration;
-import javax.naming.PartialResultException;
-import javax.naming.directory.Attributes;
-import javax.naming.directory.DirContext;
-import javax.naming.directory.ModificationItem;
-import javax.naming.directory.SearchControls;
-import javax.naming.ldap.LdapName;
-import java.util.List;
-
-/**
- * Executes core LDAP functionality and helps to avoid common errors, relieving
- * the user of the burden of looking up contexts, looping through
- * NamingEnumerations and closing contexts.
- *
- * Note for Active Directory (AD) users: AD servers are apparently unable
- * to handle referrals automatically, which causes a
- * PartialResultException to be thrown whenever a referral is
- * encountered in a search. To avoid this, set the
- * ignorePartialResultException property to true.
- * There is currently no way of manually handling these referrals in the form of
- * ReferralException, i.e. either you get the exception (and your
- * results are lost) or all referrals are ignored (if the server is unable to
- * handle them properly. Neither is there any simple way to get notified that a
- * PartialResultException has been ignored (other than in the log).
- *
- * @see org.springframework.ldap.core.ContextSource
- *
- * @author Mattias Hellborg Arthursson
- * @author Ulrik Sandberg
- */
-public class LdapTemplate implements LdapOperations, InitializingBean {
-
- private static final Log log = LogFactory.getLog(LdapTemplate.class);
-
- private static final boolean DONT_RETURN_OBJ_FLAG = false;
-
- private static final boolean RETURN_OBJ_FLAG = true;
-
- private static final String[] ALL_ATTRIBUTES = null;
-
- private ContextSource contextSource;
-
- private boolean ignorePartialResultException = false;
-
- private boolean ignoreNameNotFoundException = false;
-
- private int defaultSearchScope = SearchControls.SUBTREE_SCOPE;
-
- private int defaultTimeLimit = 0;
-
- private int defaultCountLimit = 0;
-
- private ObjectDirectoryMapper odm = new DefaultObjectDirectoryMapper();
-
- /**
- * Constructor for bean usage.
- */
- public LdapTemplate() {
- }
-
- /**
- * Constructor to setup instance directly.
- *
- * @param contextSource the ContextSource to use.
- */
- public LdapTemplate(ContextSource contextSource) {
- this.contextSource = contextSource;
- }
-
- /**
- * Set the ContextSource. Call this method when the default constructor has
- * been used.
- *
- * @param contextSource the ContextSource.
- */
- public void setContextSource(ContextSource contextSource) {
- this.contextSource = contextSource;
- }
-
- @Override
- public ObjectDirectoryMapper getObjectDirectoryMapper() {
- return odm;
- }
-
- /**
- * Set the ObjectDirectoryMapper instance to use.
- *
- * @param odm the ObejctDirectoryMapper to use.
- * @since 2.0
- */
- public void setObjectDirectoryMapper(ObjectDirectoryMapper odm) {
- this.odm = odm;
- }
-
- /**
- * Get the ContextSource.
- *
- * @return the ContextSource.
- */
- public ContextSource getContextSource() {
- return contextSource;
- }
-
- /**
- * Specify whether NameNotFoundException should be ignored in
- * searches. In previous version, NameNotFoundException caused
- * by the search base not being found was silently ignored. The default
- * behavior is now to treat this as an error (as it should), and to convert
- * and re-throw the exception. The ability to revert to the previous
- * behavior still exists. The only difference is that the incident is in
- * that case no longer silently ignored, but logged as a warning.
- *
- * @param ignore true if NameNotFoundException
- * should be ignored in searches, false otherwise. Default is
- * false.
- *
- * @since 1.3
- */
- public void setIgnoreNameNotFoundException(boolean ignore) {
- this.ignoreNameNotFoundException = ignore;
- }
-
- /**
- * Specify whether PartialResultException should be ignored in
- * searches. AD servers typically have a problem with referrals. Normally a
- * referral should be followed automatically, but this does not seem to work
- * with AD servers. The problem manifests itself with a a
- * PartialResultException being thrown when a referral is
- * encountered by the server. Setting this property to true
- * presents a workaround to this problem by causing
- * PartialResultException to be ignored, so that the search
- * method returns normally. Default value of this parameter is
- * false.
- *
- * @param ignore true if PartialResultException
- * should be ignored in searches, false otherwise. Default is
- * false.
- */
- public void setIgnorePartialResultException(boolean ignore) {
- this.ignorePartialResultException = ignore;
- }
-
- /**
- * Set the default scope to be used in searches if not explicitly specified.
- * Default is {@link SearchControls.SUBTREE_SCOPE}.
- *
- * @param defaultSearchScope the default search scope to use in searches.
- * One of {@link SearchControls.OBJECT_SCOPE},
- * {@link SearchControls.ONELEVEL_SCOPE},
- * or {@link SearchControls.SUBTREE_SCOPE}
- * @since 2.0
- */
- public void setDefaultSearchScope(int defaultSearchScope) {
- this.defaultSearchScope = defaultSearchScope;
- }
-
- /**
- * Set the default time limit be used in searches if not explicitly specified.
- * Default is 0, indicating no time limit.
- *
- * @param defaultTimeLimit the default time limit to use in searches.
- * @since 2.0
- */
- public void setDefaultTimeLimit(int defaultTimeLimit) {
- this.defaultTimeLimit = defaultTimeLimit;
- }
-
- /**
- * Set the default count limit be used in searches if not explicitly specified.
- * Default is 0, indicating no count limit.
- *
- * @param defaultCountLimit the default count limit to use in searches.
- * @since 2.0
- */
- public void setDefaultCountLimit(int defaultCountLimit) {
- this.defaultCountLimit = defaultCountLimit;
- }
-
- /*
- * @see
- * org.springframework.ldap.core.LdapOperations#search(javax.naming.Name,
- * java.lang.String, int, boolean,
- * org.springframework.ldap.core.NameClassPairCallbackHandler)
- */
- public void search(Name base, String filter, int searchScope, boolean returningObjFlag,
- NameClassPairCallbackHandler handler) {
-
- search(base, filter, getDefaultSearchControls(searchScope, returningObjFlag, ALL_ATTRIBUTES), handler);
- }
-
- /*
- * @see
- * org.springframework.ldap.core.LdapOperations#search(java.lang.String,
- * java.lang.String, int, boolean,
- * org.springframework.ldap.core.NameClassPairCallbackHandler)
- */
- public void search(String base, String filter, int searchScope, boolean returningObjFlag,
- NameClassPairCallbackHandler handler) {
-
- search(base, filter, getDefaultSearchControls(searchScope, returningObjFlag, ALL_ATTRIBUTES), handler);
- }
-
- /*
- * @see
- * org.springframework.ldap.core.LdapOperations#search(javax.naming.Name,
- * java.lang.String, javax.naming.directory.SearchControls,
- * org.springframework.ldap.core.NameClassPairCallbackHandler)
- */
- public void search(final Name base, final String filter, final SearchControls controls,
- NameClassPairCallbackHandler handler) {
-
- // Create a SearchExecutor to perform the search.
- SearchExecutor se = new SearchExecutor() {
- public NamingEnumeration executeSearch(DirContext ctx) throws javax.naming.NamingException {
- return ctx.search(base, filter, controls);
- }
- };
- if (handler instanceof ContextMapperCallbackHandler) {
- assureReturnObjFlagSet(controls);
- }
- search(se, handler);
- }
-
- /*
- * @see
- * org.springframework.ldap.core.LdapOperations#search(java.lang.String,
- * java.lang.String, javax.naming.directory.SearchControls,
- * org.springframework.ldap.core.NameClassPairCallbackHandler)
- */
- public void search(final String base, final String filter, final SearchControls controls,
- NameClassPairCallbackHandler handler) {
-
- // Create a SearchExecutor to perform the search.
- SearchExecutor se = new SearchExecutor() {
- public NamingEnumeration executeSearch(DirContext ctx) throws javax.naming.NamingException {
- return ctx.search(base, filter, controls);
- }
- };
- if (handler instanceof ContextMapperCallbackHandler) {
- assureReturnObjFlagSet(controls);
- }
- search(se, handler);
- }
-
- /*
- * @see
- * org.springframework.ldap.core.LdapOperations#search(javax.naming.Name,
- * java.lang.String, javax.naming.directory.SearchControls,
- * org.springframework.ldap.core.NameClassPairCallbackHandler,
- * org.springframework.ldap.core.DirContextProcessor)
- */
- public void search(final Name base, final String filter, final SearchControls controls,
- NameClassPairCallbackHandler handler, DirContextProcessor processor) {
-
- // Create a SearchExecutor to perform the search.
- SearchExecutor se = new SearchExecutor() {
- public NamingEnumeration executeSearch(DirContext ctx) throws javax.naming.NamingException {
- return ctx.search(base, filter, controls);
- }
- };
- if (handler instanceof ContextMapperCallbackHandler) {
- assureReturnObjFlagSet(controls);
- }
- search(se, handler, processor);
- }
-
- /*
- * @see
- * org.springframework.ldap.core.LdapOperations#search(java.lang.String,
- * java.lang.String, javax.naming.directory.SearchControls,
- * org.springframework.ldap.core.NameClassPairCallbackHandler,
- * org.springframework.ldap.core.DirContextProcessor)
- */
- public void search(final String base, final String filter, final SearchControls controls,
- NameClassPairCallbackHandler handler, DirContextProcessor processor) {
-
- // Create a SearchExecutor to perform the search.
- SearchExecutor se = new SearchExecutor() {
- public NamingEnumeration executeSearch(DirContext ctx) throws javax.naming.NamingException {
- return ctx.search(base, filter, controls);
- }
- };
- if (handler instanceof ContextMapperCallbackHandler) {
- assureReturnObjFlagSet(controls);
- }
- search(se, handler, processor);
- }
-
- /**
- * Perform a search operation, such as a search(), list() or listBindings().
- * This method handles all the plumbing; getting a readonly context; looping
- * through the NamingEnumeration and closing the context and enumeration. It
- * also calls the supplied DirContextProcessor before and after the search,
- * respectively. This enables custom pre-processing and post-processing,
- * like for example when handling paged results or other search controls.
- *
- * The actual list is delegated to the {@link SearchExecutor} and each
- * {@link NameClassPair} (this might be a NameClassPair or a subclass
- * thereof) is passed to the CallbackHandler. Any encountered
- * NamingException will be translated using the NamingExceptionTranslator.
- *
- * @param se the SearchExecutor to use for performing the actual list.
- * @param handler the NameClassPairCallbackHandler to which each found entry
- * will be passed.
- * @param processor DirContextProcessor for custom pre- and post-processing.
- * Must not be null. If no custom processing should take place,
- * please use e.g.
- * {@link #search(SearchExecutor, NameClassPairCallbackHandler)}.
- * @throws NamingException if any error occurs. Note that a
- * NameNotFoundException will be ignored. Instead this is interpreted that
- * no entries were found.
- */
- public void search(SearchExecutor se, NameClassPairCallbackHandler handler, DirContextProcessor processor) {
- DirContext ctx = contextSource.getReadOnlyContext();
-
- NamingEnumeration results = null;
- RuntimeException ex = null;
- try {
- processor.preProcess(ctx);
- results = se.executeSearch(ctx);
-
- while (results.hasMore()) {
- NameClassPair result = (NameClassPair) results.next();
- handler.handleNameClassPair(result);
- }
- }
- catch (NameNotFoundException e) {
- // It is possible to ignore errors caused by base not found
- if (ignoreNameNotFoundException) {
- log.warn("Base context not found, ignoring: " + e.getMessage());
- }
- else {
- ex = LdapUtils.convertLdapException(e);
- }
- }
- catch (PartialResultException e) {
- // Workaround for AD servers not handling referrals correctly.
- if (ignorePartialResultException) {
- log.debug("PartialResultException encountered and ignored", e);
- }
- else {
- ex = LdapUtils.convertLdapException(e);
- }
- }
- catch (javax.naming.NamingException e) {
- ex = LdapUtils.convertLdapException(e);
- }
- finally {
- try {
- processor.postProcess(ctx);
- }
- catch (javax.naming.NamingException e) {
- if (ex == null) {
- ex = LdapUtils.convertLdapException(e);
- }
- else {
- // We already had an exception from above and should ignore
- // this one.
- log.debug("Ignoring Exception from postProcess, " + "main exception thrown instead", e);
- }
- }
- closeContextAndNamingEnumeration(ctx, results);
- // If we got an exception it should be thrown.
- if (ex != null) {
- throw ex;
- }
- }
- }
-
- /**
- * Perform a search operation, such as a search(), list() or listBindings().
- * This method handles all the plumbing; getting a readonly context; looping
- * through the NamingEnumeration and closing the context and enumeration.
- *
- * The actual list is delegated to the {@link SearchExecutor} and each
- * {@link NameClassPair} (this might be a NameClassPair or a subclass
- * thereof) is passed to the CallbackHandler. Any encountered
- * NamingException will be translated using the NamingExceptionTranslator.
- *
- * @param se the SearchExecutor to use for performing the actual list.
- * @param handler the NameClassPairCallbackHandler to which each found entry
- * will be passed.
- * @throws NamingException if any error occurs. Note that a
- * NameNotFoundException will be ignored. Instead this is interpreted that
- * no entries were found.
- */
- public void search(SearchExecutor se, NameClassPairCallbackHandler handler) {
- search(se, handler, new NullDirContextProcessor());
- }
-
- /*
- * @see
- * org.springframework.ldap.core.LdapOperations#search(javax.naming.Name,
- * java.lang.String,
- * org.springframework.ldap.core.NameClassPairCallbackHandler)
- */
- public void search(Name base, String filter, NameClassPairCallbackHandler handler) {
-
- SearchControls controls = getDefaultSearchControls(defaultSearchScope, DONT_RETURN_OBJ_FLAG, ALL_ATTRIBUTES);
- if (handler instanceof ContextMapperCallbackHandler) {
- assureReturnObjFlagSet(controls);
- }
- search(base, filter, controls, handler);
- }
-
- /*
- * @see
- * org.springframework.ldap.core.LdapOperations#search(java.lang.String,
- * java.lang.String,
- * org.springframework.ldap.core.NameClassPairCallbackHandler)
- */
- public void search(String base, String filter, NameClassPairCallbackHandler handler) {
-
- SearchControls controls = getDefaultSearchControls(defaultSearchScope, DONT_RETURN_OBJ_FLAG, ALL_ATTRIBUTES);
- if (handler instanceof ContextMapperCallbackHandler) {
- assureReturnObjFlagSet(controls);
- }
- search(base, filter, controls, handler);
- }
-
- /*
- * @see
- * org.springframework.ldap.core.LdapOperations#search(javax.naming.Name,
- * java.lang.String, int, java.lang.String[],
- * org.springframework.ldap.core.AttributesMapper)
- */
- public