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 */
020package org.apache.directory.api.ldap.model.name;
021
022
023import java.io.Externalizable;
024import java.io.IOException;
025import java.io.ObjectInput;
026import java.io.ObjectOutput;
027import java.util.Arrays;
028
029import org.apache.directory.api.i18n.I18n;
030import org.apache.directory.api.ldap.model.entry.Value;
031import org.apache.directory.api.ldap.model.exception.LdapException;
032import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
033import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
034import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
035import org.apache.directory.api.ldap.model.schema.AttributeType;
036import org.apache.directory.api.ldap.model.schema.LdapComparator;
037import org.apache.directory.api.ldap.model.schema.MatchingRule;
038import org.apache.directory.api.ldap.model.schema.Normalizer;
039import org.apache.directory.api.ldap.model.schema.SchemaManager;
040import org.apache.directory.api.util.Serialize;
041import org.apache.directory.api.util.Strings;
042import org.slf4j.Logger;
043import org.slf4j.LoggerFactory;
044
045
046/**
047 * <p>
048 * A Attribute Type And Value, which is the basis of all Rdn. It contains a
049 * type, and a value. The type must not be case sensitive. Superfluous leading
050 * and trailing spaces MUST have been trimmed before. The value MUST be in UTF8
051 * format, according to RFC 2253. If the type is in OID form, then the value
052 * must be a hexadecimal string prefixed by a '#' character. Otherwise, the
053 * string must respect the RC 2253 grammar.
054 * </p>
055 * <p>
056 * We will also keep a User Provided form of the AVA (Attribute Type And Value),
057 * called upName.
058 * </p>
059 * <p>
060 * This class is immutable
061 * </p>
062 *
063 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
064 */
065public class Ava implements Externalizable, Cloneable, Comparable<Ava>
066{
067    /**
068     * Declares the Serial Version Uid.
069     *
070     * @see <a
071     *      href="http://c2.com/cgi/wiki?AlwaysDeclareSerialVersionUid">Always
072     *      Declare Serial Version Uid</a>
073     */
074    private static final long serialVersionUID = 1L;
075
076    /** The LoggerFactory used by this class */
077    private static final Logger LOG = LoggerFactory.getLogger( Ava.class );
078
079    /** The normalized Name type */
080    private String normType;
081
082    /** The user provided Name type */
083    private String upType;
084
085    /** The value. It can be a String or a byte array */
086    private Value value;
087
088    /** The user provided Ava */
089    private String upName;
090
091    /** The attributeType if the Ava is schemaAware */
092    private AttributeType attributeType;
093
094    /** the schema manager */
095    private transient SchemaManager schemaManager;
096
097    /** The computed hashcode */
098    private volatile int h;
099
100
101    /**
102     * Constructs an empty Ava
103     */
104    public Ava()
105    {
106        this( null );
107    }
108
109
110    /**
111     * Constructs an empty schema aware Ava.
112     * 
113     * @param schemaManager The SchemaManager instance
114     */
115    public Ava( SchemaManager schemaManager )
116    {
117        normType = null;
118        upType = null;
119        value = null;
120        upName = "";
121        this.schemaManager = schemaManager;
122        attributeType = null;
123    }
124
125
126    /**
127     * Constructs new Ava using the provided SchemaManager and AVA
128     * 
129     * @param schemaManager The SchemaManager instance
130     * @param ava The AVA to copy
131     * @throws LdapInvalidDnException If the Ava is invalid
132     */
133    public Ava( SchemaManager schemaManager, Ava ava ) throws LdapInvalidDnException
134    {
135        upType = ava.upType;
136        
137        if ( ava.isSchemaAware() )
138        {
139            normType = ava.normType;
140            value = ava.value;
141            attributeType = ava.getAttributeType();
142        }
143        else
144        {
145            if ( schemaManager != null )
146            {
147                attributeType = schemaManager.getAttributeType( ava.normType );
148                
149                if ( attributeType != null )
150                {
151                    normType = attributeType.getOid();
152
153                    try
154                    {
155                        value = new Value( attributeType, ava.value );
156                    }
157                    catch ( LdapInvalidAttributeValueException e )
158                    {
159                        throw new LdapInvalidDnException( e.getResultCode() );
160                    }
161                }
162                else
163                {
164                    normType = ava.normType;
165                    value = ava.value;
166                }
167            }
168            else
169            {
170                normType = ava.normType;
171                value = ava.value;
172            }
173        }
174        
175        upName = getEscaped();
176
177        hashCode();
178    }
179
180
181    /**
182     * Construct an Ava containing a binary value.
183     * <p>
184     * Note that the upValue should <b>not</b> be null or empty, or resolve
185     * to an empty string after having trimmed it.
186     *
187     * @param upType The User Provided type
188     * @param upValue The User Provided binary value
189     * 
190     * @throws LdapInvalidDnException If the given type or value are invalid
191     */
192    public Ava( String upType, byte[] upValue ) throws LdapInvalidDnException
193    {
194        this( null, upType, upValue );
195    }
196
197
198    /**
199     * Construct a schema aware Ava containing a binary value. The AttributeType
200     * and value will be normalized accordingly to the given SchemaManager.
201     * <p>
202     * Note that the upValue should <b>not</b> be null or empty, or resolve
203     * to an empty string after having trimmed it.
204     *
205     * @param schemaManager The SchemaManager instance
206     * @param upType The User Provided type
207     * @param upValue The User Provided binary value
208     * 
209     * @throws LdapInvalidDnException If the given type or value are invalid
210     */
211    public Ava( SchemaManager schemaManager, String upType, byte[] upValue ) throws LdapInvalidDnException
212    {
213        if ( schemaManager != null )
214        {
215            this.schemaManager = schemaManager;
216
217            try
218            {
219                attributeType = schemaManager.lookupAttributeTypeRegistry( upType );
220            }
221            catch ( LdapException le )
222            {
223                String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY );
224                // Do NOT log the message here. The error may be handled and therefore the log message may polute the log files.
225                // Let the caller log the exception if needed.
226                throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le );
227            }
228
229            try
230            {
231                createAva( schemaManager, upType, new Value( attributeType, upValue ) );
232            }
233            catch ( LdapInvalidAttributeValueException liave )
234            {
235                String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY );
236                throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, liave );
237            }
238        }
239        else
240        {
241            createAva( upType, new Value( upValue ) );
242        }
243    }
244
245
246    /**
247     * Construct a schema aware Ava containing a binary value. The AttributeType
248     * and value will be normalized accordingly to the given SchemaManager.
249     * <p>
250     * Note that the upValue should <b>not</b> be null or empty, or resolve
251     * to an empty string after having trimmed it.
252     *
253     * @param schemaManager The SchemaManager instance
254     * @param upType The User Provided type
255     * @param upName the User Provided AVA
256     * @param upValue The User Provided binary value
257     * 
258     * @throws LdapInvalidDnException If the given type or value are invalid
259     */
260    public Ava( SchemaManager schemaManager, String upType, String upName, byte[] upValue ) throws LdapInvalidDnException
261    {
262        if ( schemaManager != null )
263        {
264            this.schemaManager = schemaManager;
265
266            try
267            {
268                attributeType = schemaManager.lookupAttributeTypeRegistry( upType );
269            }
270            catch ( LdapException le )
271            {
272                String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY );
273                // Do NOT log the message here. The error may be handled and therefore the log message may polute the log files.
274                // Let the caller log the exception if needed.
275                throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le );
276            }
277
278            try
279            {
280                createAva( schemaManager, upType, new Value( attributeType, upValue ) );
281            }
282            catch ( LdapInvalidAttributeValueException liave )
283            {
284                String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY );
285                throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, liave );
286            }
287        }
288        else
289        {
290            createAva( upType, new Value( upValue ) );
291        }
292        
293        this.upName = upName;
294    }
295
296
297    /**
298     * Construct an Ava with a String value.
299     * <p>
300     * Note that the upValue should <b>not</b> be null or empty, or resolve
301     * to an empty string after having trimmed it.
302     *
303     * @param upType The User Provided type
304     * @param upValue The User Provided String value
305     * 
306     * @throws LdapInvalidDnException If the given type or value are invalid
307     */
308    public Ava( String upType, String upValue ) throws LdapInvalidDnException
309    {
310        this( null, upType, upValue );
311    }
312
313
314    /**
315     * Construct a schema aware Ava with a String value.
316     * <p>
317     * Note that the upValue should <b>not</b> be null or empty, or resolve
318     * to an empty string after having trimmed it.
319     *
320     * @param schemaManager The SchemaManager instance
321     * @param upType The User Provided type
322     * @param upValue The User Provided String value
323     * 
324     * @throws LdapInvalidDnException If the given type or value are invalid
325     */
326    public Ava( SchemaManager schemaManager, String upType, String upValue ) throws LdapInvalidDnException
327    {
328        if ( schemaManager != null )
329        {
330            this.schemaManager = schemaManager;
331
332            try
333            {
334                attributeType = schemaManager.lookupAttributeTypeRegistry( upType );
335            }
336            catch ( LdapException le )
337            {
338                String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY );
339                // Do NOT log the message here. The error may be handled and therefore the log message may polute the log files.
340                // Let the caller log the exception if needed.
341                throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le );
342            }
343
344            try
345            {
346                createAva( schemaManager, upType, new Value( attributeType, upValue ) );
347            }
348            catch ( LdapInvalidAttributeValueException liave )
349            {
350                String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY );
351                throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, liave );
352            }
353        }
354        else
355        {
356            createAva( upType, new Value( upValue ) );
357        }
358    }
359
360
361    /**
362     * Construct a schema aware Ava with a String value.
363     * <p>
364     * Note that the upValue should <b>not</b> be null or empty, or resolve
365     * to an empty string after having trimmed it.
366     *
367     * @param schemaManager The SchemaManager instance
368     * @param upType The User Provided type
369     * @param upName the User provided AVA
370     * @param upValue The User Provided String value
371     * 
372     * @throws LdapInvalidDnException If the given type or value are invalid
373     */
374    public Ava( SchemaManager schemaManager, String upType, String upName, String upValue ) throws LdapInvalidDnException
375    {
376        if ( schemaManager != null )
377        {
378            this.schemaManager = schemaManager;
379
380            try
381            {
382                attributeType = schemaManager.lookupAttributeTypeRegistry( upType );
383            }
384            catch ( LdapException le )
385            {
386                String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY );
387                throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le );
388            }
389
390            try
391            {
392                createAva( schemaManager, upType, new Value( attributeType, upValue ) );
393            }
394            catch ( LdapInvalidAttributeValueException liave )
395            {
396                String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY );
397                throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, liave );
398            }
399        }
400        else
401        {
402            createAva( upType, new Value( upValue ) );
403        }
404        
405        this.upName = upName;
406    }
407    
408    
409    /**
410     * Construct an Ava. The type and value are normalized :
411     * <ul>
412     *   <li> the type is trimmed and lowercased </li>
413     *   <li> the value is trimmed </li>
414     * </ul>
415     * <p>
416     * Note that the upValue should <b>not</b> be null or empty, or resolved
417     * to an empty string after having trimmed it.
418     *
419     * @param upType The User Provided type
420     * @param normType The normalized type
421     * @param value The User Provided value
422     * @param upName The User Provided name (may be escaped)
423     * 
424     * @throws LdapInvalidDnException If the given type or value are invalid
425     */
426    // WARNING : The protection level is left unspecified intentionally.
427    // We need this method to be visible from the DnParser class, but not
428    // from outside this package.
429    /* Unspecified protection */Ava( String upType, String normType, Value value, String upName )
430        throws LdapInvalidDnException
431    {
432        this( null, upType, normType, value, upName );
433    }
434    
435    
436    /**
437     * Construct an Ava. The type and value are normalized :
438     * <ul>
439     *   <li> the type is trimmed and lowercased </li>
440     *   <li> the value is trimmed </li>
441     * </ul>
442     * <p>
443     * Note that the upValue should <b>not</b> be null or empty, or resolved
444     * to an empty string after having trimmed it.
445     *
446     * @param attributeType The AttributeType for this value
447     * @param upType The User Provided type
448     * @param normType The normalized type
449     * @param value The value
450     * @param upName The User Provided name (may be escaped)
451     * 
452     * @throws LdapInvalidDnException If the given type or value are invalid
453     */
454    // WARNING : The protection level is left unspecified intentionally.
455    // We need this method to be visible from the DnParser class, but not
456    // from outside this package.
457    /* Unspecified protection */Ava( AttributeType attributeType, String upType, String normType, Value value, String upName )
458        throws LdapInvalidDnException
459    {
460        this.attributeType = attributeType;
461        String upTypeTrimmed = Strings.trim( upType );
462        String normTypeTrimmed = Strings.trim( normType );
463
464        if ( Strings.isEmpty( upTypeTrimmed ) )
465        {
466            if ( Strings.isEmpty( normTypeTrimmed ) )
467            {
468                String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY );
469                LOG.error( message );
470                throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message );
471            }
472            else
473            {
474                // In this case, we will use the normType instead
475                this.normType = Strings.lowerCaseAscii( normTypeTrimmed );
476                this.upType = normType;
477            }
478        }
479        else if ( Strings.isEmpty( normTypeTrimmed ) )
480        {
481            // In this case, we will use the upType instead
482            this.normType = Strings.lowerCaseAscii( upTypeTrimmed );
483            this.upType = upType;
484        }
485        else
486        {
487            this.normType = Strings.lowerCaseAscii( normTypeTrimmed );
488            this.upType = upType;
489        }
490
491        this.value = value;
492        this.upName = upName;
493        hashCode();
494    }
495
496
497    /**
498     * Construct an Ava. The type and value are normalized :
499     * <ul>
500     *   <li> the type is trimmed and lowercased </li>
501     *   <li> the value is trimmed </li>
502     * </ul>
503     * <p>
504     * Note that the upValue should <b>not</b> be null or empty, or resolved
505     * to an empty string after having trimmed it.
506     *
507     * @param schemaManager The SchemaManager
508     * @param upType The User Provided type
509     * @param normType The normalized type
510     * @param value The value
511     * 
512     * @throws LdapInvalidDnException If the given type or value are invalid
513     */
514    // WARNING : The protection level is left unspecified intentionally.
515    // We need this method to be visible from the DnParser class, but not
516    // from outside this package.
517    /* Unspecified protection */Ava( SchemaManager schemaManager, String upType, String normType, Value value )
518        throws LdapInvalidDnException
519    {
520        StringBuilder sb = new StringBuilder();
521
522        this.upType = upType;
523        this.normType = normType;
524        this.value = value;
525        
526        sb.append( upType );
527        sb.append( '=' );
528        
529        if ( ( value != null ) && ( value.getString() != null ) )
530        {
531            sb.append( value.getString() );
532        }
533        
534        upName = sb.toString();
535
536        if ( schemaManager != null )
537        {
538            apply( schemaManager );
539        }
540
541        hashCode();
542    }
543
544
545    /**
546     * Construct a schema aware Ava. The AttributeType and value will be checked accordingly
547     * to the SchemaManager.
548     * <p>
549     * Note that the upValue should <b>not</b> be null or empty, or resolve
550     * to an empty string after having trimmed it.
551     *
552     * @param schemaManager The SchemaManager instance
553     * @param upType The User Provided type
554     * @param value The value
555     */
556    private void createAva( SchemaManager schemaManager, String upType, Value value )
557    {
558        StringBuilder sb = new StringBuilder();
559
560        normType = attributeType.getOid();
561        this.upType = upType;
562        this.value = value;
563        
564        sb.append( upType );
565        sb.append( '=' );
566        
567        if ( value != null )
568        {
569            sb.append( Rdn.escapeValue( value.getString() ) );
570        }
571        
572        upName = sb.toString();
573
574        hashCode();
575    }
576
577
578    /**
579     * Construct an Ava. The type and value are normalized :
580     * <ul>
581     *   <li> the type is trimmed and lowercased </li>
582     *   <li> the value is trimmed </li>
583     * </ul>
584     * <p>
585     * Note that the upValue should <b>not</b> be null or empty, or resolved
586     * to an empty string after having trimmed it.
587     *
588     * @param upType The User Provided type
589     * @param upValue The User Provided value
590     * 
591     * @throws LdapInvalidDnException If the given type or value are invalid
592     */
593    private void createAva( String upType, Value upValue ) throws LdapInvalidDnException
594    {
595        String upTypeTrimmed = Strings.trim( upType );
596        String normTypeTrimmed = Strings.trim( normType );
597
598        if ( Strings.isEmpty( upTypeTrimmed ) )
599        {
600            if ( Strings.isEmpty( normTypeTrimmed ) )
601            {
602                String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY );
603                // Do NOT log the message here. The error may be handled and therefore the log message may polute the log files.
604                // Let the caller log the exception if needed.
605                throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message );
606            }
607            else
608            {
609                // In this case, we will use the normType instead
610                this.normType = Strings.lowerCaseAscii( normTypeTrimmed );
611                this.upType = normType;
612            }
613        }
614        else if ( Strings.isEmpty( normTypeTrimmed ) )
615        {
616            // In this case, we will use the upType instead
617            this.normType = Strings.lowerCaseAscii( upTypeTrimmed );
618            this.upType = upType;
619        }
620        else
621        {
622            this.normType = Strings.lowerCaseAscii( normTypeTrimmed );
623            this.upType = upType;
624
625        }
626
627        value = upValue;
628
629        upName = getEscaped();
630        
631        hashCode();
632    }
633
634
635    /**
636     * Apply a SchemaManager to the Ava. It will normalize the Ava.<br>
637     * If the Ava already had a SchemaManager, then the new SchemaManager will be
638     * used instead.
639     * 
640     * @param schemaManager The SchemaManager instance to use
641     * @throws LdapInvalidDnException If the Ava can't be normalized accordingly
642     * to the given SchemaManager
643     */
644    private void apply( SchemaManager schemaManager ) throws LdapInvalidDnException
645    {
646        if ( schemaManager != null )
647        {
648            this.schemaManager = schemaManager;
649
650            AttributeType tmpAttributeType = null;
651
652            try
653            {
654                tmpAttributeType = schemaManager.lookupAttributeTypeRegistry( normType );
655            }
656            catch ( LdapException le )
657            {
658                if ( schemaManager.isRelaxed() )
659                {
660                    // No attribute in the schema, but the schema is relaxed : get out
661                    return;
662                }
663                else
664                {
665                    String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY );
666                    // Do NOT log the message here. The error may be handled and therefore the log message may polute the log files.
667                    // Let the caller log the exception if needed.
668                    throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le );
669                }
670            }
671
672            if ( this.attributeType == tmpAttributeType )
673            {
674                // No need to normalize again
675                return;
676            }
677            else
678            {
679                this.attributeType = tmpAttributeType;
680            }
681
682            try
683            {
684                value = new Value( tmpAttributeType, value );
685            }
686            catch ( LdapException le )
687            {
688                String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY );
689                throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le );
690            }
691
692            hashCode();
693        }
694    }
695
696
697    /**
698     * Get the normalized type of a Ava
699     *
700     * @return The normalized type
701     */
702    public String getNormType()
703    {
704        return normType;
705    }
706
707
708    /**
709     * Get the user provided type of a Ava
710     *
711     * @return The user provided type
712     */
713    public String getType()
714    {
715        return upType;
716    }
717
718
719    /**
720     * Get the Value of a Ava
721     *
722     * @return The value
723     */
724    public Value getValue()
725    {
726        return value.clone();
727    }
728
729
730    /**
731     * Get the user provided form of this attribute type and value
732     *
733     * @return The user provided form of this ava
734     */
735    public String getName()
736    {
737        return upName;
738    }
739    
740    
741    /**
742     * @return The Ava as an escaped String
743     */
744    public String getEscaped()
745    {
746        StringBuilder sb = new StringBuilder();
747        
748        sb.append( getType() );
749        sb.append( '=' );
750        
751        if ( value == null )
752        {
753            return sb.toString();
754        }
755        
756        byte[] bytes = value.getBytes();
757        
758        if ( Strings.isEmpty( bytes ) )
759        {
760            return sb.toString();
761        }
762        
763        boolean leadChar = true;
764        
765        for ( int pos = 0; pos < bytes.length; pos++  )
766        {
767            boolean trailChar = pos == bytes.length - 1;
768            byte b = bytes[pos];
769
770            switch ( b )
771            {
772                case 0x00 :
773                    sb.append( "\\00" );
774                    break;
775
776                case 0x01 :
777                case 0x02 :
778                case 0x03 :
779                case 0x04 :
780                case 0x05 :
781                case 0x06 :
782                case 0x07 :
783                case 0x08 :
784                case 0x09 :
785                case 0x0A :
786                case 0x0B :
787                case 0x0C :
788                case 0x0D :
789                case 0x0E :
790                case 0x0F :
791                case 0x10 :
792                case 0x11 :
793                case 0x12 :
794                case 0x13 :
795                case 0x14 :
796                case 0x15 :
797                case 0x16 :
798                case 0x17 :
799                case 0x18 :
800                case 0x19 :
801                case 0x1A :
802                case 0x1B :
803                case 0x1C :
804                case 0x1D :
805                case 0x1E :
806                case 0x1F :
807                    sb.append( ( char ) b );
808                    break;
809                    
810                case 0x20 :
811                    if ( leadChar || trailChar )
812                    {
813                        sb.append( "\\ " );
814                    }
815                    else
816                    {
817                        sb.append( ( char ) b );
818                    }
819                    
820                    break;
821                    
822                case 0x21 :
823                    sb.append( ( char ) b );
824                    break;
825                    
826                    
827                case 0x22 :
828                    sb.append( "\\\"" );
829                    break;
830
831                case 0x23 :
832                    if ( leadChar )
833                    {
834                        sb.append( "\\#" );
835                    }
836                    else
837                    {
838                        sb.append( '#' );
839                    }
840                    
841                    break;
842
843                case 0x24 :
844                case 0x25 :
845                case 0x26 :
846                case 0x27 :
847                case 0x28 :
848                case 0x29 :
849                case 0x2A :
850                    sb.append( ( char ) b );
851                    break;
852                    
853                case 0x2B :
854                    sb.append( "\\+" );
855                    break;
856
857                case 0x2C :
858                    sb.append( "\\," );
859                    break;
860
861                case 0x2D :
862                case 0x2E :
863                case 0x2F :
864                case 0x30 :
865                case 0x31 :
866                case 0x32 :
867                case 0x33 :
868                case 0x34 :
869                case 0x35 :
870                case 0x36 :
871                case 0x37 :
872                case 0x38 :
873                case 0x39 :
874                case 0x3A :
875                    sb.append( ( char ) b );
876                    break;
877                    
878                case 0x3B :
879                    sb.append( "\\;" );
880                    break;
881
882                case 0x3C :
883                    sb.append( "\\<" );
884                    break;
885
886                case 0x3D :
887                    sb.append( ( char ) b );
888                    break;
889                    
890                case 0x3E :
891                    sb.append( "\\>" );
892                    break;
893                
894                case 0x3F :
895                case 0x40 :
896                case 0x41 :
897                case 0x42 :
898                case 0x43 :
899                case 0x44 :
900                case 0x45 :
901                case 0x46 :
902                case 0x47 :
903                case 0x48 :
904                case 0x49 :
905                case 0x4A :
906                case 0x4B :
907                case 0x4C :
908                case 0x4D :
909                case 0x4E :
910                case 0x4F :
911                case 0x50 :
912                case 0x51 :
913                case 0x52 :
914                case 0x53 :
915                case 0x54 :
916                case 0x55 :
917                case 0x56 :
918                case 0x57 :
919                case 0x58 :
920                case 0x59 :
921                case 0x5A :
922                case 0x5B :
923                    sb.append( ( char ) b );
924                    break;
925                    
926                case 0x5C :
927                    sb.append( "\\\\" );
928                    break;
929
930                case 0x5D :
931                case 0x5E :
932                case 0x5F :
933                case 0x60 :
934                case 0x61 :
935                case 0x62 :
936                case 0x63 :
937                case 0x64 :
938                case 0x65 :
939                case 0x66 :
940                case 0x67 :
941                case 0x68 :
942                case 0x69 :
943                case 0x6A :
944                case 0x6B :
945                case 0x6C :
946                case 0x6D :
947                case 0x6E :
948                case 0x6F :
949                case 0x70 :
950                case 0x71 :
951                case 0x72 :
952                case 0x73 :
953                case 0x74 :
954                case 0x75 :
955                case 0x76 :
956                case 0x77 :
957                case 0x78 :
958                case 0x79 :
959                case 0x7A :
960                case 0x7B :
961                case 0x7C :
962                case 0x7D :
963                case 0x7E :
964                case 0x7F :
965                    sb.append( ( char ) b );
966                    break;
967
968                // Between 0x80 and 0xC1, this is an octet
969                case ( byte ) 0x80 :
970                case ( byte ) 0x81 :
971                case ( byte ) 0x82 :
972                case ( byte ) 0x83 :
973                case ( byte ) 0x84 :
974                case ( byte ) 0x85 :
975                case ( byte ) 0x86 :
976                case ( byte ) 0x87 :
977                case ( byte ) 0x88 :
978                case ( byte ) 0x89 :
979                case ( byte ) 0x8A :
980                case ( byte ) 0x8B :
981                case ( byte ) 0x8C :
982                case ( byte ) 0x8D :
983                case ( byte ) 0x8E :
984                case ( byte ) 0x8F :
985                case ( byte ) 0x90 :
986                case ( byte ) 0x91 :
987                case ( byte ) 0x92 :
988                case ( byte ) 0x93 :
989                case ( byte ) 0x94 :
990                case ( byte ) 0x95 :
991                case ( byte ) 0x96 :
992                case ( byte ) 0x97 :
993                case ( byte ) 0x98 :
994                case ( byte ) 0x99 :
995                case ( byte ) 0x9A :
996                case ( byte ) 0x9B :
997                case ( byte ) 0x9C :
998                case ( byte ) 0x9D :
999                case ( byte ) 0x9E :
1000                case ( byte ) 0x9F :
1001                case ( byte ) 0xA0 :
1002                case ( byte ) 0xA1 :
1003                case ( byte ) 0xA2 :
1004                case ( byte ) 0xA3 :
1005                case ( byte ) 0xA4 :
1006                case ( byte ) 0xA5 :
1007                case ( byte ) 0xA6 :
1008                case ( byte ) 0xA7 :
1009                case ( byte ) 0xA8 :
1010                case ( byte ) 0xA9 :
1011                case ( byte ) 0xAA :
1012                case ( byte ) 0xAB :
1013                case ( byte ) 0xAC :
1014                case ( byte ) 0xAD :
1015                case ( byte ) 0xAE :
1016                case ( byte ) 0xAF :
1017                case ( byte ) 0xB0 :
1018                case ( byte ) 0xB1 :
1019                case ( byte ) 0xB2 :
1020                case ( byte ) 0xB3 :
1021                case ( byte ) 0xB4 :
1022                case ( byte ) 0xB5 :
1023                case ( byte ) 0xB6 :
1024                case ( byte ) 0xB7 :
1025                case ( byte ) 0xB8 :
1026                case ( byte ) 0xB9 :
1027                case ( byte ) 0xBA :
1028                case ( byte ) 0xBB :
1029                case ( byte ) 0xBC :
1030                case ( byte ) 0xBD :
1031                case ( byte ) 0xBE :
1032                case ( byte ) 0xBF :
1033                case ( byte ) 0xC0 :
1034                case ( byte ) 0xC1 :
1035                    sb.append( '\\' ).append( Strings.byteToString( b ) );
1036                    break;
1037
1038                // Between 0xC2 and 0xDF, we may have a UTF-2 char
1039                case ( byte ) 0xC2 :
1040                case ( byte ) 0xC3 :
1041                case ( byte ) 0xC4 :
1042                case ( byte ) 0xC5 :
1043                case ( byte ) 0xC6 :
1044                case ( byte ) 0xC7 :
1045                case ( byte ) 0xC8 :
1046                case ( byte ) 0xC9 :
1047                case ( byte ) 0xCA :
1048                case ( byte ) 0xCB :
1049                case ( byte ) 0xCC :
1050                case ( byte ) 0xCD :
1051                case ( byte ) 0xCE :
1052                case ( byte ) 0xCF :
1053                case ( byte ) 0xD0 :
1054                case ( byte ) 0xD1 :
1055                case ( byte ) 0xD2 :
1056                case ( byte ) 0xD3 :
1057                case ( byte ) 0xD4 :
1058                case ( byte ) 0xD5 :
1059                case ( byte ) 0xD6 :
1060                case ( byte ) 0xD7 :
1061                case ( byte ) 0xD8 :
1062                case ( byte ) 0xD9 :
1063                case ( byte ) 0xDA :
1064                case ( byte ) 0xDB :
1065                case ( byte ) 0xDC :
1066                case ( byte ) 0xDD :
1067                case ( byte ) 0xDE :
1068                case ( byte ) 0xDF :
1069                    // UTF2, if the following byte is in [0x80-0xBF]
1070                    if ( trailChar )
1071                    {
1072                        // No next byte : this is an octet
1073                        sb.append( '\\' ).append( Strings.byteToString( b ) );
1074                    }
1075                    else
1076                    {
1077                        int b2 = bytes[pos + 1] & 0x00FF;
1078                        
1079                        if ( ( b2 >= 0x0080 ) && ( b2 <= 0x00BF ) )
1080                        {
1081                            // This is an UTF-2 char
1082                            sb.append( Strings.utf8ToString( bytes, pos, 2 ) );
1083                            pos++;
1084                        }
1085                        else
1086                        {
1087                            // Not an UTF-2
1088                            sb.append( '\\' ).append( Strings.byteToString( b ) );
1089                        }
1090                    }
1091                
1092                    break;
1093
1094                case ( byte ) 0xE0 :
1095                    // May be an UTF-3, if the next byte is in [0xA0-0xBF], followed by a byte in [0x80-0xBF]
1096                    if ( trailChar )
1097                    {
1098                        // No next byte : this is an octet
1099                        sb.append( '\\' ).append( Strings.byteToString( b ) );
1100                        break;
1101                    }
1102                    
1103                    if ( pos == bytes.length - 2 )
1104                    {
1105                        // We only have 2 bytes : not an UTF-3
1106                        sb.append( '\\' ).append( Strings.byteToString( b ) );
1107                    }
1108                    else
1109                    {
1110                        int b2 = bytes[pos + 1] & 0x00FF;
1111                        
1112                        if ( ( b2 >= 0x00A0 ) && ( b2 <= 0x00BF ) )
1113                        {
1114                            int b3 = bytes[pos + 2] & 0x00FF;
1115                            
1116                            // Check that the third byte is in between 0x80-0xBF
1117                            if ( ( b3 >= 0x0080 ) && ( b3 <= 0x00BF ) )
1118                            {
1119                                // UTF-3
1120                                sb.append( Strings.utf8ToString( bytes, pos, 3 ) );
1121                                pos += 2;
1122                            }
1123                            else
1124                            {
1125                                // Not an UTF-3, dump one bytes
1126                                sb.append( '\\' ).append( Strings.byteToString( b ) );
1127                            }
1128                        }
1129                        else
1130                        {
1131                            // Not an UTF-3 : dump two byte
1132                            sb.append( '\\' ).append( Strings.byteToString( b ) );
1133                        }
1134                    }
1135                    
1136                    break;
1137                    
1138
1139                // Between E1 and EC, this may be an UTF-3 if the next two bytes are between 0x80 and 0xBF
1140                case ( byte ) 0xE1 :
1141                case ( byte ) 0xE2 :
1142                case ( byte ) 0xE3 :
1143                case ( byte ) 0xE4 :
1144                case ( byte ) 0xE5 :
1145                case ( byte ) 0xE6 :
1146                case ( byte ) 0xE7 :
1147                case ( byte ) 0xE8 :
1148                case ( byte ) 0xE9 :
1149                case ( byte ) 0xEA :
1150                case ( byte ) 0xEB :
1151                case ( byte ) 0xEC :
1152                case ( byte ) 0xEE :
1153                case ( byte ) 0xEF :
1154                    if ( trailChar )
1155                    {
1156                        // No next byte : this is an octet
1157                        sb.append( '\\' ).append( Strings.byteToString( b ) );
1158                        break;
1159                    }
1160                    
1161                    if ( pos == bytes.length - 2 )
1162                    {
1163                        // We only have 2 bytes : not an UTF-3
1164                        sb.append( '\\' ).append( Strings.byteToString( b ) );
1165                    }
1166                    else
1167                    {
1168                        int b2 = bytes[pos + 1] & 0x00FF;
1169                        
1170                        if ( ( b2 >= 0x0080 ) && ( b2 <= 0x00BF ) )
1171                        {
1172                            int b3 = bytes[pos + 2] & 0x00FF;
1173                            
1174                            // Check that the third byte is in between 0x80-0xBF
1175                            if ( ( b3 >= 0x0080 ) && ( b3 <= 0x00BF ) )
1176                            {
1177                                // UTF-3
1178                                sb.append( Strings.utf8ToString( bytes, pos, 3 ) );
1179                                pos += 2;
1180                            }
1181                            else
1182                            {
1183                                // Not an UTF-3, dump one byte
1184                                sb.append( '\\' ).append( Strings.byteToString( b ) );
1185                            }
1186                        }
1187                        else
1188                        {
1189                            // Not an UTF-3 : dump one byte
1190                            sb.append( '\\' ).append( Strings.byteToString( b ) );
1191                            pos++;
1192                        }
1193                    }
1194                    
1195                    break;
1196
1197                case ( byte ) 0xED :
1198                    // May be an UTF-3 if the second byte is in [0x80-0x9F] and the third byte in [0x80-0xBF]
1199                    if ( trailChar )
1200                    {
1201                        // No next byte : this is an octet
1202                        sb.append( '\\' ).append( Strings.byteToString( b ) );
1203                        break;
1204                    }
1205                    
1206                    if ( pos == bytes.length - 2 )
1207                    {
1208                        // We only have 2 bytes : not an UTF-3
1209                        sb.append( '\\' ).append( Strings.byteToString( b ) );
1210                    }
1211                    else
1212                    {
1213                        int b2 = bytes[pos + 1] & 0x00FF;
1214                        
1215                        if ( ( b2 >= 0x0080 ) && ( b2 <= 0x009F ) )
1216                        {
1217                            int b3 = bytes[pos + 2] & 0x00FF;
1218                            
1219                            // Check that the third byte is in between 0x80-0xBF
1220                            if ( ( b3 >= 0x0080 ) && ( b3 <= 0x00BF ) )
1221                            {
1222                                // UTF-3
1223                                sb.append( Strings.utf8ToString( bytes, pos, 3 ) );
1224                                pos += 2;
1225                            }
1226                            else
1227                            {
1228                                // Not an UTF-3, dump one byte
1229                                sb.append( '\\' ).append( Strings.byteToString( b ) );
1230                            }
1231                        }
1232                        else
1233                        {
1234                            // Not an UTF-3 : dump one byte
1235                            sb.append( '\\' ).append( Strings.byteToString( b ) );
1236                            pos++;
1237                        }
1238                    }
1239                    
1240                    break;
1241
1242                case ( byte ) 0xF0 :
1243                    // May be an UTF-4 if the second byte is in [0x90-0xBF] followed by two bytes in [0x80-0xBF]
1244                    if ( trailChar )
1245                    {
1246                        // No next byte : this is an octet
1247                        sb.append( '\\' ).append( Strings.byteToString( b ) );
1248                        break;
1249                    }
1250                    
1251                    if ( pos == bytes.length - 3 )
1252                    {
1253                        // We only have 2 bytes : not an UTF-4
1254                        sb.append( '\\' ).append( Strings.byteToString( b ) );
1255                    }
1256                    else
1257                    {
1258                        int b2 = bytes[pos + 1] & 0x00FF;
1259                        
1260                        if ( ( b2 >= 0x0090 ) && ( b2 <= 0x00BF ) )
1261                        {
1262                            int b3 = bytes[pos + 2] & 0x00FF;
1263                            
1264                            // Check that the third byte is in between 0x80-0xBF
1265                            if ( ( b3 >= 0x0080 ) && ( b3 <= 0x00BF ) )
1266                            {
1267                                int b4 = bytes[pos + 3] & 0x00FF;
1268                                
1269                                // Check that the forth byte is in between 0x80-0xBF
1270                                if ( ( b4 >= 0x0080 ) && ( b4 <= 0x00BF ) )
1271                                {
1272                                    // UTF-4
1273                                    sb.append( Strings.utf8ToString( bytes, pos, 4 ) );
1274                                    pos += 3;
1275                                }
1276                                else
1277                                {
1278                                    // Not an UTF-4, dump one byte
1279                                    sb.append( '\\' ).append( Strings.byteToString( b ) );
1280                                }
1281                            }
1282                            else
1283                            {
1284                                // Not an UTF-4, dump one byte
1285                                sb.append( '\\' ).append( Strings.byteToString( b ) );
1286                            }
1287                        }
1288                        else
1289                        {
1290                            // Not an UTF-4 : dump one byte
1291                            sb.append( '\\' ).append( Strings.byteToString( b ) );
1292                            pos++;
1293                        }
1294                    }
1295                    
1296                    break;
1297
1298                case ( byte ) 0xF1 :
1299                case ( byte ) 0xF2 :
1300                case ( byte ) 0xF3 :
1301                    // May be an UTF-4
1302                    // May be an UTF-4 if it's followed by three bytes in [0x80-0xBF]
1303                    if ( trailChar )
1304                    {
1305                        // No next byte : this is an octet
1306                        sb.append( '\\' ).append( Strings.byteToString( b ) );
1307                        break;
1308                    }
1309                    
1310                    if ( pos == bytes.length - 3 )
1311                    {
1312                        // We only have 2 bytes : not an UTF-4
1313                        sb.append( '\\' ).append( Strings.byteToString( b ) );
1314                    }
1315                    else
1316                    {
1317                        int b2 = bytes[pos + 1] & 0x00FF;
1318                        
1319                        if ( ( b2 >= 0x0080 ) && ( b2 <= 0x00BF ) )
1320                        {
1321                            int b3 = bytes[pos + 2] & 0x00FF;
1322                            
1323                            // Check that the third byte is in between 0x80-0xBF
1324                            if ( ( b3 >= 0x0080 ) && ( b3 <= 0x00BF ) )
1325                            {
1326                                int b4 = bytes[pos + 3] & 0x00FF;
1327                                
1328                                // Check that the forth byte is in between 0x80-0xBF
1329                                if ( ( b4 >= 0x0080 ) && ( b4 <= 0x00BF ) )
1330                                {
1331                                    // UTF-4
1332                                    sb.append( Strings.utf8ToString( bytes, pos, 4 ) );
1333                                    pos += 3;
1334                                }
1335                                else
1336                                {
1337                                    // Not an UTF-4, dump one byte
1338                                    sb.append( '\\' ).append( Strings.byteToString( b ) );
1339                                }
1340                            }
1341                            else
1342                            {
1343                                // Not an UTF-4, dump one byte
1344                                sb.append( '\\' ).append( Strings.byteToString( b ) );
1345                            }
1346                        }
1347                        else
1348                        {
1349                            // Not an UTF-4 : dump one byte
1350                            sb.append( '\\' ).append( Strings.byteToString( b ) );
1351                            pos++;
1352                        }
1353                    }
1354                    
1355                    break;
1356
1357                case ( byte ) 0xF4 :
1358                    // May be an UTF-4 if the second byte is in [0x80-0x8F] followed by two bytes in [0x80-0xBF]
1359                    if ( trailChar )
1360                    {
1361                        // No next byte : this is an octet
1362                        sb.append( '\\' ).append( Strings.byteToString( b ) );
1363                        break;
1364                    }
1365                    
1366                    if ( pos == bytes.length - 3 )
1367                    {
1368                        // We only have 2 bytes : not an UTF-4
1369                        sb.append( '\\' ).append( Strings.byteToString( b ) );
1370                    }
1371                    else
1372                    {
1373                        int b2 = bytes[pos + 1] & 0x00FF;
1374                        
1375                        if ( ( b2 >= 0x0080 ) && ( b2 <= 0x008F ) )
1376                        {
1377                            int b3 = bytes[pos + 2] & 0x00FF;
1378                            
1379                            // Check that the third byte is in between 0x80-0xBF
1380                            if ( ( b3 >= 0x0080 ) && ( b3 <= 0x00BF ) )
1381                            {
1382                                int b4 = bytes[pos + 3] & 0x00FF;
1383                                
1384                                // Check that the forth byte is in between 0x80-0xBF
1385                                if ( ( b4 >= 0x0080 ) && ( b4 <= 0x00BF ) )
1386                                {
1387                                    // UTF-4
1388                                    sb.append( Strings.utf8ToString( bytes, pos, 4 ) );
1389                                    pos += 3;
1390                                }
1391                                else
1392                                {
1393                                    // Not an UTF-4, dump one byte
1394                                    sb.append( '\\' ).append( Strings.byteToString( b ) );
1395                                }
1396                            }
1397                            else
1398                            {
1399                                // Not an UTF-4, dump one byte
1400                                sb.append( '\\' ).append( Strings.byteToString( b ) );
1401                            }
1402                        }
1403                        else
1404                        {
1405                            // Not an UTF-4 : dump one byte
1406                            sb.append( '\\' ).append( Strings.byteToString( b ) );
1407                            pos++;
1408                        }
1409                    }
1410                    
1411                    break;
1412
1413
1414                default :
1415                    // octet
1416                    sb.append( '\\' ).append( Strings.byteToString( b ) );
1417
1418                    break;
1419                    
1420            }
1421            
1422            if ( leadChar )
1423            {
1424                leadChar = false;
1425            }
1426        }
1427        
1428        return sb.toString();
1429    }
1430
1431
1432    /**
1433     * Implements the cloning.
1434     *
1435     * @return a clone of this object
1436     */
1437    @Override
1438    public Ava clone()
1439    {
1440        try
1441        {
1442            Ava clone = ( Ava ) super.clone();
1443            clone.value = value.clone();
1444
1445            return clone;
1446        }
1447        catch ( CloneNotSupportedException cnse )
1448        {
1449            throw new Error( I18n.err( I18n.ERR_13621_ASSERTION_FAILURE ), cnse );
1450        }
1451    }
1452
1453
1454    /**
1455     * Gets the hashcode of this object.
1456     *
1457     * @see java.lang.Object#hashCode()
1458     * @return The instance hash code
1459     */
1460    @Override
1461    public int hashCode()
1462    {
1463        if ( h == 0 )
1464        {
1465            int hTmp = 37;
1466
1467            hTmp = hTmp * 17 + ( normType != null ? normType.hashCode() : 0 );
1468            h = hTmp * 17 + ( value != null ? value.hashCode() : 0 );
1469        }
1470
1471        return h;
1472    }
1473
1474
1475    /**
1476     * @see Object#equals(Object)
1477     */
1478    @Override
1479    public boolean equals( Object obj )
1480    {
1481        if ( this == obj )
1482        {
1483            return true;
1484        }
1485
1486        if ( !( obj instanceof Ava ) )
1487        {
1488            return false;
1489        }
1490
1491        Ava instance = ( Ava ) obj;
1492
1493        // Compare the type
1494        if ( attributeType == null )
1495        {
1496            if ( normType == null )
1497            {
1498                if ( instance.normType != null )
1499                {
1500                    return false;
1501                }
1502            }
1503            else
1504            {
1505                if ( !normType.equals( instance.normType ) )
1506                {
1507                    return false;
1508                }
1509            }
1510        }
1511        else
1512        {
1513            if ( instance.getAttributeType() == null )
1514            {
1515                if ( ( schemaManager != null ) 
1516                        && !attributeType.equals( schemaManager.getAttributeType( instance.getType() ) ) )
1517                {
1518                    return false;
1519                }
1520            }
1521            else if ( !attributeType.equals( instance.getAttributeType() ) )
1522            {
1523                return false;
1524            }
1525        }
1526
1527        // Compare the values
1528        if ( ( value == null ) || value.isNull() )
1529        {
1530            return ( instance.value == null ) || instance.value.isNull();
1531        }
1532        else
1533        {
1534            if ( schemaManager != null )
1535            {
1536                if ( ( value.getString() != null ) && value.getString().equals( instance.value.getString() ) )
1537                {
1538                    return true;
1539                }
1540
1541                if ( attributeType == null )
1542                {
1543                    attributeType = schemaManager.getAttributeType( normType );
1544                }
1545                
1546                MatchingRule equalityMatchingRule = attributeType.getEquality();
1547
1548                if ( equalityMatchingRule != null )
1549                {
1550                    Normalizer normalizer = equalityMatchingRule.getNormalizer();
1551                    
1552                    try
1553                    {
1554                        return equalityMatchingRule.getLdapComparator().compare( normalizer.normalize( value.getString() ),
1555                            instance.value.getString() ) == 0;
1556                    }
1557                    catch ( LdapException le )
1558                    {
1559                        // TODO: is this OK? If the comparison is not reliable without normalization then we should throw exception
1560                        // instead returning false. Returning false may be misleading and the log message can be easily overlooked. 
1561                        // If the comparison is reliable, this should not really be an error. Maybe use debug or trace instead?
1562                        LOG.error( I18n.err( I18n.ERR_13620_CANNOT_NORMALIZE_VALUE ), le.getMessage() );
1563                        return false;
1564                    }
1565                }
1566                else
1567                {
1568                    // No Equality MR, use a direct comparison
1569                    if ( !value.isHumanReadable() )
1570                    {
1571                        return Arrays.equals( value.getBytes(), instance.value.getBytes() );
1572                    }
1573                    else
1574                    {
1575                        return value.getString().equals( instance.value.getString() );
1576                    }
1577                }
1578            }
1579            else
1580            {
1581                return value.equals( instance.value );
1582            }
1583        }
1584    }
1585
1586
1587    /**
1588     * Serialize the AVA into a buffer at the given position.
1589     * 
1590     * @param buffer The buffer which will contain the serialized Ava
1591     * @param pos The position in the buffer for the serialized value
1592     * @return The new position in the buffer
1593     * @throws IOException Id the serialization failed
1594     */
1595    public int serialize( byte[] buffer, int pos ) throws IOException
1596    {
1597        if ( Strings.isEmpty( upName )
1598            || Strings.isEmpty( upType )
1599            || Strings.isEmpty( normType )
1600            || ( value.isNull() ) )
1601        {
1602            String message;
1603
1604            if ( Strings.isEmpty( upName ) )
1605            {
1606                message = I18n.err( I18n.ERR_13616_CANNOT_SERIALIZE_AVA_UPNAME_NULL );
1607            }
1608            else if ( Strings.isEmpty( upType ) )
1609            {
1610                message = I18n.err( I18n.ERR_13617_CANNOT_SERIALIZE_AVA_UPTYPE_NULL );
1611            }
1612            else if ( Strings.isEmpty( normType ) )
1613            {
1614                message = I18n.err( I18n.ERR_13618_CANNOT_SERIALIZE_AVA_NORMTYPE_NULL );
1615            }
1616            else
1617            {
1618                message = I18n.err( I18n.ERR_13619_CANNOT_SERIALIZE_AVA_VALUE_NULL );
1619            }
1620
1621            LOG.error( message );
1622            throw new IOException( message );
1623        }
1624
1625        int length = 0;
1626
1627        // The upName
1628        byte[] upNameBytes = null;
1629
1630        if ( upName != null )
1631        {
1632            upNameBytes = Strings.getBytesUtf8( upName );
1633            length += 1 + 4 + upNameBytes.length;
1634        }
1635
1636        // The upType
1637        byte[] upTypeBytes = null;
1638
1639        if ( upType != null )
1640        {
1641            upTypeBytes = Strings.getBytesUtf8( upType );
1642            length += 1 + 4 + upTypeBytes.length;
1643        }
1644
1645        // Is HR
1646        length++;
1647
1648        // The hash code
1649        length += 4;
1650
1651        // Check that we will be able to store the data in the buffer
1652        if ( buffer.length - pos < length )
1653        {
1654            throw new ArrayIndexOutOfBoundsException();
1655        }
1656
1657        // Write the upName
1658        if ( upName != null )
1659        {
1660            buffer[pos++] = Serialize.TRUE;
1661            pos = Serialize.serialize( upNameBytes, buffer, pos );
1662        }
1663        else
1664        {
1665            buffer[pos++] = Serialize.FALSE;
1666        }
1667
1668        // Write the upType
1669        if ( upType != null )
1670        {
1671            buffer[pos++] = Serialize.TRUE;
1672            pos = Serialize.serialize( upTypeBytes, buffer, pos );
1673        }
1674        else
1675        {
1676            buffer[pos++] = Serialize.FALSE;
1677        }
1678
1679        // Write the isHR flag
1680        if ( value.isHumanReadable() )
1681        {
1682            buffer[pos++] = Serialize.TRUE;
1683        }
1684        else
1685        {
1686            buffer[pos++] = Serialize.FALSE;
1687        }
1688
1689        // Write the upValue
1690        if ( value.isHumanReadable() )
1691        {
1692            pos = value.serialize( buffer, pos );
1693        }
1694
1695        // Write the hash code
1696        pos = Serialize.serialize( h, buffer, pos );
1697
1698        return pos;
1699    }
1700
1701
1702    /**
1703     * Deserialize an AVA from a byte[], starting at a given position
1704     * 
1705     * @param buffer The buffer containing the AVA
1706     * @param pos The position in the buffer
1707     * @return The new position
1708     * @throws IOException If the serialized value is not an AVA
1709     * @throws LdapInvalidAttributeValueException If the serialized AVA is invalid
1710     */
1711    public int deserialize( byte[] buffer, int pos ) throws IOException, LdapInvalidAttributeValueException
1712    {
1713        if ( ( pos < 0 ) || ( pos >= buffer.length ) )
1714        {
1715            throw new ArrayIndexOutOfBoundsException();
1716        }
1717
1718        // Read the upName value, if it's not null
1719        boolean hasUpName = Serialize.deserializeBoolean( buffer, pos );
1720        pos++;
1721
1722        if ( hasUpName )
1723        {
1724            byte[] wrappedValueBytes = Serialize.deserializeBytes( buffer, pos );
1725            pos += 4 + wrappedValueBytes.length;
1726            upName = Strings.utf8ToString( wrappedValueBytes );
1727        }
1728
1729        // Read the upType value, if it's not null
1730        boolean hasUpType = Serialize.deserializeBoolean( buffer, pos );
1731        pos++;
1732
1733        if ( hasUpType )
1734        {
1735            byte[] upTypeBytes = Serialize.deserializeBytes( buffer, pos );
1736            pos += 4 + upTypeBytes.length;
1737            upType = Strings.utf8ToString( upTypeBytes );
1738        }
1739
1740        // Update the AtributeType
1741        if ( schemaManager != null )
1742        {
1743            if ( !Strings.isEmpty( upType ) )
1744            {
1745                attributeType = schemaManager.getAttributeType( upType );
1746            }
1747            else
1748            {
1749                attributeType = schemaManager.getAttributeType( normType );
1750            }
1751        }
1752
1753        if ( attributeType != null )
1754        {
1755            normType = attributeType.getOid();
1756        }
1757        else
1758        {
1759            normType = upType;
1760        }
1761
1762        // Read the isHR flag
1763        boolean isHR = Serialize.deserializeBoolean( buffer, pos );
1764        pos++;
1765
1766        if ( isHR )
1767        {
1768            // Read the upValue
1769            value = Value.createValue( attributeType );
1770            pos = value.deserialize( buffer, pos );
1771        }
1772
1773        // Read the hashCode
1774        h = Serialize.deserializeInt( buffer, pos );
1775        pos += 4;
1776
1777        return pos;
1778    }
1779
1780
1781    /**
1782     * 
1783     * An Ava is composed of  a type and a value.
1784     * The data are stored following the structure :
1785     * <ul>
1786     *   <li>
1787     *     <b>upName</b> The User provided ATAV
1788     *   </li>
1789     *   <li>
1790     *     <b>start</b> The position of this ATAV in the Dn
1791     *   </li>
1792     *   <li>
1793     *     <b>length</b> The ATAV length
1794     *   </li>
1795     *   <li>
1796     *     <b>upType</b> The user Provided Type
1797     *   </li>
1798     *   <li>
1799     *     <b>normType</b> The normalized AttributeType
1800     *   </li>
1801     *   <li>
1802     *     <b>isHR</b> Tells if the value is a String or not
1803     *   </li>
1804     * </ul>
1805     * <br>
1806     * if the value is a String :
1807     * <ul>
1808     *   <li>
1809     *     <b>value</b> The value
1810     *   </li>
1811     * </ul>
1812     * <br>
1813     * if the value is binary :
1814     * <ul>
1815     *   <li>
1816     *     <b>valueLength</b>
1817     *   </li>
1818     *   <li>
1819     *     <b>value</b> The value
1820     *   </li>
1821     * </ul>
1822     * 
1823     * @see Externalizable#readExternal(ObjectInput)
1824     * 
1825     * @throws IOException If the Ava can't be written in the stream
1826     */
1827    @Override
1828    public void writeExternal( ObjectOutput out ) throws IOException
1829    {
1830        if ( Strings.isEmpty( upName )
1831            || Strings.isEmpty( upType )
1832            || Strings.isEmpty( normType )
1833            || ( value.isNull() ) )
1834        {
1835            String message;
1836
1837            if ( Strings.isEmpty( upName ) )
1838            {
1839                message = I18n.err( I18n.ERR_13616_CANNOT_SERIALIZE_AVA_UPNAME_NULL );
1840            }
1841            else if ( Strings.isEmpty( upType ) )
1842            {
1843                message = I18n.err( I18n.ERR_13617_CANNOT_SERIALIZE_AVA_UPTYPE_NULL );
1844            }
1845            else if ( Strings.isEmpty( normType ) )
1846            {
1847                message = I18n.err( I18n.ERR_13618_CANNOT_SERIALIZE_AVA_NORMTYPE_NULL );
1848            }
1849            else
1850            {
1851                message = I18n.err( I18n.ERR_13619_CANNOT_SERIALIZE_AVA_VALUE_NULL );
1852            }
1853
1854            LOG.error( message );
1855            throw new IOException( message );
1856        }
1857
1858        if ( upName != null )
1859        {
1860            out.writeBoolean( true );
1861            out.writeUTF( upName );
1862        }
1863        else
1864        {
1865            out.writeBoolean( false );
1866        }
1867
1868        if ( upType != null )
1869        {
1870            out.writeBoolean( true );
1871            out.writeUTF( upType );
1872        }
1873        else
1874        {
1875            out.writeBoolean( false );
1876        }
1877
1878        if ( normType != null )
1879        {
1880            out.writeBoolean( true );
1881            out.writeUTF( normType );
1882        }
1883        else
1884        {
1885            out.writeBoolean( false );
1886        }
1887
1888        boolean isHR = value.isHumanReadable();
1889
1890        out.writeBoolean( isHR );
1891
1892        value.writeExternal( out );
1893
1894        // Write the hashCode
1895        out.writeInt( h );
1896
1897        out.flush();
1898    }
1899
1900
1901    /**
1902     * We read back the data to create a new ATAV. The structure
1903     * read is exposed in the {@link Ava#writeExternal(ObjectOutput)}
1904     * method
1905     * 
1906     * @see Externalizable#readExternal(ObjectInput)
1907     * 
1908     * @throws IOException If the Ava can't b written to the stream
1909     * @throws ClassNotFoundException If we can't deserialize an Ava from the stream
1910     */
1911    @Override
1912    public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
1913    {
1914        boolean hasUpName = in.readBoolean();
1915
1916        if ( hasUpName )
1917        {
1918            upName = in.readUTF();
1919        }
1920
1921        boolean hasUpType = in.readBoolean();
1922
1923        if ( hasUpType )
1924        {
1925            upType = in.readUTF();
1926        }
1927
1928        boolean hasNormType = in.readBoolean();
1929
1930        if ( hasNormType )
1931        {
1932            normType = in.readUTF();
1933        }
1934
1935        if ( schemaManager != null )
1936        {
1937            if ( !Strings.isEmpty( upType ) )
1938            {
1939                attributeType = schemaManager.getAttributeType( upType );
1940            }
1941            else
1942            {
1943                attributeType = schemaManager.getAttributeType( normType );
1944            }
1945        }
1946
1947        in.readBoolean();
1948
1949        value = Value.deserialize( attributeType, in );
1950
1951        h = in.readInt();
1952    }
1953
1954
1955    /**
1956     * Tells if the Ava is schema aware or not.
1957     * 
1958     * @return <tt>true</tt> if the Ava is schema aware
1959     */
1960    public boolean isSchemaAware()
1961    {
1962        return attributeType != null;
1963    }
1964
1965
1966    /**
1967     * @return the attributeType
1968     */
1969    public AttributeType getAttributeType()
1970    {
1971        return attributeType;
1972    }
1973
1974
1975    private int compareValues( Ava that )
1976    {
1977        int comp;
1978
1979        if ( value.isHumanReadable() )
1980        {
1981            comp = value.compareTo( that.value );
1982
1983            return comp;
1984        }
1985        else
1986        {
1987            byte[] bytes1 = value.getBytes();
1988            byte[] bytes2 = that.value.getBytes();
1989
1990            for ( int pos = 0; pos < bytes1.length; pos++ )
1991            {
1992                int v1 = bytes1[pos] & 0x00FF;
1993                int v2 = bytes2[pos] & 0x00FF;
1994
1995                if ( v1 > v2 )
1996                {
1997                    return 1;
1998                }
1999                else if ( v2 > v1 )
2000                {
2001                    return -1;
2002                }
2003            }
2004
2005            return 0;
2006        }
2007
2008    }
2009
2010
2011    /**
2012     * @see Comparable#compareTo(Object)
2013     */
2014    @Override
2015    public int compareTo( Ava that )
2016    {
2017        if ( that == null )
2018        {
2019            return 1;
2020        }
2021
2022        int comp;
2023
2024        if ( schemaManager == null )
2025        {
2026            // Compare the ATs
2027            comp = normType.compareTo( that.normType );
2028
2029            if ( comp != 0 )
2030            {
2031                return comp;
2032            }
2033
2034            // and compare the values
2035            if ( value == null )
2036            {
2037                if ( that.value == null )
2038                {
2039                    return 0;
2040                }
2041                else
2042                {
2043                    return -1;
2044                }
2045            }
2046            else
2047            {
2048                if ( that.value == null )
2049                {
2050                    return 1;
2051                }
2052                else
2053                {
2054                    comp = value.compareTo( ( Value ) that.value );
2055
2056                    return comp;
2057                }
2058            }
2059        }
2060        else
2061        {
2062            if ( that.schemaManager == null )
2063            {
2064                // Problem : we will apply the current Ava SchemaManager to the given Ava
2065                try
2066                {
2067                    that.apply( schemaManager );
2068                }
2069                catch ( LdapInvalidDnException lide )
2070                {
2071                    return 1;
2072                }
2073            }
2074
2075            // First compare the AT OID
2076            comp = attributeType.getOid().compareTo( that.attributeType.getOid() );
2077
2078            if ( comp != 0 )
2079            {
2080                return comp;
2081            }
2082
2083            // Now, compare the two values using the ordering matchingRule comparator, if any
2084            MatchingRule orderingMR = attributeType.getOrdering();
2085
2086            if ( orderingMR != null )
2087            {
2088                LdapComparator<Object> comparator = ( LdapComparator<Object> ) orderingMR.getLdapComparator();
2089
2090                if ( comparator != null )
2091                {
2092                    comp = value.compareTo( that.value );
2093
2094                    return comp;
2095                }
2096                else
2097                {
2098                    comp = compareValues( that );
2099
2100                    return comp;
2101                }
2102            }
2103            else
2104            {
2105                comp = compareValues( that );
2106
2107                return comp;
2108            }
2109        }
2110    }
2111    
2112    
2113    /**
2114     * A String representation of an Ava, as provided by the user.
2115     *
2116     * @return A string representing an Ava
2117     */
2118    @Override
2119    public String toString()
2120    {
2121        return upName;
2122    }
2123}