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