View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    https://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.directory.api.ldap.model.entry;
21  
22  
23  import java.io.Externalizable;
24  import java.io.IOException;
25  import java.io.ObjectInput;
26  import java.io.ObjectOutput;
27  import java.util.Arrays;
28  
29  import org.apache.directory.api.i18n.I18n;
30  import org.apache.directory.api.ldap.model.exception.LdapException;
31  import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
32  import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
33  import org.apache.directory.api.ldap.model.schema.AttributeType;
34  import org.apache.directory.api.ldap.model.schema.LdapComparator;
35  import org.apache.directory.api.ldap.model.schema.LdapSyntax;
36  import org.apache.directory.api.ldap.model.schema.MatchingRule;
37  import org.apache.directory.api.ldap.model.schema.Normalizer;
38  import org.apache.directory.api.ldap.model.schema.SyntaxChecker;
39  import org.apache.directory.api.ldap.model.schema.comparators.StringComparator;
40  import org.apache.directory.api.ldap.model.schema.normalizers.NoOpNormalizer;
41  import org.apache.directory.api.util.Serialize;
42  import org.apache.directory.api.util.Strings;
43  import org.slf4j.Logger;
44  import org.slf4j.LoggerFactory;
45  
46  
47  /**
48   * A Class for wrapping attribute values stored into an Entry Attribute, or a AVA.
49   * 
50   * We keep the value as byte[] unless we need to convert them to a String (if we have
51   * a HR Value).
52   * 
53   * The serialized Value will be stored as :
54   * 
55   * <pre>
56   *  +---------+
57   *  | boolean | isHR flag
58   *  +---------+
59   *  | boolean | TRUE if the value is not null, FALSE otherwise
60   *  +---------+
61   * [|   int   |]  If the previous flag is TRUE, the length of the value
62   * [+---------+]
63   * [| byte[]  |] The value itself
64   * [+---------+]
65   *  | boolean | TRUE if we have a prepared String
66   *  +---------+
67   * [| String  |] The prepared String if we have it
68   * [+---------+]
69   * </pre>
70   *
71   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
72   */
73  public class Value implements Cloneable, Externalizable, Comparable<Value>
74  {
75      /** Used for serialization */
76      private static final long serialVersionUID = 2L;
77  
78      /** logger for reporting errors that might not be handled properly upstream */
79      private static final Logger LOG = LoggerFactory.getLogger( Value.class );
80  
81      /** reference to the attributeType associated with the value */
82      private transient AttributeType attributeType;
83  
84      /** the User Provided value if it's a String */
85      private String upValue;
86  
87      /** the prepared representation of the user provided value if it's a String */
88      private String normValue;
89  
90      /** The computed hashcode. We don't want to compute it each time the hashcode() method is called */
91      private volatile int h;
92  
93      /** The UTF-8 bytes for this value (we use the UP value) */
94      private byte[] bytes;
95  
96      /** Two flags used to tell if the value is HR or not in serialization */
97      private boolean isHR = true;
98      
99      /** 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 }