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