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