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