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 * https://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.entry; 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.exception.LdapException; 031import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException; 032import org.apache.directory.api.ldap.model.message.ResultCodeEnum; 033import org.apache.directory.api.ldap.model.schema.AttributeType; 034import org.apache.directory.api.ldap.model.schema.LdapComparator; 035import org.apache.directory.api.ldap.model.schema.LdapSyntax; 036import org.apache.directory.api.ldap.model.schema.MatchingRule; 037import org.apache.directory.api.ldap.model.schema.Normalizer; 038import org.apache.directory.api.ldap.model.schema.SyntaxChecker; 039import org.apache.directory.api.ldap.model.schema.comparators.StringComparator; 040import org.apache.directory.api.ldap.model.schema.normalizers.NoOpNormalizer; 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 Class for wrapping attribute values stored into an Entry Attribute, or a AVA. 049 * 050 * We keep the value as byte[] unless we need to convert them to a String (if we have 051 * a HR Value). 052 * 053 * The serialized Value will be stored as : 054 * 055 * <pre> 056 * +---------+ 057 * | boolean | isHR flag 058 * +---------+ 059 * | boolean | TRUE if the value is not null, FALSE otherwise 060 * +---------+ 061 * [| int |] If the previous flag is TRUE, the length of the value 062 * [+---------+] 063 * [| byte[] |] The value itself 064 * [+---------+] 065 * | boolean | TRUE if we have a prepared String 066 * +---------+ 067 * [| String |] The prepared String if we have it 068 * [+---------+] 069 * </pre> 070 * 071 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 072 */ 073public class Value implements Cloneable, Externalizable, Comparable<Value> 074{ 075 /** Used for serialization */ 076 private static final long serialVersionUID = 2L; 077 078 /** logger for reporting errors that might not be handled properly upstream */ 079 private static final Logger LOG = LoggerFactory.getLogger( Value.class ); 080 081 /** reference to the attributeType associated with the value */ 082 private transient AttributeType attributeType; 083 084 /** the User Provided value if it's a String */ 085 private String upValue; 086 087 /** the prepared representation of the user provided value if it's a String */ 088 private String normValue; 089 090 /** The computed hashcode. We don't want to compute it each time the hashcode() method is called */ 091 private volatile int h; 092 093 /** The UTF-8 bytes for this value (we use the UP value) */ 094 private byte[] bytes; 095 096 /** Two flags used to tell if the value is HR or not in serialization */ 097 private boolean isHR = true; 098 099 /** A default comparator if we don't have an EQUALITY MR */ 100 private static StringComparator stringComparator = new StringComparator( null ); 101 102 // ----------------------------------------------------------------------- 103 // Constructors 104 // ----------------------------------------------------------------------- 105 /** 106 * Creates a Value with an initial user provided String value. 107 * 108 * @param upValue the value to wrap. It can be null 109 */ 110 public Value( String upValue ) 111 { 112 this.upValue = upValue; 113 114 // We can't normalize the value, we store it as is 115 normValue = upValue; 116 117 if ( upValue != null ) 118 { 119 bytes = Strings.getBytesUtf8( upValue ); 120 } 121 122 hashCode(); 123 } 124 125 126 /** 127 * Creates a Value with an initial user provided binary value. 128 * 129 * @param value the binary value to wrap which may be null, or a zero length byte array 130 */ 131 public Value( byte[] value ) 132 { 133 if ( value != null ) 134 { 135 bytes = new byte[value.length]; 136 System.arraycopy( value, 0, bytes, 0, value.length ); 137 } 138 else 139 { 140 bytes = null; 141 } 142 143 isHR = false; 144 145 hashCode(); 146 } 147 148 149 /** 150 * Creates a schema aware binary Value with an initial value. 151 * 152 * @param attributeType the schema type associated with this Value 153 * @param upValue the value to wrap 154 * @throws LdapInvalidAttributeValueException If the added value is invalid accordingly 155 * to the schema 156 */ 157 public Value( AttributeType attributeType, byte[] upValue ) throws LdapInvalidAttributeValueException 158 { 159 init( attributeType ); 160 161 if ( upValue != null ) 162 { 163 bytes = new byte[upValue.length]; 164 System.arraycopy( upValue, 0, bytes, 0, upValue.length ); 165 166 if ( isHR ) 167 { 168 this.upValue = Strings.utf8ToString( upValue ); 169 } 170 } 171 else 172 { 173 bytes = null; 174 } 175 176 if ( ( attributeType != null ) && !attributeType.isRelaxed() ) 177 { 178 // Check the value 179 SyntaxChecker syntaxChecker = attributeType.getSyntax().getSyntaxChecker(); 180 181 if ( syntaxChecker != null ) 182 { 183 if ( !syntaxChecker.isValidSyntax( bytes ) ) 184 { 185 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, 186 I18n.err( I18n.ERR_13246_INVALID_VALUE_PER_SYNTAX ) ); 187 } 188 } 189 else 190 { 191 // We should always have a SyntaxChecker 192 throw new IllegalArgumentException( I18n.err( I18n.ERR_13219_NULL_SYNTAX_CHECKER, normValue ) ); 193 } 194 } 195 196 hashCode(); 197 } 198 199 200 private void init( AttributeType attributeType ) 201 { 202 if ( attributeType != null ) 203 { 204 if ( attributeType.getSyntax() == null ) 205 { 206 // Some broken LDAP servers do not have proper syntax definitions, default to HR 207 // Log this on trace level only. Otherwise we get logs full of errors when working 208 // with AD and similar not-really-LDAP-compliant servers. 209 if ( LOG.isTraceEnabled() ) 210 { 211 LOG.trace( I18n.err( I18n.ERR_13225_NO_SYNTAX ) ); 212 } 213 214 isHR = true; 215 } 216 else 217 { 218 isHR = attributeType.getSyntax().isHumanReadable(); 219 } 220 } 221 else 222 { 223 if ( LOG.isWarnEnabled() ) 224 { 225 LOG.warn( I18n.msg( I18n.MSG_13202_AT_IS_NULL ) ); 226 } 227 } 228 229 this.attributeType = attributeType; 230 } 231 232 233 /** 234 * Creates a schema aware binary Value with an initial value. This method is 235 * only to be used by deserializers. 236 * 237 * @param attributeType the schema type associated with this Value 238 */ 239 /* Package protected*/ Value( AttributeType attributeType ) 240 { 241 init( attributeType ); 242 } 243 244 245 /** 246 * Creates a schema aware StringValue with an initial user provided String value. 247 * 248 * @param attributeType the schema type associated with this StringValue 249 * @param upValue the value to wrap 250 * @throws LdapInvalidAttributeValueException If the added value is invalid accordingly 251 * to the schema 252 */ 253 public Value( AttributeType attributeType, String upValue ) throws LdapInvalidAttributeValueException 254 { 255 init( attributeType ); 256 this.upValue = upValue; 257 258 if ( upValue != null ) 259 { 260 bytes = Strings.getBytesUtf8( upValue ); 261 } 262 else 263 { 264 bytes = null; 265 } 266 267 try 268 { 269 computeNormValue(); 270 } 271 catch ( LdapException le ) 272 { 273 LOG.error( le.getMessage() ); 274 throw new IllegalArgumentException( I18n.err( I18n.ERR_13247_INVALID_VALUE_CANT_NORMALIZE, upValue ) ); 275 } 276 277 if ( !attributeType.isRelaxed() ) 278 { 279 // Check the value 280 LdapSyntax syntax = attributeType.getSyntax(); 281 282 if ( ( syntax != null ) && ( syntax.getSyntaxChecker() != null ) ) 283 { 284 if ( !attributeType.getSyntax().getSyntaxChecker().isValidSyntax( upValue ) ) 285 { 286 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, 287 I18n.err( I18n.ERR_13246_INVALID_VALUE_PER_SYNTAX ) ); 288 } 289 } 290 else 291 { 292 // We should always have a SyntaxChecker 293 throw new IllegalArgumentException( I18n.err( I18n.ERR_13219_NULL_SYNTAX_CHECKER, normValue ) ); 294 } 295 } 296 297 hashCode(); 298 } 299 300 301 /** 302 * Creates a schema aware StringValue with an initial user provided String value and 303 * its normalized Value 304 * 305 * @param attributeType the schema type associated with this StringValue 306 * @param upValue the value to wrap 307 * @param normValue the normalized value to wrap 308 * @throws LdapInvalidAttributeValueException If the added value is invalid accordingly 309 * to the schema 310 */ 311 public Value( AttributeType attributeType, String upValue, String normValue ) throws LdapInvalidAttributeValueException 312 { 313 init( attributeType ); 314 this.upValue = upValue; 315 316 if ( upValue != null ) 317 { 318 bytes = Strings.getBytesUtf8( upValue ); 319 } 320 else 321 { 322 bytes = null; 323 } 324 325 this.normValue = normValue; 326 327 if ( !attributeType.isRelaxed() ) 328 { 329 // Check the value 330 if ( attributeType.getSyntax().getSyntaxChecker() != null ) 331 { 332 if ( !attributeType.getSyntax().getSyntaxChecker().isValidSyntax( upValue ) ) 333 { 334 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, 335 I18n.err( I18n.ERR_13246_INVALID_VALUE_PER_SYNTAX ) ); 336 } 337 } 338 else 339 { 340 // We should always have a SyntaxChecker 341 throw new IllegalArgumentException( I18n.err( I18n.ERR_13219_NULL_SYNTAX_CHECKER, normValue ) ); 342 } 343 } 344 345 hashCode(); 346 } 347 348 349 /** 350 * Creates a Value from an existing Value with an AttributeType 351 * 352 * @param attributeType the schema attribute type associated with this StringValue 353 * @param value the original Value 354 * @throws LdapInvalidAttributeValueException If the value is invalid 355 */ 356 public Value( AttributeType attributeType, Value value ) throws LdapInvalidAttributeValueException 357 { 358 init( attributeType ); 359 360 if ( isHR ) 361 { 362 if ( value.isHR ) 363 { 364 this.upValue = value.upValue; 365 } 366 else 367 { 368 this.upValue = Strings.utf8ToString( value.bytes ); 369 } 370 } 371 372 try 373 { 374 computeNormValue(); 375 } 376 catch ( LdapException le ) 377 { 378 LOG.error( le.getMessage() ); 379 throw new IllegalArgumentException( I18n.err( I18n.ERR_13247_INVALID_VALUE_CANT_NORMALIZE, upValue ) ); 380 } 381 382 // Check the normValue 383 if ( !attributeType.isRelaxed() ) 384 { 385 // Check the value 386 if ( attributeType.getSyntax().getSyntaxChecker() != null ) 387 { 388 attributeType.getSyntax().getSyntaxChecker().isValidSyntax( value.normValue ); 389 } 390 else 391 { 392 // We should always have a SyntaxChecker 393 throw new IllegalArgumentException( I18n.err( I18n.ERR_13219_NULL_SYNTAX_CHECKER, normValue ) ); 394 } 395 } 396 397 // We have to copy the byte[], they are just referenced by super.clone() 398 if ( value.bytes != null ) 399 { 400 bytes = new byte[value.bytes.length]; 401 System.arraycopy( value.bytes, 0, bytes, 0, value.bytes.length ); 402 } 403 404 hashCode(); 405 } 406 407 408 /** 409 * Create a Value with an AttributeType. It will not contain anything and will only be used by 410 * the deserializer. 411 * 412 * @param attributeType The ATttributeType to use 413 * @return An instance of value. 414 */ 415 public static Value createValue( AttributeType attributeType ) 416 { 417 return new Value( attributeType ); 418 } 419 420 421 /** 422 * Clone a Value 423 * 424 * @return A cloned value 425 */ 426 @Override 427 public Value clone() 428 { 429 try 430 { 431 Value clone = ( Value ) super.clone(); 432 433 if ( isHR ) 434 { 435 return clone; 436 } 437 else 438 { 439 // We have to copy the byte[], they are just referenced by suoer.clone() 440 if ( bytes != null ) 441 { 442 clone.bytes = new byte[bytes.length]; 443 System.arraycopy( bytes, 0, clone.bytes, 0, bytes.length ); 444 } 445 } 446 447 return clone; 448 } 449 catch ( CloneNotSupportedException cnse ) 450 { 451 // Do nothing 452 return null; 453 } 454 } 455 456 457 /** 458 * Check if the contained value is null or not 459 * 460 * @return <code>true</code> if the inner value is null. 461 */ 462 public boolean isNull() 463 { 464 if ( isHR ) 465 { 466 return upValue == null; 467 } 468 else 469 { 470 return bytes == null; 471 } 472 } 473 474 475 /** 476 * Get the associated AttributeType 477 * 478 * @return The AttributeType 479 */ 480 public AttributeType getAttributeType() 481 { 482 return attributeType; 483 } 484 485 486 /** 487 * Check if the value is stored into an instance of the given 488 * AttributeType, or one of its ascendant. 489 * 490 * For instance, if the Value is associated with a CommonName, 491 * checking for Name will match. 492 * 493 * @param attributeType The AttributeType we are looking at 494 * @return <code>true</code> if the value is associated with the given 495 * attributeType or one of its ascendant 496 */ 497 public boolean isInstanceOf( AttributeType attributeType ) 498 { 499 return ( attributeType != null ) 500 && ( this.attributeType.equals( attributeType ) || this.attributeType.isDescendantOf( attributeType ) ); 501 } 502 503 504 505 506 /** 507 * @return The value as an escaped String 508 */ 509 public String getEscaped() 510 { 511 if ( Strings.isEmpty( bytes ) ) 512 { 513 return Strings.EMPTY_STRING; 514 } 515 516 StringBuilder sb = new StringBuilder(); 517 518 boolean leadChar = true; 519 520 for ( int pos = 0; pos < bytes.length; pos++ ) 521 { 522 boolean trailChar = pos == bytes.length - 1; 523 byte b = bytes[pos]; 524 525 switch ( b ) 526 { 527 case 0x00 : 528 sb.append( "\\00" ); 529 break; 530 531 case 0x01 : 532 case 0x02 : 533 case 0x03 : 534 case 0x04 : 535 case 0x05 : 536 case 0x06 : 537 case 0x07 : 538 case 0x08 : 539 case 0x09 : 540 case 0x0A : 541 case 0x0B : 542 case 0x0C : 543 case 0x0D : 544 case 0x0E : 545 case 0x0F : 546 case 0x10 : 547 case 0x11 : 548 case 0x12 : 549 case 0x13 : 550 case 0x14 : 551 case 0x15 : 552 case 0x16 : 553 case 0x17 : 554 case 0x18 : 555 case 0x19 : 556 case 0x1A : 557 case 0x1B : 558 case 0x1C : 559 case 0x1D : 560 case 0x1E : 561 case 0x1F : 562 sb.append( ( char ) b ); 563 break; 564 565 case 0x20 : 566 if ( leadChar || trailChar ) 567 { 568 sb.append( "\\ " ); 569 } 570 else 571 { 572 sb.append( ( char ) b ); 573 } 574 575 break; 576 577 case 0x21 : 578 sb.append( ( char ) b ); 579 break; 580 581 582 case 0x22 : 583 sb.append( "\\\"" ); 584 break; 585 586 case 0x23 : 587 if ( leadChar ) 588 { 589 sb.append( "\\#" ); 590 } 591 else 592 { 593 sb.append( '#' ); 594 } 595 596 break; 597 598 case 0x24 : 599 case 0x25 : 600 case 0x26 : 601 case 0x27 : 602 case 0x28 : 603 case 0x29 : 604 case 0x2A : 605 sb.append( ( char ) b ); 606 break; 607 608 case 0x2B : 609 sb.append( "\\+" ); 610 break; 611 612 case 0x2C : 613 sb.append( "\\," ); 614 break; 615 616 case 0x2D : 617 case 0x2E : 618 case 0x2F : 619 case 0x30 : 620 case 0x31 : 621 case 0x32 : 622 case 0x33 : 623 case 0x34 : 624 case 0x35 : 625 case 0x36 : 626 case 0x37 : 627 case 0x38 : 628 case 0x39 : 629 case 0x3A : 630 sb.append( ( char ) b ); 631 break; 632 633 case 0x3B : 634 sb.append( "\\;" ); 635 break; 636 637 case 0x3C : 638 sb.append( "\\<" ); 639 break; 640 641 case 0x3D : 642 sb.append( ( char ) b ); 643 break; 644 645 case 0x3E : 646 sb.append( "\\>" ); 647 break; 648 649 case 0x3F : 650 case 0x40 : 651 case 0x41 : 652 case 0x42 : 653 case 0x43 : 654 case 0x44 : 655 case 0x45 : 656 case 0x46 : 657 case 0x47 : 658 case 0x48 : 659 case 0x49 : 660 case 0x4A : 661 case 0x4B : 662 case 0x4C : 663 case 0x4D : 664 case 0x4E : 665 case 0x4F : 666 case 0x50 : 667 case 0x51 : 668 case 0x52 : 669 case 0x53 : 670 case 0x54 : 671 case 0x55 : 672 case 0x56 : 673 case 0x57 : 674 case 0x58 : 675 case 0x59 : 676 case 0x5A : 677 case 0x5B : 678 sb.append( ( char ) b ); 679 break; 680 681 case 0x5C : 682 sb.append( "\\\\" ); 683 break; 684 685 case 0x5D : 686 case 0x5E : 687 case 0x5F : 688 case 0x60 : 689 case 0x61 : 690 case 0x62 : 691 case 0x63 : 692 case 0x64 : 693 case 0x65 : 694 case 0x66 : 695 case 0x67 : 696 case 0x68 : 697 case 0x69 : 698 case 0x6A : 699 case 0x6B : 700 case 0x6C : 701 case 0x6D : 702 case 0x6E : 703 case 0x6F : 704 case 0x70 : 705 case 0x71 : 706 case 0x72 : 707 case 0x73 : 708 case 0x74 : 709 case 0x75 : 710 case 0x76 : 711 case 0x77 : 712 case 0x78 : 713 case 0x79 : 714 case 0x7A : 715 case 0x7B : 716 case 0x7C : 717 case 0x7D : 718 case 0x7E : 719 case 0x7F : 720 sb.append( ( char ) b ); 721 break; 722 723 // Between 0x80 and 0xC1, this is an octet 724 case ( byte ) 0x80 : 725 case ( byte ) 0x81 : 726 case ( byte ) 0x82 : 727 case ( byte ) 0x83 : 728 case ( byte ) 0x84 : 729 case ( byte ) 0x85 : 730 case ( byte ) 0x86 : 731 case ( byte ) 0x87 : 732 case ( byte ) 0x88 : 733 case ( byte ) 0x89 : 734 case ( byte ) 0x8A : 735 case ( byte ) 0x8B : 736 case ( byte ) 0x8C : 737 case ( byte ) 0x8D : 738 case ( byte ) 0x8E : 739 case ( byte ) 0x8F : 740 case ( byte ) 0x90 : 741 case ( byte ) 0x91 : 742 case ( byte ) 0x92 : 743 case ( byte ) 0x93 : 744 case ( byte ) 0x94 : 745 case ( byte ) 0x95 : 746 case ( byte ) 0x96 : 747 case ( byte ) 0x97 : 748 case ( byte ) 0x98 : 749 case ( byte ) 0x99 : 750 case ( byte ) 0x9A : 751 case ( byte ) 0x9B : 752 case ( byte ) 0x9C : 753 case ( byte ) 0x9D : 754 case ( byte ) 0x9E : 755 case ( byte ) 0x9F : 756 case ( byte ) 0xA0 : 757 case ( byte ) 0xA1 : 758 case ( byte ) 0xA2 : 759 case ( byte ) 0xA3 : 760 case ( byte ) 0xA4 : 761 case ( byte ) 0xA5 : 762 case ( byte ) 0xA6 : 763 case ( byte ) 0xA7 : 764 case ( byte ) 0xA8 : 765 case ( byte ) 0xA9 : 766 case ( byte ) 0xAA : 767 case ( byte ) 0xAB : 768 case ( byte ) 0xAC : 769 case ( byte ) 0xAD : 770 case ( byte ) 0xAE : 771 case ( byte ) 0xAF : 772 case ( byte ) 0xB0 : 773 case ( byte ) 0xB1 : 774 case ( byte ) 0xB2 : 775 case ( byte ) 0xB3 : 776 case ( byte ) 0xB4 : 777 case ( byte ) 0xB5 : 778 case ( byte ) 0xB6 : 779 case ( byte ) 0xB7 : 780 case ( byte ) 0xB8 : 781 case ( byte ) 0xB9 : 782 case ( byte ) 0xBA : 783 case ( byte ) 0xBB : 784 case ( byte ) 0xBC : 785 case ( byte ) 0xBD : 786 case ( byte ) 0xBE : 787 case ( byte ) 0xBF : 788 case ( byte ) 0xC0 : 789 case ( byte ) 0xC1 : 790 sb.append( '\\' ).append( Strings.byteToString( b ) ); 791 break; 792 793 // Between 0xC2 and 0xDF, we may have a UTF-2 char 794 case ( byte ) 0xC2 : 795 case ( byte ) 0xC3 : 796 case ( byte ) 0xC4 : 797 case ( byte ) 0xC5 : 798 case ( byte ) 0xC6 : 799 case ( byte ) 0xC7 : 800 case ( byte ) 0xC8 : 801 case ( byte ) 0xC9 : 802 case ( byte ) 0xCA : 803 case ( byte ) 0xCB : 804 case ( byte ) 0xCC : 805 case ( byte ) 0xCD : 806 case ( byte ) 0xCE : 807 case ( byte ) 0xCF : 808 case ( byte ) 0xD0 : 809 case ( byte ) 0xD1 : 810 case ( byte ) 0xD2 : 811 case ( byte ) 0xD3 : 812 case ( byte ) 0xD4 : 813 case ( byte ) 0xD5 : 814 case ( byte ) 0xD6 : 815 case ( byte ) 0xD7 : 816 case ( byte ) 0xD8 : 817 case ( byte ) 0xD9 : 818 case ( byte ) 0xDA : 819 case ( byte ) 0xDB : 820 case ( byte ) 0xDC : 821 case ( byte ) 0xDD : 822 case ( byte ) 0xDE : 823 case ( byte ) 0xDF : 824 // UTF2, if the following byte is in [0x80-0xBF] 825 if ( trailChar ) 826 { 827 // No next byte : this is an octet 828 sb.append( '\\' ).append( Strings.byteToString( b ) ); 829 } 830 else 831 { 832 int b2 = bytes[pos + 1] & 0x00FF; 833 834 if ( ( b2 >= 0x0080 ) && ( b2 <= 0x00BF ) ) 835 { 836 // This is an UTF-2 char 837 sb.append( Strings.utf8ToString( bytes, pos, 2 ) ); 838 pos++; 839 } 840 else 841 { 842 // Not an UTF-2 843 sb.append( '\\' ).append( Strings.byteToString( b ) ); 844 } 845 } 846 847 break; 848 849 case ( byte ) 0xE0 : 850 // May be an UTF-3, if the next byte is in [0xA0-0xBF], followed by a byte in [0x80-0xBF] 851 if ( trailChar ) 852 { 853 // No next byte : this is an octet 854 sb.append( '\\' ).append( Strings.byteToString( b ) ); 855 break; 856 } 857 858 if ( pos == bytes.length - 2 ) 859 { 860 // We only have 2 bytes : not an UTF-3 861 sb.append( '\\' ).append( Strings.byteToString( b ) ); 862 } 863 else 864 { 865 int b2 = bytes[pos + 1] & 0x00FF; 866 867 if ( ( b2 >= 0x00A0 ) && ( b2 <= 0x00BF ) ) 868 { 869 int b3 = bytes[pos + 2] & 0x00FF; 870 871 // Check that the third byte is in between 0x80-0xBF 872 if ( ( b3 >= 0x0080 ) && ( b3 <= 0x00BF ) ) 873 { 874 // UTF-3 875 sb.append( Strings.utf8ToString( bytes, pos, 3 ) ); 876 pos += 2; 877 } 878 else 879 { 880 // Not an UTF-3, dump one bytes 881 sb.append( '\\' ).append( Strings.byteToString( b ) ); 882 } 883 } 884 else 885 { 886 // Not an UTF-3 : dump two byte 887 sb.append( '\\' ).append( Strings.byteToString( b ) ); 888 } 889 } 890 891 break; 892 893 894 // Between E1 and EC, this may be an UTF-3 if the next two bytes are between 0x80 and 0xBF 895 case ( byte ) 0xE1 : 896 case ( byte ) 0xE2 : 897 case ( byte ) 0xE3 : 898 case ( byte ) 0xE4 : 899 case ( byte ) 0xE5 : 900 case ( byte ) 0xE6 : 901 case ( byte ) 0xE7 : 902 case ( byte ) 0xE8 : 903 case ( byte ) 0xE9 : 904 case ( byte ) 0xEA : 905 case ( byte ) 0xEB : 906 case ( byte ) 0xEC : 907 case ( byte ) 0xEE : 908 case ( byte ) 0xEF : 909 if ( trailChar ) 910 { 911 // No next byte : this is an octet 912 sb.append( '\\' ).append( Strings.byteToString( b ) ); 913 break; 914 } 915 916 if ( pos == bytes.length - 2 ) 917 { 918 // We only have 2 bytes : not an UTF-3 919 sb.append( '\\' ).append( Strings.byteToString( b ) ); 920 } 921 else 922 { 923 int b2 = bytes[pos + 1] & 0x00FF; 924 925 if ( ( b2 >= 0x0080 ) && ( b2 <= 0x00BF ) ) 926 { 927 int b3 = bytes[pos + 2] & 0x00FF; 928 929 // Check that the third byte is in between 0x80-0xBF 930 if ( ( b3 >= 0x0080 ) && ( b3 <= 0x00BF ) ) 931 { 932 // UTF-3 933 sb.append( Strings.utf8ToString( bytes, pos, 3 ) ); 934 pos += 2; 935 } 936 else 937 { 938 // Not an UTF-3, dump one byte 939 sb.append( '\\' ).append( Strings.byteToString( b ) ); 940 } 941 } 942 else 943 { 944 // Not an UTF-3 : dump one byte 945 sb.append( '\\' ).append( Strings.byteToString( b ) ); 946 pos++; 947 } 948 } 949 950 break; 951 952 case ( byte ) 0xED : 953 // May be an UTF-3 if the second byte is in [0x80-0x9F] and the third byte in [0x80-0xBF] 954 if ( trailChar ) 955 { 956 // No next byte : this is an octet 957 sb.append( '\\' ).append( Strings.byteToString( b ) ); 958 break; 959 } 960 961 if ( pos == bytes.length - 2 ) 962 { 963 // We only have 2 bytes : not an UTF-3 964 sb.append( '\\' ).append( Strings.byteToString( b ) ); 965 } 966 else 967 { 968 int b2 = bytes[pos + 1] & 0x00FF; 969 970 if ( ( b2 >= 0x0080 ) && ( b2 <= 0x009F ) ) 971 { 972 int b3 = bytes[pos + 2] & 0x00FF; 973 974 // Check that the third byte is in between 0x80-0xBF 975 if ( ( b3 >= 0x0080 ) && ( b3 <= 0x00BF ) ) 976 { 977 // UTF-3 978 sb.append( Strings.utf8ToString( bytes, pos, 3 ) ); 979 pos += 2; 980 } 981 else 982 { 983 // Not an UTF-3, dump one byte 984 sb.append( '\\' ).append( Strings.byteToString( b ) ); 985 } 986 } 987 else 988 { 989 // Not an UTF-3 : dump one byte 990 sb.append( '\\' ).append( Strings.byteToString( b ) ); 991 pos++; 992 } 993 } 994 995 break; 996 997 case ( byte ) 0xF0 : 998 // May be an UTF-4 if the second byte is in [0x90-0xBF] followed by two bytes in [0x80-0xBF] 999 if ( trailChar ) 1000 { 1001 // No next byte : this is an octet 1002 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1003 break; 1004 } 1005 1006 if ( pos == bytes.length - 3 ) 1007 { 1008 // We only have 2 bytes : not an UTF-4 1009 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1010 } 1011 else 1012 { 1013 int b2 = bytes[pos + 1] & 0x00FF; 1014 1015 if ( ( b2 >= 0x0090 ) && ( b2 <= 0x00BF ) ) 1016 { 1017 int b3 = bytes[pos + 2] & 0x00FF; 1018 1019 // Check that the third byte is in between 0x80-0xBF 1020 if ( ( b3 >= 0x0080 ) && ( b3 <= 0x00BF ) ) 1021 { 1022 int b4 = bytes[pos + 3] & 0x00FF; 1023 1024 // Check that the forth byte is in between 0x80-0xBF 1025 if ( ( b4 >= 0x0080 ) && ( b4 <= 0x00BF ) ) 1026 { 1027 // UTF-4 1028 sb.append( Strings.utf8ToString( bytes, pos, 4 ) ); 1029 pos += 3; 1030 } 1031 else 1032 { 1033 // Not an UTF-4, dump one byte 1034 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1035 } 1036 } 1037 else 1038 { 1039 // Not an UTF-4, dump one byte 1040 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1041 } 1042 } 1043 else 1044 { 1045 // Not an UTF-4 : dump one byte 1046 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1047 pos++; 1048 } 1049 } 1050 1051 break; 1052 1053 case ( byte ) 0xF1 : 1054 case ( byte ) 0xF2 : 1055 case ( byte ) 0xF3 : 1056 // May be an UTF-4 1057 // May be an UTF-4 if it's followed by three bytes in [0x80-0xBF] 1058 if ( trailChar ) 1059 { 1060 // No next byte : this is an octet 1061 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1062 break; 1063 } 1064 1065 if ( pos == bytes.length - 3 ) 1066 { 1067 // We only have 2 bytes : not an UTF-4 1068 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1069 } 1070 else 1071 { 1072 int b2 = bytes[pos + 1] & 0x00FF; 1073 1074 if ( ( b2 >= 0x0080 ) && ( b2 <= 0x00BF ) ) 1075 { 1076 int b3 = bytes[pos + 2] & 0x00FF; 1077 1078 // Check that the third byte is in between 0x80-0xBF 1079 if ( ( b3 >= 0x0080 ) && ( b3 <= 0x00BF ) ) 1080 { 1081 int b4 = bytes[pos + 3] & 0x00FF; 1082 1083 // Check that the forth byte is in between 0x80-0xBF 1084 if ( ( b4 >= 0x0080 ) && ( b4 <= 0x00BF ) ) 1085 { 1086 // UTF-4 1087 sb.append( Strings.utf8ToString( bytes, pos, 4 ) ); 1088 pos += 3; 1089 } 1090 else 1091 { 1092 // Not an UTF-4, dump one byte 1093 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1094 } 1095 } 1096 else 1097 { 1098 // Not an UTF-4, dump one byte 1099 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1100 } 1101 } 1102 else 1103 { 1104 // Not an UTF-4 : dump one byte 1105 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1106 pos++; 1107 } 1108 } 1109 1110 break; 1111 1112 case ( byte ) 0xF4 : 1113 // May be an UTF-4 if the second byte is in [0x80-0x8F] followed by two bytes in [0x80-0xBF] 1114 if ( trailChar ) 1115 { 1116 // No next byte : this is an octet 1117 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1118 break; 1119 } 1120 1121 if ( pos == bytes.length - 3 ) 1122 { 1123 // We only have 2 bytes : not an UTF-4 1124 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1125 } 1126 else 1127 { 1128 int b2 = bytes[pos + 1] & 0x00FF; 1129 1130 if ( ( b2 >= 0x0080 ) && ( b2 <= 0x008F ) ) 1131 { 1132 int b3 = bytes[pos + 2] & 0x00FF; 1133 1134 // Check that the third byte is in between 0x80-0xBF 1135 if ( ( b3 >= 0x0080 ) && ( b3 <= 0x00BF ) ) 1136 { 1137 int b4 = bytes[pos + 3] & 0x00FF; 1138 1139 // Check that the forth byte is in between 0x80-0xBF 1140 if ( ( b4 >= 0x0080 ) && ( b4 <= 0x00BF ) ) 1141 { 1142 // UTF-4 1143 sb.append( Strings.utf8ToString( bytes, pos, 4 ) ); 1144 pos += 3; 1145 } 1146 else 1147 { 1148 // Not an UTF-4, dump one byte 1149 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1150 } 1151 } 1152 else 1153 { 1154 // Not an UTF-4, dump one byte 1155 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1156 } 1157 } 1158 else 1159 { 1160 // Not an UTF-4 : dump one byte 1161 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1162 pos++; 1163 } 1164 } 1165 1166 break; 1167 1168 1169 default : 1170 // octet 1171 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1172 1173 break; 1174 1175 } 1176 1177 if ( leadChar ) 1178 { 1179 leadChar = false; 1180 } 1181 } 1182 1183 return sb.toString(); 1184 } 1185 1186 1187 /** 1188 * Get the User Provided value. If the value is Human Readable, it will return 1189 * the stored String, otherwise it will returns a String based on the bytes - which may be 1190 * invalid if the value is a pure binary -. 1191 * 1192 * @return The user provided value 1193 */ 1194 public String getString() 1195 { 1196 if ( isHR ) 1197 { 1198 return upValue; 1199 } 1200 else 1201 { 1202 return Strings.utf8ToString( bytes ); 1203 } 1204 } 1205 1206 1207 /** 1208 * Compute the normalized value 1209 * 1210 * @throws LdapException If we were'nt able to normalize the value 1211 */ 1212 private void computeNormValue() throws LdapException 1213 { 1214 if ( upValue == null ) 1215 { 1216 return; 1217 } 1218 1219 Normalizer normalizer; 1220 1221 // We should have a Equality MatchingRule 1222 MatchingRule equality = attributeType.getEquality(); 1223 1224 if ( equality == null ) 1225 { 1226 // Let's try with the Substring MatchingRule 1227 MatchingRule subString = attributeType.getSubstring(); 1228 1229 if ( subString == null ) 1230 { 1231 // last chance : ordering matching rule 1232 MatchingRule ordering = attributeType.getOrdering(); 1233 1234 if ( ordering == null ) 1235 { 1236 // Ok, no luck. Use a NoOp normalizer 1237 normalizer = new NoOpNormalizer(); 1238 } 1239 else 1240 { 1241 normalizer = ordering.getNormalizer(); 1242 } 1243 } 1244 else 1245 { 1246 normalizer = subString.getNormalizer(); 1247 } 1248 } 1249 else 1250 { 1251 normalizer = equality.getNormalizer(); 1252 } 1253 1254 if ( normalizer == null ) 1255 { 1256 throw new IllegalArgumentException( I18n.err( I18n.ERR_13220_NO_NORMALIZER ) ); 1257 } 1258 1259 // Now, normalize the upValue 1260 normValue = normalizer.normalize( upValue ); 1261 } 1262 1263 1264 /** 1265 * @return The normalized value 1266 */ 1267 public String getNormalized() 1268 { 1269 return normValue; 1270 } 1271 1272 1273 /** 1274 * @return The User Provided value 1275 */ 1276 public String getUpValue() 1277 { 1278 if ( isHR ) 1279 { 1280 return upValue; 1281 } 1282 else 1283 { 1284 return getEscaped(); 1285 } 1286 } 1287 1288 1289 /** 1290 * Get the wrapped value as a byte[], if and only if the Value is binary, 1291 * otherwise returns null. 1292 * 1293 * @return the wrapped value as a byte[] 1294 */ 1295 public byte[] getBytes() 1296 { 1297 if ( bytes == null ) 1298 { 1299 return null; 1300 } 1301 1302 if ( bytes.length == 0 ) 1303 { 1304 return Strings.EMPTY_BYTES; 1305 } 1306 1307 byte[] copy = new byte[bytes.length]; 1308 System.arraycopy( bytes, 0, copy, 0, bytes.length ); 1309 1310 return copy; 1311 } 1312 1313 1314 /** 1315 * Tells if the value is schema aware or not. 1316 * 1317 * @return <code>true</code> if the value is sxhema aware 1318 */ 1319 public boolean isSchemaAware() 1320 { 1321 return attributeType != null; 1322 } 1323 1324 1325 /** 1326 * Uses the syntaxChecker associated with the attributeType to check if the 1327 * value is valid. 1328 * 1329 * @param syntaxChecker the SyntaxChecker to use to validate the value 1330 * @return <code>true</code> if the value is valid 1331 * @exception LdapInvalidAttributeValueException if the value cannot be validated 1332 */ 1333 public final boolean isValid( SyntaxChecker syntaxChecker ) throws LdapInvalidAttributeValueException 1334 { 1335 if ( syntaxChecker == null ) 1336 { 1337 String message = I18n.err( I18n.ERR_13219_NULL_SYNTAX_CHECKER, toString() ); 1338 LOG.error( message ); 1339 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message ); 1340 } 1341 1342 // No attributeType, or it's in relaxed mode 1343 if ( isHR ) 1344 { 1345 // We need to prepare the String in this case 1346 return syntaxChecker.isValidSyntax( getString() ); 1347 } 1348 else 1349 { 1350 return syntaxChecker.isValidSyntax( bytes ); 1351 } 1352 } 1353 1354 1355 /** 1356 * Tells if the current value is Human Readable 1357 * 1358 * @return <code>true</code> if the value is a String, <code>false</code> otherwise 1359 */ 1360 public boolean isHumanReadable() 1361 { 1362 return isHR; 1363 } 1364 1365 1366 /** 1367 * @return The length of the interned value 1368 */ 1369 public int length() 1370 { 1371 if ( isHR ) 1372 { 1373 return upValue != null ? upValue.length() : 0; 1374 } 1375 else 1376 { 1377 return bytes != null ? bytes.length : 0; 1378 } 1379 } 1380 1381 1382 /** 1383 * Gets a comparator using getMatchingRule() to resolve the matching 1384 * that the comparator is extracted from. 1385 * 1386 * @return a comparator associated with the attributeType or null if one cannot be found 1387 */ 1388 private LdapComparator<?> getLdapComparator() 1389 { 1390 if ( attributeType != null ) 1391 { 1392 MatchingRule mr = attributeType.getEquality(); 1393 1394 if ( mr != null ) 1395 { 1396 return mr.getLdapComparator(); 1397 } 1398 } 1399 1400 return null; 1401 } 1402 1403 1404 /** 1405 * Serialize the Value into a buffer at the given position. 1406 * 1407 * @param buffer The buffer which will contain the serialized StringValue 1408 * @param pos The position in the buffer for the serialized value 1409 * @return The new position in the buffer 1410 */ 1411 public int serialize( byte[] buffer, int pos ) 1412 { 1413 // Compute the length : the isHR flag first, the value and prepared value presence flags 1414 int length = 1; 1415 byte[] preparedBytes = null; 1416 1417 if ( isHR ) 1418 { 1419 if ( upValue != null ) 1420 { 1421 // The presence flag, the length and the value 1422 length += 1 + 4 + bytes.length; 1423 } 1424 1425 if ( normValue != null ) 1426 { 1427 // The presence flag, the length and the value 1428 preparedBytes = Strings.getBytesUtf8( normValue ); 1429 length += 1 + 4 + preparedBytes.length; 1430 } 1431 } 1432 else 1433 { 1434 if ( bytes != null ) 1435 { 1436 length = 1 + 1 + 4 + bytes.length; 1437 } 1438 else 1439 { 1440 length = 1 + 1; 1441 } 1442 } 1443 1444 // Check that we will be able to store the data in the buffer 1445 if ( buffer.length - pos < length ) 1446 { 1447 throw new ArrayIndexOutOfBoundsException(); 1448 } 1449 1450 if ( isHR ) 1451 { 1452 buffer[pos++] = Serialize.TRUE; 1453 1454 // Write the user provided value, if not null 1455 if ( bytes != null ) 1456 { 1457 buffer[pos++] = Serialize.TRUE; 1458 pos = Serialize.serialize( bytes, buffer, pos ); 1459 } 1460 else 1461 { 1462 buffer[pos++] = Serialize.FALSE; 1463 } 1464 1465 // Write the prepared value, if not null 1466 if ( normValue != null ) 1467 { 1468 buffer[pos++] = Serialize.TRUE; 1469 pos = Serialize.serialize( preparedBytes, buffer, pos ); 1470 } 1471 else 1472 { 1473 buffer[pos++] = Serialize.FALSE; 1474 } 1475 } 1476 else 1477 { 1478 buffer[pos++] = Serialize.FALSE; 1479 1480 if ( bytes != null ) 1481 { 1482 buffer[pos++] = Serialize.TRUE; 1483 pos = Serialize.serialize( bytes, buffer, pos ); 1484 } 1485 else 1486 { 1487 buffer[pos++] = Serialize.FALSE; 1488 } 1489 } 1490 1491 return pos; 1492 } 1493 1494 1495 /** 1496 * Deserialize a Value. It will return a new Value instance. 1497 * 1498 * @param in The input stream 1499 * @return A new Value instance 1500 * @throws IOException If the stream can't be read 1501 * @throws ClassNotFoundException If we can't instanciate a Value 1502 */ 1503 public static Value deserialize( ObjectInput in ) throws IOException, ClassNotFoundException 1504 { 1505 Value value = new Value( ( AttributeType ) null ); 1506 value.readExternal( in ); 1507 1508 return value; 1509 } 1510 1511 1512 /** 1513 * Deserialize a Value. It will return a new Value instance. 1514 * 1515 * @param attributeType The AttributeType associated with the Value. Can be null 1516 * @param in The input stream 1517 * @return A new Value instance 1518 * @throws IOException If the stream can't be read 1519 * @throws ClassNotFoundException If we can't instanciate a Value 1520 */ 1521 public static Value deserialize( AttributeType attributeType, ObjectInput in ) throws IOException, ClassNotFoundException 1522 { 1523 Value value = new Value( attributeType ); 1524 value.readExternal( in ); 1525 1526 return value; 1527 } 1528 1529 1530 /** 1531 * Deserialize a StringValue from a byte[], starting at a given position 1532 * 1533 * @param buffer The buffer containing the StringValue 1534 * @param pos The position in the buffer 1535 * @return The new position 1536 * @throws IOException If the serialized value is not a StringValue 1537 * @throws LdapInvalidAttributeValueException If the value is invalid 1538 */ 1539 public int deserialize( byte[] buffer, int pos ) throws IOException, LdapInvalidAttributeValueException 1540 { 1541 if ( ( pos < 0 ) || ( pos >= buffer.length ) ) 1542 { 1543 throw new ArrayIndexOutOfBoundsException(); 1544 } 1545 1546 // Read the isHR flag 1547 isHR = Serialize.deserializeBoolean( buffer, pos ); 1548 pos++; 1549 1550 if ( isHR ) 1551 { 1552 // Read the user provided value, if it's not null 1553 boolean hasValue = Serialize.deserializeBoolean( buffer, pos ); 1554 pos++; 1555 1556 if ( hasValue ) 1557 { 1558 bytes = Serialize.deserializeBytes( buffer, pos ); 1559 pos += 4 + bytes.length; 1560 1561 upValue = Strings.utf8ToString( bytes ); 1562 } 1563 1564 // Read the prepared value, if not null 1565 boolean hasPreparedValue = Serialize.deserializeBoolean( buffer, pos ); 1566 pos++; 1567 1568 if ( hasPreparedValue ) 1569 { 1570 byte[] preparedBytes = Serialize.deserializeBytes( buffer, pos ); 1571 pos += 4 + preparedBytes.length; 1572 normValue = Strings.utf8ToString( preparedBytes ); 1573 } 1574 } 1575 else 1576 { 1577 // Read the user provided value, if it's not null 1578 boolean hasBytes = Serialize.deserializeBoolean( buffer, pos ); 1579 pos++; 1580 1581 if ( hasBytes ) 1582 { 1583 bytes = Serialize.deserializeBytes( buffer, pos ); 1584 pos += 4 + bytes.length; 1585 } 1586 1587 } 1588 1589 if ( attributeType != null ) 1590 { 1591 try 1592 { 1593 computeNormValue(); 1594 } 1595 catch ( LdapException le ) 1596 { 1597 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, le.getMessage() ); 1598 } 1599 } 1600 1601 hashCode(); 1602 1603 return pos; 1604 } 1605 1606 1607 /** 1608 * {@inheritDoc} 1609 */ 1610 @Override 1611 public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException 1612 { 1613 // Read the isHR flag 1614 isHR = in.readBoolean(); 1615 1616 if ( isHR ) 1617 { 1618 // Read the value if any 1619 if ( in.readBoolean() ) 1620 { 1621 int length = in.readInt(); 1622 bytes = new byte[length]; 1623 1624 if ( length != 0 ) 1625 { 1626 in.readFully( bytes ); 1627 } 1628 1629 upValue = Strings.utf8ToString( bytes ); 1630 } 1631 1632 // Read the prepared String if any 1633 if ( in.readBoolean() ) 1634 { 1635 normValue = in.readUTF(); 1636 } 1637 } 1638 else 1639 { 1640 if ( in.readBoolean() ) 1641 { 1642 int length = in.readInt(); 1643 bytes = new byte[length]; 1644 1645 if ( length != 0 ) 1646 { 1647 in.readFully( bytes ); 1648 } 1649 } 1650 } 1651 1652 hashCode(); 1653 } 1654 1655 1656 /** 1657 * {@inheritDoc} 1658 */ 1659 @Override 1660 public void writeExternal( ObjectOutput out ) throws IOException 1661 { 1662 // Write a boolean for the HR flag 1663 out.writeBoolean( isHR ); 1664 1665 if ( isHR ) 1666 { 1667 // Write the value if any 1668 out.writeBoolean( upValue != null ); 1669 1670 if ( upValue != null ) 1671 { 1672 // Write the value 1673 out.writeInt( bytes.length ); 1674 1675 if ( bytes.length > 0 ) 1676 { 1677 out.write( bytes ); 1678 } 1679 } 1680 1681 // Write the prepared value if any 1682 out.writeBoolean( normValue != null ); 1683 1684 if ( normValue != null ) 1685 { 1686 // Write the value 1687 out.writeUTF( normValue ); 1688 } 1689 } 1690 else 1691 { 1692 // Just write the bytes if not null 1693 out.writeBoolean( bytes != null ); 1694 1695 if ( bytes != null ) 1696 { 1697 out.writeInt( bytes.length ); 1698 1699 if ( bytes.length > 0 ) 1700 { 1701 out.write( bytes ); 1702 } 1703 } 1704 } 1705 1706 // and flush the data 1707 out.flush(); 1708 } 1709 1710 1711 /** 1712 * Compare the current value with a String. 1713 * 1714 * @param other the String we want to compare the current value with 1715 * @return a positive value if the current value is above the provided String, a negative value 1716 * if it's below, 0 if they are equal. 1717 * @throws IllegalStateException on failures to extract the comparator, or the 1718 * normalizers needed to perform the required comparisons based on the schema 1719 */ 1720 public int compareTo( String other ) 1721 { 1722 if ( !isHR ) 1723 { 1724 String msg = I18n.err( I18n.ERR_13224_FAILED_TO_COMPARE_NORM_VALUES, this, other ); 1725 LOG.error( msg ); 1726 throw new IllegalStateException( msg ); 1727 } 1728 1729 // Check if both value are null 1730 if ( bytes == null ) 1731 { 1732 if ( other == null ) 1733 { 1734 return 0; 1735 } 1736 else 1737 { 1738 return -1; 1739 } 1740 } 1741 else if ( other == null ) 1742 { 1743 return 1; 1744 } 1745 1746 // We have HR values. We may have an attributeType for the base Value 1747 // It actually does not matter if the second value has an attributeType 1748 // which is different 1749 try 1750 { 1751 if ( attributeType != null ) 1752 { 1753 // No normalization. Use the base AttributeType to normalize 1754 // the other value 1755 String normalizedOther = attributeType.getEquality().getNormalizer().normalize( other ); 1756 1757 return normValue.compareTo( normalizedOther ); 1758 } 1759 else 1760 { 1761 // No AtributeType... Compare the normValue 1762 return normValue.compareTo( other ); 1763 } 1764 } 1765 catch ( LdapException le ) 1766 { 1767 return -1; 1768 } 1769 } 1770 1771 1772 /** 1773 * Compare two values. We compare the stored bytes 1774 * 1775 * @param other the byte[] we want to compare the current value with 1776 * @return a positive value if the current value is above the provided byte[], a negative value 1777 * if it's below, 0 if they are equal. 1778 * @throws IllegalStateException on failures to extract the comparator, or the 1779 * normalizers needed to perform the required comparisons based on the schema 1780 */ 1781 public int compareTo( byte[] other ) 1782 { 1783 if ( isHR ) 1784 { 1785 String msg = I18n.err( I18n.ERR_13224_FAILED_TO_COMPARE_NORM_VALUES, this, other ); 1786 LOG.error( msg ); 1787 throw new IllegalStateException( msg ); 1788 } 1789 1790 // Check if both value are null 1791 if ( bytes == null ) 1792 { 1793 if ( other == null ) 1794 { 1795 return 0; 1796 } 1797 else 1798 { 1799 return -1; 1800 } 1801 } 1802 else if ( other == null ) 1803 { 1804 return 1; 1805 } 1806 1807 // Default : compare the bytes 1808 return Strings.compare( bytes, other ); 1809 } 1810 1811 1812 /** 1813 * Compare two values. We either compare the stored bytes, or we use the 1814 * AttributeType Comparator, if we have an Ordered MatchingRule. 1815 * 1816 * @param other The other Value we want to compare the current value with 1817 * @return a positive value if the current value is above the provided value, a negative value 1818 * if it's below, 0 if they are equal. 1819 * @throws IllegalStateException on failures to extract the comparator, or the 1820 * normalizers needed to perform the required comparisons based on the schema 1821 */ 1822 @Override 1823 public int compareTo( Value other ) 1824 { 1825 // The two values must have the same type 1826 if ( isHR != other.isHR ) 1827 { 1828 String msg = I18n.err( I18n.ERR_13224_FAILED_TO_COMPARE_NORM_VALUES, this, other ); 1829 LOG.error( msg ); 1830 throw new IllegalStateException( msg ); 1831 } 1832 1833 // Check if both value are null 1834 if ( bytes == null ) 1835 { 1836 if ( other.bytes == null ) 1837 { 1838 return 0; 1839 } 1840 else 1841 { 1842 return -1; 1843 } 1844 } 1845 else if ( other.bytes == null ) 1846 { 1847 return 1; 1848 } 1849 1850 // Ok, neither this nor the other have null values. 1851 1852 // Shortcut when the value are not HR 1853 if ( !isHR ) 1854 { 1855 return Strings.compare( bytes, other.bytes ); 1856 } 1857 1858 // We have HR values. We may have an attributeType for the base Value 1859 // It actually does not matter if the second value has an attributeType 1860 // which is different 1861 try 1862 { 1863 if ( attributeType != null ) 1864 { 1865 // Check if the other value has been normalized or not 1866 if ( other.attributeType == null ) 1867 { 1868 // No normalization. Use the base AttributeType to normalize 1869 // the other value 1870 String normalizedOther = attributeType.getEquality().getNormalizer().normalize( other.upValue ); 1871 1872 return normValue.compareTo( normalizedOther ); 1873 } 1874 else 1875 { 1876 return normValue.compareTo( other.normValue ); 1877 } 1878 } 1879 else 1880 { 1881 if ( other.attributeType != null ) 1882 { 1883 // Normalize the current value with the other value normalizer 1884 String normalizedThis = other.attributeType.getEquality().getNormalizer().normalize( upValue ); 1885 1886 return normalizedThis.compareTo( other.normValue ); 1887 } 1888 else 1889 { 1890 // No AtributeType... Compare the normValue 1891 return normValue.compareTo( other.normValue ); 1892 } 1893 } 1894 } 1895 catch ( LdapException le ) 1896 { 1897 return -1; 1898 } 1899 } 1900 1901 1902 /** 1903 * We compare two values using their Comparator, if any. 1904 * 1905 * @see Object#equals(Object) 1906 */ 1907 @Override 1908 public boolean equals( Object obj ) 1909 { 1910 if ( this == obj ) 1911 { 1912 return true; 1913 } 1914 1915 if ( obj instanceof String ) 1916 { 1917 String other = ( String ) obj; 1918 1919 if ( !isHR ) 1920 { 1921 return false; 1922 } 1923 1924 if ( attributeType == null ) 1925 { 1926 if ( upValue != null ) 1927 { 1928 return upValue.equals( other ); 1929 } 1930 else 1931 { 1932 return obj == null; 1933 } 1934 } 1935 else 1936 { 1937 // Use the comparator 1938 // We have an AttributeType, we use the associated comparator 1939 try 1940 { 1941 LdapComparator<String> comparator = ( LdapComparator<String> ) getLdapComparator(); 1942 1943 Normalizer normalizer = null; 1944 1945 if ( attributeType.getEquality() != null ) 1946 { 1947 normalizer = attributeType.getEquality().getNormalizer(); 1948 } 1949 1950 if ( normalizer == null ) 1951 { 1952 if ( comparator == null ) 1953 { 1954 return normValue.equals( other ); 1955 } 1956 else 1957 { 1958 return comparator.compare( normValue, other ) == 0; 1959 } 1960 } 1961 1962 String thisNormValue = normValue; 1963 String otherNormValue = normalizer.normalize( other ); 1964 1965 // Compare normalized values 1966 if ( comparator == null ) 1967 { 1968 return thisNormValue.equals( otherNormValue ); 1969 } 1970 else 1971 { 1972 return comparator.compare( thisNormValue, otherNormValue ) == 0; 1973 } 1974 } 1975 catch ( LdapException ne ) 1976 { 1977 return false; 1978 } 1979 } 1980 } 1981 1982 if ( !( obj instanceof Value ) ) 1983 { 1984 return false; 1985 } 1986 1987 Value other = ( Value ) obj; 1988 1989 // Check if the values aren't of the same type 1990 if ( isHR != other.isHR ) 1991 { 1992 // Both values must be HR or not HR 1993 return false; 1994 } 1995 1996 if ( !isHR ) 1997 { 1998 // Shortcut for binary values 1999 return Arrays.equals( bytes, other.bytes ); 2000 } 2001 2002 // HR values 2003 if ( bytes == null ) 2004 { 2005 return other.bytes == null; 2006 } 2007 2008 // Special case 2009 if ( other.bytes == null ) 2010 { 2011 return false; 2012 } 2013 2014 // Not null, but empty. We try to avoid a spurious String Preparation 2015 if ( bytes.length == 0 ) 2016 { 2017 return other.bytes.length == 0; 2018 } 2019 else if ( other.bytes.length == 0 ) 2020 { 2021 return false; 2022 } 2023 2024 // Ok, now, let's see if we have an AttributeType at all. If both have one, 2025 // and if they aren't equal, then we get out. If one of them has an AttributeType and 2026 // not the other, we will assume that this is the AttributeType to use. 2027 MatchingRule equalityMR; 2028 2029 if ( attributeType == null ) 2030 { 2031 if ( other.attributeType != null ) 2032 { 2033 // Use the Other value AT 2034 equalityMR = other.attributeType.getEquality(); 2035 2036 // We may not have an Equality MR, and in tjis case, we compare the bytes 2037 if ( equalityMR == null ) 2038 { 2039 return Arrays.equals( bytes, other.bytes ); 2040 } 2041 2042 LdapComparator<Object> ldapComparator = equalityMR.getLdapComparator(); 2043 2044 if ( ldapComparator == null ) 2045 { 2046 // This is an error ! 2047 LOG.error( I18n.err( I18n.ERR_13249_NO_COMPARATOR_FOR_AT, other.attributeType ) ); 2048 2049 return false; 2050 } 2051 2052 return ldapComparator.compare( normValue, other.normValue ) == 0; 2053 } 2054 else 2055 { 2056 // Both are null. We will compare the prepared String if we have one, 2057 // or the bytes otherwise. 2058 if ( upValue != null ) 2059 { 2060 return upValue.equals( other.upValue ); 2061 } 2062 else 2063 { 2064 return Arrays.equals( bytes, other.bytes ); 2065 } 2066 } 2067 } 2068 else 2069 { 2070 if ( other.attributeType != null ) 2071 { 2072 // Both attributeType must be equal 2073 if ( !attributeType.equals( other.attributeType ) ) 2074 { 2075 return false; 2076 } 2077 2078 // Use the comparator 2079 // We have an AttributeType, we use the associated comparator 2080 LdapComparator<String> comparator = ( LdapComparator<String> ) getLdapComparator(); 2081 2082 if ( other.attributeType.getEquality() == null ) 2083 { 2084 // No equality ? Default to comparing using a String comparator 2085 return stringComparator.compare( normValue, other.normValue ) == 0; 2086 } 2087 2088 2089 // Compare normalized values 2090 if ( comparator == null ) 2091 { 2092 return normValue.equals( other.normValue ); 2093 } 2094 else 2095 { 2096 return comparator.compare( normValue, other.normValue ) == 0; 2097 } 2098 } 2099 2100 // No attributeType 2101 if ( normValue == null ) 2102 { 2103 return other.normValue == null; 2104 } 2105 else 2106 { 2107 return normValue.equals( other.normValue ); 2108 } 2109 } 2110 } 2111 2112 2113 /** 2114 * @see Object#hashCode() 2115 * @return the instance's hashcode 2116 */ 2117 @Override 2118 public int hashCode() 2119 { 2120 if ( h == 0 ) 2121 { 2122 // return zero if the value is null so only one null value can be 2123 // stored in an attribute - the binary version does the same 2124 if ( isHR ) 2125 { 2126 if ( normValue != null ) 2127 { 2128 h = normValue.hashCode(); 2129 } 2130 else 2131 { 2132 h = 0; 2133 } 2134 } 2135 else 2136 { 2137 h = Arrays.hashCode( bytes ); 2138 } 2139 } 2140 2141 return h; 2142 } 2143 2144 2145 /** 2146 * @see Object#toString() 2147 */ 2148 @Override 2149 public String toString() 2150 { 2151 if ( isHR ) 2152 { 2153 return upValue == null ? "null" : upValue; 2154 } 2155 else 2156 { 2157 // Dumps binary in hex with label. 2158 if ( bytes == null ) 2159 { 2160 return "null"; 2161 } 2162 else if ( bytes.length > 16 ) 2163 { 2164 // Just dump the first 16 bytes... 2165 byte[] copy = new byte[16]; 2166 2167 System.arraycopy( bytes, 0, copy, 0, 16 ); 2168 2169 return Strings.dumpBytes( copy ) + "..."; 2170 } 2171 else 2172 { 2173 return Strings.dumpBytes( bytes ); 2174 } 2175 } 2176 } 2177}