You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by el...@apache.org on 2009/08/06 01:00:19 UTC

svn commit: r801455 - /directory/shared/trunk/ldap/src/main/java/org/apache/directory/shared/ldap/name/Rdn.java

Author: elecharny
Date: Wed Aug  5 23:00:18 2009
New Revision: 801455

URL: http://svn.apache.org/viewvc?rev=801455&view=rev
Log:
Removed ^M

Modified:
    directory/shared/trunk/ldap/src/main/java/org/apache/directory/shared/ldap/name/Rdn.java

Modified: directory/shared/trunk/ldap/src/main/java/org/apache/directory/shared/ldap/name/Rdn.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/src/main/java/org/apache/directory/shared/ldap/name/Rdn.java?rev=801455&r1=801454&r2=801455&view=diff
==============================================================================
--- directory/shared/trunk/ldap/src/main/java/org/apache/directory/shared/ldap/name/Rdn.java (original)
+++ directory/shared/trunk/ldap/src/main/java/org/apache/directory/shared/ldap/name/Rdn.java Wed Aug  5 23:00:18 2009
@@ -117,825 +117,830 @@
     *      href="http://c2.com/cgi/wiki?AlwaysDeclareSerialVersionUid">Always
     *      Declare Serial Version Uid</a>
     */
-   private static final long serialVersionUID = 1L;
+    private static final long serialVersionUID = 1L;
 
-   /** The User Provided RDN */
-   private String upName = null;
+    /** The User Provided RDN */
+    private String upName = null;
 
-   /** The normalized RDN */
-   private String normName = null;
+    /** The normalized RDN */
+    private String normName = null;
 
-   /** The starting position of this RDN in the given string from which
-    * we have extracted the upName */
-   private int start;
-
-   /** The length of this RDN upName */
-   private int length;
-
-   /**
-    * Stores all couple type = value. We may have more than one type, if the
-    * '+' character appears in the AttributeTypeAndValue. This is a TreeSet,
-    * because we want the ATAVs to be sorted. An atav may contain more than one
-    * value. In this case, the values are String stored in a List.
-    */
-   private Set<AttributeTypeAndValue> atavs = null;
+    /** The starting position of this RDN in the given string from which
+     * we have extracted the upName */
+    private int start;
 
-   /**
-    * We also keep a set of types, in order to use manipulations. A type is
-    * connected with the atav it represents.
-    * 
-    * Note : there is no Generic available classes in commons-collection...
-    */
-   @SuppressWarnings({"unchecked"})
-   private Map<String, AttributeTypeAndValue> atavTypes = new MultiValueMap();
+    /** The length of this RDN upName */
+    private int length;
 
-   /**
-    * We keep the type for a single valued RDN, to avoid the creation of an HashMap
-    */
-   private String atavType = null;
+    /**
+     * Stores all couple type = value. We may have more than one type, if the
+     * '+' character appears in the AttributeTypeAndValue. This is a TreeSet,
+     * because we want the ATAVs to be sorted. An atav may contain more than one
+     * value. In this case, the values are String stored in a List.
+     */
+    private Set<AttributeTypeAndValue> atavs = null;
 
-   /**
-    * A simple AttributeTypeAndValue is used to store the Rdn for the simple
-    * case where we only have a single type=value. This will be 99.99% the
-    * case. This avoids the creation of a HashMap.
-    */
-   protected AttributeTypeAndValue atav = null;
+    /**
+     * We also keep a set of types, in order to use manipulations. A type is
+     * connected with the atav it represents.
+     * 
+     * Note : there is no Generic available classes in commons-collection...
+     */
+    @SuppressWarnings(
+        { "unchecked" })
+    private Map<String, AttributeTypeAndValue> atavTypes = new MultiValueMap();
 
-   /**
-    * The number of atavs. We store this number here to avoid complex
-    * manipulation of atav and atavs
-    */
-   private int nbAtavs = 0;
+    /**
+     * We keep the type for a single valued RDN, to avoid the creation of an HashMap
+     */
+    private String atavType = null;
 
-   /** CompareTo() results */
-   public static final int UNDEFINED = Integer.MAX_VALUE;
+    /**
+     * A simple AttributeTypeAndValue is used to store the Rdn for the simple
+     * case where we only have a single type=value. This will be 99.99% the
+     * case. This avoids the creation of a HashMap.
+     */
+    protected AttributeTypeAndValue atav = null;
 
-   /** Constant used in comparisons */
-   public static final int SUPERIOR = 1;
+    /**
+     * The number of atavs. We store this number here to avoid complex
+     * manipulation of atav and atavs
+     */
+    private int nbAtavs = 0;
 
-   /** Constant used in comparisons */
-   public static final int INFERIOR = -1;
+    /** CompareTo() results */
+    public static final int UNDEFINED = Integer.MAX_VALUE;
 
-   /** Constant used in comparisons */
-   public static final int EQUAL = 0;
+    /** Constant used in comparisons */
+    public static final int SUPERIOR = 1;
 
+    /** Constant used in comparisons */
+    public static final int INFERIOR = -1;
 
-   /**
-    * A empty constructor.
-    */
-   public Rdn()
-   {
-       // Don't waste space... This is not so often we have multiple
-       // name-components in a RDN... So we won't initialize the Map and the
-       // treeSet.
-       upName = "";
-       normName = "";
-   }
+    /** Constant used in comparisons */
+    public static final int EQUAL = 0;
 
 
-   /**
-    * A constructor that parse a String representing a RDN.
-    *
-    * @param rdn The String containing the RDN to parse
-    * @throws InvalidNameException If the RDN is invalid
-    */
-   public Rdn( String rdn ) throws InvalidNameException
-   {
-       start = 0;
-
-       if ( StringTools.isNotEmpty( rdn ) )
-       {
-           // Parse the string. The Rdn will be updated.
-           RdnParser.parse( rdn, this );
-
-           // create the internal normalized form
-           // and store the user provided form
-           normalize();
-           upName = rdn;
-           length = rdn.length();
-       }
-       else
-       {
-           upName = "";
-           normName = "";
-           length = 0;
-       }
-   }
-
-
-   /**
-    * A constructor that constructs a RDN from a type and a value. Constructs
-    * an Rdn from the given attribute type and value. The string attribute
-    * values are not interpreted as RFC 2253 formatted RDN strings. That is,
-    * the values are used literally (not parsed) and assumed to be un-escaped.
-    *
-    * @param upType The user provided type of the RDN
-    * @param upValue The user provided value of the RDN
-    * @param normType The normalized provided type of the RDN
-    * @param normValue The normalized provided value of the RDN
-    * @throws InvalidNameException If the RDN is invalid
-    */
-   public Rdn( String upType, String normType, String upValue, String normValue ) throws InvalidNameException
-   {
-       addAttributeTypeAndValue( upType, normType, 
-           new ClientStringValue( upValue), 
-           new ClientStringValue( normValue ) );
-
-       upName = upType + '=' + upValue;
-       start = 0;
-       length = upName.length();
-       // create the internal normalized form
-       normalize();
-   }
-
-
-   /**
-    * A constructor that constructs a RDN from a type, a position and a length.
-    *
-    * @param start The starting point for this RDN in the user provided DN
-    * @param length The RDN's length
-    * @param upName The user provided name
-    * @param normName the normalized name
-    */
-   /* No protection */ Rdn( int start, int length, String upName, String normName )
-   {
-       this.start = 0;
-       this.length = length;
-       this.upName = upName;
-       this.normName = normName;
-   }
-
-   
-   /**
-    * Constructs an Rdn from the given rdn. The contents of the rdn are simply
-    * copied into the newly created
-    *
-    * @param rdn
-    *            The non-null Rdn to be copied.
-    */
-   @SuppressWarnings({"unchecked"})
-   public Rdn( Rdn rdn )
-   {
-       nbAtavs = rdn.getNbAtavs();
-       this.normName = rdn.normName;
-       this.upName = rdn.getUpName();
-       this.start = rdn.start;
-       this.length = rdn.length;
-
-       switch ( rdn.getNbAtavs() )
-       {
-           case 0:
-               return;
-
-           case 1:
-               this.atav = ( AttributeTypeAndValue ) rdn.atav.clone();
-               return;
-
-           default:
-               // We must duplicate the treeSet and the hashMap
-               atavs = new TreeSet<AttributeTypeAndValue>();
-               atavTypes = new MultiValueMap();
-
-               for ( AttributeTypeAndValue currentAtav:rdn.atavs )
-               {
-                   atavs.add( (AttributeTypeAndValue)currentAtav.clone() );
-                   atavTypes.put( currentAtav.getNormType(), currentAtav );
-               }
-               
-               return;
-       }
-   }
-
-
-   /**
-    * Transform the external representation of the current RDN to an internal
-    * normalized form where : - types are trimmed and lowercased - values are
-    * trimmed and lowercased
-    */
-   // WARNING : The protection level is left unspecified intentionnaly.
-   // We need this method to be visible from the DnParser class, but not
-   // from outside this package.
-   /* Unspecified protection */void normalize()
-   {
-       switch ( nbAtavs )
-       {
-           case 0:
-               // An empty RDN
-               normName = "";
-               break;
-
-           case 1:
-               // We have a single AttributeTypeAndValue
-               // We will trim and lowercase type and value.
-               if ( !atav.getNormValue().isBinary() )
-               {
-                   normName = atav.getNormalizedValue();
-               }
-               else
-               {
-                   normName = atav.getNormType() + "=#" + StringTools.dumpHexPairs( atav.getNormValue().getBytes() );
-               }
-
-               break;
-
-           default:
-               // We have more than one AttributeTypeAndValue
-               StringBuffer sb = new StringBuffer();
-
-               boolean isFirst = true;
-
-               for ( AttributeTypeAndValue ata:atavs )
-               {
-                   if ( isFirst )
-                   {
-                       isFirst = false;
-                   }
-                   else
-                   {
-                       sb.append( '+' );
-                   }
-
-                   sb.append( ata.normalize() );
-               }
-
-               normName = sb.toString();
-               break;
-       }
-   }
-
-
-   /**
-    * Add a AttributeTypeAndValue to the current RDN
-    *
-    * @param upType The user provided type of the added RDN.
-    * @param type The normalized provided type of the added RDN.
-    * @param upValue The user provided value of the added RDN
-    * @param value The normalized provided value of the added RDN
-    * @throws InvalidNameException
-    *             If the RDN is invalid
-    */
-   // WARNING : The protection level is left unspecified intentionally.
-   // We need this method to be visible from the DnParser class, but not
-   // from outside this package.
-   @SuppressWarnings({"unchecked"})
-   /* Unspecified protection */void addAttributeTypeAndValue( String upType, String type, Value<?> upValue, Value<?> value ) 
-       throws InvalidNameException
-   {
-       // First, let's normalize the type
-       String normalizedType = StringTools.lowerCaseAscii(type);
-       Value<?> normalizedValue = value;
-
-       switch ( nbAtavs )
-       {
-           case 0:
-               // This is the first AttributeTypeAndValue. Just stores it.
-               atav = new AttributeTypeAndValue( upType, type, upValue, normalizedValue );
-               nbAtavs = 1;
-               atavType = normalizedType;
-               return;
-
-           case 1:
-               // We already have an atav. We have to put it in the HashMap
-               // before adding a new one.
-               // First, create the HashMap,
-               atavs = new TreeSet<AttributeTypeAndValue>();
-
-               // and store the existing AttributeTypeAndValue into it.
-               atavs.add( atav );
-               atavTypes = new MultiValueMap();
-               atavTypes.put( atavType, atav );
-
-               atav = null;
-
-           // Now, fall down to the commmon case
-           // NO BREAK !!!
-
-           default:
-               // add a new AttributeTypeAndValue
-               AttributeTypeAndValue newAtav = new AttributeTypeAndValue( upType, type, upValue, normalizedValue );
-               atavs.add( newAtav );
-               atavTypes.put( normalizedType, newAtav );
-
-               nbAtavs++;
-               break;
-
-       }
-   }
-
-
-   /**
-    * Add a AttributeTypeAndValue to the current RDN
-    *
-    * @param value The added AttributeTypeAndValue
-    */
-   // WARNING : The protection level is left unspecified intentionnaly.
-   // We need this method to be visible from the DnParser class, but not
-   // from outside this package.
-   @SuppressWarnings({"unchecked"})
-   /* Unspecified protection */void addAttributeTypeAndValue( AttributeTypeAndValue value )
-   {
-       String normalizedType = value.getNormType();
-
-       switch ( nbAtavs )
-       {
-           case 0:
-               // This is the first AttributeTypeAndValue. Just stores it.
-               this.atav = value;
-               nbAtavs = 1;
-               atavType = normalizedType;
-               return;
-
-           case 1:
-               // We already have an atav. We have to put it in the HashMap
-               // before adding a new one.
-               // First, create the HashMap,
-               atavs = new TreeSet<AttributeTypeAndValue>();
-
-               // and store the existing AttributeTypeAndValue into it.
-               atavs.add( this.atav );
-               atavTypes = new MultiValueMap();
-               atavTypes.put( atavType, this.atav );
-
-               this.atav = null;
-
-           // Now, fall down to the commmon case
-           // NO BREAK !!!
-
-           default:
-               // add a new AttributeTypeAndValue
-               atavs.add( value );
-               atavTypes.put( normalizedType, value );
-
-               nbAtavs++;
-               break;
-
-       }
-   }
-
-   
-   /**
-    * Clear the RDN, removing all the AttributeTypeAndValues.
-    */
-   public void clear()
-   {
-       atav = null;
-       atavs = null;
-       atavType = null;
-       atavTypes.clear();
-       nbAtavs = 0;
-       normName = "";
-       upName = "";
-       start = -1;
-       length = 0;
-   }
-
-
-   /**
-    * Get the Value of the AttributeTypeAndValue which type is given as an
-    * argument.
-    *
-    * @param type
-    *            The type of the NameArgument
-    * @return The Value to be returned, or null if none found.
-    * @throws InvalidNameException 
-    */
-   public Object getValue( String type ) throws InvalidNameException
-   {
-       // First, let's normalize the type
-       String normalizedType = StringTools.lowerCaseAscii( StringTools.trim( type ) );
-
-       switch ( nbAtavs )
-       {
-           case 0:
-               return "";
-
-           case 1:
-               if ( StringTools.equals( atav.getNormType(), normalizedType ) )
-               {
-                   return atav.getNormValue().get();
-               }
-
-               return "";
-
-           default:
-               if ( atavTypes.containsKey( normalizedType ) )
-               {
-                   Object obj = atavTypes.get( normalizedType );
-
-                   if ( obj instanceof AttributeTypeAndValue )
-                   {
-                       return ( ( AttributeTypeAndValue ) obj ).getNormValue();
-                   }
-                   else if ( obj instanceof List )
-                   {
-                       StringBuffer sb = new StringBuffer();
-                       boolean isFirst = true;
-                       List<AttributeTypeAndValue> atavList = ( ( List<AttributeTypeAndValue> ) obj );
-
-                       for ( AttributeTypeAndValue elem:atavList )
-                       {
-                           if ( isFirst )
-                           {
-                               isFirst = false;
-                           }
-                           else
-                           {
-                               sb.append( ',' );
-                           }
-
-                           sb.append( elem.getNormValue() );
-                       }
-
-                       return sb.toString();
-                   }
-                   else
-                   {
-                       throw new InvalidNameException( "Bad object stored in the RDN" );
-                   }
-               }
-
-               return "";
-       }
-   }
-
-   
-   /** 
-    * Get the start position
-    *
-    * @return The start position in the DN
-    */
-   public int getStart()
-   {
-       return start;
-   }
-   
-
-   /**
-    * Get the Rdn length
-    *
-    * @return The Rdn length
-    */
-   public int getLength()
-   {
-       return length;
-   }
-   
-   /**
-    * Get the AttributeTypeAndValue which type is given as an argument. If we
-    * have more than one value associated with the type, we will return only
-    * the first one.
-    *
-    * @param type
-    *            The type of the NameArgument to be returned
-    * @return The AttributeTypeAndValue, of null if none is found.
-    */
-   public AttributeTypeAndValue getAttributeTypeAndValue( String type )
-   {
-       // First, let's normalize the type
-       String normalizedType = StringTools.lowerCaseAscii( StringTools.trim( type ) );
-
-       switch ( nbAtavs )
-       {
-           case 0:
-               return null;
-
-           case 1:
-               if ( atav.getNormType().equals( normalizedType ) )
-               {
-                   return atav;
-               }
-
-               return null;
-
-           default:
-               if ( atavTypes.containsKey( normalizedType ) )
-               {
-                   return atavTypes.get( normalizedType );
-               }
-
-               return null;
-       }
-   }
-
-
-   /**
-    * Retrieves the components of this RDN as an iterator of AttributeTypeAndValue. 
-    * The effect on the iterator of updates to this RDN is undefined. If the
-    * RDN has zero components, an empty (non-null) iterator is returned.
-    *
-    * @return an iterator of the components of this RDN, each an AttributeTypeAndValue
-    */
-   public Iterator<AttributeTypeAndValue> iterator()
-   {
-       if ( nbAtavs == 1 || nbAtavs == 0 )
-       {
-           return new Iterator<AttributeTypeAndValue>()
-           {
-               private boolean hasMoreElement = nbAtavs == 1;
-
-
-               public boolean hasNext()
-               {
-                   return hasMoreElement;
-               }
-
-
-               public AttributeTypeAndValue next()
-               {
-                   AttributeTypeAndValue obj = atav;
-                   hasMoreElement = false;
-                   return obj;
-               }
-
-
-               public void remove()
-               {
-                   // nothing to do
-               }
-           };
-       }
-       else
-       {
-           return atavs.iterator();
-       }
-   }
-
-
-   /**
-    * Clone the Rdn
-    * 
-    * @return A clone of the current RDN
-    */
-   public Object clone()
-   {
-       try
-       {
-           Rdn rdn = ( Rdn ) super.clone();
-
-           // The AttributeTypeAndValue is immutable. We won't clone it
-
-           switch ( rdn.getNbAtavs() )
-           {
-               case 0:
-                   break;
-
-               case 1:
-                   rdn.atav = ( AttributeTypeAndValue ) this.atav.clone();
-                   rdn.atavTypes = atavTypes;
-                   break;
-
-               default:
-                   // We must duplicate the treeSet and the hashMap
-                   rdn.atavTypes = new MultiValueMap();
-                   rdn.atavs = new TreeSet<AttributeTypeAndValue>();
-
-                   for ( AttributeTypeAndValue currentAtav:this.atavs )
-                   {
-                       rdn.atavs.add( (AttributeTypeAndValue)currentAtav.clone() );
-                       rdn.atavTypes.put( currentAtav.getNormType(), currentAtav );
-                   }
-
-                   break;
-           }
-
-           return rdn;
-       }
-       catch ( CloneNotSupportedException cnse )
-       {
-           throw new Error( "Assertion failure" );
-       }
-   }
-
-
-   /**
-    * Compares two RDNs. They are equals if : 
-    * <li>their have the same number of NC (AttributeTypeAndValue) 
-    * <li>each ATAVs are equals 
-    * <li>comparison of type are done case insensitive 
-    * <li>each value is equal, case sensitive 
-    * <li>Order of ATAV is not important If the RDNs are not equals, a positive number is
-    * returned if the first RDN is greater, negative otherwise
-    *
-    * @param object
-    * @return 0 if both rdn are equals. -1 if the current RDN is inferior, 1 if
-    *         the current Rdn is superior, UNDEFINED otherwise.
-    */
-   public int compareTo( Object object )
-   {
-       if ( object == null )
-       {
-           return SUPERIOR;
-       }
-
-       if ( object instanceof Rdn )
-       {
-           Rdn rdn = ( Rdn ) object;
-
-           if ( rdn.nbAtavs != nbAtavs )
-           {
-               // We don't have the same number of ATAVs. The Rdn which
-               // has the higher number of Atav is the one which is
-               // superior
-               return nbAtavs - rdn.nbAtavs;
-           }
-
-           switch ( nbAtavs )
-           {
-               case 0:
-                   return EQUAL;
-
-               case 1:
-                   return atav.compareTo( rdn.atav );
-
-               default:
-                   // We have more than one value. We will
-                   // go through all of them.
-
-                   // the types are already normalized and sorted in the atavs TreeSet
-                   // so we could compare the 1st with the 1st, then the 2nd with the 2nd, etc.
-                   Iterator<AttributeTypeAndValue> localIterator = atavs.iterator();
-                   Iterator<AttributeTypeAndValue> paramIterator = rdn.atavs.iterator();
-                   
-                   while(localIterator.hasNext() || paramIterator.hasNext())
-                   {
-                       if(!localIterator.hasNext())
-                       {
-                           return SUPERIOR;
-                       }
-                       if(!paramIterator.hasNext())
-                       {
-                           return INFERIOR;
-                       }
-                       
-                       AttributeTypeAndValue localAtav = localIterator.next();
-                       AttributeTypeAndValue paramAtav = paramIterator.next();
-                       int result = localAtav.compareTo( paramAtav );
-                       if ( result != EQUAL )
-                       {
-                           return result;
-                       }
-                   }
-
-                   return EQUAL;
-           }
-       }
-       else
-       {
-           return UNDEFINED;
-       }
-   }
+    /**
+     * A empty constructor.
+     */
+    public Rdn()
+    {
+        // Don't waste space... This is not so often we have multiple
+        // name-components in a RDN... So we won't initialize the Map and the
+        // treeSet.
+        upName = "";
+        normName = "";
+    }
 
 
-   /**
-    * @return a String representation of the RDN
-    */
-   public String toString()
-   {
-       return normName == null ? "" : normName;
-   }
+    /**
+     * A constructor that parse a String representing a RDN.
+     *
+     * @param rdn The String containing the RDN to parse
+     * @throws InvalidNameException If the RDN is invalid
+     */
+    public Rdn( String rdn ) throws InvalidNameException
+    {
+        start = 0;
 
+        if ( StringTools.isNotEmpty( rdn ) )
+        {
+            // Parse the string. The Rdn will be updated.
+            RdnParser.parse( rdn, this );
 
-   /**
-    * @return the user provided name
-    */
-   public String getUpName()
-   {
-       return upName;
-   }
+            // create the internal normalized form
+            // and store the user provided form
+            normalize();
+            upName = rdn;
+            length = rdn.length();
+        }
+        else
+        {
+            upName = "";
+            normName = "";
+            length = 0;
+        }
+    }
 
 
-   /**
-    * @return The normalized name
-    */
-   public String getNormName()
-   {
-       return normName == null ? "" : normName;
-   }
+    /**
+     * A constructor that constructs a RDN from a type and a value. Constructs
+     * an Rdn from the given attribute type and value. The string attribute
+     * values are not interpreted as RFC 2253 formatted RDN strings. That is,
+     * the values are used literally (not parsed) and assumed to be un-escaped.
+     *
+     * @param upType The user provided type of the RDN
+     * @param upValue The user provided value of the RDN
+     * @param normType The normalized provided type of the RDN
+     * @param normValue The normalized provided value of the RDN
+     * @throws InvalidNameException If the RDN is invalid
+     */
+    public Rdn( String upType, String normType, String upValue, String normValue ) throws InvalidNameException
+    {
+        addAttributeTypeAndValue( upType, normType, new ClientStringValue( upValue ), new ClientStringValue( normValue ) );
 
+        upName = upType + '=' + upValue;
+        start = 0;
+        length = upName.length();
+        // create the internal normalized form
+        normalize();
+    }
 
-   /**
-    * Set the User Provided Name
-    * @param upName the User Provided dame 
-    */
-   public void setUpName( String upName )
-   {
-       this.upName = upName;
-   }
 
+    /**
+     * A constructor that constructs a RDN from a type, a position and a length.
+     *
+     * @param start The starting point for this RDN in the user provided DN
+     * @param length The RDN's length
+     * @param upName The user provided name
+     * @param normName the normalized name
+     */
+    /* No protection */Rdn( int start, int length, String upName, String normName )
+    {
+        this.start = 0;
+        this.length = length;
+        this.upName = upName;
+        this.normName = normName;
+    }
 
-   /**
-    * @return Returns the nbAtavs.
-    */
-   public int getNbAtavs()
-   {
-       return nbAtavs;
-   }
 
+    /**
+     * Constructs an Rdn from the given rdn. The contents of the rdn are simply
+     * copied into the newly created
+     *
+     * @param rdn
+     *            The non-null Rdn to be copied.
+     */
+    @SuppressWarnings(
+        { "unchecked" })
+    public Rdn( Rdn rdn )
+    {
+        nbAtavs = rdn.getNbAtavs();
+        this.normName = rdn.normName;
+        this.upName = rdn.getUpName();
+        this.start = rdn.start;
+        this.length = rdn.length;
+
+        switch ( rdn.getNbAtavs() )
+        {
+            case 0:
+                return;
+
+            case 1:
+                this.atav = ( AttributeTypeAndValue ) rdn.atav.clone();
+                return;
+
+            default:
+                // We must duplicate the treeSet and the hashMap
+                atavs = new TreeSet<AttributeTypeAndValue>();
+                atavTypes = new MultiValueMap();
+
+                for ( AttributeTypeAndValue currentAtav : rdn.atavs )
+                {
+                    atavs.add( ( AttributeTypeAndValue ) currentAtav.clone() );
+                    atavTypes.put( currentAtav.getNormType(), currentAtav );
+                }
+
+                return;
+        }
+    }
+
+
+    /**
+     * Transform the external representation of the current RDN to an internal
+     * normalized form where : - types are trimmed and lowercased - values are
+     * trimmed and lowercased
+     */
+    // WARNING : The protection level is left unspecified intentionnaly.
+    // We need this method to be visible from the DnParser class, but not
+    // from outside this package.
+    /* Unspecified protection */void normalize()
+    {
+        switch ( nbAtavs )
+        {
+            case 0:
+                // An empty RDN
+                normName = "";
+                break;
+
+            case 1:
+                // We have a single AttributeTypeAndValue
+                // We will trim and lowercase type and value.
+                if ( !atav.getNormValue().isBinary() )
+                {
+                    normName = atav.getNormalizedValue();
+                }
+                else
+                {
+                    normName = atav.getNormType() + "=#" + StringTools.dumpHexPairs( atav.getNormValue().getBytes() );
+                }
+
+                break;
+
+            default:
+                // We have more than one AttributeTypeAndValue
+                StringBuffer sb = new StringBuffer();
+
+                boolean isFirst = true;
+
+                for ( AttributeTypeAndValue ata : atavs )
+                {
+                    if ( isFirst )
+                    {
+                        isFirst = false;
+                    }
+                    else
+                    {
+                        sb.append( '+' );
+                    }
+
+                    sb.append( ata.normalize() );
+                }
+
+                normName = sb.toString();
+                break;
+        }
+    }
+
+
+    /**
+     * Add a AttributeTypeAndValue to the current RDN
+     *
+     * @param upType The user provided type of the added RDN.
+     * @param type The normalized provided type of the added RDN.
+     * @param upValue The user provided value of the added RDN
+     * @param value The normalized provided value of the added RDN
+     * @throws InvalidNameException
+     *             If the RDN is invalid
+     */
+    // WARNING : The protection level is left unspecified intentionally.
+    // We need this method to be visible from the DnParser class, but not
+    // from outside this package.
+    @SuppressWarnings(
+        { "unchecked" })
+    /* Unspecified protection */void addAttributeTypeAndValue( String upType, String type, Value<?> upValue,
+        Value<?> value ) throws InvalidNameException
+    {
+        // First, let's normalize the type
+        String normalizedType = StringTools.lowerCaseAscii( type );
+        Value<?> normalizedValue = value;
+
+        switch ( nbAtavs )
+        {
+            case 0:
+                // This is the first AttributeTypeAndValue. Just stores it.
+                atav = new AttributeTypeAndValue( upType, type, upValue, normalizedValue );
+                nbAtavs = 1;
+                atavType = normalizedType;
+                return;
+
+            case 1:
+                // We already have an atav. We have to put it in the HashMap
+                // before adding a new one.
+                // First, create the HashMap,
+                atavs = new TreeSet<AttributeTypeAndValue>();
+
+                // and store the existing AttributeTypeAndValue into it.
+                atavs.add( atav );
+                atavTypes = new MultiValueMap();
+                atavTypes.put( atavType, atav );
+
+                atav = null;
+
+                // Now, fall down to the commmon case
+                // NO BREAK !!!
+
+            default:
+                // add a new AttributeTypeAndValue
+                AttributeTypeAndValue newAtav = new AttributeTypeAndValue( upType, type, upValue, normalizedValue );
+                atavs.add( newAtav );
+                atavTypes.put( normalizedType, newAtav );
+
+                nbAtavs++;
+                break;
+
+        }
+    }
+
+
+    /**
+     * Add a AttributeTypeAndValue to the current RDN
+     *
+     * @param value The added AttributeTypeAndValue
+     */
+    // WARNING : The protection level is left unspecified intentionnaly.
+    // We need this method to be visible from the DnParser class, but not
+    // from outside this package.
+    @SuppressWarnings(
+        { "unchecked" })
+    /* Unspecified protection */void addAttributeTypeAndValue( AttributeTypeAndValue value )
+    {
+        String normalizedType = value.getNormType();
+
+        switch ( nbAtavs )
+        {
+            case 0:
+                // This is the first AttributeTypeAndValue. Just stores it.
+                this.atav = value;
+                nbAtavs = 1;
+                atavType = normalizedType;
+                return;
+
+            case 1:
+                // We already have an atav. We have to put it in the HashMap
+                // before adding a new one.
+                // First, create the HashMap,
+                atavs = new TreeSet<AttributeTypeAndValue>();
+
+                // and store the existing AttributeTypeAndValue into it.
+                atavs.add( this.atav );
+                atavTypes = new MultiValueMap();
+                atavTypes.put( atavType, this.atav );
+
+                this.atav = null;
+
+                // Now, fall down to the commmon case
+                // NO BREAK !!!
+
+            default:
+                // add a new AttributeTypeAndValue
+                atavs.add( value );
+                atavTypes.put( normalizedType, value );
+
+                nbAtavs++;
+                break;
+
+        }
+    }
+
+
+    /**
+     * Clear the RDN, removing all the AttributeTypeAndValues.
+     */
+    public void clear()
+    {
+        atav = null;
+        atavs = null;
+        atavType = null;
+        atavTypes.clear();
+        nbAtavs = 0;
+        normName = "";
+        upName = "";
+        start = -1;
+        length = 0;
+    }
+
+
+    /**
+     * Get the Value of the AttributeTypeAndValue which type is given as an
+     * argument.
+     *
+     * @param type
+     *            The type of the NameArgument
+     * @return The Value to be returned, or null if none found.
+     * @throws InvalidNameException 
+     */
+    public Object getValue( String type ) throws InvalidNameException
+    {
+        // First, let's normalize the type
+        String normalizedType = StringTools.lowerCaseAscii( StringTools.trim( type ) );
+
+        switch ( nbAtavs )
+        {
+            case 0:
+                return "";
+
+            case 1:
+                if ( StringTools.equals( atav.getNormType(), normalizedType ) )
+                {
+                    return atav.getNormValue().get();
+                }
+
+                return "";
+
+            default:
+                if ( atavTypes.containsKey( normalizedType ) )
+                {
+                    Object obj = atavTypes.get( normalizedType );
+
+                    if ( obj instanceof AttributeTypeAndValue )
+                    {
+                        return ( ( AttributeTypeAndValue ) obj ).getNormValue();
+                    }
+                    else if ( obj instanceof List )
+                    {
+                        StringBuffer sb = new StringBuffer();
+                        boolean isFirst = true;
+                        List<AttributeTypeAndValue> atavList = ( ( List<AttributeTypeAndValue> ) obj );
+
+                        for ( AttributeTypeAndValue elem : atavList )
+                        {
+                            if ( isFirst )
+                            {
+                                isFirst = false;
+                            }
+                            else
+                            {
+                                sb.append( ',' );
+                            }
+
+                            sb.append( elem.getNormValue() );
+                        }
+
+                        return sb.toString();
+                    }
+                    else
+                    {
+                        throw new InvalidNameException( "Bad object stored in the RDN" );
+                    }
+                }
+
+                return "";
+        }
+    }
+
+
+    /** 
+     * Get the start position
+     *
+     * @return The start position in the DN
+     */
+    public int getStart()
+    {
+        return start;
+    }
+
+
+    /**
+     * Get the Rdn length
+     *
+     * @return The Rdn length
+     */
+    public int getLength()
+    {
+        return length;
+    }
+
+
+    /**
+     * Get the AttributeTypeAndValue which type is given as an argument. If we
+     * have more than one value associated with the type, we will return only
+     * the first one.
+     *
+     * @param type
+     *            The type of the NameArgument to be returned
+     * @return The AttributeTypeAndValue, of null if none is found.
+     */
+    public AttributeTypeAndValue getAttributeTypeAndValue( String type )
+    {
+        // First, let's normalize the type
+        String normalizedType = StringTools.lowerCaseAscii( StringTools.trim( type ) );
+
+        switch ( nbAtavs )
+        {
+            case 0:
+                return null;
+
+            case 1:
+                if ( atav.getNormType().equals( normalizedType ) )
+                {
+                    return atav;
+                }
+
+                return null;
+
+            default:
+                if ( atavTypes.containsKey( normalizedType ) )
+                {
+                    return atavTypes.get( normalizedType );
+                }
+
+                return null;
+        }
+    }
+
+
+    /**
+     * Retrieves the components of this RDN as an iterator of AttributeTypeAndValue. 
+     * The effect on the iterator of updates to this RDN is undefined. If the
+     * RDN has zero components, an empty (non-null) iterator is returned.
+     *
+     * @return an iterator of the components of this RDN, each an AttributeTypeAndValue
+     */
+    public Iterator<AttributeTypeAndValue> iterator()
+    {
+        if ( nbAtavs == 1 || nbAtavs == 0 )
+        {
+            return new Iterator<AttributeTypeAndValue>()
+            {
+                private boolean hasMoreElement = nbAtavs == 1;
+
+
+                public boolean hasNext()
+                {
+                    return hasMoreElement;
+                }
+
+
+                public AttributeTypeAndValue next()
+                {
+                    AttributeTypeAndValue obj = atav;
+                    hasMoreElement = false;
+                    return obj;
+                }
+
+
+                public void remove()
+                {
+                    // nothing to do
+                }
+            };
+        }
+        else
+        {
+            return atavs.iterator();
+        }
+    }
+
+
+    /**
+     * Clone the Rdn
+     * 
+     * @return A clone of the current RDN
+     */
+    public Object clone()
+    {
+        try
+        {
+            Rdn rdn = ( Rdn ) super.clone();
+
+            // The AttributeTypeAndValue is immutable. We won't clone it
+
+            switch ( rdn.getNbAtavs() )
+            {
+                case 0:
+                    break;
+
+                case 1:
+                    rdn.atav = ( AttributeTypeAndValue ) this.atav.clone();
+                    rdn.atavTypes = atavTypes;
+                    break;
+
+                default:
+                    // We must duplicate the treeSet and the hashMap
+                    rdn.atavTypes = new MultiValueMap();
+                    rdn.atavs = new TreeSet<AttributeTypeAndValue>();
+
+                    for ( AttributeTypeAndValue currentAtav : this.atavs )
+                    {
+                        rdn.atavs.add( ( AttributeTypeAndValue ) currentAtav.clone() );
+                        rdn.atavTypes.put( currentAtav.getNormType(), currentAtav );
+                    }
+
+                    break;
+            }
+
+            return rdn;
+        }
+        catch ( CloneNotSupportedException cnse )
+        {
+            throw new Error( "Assertion failure" );
+        }
+    }
+
+
+    /**
+     * Compares two RDNs. They are equals if : 
+     * <li>their have the same number of NC (AttributeTypeAndValue) 
+     * <li>each ATAVs are equals 
+     * <li>comparison of type are done case insensitive 
+     * <li>each value is equal, case sensitive 
+     * <li>Order of ATAV is not important If the RDNs are not equals, a positive number is
+     * returned if the first RDN is greater, negative otherwise
+     *
+     * @param object
+     * @return 0 if both rdn are equals. -1 if the current RDN is inferior, 1 if
+     *         the current Rdn is superior, UNDEFINED otherwise.
+     */
+    public int compareTo( Object object )
+    {
+        if ( object == null )
+        {
+            return SUPERIOR;
+        }
+
+        if ( object instanceof Rdn )
+        {
+            Rdn rdn = ( Rdn ) object;
+
+            if ( rdn.nbAtavs != nbAtavs )
+            {
+                // We don't have the same number of ATAVs. The Rdn which
+                // has the higher number of Atav is the one which is
+                // superior
+                return nbAtavs - rdn.nbAtavs;
+            }
+
+            switch ( nbAtavs )
+            {
+                case 0:
+                    return EQUAL;
+
+                case 1:
+                    return atav.compareTo( rdn.atav );
+
+                default:
+                    // We have more than one value. We will
+                    // go through all of them.
+
+                    // the types are already normalized and sorted in the atavs TreeSet
+                    // so we could compare the 1st with the 1st, then the 2nd with the 2nd, etc.
+                    Iterator<AttributeTypeAndValue> localIterator = atavs.iterator();
+                    Iterator<AttributeTypeAndValue> paramIterator = rdn.atavs.iterator();
+
+                    while ( localIterator.hasNext() || paramIterator.hasNext() )
+                    {
+                        if ( !localIterator.hasNext() )
+                        {
+                            return SUPERIOR;
+                        }
+                        if ( !paramIterator.hasNext() )
+                        {
+                            return INFERIOR;
+                        }
+
+                        AttributeTypeAndValue localAtav = localIterator.next();
+                        AttributeTypeAndValue paramAtav = paramIterator.next();
+                        int result = localAtav.compareTo( paramAtav );
+                        if ( result != EQUAL )
+                        {
+                            return result;
+                        }
+                    }
+
+                    return EQUAL;
+            }
+        }
+        else
+        {
+            return UNDEFINED;
+        }
+    }
+
+
+    /**
+     * @return a String representation of the RDN
+     */
+    public String toString()
+    {
+        return normName == null ? "" : normName;
+    }
+
+
+    /**
+     * @return the user provided name
+     */
+    public String getUpName()
+    {
+        return upName;
+    }
+
+
+    /**
+     * @return The normalized name
+     */
+    public String getNormName()
+    {
+        return normName == null ? "" : normName;
+    }
+
+
+    /**
+     * Set the User Provided Name
+     * @param upName the User Provided dame 
+     */
+    public void setUpName( String upName )
+    {
+        this.upName = upName;
+    }
+
+
+    /**
+     * @return Returns the nbAtavs.
+     */
+    public int getNbAtavs()
+    {
+        return nbAtavs;
+    }
+
+
+    /**
+     * Return the unique AttributeTypeAndValue, or the first one of we have more
+     * than one
+     *
+     * @return The first AttributeTypeAndValue of this RDN
+     */
+    public AttributeTypeAndValue getAtav()
+    {
+        switch ( nbAtavs )
+        {
+            case 0:
+                return null;
+
+            case 1:
+                return atav;
+
+            default:
+                return ( ( TreeSet<AttributeTypeAndValue> ) atavs ).first();
+        }
+    }
+
+
+    /**
+     * Return the user provided type, or the first one of we have more than one (the lowest)
+     *
+     * @return The first user provided type of this RDN
+     */
+    public String getUpType()
+    {
+        switch ( nbAtavs )
+        {
+            case 0:
+                return null;
+
+            case 1:
+                return atav.getUpType();
+
+            default:
+                return ( ( TreeSet<AttributeTypeAndValue> ) atavs ).first().getUpType();
+        }
+    }
+
+
+    /**
+     * Return the normalized type, or the first one of we have more than one (the lowest)
+     *
+     * @return The first normalized type of this RDN
+     */
+    public String getNormType()
+    {
+        switch ( nbAtavs )
+        {
+            case 0:
+                return null;
+
+            case 1:
+                return atav.getNormType();
+
+            default:
+                return ( ( TreeSet<AttributeTypeAndValue> ) atavs ).first().getNormType();
+        }
+    }
+
+
+    /**
+     * Return the value, or the first one of we have more than one (the lowest)
+     *
+     * @return The first value of this RDN
+     */
+    public Object getValue()
+    {
+        switch ( nbAtavs )
+        {
+            case 0:
+                return null;
+
+            case 1:
+                return atav.getNormValue().get();
+
+            default:
+                return ( ( TreeSet<AttributeTypeAndValue> ) atavs ).first().getNormValue().get();
+        }
+    }
+
+
+    /**
+     * Return the User Provided value
+     * 
+     * @return The first User provided value of this RDN
+     */
+    public String getUpValue()
+    {
+        switch ( nbAtavs )
+        {
+            case 0:
+                return null;
+
+            case 1:
+                return atav.getUpValue().getString();
+
+            default:
+                return ( ( TreeSet<AttributeTypeAndValue> ) atavs ).first().getUpValue().getString();
+        }
+    }
 
-   /**
-    * Return the unique AttributeTypeAndValue, or the first one of we have more
-    * than one
-    *
-    * @return The first AttributeTypeAndValue of this RDN
-    */
-   public AttributeTypeAndValue getAtav()
-   {
-       switch ( nbAtavs )
-       {
-           case 0:
-               return null;
-
-           case 1:
-               return atav;
-
-           default:
-               return ((TreeSet<AttributeTypeAndValue>)atavs).first();
-       }
-   }
-
-
-   /**
-    * Return the user provided type, or the first one of we have more than one (the lowest)
-    *
-    * @return The first user provided type of this RDN
-    */
-   public String getUpType()
-   {
-       switch ( nbAtavs )
-       {
-           case 0:
-               return null;
-
-           case 1:
-               return atav.getUpType();
-
-           default:
-               return ((TreeSet<AttributeTypeAndValue>)atavs).first().getUpType();
-       }
-   }
-
-   /**
-    * Return the normalized type, or the first one of we have more than one (the lowest)
-    *
-    * @return The first normalized type of this RDN
-    */
-   public String getNormType()
-   {
-       switch ( nbAtavs )
-       {
-           case 0:
-               return null;
-
-           case 1:
-               return atav.getNormType();
-
-           default:
-               return ((TreeSet<AttributeTypeAndValue>)atavs).first().getNormType();
-       }
-   }
-
-   /**
-    * Return the value, or the first one of we have more than one (the lowest)
-    *
-    * @return The first value of this RDN
-    */
-   public Object getValue()
-   {
-       switch ( nbAtavs )
-       {
-           case 0:
-               return null;
-
-           case 1:
-               return atav.getNormValue().get();
-
-           default:
-               return ((TreeSet<AttributeTypeAndValue>)atavs).first().getNormValue().get();
-       }
-   }
-
-
-   /**
-    * Return the User Provided value
-    * 
-    * @return The first User provided value of this RDN
-    */
-   public String getUpValue()
-   {
-       switch ( nbAtavs )
-       {
-           case 0:
-               return null;
-
-           case 1:
-               return atav.getUpValue().getString();
-
-           default:
-               return ((TreeSet<AttributeTypeAndValue>)atavs).first().getUpValue().getString();
-       }
-   }
 
-      
     /**
      * Return the normalized value, or the first one of we have more than one (the lowest)
      *
@@ -947,509 +952,511 @@
         {
             case 0:
                 return null;
-                
+
             case 1:
                 return atav.getNormValue().getString();
-                
+
             default:
-                return ((TreeSet<AttributeTypeAndValue>)atavs).first().getNormalizedValue();
+                return ( ( TreeSet<AttributeTypeAndValue> ) atavs ).first().getNormalizedValue();
+        }
+    }
+
+
+    /**
+     * Compares the specified Object with this Rdn for equality. Returns true if
+     * the given object is also a Rdn and the two Rdns represent the same
+     * attribute type and value mappings. The order of components in
+     * multi-valued Rdns is not significant.
+     *
+     * @param rdn
+     *            Rdn to be compared for equality with this Rdn
+     * @return true if the specified object is equal to this Rdn
+     */
+    public boolean equals( Object rdn )
+    {
+        if ( this == rdn )
+        {
+            return true;
+        }
+
+        if ( !( rdn instanceof Rdn ) )
+        {
+            return false;
+        }
+
+        return compareTo( rdn ) == EQUAL;
+    }
+
+
+    /**
+     * Get the number of Attribute type and value of this Rdn
+     *
+     * @return The number of ATAVs in this Rdn
+     */
+    public int size()
+    {
+        return nbAtavs;
+    }
+
+
+    /**
+     * Unescape the given string according to RFC 2253 If in <string> form, a
+     * LDAP string representation asserted value can be obtained by replacing
+     * (left-to-right, non-recursively) each <pair> appearing in the <string> as
+     * follows: replace <ESC><ESC> with <ESC>; replace <ESC><special> with
+     * <special>; replace <ESC><hexpair> with the octet indicated by the
+     * <hexpair> If in <hexstring> form, a BER representation can be obtained
+     * from converting each <hexpair> of the <hexstring> to the octet indicated
+     * by the <hexpair>
+     *
+     * @param value
+     *            The value to be unescaped
+     * @return Returns a string value as a String, and a binary value as a byte
+     *         array.
+     * @throws IllegalArgumentException -
+     *             When an Illegal value is provided.
+     */
+    public static Object unescapeValue( String value )
+    {
+        if ( StringTools.isEmpty( value ) )
+        {
+            return "";
+        }
+
+        char[] chars = value.toCharArray();
+
+        if ( chars[0] == '#' )
+        {
+            if ( chars.length == 1 )
+            {
+                // The value is only containing a #
+                return StringTools.EMPTY_BYTES;
+            }
+
+            if ( ( chars.length % 2 ) != 1 )
+            {
+                throw new IllegalArgumentException( "This value is not in hex form, we have an odd number of hex chars" );
+            }
+
+            // HexString form
+            byte[] hexValue = new byte[( chars.length - 1 ) / 2];
+            int pos = 0;
+
+            for ( int i = 1; i < chars.length; i += 2 )
+            {
+                if ( StringTools.isHex( chars, i ) && StringTools.isHex( chars, i + 1 ) )
+                {
+                    hexValue[pos++] = StringTools.getHexValue( chars[i], chars[i + 1] );
+                }
+                else
+                {
+                    throw new IllegalArgumentException( "This value is not in hex form" );
+                }
+            }
+
+            return hexValue;
+        }
+        else
+        {
+            boolean escaped = false;
+            boolean isHex = false;
+            byte pair = -1;
+            int pos = 0;
+
+            byte[] bytes = new byte[chars.length * 6];
+
+            for ( int i = 0; i < chars.length; i++ )
+            {
+                if ( escaped )
+                {
+                    escaped = false;
+
+                    switch ( chars[i] )
+                    {
+                        case '\\':
+                        case '"':
+                        case '+':
+                        case ',':
+                        case ';':
+                        case '<':
+                        case '>':
+                        case '#':
+                        case '=':
+                        case ' ':
+                            bytes[pos++] = ( byte ) chars[i];
+                            break;
+
+                        default:
+                            if ( StringTools.isHex( chars, i ) )
+                            {
+                                isHex = true;
+                                pair = ( ( byte ) ( StringTools.getHexValue( chars[i] ) << 4 ) );
+                            }
+
+                            break;
+                    }
+                }
+                else
+                {
+                    if ( isHex )
+                    {
+                        if ( StringTools.isHex( chars, i ) )
+                        {
+                            pair += StringTools.getHexValue( chars[i] );
+                            bytes[pos++] = pair;
+                        }
+                    }
+                    else
+                    {
+                        switch ( chars[i] )
+                        {
+                            case '\\':
+                                escaped = true;
+                                break;
+
+                            // We must not have a special char
+                            // Specials are : '"', '+', ',', ';', '<', '>', ' ',
+                            // '#' and '='
+                            case '"':
+                            case '+':
+                            case ',':
+                            case ';':
+                            case '<':
+                            case '>':
+                            case '#':
+                                if ( i != 0 )
+                                {
+                                    // '#' are allowed if not in first position
+                                    bytes[pos++] = '#';
+                                    break;
+                                }
+                            case '=':
+                                throw new IllegalArgumentException( "Unescaped special characters are not allowed" );
+
+                            case ' ':
+                                if ( ( i == 0 ) || ( i == chars.length - 1 ) )
+                                {
+                                    throw new IllegalArgumentException( "Unescaped special characters are not allowed" );
+                                }
+                                else
+                                {
+                                    bytes[pos++] = ' ';
+                                    break;
+                                }
+
+                            default:
+                                if ( ( chars[i] >= 0 ) && ( chars[i] < 128 ) )
+                                {
+                                    bytes[pos++] = ( byte ) chars[i];
+                                }
+                                else
+                                {
+                                    byte[] result = StringTools.charToBytes( chars[i] );
+                                    System.arraycopy( result, 0, bytes, pos, result.length );
+                                    pos += result.length;
+                                }
+
+                                break;
+                        }
+                    }
+                }
+            }
+
+            return StringTools.utf8ToString( bytes, pos );
+        }
+    }
+
+
+    /**
+     * Transform a value in a String, accordingly to RFC 2253
+     *
+     * @param value The attribute value to be escaped
+     * @return The escaped string value.
+     */
+    public static String escapeValue( String value )
+    {
+        if ( StringTools.isEmpty( value ) )
+        {
+            return "";
+        }
+
+        char[] chars = value.toCharArray();
+        char[] newChars = new char[chars.length * 3];
+        int pos = 0;
+
+        for ( int i = 0; i < chars.length; i++ )
+        {
+            switch ( chars[i] )
+            {
+                case ' ':
+                    if ( ( i > 0 ) && ( i < chars.length - 1 ) )
+                    {
+                        newChars[pos++] = chars[i];
+                    }
+                    else
+                    {
+                        newChars[pos++] = '\\';
+                        newChars[pos++] = chars[i];
+                    }
+
+                    break;
+
+                case '#':
+                    if ( i != 0 )
+                    {
+                        newChars[pos++] = chars[i];
+                    }
+                    else
+                    {
+                        newChars[pos++] = '\\';
+                        newChars[pos++] = chars[i];
+                    }
+
+                    break;
+
+                case '"':
+                case '+':
+                case ',':
+                case ';':
+                case '=':
+                case '<':
+                case '>':
+                case '\\':
+                    newChars[pos++] = '\\';
+                    newChars[pos++] = chars[i];
+                    break;
+
+                case 0x7F:
+                    newChars[pos++] = '\\';
+                    newChars[pos++] = '7';
+                    newChars[pos++] = 'F';
+                    break;
+
+                case 0x00:
+                case 0x01:
+                case 0x02:
+                case 0x03:
+                case 0x04:
+                case 0x05:
+                case 0x06:
+                case 0x07:
+                case 0x08:
+                case 0x09:
+                case 0x0A:
+                case 0x0B:
+                case 0x0C:
+                case 0x0D:
+                case 0x0E:
+                case 0x0F:
+                    newChars[pos++] = '\\';
+                    newChars[pos++] = '0';
+                    newChars[pos++] = StringTools.dumpHex( ( byte ) ( chars[i] & 0x0F ) );
+                    break;
+
+                case 0x10:
+                case 0x11:
+                case 0x12:
+                case 0x13:
+                case 0x14:
+                case 0x15:
+                case 0x16:
+                case 0x17:
+                case 0x18:
+                case 0x19:
+                case 0x1A:
+                case 0x1B:
+                case 0x1C:
+                case 0x1D:
+                case 0x1E:
+                case 0x1F:
+                    newChars[pos++] = '\\';
+                    newChars[pos++] = '1';
+                    newChars[pos++] = StringTools.dumpHex( ( byte ) ( chars[i] & 0x0F ) );
+                    break;
+
+                default:
+                    newChars[pos++] = chars[i];
+                    break;
+
+            }
+        }
+
+        return new String( newChars, 0, pos );
+    }
+
+
+    /**
+     * Transform a value in a String, accordingly to RFC 2253
+     *
+     * @param attrValue
+     *            The attribute value to be escaped
+     * @return The escaped string value.
+     */
+    public static String escapeValue( byte[] attrValue )
+    {
+        if ( StringTools.isEmpty( attrValue ) )
+        {
+            return "";
+        }
+
+        String value = StringTools.utf8ToString( attrValue );
+
+        return escapeValue( value );
+    }
+
+
+    /**
+      * Gets the hashcode of this rdn.
+      *
+      * @see java.lang.Object#hashCode()
+      * @return the instance's hash code 
+      */
+    public int hashCode()
+    {
+        int result = 37;
+
+        switch ( nbAtavs )
+        {
+            case 0:
+                // An empty RDN
+                break;
+
+            case 1:
+                // We have a single AttributeTypeAndValue
+                result = result * 17 + atav.hashCode();
+                break;
+
+            default:
+                // We have more than one AttributeTypeAndValue
+
+                for ( AttributeTypeAndValue ata : atavs )
+                {
+                    result = result * 17 + ata.hashCode();
+                }
+
+                break;
+        }
+
+        return result;
+    }
+
+
+    /**
+     * @see Externalizable#readExternal(ObjectInput)<p>
+     * 
+     * A RDN is composed of on to many ATAVs (AttributeType And Value).
+     * We should write all those ATAVs sequencially, following the 
+     * structure :
+     * 
+     * <li>nbAtavs</li> The number of ATAVs to write. Can't be 0.
+     * <li>upName</li> The User provided RDN
+     * <li>normName</li> The normalized RDN. It can be empty if the normalized
+     * name equals the upName.
+     * <li>atavs</li>
+     * <p>
+     * For each ATAV :<p>
+     * <li>start</li> The position of this ATAV in the upName string
+     * <li>length</li> The ATAV user provided length
+     * <li>Call the ATAV write method</li> The ATAV itself
+     * 
+     * @param out The stream into which the serialized RDN will be put
+     * @throws IOException If the stream can't be written
+     */
+    public void writeExternal( ObjectOutput out ) throws IOException
+    {
+        out.writeInt( nbAtavs );
+        out.writeUTF( upName );
+
+        if ( upName.equals( normName ) )
+        {
+            out.writeUTF( "" );
+        }
+        else
+        {
+            out.writeUTF( normName );
+        }
+
+        out.writeInt( start );
+        out.writeInt( length );
+
+        switch ( nbAtavs )
+        {
+            case 0:
+                break;
+
+            case 1:
+                out.writeObject( atav );
+                break;
+
+            default:
+                for ( AttributeTypeAndValue value : atavs )
+                {
+                    out.writeObject( value );
+                }
+
+                break;
+        }
+    }
+
+
+    /**
+     * @see Externalizable#readExternal(ObjectInput)
+     * 
+     * We read back the data to create a new RDB. The structure 
+     * read is exposed in the {@link Rdn#writeExternal(ObjectOutput)} 
+     * method<p>
+     * 
+     * @param in The input stream from which the RDN will be read
+     * @throws IOException If we can't read from the input stream
+     * @throws ClassNotFoundException If we can't create a new RDN
+     */
+    public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
+    {
+        // Read the ATAV number
+        nbAtavs = in.readInt();
+
+        // Read the UPName
+        upName = in.readUTF();
+
+        // Read the normName
+        normName = in.readUTF();
+
+        if ( StringTools.isEmpty( normName ) )
+        {
+            normName = upName;
+        }
+
+        start = in.readInt();
+        length = in.readInt();
+
+        switch ( nbAtavs )
+        {
+            case 0:
+                atav = null;
+                break;
+
+            case 1:
+                atav = ( AttributeTypeAndValue ) in.readObject();
+                atavType = atav.getNormType();
+
+                break;
+
+            default:
+                atavs = new TreeSet<AttributeTypeAndValue>();
+
+                atavTypes = new MultiValueMap();
+
+                for ( int i = 0; i < nbAtavs; i++ )
+                {
+                    AttributeTypeAndValue value = ( AttributeTypeAndValue ) in.readObject();
+                    atavs.add( value );
+                    atavTypes.put( value.getNormType(), value );
+                }
+
+                atav = null;
+                atavType = null;
+
+                break;
         }
     }
-   
-   
-   /**
-    * Compares the specified Object with this Rdn for equality. Returns true if
-    * the given object is also a Rdn and the two Rdns represent the same
-    * attribute type and value mappings. The order of components in
-    * multi-valued Rdns is not significant.
-    *
-    * @param rdn
-    *            Rdn to be compared for equality with this Rdn
-    * @return true if the specified object is equal to this Rdn
-    */
-   public boolean equals( Object rdn )
-   {
-       if ( this == rdn )
-       {
-           return true;
-       }
-
-       if ( !( rdn instanceof Rdn ) )
-       {
-           return false;
-       }
-
-       return compareTo( rdn ) == EQUAL;
-   }
-
-
-   /**
-    * Get the number of Attribute type and value of this Rdn
-    *
-    * @return The number of ATAVs in this Rdn
-    */
-   public int size()
-   {
-       return nbAtavs;
-   }
-
-
-   /**
-    * Unescape the given string according to RFC 2253 If in <string> form, a
-    * LDAP string representation asserted value can be obtained by replacing
-    * (left-to-right, non-recursively) each <pair> appearing in the <string> as
-    * follows: replace <ESC><ESC> with <ESC>; replace <ESC><special> with
-    * <special>; replace <ESC><hexpair> with the octet indicated by the
-    * <hexpair> If in <hexstring> form, a BER representation can be obtained
-    * from converting each <hexpair> of the <hexstring> to the octet indicated
-    * by the <hexpair>
-    *
-    * @param value
-    *            The value to be unescaped
-    * @return Returns a string value as a String, and a binary value as a byte
-    *         array.
-    * @throws IllegalArgumentException -
-    *             When an Illegal value is provided.
-    */
-   public static Object unescapeValue( String value )
-   {
-       if ( StringTools.isEmpty( value ) )
-       {
-           return "";
-       }
-
-       char[] chars = value.toCharArray();
-
-       if ( chars[0] == '#' )
-       {
-           if ( chars.length == 1 )
-           {
-               // The value is only containing a #
-               return StringTools.EMPTY_BYTES;
-           }
-
-           if ( ( chars.length % 2 ) != 1 )
-           {
-               throw new IllegalArgumentException( "This value is not in hex form, we have an odd number of hex chars" );
-           }
-
-           // HexString form
-           byte[] hexValue = new byte[( chars.length - 1 ) / 2];
-           int pos = 0;
-
-           for ( int i = 1; i < chars.length; i += 2 )
-           {
-               if ( StringTools.isHex( chars, i ) && StringTools.isHex( chars, i + 1 ) )
-               {
-                   hexValue[pos++] = StringTools.getHexValue( chars[i], chars[i + 1] );
-               }
-               else
-               {
-                   throw new IllegalArgumentException( "This value is not in hex form" );
-               }
-           }
-
-           return hexValue;
-       }
-       else
-       {
-           boolean escaped = false;
-           boolean isHex = false;
-           byte pair = -1;
-           int pos = 0;
-
-           byte[] bytes = new byte[chars.length * 6];
-
-           for ( int i = 0; i < chars.length; i++ )
-           {
-               if ( escaped )
-               {
-                   escaped = false;
-
-                   switch ( chars[i] )
-                   {
-                       case '\\':
-                       case '"':
-                       case '+':
-                       case ',':
-                       case ';':
-                       case '<':
-                       case '>':
-                       case '#':
-                       case '=':
-                       case ' ':
-                           bytes[pos++] = ( byte ) chars[i];
-                           break;
-
-                       default:
-                           if ( StringTools.isHex( chars, i ) )
-                           {
-                               isHex = true;
-                               pair = ( (byte)( StringTools.getHexValue( chars[i] ) << 4 ) );
-                           }
-                       
-                           break;
-                   }
-               }
-               else
-               {
-                   if ( isHex )
-                   {
-                       if ( StringTools.isHex( chars, i ) )
-                       {
-                           pair += StringTools.getHexValue( chars[i] );
-                           bytes[pos++] = pair;
-                       }
-                   }
-                   else
-                   {
-                       switch ( chars[i] )
-                       {
-                           case '\\':
-                               escaped = true;
-                               break;
-
-                           // We must not have a special char
-                           // Specials are : '"', '+', ',', ';', '<', '>', ' ',
-                           // '#' and '='
-                           case '"':
-                           case '+':
-                           case ',':
-                           case ';':
-                           case '<':
-                           case '>':
-                           case '#':
-                               if ( i != 0)
-                               {
-                                   // '#' are allowed if not in first position
-                                   bytes[pos++] = '#';
-                                   break;
-                               }
-                           case '=':
-                               throw new IllegalArgumentException( "Unescaped special characters are not allowed" );
-
-                           case ' ':
-                               if ( ( i == 0 ) || ( i == chars.length - 1) )
-                               {
-                                   throw new IllegalArgumentException( "Unescaped special characters are not allowed" );
-                               }
-                               else
-                               {
-                                   bytes[pos++] = ' ';
-                                   break;
-                               }
-
-                           default:
-                               if ( ( chars[i] >= 0 ) && ( chars[i] < 128 ) )
-                               {
-                                   bytes[pos++] = (byte)chars[i];
-                               }
-                               else
-                               {
-                                   byte[] result = StringTools.charToBytes( chars[i] );
-                                   System.arraycopy( result, 0, bytes, pos, result.length );
-                                   pos += result.length;
-                               }
-                               
-                               break;
-                       }
-                   }
-               }
-           }
-
-           return StringTools.utf8ToString( bytes, pos );
-       }
-   }
-
-
-   /**
-    * Transform a value in a String, accordingly to RFC 2253
-    *
-    * @param value The attribute value to be escaped
-    * @return The escaped string value.
-    */
-   public static String escapeValue( String value )
-   {
-       if ( StringTools.isEmpty( value ) )
-       {
-           return "";
-       }
-       
-       char[] chars = value.toCharArray();
-       char[] newChars = new char[chars.length * 3];
-       int pos = 0;
-
-       for ( int i = 0; i < chars.length; i++ )
-       {
-           switch ( chars[i] )
-           {
-               case ' ':
-                   if ( ( i > 0 ) && ( i < chars.length - 1 ) )
-                   {
-                       newChars[pos++] = chars[i];
-                   }
-                   else
-                   {
-                       newChars[pos++] = '\\';
-                       newChars[pos++] = chars[i];
-                   }
-                   
-                   break;
-                   
-               case '#':
-                   if ( i != 0 )
-                   {
-                       newChars[pos++] = chars[i];
-                   }
-                   else
-                   {
-                       newChars[pos++] = '\\';
-                       newChars[pos++] = chars[i];
-                   }
-                   
-                   break;
-                   
-               case '"':
-               case '+':
-               case ',':
-               case ';':
-               case '=':
-               case '<':
-               case '>':
-               case '\\':
-                   newChars[pos++] = '\\';
-                   newChars[pos++] = chars[i];
-                   break;
-
-               case 0x7F:
-                   newChars[pos++] = '\\';
-                   newChars[pos++] = '7';
-                   newChars[pos++] = 'F';
-                   break;
-
-               case 0x00:
-               case 0x01:
-               case 0x02:
-               case 0x03:
-               case 0x04:
-               case 0x05:
-               case 0x06:
-               case 0x07:
-               case 0x08:
-               case 0x09:
-               case 0x0A:
-               case 0x0B:
-               case 0x0C:
-               case 0x0D:
-               case 0x0E:
-               case 0x0F:
-                   newChars[pos++] = '\\';
-                   newChars[pos++] = '0';
-                   newChars[pos++] = StringTools.dumpHex( ( byte ) ( chars[i] & 0x0F ) );
-                   break;
-
-               case 0x10:
-               case 0x11:
-               case 0x12:
-               case 0x13:
-               case 0x14:
-               case 0x15:
-               case 0x16:
-               case 0x17:
-               case 0x18:
-               case 0x19:
-               case 0x1A:
-               case 0x1B:
-               case 0x1C:
-               case 0x1D:
-               case 0x1E:
-               case 0x1F:
-                   newChars[pos++] = '\\';
-                   newChars[pos++] = '1';
-                   newChars[pos++] = StringTools.dumpHex( ( byte ) ( chars[i] & 0x0F ) );
-                   break;
-
-               default:
-                   newChars[pos++] = chars[i];
-                   break;
-
-           }
-       }
-
-       return new String( newChars, 0, pos );
-   }
-   
-   /**
-    * Transform a value in a String, accordingly to RFC 2253
-    *
-    * @param attrValue
-    *            The attribute value to be escaped
-    * @return The escaped string value.
-    */
-   public static String escapeValue( byte[] attrValue )
-   {
-       if ( StringTools.isEmpty( attrValue ) )
-       {
-           return "";
-       }
-
-       String value = StringTools.utf8ToString( attrValue );
-       
-       return escapeValue( value );
-   }
-
-   /**
-     * Gets the hashcode of this rdn.
-     *
-     * @see java.lang.Object#hashCode()
-     * @return the instance's hash code 
-     */
-   public int hashCode()
-   {
-       int result = 37;
-
-       switch ( nbAtavs )
-       {
-           case 0:
-               // An empty RDN
-               break;
-
-           case 1:
-               // We have a single AttributeTypeAndValue
-               result = result * 17 + atav.hashCode();
-               break;
-
-           default:
-               // We have more than one AttributeTypeAndValue
-
-               for ( AttributeTypeAndValue ata:atavs )
-               {
-                   result = result * 17 + ata.hashCode();
-               }
-           
-               break;
-       }
-
-       return result;
-   }
-
-
-   /**
-    * @see Externalizable#readExternal(ObjectInput)<p>
-    * 
-    * A RDN is composed of on to many ATAVs (AttributeType And Value).
-    * We should write all those ATAVs sequencially, following the 
-    * structure :
-    * 
-    * <li>nbAtavs</li> The number of ATAVs to write. Can't be 0.
-    * <li>upName</li> The User provided RDN
-    * <li>normName</li> The normalized RDN. It can be empty if the normalized
-    * name equals the upName.
-    * <li>atavs</li>
-    * <p>
-    * For each ATAV :<p>
-    * <li>start</li> The position of this ATAV in the upName string
-    * <li>length</li> The ATAV user provided length
-    * <li>Call the ATAV write method</li> The ATAV itself
-    * 
-    * @param out The stream into which the serialized RDN will be put
-    * @throws IOException If the stream can't be written
-    */
-   public void writeExternal( ObjectOutput out ) throws IOException
-   {
-       out.writeInt( nbAtavs );
-       out.writeUTF( upName );
-       
-       if ( upName.equals( normName ) )
-       {
-           out.writeUTF( "" );
-       }
-       else
-       {
-           out.writeUTF( normName );
-       }
-
-       out.writeInt( start );
-       out.writeInt( length );
-
-       switch ( nbAtavs )
-       {
-           case 0 :
-               break;
-
-           case 1 :
-               out.writeObject( atav );
-               break;
-               
-           default :
-               for ( AttributeTypeAndValue value:atavs )
-               {
-                   out.writeObject( value );
-               }
-           
-               break;
-       }
-   }
-
-
-   /**
-    * @see Externalizable#readExternal(ObjectInput)
-    * 
-    * We read back the data to create a new RDB. The structure 
-    * read is exposed in the {@link Rdn#writeExternal(ObjectOutput)} 
-    * method<p>
-    * 
-    * @param in The input stream from which the RDN will be read
-    * @throws IOException If we can't read from the input stream
-    * @throws ClassNotFoundException If we can't create a new RDN
-    */
-   public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
-   {
-       // Read the ATAV number
-       nbAtavs = in.readInt();
-       
-       // Read the UPName
-       upName = in.readUTF();
-       
-       // Read the normName
-       normName = in.readUTF();
-       
-       if ( StringTools.isEmpty( normName ) )
-       {
-           normName = upName;
-       }
-       
-       start = in.readInt();
-       length = in.readInt();
-
-       switch ( nbAtavs )
-       {
-           case 0 :
-               atav = null;
-               break;
-               
-           case 1 :
-               atav = (AttributeTypeAndValue)in.readObject();
-               atavType = atav.getNormType();
-               
-               break;
-               
-           default :
-               atavs = new TreeSet<AttributeTypeAndValue>();
-
-               atavTypes = new MultiValueMap();
-
-               for ( int i = 0; i < nbAtavs; i++  )
-               {
-                   AttributeTypeAndValue value = (AttributeTypeAndValue)in.readObject();
-                   atavs.add( value );
-                   atavTypes.put( value.getNormType(), value );
-               }
-           
-               atav = null;
-               atavType = null;
-
-               break;
-       }
-   }
 }