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