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   *    http://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.name;
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.entry.BinaryValue;
31  import org.apache.directory.api.ldap.model.entry.StringValue;
32  import org.apache.directory.api.ldap.model.entry.Value;
33  import org.apache.directory.api.ldap.model.exception.LdapException;
34  import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
35  import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
36  import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
37  import org.apache.directory.api.ldap.model.schema.AttributeType;
38  import org.apache.directory.api.ldap.model.schema.LdapComparator;
39  import org.apache.directory.api.ldap.model.schema.MatchingRule;
40  import org.apache.directory.api.ldap.model.schema.SchemaManager;
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 Attribute Type And Value, which is the basis of all Rdn. It contains a
49   * type, and a value. The type must not be case sensitive. Superfluous leading
50   * and trailing spaces MUST have been trimmed before. The value MUST be in UTF8
51   * format, according to RFC 2253. If the type is in OID form, then the value
52   * must be a hexadecimal string prefixed by a '#' character. Otherwise, the
53   * string must respect the RC 2253 grammar.
54   *
55   * We will also keep a User Provided form of the AVA (Attribute Type And Value),
56   * called upName.
57   *
58   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
59   */
60  public class Ava implements Externalizable, Cloneable, Comparable<Ava>
61  {
62      /**
63       * Declares the Serial Version Uid.
64       *
65       * @see <a
66       *      href="http://c2.com/cgi/wiki?AlwaysDeclareSerialVersionUid">Always
67       *      Declare Serial Version Uid</a>
68       */
69      private static final long serialVersionUID = 1L;
70  
71      /** The LoggerFactory used by this class */
72      private static final Logger LOG = LoggerFactory.getLogger( Ava.class );
73  
74      /** The normalized Name type */
75      private String normType;
76  
77      /** The user provided Name type */
78      private String upType;
79  
80      /** The value. It can be a String or a byte array */
81      private Value<?> value;
82  
83      /** The user provided Ava */
84      private String upName;
85  
86      /** The attributeType if the Ava is schemaAware */
87      private AttributeType attributeType;
88  
89      /** the schema manager */
90      private SchemaManager schemaManager;
91  
92      /** The computed hashcode */
93      private volatile int h;
94  
95  
96      /**
97       * Constructs an empty Ava
98       */
99      public Ava()
100     {
101         this( null );
102     }
103 
104 
105     /**
106      * Constructs an empty schema aware Ava.
107      * 
108      * @param schemaManager The SchemaManager instance
109      */
110     public Ava( SchemaManager schemaManager )
111     {
112         normType = null;
113         upType = null;
114         value = null;
115         upName = "";
116         this.schemaManager = schemaManager;
117         this.attributeType = null;
118     }
119 
120 
121     /**
122      * Construct an Ava containing a binary value.
123      * <p>
124      * Note that the upValue should <b>not</b> be null or empty, or resolve
125      * to an empty string after having trimmed it.
126      *
127      * @param upType The User Provided type
128      * @param upValue The User Provided binary value
129      * 
130      * @throws LdapInvalidDnException If the given type or value are invalid
131      */
132     public Ava( String upType, byte[] upValue ) throws LdapInvalidDnException
133     {
134         this( null, upType, upValue );
135     }
136 
137 
138     /**
139      * Construct a schema aware Ava containing a binary value. The AttributeType
140      * and value will be normalized accordingly to the given SchemaManager.
141      * <p>
142      * Note that the upValue should <b>not</b> be null or empty, or resolve
143      * to an empty string after having trimmed it.
144      *
145      * @param schemaManager The SchemaManager instance
146      * @param upType The User Provided type
147      * @param upValue The User Provided binary value
148      * 
149      * @throws LdapInvalidDnException If the given type or value are invalid
150      */
151     public Ava( SchemaManager schemaManager, String upType, byte[] upValue ) throws LdapInvalidDnException
152     {
153         if ( schemaManager != null )
154         {
155             this.schemaManager = schemaManager;
156 
157             try
158             {
159                 attributeType = schemaManager.lookupAttributeTypeRegistry( upType );
160             }
161             catch ( LdapException le )
162             {
163                 String message = I18n.err( I18n.ERR_04188 );
164                 LOG.error( message );
165                 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le );
166             }
167 
168             try
169             {
170                 createAva( schemaManager, upType, new BinaryValue( attributeType, upValue ) );
171             }
172             catch ( LdapInvalidAttributeValueException liave )
173             {
174                 String message = I18n.err( I18n.ERR_04188 );
175                 LOG.error( message );
176                 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, liave );
177             }
178         }
179         else
180         {
181             createAva( upType, new BinaryValue( upValue ) );
182         }
183     }
184 
185 
186     /**
187      * Construct an Ava with a String value.
188      * <p>
189      * Note that the upValue should <b>not</b> be null or empty, or resolve
190      * to an empty string after having trimmed it.
191      *
192      * @param upType The User Provided type
193      * @param upValue The User Provided String value
194      * 
195      * @throws LdapInvalidDnException If the given type or value are invalid
196      */
197     public Ava( String upType, String upValue ) throws LdapInvalidDnException
198     {
199         this( null, upType, upValue );
200     }
201 
202 
203     /**
204      * Construct a schema aware Ava with a String value.
205      * <p>
206      * Note that the upValue should <b>not</b> be null or empty, or resolve
207      * to an empty string after having trimmed it.
208      *
209      * @param schemaManager The SchemaManager instance
210      * @param upType The User Provided type
211      * @param upValue The User Provided String value
212      * 
213      * @throws LdapInvalidDnException If the given type or value are invalid
214      */
215     public Ava( SchemaManager schemaManager, String upType, String upValue ) throws LdapInvalidDnException
216     {
217         if ( schemaManager != null )
218         {
219             this.schemaManager = schemaManager;
220 
221             try
222             {
223                 attributeType = schemaManager.lookupAttributeTypeRegistry( upType );
224             }
225             catch ( LdapException le )
226             {
227                 String message = I18n.err( I18n.ERR_04188 );
228                 LOG.error( message );
229                 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le );
230             }
231 
232             try
233             {
234                 createAva( schemaManager, upType, new StringValue( attributeType, upValue ) );
235             }
236             catch ( LdapInvalidAttributeValueException liave )
237             {
238                 String message = I18n.err( I18n.ERR_04188 );
239                 LOG.error( message );
240                 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, liave );
241             }
242         }
243         else
244         {
245             createAva( upType, new StringValue( upValue ) );
246         }
247     }
248 
249 
250     /**
251      * Construct an Ava. The type and value are normalized :
252      * <li> the type is trimmed and lowercased </li>
253      * <li> the value is trimmed </li>
254      * <p>
255      * Note that the upValue should <b>not</b> be null or empty, or resolved
256      * to an empty string after having trimmed it.
257      *
258      * @param schemaManager The SchemaManager
259      * @param upType The User Provided type
260      * @param normType The normalized type
261      * @param value The value
262      * 
263      * @throws LdapInvalidDnException If the given type or value are invalid
264      */
265     // WARNING : The protection level is left unspecified intentionally.
266     // We need this method to be visible from the DnParser class, but not
267     // from outside this package.
268     /* Unspecified protection */Ava( SchemaManager schemaManager, String upType, String normType, Value<?> value )
269         throws LdapInvalidDnException
270     {
271         this.upType = upType;
272         this.normType = normType;
273         this.value = value;
274         upName = this.upType + '=' + ( this.value == null ? "" : this.value.getString() );
275 
276         if ( schemaManager != null )
277         {
278             apply( schemaManager );
279         }
280 
281         hashCode();
282     }
283 
284 
285     /**
286      * Construct an Ava. The type and value are normalized :
287      * <li> the type is trimmed and lowercased </li>
288      * <li> the value is trimmed </li>
289      * <p>
290      * Note that the upValue should <b>not</b> be null or empty, or resolved
291      * to an empty string after having trimmed it.
292      *
293      * @param upType The User Provided type
294      * @param normType The normalized type
295      * @param value The User Provided value
296      * @param upName The User Provided name (may be escaped)
297      * 
298      * @throws LdapInvalidDnException If the given type or value are invalid
299      */
300     // WARNING : The protection level is left unspecified intentionally.
301     // We need this method to be visible from the DnParser class, but not
302     // from outside this package.
303     /* Unspecified protection */Ava( String upType, String normType, Value<?> value, String upName )
304         throws LdapInvalidDnException
305     {
306         this( null, upType, normType, value, upName );
307     }
308     
309     
310     /**
311      * Construct an Ava. The type and value are normalized :
312      * <li> the type is trimmed and lowercased </li>
313      * <li> the value is trimmed </li>
314      * <p>
315      * Note that the upValue should <b>not</b> be null or empty, or resolved
316      * to an empty string after having trimmed it.
317      *
318      * @param attributeType The AttributeType for this value
319      * @param upType The User Provided type
320      * @param normType The normalized type
321      * @param value The value
322      * @param upName The User Provided name (may be escaped)
323      * 
324      * @throws LdapInvalidDnException If the given type or value are invalid
325      */
326     // WARNING : The protection level is left unspecified intentionally.
327     // We need this method to be visible from the DnParser class, but not
328     // from outside this package.
329     /* Unspecified protection */Ava( AttributeType attributeType, String upType, String normType, Value<?> value, String upName )
330         throws LdapInvalidDnException
331     {
332         this.attributeType = attributeType;
333         String upTypeTrimmed = Strings.trim( upType );
334         String normTypeTrimmed = Strings.trim( normType );
335 
336         if ( Strings.isEmpty( upTypeTrimmed ) )
337         {
338             if ( Strings.isEmpty( normTypeTrimmed ) )
339             {
340                 String message = I18n.err( I18n.ERR_04188 );
341                 LOG.error( message );
342                 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message );
343             }
344             else
345             {
346                 // In this case, we will use the normType instead
347                 this.normType = Strings.lowerCaseAscii( normTypeTrimmed );
348                 this.upType = normType;
349             }
350         }
351         else if ( Strings.isEmpty( normTypeTrimmed ) )
352         {
353             // In this case, we will use the upType instead
354             this.normType = Strings.lowerCaseAscii( upTypeTrimmed );
355             this.upType = upType;
356         }
357         else
358         {
359             this.normType = Strings.lowerCaseAscii( normTypeTrimmed );
360             this.upType = upType;
361         }
362 
363         this.value = value;
364         this.upName = upName;
365         hashCode();
366     }
367 
368 
369     /**
370      * Construct a schema aware Ava. The AttributeType and value will be checked accordingly
371      * to the SchemaManager.
372      * <p>
373      * Note that the upValue should <b>not</b> be null or empty, or resolve
374      * to an empty string after having trimmed it.
375      *
376      * @param schemaManager The SchemaManager instance
377      * @param upType The User Provided type
378      * @param value The value
379      * 
380      * @throws LdapInvalidDnException If the given type or value are invalid
381      */
382     private void createAva( SchemaManager schemaManager, String upType, Value<?> value )
383         throws LdapInvalidDnException
384     {
385         normType = attributeType.getOid();
386         this.upType = upType;
387         this.value = value;
388         upName = this.upType + '=' + ( value == null ? "" : Rdn.escapeValue( value.getString() ) );
389         hashCode();
390     }
391 
392 
393     /**
394      * Construct an Ava. The type and value are normalized :
395      * <li> the type is trimmed and lowercased </li>
396      * <li> the value is trimmed </li>
397      * <p>
398      * Note that the upValue should <b>not</b> be null or empty, or resolved
399      * to an empty string after having trimmed it.
400      *
401      * @param upType The User Provided type
402      * @param upValue The User Provided value
403      * 
404      * @throws LdapInvalidDnException If the given type or value are invalid
405      */
406     private void createAva( String upType, Value<?> upValue ) throws LdapInvalidDnException
407     {
408         String upTypeTrimmed = Strings.trim( upType );
409         String normTypeTrimmed = Strings.trim( normType );
410 
411         if ( Strings.isEmpty( upTypeTrimmed ) )
412         {
413             if ( Strings.isEmpty( normTypeTrimmed ) )
414             {
415                 String message = I18n.err( I18n.ERR_04188 );
416                 LOG.error( message );
417                 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message );
418             }
419             else
420             {
421                 // In this case, we will use the normType instead
422                 this.normType = Strings.lowerCaseAscii( normTypeTrimmed );
423                 this.upType = normType;
424             }
425         }
426         else if ( Strings.isEmpty( normTypeTrimmed ) )
427         {
428             // In this case, we will use the upType instead
429             this.normType = Strings.lowerCaseAscii( upTypeTrimmed );
430             this.upType = upType;
431         }
432         else
433         {
434             this.normType = Strings.lowerCaseAscii( normTypeTrimmed );
435             this.upType = upType;
436 
437         }
438 
439         value = upValue;
440 
441         upName = this.upType + '=' + ( value == null ? "" : Rdn.escapeValue( value.getString() ) );
442         hashCode();
443     }
444 
445 
446     /**
447      * Apply a SchemaManager to the Ava. It will normalize the Ava.<br>
448      * If the Ava already had a SchemaManager, then the new SchemaManager will be
449      * used instead.
450      * 
451      * @param schemaManager The SchemaManager instance to use
452      * @throws LdapInvalidDnException If the Ava can't be normalized accordingly
453      * to the given SchemaManager
454      */
455     public void apply( SchemaManager schemaManager ) throws LdapInvalidDnException
456     {
457         if ( schemaManager != null )
458         {
459             this.schemaManager = schemaManager;
460 
461             AttributeType tmpAttributeType = null;
462 
463             try
464             {
465                 tmpAttributeType = schemaManager.lookupAttributeTypeRegistry( normType );
466             }
467             catch ( LdapException le )
468             {
469                 if ( schemaManager.isRelaxed() )
470                 {
471                     // No attribute in the schema, but the schema is relaxed : get out
472                     return;
473                 }
474                 else
475                 {
476                     String message = I18n.err( I18n.ERR_04188 );
477                     LOG.error( message );
478                     throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le );
479                 }
480             }
481 
482             if ( this.attributeType == tmpAttributeType )
483             {
484                 // No need to normalize again
485                 return;
486             }
487             else
488             {
489                 this.attributeType = tmpAttributeType;
490             }
491 
492             normType = tmpAttributeType.getOid();
493 
494             try
495             {
496                 this.value.apply( tmpAttributeType );
497             }
498             catch ( LdapException le )
499             {
500                 String message = I18n.err( I18n.ERR_04188 );
501                 LOG.error( message );
502                 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le );
503             }
504 
505             hashCode();
506         }
507     }
508 
509 
510     /**
511      * Get the normalized type of a Ava
512      *
513      * @return The normalized type
514      */
515     public String getNormType()
516     {
517         return normType;
518     }
519 
520 
521     /**
522      * Get the user provided type of a Ava
523      *
524      * @return The user provided type
525      */
526     public String getType()
527     {
528         return upType;
529     }
530 
531 
532     /**
533      * Get the Value of a Ava
534      *
535      * @return The value
536      */
537     public Value<?> getValue()
538     {
539         return value.clone();
540     }
541 
542 
543     /**
544      * Get the normalized Name of a Ava
545      *
546      * @return The name
547      */
548     public String getNormName()
549     {
550         return normalize();
551     }
552 
553 
554     /**
555      * Get the user provided form of this attribute type and value
556      *
557      * @return The user provided form of this ava
558      */
559     public String getName()
560     {
561         return upName;
562     }
563 
564 
565     /**
566      * Implements the cloning.
567      *
568      * @return a clone of this object
569      */
570     @Override
571     public Ava clone()
572     {
573         try
574         {
575             Ava clone = ( Ava ) super.clone();
576             clone.value = value.clone();
577 
578             return clone;
579         }
580         catch ( CloneNotSupportedException cnse )
581         {
582             throw new Error( "Assertion failure", cnse );
583         }
584     }
585 
586 
587     /**
588      * A Normalized String representation of a Ava :
589      * <ul>
590      * <li>type is trimed and lowercased</li>
591      * <li>value is trimed and lowercased, and special characters are escaped if needed.</li>
592      * </ul>
593      *
594      * @return A normalized string representing an Ava
595      */
596     public String normalize()
597     {
598         if ( value.isHumanReadable() )
599         {
600             // The result will be gathered in a stringBuilder
601             StringBuilder sb = new StringBuilder();
602 
603             // First, store the type and the '=' char
604             sb.append( normType ).append( '=' );
605 
606             String normalizedValue = ( String ) value.getNormValue();
607 
608             if ( ( normalizedValue != null ) && ( normalizedValue.length() > 0 ) )
609             {
610                 sb.append( Rdn.escapeValue( normalizedValue ) );
611             }
612 
613             return sb.toString();
614         }
615         else
616         {
617             return normType + "=#"
618                 + Strings.dumpHexPairs( value.getBytes() );
619         }
620     }
621 
622 
623     /**
624      * Gets the hashcode of this object.
625      *
626      * @see java.lang.Object#hashCode()
627      * @return The instance hash code
628      */
629     @Override
630     public int hashCode()
631     {
632         if ( h == 0 )
633         {
634             h = 37;
635 
636             h = h * 17 + ( normType != null ? normType.hashCode() : 0 );
637             h = h * 17 + ( value != null ? value.hashCode() : 0 );
638         }
639 
640         return h;
641     }
642 
643 
644     /**
645      * @see Object#equals(Object)
646      */
647     @Override
648     public boolean equals( Object obj )
649     {
650         if ( this == obj )
651         {
652             return true;
653         }
654 
655         if ( !( obj instanceof Ava ) )
656         {
657             return false;
658         }
659 
660         Ava instance = ( Ava ) obj;
661 
662         // Compare the type
663         if ( normType == null )
664         {
665             if ( instance.normType != null )
666             {
667                 return false;
668             }
669         }
670         else
671         {
672             if ( !normType.equals( instance.normType ) )
673             {
674                 return false;
675             }
676         }
677 
678         // Compare the values
679         if ( value.isNull() )
680         {
681             return instance.value.isNull();
682         }
683         else
684         {
685             if ( schemaManager != null )
686             {
687                 MatchingRule equalityMatchingRule = attributeType.getEquality();
688 
689                 if ( equalityMatchingRule != null )
690                 {
691                     return equalityMatchingRule.getLdapComparator().compare( value.getValue(),
692                         instance.value.getValue() ) == 0;
693                 }
694                 else
695                 {
696                     // No Equality MR, use a direct comparison
697                     if ( value instanceof BinaryValue )
698                     {
699                         return Arrays.equals( value.getBytes(), instance.value.getBytes() );
700                     }
701                     else
702                     {
703                         return value.getString().equals( instance.value.getString() );
704                     }
705                 }
706             }
707             else
708             {
709                 return value.equals( instance.value );
710             }
711         }
712     }
713 
714 
715     /**
716      * Serialize the AVA into a buffer at the given position.
717      * 
718      * @param buffer The buffer which will contain the serialized Ava
719      * @param pos The position in the buffer for the serialized value
720      * @return The new position in the buffer
721      * @throws IOException If teh serialization failed
722      */
723     public int serialize( byte[] buffer, int pos ) throws IOException
724     {
725         if ( Strings.isEmpty( upName )
726             || Strings.isEmpty( upType )
727             || Strings.isEmpty( normType )
728             || ( value.isNull() ) )
729         {
730             String message = "Cannot serialize an wrong ATAV, ";
731 
732             if ( Strings.isEmpty( upName ) )
733             {
734                 message += "the upName should not be null or empty";
735             }
736             else if ( Strings.isEmpty( upType ) )
737             {
738                 message += "the upType should not be null or empty";
739             }
740             else if ( Strings.isEmpty( normType ) )
741             {
742                 message += "the normType should not be null or empty";
743             }
744             else if ( value.isNull() )
745             {
746                 message += "the value should not be null";
747             }
748 
749             LOG.error( message );
750             throw new IOException( message );
751         }
752 
753         int length = 0;
754 
755         // The upName
756         byte[] upNameBytes = null;
757 
758         if ( upName != null )
759         {
760             upNameBytes = Strings.getBytesUtf8( upName );
761             length += 1 + 4 + upNameBytes.length;
762         }
763 
764         // The upType
765         byte[] upTypeBytes = null;
766 
767         if ( upType != null )
768         {
769             upTypeBytes = Strings.getBytesUtf8( upType );
770             length += 1 + 4 + upTypeBytes.length;
771         }
772 
773         // The normType
774         byte[] normTypeBytes = null;
775 
776         if ( normType != null )
777         {
778             normTypeBytes = Strings.getBytesUtf8( normType );
779             length += 1 + 4 + normTypeBytes.length;
780         }
781 
782         // Is HR
783         length++;
784 
785         // The hash code
786         length += 4;
787 
788         // Check that we will be able to store the data in the buffer
789         if ( buffer.length - pos < length )
790         {
791             throw new ArrayIndexOutOfBoundsException();
792         }
793 
794         // Write the upName
795         if ( upName != null )
796         {
797             buffer[pos++] = Serialize.TRUE;
798             pos = Serialize.serialize( upNameBytes, buffer, pos );
799         }
800         else
801         {
802             buffer[pos++] = Serialize.FALSE;
803         }
804 
805         // Write the upType
806         if ( upType != null )
807         {
808             buffer[pos++] = Serialize.TRUE;
809             pos = Serialize.serialize( upTypeBytes, buffer, pos );
810         }
811         else
812         {
813             buffer[pos++] = Serialize.FALSE;
814         }
815 
816         // Write the normType
817         if ( normType != null )
818         {
819             buffer[pos++] = Serialize.TRUE;
820             pos = Serialize.serialize( normTypeBytes, buffer, pos );
821         }
822         else
823         {
824             buffer[pos++] = Serialize.FALSE;
825         }
826 
827         // Write the isHR flag
828         if ( value.isHumanReadable() )
829         {
830             buffer[pos++] = Serialize.TRUE;
831         }
832         else
833         {
834             buffer[pos++] = Serialize.FALSE;
835         }
836 
837         // Write the upValue
838         if ( value.isHumanReadable() )
839         {
840             pos = ( ( StringValue ) value ).serialize( buffer, pos );
841         }
842 
843         // Write the hash code
844         pos = Serialize.serialize( h, buffer, pos );
845 
846         return pos;
847     }
848 
849 
850     /**
851      * Deserialize an AVA from a byte[], starting at a given position
852      * 
853      * @param buffer The buffer containing the AVA
854      * @param pos The position in the buffer
855      * @return The new position
856      * @throws IOException If the serialized value is not an AVA
857      * @throws LdapInvalidAttributeValueException If the serialized AVA is invalid
858      */
859     public int deserialize( byte[] buffer, int pos ) throws IOException, LdapInvalidAttributeValueException
860     {
861         if ( ( pos < 0 ) || ( pos >= buffer.length ) )
862         {
863             throw new ArrayIndexOutOfBoundsException();
864         }
865 
866         // Read the upName value, if it's not null
867         boolean hasUpName = Serialize.deserializeBoolean( buffer, pos );
868         pos++;
869 
870         if ( hasUpName )
871         {
872             byte[] wrappedValueBytes = Serialize.deserializeBytes( buffer, pos );
873             pos += 4 + wrappedValueBytes.length;
874             upName = Strings.utf8ToString( wrappedValueBytes );
875         }
876 
877         // Read the upType value, if it's not null
878         boolean hasUpType = Serialize.deserializeBoolean( buffer, pos );
879         pos++;
880 
881         if ( hasUpType )
882         {
883             byte[] upTypeBytes = Serialize.deserializeBytes( buffer, pos );
884             pos += 4 + upTypeBytes.length;
885             upType = Strings.utf8ToString( upTypeBytes );
886         }
887 
888         // Read the normType value, if it's not null
889         boolean hasNormType = Serialize.deserializeBoolean( buffer, pos );
890         pos++;
891 
892         if ( hasNormType )
893         {
894             byte[] normTypeBytes = Serialize.deserializeBytes( buffer, pos );
895             pos += 4 + normTypeBytes.length;
896             normType = Strings.utf8ToString( normTypeBytes );
897         }
898 
899         // Update the AtributeType
900         if ( schemaManager != null )
901         {
902             if ( !Strings.isEmpty( upType ) )
903             {
904                 attributeType = schemaManager.getAttributeType( upType );
905             }
906             else
907             {
908                 attributeType = schemaManager.getAttributeType( normType );
909             }
910         }
911 
912         // Read the isHR flag
913         boolean isHR = Serialize.deserializeBoolean( buffer, pos );
914         pos++;
915 
916         if ( isHR )
917         {
918             // Read the upValue
919             value = new StringValue( attributeType );
920             pos = ( ( StringValue ) value ).deserialize( buffer, pos );
921         }
922 
923         // Read the hashCode
924         h = Serialize.deserializeInt( buffer, pos );
925         pos += 4;
926 
927         return pos;
928     }
929 
930 
931     /**
932      * 
933      * An Ava is composed of  a type and a value.
934      * The data are stored following the structure :
935      * <ul>
936      *   <li>
937      *     <b>upName</b> The User provided ATAV
938      *   </li>
939      *   <li>
940      *     <b>start</b> The position of this ATAV in the Dn
941      *   </li>
942      *   <li>
943      *     <b>length</b> The ATAV length
944      *   </li>
945      *   <li>
946      *     <b>upType</b> The user Provided Type
947      *   </li>
948      *   <li>
949      *     <b>normType</b> The normalized AttributeType
950      *   </li>
951      *   <li>
952      *     <b>isHR</b> Tells if the value is a String or not
953      *   </li>
954      * </ul>
955      * <br>
956      * if the value is a String :
957      * <ul>
958      *   <li>
959      *     <b>value</b> The value
960      *   </li>
961      * </ul>
962      * <br>
963      * if the value is binary :
964      * <ul>
965      *   <li>
966      *     <b>valueLength</b>
967      *   </li>
968      *   <li>
969      *     <b>value</b> The value
970      *   </li>
971      * </ul>
972      * 
973      * @see Externalizable#readExternal(ObjectInput)
974      * 
975      * @throws IOException If the Ava can't be written in the stream
976      */
977     @Override
978     public void writeExternal( ObjectOutput out ) throws IOException
979     {
980         if ( Strings.isEmpty( upName )
981             || Strings.isEmpty( upType )
982             || Strings.isEmpty( normType )
983             || ( value.isNull() ) )
984         {
985             String message = "Cannot serialize a wrong ATAV, ";
986 
987             if ( Strings.isEmpty( upName ) )
988             {
989                 message += "the upName should not be null or empty";
990             }
991             else if ( Strings.isEmpty( upType ) )
992             {
993                 message += "the upType should not be null or empty";
994             }
995             else if ( Strings.isEmpty( normType ) )
996             {
997                 message += "the normType should not be null or empty";
998             }
999             else if ( value.isNull() )
1000             {
1001                 message += "the value should not be null";
1002             }
1003 
1004             LOG.error( message );
1005             throw new IOException( message );
1006         }
1007 
1008         if ( upName != null )
1009         {
1010             out.writeBoolean( true );
1011             out.writeUTF( upName );
1012         }
1013         else
1014         {
1015             out.writeBoolean( false );
1016         }
1017 
1018         if ( upType != null )
1019         {
1020             out.writeBoolean( true );
1021             out.writeUTF( upType );
1022         }
1023         else
1024         {
1025             out.writeBoolean( false );
1026         }
1027 
1028         if ( normType != null )
1029         {
1030             out.writeBoolean( true );
1031             out.writeUTF( normType );
1032         }
1033         else
1034         {
1035             out.writeBoolean( false );
1036         }
1037 
1038         boolean isHR = value.isHumanReadable();
1039 
1040         out.writeBoolean( isHR );
1041 
1042         value.writeExternal( out );
1043 
1044         // Write the hashCode
1045         out.writeInt( h );
1046 
1047         out.flush();
1048     }
1049 
1050 
1051     /**
1052      * We read back the data to create a new ATAV. The structure
1053      * read is exposed in the {@link Ava#writeExternal(ObjectOutput)}
1054      * method
1055      * 
1056      * @see Externalizable#readExternal(ObjectInput)
1057      * 
1058      * @throws IOException If the Ava can't b written to the stream
1059      * @throws ClassNotFoundException If we can't deserialize an Ava from the stream
1060      */
1061     @Override
1062     public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
1063     {
1064         boolean hasUpName = in.readBoolean();
1065 
1066         if ( hasUpName )
1067         {
1068             upName = in.readUTF();
1069         }
1070 
1071         boolean hasUpType = in.readBoolean();
1072 
1073         if ( hasUpType )
1074         {
1075             upType = in.readUTF();
1076         }
1077 
1078         boolean hasNormType = in.readBoolean();
1079 
1080         if ( hasNormType )
1081         {
1082             normType = in.readUTF();
1083         }
1084 
1085         if ( schemaManager != null )
1086         {
1087             if ( !Strings.isEmpty( upType ) )
1088             {
1089                 attributeType = schemaManager.getAttributeType( upType );
1090             }
1091             else
1092             {
1093                 attributeType = schemaManager.getAttributeType( normType );
1094             }
1095         }
1096 
1097         boolean isHR = in.readBoolean();
1098 
1099         if ( isHR )
1100         {
1101             value = StringValue.deserialize( attributeType, in );
1102         }
1103         else
1104         {
1105             value = BinaryValue.deserialize( attributeType, in );
1106         }
1107 
1108         h = in.readInt();
1109 
1110         if ( schemaManager != null )
1111         {
1112             attributeType = schemaManager.getAttributeType( upType );
1113         }
1114     }
1115 
1116 
1117     /**
1118      * Tells if the Ava is schema aware or not.
1119      * 
1120      * @return true if the Ava is schema aware
1121      */
1122     public boolean isSchemaAware()
1123     {
1124         return attributeType != null;
1125     }
1126 
1127 
1128     /**
1129      * @return the attributeType
1130      */
1131     public AttributeType getAttributeType()
1132     {
1133         return attributeType;
1134     }
1135 
1136 
1137     private int compareValues( Ava that )
1138     {
1139         int comp;
1140 
1141         if ( value.getNormValue() instanceof String )
1142         {
1143             comp = ( ( String ) value.getNormValue() ).compareTo( ( String ) that.value.getNormValue() );
1144 
1145             return comp;
1146         }
1147         else
1148         {
1149             byte[] bytes1 = ( byte[] ) value.getNormValue();
1150             byte[] bytes2 = ( byte[] ) that.value.getNormValue();
1151 
1152             for ( int pos = 0; pos < bytes1.length; pos++ )
1153             {
1154                 int v1 = bytes1[pos] & 0x00FF;
1155                 int v2 = bytes2[pos] & 0x00FF;
1156 
1157                 if ( v1 > v2 )
1158                 {
1159                     return 1;
1160                 }
1161                 else if ( v2 > v1 )
1162                 {
1163                     return -1;
1164                 }
1165             }
1166 
1167             return 0;
1168         }
1169 
1170     }
1171 
1172 
1173     /**
1174      * @see Comparable#compareTo(Object)
1175      */
1176     @Override
1177     public int compareTo( Ava that )
1178     {
1179         if ( that == null )
1180         {
1181             return 1;
1182         }
1183 
1184         int comp;
1185 
1186         if ( schemaManager == null )
1187         {
1188             // Compare the ATs
1189             comp = normType.compareTo( that.normType );
1190 
1191             if ( comp != 0 )
1192             {
1193                 return comp;
1194             }
1195 
1196             // and compare the values
1197             if ( value == null )
1198             {
1199                 if ( that.value == null )
1200                 {
1201                     return 0;
1202                 }
1203                 else
1204                 {
1205                     return -1;
1206                 }
1207             }
1208             else
1209             {
1210                 if ( that.value == null )
1211                 {
1212                     return 1;
1213                 }
1214                 else
1215                 {
1216                     if ( value instanceof StringValue )
1217                     {
1218                         comp = ( ( StringValue ) value ).compareTo( ( StringValue ) that.value );
1219 
1220                         return comp;
1221                     }
1222                     else
1223                     {
1224                         comp = ( ( BinaryValue ) value ).compareTo( ( BinaryValue ) that.value );
1225 
1226                         return comp;
1227                     }
1228                 }
1229             }
1230         }
1231         else
1232         {
1233             if ( that.schemaManager == null )
1234             {
1235                 // Problem : we will apply the current Ava SchemaManager to the given Ava
1236                 try
1237                 {
1238                     that.apply( schemaManager );
1239                 }
1240                 catch ( LdapInvalidDnException lide )
1241                 {
1242                     return 1;
1243                 }
1244             }
1245 
1246             // First compare the AT OID
1247             comp = attributeType.getOid().compareTo( that.attributeType.getOid() );
1248 
1249             if ( comp != 0 )
1250             {
1251                 return comp;
1252             }
1253 
1254             // Now, compare the two values using the ordering matchingRule comparator, if any
1255             MatchingRule orderingMR = attributeType.getOrdering();
1256 
1257             if ( orderingMR != null )
1258             {
1259                 LdapComparator<Object> comparator = ( LdapComparator<Object> ) orderingMR.getLdapComparator();
1260 
1261                 if ( comparator != null )
1262                 {
1263                     comp = comparator.compare( value.getNormValue(), that.value.getNormValue() );
1264 
1265                     return comp;
1266                 }
1267                 else
1268                 {
1269                     comp = compareValues( that );
1270 
1271                     return comp;
1272                 }
1273             }
1274             else
1275             {
1276                 comp = compareValues( that );
1277 
1278                 return comp;
1279             }
1280         }
1281     }
1282     
1283     
1284     /**
1285      * A String representation of an Ava, as provided by the user.
1286      *
1287      * @return A string representing an Ava
1288      */
1289     @Override
1290     public String toString()
1291     {
1292         return upName;
1293     }
1294 }