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