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