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 */ 020package org.apache.directory.shared.ldap.model.name; 021 022 023import java.io.Externalizable; 024import java.io.IOException; 025import java.io.ObjectInput; 026import java.io.ObjectOutput; 027 028import org.apache.directory.shared.i18n.I18n; 029import org.apache.directory.shared.ldap.model.entry.BinaryValue; 030import org.apache.directory.shared.ldap.model.entry.StringValue; 031import org.apache.directory.shared.ldap.model.entry.Value; 032import org.apache.directory.shared.ldap.model.exception.LdapException; 033import org.apache.directory.shared.ldap.model.exception.LdapInvalidAttributeValueException; 034import org.apache.directory.shared.ldap.model.exception.LdapInvalidDnException; 035import org.apache.directory.shared.ldap.model.message.ResultCodeEnum; 036import org.apache.directory.shared.ldap.model.schema.AttributeType; 037import org.apache.directory.shared.ldap.model.schema.MatchingRule; 038import org.apache.directory.shared.ldap.model.schema.SchemaManager; 039import org.apache.directory.shared.util.Strings; 040import org.slf4j.Logger; 041import org.slf4j.LoggerFactory; 042 043 044/** 045 * A Attribute Type And Value, which is the basis of all Rdn. It contains a 046 * type, and a value. The type must not be case sensitive. Superfluous leading 047 * and trailing spaces MUST have been trimmed before. The value MUST be in UTF8 048 * format, according to RFC 2253. If the type is in OID form, then the value 049 * must be a hexadecimal string prefixed by a '#' character. Otherwise, the 050 * string must respect the RC 2253 grammar. 051 * 052 * We will also keep a User Provided form of the AVA (Attribute Type And Value), 053 * called upName. 054 * 055 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 056 */ 057public class Ava implements Externalizable, Cloneable 058{ 059 /** 060 * Declares the Serial Version Uid. 061 * 062 * @see <a 063 * href="http://c2.com/cgi/wiki?AlwaysDeclareSerialVersionUid">Always 064 * Declare Serial Version Uid</a> 065 */ 066 private static final long serialVersionUID = 1L; 067 068 /** The LoggerFactory used by this class */ 069 private static final Logger LOG = LoggerFactory.getLogger( Ava.class ); 070 071 /** The normalized Name type */ 072 private String normType; 073 074 /** The user provided Name type */ 075 private String upType; 076 077 /** The name value. It can be a String or a byte array */ 078 private Value<?> normValue; 079 080 /** The name user provided value. It can be a String or a byte array */ 081 private Value<?> upValue; 082 083 /** The user provided Ava */ 084 private String upName; 085 086 /** The attributeType if the Ava is schemaAware */ 087 private AttributeType attributeType; 088 089 /** the schema manager */ 090 private SchemaManager schemaManager; 091 092 /** The computed hashcode */ 093 private volatile int h; 094 095 /** 096 * Constructs an empty Ava 097 */ 098 public Ava() 099 { 100 this( null ); 101 } 102 103 104 /** 105 * Constructs an empty schema aware Ava. 106 * 107 * @param schemaManager The SchemaManager instance 108 */ 109 public Ava( SchemaManager schemaManager ) 110 { 111 normType = null; 112 upType = null; 113 normValue = null; 114 upValue = 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 try 156 { 157 attributeType = schemaManager.lookupAttributeTypeRegistry( upType ); 158 } 159 catch ( LdapException le ) 160 { 161 String message = I18n.err( I18n.ERR_04188 ); 162 LOG.error( message ); 163 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le ); 164 } 165 166 try 167 { 168 createAva( schemaManager, upType, new BinaryValue( attributeType, upValue ) ); 169 } 170 catch ( LdapInvalidAttributeValueException liave ) 171 { 172 String message = I18n.err( I18n.ERR_04188 ); 173 LOG.error( message ); 174 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, liave ); 175 } 176 } 177 else 178 { 179 createAva( upType, new BinaryValue( upValue ) ); 180 } 181 } 182 183 184 /** 185 * Construct an Ava with a String value. 186 * <p> 187 * Note that the upValue should <b>not</b> be null or empty, or resolve 188 * to an empty string after having trimmed it. 189 * 190 * @param upType The User Provided type 191 * @param upValue The User Provided String value 192 * 193 * @throws LdapInvalidDnException If the given type or value are invalid 194 */ 195 public Ava( String upType, String upValue ) throws LdapInvalidDnException 196 { 197 this( null, upType, upValue ); 198 } 199 200 201 /** 202 * Construct a schema aware Ava with a String value. 203 * <p> 204 * Note that the upValue should <b>not</b> be null or empty, or resolve 205 * to an empty string after having trimmed it. 206 * 207 * @param schemaManager The SchemaManager instance 208 * @param upType The User Provided type 209 * @param upValue The User Provided String value 210 * 211 * @throws LdapInvalidDnException If the given type or value are invalid 212 */ 213 public Ava( SchemaManager schemaManager, String upType, String upValue ) throws LdapInvalidDnException 214 { 215 if ( schemaManager != null ) 216 { 217 try 218 { 219 attributeType = schemaManager.lookupAttributeTypeRegistry( upType ); 220 } 221 catch ( LdapException le ) 222 { 223 String message = I18n.err( I18n.ERR_04188 ); 224 LOG.error( message ); 225 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le ); 226 } 227 228 try 229 { 230 createAva( schemaManager, upType, new StringValue( attributeType, upValue ) ); 231 } 232 catch ( LdapInvalidAttributeValueException liave ) 233 { 234 String message = I18n.err( I18n.ERR_04188 ); 235 LOG.error( message ); 236 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, liave ); 237 } 238 } 239 else 240 { 241 createAva( upType, new StringValue( upValue ) ); 242 } 243 } 244 245 246 /** 247 * Construct a schema aware Ava. The AttributeType and value will be checked accordingly 248 * to the SchemaManager. 249 * <p> 250 * Note that the upValue should <b>not</b> be null or empty, or resolve 251 * to an empty string after having trimmed it. 252 * 253 * @param schemaManager The SchemaManager instance 254 * @param upType The User Provided type 255 * @param upValue The User Provided value 256 * 257 * @throws LdapInvalidDnException If the given type or value are invalid 258 */ 259 private void createAva( SchemaManager schemaManager, String upType, Value<?> upValue ) throws LdapInvalidDnException 260 { 261 normType = attributeType.getOid(); 262 this.upType = upType; 263 264 try 265 { 266 MatchingRule equalityMatchingRule = attributeType.getEquality(); 267 268 if ( equalityMatchingRule != null ) 269 { 270 this.normValue = equalityMatchingRule.getNormalizer().normalize( upValue ); 271 } 272 else 273 { 274 this.normValue = upValue; 275 } 276 } 277 catch ( LdapException le ) 278 { 279 String message = I18n.err( I18n.ERR_04188 ); 280 LOG.error( message ); 281 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le ); 282 } 283 284 this.upValue = upValue; 285 286 upName = this.upType + '=' + ( this.upValue == null ? "" : this.upValue.getString() ); 287 hashCode(); 288 } 289 290 291 /** 292 * Construct an Ava. The type and value are normalized : 293 * <li> the type is trimmed and lowercased </li> 294 * <li> the value is trimmed </li> 295 * <p> 296 * Note that the upValue should <b>not</b> be null or empty, or resolved 297 * to an empty string after having trimmed it. 298 * 299 * @param upType The User Provided type 300 * @param upValue The User Provided value 301 * 302 * @throws LdapInvalidDnException If the given type or value are invalid 303 */ 304 private void createAva( String upType, Value<?> upValue ) throws LdapInvalidDnException 305 { 306 String upTypeTrimmed = Strings.trim(upType); 307 String normTypeTrimmed = Strings.trim(normType); 308 309 if ( Strings.isEmpty(upTypeTrimmed) ) 310 { 311 if ( Strings.isEmpty(normTypeTrimmed) ) 312 { 313 String message = I18n.err( I18n.ERR_04188 ); 314 LOG.error( message ); 315 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message ); 316 } 317 else 318 { 319 // In this case, we will use the normType instead 320 this.normType = Strings.lowerCaseAscii( normTypeTrimmed ); 321 this.upType = normType; 322 } 323 } 324 else if ( Strings.isEmpty(normTypeTrimmed) ) 325 { 326 // In this case, we will use the upType instead 327 this.normType = Strings.lowerCaseAscii( upTypeTrimmed ); 328 this.upType = upType; 329 } 330 else 331 { 332 this.normType = Strings.lowerCaseAscii( normTypeTrimmed ); 333 this.upType = upType; 334 335 } 336 337 this.normValue = upValue; 338 this.upValue = upValue; 339 340 upName = this.upType + '=' + ( this.upValue == null ? "" : this.upValue.getString() ); 341 hashCode(); 342 } 343 344 345 /** 346 * Construct an Ava. The type and value are normalized : 347 * <li> the type is trimmed and lowercased </li> 348 * <li> the value is trimmed </li> 349 * <p> 350 * Note that the upValue should <b>not</b> be null or empty, or resolved 351 * to an empty string after having trimmed it. 352 * 353 * @param schemaManager The SchemaManager 354 * @param upType The User Provided type 355 * @param normType The normalized type 356 * @param upValue The User Provided value 357 * @param normValue The normalized value 358 * 359 * @throws LdapInvalidDnException If the given type or value are invalid 360 */ 361 // WARNING : The protection level is left unspecified intentionally. 362 // We need this method to be visible from the DnParser class, but not 363 // from outside this package. 364 /* Unspecified protection */ Ava( SchemaManager schemaManager, String upType, String normType, Value<?> upValue, Value<?> normValue ) 365 throws LdapInvalidDnException 366 { 367 this.upType = upType; 368 this.normType = normType; 369 this.upValue = upValue; 370 this.normValue = normValue; 371 upName = this.upType + '=' + ( this.upValue == null ? "" : this.upValue.getString() ); 372 373 if ( schemaManager != null ) 374 { 375 apply( schemaManager ); 376 } 377 378 hashCode(); 379 } 380 381 382 /** 383 * Construct an Ava. The type and value are normalized : 384 * <li> the type is trimmed and lowercased </li> 385 * <li> the value is trimmed </li> 386 * <p> 387 * Note that the upValue should <b>not</b> be null or empty, or resolved 388 * to an empty string after having trimmed it. 389 * 390 * @param upType The User Provided type 391 * @param normType The normalized type 392 * @param upValue The User Provided value 393 * @param normValue The normalized value 394 * @param upName The User Provided name (may be escaped) 395 * 396 * @throws LdapInvalidDnException If the given type or value are invalid 397 */ 398 // WARNING : The protection level is left unspecified intentionally. 399 // We need this method to be visible from the DnParser class, but not 400 // from outside this package. 401 /* Unspecified protection */ Ava( String upType, String normType, Value<?> upValue, Value<?> normValue, String upName ) 402 throws LdapInvalidDnException 403 { 404 String upTypeTrimmed = Strings.trim( upType ); 405 String normTypeTrimmed = Strings.trim( normType ); 406 407 if ( Strings.isEmpty( upTypeTrimmed ) ) 408 { 409 if ( Strings.isEmpty( normTypeTrimmed ) ) 410 { 411 String message = I18n.err( I18n.ERR_04188 ); 412 LOG.error( message ); 413 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message ); 414 } 415 else 416 { 417 // In this case, we will use the normType instead 418 this.normType = Strings.lowerCaseAscii( normTypeTrimmed ); 419 this.upType = normType; 420 } 421 } 422 else if ( Strings.isEmpty( normTypeTrimmed ) ) 423 { 424 // In this case, we will use the upType instead 425 this.normType = Strings.lowerCaseAscii( upTypeTrimmed ); 426 this.upType = upType; 427 } 428 else 429 { 430 this.normType = Strings.lowerCaseAscii( normTypeTrimmed ); 431 this.upType = upType; 432 } 433 434 this.normValue = normValue; 435 this.upValue = upValue; 436 this.upName = upName; 437 hashCode(); 438 } 439 440 441 /** 442 * Apply a SchemaManager to the Ava. It will normalize the Ava.<br/> 443 * If the Ava already had a SchemaManager, then the new SchemaManager will be 444 * used instead. 445 * 446 * @param schemaManager The SchemaManager instance to use 447 * @throws LdapInvalidDnException If the Ava can't be normalized accordingly 448 * to the given SchemaManager 449 */ 450 public void apply( SchemaManager schemaManager ) throws LdapInvalidDnException 451 { 452 if ( schemaManager != null ) 453 { 454 this.schemaManager = schemaManager; 455 456 AttributeType attributeType = null; 457 458 try 459 { 460 attributeType = schemaManager.lookupAttributeTypeRegistry( normType ); 461 } 462 catch ( LdapException le ) 463 { 464 String message = I18n.err( I18n.ERR_04188 ); 465 LOG.error( message ); 466 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le ); 467 } 468 469 if ( this.attributeType == attributeType ) 470 { 471 // No need to normalize again 472 return; 473 } 474 else 475 { 476 this.attributeType = attributeType; 477 } 478 479 normType = attributeType.getOid(); 480 481 if ( normValue != null ) 482 { 483 return; 484 } 485 486 try 487 { 488 // We use the Equality matching rule to normalize the value 489 MatchingRule equalityMatchingRule = attributeType.getEquality(); 490 491 if ( equalityMatchingRule != null ) 492 { 493 this.normValue = equalityMatchingRule.getNormalizer().normalize( upValue ); 494 } 495 else 496 { 497 this.normValue = upValue; 498 } 499 } 500 catch ( LdapException le ) 501 { 502 String message = I18n.err( I18n.ERR_04188 ); 503 LOG.error( message ); 504 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le ); 505 } 506 507 hashCode(); 508 } 509 } 510 511 512 /** 513 * Get the normalized type of a Ava 514 * 515 * @return The normalized type 516 */ 517 public String getNormType() 518 { 519 return normType; 520 } 521 522 /** 523 * Get the user provided type of a Ava 524 * 525 * @return The user provided type 526 */ 527 public String getUpType() 528 { 529 return upType; 530 } 531 532 533 /** 534 * Get the Value of a Ava 535 * 536 * @return The value 537 */ 538 public Value<?> getNormValue() 539 { 540 return normValue.clone(); 541 } 542 543 /** 544 * Get the User Provided Value of a Ava 545 * 546 * @return The value 547 */ 548 public Value<?> getUpValue() 549 { 550 return upValue.clone(); 551 } 552 553 /** 554 * Get the normalized Name of a Ava 555 * 556 * @return The name 557 */ 558 public String getNormName() 559 { 560 return normalize(); 561 } 562 563 564 /** 565 * Get the user provided form of this attribute type and value 566 * 567 * @return The user provided form of this ava 568 */ 569 public String getUpName() 570 { 571 return upName; 572 } 573 574 575 /** 576 * Implements the cloning. 577 * 578 * @return a clone of this object 579 */ 580 public Ava clone() 581 { 582 try 583 { 584 Ava clone = (Ava) super.clone(); 585 clone.upValue = upValue.clone(); 586 clone.normValue = normValue.clone(); 587 588 return clone; 589 } 590 catch ( CloneNotSupportedException cnse ) 591 { 592 throw new Error( "Assertion failure" ); 593 } 594 } 595 596 597 private static final boolean[] DN_ESCAPED_CHARS = new boolean[] 598 { 599 true, true, true, true, true, true, true, true, // 0x00 -> 0x07 600 true, true, true, true, true, true, true, true, // 0x08 -> 0x0F 601 true, true, true, true, true, true, true, true, // 0x10 -> 0x17 602 true, true, true, true, true, true, true, true, // 0x18 -> 0x1F 603 true, false, true, true, false, false, false, false, // 0x20 -> 0x27 ' ', '"', '#' 604 false, false, false, true, true, false, false, false, // 0x28 -> 0x2F '+', ',' 605 false, false, false, false, false, false, false, false, // 0x30 -> 0x37 606 false, false, false, true, true, false, true, false, // 0x38 -> 0x3F ';', '<', '>' 607 false, false, false, false, false, false, false, false, // 0x40 -> 0x47 608 false, false, false, false, false, false, false, false, // 0x48 -> 0x4F 609 false, false, false, false, false, false, false, false, // 0x50 -> 0x57 610 false, false, false, false, true, false, false, false, // 0x58 -> 0x5F 611 false, false, false, false, false, false, false, false, // 0x60 -> 0x67 612 false, false, false, false, false, false, false, false, // 0x68 -> 0x6F 613 false, false, false, false, false, false, false, false, // 0x70 -> 0x77 614 false, false, false, false, false, false, false, false, // 0x78 -> 0x7F 615 }; 616 617 618 /** 619 * Normalize the value in order to be able to use it in a DN as a String. Some 620 * characters will be escaped (prefixed with '\'), 621 * 622 * @return The normalized Ava 623 */ 624 private String normalizeValue() 625 { 626 // The result will be gathered in a stringBuilder 627 StringBuilder sb = new StringBuilder(); 628 629 String normalizedValue = normValue.getString(); 630 int valueLength = normalizedValue.length(); 631 632 if ( normalizedValue.length() > 0 ) 633 { 634 char[] chars = normalizedValue.toCharArray(); 635 636 // Here, we have a char to escape. Start again the loop... 637 for ( int i = 0; i < valueLength; i++ ) 638 { 639 char c = chars[i]; 640 641 if ( ( c >= 0 ) && ( c < DN_ESCAPED_CHARS.length ) && DN_ESCAPED_CHARS[ c ] ) 642 { 643 // Some chars need to be escaped even if they are US ASCII 644 // Just prefix them with a '\' 645 // Special cases are ' ' (space), '#') which need a special 646 // treatment. 647 switch ( c ) 648 { 649 case ' ' : 650 if ( ( i == 0 ) || ( i == valueLength - 1 ) ) 651 { 652 sb.append( "\\ " ); 653 } 654 else 655 { 656 sb.append( ' ' ); 657 } 658 659 break; 660 661 case '#' : 662 if ( i == 0 ) 663 { 664 sb.append( "\\#" ); 665 continue; 666 } 667 else 668 { 669 sb.append( '#' ); 670 } 671 672 break; 673 674 default : 675 sb.append( '\\' ).append( c ); 676 } 677 } 678 else 679 { 680 // Standard ASCII chars are just appended 681 sb.append( c ); 682 } 683 } 684 } 685 686 return sb.toString(); 687 } 688 689 690 /** 691 * A Normalized String representation of a Ava : 692 * <ul> 693 * <li>type is trimed and lowercased</li> 694 * <li>value is trimed and lowercased, and special characters</li> 695 * </ul> 696 * are escaped if needed. 697 * 698 * @return A normalized string representing an Ava 699 */ 700 public String normalize() 701 { 702 if ( normValue.isHumanReadable() ) 703 { 704 // The result will be gathered in a stringBuilder 705 StringBuilder sb = new StringBuilder(); 706 707 // First, store the type and the '=' char 708 sb.append( normType ).append( '=' ); 709 710 String normalizedValue = normValue.getString(); 711 712 if ( normalizedValue.length() > 0 ) 713 { 714 sb.append( normalizeValue() ); 715 } 716 717 return sb.toString(); 718 } 719 else 720 { 721 return normType + "=#" 722 + Strings.dumpHexPairs( normValue .getBytes() ); 723 } 724 } 725 726 727 /** 728 * Gets the hashcode of this object. 729 * 730 * @see java.lang.Object#hashCode() 731 * @return The instance hash code 732 */ 733 public int hashCode() 734 { 735 if ( h == 0 ) 736 { 737 h = 37; 738 739 h = h*17 + ( normType != null ? normType.hashCode() : 0 ); 740 h = h*17 + ( normValue != null ? normValue.hashCode() : 0 ); 741 } 742 743 return h; 744 } 745 746 747 /** 748 * @see Object#equals(Object) 749 */ 750 public boolean equals( Object obj ) 751 { 752 if ( this == obj ) 753 { 754 return true; 755 } 756 757 if ( !( obj instanceof Ava) ) 758 { 759 return false; 760 } 761 762 Ava instance = (Ava)obj; 763 764 // Compare the type 765 if ( normType == null ) 766 { 767 if ( instance.normType != null ) 768 { 769 return false; 770 } 771 } 772 else 773 { 774 if ( !normType.equals( instance.normType ) ) 775 { 776 return false; 777 } 778 } 779 780 // Compare the values 781 if ( normValue.isNull() ) 782 { 783 return instance.normValue.isNull(); 784 } 785 else 786 { 787 if ( schemaManager != null ) 788 { 789 MatchingRule equalityMatchingRule = attributeType.getEquality(); 790 791 if ( equalityMatchingRule != null ) 792 { 793 return equalityMatchingRule.getLdapComparator().compare( normValue.getValue(), instance.normValue.getValue() ) == 0; 794 } 795 796 return false; 797 } 798 else 799 { 800 return normValue.equals( instance.normValue ); 801 } 802 } 803 } 804 805 806 /** 807 * 808 * An Ava is composed of a type and a value. 809 * The data are stored following the structure : 810 * <ul> 811 * <li> 812 * <b>upName</b> The User provided ATAV 813 * </li> 814 * <li> 815 * <b>start</b> The position of this ATAV in the Dn 816 * </li> 817 * <li> 818 * <b>length</b> The ATAV length 819 * </li> 820 * <li> 821 * <b>upType</b> The user Provided Type 822 * </li> 823 * <li> 824 * <b>normType</b> The normalized AttributeType 825 * </li> 826 * <li> 827 * <b>isHR</b> Tells if the value is a String or not 828 * </li> 829 * </ul> 830 * <br/> 831 * if the value is a String : 832 * <ul> 833 * <li> 834 * <b>upValue</b> The User Provided value 835 * </li> 836 * <li> 837 * <b>value</b> The normalized value 838 * </li> 839 * </ul> 840 * <br/> 841 * if the value is binary : 842 * <ul> 843 * <li> 844 * <b>upValueLength</b> 845 * </li> 846 * <li> 847 * <b>upValue</b> The User Provided value 848 * </li> 849 * <li> 850 * <b>valueLength</b> 851 * </li> 852 * <li> 853 * <b>value</b> The normalized value 854 * </li> 855 * </ul> 856 * 857 * @see Externalizable#readExternal(ObjectInput) 858 * 859 * @throws IoException If the Ava can't be written in the stream 860 */ 861 public void writeExternal( ObjectOutput out ) throws IOException 862 { 863 if ( Strings.isEmpty( upName ) 864 || Strings.isEmpty( upType ) 865 || Strings.isEmpty( normType ) 866 || ( upValue.isNull() ) 867 || ( normValue.isNull() ) ) 868 { 869 String message = "Cannot serialize an wrong ATAV, "; 870 871 if ( Strings.isEmpty(upName) ) 872 { 873 message += "the upName should not be null or empty"; 874 } 875 else if ( Strings.isEmpty(upType) ) 876 { 877 message += "the upType should not be null or empty"; 878 } 879 else if ( Strings.isEmpty(normType) ) 880 { 881 message += "the normType should not be null or empty"; 882 } 883 else if ( upValue.isNull() ) 884 { 885 message += "the upValue should not be null"; 886 } 887 else if ( normValue.isNull() ) 888 { 889 message += "the value should not be null"; 890 } 891 892 LOG.error( message ); 893 throw new IOException( message ); 894 } 895 896 if ( upName != null ) 897 { 898 out.writeBoolean( true ); 899 out.writeUTF( upName ); 900 } 901 else 902 { 903 out.writeBoolean( false); 904 } 905 906 if ( upType != null ) 907 { 908 out.writeBoolean( true ); 909 out.writeUTF( upType ); 910 } 911 else 912 { 913 out.writeBoolean( false); 914 } 915 916 if ( normType != null ) 917 { 918 out.writeBoolean( true ); 919 out.writeUTF( normType ); 920 } 921 else 922 { 923 out.writeBoolean( false); 924 } 925 926 boolean isHR = normValue.isHumanReadable(); 927 928 out.writeBoolean( isHR ); 929 930 upValue.writeExternal( out ); 931 normValue.writeExternal( out ); 932 933 // Write the hashCode 934 out.writeInt( h ); 935 936 out.flush(); 937 } 938 939 940 /** 941 * We read back the data to create a new ATAV. The structure 942 * read is exposed in the {@link Ava#writeExternal(ObjectOutput)} 943 * method 944 * 945 * @see Externalizable#readExternal(ObjectInput) 946 * 947 * @throws IOException If the Ava can't b written to the stream 948 * @throws ClassNotFoundException If we can't deserialize an Ava from the stream 949 */ 950 public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException 951 { 952 boolean hasUpName = in.readBoolean(); 953 954 if ( hasUpName ) 955 { 956 upName = in.readUTF(); 957 } 958 959 boolean hasUpType = in.readBoolean(); 960 961 if ( hasUpType ) 962 { 963 upType = in.readUTF(); 964 } 965 966 boolean hasNormType = in.readBoolean(); 967 968 if ( hasNormType ) 969 { 970 normType = in.readUTF(); 971 } 972 973 if ( schemaManager != null ) 974 { 975 if ( !Strings.isEmpty( upType ) ) 976 { 977 attributeType = schemaManager.getAttributeType( upType ); 978 } 979 else 980 { 981 attributeType = schemaManager.getAttributeType( normType ); 982 } 983 } 984 985 boolean isHR = in.readBoolean(); 986 987 if ( isHR ) 988 { 989 upValue = StringValue.deserialize( attributeType, in ); 990 normValue = StringValue.deserialize( attributeType, in ); 991 } 992 else 993 { 994 upValue = BinaryValue.deserialize( attributeType, in ); 995 normValue = BinaryValue.deserialize( attributeType, in ); 996 } 997 998 h = in.readInt(); 999 1000 if ( schemaManager != null ) 1001 { 1002 attributeType = schemaManager.getAttributeType( upType ); 1003 } 1004 } 1005 1006 1007 /** 1008 * Tells if the Ava is schema aware or not. 1009 * 1010 * @return true if the Ava is schema aware 1011 */ 1012 public boolean isSchemaAware() 1013 { 1014 return attributeType != null; 1015 } 1016 1017 1018 /** 1019 * @return the attributeType 1020 */ 1021 public AttributeType getAttributeType() 1022 { 1023 return attributeType; 1024 } 1025 1026 1027 /** 1028 * A String representation of an Ava, as provided by the user. 1029 * 1030 * @return A string representing an Ava 1031 */ 1032 public String toString() 1033 { 1034 return upName; 1035 } 1036}