| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.apache.directory.shared.ldap.name; |
| |
| |
| import java.io.UnsupportedEncodingException; |
| import java.util.ArrayList; |
| import java.util.Enumeration; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.NoSuchElementException; |
| |
| import javax.naming.InvalidNameException; |
| import javax.naming.Name; |
| import javax.naming.NamingException; |
| |
| import org.apache.directory.shared.ldap.name.DefaultStringNormalizer; |
| import org.apache.directory.shared.ldap.schema.OidNormalizer; |
| import org.apache.directory.shared.ldap.util.StringTools; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| |
| /** |
| * The LdapDN class contains a DN (Distinguished Name). |
| * |
| * Its specification can be found in RFC 2253, |
| * "UTF-8 String Representation of Distinguished Names". |
| * |
| * We will store two representation of a DN : |
| * - a user Provider represeentation, which is the parsed String given by a user |
| * - an internal representation. |
| * |
| * A DN is formed of RDNs, in a specific order : |
| * RDN[n], RDN[n-1], ... RDN[1], RDN[0] |
| * |
| * It represents a tree, in which the root is the last RDN (RDN[0]) and the leaf |
| * is the first RDN (RDN[n]). |
| * |
| * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> |
| */ |
| public class LdapDN implements Name |
| { |
| /** The LoggerFactory used by this class */ |
| protected static final Logger log = LoggerFactory.getLogger( LdapDN.class ); |
| |
| /** |
| * Declares the Serial Version Uid. |
| * |
| * @see <a |
| * href="http://c2.com/cgi/wiki?AlwaysDeclareSerialVersionUid">Always |
| * Declare Serial Version Uid</a> |
| */ |
| private static final long serialVersionUID = 1L; |
| |
| /** Value returned by the compareTo method if values are not equals */ |
| public final static int NOT_EQUALS = -1; |
| |
| /** Value returned by the compareTo method if values are equals */ |
| public final static int EQUALS = 0; |
| |
| /** A flag used to tell if the DN has been normalized */ |
| private boolean normalized; |
| |
| // ~ Static fields/initializers |
| // ----------------------------------------------------------------- |
| /** The RDNs that are elements of the DN */ |
| protected List<Rdn> rdns = new ArrayList<Rdn>( 5 ); |
| |
| /** The user provided name */ |
| private String upName; |
| |
| /** The normalized name */ |
| private String normName; |
| |
| /** The bytes representation of the normName */ |
| private byte[] bytes; |
| |
| /** A null LdapDN */ |
| public static final LdapDN EMPTY_LDAPDN = new LdapDN(); |
| |
| // ~ Methods |
| // ------------------------------------------------------------------------------------ |
| |
| /** |
| * Construct an empty LdapDN object |
| */ |
| public LdapDN() |
| { |
| super(); |
| upName = ""; |
| normName = ""; |
| normalized = true; |
| } |
| |
| |
| /** |
| * Transduces, or copies a Name to an LdapDN. |
| * |
| * @param name composed of String name components. |
| */ |
| public LdapDN( Name name ) throws InvalidNameException |
| { |
| if ( ( name != null ) && ( name.size() != 0 ) ) |
| { |
| for ( int ii = 0; ii < name.size(); ii++ ) |
| { |
| String nameComponent = name.get( ii ); |
| add( nameComponent ); |
| } |
| } |
| |
| normalized = false; |
| |
| } |
| |
| |
| /** |
| * Creates an ldap name using a list of NameComponents. Each NameComponent |
| * is a String |
| * |
| * @param list of String name components. |
| */ |
| LdapDN( List<String> list ) throws InvalidNameException |
| { |
| if ( ( list != null ) && ( list.size() != 0 ) ) |
| { |
| for ( String nameComponent:list ) |
| { |
| add( 0, nameComponent ); |
| } |
| } |
| |
| normalized = false; |
| |
| } |
| |
| |
| /** |
| * Creates an ldap name using a list of name components. |
| * |
| * @param nameComponents |
| * List of String name components. |
| */ |
| LdapDN( Iterator nameComponents ) throws InvalidNameException |
| { |
| if ( nameComponents != null ) |
| { |
| while ( nameComponents.hasNext() ) |
| { |
| String nameComponent = ( String ) nameComponents.next(); |
| add( 0, nameComponent ); |
| } |
| } |
| |
| normalized = false; |
| } |
| |
| |
| /** |
| * Parse a String and checks that it is a valid DN <br> |
| * <p> |
| * <distinguishedName> ::= <name> | e <br> |
| * <name> ::= <name-component> <name-components> <br> |
| * <name-components> ::= <spaces> <separator> |
| * <spaces> <name-component> <name-components> | e <br> |
| * </p> |
| * |
| * @param upName |
| * The String that contains the DN |
| * @exception InvalidNameException is thrown if the buffer does not |
| * contains a valid DN. |
| */ |
| public LdapDN( String upName ) throws InvalidNameException |
| { |
| if ( upName != null ) |
| { |
| LdapDnParser.parseInternal( upName, rdns ); |
| } |
| |
| // Stores the representations of a DN : internal (as a string and as a |
| // byte[]) and external. |
| normalizeInternal(); |
| normalized = false; |
| |
| this.upName = upName; |
| } |
| |
| /** |
| * Static factory which creates a normalized DN from a String and a Map of OIDs. |
| * |
| * @param name The DN as a String |
| * @param oidsMap The OID mapping |
| * @return A valid DN |
| * @throws InvalidNameException If the DN is invalid |
| */ |
| public static Name normalize( String name, Map<String, OidNormalizer> oidsMap ) throws InvalidNameException, NamingException |
| { |
| if ( ( name == null ) || ( name.length() == 0 ) || ( oidsMap == null ) || ( oidsMap.size() == 0 ) ) |
| { |
| return LdapDN.EMPTY_LDAPDN; |
| } |
| |
| try |
| { |
| LdapDN newDn = new LdapDN( name ); |
| |
| Enumeration<Rdn> rdns = newDn.getAllRdn(); |
| |
| // Loop on all RDNs |
| while ( rdns.hasMoreElements() ) |
| { |
| Rdn rdn = rdns.nextElement(); |
| String upName = rdn.getUpName(); |
| rdnOidToName( rdn, oidsMap ); |
| rdn.normalize(); |
| rdn.setUpName( upName ); |
| } |
| |
| newDn.normalizeInternal(); |
| newDn.normalized = true; |
| |
| return newDn; |
| } |
| catch ( NamingException ne ) |
| { |
| throw new InvalidNameException( ne.getMessage() ); |
| } |
| } |
| |
| /** |
| * Parse a buffer and checks that it is a valid DN <br> |
| * <p> |
| * <distinguishedName> ::= <name> | e <br> |
| * <name> ::= <name-component> <name-components> <br> |
| * <name-components> ::= <spaces> <separator> |
| * <spaces> <name-component> <name-components> | e <br> |
| * </p> |
| * |
| * @param bytes |
| * The byte buffer that contains the DN |
| * @exception A |
| * InvalidNameException is thrown if the buffer does not |
| * contains a valid DN. |
| */ |
| public LdapDN(byte[] bytes) throws InvalidNameException |
| { |
| try |
| { |
| upName = new String( bytes, "UTF-8" ); |
| LdapDnParser.parseInternal( upName, rdns ); |
| this.normName = toNormName(); |
| normalized = false; |
| } |
| catch ( UnsupportedEncodingException uee ) |
| { |
| log.error( "The byte array is not an UTF-8 encoded Unicode String : " + uee.getMessage() ); |
| throw new InvalidNameException( "The byte array is not an UTF-8 encoded Unicode String : " |
| + uee.getMessage() ); |
| } |
| } |
| |
| |
| /** |
| * Normalize the DN by triming useless spaces and lowercasing names. |
| * |
| * @return a normalized form of the DN |
| */ |
| private void normalizeInternal() |
| { |
| normName = toNormName(); |
| } |
| |
| |
| /** |
| * Build the normalized DN as a String, |
| * |
| * @return A String representing the normalized DN |
| */ |
| public String toNormName() |
| { |
| if ( ( rdns == null ) || ( rdns.size() == 0 ) ) |
| { |
| bytes = null; |
| return ""; |
| } |
| else |
| { |
| StringBuffer sb = new StringBuffer(); |
| boolean isFirst = true; |
| |
| for ( Rdn rdn:rdns ) |
| { |
| if ( isFirst ) |
| { |
| isFirst = false; |
| } |
| else |
| { |
| sb.append( ',' ); |
| } |
| |
| sb.append( rdn ); |
| } |
| |
| String newNormName = sb.toString(); |
| |
| if ( ( normName == null ) || !normName.equals( newNormName ) ) |
| { |
| bytes = StringTools.getBytesUtf8( newNormName ); |
| normName = newNormName; |
| } |
| |
| return normName; |
| } |
| } |
| |
| |
| /** |
| * Return the normalized DN as a String. It returns the same value as the |
| * getNormName method |
| * |
| * @return A String representing the normalized DN |
| */ |
| public String toString() |
| { |
| return normName == null ? "" : normName; |
| } |
| |
| |
| /** |
| * Return the User Provided DN as a String, |
| * |
| * @return A String representing the User Provided DN |
| */ |
| private String toUpName() |
| { |
| if ( ( rdns == null ) || ( rdns.size() == 0 ) ) |
| { |
| upName = ""; |
| } |
| else |
| { |
| StringBuffer sb = new StringBuffer(); |
| boolean isFirst = true; |
| |
| for ( Rdn rdn:rdns ) |
| { |
| if ( isFirst ) |
| { |
| isFirst = false; |
| } |
| else |
| { |
| sb.append( ',' ); |
| } |
| |
| sb.append( rdn.getUpName() ); |
| } |
| |
| upName = sb.toString(); |
| } |
| |
| return upName; |
| } |
| |
| |
| /** |
| * Return the User Provided prefix representation of the DN starting at the |
| * posn position. |
| * |
| * If posn = 0, return an empty string. |
| * |
| * for DN : sn=smith, dc=apache, dc=org |
| * getUpname(0) -> "" |
| * getUpName(1) -> "dc=org" |
| * getUpname(3) -> "sn=smith, dc=apache, dc=org" |
| * getUpName(4) -> ArrayOutOfBoundException |
| * |
| * Warning ! The returned String is not exactly the |
| * user provided DN, as spaces before and after each RDNs have been trimmed. |
| * |
| * @param posn |
| * The starting position |
| * @return The truncated DN |
| */ |
| private String getUpNamePrefix( int posn ) |
| { |
| if ( posn == 0 ) |
| { |
| return ""; |
| } |
| |
| if ( posn > rdns.size() ) |
| { |
| String message = "Impossible to get the position " + posn + ", the DN only has " + rdns.size() + " RDNs"; |
| log.error( message ); |
| throw new ArrayIndexOutOfBoundsException( message ); |
| } |
| |
| int start = rdns.size() - posn; |
| StringBuffer sb = new StringBuffer(); |
| boolean isFirst = true; |
| |
| for ( int i = start; i < rdns.size(); i++ ) |
| { |
| if ( isFirst ) |
| { |
| isFirst = false; |
| } |
| else |
| { |
| sb.append( ',' ); |
| } |
| |
| sb.append( rdns.get( i ).getUpName() ); |
| } |
| |
| return sb.toString(); |
| } |
| |
| |
| /** |
| * Return the User Provided suffix representation of the DN starting at the |
| * posn position. |
| * If posn = 0, return an empty string. |
| * |
| * for DN : sn=smith, dc=apache, dc=org |
| * getUpname(0) -> "sn=smith, dc=apache, dc=org" |
| * getUpName(1) -> "sn=smith, dc=apache" |
| * getUpname(3) -> "sn=smith" |
| * getUpName(4) -> "" |
| * |
| * Warning ! The returned String is not exactly the user |
| * provided DN, as spaces before and after each RDNs have been trimmed. |
| * |
| * @param posn The starting position |
| * @return The truncated DN |
| */ |
| private String getUpNameSuffix( int posn ) |
| { |
| if ( posn > rdns.size() ) |
| { |
| return ""; |
| } |
| |
| int end = rdns.size() - posn; |
| StringBuffer sb = new StringBuffer(); |
| boolean isFirst = true; |
| |
| for ( int i = 0; i < end; i++ ) |
| { |
| if ( isFirst ) |
| { |
| isFirst = false; |
| } |
| else |
| { |
| sb.append( ',' ); |
| } |
| |
| sb.append( rdns.get( i ).getUpName() ); |
| } |
| |
| return sb.toString(); |
| } |
| |
| |
| /** |
| * Gets the hashcode of this name. |
| * |
| * @see java.lang.Object#hashCode() |
| */ |
| public int hashCode() |
| { |
| int result = 17; |
| |
| if ( ( rdns != null ) && ( rdns.size() == 0 ) ) |
| { |
| for ( Rdn rdn:rdns ) |
| { |
| result = result * 37 + rdn.hashCode(); |
| } |
| } |
| |
| return result; |
| } |
| |
| |
| /** |
| * Get the initial DN (without normalization) |
| * |
| * @return The DN as a String |
| */ |
| public String getUpName() |
| { |
| return ( upName == null ? "" : upName ); |
| } |
| |
| |
| /** |
| * Get the initial DN (without normalization) |
| * |
| * @return The DN as a String |
| */ |
| public String getNormName() |
| { |
| return ( normName == null ? "" : normName ); |
| } |
| |
| /** |
| * Get the number of NameComponent conatained in this LdapDN |
| * |
| * @return The number of NameComponent conatained in this LdapDN |
| */ |
| public int size() |
| { |
| return rdns.size(); |
| } |
| |
| |
| /** |
| * Get the number of bytes necessary to store this DN |
| * |
| * @return A integer, which is the size of the UTF-8 byte array |
| */ |
| public static int getNbBytes( Name dn ) |
| { |
| LdapDN ldapDn = ( LdapDN ) dn; |
| return ldapDn.bytes == null ? 0 : ldapDn.bytes.length; |
| } |
| |
| |
| /** |
| * Get an UTF-8 representation of the normalized form of the DN |
| * |
| * @return A byte[] representation of the DN |
| */ |
| public static byte[] getBytes( LdapDN dn ) |
| { |
| return dn == null ? null : dn.bytes; |
| } |
| |
| |
| /** |
| * Determines whether this name starts with a specified prefix. A name |
| * <tt>name</tt> is a prefix if it is equal to |
| * <tt>getPrefix(name.size())</tt>. |
| * |
| * Be aware that for a specific DN like : |
| * cn=xxx, ou=yyy |
| * the startsWith method will return true with ou=yyy, and |
| * false with cn=xxx |
| * |
| * @param name |
| * the name to check |
| * @return true if <tt>name</tt> is a prefix of this name, false otherwise |
| */ |
| public boolean startsWith( Name name ) |
| { |
| if ( name == null ) |
| { |
| return true; |
| } |
| else if ( name instanceof LdapDN ) |
| { |
| LdapDN nameDN = ( LdapDN ) name; |
| |
| if ( nameDN.size() == 0 ) |
| { |
| return true; |
| } |
| |
| if ( nameDN.size() > size() ) |
| { |
| // The name is longer than the current LdapDN. |
| return false; |
| } |
| |
| // Ok, iterate through all the RDN of the name, |
| // starting a the end of the current list. |
| |
| for ( int i = nameDN.size() - 1; i >= 0; i-- ) |
| { |
| Rdn nameRdn = nameDN.rdns.get( nameDN.rdns.size() - i - 1 ); |
| Rdn ldapRdn = rdns.get( rdns.size() - i - 1 ); |
| |
| if ( nameRdn.compareTo( ldapRdn ) != 0 ) |
| { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| else |
| { |
| if ( name.size() == 0 ) |
| { |
| return true; |
| } |
| |
| if ( name.size() > size() ) |
| { |
| // The name is longer than the current LdapDN. |
| return false; |
| } |
| |
| // Ok, iterate through all the RDN of the name, |
| // starting a the end of the current list. |
| |
| for ( int i = name.size() - 1; i >= 0; i-- ) |
| { |
| Rdn ldapRdn = rdns.get( rdns.size() - i - 1 ); |
| Rdn nameRdn = null; |
| |
| try |
| { |
| nameRdn = new Rdn( name.get( name.size() - i - 1 ) ); |
| } |
| catch ( InvalidNameException e ) |
| { |
| e.printStackTrace(); |
| log.error( "Failed to parse RDN for name " + name.toString(), e ); |
| return false; |
| } |
| |
| if ( nameRdn.compareTo( ldapRdn ) != 0 ) |
| { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| } |
| |
| |
| /** |
| * Determines whether this name ends with a specified suffix. A name |
| * <tt>name</tt> is a suffix if it is equal to |
| * <tt>getSuffix(size()-name.size())</tt>. |
| * |
| * Be aware that for a specific |
| * DN like : cn=xxx, ou=yyy the endsWith method will return true with |
| * cn=xxx, and false with ou=yyy |
| * |
| * @param name |
| * the name to check |
| * @return true if <tt>name</tt> is a suffix of this name, false otherwise |
| */ |
| public boolean endsWith( Name name ) |
| { |
| if ( name instanceof LdapDN ) |
| { |
| LdapDN nameDN = ( LdapDN ) name; |
| |
| if ( nameDN.size() == 0 ) |
| { |
| return true; |
| } |
| |
| if ( nameDN.size() > size() ) |
| { |
| // The name is longer than the current LdapDN. |
| return false; |
| } |
| |
| // Ok, iterate through all the RDN of the name |
| for ( int i = 0; i < nameDN.size(); i++ ) |
| { |
| Rdn nameRdn = nameDN.rdns.get( i ); |
| Rdn ldapRdn = rdns.get( i ); |
| |
| if ( nameRdn.compareTo( ldapRdn ) != 0 ) |
| { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| else |
| { |
| // We don't accept a Name which is not a LdapName |
| return name == null; |
| } |
| } |
| |
| |
| /** |
| * Determines whether this name is empty. An empty name is one with zero |
| * components. |
| * |
| * @return true if this name is empty, false otherwise |
| */ |
| public boolean isEmpty() |
| { |
| return ( rdns.size() == 0 ); |
| } |
| |
| |
| /** |
| * Retrieves a component of this name. |
| * |
| * @param posn |
| * the 0-based index of the component to retrieve. Must be in the |
| * range [0,size()). |
| * @return the component at index posn |
| * @throws ArrayIndexOutOfBoundsException |
| * if posn is outside the specified range |
| */ |
| public String get( int posn ) |
| { |
| if ( rdns.size() == 0 ) |
| { |
| return ""; |
| } |
| else |
| { |
| Rdn rdn = rdns.get( rdns.size() - posn - 1 ); |
| |
| return rdn.toString(); |
| } |
| } |
| |
| |
| /** |
| * Retrieves a component of this name. |
| * |
| * @param posn |
| * the 0-based index of the component to retrieve. Must be in the |
| * range [0,size()). |
| * @return the component at index posn |
| * @throws ArrayIndexOutOfBoundsException |
| * if posn is outside the specified range |
| */ |
| public Rdn getRdn( int posn ) |
| { |
| if ( rdns.size() == 0 ) |
| { |
| return null; |
| } |
| else |
| { |
| Rdn rdn = rdns.get( rdns.size() - posn - 1 ); |
| |
| return rdn; |
| } |
| } |
| |
| /** |
| * Retrieves the last component of this name. |
| * |
| * @return the last component of this DN |
| */ |
| public Rdn getRdn() |
| { |
| if ( rdns.size() == 0 ) |
| { |
| return null; |
| } |
| else |
| { |
| return rdns.get( 0 ); |
| } |
| } |
| |
| |
| /** |
| * Retrieves all the components of this name. |
| * |
| * @return All the components |
| */ |
| public List<Rdn> getRdns() |
| { |
| List<Rdn> newRdns = new ArrayList<Rdn>(); |
| |
| // We will clone the list, to avoid user modifications |
| for ( Rdn rdn:rdns ) |
| { |
| newRdns.add( (Rdn)rdn.clone() ); |
| } |
| |
| return newRdns; |
| } |
| |
| |
| /** |
| * Retrieves the components of this name as an enumeration of strings. The |
| * effect on the enumeration of updates to this name is undefined. If the |
| * name has zero components, an empty (non-null) enumeration is returned. |
| * |
| * @return an enumeration of the components of this name, each as string |
| */ |
| public Enumeration<String> getAll() |
| { |
| /* |
| * Note that by accessing the name component using the get() method on |
| * the name rather than get() on the list we are reading components from |
| * right to left with increasing index values. LdapName.get() does the |
| * index translation on m_list for us. |
| */ |
| return new Enumeration<String>() |
| { |
| private int pos; |
| |
| |
| public boolean hasMoreElements() |
| { |
| return pos < rdns.size(); |
| } |
| |
| |
| public String nextElement() |
| { |
| if ( pos >= rdns.size() ) |
| { |
| log.error( "Exceeded number of elements in the current object" ); |
| throw new NoSuchElementException(); |
| } |
| |
| Rdn rdn = rdns.get( rdns.size() - pos - 1 ); |
| pos++; |
| return rdn.toString(); |
| } |
| }; |
| } |
| |
| |
| /** |
| * Retrieves the components of this name as an enumeration of strings. The |
| * effect on the enumeration of updates to this name is undefined. If the |
| * name has zero components, an empty (non-null) enumeration is returned. |
| * |
| * @return an enumeration of the components of this name, as Rdn |
| */ |
| public Enumeration<Rdn> getAllRdn() |
| { |
| /* |
| * Note that by accessing the name component using the get() method on |
| * the name rather than get() on the list we are reading components from |
| * right to left with increasing index values. LdapName.get() does the |
| * index translation on m_list for us. |
| */ |
| return new Enumeration<Rdn>() |
| { |
| private int pos; |
| |
| |
| public boolean hasMoreElements() |
| { |
| return pos < rdns.size(); |
| } |
| |
| |
| public Rdn nextElement() |
| { |
| if ( pos >= rdns.size() ) |
| { |
| log.error( "Exceeded number of elements in the current object" ); |
| throw new NoSuchElementException(); |
| } |
| |
| Rdn rdn = rdns.get( rdns.size() - pos - 1 ); |
| pos++; |
| return rdn; |
| } |
| }; |
| } |
| |
| |
| /** |
| * Creates a name whose components consist of a prefix of the components of |
| * this name. Subsequent changes to this name will not affect the name that |
| * is returned and vice versa. |
| * |
| * @param posn |
| * the 0-based index of the component at which to stop. Must be |
| * in the range [0,size()]. |
| * @return a name consisting of the components at indexes in the range |
| * [0,posn]. |
| * @throws ArrayIndexOutOfBoundsException |
| * if posn is outside the specified range |
| */ |
| public Name getPrefix( int posn ) |
| { |
| if ( rdns.size() == 0 ) |
| { |
| return EMPTY_LDAPDN; |
| } |
| |
| if ( ( posn < 0 ) || ( posn > rdns.size() ) ) |
| { |
| String message = "The posn(" + posn + ") should be in the range [0, " + rdns.size() + "]"; |
| log.error( message ); |
| throw new ArrayIndexOutOfBoundsException( message ); |
| } |
| |
| LdapDN newLdapDN = new LdapDN(); |
| |
| for ( int i = rdns.size() - posn; i < rdns.size(); i++ ) |
| { |
| // Don't forget to clone the rdns ! |
| newLdapDN.rdns.add( ( Rdn ) rdns.get( i ).clone() ); |
| } |
| |
| newLdapDN.normName = newLdapDN.toNormName(); |
| newLdapDN.upName = getUpNamePrefix( posn ); |
| |
| return newLdapDN; |
| } |
| |
| |
| /** |
| * Creates a name whose components consist of a suffix of the components in |
| * this name. Subsequent changes to this name do not affect the name that is |
| * returned and vice versa. |
| * |
| * @param posn |
| * the 0-based index of the component at which to start. Must be |
| * in the range [0,size()]. |
| * @return a name consisting of the components at indexes in the range |
| * [posn,size()]. If posn is equal to size(), an empty name is |
| * returned. |
| * @throws ArrayIndexOutOfBoundsException |
| * if posn is outside the specified range |
| */ |
| public Name getSuffix( int posn ) |
| { |
| if ( rdns.size() == 0 ) |
| { |
| return EMPTY_LDAPDN; |
| } |
| |
| if ( ( posn < 0 ) || ( posn > rdns.size() ) ) |
| { |
| String message = "The posn(" + posn + ") should be in the range [0, " + rdns.size() + "]"; |
| log.error( message ); |
| throw new ArrayIndexOutOfBoundsException( message ); |
| } |
| |
| LdapDN newLdapDN = new LdapDN(); |
| |
| for ( int i = 0; i < size() - posn; i++ ) |
| { |
| // Don't forget to clone the rdns ! |
| newLdapDN.rdns.add( ( Rdn ) rdns.get( i ).clone() ); |
| } |
| |
| newLdapDN.normName = newLdapDN.toNormName(); |
| newLdapDN.upName = getUpNameSuffix( posn ); |
| |
| return newLdapDN; |
| } |
| |
| |
| /** |
| * Adds the components of a name -- in order -- to the end of this name. |
| * |
| * @param suffix |
| * the components to add |
| * @return the updated name (not a new one) |
| * @throws InvalidNameException |
| * if <tt>suffix</tt> is not a valid name, or if the addition |
| * of the components would violate the syntax rules of this name |
| */ |
| public Name addAll( Name suffix ) throws InvalidNameException |
| { |
| addAll( rdns.size(), suffix ); |
| normalizeInternal(); |
| toUpName(); |
| |
| return this; |
| } |
| |
| |
| /** |
| * Adds the components of a name -- in order -- at a specified position |
| * within this name. Components of this name at or after the index of the |
| * first new component are shifted up (away from 0) to accommodate the new |
| * components. |
| * |
| * @param name |
| * the components to add |
| * @param posn |
| * the index in this name at which to add the new components. |
| * Must be in the range [0,size()]. |
| * @return the updated name (not a new one) |
| * @throws ArrayIndexOutOfBoundsException |
| * if posn is outside the specified range |
| * @throws InvalidNameException |
| * if <tt>n</tt> is not a valid name, or if the addition of |
| * the components would violate the syntax rules of this name |
| */ |
| public Name addAll( int posn, Name name ) throws InvalidNameException |
| { |
| if ( name instanceof LdapDN ) |
| { |
| if ( ( name == null ) || ( name.size() == 0 ) ) |
| { |
| return this; |
| } |
| |
| // Concatenate the rdns |
| rdns.addAll( size() - posn, ( ( LdapDN ) name ).rdns ); |
| |
| // Regenerate the normalized name and the original string |
| normalizeInternal(); |
| toUpName(); |
| } |
| else |
| { |
| if ( ( name == null ) || ( name.size() == 0 ) ) |
| { |
| return this; |
| } |
| |
| for ( int i = name.size() - 1; i >= 0; i-- ) |
| { |
| Rdn rdn = new Rdn( name.get( i ) ); |
| rdns.add( size() - posn, rdn ); |
| } |
| |
| normalizeInternal(); |
| toUpName(); |
| } |
| |
| return this; |
| } |
| |
| |
| /** |
| * Adds a single component to the end of this name. |
| * |
| * @param comp |
| * the component to add |
| * @return the updated name (not a new one) |
| * @throws InvalidNameException |
| * if adding <tt>comp</tt> would violate the syntax rules of |
| * this name |
| */ |
| public Name add( String comp ) throws InvalidNameException |
| { |
| if ( comp.length() == 0 ) |
| { |
| return this; |
| } |
| |
| // We have to parse the nameComponent which is given as an argument |
| Rdn newRdn = new Rdn( comp ); |
| |
| rdns.add( 0, newRdn ); |
| normalizeInternal(); |
| toUpName(); |
| |
| return this; |
| } |
| |
| /** |
| * Adds a single RDN to the end of this name. |
| * |
| * @param newRdn |
| * the RDN to add |
| * @return the updated name (not a new one) |
| * @throws InvalidNameException |
| * if adding <tt>RDN</tt> would violate the syntax rules of |
| * this name |
| */ |
| public Name add( Rdn newRdn ) |
| { |
| rdns.add( 0, newRdn ); |
| normalizeInternal(); |
| toUpName(); |
| |
| return this; |
| } |
| |
| /** |
| * Adds a single component at a specified position within this name. |
| * Components of this name at or after the index of the new component are |
| * shifted up by one (away from index 0) to accommodate the new component. |
| * |
| * @param comp |
| * the component to add |
| * @param posn |
| * the index at which to add the new component. Must be in the |
| * range [0,size()]. |
| * @return the updated name (not a new one) |
| * @throws ArrayIndexOutOfBoundsException |
| * if posn is outside the specified range |
| * @throws InvalidNameException |
| * if adding <tt>comp</tt> would violate the syntax rules of |
| * this name |
| */ |
| public Name add( int posn, String comp ) throws InvalidNameException |
| { |
| if ( ( posn < 0 ) || ( posn > size() ) ) |
| { |
| String message = "The posn(" + posn + ") should be in the range [0, " + rdns.size() + "]"; |
| log.error( message ); |
| throw new ArrayIndexOutOfBoundsException( message ); |
| } |
| |
| // We have to parse the nameComponent which is given as an argument |
| Rdn newRdn = new Rdn( comp ); |
| |
| int realPos = size() - posn; |
| rdns.add( realPos, newRdn ); |
| |
| normalizeInternal(); |
| toUpName(); |
| |
| return this; |
| } |
| |
| |
| /** |
| * Removes a component from this name. The component of this name at the |
| * specified position is removed. Components with indexes greater than this |
| * position are shifted down (toward index 0) by one. |
| * |
| * @param posn |
| * the index of the component to remove. Must be in the range |
| * [0,size()). |
| * @return the component removed (a String) |
| * @throws ArrayIndexOutOfBoundsException |
| * if posn is outside the specified range |
| * @throws InvalidNameException |
| * if deleting the component would violate the syntax rules of |
| * the name |
| */ |
| public Object remove( int posn ) throws InvalidNameException |
| { |
| if ( rdns.size() == 0 ) |
| { |
| return EMPTY_LDAPDN; |
| } |
| |
| if ( ( posn < 0 ) || ( posn >= rdns.size() ) ) |
| { |
| String message = "The posn(" + posn + ") should be in the range [0, " + rdns.size() + "]"; |
| log.error( message ); |
| throw new ArrayIndexOutOfBoundsException( message ); |
| } |
| |
| int realPos = size() - posn - 1; |
| Rdn rdn = rdns.remove( realPos ); |
| |
| normalizeInternal(); |
| toUpName(); |
| |
| return rdn; |
| } |
| |
| |
| /** |
| * Generates a new copy of this name. Subsequent changes to the components |
| * of this name will not affect the new copy, and vice versa. |
| * |
| * @return a copy of this name |
| * @see Object#clone() |
| */ |
| public Object clone() |
| { |
| try |
| { |
| LdapDN dn = ( LdapDN ) super.clone(); |
| dn.rdns = new ArrayList<Rdn>(); |
| |
| for ( Rdn rdn:rdns ) |
| { |
| dn.rdns.add( ( Rdn ) rdn.clone() ); |
| } |
| |
| return dn; |
| } |
| catch ( CloneNotSupportedException cnse ) |
| { |
| log.error( "The clone operation has failed" ); |
| throw new Error( "Assertion failure : cannot clone the object" ); |
| } |
| } |
| |
| |
| /** |
| * @see java.lang.Object#equals(java.lang.Object) |
| */ |
| public boolean equals( Object obj ) |
| { |
| if ( obj instanceof String ) |
| { |
| return normName.equals( obj ); |
| } |
| else if ( obj instanceof LdapDN ) |
| { |
| LdapDN name = ( LdapDN ) obj; |
| |
| if ( name.size() != this.size() ) |
| { |
| return false; |
| } |
| |
| for ( int i = 0; i < this.size(); i++ ) |
| { |
| if ( name.rdns.get( i ).compareTo( rdns.get( i ) ) != 0 ) |
| { |
| return false; |
| } |
| } |
| |
| // All components matched so we return true |
| return true; |
| } |
| else |
| { |
| return false; |
| } |
| } |
| |
| |
| /** |
| * Compares this name with another name for order. Returns a negative |
| * integer, zero, or a positive integer as this name is less than, equal to, |
| * or greater than the given name. |
| * <p> |
| * As with <tt>Object.equals()</tt>, the notion of ordering for names |
| * depends on the class that implements this interface. For example, the |
| * ordering may be based on lexicographical ordering of the name components. |
| * Specific attributes of the name, such as how it treats case, may affect |
| * the ordering. In general, two names of different classes may not be |
| * compared. |
| * |
| * @param obj |
| * the non-null object to compare against. |
| * @return a negative integer, zero, or a positive integer as this name is |
| * less than, equal to, or greater than the given name |
| * @throws ClassCastException |
| * if obj is not a <tt>Name</tt> of a type that may be |
| * compared with this name |
| * @see Comparable#compareTo(Object) |
| */ |
| public int compareTo( Object obj ) |
| { |
| if ( obj instanceof LdapDN ) |
| { |
| LdapDN ldapDN = ( LdapDN ) obj; |
| |
| if ( ldapDN.size() != size() ) |
| { |
| return size() - ldapDN.size(); |
| } |
| |
| for ( int i = rdns.size(); i > 0; i-- ) |
| { |
| Rdn rdn1 = rdns.get( i - 1 ); |
| Rdn rdn2 = ldapDN.rdns.get( i - 1 ); |
| int res = rdn1.compareTo( rdn2 ); |
| |
| if ( res != 0 ) |
| { |
| return res; |
| } |
| } |
| |
| return EQUALS; |
| } |
| else |
| { |
| return 1; |
| } |
| } |
| |
| |
| private static AttributeTypeAndValue atavOidToName( AttributeTypeAndValue atav, Map<String, OidNormalizer> oidsMap ) |
| throws InvalidNameException, NamingException |
| { |
| String type = StringTools.trim( atav.getNormType() ); |
| |
| if ( ( type.startsWith( "oid." ) ) || ( type.startsWith( "OID." ) ) ) |
| { |
| type = type.substring( 4 ); |
| } |
| |
| if ( StringTools.isNotEmpty( type ) ) |
| { |
| if ( oidsMap == null ) |
| { |
| return atav; |
| } |
| else |
| { |
| OidNormalizer oidNormalizer = oidsMap.get( type ); |
| |
| if ( oidNormalizer != null ) |
| { |
| return new AttributeTypeAndValue( oidNormalizer.getAttributeTypeOid(), oidNormalizer.getNormalizer() |
| .normalize( atav.getValue() ) ); |
| |
| } |
| else |
| { |
| // We don't have a normalizer for this OID : just do nothing. |
| return atav; |
| } |
| } |
| } |
| else |
| { |
| // The type is empty : this is not possible... |
| log.error( "Empty type not allowed in a DN" ); |
| throw new InvalidNameException( "Empty type not allowed in a DN" ); |
| } |
| |
| } |
| |
| |
| /** |
| * Transform a RDN by changing the value to its OID counterpart and |
| * normalizing the value accordingly to its type. |
| * |
| * @param rdn |
| * The RDN to modify |
| * @param oidsMap |
| * The map of all existing oids and normalizer |
| * @throws InvalidNameException |
| * If |
| * @throws NamingException |
| */ |
| private static void rdnOidToName( Rdn rdn, Map<String, OidNormalizer> oidsMap ) throws InvalidNameException, NamingException |
| { |
| if ( rdn.getNbAtavs() > 1 ) |
| { |
| // We have more than one ATAV for this RDN. We will loop on all |
| // ATAVs |
| Rdn rdnCopy = ( Rdn ) rdn.clone(); |
| rdn.clear(); |
| |
| Iterator atavs = rdnCopy.iterator(); |
| |
| while ( atavs.hasNext() ) |
| { |
| Object val = atavs.next(); |
| AttributeTypeAndValue newAtav = atavOidToName( ( AttributeTypeAndValue ) val, oidsMap ); |
| rdn.addAttributeTypeAndValue( newAtav.getUpType(), newAtav.getValue() ); |
| } |
| |
| } |
| else |
| { |
| String type = rdn.getNormType(); |
| |
| if ( ( type.startsWith( "oid." ) ) || ( type.startsWith( "OID." ) ) ) |
| { |
| type = type.substring( 4 ); |
| } |
| |
| if ( StringTools.isNotEmpty( type ) ) |
| { |
| if ( oidsMap == null ) |
| { |
| return; |
| } |
| else |
| { |
| OidNormalizer oidNormalizer = oidsMap.get( type ); |
| |
| if ( oidNormalizer != null ) |
| { |
| // Alex asks: Why clone here when we do not use the cloned copy? |
| Rdn rdnCopy = ( Rdn ) rdn.clone(); |
| rdn.clear(); |
| Object value = rdnCopy.getValue(); |
| Object normValue = DefaultStringNormalizer.normalizeString( ( String ) value ); |
| |
| rdn.addAttributeTypeAndValue( oidNormalizer.getAttributeTypeOid(), |
| oidNormalizer.getNormalizer() |
| .normalize( normValue ) ); |
| |
| } |
| else |
| { |
| // We don't have a normalizer for this OID : just do |
| // nothing. |
| return; |
| } |
| } |
| } |
| else |
| { |
| // The type is empty : this is not possible... |
| log.error( "We should not have an empty DN" ); |
| throw new InvalidNameException( "Empty type not allowed in a DN" ); |
| } |
| } |
| } |
| |
| |
| /** |
| * Change the internal DN, using the first alias instead of oids or other |
| * aliases. As we still have the UP name of each RDN, we will be able to |
| * provide both representation of the DN. example : dn: 2.5.4.3=People, |
| * dc=example, domainComponent=com will be transformed to : cn=People, |
| * dc=example, dc=com because 2.5.4.3 is the OID for cn and dc is the first |
| * alias of the couple of aliases (dc, domaincomponent). This is really |
| * important do have such a representation, as 'cn' and 'commonname' share |
| * the same OID. |
| * |
| * @param dn |
| * The DN to transform |
| * @param oidsMap |
| * The mapping between names and oids. |
| * @return A normalized form of the DN |
| * @throws InvalidNameException |
| * If the DN is invalid |
| */ |
| public static LdapDN normalize( LdapDN dn, Map<String, OidNormalizer> oidsMap ) throws InvalidNameException, NamingException |
| { |
| if ( ( dn == null ) || ( dn.size() == 0 ) || ( oidsMap == null ) || ( oidsMap.size() == 0 ) ) |
| { |
| return dn; |
| } |
| |
| LdapDN newDn = ( LdapDN ) dn.clone(); |
| |
| Enumeration<Rdn> rdns = newDn.getAllRdn(); |
| |
| // Loop on all RDNs |
| while ( rdns.hasMoreElements() ) |
| { |
| Rdn rdn = rdns.nextElement(); |
| String upName = rdn.getUpName(); |
| rdnOidToName( rdn, oidsMap ); |
| rdn.normalize(); |
| rdn.setUpName( upName ); |
| } |
| |
| newDn.normalizeInternal(); |
| |
| newDn.normalized = true; |
| return newDn; |
| } |
| |
| /** |
| * Change the internal DN, using the first alias instead of oids or other |
| * aliases. As we still have the UP name of each RDN, we will be able to |
| * provide both representation of the DN. example : dn: 2.5.4.3=People, |
| * dc=example, domainComponent=com will be transformed to : cn=People, |
| * dc=example, dc=com because 2.5.4.3 is the OID for cn and dc is the first |
| * alias of the couple of aliases (dc, domaincomponent). This is really |
| * important do have such a representation, as 'cn' and 'commonname' share |
| * the same OID. |
| * |
| * @param oidsMap |
| * The mapping between names and oids. |
| * @throws InvalidNameException |
| * If the DN is invalid |
| */ |
| public void normalize( Map<String, OidNormalizer> oidsMap ) throws InvalidNameException, NamingException |
| { |
| if ( ( oidsMap == null ) || ( oidsMap.size() == 0 ) ) |
| { |
| return; |
| } |
| |
| if ( size() == 0 ) |
| { |
| return; |
| } |
| |
| Enumeration<Rdn> localRdns = getAllRdn(); |
| |
| // Loop on all RDNs |
| while ( localRdns.hasMoreElements() ) |
| { |
| Rdn rdn = localRdns.nextElement(); |
| String localUpName = rdn.getUpName(); |
| rdnOidToName( rdn, oidsMap ); |
| rdn.normalize(); |
| rdn.setUpName( localUpName ); |
| } |
| |
| normalizeInternal(); |
| normalized = true; |
| } |
| |
| /** |
| * Check if a DistinguishedName is syntaxically valid |
| * |
| * @param dn The DN to validate |
| * @return <code>true></code> if the DN is valid, <code>false</code> |
| * otherwise |
| */ |
| public static boolean isValid( String dn ) |
| { |
| return LdapDnParser.validateInternal( dn ); |
| } |
| |
| |
| public boolean isNormalized() |
| { |
| return normalized; |
| } |
| } |