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 org.apache.directory.shared.i18n.I18n; 030 import org.apache.directory.shared.ldap.entry.BinaryValue; 031 import org.apache.directory.shared.ldap.entry.StringValue; 032 import org.apache.directory.shared.ldap.entry.Value; 033 import org.apache.directory.shared.ldap.exception.LdapInvalidDnException; 034 import org.apache.directory.shared.ldap.message.ResultCodeEnum; 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: 928945 $, $Date: 2010-03-30 01:59:49 +0200 (Tue, 30 Mar 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 LdapInvalidDnException 127 { 128 this( upType, normType, new StringValue( upValue ), new StringValue( 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 LdapInvalidDnException 148 { 149 this( upType, normType, new BinaryValue( upValue ), new BinaryValue( 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 LdapInvalidDnException 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 LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, 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 LdapInvalidDnException 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 LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, 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 LdapInvalidDnException 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 LdapInvalidDnException 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 LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, 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 LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, 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 LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, 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 LdapInvalidDnException 334 { 335 if ( StringTools.isEmpty( type ) || StringTools.isEmpty( type.trim() ) ) 336 { 337 LOG.error( I18n.err( I18n.ERR_04191 ) ); 338 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, 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 StringValue( "" ); 441 } 442 else 443 { 444 this.normValue = new StringValue( 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 StringValue( in.readUTF() ); 904 normValue = new StringValue( in.readUTF() ); 905 } 906 else 907 { 908 int upValueLength = in.readInt(); 909 byte[] upValueBytes = new byte[upValueLength]; 910 in.readFully( upValueBytes ); 911 upValue = new BinaryValue( upValueBytes ); 912 913 int valueLength = in.readInt(); 914 byte[] normValueBytes = new byte[valueLength]; 915 in.readFully( normValueBytes ); 916 normValue = new BinaryValue( 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 }