001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.shared.ldap.name;
021    
022    
023    import java.io.Externalizable;
024    import java.io.IOException;
025    import java.io.ObjectInput;
026    import java.io.ObjectOutput;
027    import java.util.Collection;
028    import java.util.Iterator;
029    import java.util.Map;
030    import java.util.Set;
031    import java.util.TreeSet;
032    
033    import javax.naming.InvalidNameException;
034    import javax.naming.NamingException;
035    
036    import org.apache.commons.collections.MultiMap;
037    import org.apache.commons.collections.map.MultiValueMap;
038    import org.apache.directory.shared.i18n.I18n;
039    import org.apache.directory.shared.ldap.entry.Value;
040    import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
041    import org.apache.directory.shared.ldap.schema.normalizers.OidNormalizer;
042    import org.apache.directory.shared.ldap.util.StringTools;
043    import org.slf4j.Logger;
044    import org.slf4j.LoggerFactory;
045    
046    
047    /**
048     * This class store the name-component part or the following BNF grammar (as of
049     * RFC2253, par. 3, and RFC1779, fig. 1) : <br> - &lt;name-component&gt; ::=
050     * &lt;attributeType&gt; &lt;spaces&gt; '=' &lt;spaces&gt;
051     * &lt;attributeValue&gt; &lt;attributeTypeAndValues&gt; <br> -
052     * &lt;attributeTypeAndValues&gt; ::= &lt;spaces&gt; '+' &lt;spaces&gt;
053     * &lt;attributeType&gt; &lt;spaces&gt; '=' &lt;spaces&gt;
054     * &lt;attributeValue&gt; &lt;attributeTypeAndValues&gt; | e <br> -
055     * &lt;attributeType&gt; ::= [a-zA-Z] &lt;keychars&gt; | &lt;oidPrefix&gt; [0-9]
056     * &lt;digits&gt; &lt;oids&gt; | [0-9] &lt;digits&gt; &lt;oids&gt; <br> -
057     * &lt;keychars&gt; ::= [a-zA-Z] &lt;keychars&gt; | [0-9] &lt;keychars&gt; | '-'
058     * &lt;keychars&gt; | e <br> - &lt;oidPrefix&gt; ::= 'OID.' | 'oid.' | e <br> -
059     * &lt;oids&gt; ::= '.' [0-9] &lt;digits&gt; &lt;oids&gt; | e <br> -
060     * &lt;attributeValue&gt; ::= &lt;pairs-or-strings&gt; | '#' &lt;hexstring&gt;
061     * |'"' &lt;quotechar-or-pairs&gt; '"' <br> - &lt;pairs-or-strings&gt; ::= '\'
062     * &lt;pairchar&gt; &lt;pairs-or-strings&gt; | &lt;stringchar&gt;
063     * &lt;pairs-or-strings&gt; | e <br> - &lt;quotechar-or-pairs&gt; ::=
064     * &lt;quotechar&gt; &lt;quotechar-or-pairs&gt; | '\' &lt;pairchar&gt;
065     * &lt;quotechar-or-pairs&gt; | e <br> - &lt;pairchar&gt; ::= ',' | '=' | '+' |
066     * '&lt;' | '&gt;' | '#' | ';' | '\' | '"' | [0-9a-fA-F] [0-9a-fA-F] <br> -
067     * &lt;hexstring&gt; ::= [0-9a-fA-F] [0-9a-fA-F] &lt;hexpairs&gt; <br> -
068     * &lt;hexpairs&gt; ::= [0-9a-fA-F] [0-9a-fA-F] &lt;hexpairs&gt; | e <br> -
069     * &lt;digits&gt; ::= [0-9] &lt;digits&gt; | e <br> - &lt;stringchar&gt; ::=
070     * [0x00-0xFF] - [,=+&lt;&gt;#;\"\n\r] <br> - &lt;quotechar&gt; ::= [0x00-0xFF] -
071     * [\"] <br> - &lt;separator&gt; ::= ',' | ';' <br> - &lt;spaces&gt; ::= ' '
072     * &lt;spaces&gt; | e <br>
073     * <br>
074     * A RDN is a part of a DN. It can be composed of many types, as in the RDN
075     * following RDN :<br>
076     * ou=value + cn=other value<br>
077     * <br>
078     * or <br>
079     * ou=value + ou=another value<br>
080     * <br>
081     * In this case, we have to store an 'ou' and a 'cn' in the RDN.<br>
082     * <br>
083     * The types are case insensitive. <br>
084     * Spaces before and after types and values are not stored.<br>
085     * Spaces before and after '+' are not stored.<br>
086     * <br>
087     * Thus, we can consider that the following RDNs are equals :<br>
088     * <br>
089     * 'ou=test 1'<br> ' ou=test 1'<br>
090     * 'ou =test 1'<br>
091     * 'ou= test 1'<br>
092     * 'ou=test 1 '<br> ' ou = test 1 '<br>
093     * <br>
094     * So are the following :<br>
095     * <br>
096     * 'ou=test 1+cn=test 2'<br>
097     * 'ou = test 1 + cn = test 2'<br> ' ou =test 1+ cn =test 2 ' <br>
098     * 'cn = test 2 +ou = test 1'<br>
099     * <br>
100     * but the following are not equal :<br>
101     * 'ou=test 1' <br>
102     * 'ou=test 1'<br>
103     * because we have more than one spaces inside the value.<br>
104     * <br>
105     * The Rdn is composed of one or more AttributeTypeAndValue (atav) Those atavs
106     * are ordered in the alphabetical natural order : a < b < c ... < z As the type
107     * are not case sensitive, we can say that a = A
108     *
109     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
110     * @version $Rev: 918756 $, $Date: 2010-03-04 00:05:29 +0100 (Jeu, 04 mar 2010) $
111     */
112    public class RDN implements Cloneable, Comparable, Externalizable, Iterable<AVA>
113    {
114        /** The LoggerFactory used by this class */
115        protected static final Logger LOG = LoggerFactory.getLogger( RDN.class );
116    
117        /**
118        * Declares the Serial Version Uid.
119        *
120        * @see <a
121        *      href="http://c2.com/cgi/wiki?AlwaysDeclareSerialVersionUid">Always
122        *      Declare Serial Version Uid</a>
123        */
124        private static final long serialVersionUID = 1L;
125    
126        /** The User Provided RDN */
127        private String upName = null;
128    
129        /** The normalized RDN */
130        private String normName = null;
131    
132        /** The starting position of this RDN in the given string from which
133         * we have extracted the upName */
134        private int start;
135    
136        /** The length of this RDN upName */
137        private int length;
138    
139        /**
140         * Stores all couple type = value. We may have more than one type, if the
141         * '+' character appears in the AttributeTypeAndValue. This is a TreeSet,
142         * because we want the ATAVs to be sorted. An atav may contain more than one
143         * value. In this case, the values are String stored in a List.
144         */
145        private Set<AVA> atavs = null;
146    
147        /**
148         * We also keep a set of types, in order to use manipulations. A type is
149         * connected with the atav it represents.
150         * 
151         * Note : there is no Generic available classes in commons-collection...
152         */
153        private MultiMap atavTypes = new MultiValueMap();
154    
155        /**
156         * We keep the type for a single valued RDN, to avoid the creation of an HashMap
157         */
158        private String atavType = null;
159    
160        /**
161         * A simple AttributeTypeAndValue is used to store the Rdn for the simple
162         * case where we only have a single type=value. This will be 99.99% the
163         * case. This avoids the creation of a HashMap.
164         */
165        protected AVA atav = null;
166    
167        /**
168         * The number of atavs. We store this number here to avoid complex
169         * manipulation of atav and atavs
170         */
171        private int nbAtavs = 0;
172    
173        /** CompareTo() results */
174        public static final int UNDEFINED = Integer.MAX_VALUE;
175    
176        /** Constant used in comparisons */
177        public static final int SUPERIOR = 1;
178    
179        /** Constant used in comparisons */
180        public static final int INFERIOR = -1;
181    
182        /** Constant used in comparisons */
183        public static final int EQUAL = 0;
184    
185    
186        /**
187         * A empty constructor.
188         */
189        public RDN()
190        {
191            // Don't waste space... This is not so often we have multiple
192            // name-components in a RDN... So we won't initialize the Map and the
193            // treeSet.
194            upName = "";
195            normName = "";
196        }
197    
198    
199        /**
200         * A constructor that parse a String representing a RDN.
201         *
202         * @param rdn The String containing the RDN to parse
203         * @throws InvalidNameException If the RDN is invalid
204         */
205        public RDN( String rdn ) throws InvalidNameException
206        {
207            start = 0;
208    
209            if ( StringTools.isNotEmpty( rdn ) )
210            {
211                // Parse the string. The Rdn will be updated.
212                RdnParser.parse( rdn, this );
213    
214                // create the internal normalized form
215                // and store the user provided form
216                normalize();
217                upName = rdn;
218                length = rdn.length();
219            }
220            else
221            {
222                upName = "";
223                normName = "";
224                length = 0;
225            }
226        }
227    
228    
229        /**
230         * A constructor that constructs a RDN from a type and a value. Constructs
231         * an Rdn from the given attribute type and value. The string attribute
232         * values are not interpreted as RFC 2253 formatted RDN strings. That is,
233         * the values are used literally (not parsed) and assumed to be un-escaped.
234         *
235         * @param upType The user provided type of the RDN
236         * @param upValue The user provided value of the RDN
237         * @param normType The normalized provided type of the RDN
238         * @param normValue The normalized provided value of the RDN
239         * @throws InvalidNameException If the RDN is invalid
240         */
241        public RDN( String upType, String normType, String upValue, String normValue ) throws InvalidNameException
242        {
243            addAttributeTypeAndValue( upType, normType, new ClientStringValue( upValue ), new ClientStringValue( normValue ) );
244    
245            upName = upType + '=' + upValue;
246            start = 0;
247            length = upName.length();
248            // create the internal normalized form
249            normalize();
250        }
251    
252    
253        /**
254         * A constructor that constructs a RDN from a type and a value. Constructs
255         * an Rdn from the given attribute type and value. The string attribute
256         * values are not interpreted as RFC 2253 formatted RDN strings. That is,
257         * the values are used literally (not parsed) and assumed to be un-escaped.
258         *
259         * @param upType The user provided type of the RDN
260         * @param upValue The user provided value of the RDN
261         * @throws InvalidNameException If the RDN is invalid
262         */
263        public RDN( String upType, String upValue ) throws InvalidNameException
264        {
265            addAttributeTypeAndValue( upType, upType, new ClientStringValue( upValue ), new ClientStringValue( upValue ) );
266    
267            upName = upType + '=' + upValue;
268            start = 0;
269            length = upName.length();
270            // create the internal normalized form
271            normalize();
272        }
273    
274    
275        /**
276         * A constructor that constructs a RDN from a type, a position and a length.
277         *
278         * @param start The starting point for this RDN in the user provided DN
279         * @param length The RDN's length
280         * @param upName The user provided name
281         * @param normName the normalized name
282         */
283        RDN( int start, int length, String upName, String normName )
284        {
285            this.start = 0;
286            this.length = length;
287            this.upName = upName;
288            this.normName = normName;
289        }
290    
291    
292        /**
293         * Constructs an Rdn from the given rdn. The contents of the rdn are simply
294         * copied into the newly created
295         *
296         * @param rdn
297         *            The non-null Rdn to be copied.
298         */
299        public RDN( RDN rdn )
300        {
301            nbAtavs = rdn.getNbAtavs();
302            this.normName = rdn.normName;
303            this.upName = rdn.getUpName();
304            this.start = rdn.start;
305            this.length = rdn.length;
306    
307            switch ( rdn.getNbAtavs() )
308            {
309                case 0:
310                    return;
311    
312                case 1:
313                    this.atav = ( AVA ) rdn.atav.clone();
314                    return;
315    
316                default:
317                    // We must duplicate the treeSet and the hashMap
318                    atavs = new TreeSet<AVA>();
319                    atavTypes = new MultiValueMap();
320    
321                    for ( AVA currentAtav : rdn.atavs )
322                    {
323                        atavs.add( ( AVA ) currentAtav.clone() );
324                        atavTypes.put( currentAtav.getNormType(), currentAtav );
325                    }
326    
327                    return;
328            }
329        }
330    
331    
332        /**
333         * Transform the external representation of the current RDN to an internal
334         * normalized form where : 
335         * - types are trimmed and lower cased 
336         * - values are trimmed and lower cased
337         */
338        // WARNING : The protection level is left unspecified on purpose.
339        // We need this method to be visible from the DnParser class, but not
340        // from outside this package.
341        /* Unspecified protection */void normalize()
342        {
343            switch ( nbAtavs )
344            {
345                case 0:
346                    // An empty RDN
347                    normName = "";
348                    break;
349    
350                case 1:
351                    // We have a single AttributeTypeAndValue
352                    // We will trim and lowercase type and value.
353                    if ( !atav.getNormValue().isBinary() )
354                    {
355                        normName = atav.getNormName();
356                    }
357                    else
358                    {
359                        normName = atav.getNormType() + "=#" + StringTools.dumpHexPairs( atav.getNormValue().getBytes() );
360                    }
361    
362                    break;
363    
364                default:
365                    // We have more than one AttributeTypeAndValue
366                    StringBuffer sb = new StringBuffer();
367    
368                    boolean isFirst = true;
369    
370                    for ( AVA ata : atavs )
371                    {
372                        if ( isFirst )
373                        {
374                            isFirst = false;
375                        }
376                        else
377                        {
378                            sb.append( '+' );
379                        }
380    
381                        sb.append( ata.normalize() );
382                    }
383    
384                    normName = sb.toString();
385                    break;
386            }
387        }
388        
389        
390        /**
391         * Transform a RDN by changing the value to its OID counterpart and
392         * normalizing the value accordingly to its type.
393         *
394         * @param rdn The RDN to modify.
395         * @param oidsMap The map of all existing oids and normalizer.
396         * @throws InvalidNameException If the RDN is invalid.
397         * @throws NamingException If something went wrong.
398         */
399        public RDN normalize( Map<String, OidNormalizer> oidsMap ) throws InvalidNameException, NamingException
400        {
401            String upName = getUpName();
402            DN.rdnOidToName( this, oidsMap );
403            normalize();
404            this.upName = upName;
405    
406            
407            return this;
408        }
409    
410    
411    
412        /**
413         * Add a AttributeTypeAndValue to the current RDN
414         *
415         * @param upType The user provided type of the added RDN.
416         * @param type The normalized provided type of the added RDN.
417         * @param upValue The user provided value of the added RDN
418         * @param value The normalized provided value of the added RDN
419         * @throws InvalidNameException
420         *             If the RDN is invalid
421         */
422        // WARNING : The protection level is left unspecified intentionally.
423        // We need this method to be visible from the DnParser class, but not
424        // from outside this package.
425        /* Unspecified protection */void addAttributeTypeAndValue( String upType, String type, Value<?> upValue,
426            Value<?> value ) throws InvalidNameException
427        {
428            // First, let's normalize the type
429            String normalizedType = StringTools.lowerCaseAscii( type );
430            Value<?> normalizedValue = value;
431    
432            switch ( nbAtavs )
433            {
434                case 0:
435                    // This is the first AttributeTypeAndValue. Just stores it.
436                    atav = new AVA( upType, type, upValue, normalizedValue );
437                    nbAtavs = 1;
438                    atavType = normalizedType;
439                    return;
440    
441                case 1:
442                    // We already have an atav. We have to put it in the HashMap
443                    // before adding a new one.
444                    // First, create the HashMap,
445                    atavs = new TreeSet<AVA>();
446    
447                    // and store the existing AttributeTypeAndValue into it.
448                    atavs.add( atav );
449                    atavTypes = new MultiValueMap();
450                    atavTypes.put( atavType, atav );
451    
452                    atav = null;
453    
454                    // Now, fall down to the commmon case
455                    // NO BREAK !!!
456    
457                default:
458                    // add a new AttributeTypeAndValue
459                    AVA newAtav = new AVA( upType, type, upValue, normalizedValue );
460                    atavs.add( newAtav );
461                    atavTypes.put( normalizedType, newAtav );
462    
463                    nbAtavs++;
464                    break;
465    
466            }
467        }
468    
469    
470        /**
471         * Add a AttributeTypeAndValue to the current RDN
472         *
473         * @param value The added AttributeTypeAndValue
474         */
475        // WARNING : The protection level is left unspecified intentionnaly.
476        // We need this method to be visible from the DnParser class, but not
477        // from outside this package.
478        /* Unspecified protection */void addAttributeTypeAndValue( AVA value )
479        {
480            String normalizedType = value.getNormType();
481    
482            switch ( nbAtavs )
483            {
484                case 0:
485                    // This is the first AttributeTypeAndValue. Just stores it.
486                    atav = value;
487                    nbAtavs = 1;
488                    atavType = normalizedType;
489                    return;
490    
491                case 1:
492                    // We already have an atav. We have to put it in the HashMap
493                    // before adding a new one.
494                    // First, create the HashMap,
495                    atavs = new TreeSet<AVA>();
496    
497                    // and store the existing AttributeTypeAndValue into it.
498                    atavs.add( atav );
499                    atavTypes = new MultiValueMap();
500                    atavTypes.put( atavType, atav );
501    
502                    this.atav = null;
503    
504                    // Now, fall down to the commmon case
505                    // NO BREAK !!!
506    
507                default:
508                    // add a new AttributeTypeAndValue
509                    atavs.add( value );
510                    atavTypes.put( normalizedType, value );
511    
512                    nbAtavs++;
513                    break;
514    
515            }
516        }
517    
518    
519        /**
520         * Clear the RDN, removing all the AttributeTypeAndValues.
521         */
522        public void clear()
523        {
524            atav = null;
525            atavs = null;
526            atavType = null;
527            atavTypes.clear();
528            nbAtavs = 0;
529            normName = "";
530            upName = "";
531            start = -1;
532            length = 0;
533        }
534    
535    
536        /**
537         * Get the Value of the AttributeTypeAndValue which type is given as an
538         * argument.
539         *
540         * @param type
541         *            The type of the NameArgument
542         * @return The Value to be returned, or null if none found.
543         * @throws InvalidNameException 
544         */
545        public Object getValue( String type ) throws InvalidNameException
546        {
547            // First, let's normalize the type
548            String normalizedType = StringTools.lowerCaseAscii( StringTools.trim( type ) );
549    
550            switch ( nbAtavs )
551            {
552                case 0:
553                    return "";
554    
555                case 1:
556                    if ( StringTools.equals( atav.getNormType(), normalizedType ) )
557                    {
558                        return atav.getNormValue().get();
559                    }
560    
561                    return "";
562    
563                default:
564                    if ( atavTypes.containsKey( normalizedType ) )
565                    {
566                        Collection<AVA> atavList = ( Collection<AVA> ) atavTypes.get( normalizedType );
567                        StringBuffer sb = new StringBuffer();
568                        boolean isFirst = true;
569    
570                        for ( AVA elem : atavList )
571                        {
572                            if ( isFirst )
573                            {
574                                isFirst = false;
575                            }
576                            else
577                            {
578                                sb.append( ',' );
579                            }
580    
581                            sb.append( elem.getNormValue() );
582                        }
583    
584                        return sb.toString();
585                    }
586    
587                    return "";
588            }
589        }
590    
591    
592        /** 
593         * Get the start position
594         *
595         * @return The start position in the DN
596         */
597        public int getStart()
598        {
599            return start;
600        }
601    
602    
603        /**
604         * Get the Rdn length
605         *
606         * @return The Rdn length
607         */
608        public int getLength()
609        {
610            return length;
611        }
612    
613    
614        /**
615         * Get the AttributeTypeAndValue which type is given as an argument. If we
616         * have more than one value associated with the type, we will return only
617         * the first one.
618         *
619         * @param type
620         *            The type of the NameArgument to be returned
621         * @return The AttributeTypeAndValue, of null if none is found.
622         */
623        public AVA getAttributeTypeAndValue( String type )
624        {
625            // First, let's normalize the type
626            String normalizedType = StringTools.lowerCaseAscii( StringTools.trim( type ) );
627    
628            switch ( nbAtavs )
629            {
630                case 0:
631                    return null;
632    
633                case 1:
634                    if ( atav.getNormType().equals( normalizedType ) )
635                    {
636                        return atav;
637                    }
638    
639                    return null;
640    
641                default:
642                    if ( atavTypes.containsKey( normalizedType ) )
643                    {
644                        Collection<AVA> atavList = ( Collection<AVA> ) atavTypes.get( normalizedType );
645                        return atavList.iterator().next();
646                    }
647    
648                    return null;
649            }
650        }
651    
652    
653        /**
654         * Retrieves the components of this RDN as an iterator of AttributeTypeAndValue. 
655         * The effect on the iterator of updates to this RDN is undefined. If the
656         * RDN has zero components, an empty (non-null) iterator is returned.
657         *
658         * @return an iterator of the components of this RDN, each an AttributeTypeAndValue
659         */
660        public Iterator<AVA> iterator()
661        {
662            if ( nbAtavs == 1 || nbAtavs == 0 )
663            {
664                return new Iterator<AVA>()
665                {
666                    private boolean hasMoreElement = nbAtavs == 1;
667    
668    
669                    public boolean hasNext()
670                    {
671                        return hasMoreElement;
672                    }
673    
674    
675                    public AVA next()
676                    {
677                        AVA obj = atav;
678                        hasMoreElement = false;
679                        return obj;
680                    }
681    
682    
683                    public void remove()
684                    {
685                        // nothing to do
686                    }
687                };
688            }
689            else
690            {
691                return atavs.iterator();
692            }
693        }
694    
695    
696        /**
697         * Clone the Rdn
698         * 
699         * @return A clone of the current RDN
700         */
701        public Object clone()
702        {
703            try
704            {
705                RDN rdn = ( RDN ) super.clone();
706    
707                // The AttributeTypeAndValue is immutable. We won't clone it
708    
709                switch ( rdn.getNbAtavs() )
710                {
711                    case 0:
712                        break;
713    
714                    case 1:
715                        rdn.atav = ( AVA ) this.atav.clone();
716                        rdn.atavTypes = atavTypes;
717                        break;
718    
719                    default:
720                        // We must duplicate the treeSet and the hashMap
721                        rdn.atavTypes = new MultiValueMap();
722                        rdn.atavs = new TreeSet<AVA>();
723    
724                        for ( AVA currentAtav : this.atavs )
725                        {
726                            rdn.atavs.add( ( AVA ) currentAtav.clone() );
727                            rdn.atavTypes.put( currentAtav.getNormType(), currentAtav );
728                        }
729    
730                        break;
731                }
732    
733                return rdn;
734            }
735            catch ( CloneNotSupportedException cnse )
736            {
737                throw new Error( "Assertion failure" );
738            }
739        }
740    
741    
742        /**
743         * Compares two RDNs. They are equals if : 
744         * <li>their have the same number of NC (AttributeTypeAndValue) 
745         * <li>each ATAVs are equals 
746         * <li>comparison of type are done case insensitive 
747         * <li>each value is equal, case sensitive 
748         * <li>Order of ATAV is not important If the RDNs are not equals, a positive number is
749         * returned if the first RDN is greater, negative otherwise
750         *
751         * @param object
752         * @return 0 if both rdn are equals. -1 if the current RDN is inferior, 1 if
753         *         the current Rdn is superior, UNDEFINED otherwise.
754         */
755        public int compareTo( Object object )
756        {
757            if ( object == null )
758            {
759                return SUPERIOR;
760            }
761    
762            if ( object instanceof RDN )
763            {
764                RDN rdn = ( RDN ) object;
765    
766                if ( rdn.nbAtavs != nbAtavs )
767                {
768                    // We don't have the same number of ATAVs. The Rdn which
769                    // has the higher number of Atav is the one which is
770                    // superior
771                    return nbAtavs - rdn.nbAtavs;
772                }
773    
774                switch ( nbAtavs )
775                {
776                    case 0:
777                        return EQUAL;
778    
779                    case 1:
780                        return atav.compareTo( rdn.atav );
781    
782                    default:
783                        // We have more than one value. We will
784                        // go through all of them.
785    
786                        // the types are already normalized and sorted in the atavs TreeSet
787                        // so we could compare the 1st with the 1st, then the 2nd with the 2nd, etc.
788                        Iterator<AVA> localIterator = atavs.iterator();
789                        Iterator<AVA> paramIterator = rdn.atavs.iterator();
790    
791                        while ( localIterator.hasNext() || paramIterator.hasNext() )
792                        {
793                            if ( !localIterator.hasNext() )
794                            {
795                                return SUPERIOR;
796                            }
797                            if ( !paramIterator.hasNext() )
798                            {
799                                return INFERIOR;
800                            }
801    
802                            AVA localAtav = localIterator.next();
803                            AVA paramAtav = paramIterator.next();
804                            int result = localAtav.compareTo( paramAtav );
805                            if ( result != EQUAL )
806                            {
807                                return result;
808                            }
809                        }
810    
811                        return EQUAL;
812                }
813            }
814            else
815            {
816                return UNDEFINED;
817            }
818        }
819    
820    
821        /**
822         * @return a String representation of the RDN
823         */
824        public String toString()
825        {
826            return normName == null ? "" : normName;
827        }
828    
829    
830        /**
831         * @return the user provided name
832         */
833        public String getUpName()
834        {
835            return upName;
836        }
837    
838    
839        /**
840         * @return The normalized name
841         */
842        public String getNormName()
843        {
844            return normName == null ? "" : normName;
845        }
846    
847    
848        /**
849         * Set the User Provided Name
850         * @param upName the User Provided dame 
851         */
852        public void setUpName( String upName )
853        {
854            this.upName = upName;
855        }
856    
857    
858        /**
859         * @return Returns the nbAtavs.
860         */
861        public int getNbAtavs()
862        {
863            return nbAtavs;
864        }
865    
866    
867        /**
868         * Return the unique AttributeTypeAndValue, or the first one of we have more
869         * than one
870         *
871         * @return The first AttributeTypeAndValue of this RDN
872         */
873        public AVA getAtav()
874        {
875            switch ( nbAtavs )
876            {
877                case 0:
878                    return null;
879    
880                case 1:
881                    return atav;
882    
883                default:
884                    return ( ( TreeSet<AVA> ) atavs ).first();
885            }
886        }
887    
888    
889        /**
890         * Return the user provided type, or the first one of we have more than one (the lowest)
891         *
892         * @return The first user provided type of this RDN
893         */
894        public String getUpType()
895        {
896            switch ( nbAtavs )
897            {
898                case 0:
899                    return null;
900    
901                case 1:
902                    return atav.getUpType();
903    
904                default:
905                    return ( ( TreeSet<AVA> ) atavs ).first().getUpType();
906            }
907        }
908    
909    
910        /**
911         * Return the normalized type, or the first one of we have more than one (the lowest)
912         *
913         * @return The first normalized type of this RDN
914         */
915        public String getNormType()
916        {
917            switch ( nbAtavs )
918            {
919                case 0:
920                    return null;
921    
922                case 1:
923                    return atav.getNormType();
924    
925                default:
926                    return ( ( TreeSet<AVA> ) atavs ).first().getNormType();
927            }
928        }
929    
930    
931        /**
932         * Return the User Provided value
933         * 
934         * @return The first User provided value of this RDN
935         */
936        public String getUpValue()
937        {
938            switch ( nbAtavs )
939            {
940                case 0:
941                    return null;
942    
943                case 1:
944                    return atav.getUpValue().getString();
945    
946                default:
947                    return ( ( TreeSet<AVA> ) atavs ).first().getUpValue().getString();
948            }
949        }
950    
951    
952        /**
953         * Return the normalized value, or the first one of we have more than one (the lowest)
954         *
955         * @return The first normalized value of this RDN
956         */
957        public String getNormValue()
958        {
959            switch ( nbAtavs )
960            {
961                case 0:
962                    return null;
963    
964                case 1:
965                    return atav.getNormValue().getString();
966    
967                default:
968                    return ( ( TreeSet<AVA> ) atavs ).first().getNormValue().getString();
969            }
970        }
971    
972    
973        /**
974         * Compares the specified Object with this Rdn for equality. Returns true if
975         * the given object is also a Rdn and the two Rdns represent the same
976         * attribute type and value mappings. The order of components in
977         * multi-valued Rdns is not significant.
978         *
979         * @param rdn
980         *            Rdn to be compared for equality with this Rdn
981         * @return true if the specified object is equal to this Rdn
982         */
983        public boolean equals( Object rdn )
984        {
985            if ( this == rdn )
986            {
987                return true;
988            }
989    
990            if ( !( rdn instanceof RDN ) )
991            {
992                return false;
993            }
994    
995            return compareTo( rdn ) == EQUAL;
996        }
997    
998    
999        /**
1000         * Get the number of Attribute type and value of this Rdn
1001         *
1002         * @return The number of ATAVs in this Rdn
1003         */
1004        public int size()
1005        {
1006            return nbAtavs;
1007        }
1008    
1009    
1010        /**
1011         * Unescape the given string according to RFC 2253 If in <string> form, a
1012         * LDAP string representation asserted value can be obtained by replacing
1013         * (left-to-right, non-recursively) each <pair> appearing in the <string> as
1014         * follows: replace <ESC><ESC> with <ESC>; replace <ESC><special> with
1015         * <special>; replace <ESC><hexpair> with the octet indicated by the
1016         * <hexpair> If in <hexstring> form, a BER representation can be obtained
1017         * from converting each <hexpair> of the <hexstring> to the octet indicated
1018         * by the <hexpair>
1019         *
1020         * @param value
1021         *            The value to be unescaped
1022         * @return Returns a string value as a String, and a binary value as a byte
1023         *         array.
1024         * @throws IllegalArgumentException -
1025         *             When an Illegal value is provided.
1026         */
1027        public static Object unescapeValue( String value )
1028        {
1029            if ( StringTools.isEmpty( value ) )
1030            {
1031                return "";
1032            }
1033    
1034            char[] chars = value.toCharArray();
1035    
1036            if ( chars[0] == '#' )
1037            {
1038                if ( chars.length == 1 )
1039                {
1040                    // The value is only containing a #
1041                    return StringTools.EMPTY_BYTES;
1042                }
1043    
1044                if ( ( chars.length % 2 ) != 1 )
1045                {
1046                    throw new IllegalArgumentException( I18n.err( I18n.ERR_04213 ) );
1047                }
1048    
1049                // HexString form
1050                byte[] hexValue = new byte[( chars.length - 1 ) / 2];
1051                int pos = 0;
1052    
1053                for ( int i = 1; i < chars.length; i += 2 )
1054                {
1055                    if ( StringTools.isHex( chars, i ) && StringTools.isHex( chars, i + 1 ) )
1056                    {
1057                        hexValue[pos++] = StringTools.getHexValue( chars[i], chars[i + 1] );
1058                    }
1059                    else
1060                    {
1061                        throw new IllegalArgumentException( I18n.err( I18n.ERR_04214 ) );
1062                    }
1063                }
1064    
1065                return hexValue;
1066            }
1067            else
1068            {
1069                boolean escaped = false;
1070                boolean isHex = false;
1071                byte pair = -1;
1072                int pos = 0;
1073    
1074                byte[] bytes = new byte[chars.length * 6];
1075    
1076                for ( int i = 0; i < chars.length; i++ )
1077                {
1078                    if ( escaped )
1079                    {
1080                        escaped = false;
1081    
1082                        switch ( chars[i] )
1083                        {
1084                            case '\\':
1085                            case '"':
1086                            case '+':
1087                            case ',':
1088                            case ';':
1089                            case '<':
1090                            case '>':
1091                            case '#':
1092                            case '=':
1093                            case ' ':
1094                                bytes[pos++] = ( byte ) chars[i];
1095                                break;
1096    
1097                            default:
1098                                if ( StringTools.isHex( chars, i ) )
1099                                {
1100                                    isHex = true;
1101                                    pair = ( ( byte ) ( StringTools.getHexValue( chars[i] ) << 4 ) );
1102                                }
1103    
1104                                break;
1105                        }
1106                    }
1107                    else
1108                    {
1109                        if ( isHex )
1110                        {
1111                            if ( StringTools.isHex( chars, i ) )
1112                            {
1113                                pair += StringTools.getHexValue( chars[i] );
1114                                bytes[pos++] = pair;
1115                            }
1116                        }
1117                        else
1118                        {
1119                            switch ( chars[i] )
1120                            {
1121                                case '\\':
1122                                    escaped = true;
1123                                    break;
1124    
1125                                // We must not have a special char
1126                                // Specials are : '"', '+', ',', ';', '<', '>', ' ',
1127                                // '#' and '='
1128                                case '"':
1129                                case '+':
1130                                case ',':
1131                                case ';':
1132                                case '<':
1133                                case '>':
1134                                case '#':
1135                                    if ( i != 0 )
1136                                    {
1137                                        // '#' are allowed if not in first position
1138                                        bytes[pos++] = '#';
1139                                        break;
1140                                    }
1141                                case '=':
1142                                    throw new IllegalArgumentException( I18n.err( I18n.ERR_04215 ) );
1143    
1144                                case ' ':
1145                                    if ( ( i == 0 ) || ( i == chars.length - 1 ) )
1146                                    {
1147                                        throw new IllegalArgumentException( I18n.err( I18n.ERR_04215 ) );
1148                                    }
1149                                    else
1150                                    {
1151                                        bytes[pos++] = ' ';
1152                                        break;
1153                                    }
1154    
1155                                default:
1156                                    if ( ( chars[i] >= 0 ) && ( chars[i] < 128 ) )
1157                                    {
1158                                        bytes[pos++] = ( byte ) chars[i];
1159                                    }
1160                                    else
1161                                    {
1162                                        byte[] result = StringTools.charToBytes( chars[i] );
1163                                        System.arraycopy( result, 0, bytes, pos, result.length );
1164                                        pos += result.length;
1165                                    }
1166    
1167                                    break;
1168                            }
1169                        }
1170                    }
1171                }
1172    
1173                return StringTools.utf8ToString( bytes, pos );
1174            }
1175        }
1176    
1177    
1178        /**
1179         * Transform a value in a String, accordingly to RFC 2253
1180         *
1181         * @param value The attribute value to be escaped
1182         * @return The escaped string value.
1183         */
1184        public static String escapeValue( String value )
1185        {
1186            if ( StringTools.isEmpty( value ) )
1187            {
1188                return "";
1189            }
1190    
1191            char[] chars = value.toCharArray();
1192            char[] newChars = new char[chars.length * 3];
1193            int pos = 0;
1194    
1195            for ( int i = 0; i < chars.length; i++ )
1196            {
1197                switch ( chars[i] )
1198                {
1199                    case ' ':
1200                        if ( ( i > 0 ) && ( i < chars.length - 1 ) )
1201                        {
1202                            newChars[pos++] = chars[i];
1203                        }
1204                        else
1205                        {
1206                            newChars[pos++] = '\\';
1207                            newChars[pos++] = chars[i];
1208                        }
1209    
1210                        break;
1211    
1212                    case '#':
1213                        if ( i != 0 )
1214                        {
1215                            newChars[pos++] = chars[i];
1216                        }
1217                        else
1218                        {
1219                            newChars[pos++] = '\\';
1220                            newChars[pos++] = chars[i];
1221                        }
1222    
1223                        break;
1224    
1225                    case '"':
1226                    case '+':
1227                    case ',':
1228                    case ';':
1229                    case '=':
1230                    case '<':
1231                    case '>':
1232                    case '\\':
1233                        newChars[pos++] = '\\';
1234                        newChars[pos++] = chars[i];
1235                        break;
1236    
1237                    case 0x7F:
1238                        newChars[pos++] = '\\';
1239                        newChars[pos++] = '7';
1240                        newChars[pos++] = 'F';
1241                        break;
1242    
1243                    case 0x00:
1244                    case 0x01:
1245                    case 0x02:
1246                    case 0x03:
1247                    case 0x04:
1248                    case 0x05:
1249                    case 0x06:
1250                    case 0x07:
1251                    case 0x08:
1252                    case 0x09:
1253                    case 0x0A:
1254                    case 0x0B:
1255                    case 0x0C:
1256                    case 0x0D:
1257                    case 0x0E:
1258                    case 0x0F:
1259                        newChars[pos++] = '\\';
1260                        newChars[pos++] = '0';
1261                        newChars[pos++] = StringTools.dumpHex( ( byte ) ( chars[i] & 0x0F ) );
1262                        break;
1263    
1264                    case 0x10:
1265                    case 0x11:
1266                    case 0x12:
1267                    case 0x13:
1268                    case 0x14:
1269                    case 0x15:
1270                    case 0x16:
1271                    case 0x17:
1272                    case 0x18:
1273                    case 0x19:
1274                    case 0x1A:
1275                    case 0x1B:
1276                    case 0x1C:
1277                    case 0x1D:
1278                    case 0x1E:
1279                    case 0x1F:
1280                        newChars[pos++] = '\\';
1281                        newChars[pos++] = '1';
1282                        newChars[pos++] = StringTools.dumpHex( ( byte ) ( chars[i] & 0x0F ) );
1283                        break;
1284    
1285                    default:
1286                        newChars[pos++] = chars[i];
1287                        break;
1288    
1289                }
1290            }
1291    
1292            return new String( newChars, 0, pos );
1293        }
1294    
1295    
1296        /**
1297         * Transform a value in a String, accordingly to RFC 2253
1298         *
1299         * @param attrValue
1300         *            The attribute value to be escaped
1301         * @return The escaped string value.
1302         */
1303        public static String escapeValue( byte[] attrValue )
1304        {
1305            if ( StringTools.isEmpty( attrValue ) )
1306            {
1307                return "";
1308            }
1309    
1310            String value = StringTools.utf8ToString( attrValue );
1311    
1312            return escapeValue( value );
1313        }
1314    
1315    
1316        /**
1317          * Gets the hashcode of this rdn.
1318          *
1319          * @see java.lang.Object#hashCode()
1320          * @return the instance's hash code 
1321          */
1322        public int hashCode()
1323        {
1324            int result = 37;
1325    
1326            switch ( nbAtavs )
1327            {
1328                case 0:
1329                    // An empty RDN
1330                    break;
1331    
1332                case 1:
1333                    // We have a single AttributeTypeAndValue
1334                    result = result * 17 + atav.hashCode();
1335                    break;
1336    
1337                default:
1338                    // We have more than one AttributeTypeAndValue
1339    
1340                    for ( AVA ata : atavs )
1341                    {
1342                        result = result * 17 + ata.hashCode();
1343                    }
1344    
1345                    break;
1346            }
1347    
1348            return result;
1349        }
1350    
1351    
1352        /**
1353         * @see Externalizable#readExternal(ObjectInput)<p>
1354         * 
1355         * A RDN is composed of on to many ATAVs (AttributeType And Value).
1356         * We should write all those ATAVs sequencially, following the 
1357         * structure :
1358         * 
1359         * <li>nbAtavs</li> The number of ATAVs to write. Can't be 0.
1360         * <li>upName</li> The User provided RDN
1361         * <li>normName</li> The normalized RDN. It can be empty if the normalized
1362         * name equals the upName.
1363         * <li>atavs</li>
1364         * <p>
1365         * For each ATAV :<p>
1366         * <li>start</li> The position of this ATAV in the upName string
1367         * <li>length</li> The ATAV user provided length
1368         * <li>Call the ATAV write method</li> The ATAV itself
1369         * 
1370         * @param out The stream into which the serialized RDN will be put
1371         * @throws IOException If the stream can't be written
1372         */
1373        public void writeExternal( ObjectOutput out ) throws IOException
1374        {
1375            out.writeInt( nbAtavs );
1376            out.writeUTF( upName );
1377    
1378            if ( upName.equals( normName ) )
1379            {
1380                out.writeUTF( "" );
1381            }
1382            else
1383            {
1384                out.writeUTF( normName );
1385            }
1386    
1387            out.writeInt( start );
1388            out.writeInt( length );
1389    
1390            switch ( nbAtavs )
1391            {
1392                case 0:
1393                    break;
1394    
1395                case 1:
1396                    out.writeObject( atav );
1397                    break;
1398    
1399                default:
1400                    for ( AVA value : atavs )
1401                    {
1402                        out.writeObject( value );
1403                    }
1404    
1405                    break;
1406            }
1407        }
1408    
1409    
1410        /**
1411         * @see Externalizable#readExternal(ObjectInput)
1412         * 
1413         * We read back the data to create a new RDB. The structure 
1414         * read is exposed in the {@link RDN#writeExternal(ObjectOutput)} 
1415         * method<p>
1416         * 
1417         * @param in The input stream from which the RDN will be read
1418         * @throws IOException If we can't read from the input stream
1419         * @throws ClassNotFoundException If we can't create a new RDN
1420         */
1421        public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
1422        {
1423            // Read the ATAV number
1424            nbAtavs = in.readInt();
1425    
1426            // Read the UPName
1427            upName = in.readUTF();
1428    
1429            // Read the normName
1430            normName = in.readUTF();
1431    
1432            if ( StringTools.isEmpty( normName ) )
1433            {
1434                normName = upName;
1435            }
1436    
1437            start = in.readInt();
1438            length = in.readInt();
1439    
1440            switch ( nbAtavs )
1441            {
1442                case 0:
1443                    atav = null;
1444                    break;
1445    
1446                case 1:
1447                    atav = ( AVA ) in.readObject();
1448                    atavType = atav.getNormType();
1449    
1450                    break;
1451    
1452                default:
1453                    atavs = new TreeSet<AVA>();
1454    
1455                    atavTypes = new MultiValueMap();
1456    
1457                    for ( int i = 0; i < nbAtavs; i++ )
1458                    {
1459                        AVA value = ( AVA ) in.readObject();
1460                        atavs.add( value );
1461                        atavTypes.put( value.getNormType(), value );
1462                    }
1463    
1464                    atav = null;
1465                    atavType = null;
1466    
1467                    break;
1468            }
1469        }
1470    }