001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 *
019 */
020 package org.apache.directory.shared.ldap.name;
021
022
023 import java.io.Externalizable;
024 import java.io.IOException;
025 import java.io.ObjectInput;
026 import java.io.ObjectOutput;
027 import java.util.Arrays;
028
029 import javax.naming.InvalidNameException;
030
031 import org.apache.directory.shared.i18n.I18n;
032 import org.apache.directory.shared.ldap.entry.Value;
033 import org.apache.directory.shared.ldap.entry.client.ClientBinaryValue;
034 import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
035 import org.apache.directory.shared.ldap.util.StringTools;
036 import org.slf4j.Logger;
037 import org.slf4j.LoggerFactory;
038
039
040 /**
041 * A Attribute Type And Value, which is the basis of all RDN. It contains a
042 * type, and a value. The type must not be case sensitive. Superfluous leading
043 * and trailing spaces MUST have been trimmed before. The value MUST be in UTF8
044 * format, according to RFC 2253. If the type is in OID form, then the value
045 * must be a hexadecimal string prefixed by a '#' character. Otherwise, the
046 * string must respect the RC 2253 grammar. No further normalization will be
047 * done, because we don't have any knowledge of the Schema definition in the
048 * parser.
049 *
050 * We will also keep a User Provided form of the atav (Attribute Type And Value),
051 * called upName.
052 *
053 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
054 * @version $Rev: 912436 $, $Date: 2010-02-22 00:16:28 +0100 (Lun, 22 fév 2010) $
055 */
056 public class AVA implements Cloneable, Comparable, Externalizable
057 {
058 /**
059 * Declares the Serial Version Uid.
060 *
061 * @see <a
062 * href="http://c2.com/cgi/wiki?AlwaysDeclareSerialVersionUid">Always
063 * Declare Serial Version Uid</a>
064 */
065 private static final long serialVersionUID = 1L;
066
067 /** The LoggerFactory used by this class */
068 private static Logger LOG = LoggerFactory.getLogger( AVA.class );
069
070 /** The normalized Name type */
071 private String normType;
072
073 /** The user provided Name type */
074 private String upType;
075
076 /** The name value. It can be a String or a byte array */
077 private Value<?> normValue;
078
079 /** The name user provided value. It can be a String or a byte array */
080 private Value<?> upValue;
081
082 /** The user provided AVA */
083 private String upName;
084
085 /** The starting position of this atav in the given string from which
086 * we have extracted the upName */
087 private int start;
088
089 /** The length of this atav upName */
090 private int length;
091
092 /** Two values used for comparizon */
093 private static final boolean CASE_SENSITIVE = true;
094
095 private static final boolean CASE_INSENSITIVE = false;
096
097
098 /**
099 * Construct an empty AVA
100 */
101 public AVA()
102 {
103 normType = null;
104 upType = null;
105 normValue = null;
106 upValue = null;
107 upName = "";
108 start = -1;
109 length = 0;
110 }
111
112
113 /**
114 * Construct an AVA. The type and value are normalized :
115 * <li> the type is trimmed and lowercased </li>
116 * <li> the value is trimmed </li>
117 * <p>
118 * Note that the upValue should <b>not</b> be null or empty, or resolved
119 * to an empty string after having trimmed it.
120 *
121 * @param upType The Usrr Provided type
122 * @param normType The normalized type
123 * @param upValue The User Provided value
124 * @param normValue The normalized value
125 */
126 public AVA( String upType, String normType, String upValue, String normValue ) throws InvalidNameException
127 {
128 this( upType, normType, new ClientStringValue( upValue ), new ClientStringValue( normValue ) );
129 }
130
131
132
133
134 /**
135 * Construct an AVA. The type and value are normalized :
136 * <li> the type is trimmed and lowercased </li>
137 * <li> the value is trimmed </li>
138 * <p>
139 * Note that the upValue should <b>not</b> be null or empty, or resolved
140 * to an empty string after having trimmed it.
141 *
142 * @param upType The Usrr Provided type
143 * @param normType The normalized type
144 * @param upValue The User Provided value
145 * @param normValue The normalized value
146 */
147 public AVA( String upType, String normType, byte[] upValue, byte[] normValue ) throws InvalidNameException
148 {
149 this( upType, normType, new ClientBinaryValue( upValue ), new ClientBinaryValue( normValue ) );
150 }
151
152
153 /**
154 * Construct an AVA. The type and value are normalized :
155 * <li> the type is trimmed and lowercased </li>
156 * <li> the value is trimmed </li>
157 * <p>
158 * Note that the upValue should <b>not</b> be null or empty, or resolved
159 * to an empty string after having trimmed it.
160 *
161 * @param upType The Usrr Provided type
162 * @param normType The normalized type
163 * @param upValue The User Provided value
164 * @param normValue The normalized value
165 */
166 public AVA( String upType, String normType, Value<?> upValue, Value<?> normValue ) throws InvalidNameException
167 {
168 String upTypeTrimmed = StringTools.trim( upType );
169 String normTypeTrimmed = StringTools.trim( normType );
170
171 if ( StringTools.isEmpty( upTypeTrimmed ) )
172 {
173 if ( StringTools.isEmpty( normTypeTrimmed ) )
174 {
175 String message = I18n.err( I18n.ERR_04188 );
176 LOG.error( message );
177 throw new InvalidNameException( message );
178 }
179 else
180 {
181 // In this case, we will use the normType instead
182 this.normType = StringTools.lowerCaseAscii( normTypeTrimmed );
183 this.upType = normType;
184 }
185 }
186 else if ( StringTools.isEmpty( normTypeTrimmed ) )
187 {
188 // In this case, we will use the upType instead
189 this.normType = StringTools.lowerCaseAscii( upTypeTrimmed );
190 this.upType = upType;
191 }
192 else
193 {
194 this.normType = StringTools.lowerCaseAscii( normTypeTrimmed );
195 this.upType = upType;
196
197 }
198
199 this.normValue = normValue;
200 this.upValue = upValue;
201
202 upName = this.upType + '=' + ( this.upValue == null ? "" : this.upValue.getString() );
203 start = 0;
204 length = upName.length();
205 }
206
207
208 /**
209 * Construct an AVA. The type and value are normalized :
210 * <li> the type is trimmed and lowercased </li>
211 * <li> the value is trimmed </li>
212 * <p>
213 * Note that the upValue should <b>not</b> be null or empty, or resolved
214 * to an empty string after having trimmed it.
215 *
216 * @param upType The User Provided type
217 * @param normType The normalized type
218 * @param upValue The User Provided value
219 * @param normValue The normalized value
220 * @param upName The User Provided name (may be escaped)
221 */
222 public AVA( String upType, String normType, Value<?> upValue, Value<?> normValue, String upName )
223 throws InvalidNameException
224 {
225 String upTypeTrimmed = StringTools.trim( upType );
226 String normTypeTrimmed = StringTools.trim( normType );
227
228 if ( StringTools.isEmpty( upTypeTrimmed ) )
229 {
230 if ( StringTools.isEmpty( normTypeTrimmed ) )
231 {
232 String message = I18n.err( I18n.ERR_04188 );
233 LOG.error( message );
234 throw new InvalidNameException( message );
235 }
236 else
237 {
238 // In this case, we will use the normType instead
239 this.normType = StringTools.lowerCaseAscii( normTypeTrimmed );
240 this.upType = normType;
241 }
242 }
243 else if ( StringTools.isEmpty( normTypeTrimmed ) )
244 {
245 // In this case, we will use the upType instead
246 this.normType = StringTools.lowerCaseAscii( upTypeTrimmed );
247 this.upType = upType;
248 }
249 else
250 {
251 this.normType = StringTools.lowerCaseAscii( normTypeTrimmed );
252 this.upType = upType;
253
254 }
255
256 this.normValue = normValue;
257 this.upValue = upValue;
258
259 this.upName = upName;
260 start = 0;
261 length = upName.length();
262 }
263
264
265 /**
266 * Get the normalized type of a AVA
267 *
268 * @return The normalized type
269 */
270 public String getNormType()
271 {
272 return normType;
273 }
274
275 /**
276 * Get the user provided type of a AVA
277 *
278 * @return The user provided type
279 */
280 public String getUpType()
281 {
282 return upType;
283 }
284
285
286 /**
287 * Store a new type
288 *
289 * @param upType The AVA User Provided type
290 * @param type The AVA type
291 *
292 * @throws InvalidNameException if the type or upType are empty or null.
293 * If the upName is invalid.
294 */
295 public void setType( String upType, String type ) throws InvalidNameException
296 {
297 if ( StringTools.isEmpty( type ) || StringTools.isEmpty( type.trim() ) )
298 {
299 String message = I18n.err( I18n.ERR_04188 );
300 LOG.error( message );
301 throw new InvalidNameException( message );
302 }
303
304 if ( StringTools.isEmpty( upType ) || StringTools.isEmpty( upType.trim() ) )
305 {
306 String message = I18n.err( I18n.ERR_04189 );
307 LOG.error( message );
308 throw new InvalidNameException( message );
309 }
310
311 int equalPosition = upName.indexOf( '=' );
312
313 if ( equalPosition <= 1 )
314 {
315 String message = I18n.err( I18n.ERR_04190 );
316 LOG.error( message );
317 throw new InvalidNameException( message );
318 }
319
320 normType = type.trim().toLowerCase();
321 this.upType = upType;
322 upName = upType + upName.substring( equalPosition );
323 start = -1;
324 length = upName.length();
325 }
326
327
328 /**
329 * Store the type, after having trimmed and lowercased it.
330 *
331 * @param type The AVA type
332 */
333 public void setTypeNormalized( String type ) throws InvalidNameException
334 {
335 if ( StringTools.isEmpty( type ) || StringTools.isEmpty( type.trim() ) )
336 {
337 LOG.error( I18n.err( I18n.ERR_04191 ) );
338 throw new InvalidNameException( I18n.err( I18n.ERR_04191 ) );
339 }
340
341 normType = type.trim().toLowerCase();
342 upType = type;
343 upName = type + upName.substring( upName.indexOf( '=' ) );
344 start = -1;
345 length = upName.length();
346 }
347
348
349 /**
350 * Get the Value of a AVA
351 *
352 * @return The value
353 */
354 public Value<?> getNormValue()
355 {
356 return normValue;
357 }
358
359 /**
360 * Get the User Provided Value of a AVA
361 *
362 * @return The value
363 */
364 public Value<?> getUpValue()
365 {
366 return upValue;
367 }
368
369 /**
370 * Get the normalized Name of a AVA
371 *
372 * @return The name
373 */
374 public String getNormName()
375 {
376 return normalize();
377 }
378
379
380 /**
381 * Store the value of a AVA.
382 *
383 * @param value The user provided value of the AVA
384 * @param normValue The normalized value
385 */
386 public void setValue( Value<?> upValue, Value<?> normValue )
387 {
388 this.normValue = normValue;
389 this.upValue = upValue;
390 upName = upName.substring( 0, upName.indexOf( '=' ) + 1 ) + upValue;
391 start = -1;
392 length = upName.length();
393 }
394
395
396 /**
397 * Get the upName length
398 *
399 * @return the upName length
400 */
401 public int getLength()
402 {
403 return length;
404 }
405
406
407 /**
408 * get the position in the original upName where this atav starts.
409 *
410 * @return The starting position of this atav
411 */
412 public int getStart()
413 {
414 return start;
415 }
416
417
418 /**
419 * Get the user provided form of this attribute type and value
420 *
421 * @return The user provided form of this atav
422 */
423 public String getUpName()
424 {
425 return upName;
426 }
427
428
429 /**
430 * Store the value of a AVA, after having trimmed it.
431 *
432 * @param value The value of the AVA
433 */
434 public void setValueNormalized( String value )
435 {
436 String newValue = StringTools.trim( value );
437
438 if ( StringTools.isEmpty( newValue ) )
439 {
440 this.normValue = new ClientStringValue( "" );
441 }
442 else
443 {
444 this.normValue = new ClientStringValue( newValue );
445 }
446
447 upName = upName.substring( 0, upName.indexOf( '=' ) + 1 ) + value;
448 start = -1;
449 length = upName.length();
450 }
451
452
453 /**
454 * Implements the cloning.
455 *
456 * @return a clone of this object
457 */
458 public Object clone()
459 {
460 try
461 {
462 return super.clone();
463 }
464 catch ( CloneNotSupportedException cnse )
465 {
466 throw new Error( "Assertion failure" );
467 }
468 }
469
470
471 /**
472 * Compares two NameComponents. They are equals if :
473 * - types are equals, case insensitive,
474 * - values are equals, case sensitive
475 *
476 * @param object
477 * @return 0 if both NC are equals, otherwise a positive value if the
478 * original NC is superior to the second one, a negative value if
479 * the second NC is superior.
480 */
481 public int compareTo( Object object )
482 {
483 if ( object instanceof AVA )
484 {
485 AVA nc = ( AVA ) object;
486
487 int res = compareType( normType, nc.normType );
488
489 if ( res != 0 )
490 {
491 return res;
492 }
493 else
494 {
495 return compareValue( normValue, nc.normValue, CASE_SENSITIVE );
496 }
497 }
498 else
499 {
500 return 1;
501 }
502 }
503
504
505 /**
506 * Compares two NameComponents. They are equals if :
507 * - types are equals, case insensitive,
508 * - values are equals, case insensitive
509 *
510 * @param object
511 * @return 0 if both NC are equals, otherwise a positive value if the
512 * original NC is superior to the second one, a negative value if
513 * the second NC is superior.
514 */
515 public int compareToIgnoreCase( Object object )
516 {
517 if ( object instanceof AVA )
518 {
519 AVA nc = ( AVA ) object;
520
521 int res = compareType( normType, nc.normType );
522
523 if ( res != 0 )
524 {
525 return res;
526 }
527 else
528 {
529 return compareValue( normValue, nc.normValue, CASE_INSENSITIVE );
530 }
531 }
532 else
533 {
534 return 1;
535 }
536 }
537
538
539 /**
540 * Compare two types, trimed and case insensitive
541 *
542 * @param val1 First String
543 * @param val2 Second String
544 * @return true if both strings are equals or null.
545 */
546 private int compareType( String val1, String val2 )
547 {
548 if ( StringTools.isEmpty( val1 ) )
549 {
550 return StringTools.isEmpty( val2 ) ? 0 : -1;
551 }
552 else if ( StringTools.isEmpty( val2 ) )
553 {
554 return 1;
555 }
556 else
557 {
558 return ( StringTools.trim( val1 ) ).compareToIgnoreCase( StringTools.trim( val2 ) );
559 }
560 }
561
562
563 /**
564 * Compare two values
565 *
566 * @param val1 First value
567 * @param val2 Second value
568 * @param sensitivity A flag to define the case sensitivity
569 * @return -1 if the first value is inferior to the second one, +1 if
570 * its superior, 0 if both values are equal
571 */
572 private int compareValue( Value<?> val1, Value<?> val2, boolean sensitivity )
573 {
574 if ( !val1.isBinary() )
575 {
576 if ( !val2.isBinary() )
577 {
578 int val = ( sensitivity == CASE_SENSITIVE ) ?
579 ( val1.getString() ).compareTo( val2.getString() )
580 : ( val1.getString() ).compareToIgnoreCase( val2.getString() );
581
582 return ( val < 0 ? -1 : ( val > 0 ? 1 : val ) );
583 }
584 else
585 {
586 return 1;
587 }
588 }
589 else
590 {
591 if ( val2.isBinary() )
592 {
593 if ( Arrays.equals( val1.getBytes(), val2.getBytes() ) )
594 {
595 return 0;
596 }
597 else
598 {
599 return 1;
600 }
601 }
602 else
603 {
604 return 1;
605 }
606 }
607 }
608
609 private static final boolean[] DN_ESCAPED_CHARS = new boolean[]
610 {
611 true, true, true, true, true, true, true, true, // 0x00 -> 0x07
612 true, true, true, true, true, true, true, true, // 0x08 -> 0x0F
613 true, true, true, true, true, true, true, true, // 0x10 -> 0x17
614 true, true, true, true, true, true, true, true, // 0x18 -> 0x1F
615 true, false, true, true, false, false, false, false, // 0x20 -> 0x27 ' ', '"', '#'
616 false, false, false, true, true, false, false, false, // 0x28 -> 0x2F '+', ','
617 false, false, false, false, false, false, false, false, // 0x30 -> 0x37
618 false, false, false, true, true, false, true, false, // 0x38 -> 0x3F ';', '<', '>'
619 false, false, false, false, false, false, false, false, // 0x40 -> 0x47
620 false, false, false, false, false, false, false, false, // 0x48 -> 0x4F
621 false, false, false, false, false, false, false, false, // 0x50 -> 0x57
622 false, false, false, false, true, false, false, false, // 0x58 -> 0x5F
623 false, false, false, false, false, false, false, false, // 0x60 -> 0x67
624 false, false, false, false, false, false, false, false, // 0x68 -> 0x6F
625 false, false, false, false, false, false, false, false, // 0x70 -> 0x77
626 false, false, false, false, false, false, false, false, // 0x78 -> 0x7F
627 };
628
629
630 public String normalizeValue()
631 {
632 // The result will be gathered in a stringBuilder
633 StringBuilder sb = new StringBuilder();
634
635 String normalizedValue = normValue.getString();
636 int valueLength = normalizedValue.length();
637
638 if ( normalizedValue.length() > 0 )
639 {
640 char[] chars = normalizedValue.toCharArray();
641
642 // Here, we have a char to escape. Start again the loop...
643 for ( int i = 0; i < valueLength; i++ )
644 {
645 char c = chars[i];
646
647 if ( ( c >= 0 ) && ( c < DN_ESCAPED_CHARS.length ) && DN_ESCAPED_CHARS[ c ] )
648 {
649 // Some chars need to be escaped even if they are US ASCII
650 // Just prefix them with a '\'
651 // Special cases are ' ' (space), '#') which need a special
652 // treatment.
653 switch ( c )
654 {
655 case ' ' :
656 if ( ( i == 0 ) || ( i == valueLength - 1 ) )
657 {
658 sb.append( "\\ " );
659 }
660 else
661 {
662 sb.append( ' ' );
663 }
664
665 break;
666
667 case '#' :
668 if ( i == 0 )
669 {
670 sb.append( "\\#" );
671 continue;
672 }
673 else
674 {
675 sb.append( '#' );
676 }
677
678 break;
679
680 default :
681 sb.append( '\\' ).append( c );
682 }
683 }
684 else
685 {
686 // Standard ASCII chars are just appended
687 sb.append( c );
688 }
689 }
690 }
691
692 return sb.toString();
693 }
694
695 /**
696 * A Normalized String representation of a AVA :
697 * - type is trimed and lowercased
698 * - value is trimed and lowercased, and special characters
699 * are escaped if needed.
700 *
701 * @return A normalized string representing a AVA
702 */
703 public String normalize()
704 {
705 if ( !normValue.isBinary() )
706 {
707 // The result will be gathered in a stringBuilder
708 StringBuilder sb = new StringBuilder();
709
710 // First, store the type and the '=' char
711 sb.append( normType ).append( '=' );
712
713 String normalizedValue = normValue.getString();
714
715 if ( normalizedValue.length() > 0 )
716 {
717 sb.append( normalizeValue() );
718 }
719
720 return sb.toString();
721 }
722 else
723 {
724 return normType + "=#"
725 + StringTools.dumpHexPairs( normValue .getBytes() );
726 }
727 }
728
729
730 /**
731 * Gets the hashcode of this object.
732 *
733 * @see java.lang.Object#hashCode()
734 * @return The instance hash code
735 */
736 public int hashCode()
737 {
738 int result = 37;
739
740 result = result*17 + ( normType != null ? normType.hashCode() : 0 );
741 result = result*17 + ( normValue != null ? normValue.hashCode() : 0 );
742
743 return result;
744 }
745
746 /**
747 * @see Object#equals(Object)
748 */
749 public boolean equals( Object obj )
750 {
751 if ( this == obj )
752 {
753 return true;
754 }
755
756 if ( !( obj instanceof AVA ) )
757 {
758 return false;
759 }
760
761 AVA instance = (AVA)obj;
762
763 // Compare the type
764 if ( normType == null )
765 {
766 if ( instance.normType != null )
767 {
768 return false;
769 }
770 }
771 else
772 {
773 if ( !normType.equals( instance.normType ) )
774 {
775 return false;
776 }
777 }
778
779 // Compare the values
780 if ( normValue.isNull() )
781 {
782 return instance.normValue.isNull();
783 }
784 else
785 {
786 return normValue.equals( instance.normValue );
787 }
788 }
789
790
791 /**
792 * @see Externalizable#readExternal(ObjectInput)<p>
793 *
794 * An AVA is composed of a type and a value.
795 * The data are stored following the structure :
796 *
797 * <li>upName</li> The User provided ATAV
798 * <li>start</li> The position of this ATAV in the DN
799 * <li>length</li> The ATAV length
800 * <li>upType</li> The user Provided Type
801 * <li>normType</li> The normalized AttributeType
802 * <li>isHR<li> Tells if the value is a String or not
803 * <p>
804 * if the value is a String :
805 * <li>upValue</li> The User Provided value.
806 * <li>value</li> The normalized value.
807 * <p>
808 * if the value is binary :
809 * <li>upValueLength</li>
810 * <li>upValue</li> The User Provided value.
811 * <li>valueLength</li>
812 * <li>value</li> The normalized value.
813 */
814 public void writeExternal( ObjectOutput out ) throws IOException
815 {
816 if ( StringTools.isEmpty( upName ) ||
817 StringTools.isEmpty( upType ) ||
818 StringTools.isEmpty( normType ) ||
819 ( start < 0 ) ||
820 ( length < 2 ) || // At least a type and '='
821 ( upValue.isNull() ) ||
822 ( normValue.isNull() ) )
823 {
824 String message = "Cannot serialize an wrong ATAV, ";
825
826 if ( StringTools.isEmpty( upName ) )
827 {
828 message += "the upName should not be null or empty";
829 }
830 else if ( StringTools.isEmpty( upType ) )
831 {
832 message += "the upType should not be null or empty";
833 }
834 else if ( StringTools.isEmpty( normType ) )
835 {
836 message += "the normType should not be null or empty";
837 }
838 else if ( start < 0 )
839 {
840 message += "the start should not be < 0";
841 }
842 else if ( length < 2 )
843 {
844 message += "the length should not be < 2";
845 }
846 else if ( upValue.isNull() )
847 {
848 message += "the upValue should not be null";
849 }
850 else if ( normValue.isNull() )
851 {
852 message += "the value should not be null";
853 }
854
855 LOG.error( message );
856 throw new IOException( message );
857 }
858
859 out.writeUTF( upName );
860 out.writeInt( start );
861 out.writeInt( length );
862 out.writeUTF( upType );
863 out.writeUTF( normType );
864
865 boolean isHR = !normValue.isBinary();
866
867 out.writeBoolean( isHR );
868
869 if ( isHR )
870 {
871 out.writeUTF( upValue.getString() );
872 out.writeUTF( normValue.getString() );
873 }
874 else
875 {
876 out.writeInt( upValue.length() );
877 out.write( upValue.getBytes() );
878 out.writeInt( normValue.length() );
879 out.write( normValue.getBytes() );
880 }
881 }
882
883
884 /**
885 * @see Externalizable#readExternal(ObjectInput)
886 *
887 * We read back the data to create a new ATAV. The structure
888 * read is exposed in the {@link AVA#writeExternal(ObjectOutput)}
889 * method<p>
890 */
891 public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
892 {
893 upName = in.readUTF();
894 start = in.readInt();
895 length = in.readInt();
896 upType = in.readUTF();
897 normType = in.readUTF();
898
899 boolean isHR = in.readBoolean();
900
901 if ( isHR )
902 {
903 upValue = new ClientStringValue( in.readUTF() );
904 normValue = new ClientStringValue( in.readUTF() );
905 }
906 else
907 {
908 int upValueLength = in.readInt();
909 byte[] upValueBytes = new byte[upValueLength];
910 in.readFully( upValueBytes );
911 upValue = new ClientBinaryValue( upValueBytes );
912
913 int valueLength = in.readInt();
914 byte[] normValueBytes = new byte[valueLength];
915 in.readFully( normValueBytes );
916 normValue = new ClientBinaryValue( normValueBytes );
917 }
918 }
919
920
921 /**
922 * A String representation of a AVA.
923 *
924 * @return A string representing a AVA
925 */
926 public String toString()
927 {
928 StringBuffer sb = new StringBuffer();
929
930 if ( StringTools.isEmpty( normType ) || StringTools.isEmpty( normType.trim() ) )
931 {
932 return "";
933 }
934
935 sb.append( normType ).append( "=" );
936
937 if ( normValue != null )
938 {
939 sb.append( normValue.getString() );
940 }
941
942 return sb.toString();
943 }
944 }