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.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 ) ); 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 this.upValue = value.upValue; 363 } 364 365 try 366 { 367 computeNormValue(); 368 } 369 catch ( LdapException le ) 370 { 371 LOG.error( le.getMessage() ); 372 throw new IllegalArgumentException( I18n.err( I18n.ERR_13247_INVALID_VALUE_CANT_NORMALIZE ) ); 373 } 374 375 // Check the normValue 376 if ( !attributeType.isRelaxed() ) 377 { 378 // Check the value 379 if ( attributeType.getSyntax().getSyntaxChecker() != null ) 380 { 381 attributeType.getSyntax().getSyntaxChecker().isValidSyntax( value.normValue ); 382 } 383 else 384 { 385 // We should always have a SyntaxChecker 386 throw new IllegalArgumentException( I18n.err( I18n.ERR_13219_NULL_SYNTAX_CHECKER, normValue ) ); 387 } 388 } 389 390 // We have to copy the byte[], they are just referenced by super.clone() 391 if ( value.bytes != null ) 392 { 393 bytes = new byte[value.bytes.length]; 394 System.arraycopy( value.bytes, 0, bytes, 0, value.bytes.length ); 395 } 396 397 hashCode(); 398 } 399 400 401 /** 402 * Create a Value with an AttributeType. It will not contain anything and will only be used by 403 * the deserializer. 404 * 405 * @param attributeType The ATttributeType to use 406 * @return An instance of value. 407 */ 408 public static Value createValue( AttributeType attributeType ) 409 { 410 return new Value( attributeType ); 411 } 412 413 414 /** 415 * Clone a Value 416 * 417 * @return A cloned value 418 */ 419 @Override 420 public Value clone() 421 { 422 try 423 { 424 Value clone = ( Value ) super.clone(); 425 426 if ( isHR ) 427 { 428 return clone; 429 } 430 else 431 { 432 // We have to copy the byte[], they are just referenced by suoer.clone() 433 if ( bytes != null ) 434 { 435 clone.bytes = new byte[bytes.length]; 436 System.arraycopy( bytes, 0, clone.bytes, 0, bytes.length ); 437 } 438 } 439 440 return clone; 441 } 442 catch ( CloneNotSupportedException cnse ) 443 { 444 // Do nothing 445 return null; 446 } 447 } 448 449 450 /** 451 * Check if the contained value is null or not 452 * 453 * @return <code>true</code> if the inner value is null. 454 */ 455 public boolean isNull() 456 { 457 if ( isHR ) 458 { 459 return upValue == null; 460 } 461 else 462 { 463 return bytes == null; 464 } 465 } 466 467 468 /** 469 * Get the associated AttributeType 470 * 471 * @return The AttributeType 472 */ 473 public AttributeType getAttributeType() 474 { 475 return attributeType; 476 } 477 478 479 /** 480 * Check if the value is stored into an instance of the given 481 * AttributeType, or one of its ascendant. 482 * 483 * For instance, if the Value is associated with a CommonName, 484 * checking for Name will match. 485 * 486 * @param attributeType The AttributeType we are looking at 487 * @return <code>true</code> if the value is associated with the given 488 * attributeType or one of its ascendant 489 */ 490 public boolean isInstanceOf( AttributeType attributeType ) 491 { 492 return ( attributeType != null ) 493 && ( this.attributeType.equals( attributeType ) || this.attributeType.isDescendantOf( attributeType ) ); 494 } 495 496 497 /** 498 * Get the User Provided value. If the value is Human Readable, it will return 499 * the stored String, otherwise it will returns a String based on the bytes - which may be 500 * invalid if the value is a pure binary -. 501 * 502 * @return The user provided value 503 */ 504 public String getString() 505 { 506 if ( isHR ) 507 { 508 return upValue; 509 } 510 else 511 { 512 return Strings.utf8ToString( bytes ); 513 } 514 } 515 516 517 /** 518 * Compute the normalized value 519 * 520 * @throws LdapException If we were'nt able to normalize the value 521 */ 522 private void computeNormValue() throws LdapException 523 { 524 if ( upValue == null ) 525 { 526 return; 527 } 528 529 Normalizer normalizer; 530 531 // We should have a Equality MatchingRule 532 MatchingRule equality = attributeType.getEquality(); 533 534 if ( equality == null ) 535 { 536 // Let's try with the Substring MatchingRule 537 MatchingRule subString = attributeType.getSubstring(); 538 539 if ( subString == null ) 540 { 541 // last chance : ordering matching rule 542 MatchingRule ordering = attributeType.getOrdering(); 543 544 if ( ordering == null ) 545 { 546 // Ok, no luck. Use a NoOp normalizer 547 normalizer = new NoOpNormalizer(); 548 } 549 else 550 { 551 normalizer = ordering.getNormalizer(); 552 } 553 } 554 else 555 { 556 normalizer = subString.getNormalizer(); 557 } 558 } 559 else 560 { 561 normalizer = equality.getNormalizer(); 562 } 563 564 if ( normalizer == null ) 565 { 566 throw new IllegalArgumentException( I18n.err( I18n.ERR_13220_NO_NORMALIZER ) ); 567 } 568 569 // Now, normalize the upValue 570 normValue = normalizer.normalize( upValue ); 571 } 572 573 574 /** 575 * @return The normalized value 576 */ 577 public String getNormalized() 578 { 579 return normValue; 580 } 581 582 583 /** 584 * Get the wrapped value as a byte[], if and only if the Value is binary, 585 * otherwise returns null. 586 * 587 * @return the wrapped value as a byte[] 588 */ 589 public byte[] getBytes() 590 { 591 if ( bytes == null ) 592 { 593 return null; 594 } 595 596 if ( bytes.length == 0 ) 597 { 598 return Strings.EMPTY_BYTES; 599 } 600 601 byte[] copy = new byte[bytes.length]; 602 System.arraycopy( bytes, 0, copy, 0, bytes.length ); 603 604 return copy; 605 } 606 607 608 /** 609 * Tells if the value is schema aware or not. 610 * 611 * @return <code>true</code> if the value is sxhema aware 612 */ 613 public boolean isSchemaAware() 614 { 615 return attributeType != null; 616 } 617 618 619 /** 620 * Uses the syntaxChecker associated with the attributeType to check if the 621 * value is valid. 622 * 623 * @param syntaxChecker the SyntaxChecker to use to validate the value 624 * @return <code>true</code> if the value is valid 625 * @exception LdapInvalidAttributeValueException if the value cannot be validated 626 */ 627 public final boolean isValid( SyntaxChecker syntaxChecker ) throws LdapInvalidAttributeValueException 628 { 629 if ( syntaxChecker == null ) 630 { 631 String message = I18n.err( I18n.ERR_13219_NULL_SYNTAX_CHECKER, toString() ); 632 LOG.error( message ); 633 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message ); 634 } 635 636 // No attributeType, or it's in relaxed mode 637 if ( isHR ) 638 { 639 // We need to prepare the String in this case 640 return syntaxChecker.isValidSyntax( getString() ); 641 } 642 else 643 { 644 return syntaxChecker.isValidSyntax( bytes ); 645 } 646 } 647 648 649 /** 650 * Tells if the current value is Human Readable 651 * 652 * @return <code>true</code> if the value is a String, <code>false</code> otherwise 653 */ 654 public boolean isHumanReadable() 655 { 656 return isHR; 657 } 658 659 660 /** 661 * @return The length of the interned value 662 */ 663 public int length() 664 { 665 if ( isHR ) 666 { 667 return upValue != null ? upValue.length() : 0; 668 } 669 else 670 { 671 return bytes != null ? bytes.length : 0; 672 } 673 } 674 675 676 /** 677 * Gets a comparator using getMatchingRule() to resolve the matching 678 * that the comparator is extracted from. 679 * 680 * @return a comparator associated with the attributeType or null if one cannot be found 681 */ 682 private LdapComparator<?> getLdapComparator() 683 { 684 if ( attributeType != null ) 685 { 686 MatchingRule mr = attributeType.getEquality(); 687 688 if ( mr != null ) 689 { 690 return mr.getLdapComparator(); 691 } 692 } 693 694 return null; 695 } 696 697 698 /** 699 * Serialize the Value into a buffer at the given position. 700 * 701 * @param buffer The buffer which will contain the serialized StringValue 702 * @param pos The position in the buffer for the serialized value 703 * @return The new position in the buffer 704 */ 705 public int serialize( byte[] buffer, int pos ) 706 { 707 // Compute the length : the isHR flag first, the value and prepared value presence flags 708 int length = 1; 709 byte[] preparedBytes = null; 710 711 if ( isHR ) 712 { 713 if ( upValue != null ) 714 { 715 // The presence flag, the length and the value 716 length += 1 + 4 + bytes.length; 717 } 718 719 if ( normValue != null ) 720 { 721 // The presence flag, the length and the value 722 preparedBytes = Strings.getBytesUtf8( normValue ); 723 length += 1 + 4 + preparedBytes.length; 724 } 725 } 726 else 727 { 728 if ( bytes != null ) 729 { 730 length = 1 + 1 + 4 + bytes.length; 731 } 732 else 733 { 734 length = 1 + 1; 735 } 736 } 737 738 // Check that we will be able to store the data in the buffer 739 if ( buffer.length - pos < length ) 740 { 741 throw new ArrayIndexOutOfBoundsException(); 742 } 743 744 if ( isHR ) 745 { 746 buffer[pos++] = Serialize.TRUE; 747 748 // Write the user provided value, if not null 749 if ( bytes != null ) 750 { 751 buffer[pos++] = Serialize.TRUE; 752 pos = Serialize.serialize( bytes, buffer, pos ); 753 } 754 else 755 { 756 buffer[pos++] = Serialize.FALSE; 757 } 758 759 // Write the prepared value, if not null 760 if ( normValue != null ) 761 { 762 buffer[pos++] = Serialize.TRUE; 763 pos = Serialize.serialize( preparedBytes, buffer, pos ); 764 } 765 else 766 { 767 buffer[pos++] = Serialize.FALSE; 768 } 769 } 770 else 771 { 772 buffer[pos++] = Serialize.FALSE; 773 774 if ( bytes != null ) 775 { 776 buffer[pos++] = Serialize.TRUE; 777 pos = Serialize.serialize( bytes, buffer, pos ); 778 } 779 else 780 { 781 buffer[pos++] = Serialize.FALSE; 782 } 783 } 784 785 return pos; 786 } 787 788 789 /** 790 * Deserialize a Value. It will return a new Value instance. 791 * 792 * @param in The input stream 793 * @return A new Value instance 794 * @throws IOException If the stream can't be read 795 * @throws ClassNotFoundException If we can't instanciate a Value 796 */ 797 public static Value deserialize( ObjectInput in ) throws IOException, ClassNotFoundException 798 { 799 Value value = new Value( ( AttributeType ) null ); 800 value.readExternal( in ); 801 802 return value; 803 } 804 805 806 /** 807 * Deserialize a Value. It will return a new Value instance. 808 * 809 * @param attributeType The AttributeType associated with the Value. Can be null 810 * @param in The input stream 811 * @return A new Value instance 812 * @throws IOException If the stream can't be read 813 * @throws ClassNotFoundException If we can't instanciate a Value 814 */ 815 public static Value deserialize( AttributeType attributeType, ObjectInput in ) throws IOException, ClassNotFoundException 816 { 817 Value value = new Value( attributeType ); 818 value.readExternal( in ); 819 820 return value; 821 } 822 823 824 /** 825 * Deserialize a StringValue from a byte[], starting at a given position 826 * 827 * @param buffer The buffer containing the StringValue 828 * @param pos The position in the buffer 829 * @return The new position 830 * @throws IOException If the serialized value is not a StringValue 831 * @throws LdapInvalidAttributeValueException If the value is invalid 832 */ 833 public int deserialize( byte[] buffer, int pos ) throws IOException, LdapInvalidAttributeValueException 834 { 835 if ( ( pos < 0 ) || ( pos >= buffer.length ) ) 836 { 837 throw new ArrayIndexOutOfBoundsException(); 838 } 839 840 // Read the isHR flag 841 isHR = Serialize.deserializeBoolean( buffer, pos ); 842 pos++; 843 844 if ( isHR ) 845 { 846 // Read the user provided value, if it's not null 847 boolean hasValue = Serialize.deserializeBoolean( buffer, pos ); 848 pos++; 849 850 if ( hasValue ) 851 { 852 bytes = Serialize.deserializeBytes( buffer, pos ); 853 pos += 4 + bytes.length; 854 855 upValue = Strings.utf8ToString( bytes ); 856 } 857 858 // Read the prepared value, if not null 859 boolean hasPreparedValue = Serialize.deserializeBoolean( buffer, pos ); 860 pos++; 861 862 if ( hasPreparedValue ) 863 { 864 byte[] preparedBytes = Serialize.deserializeBytes( buffer, pos ); 865 pos += 4 + preparedBytes.length; 866 normValue = Strings.utf8ToString( preparedBytes ); 867 } 868 } 869 else 870 { 871 // Read the user provided value, if it's not null 872 boolean hasBytes = Serialize.deserializeBoolean( buffer, pos ); 873 pos++; 874 875 if ( hasBytes ) 876 { 877 bytes = Serialize.deserializeBytes( buffer, pos ); 878 pos += 4 + bytes.length; 879 } 880 881 } 882 883 if ( attributeType != null ) 884 { 885 try 886 { 887 computeNormValue(); 888 } 889 catch ( LdapException le ) 890 { 891 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, le.getMessage() ); 892 } 893 } 894 895 hashCode(); 896 897 return pos; 898 } 899 900 901 /** 902 * {@inheritDoc} 903 */ 904 @Override 905 public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException 906 { 907 // Read the isHR flag 908 isHR = in.readBoolean(); 909 910 if ( isHR ) 911 { 912 // Read the value if any 913 if ( in.readBoolean() ) 914 { 915 int length = in.readInt(); 916 bytes = new byte[length]; 917 918 if ( length != 0 ) 919 { 920 in.readFully( bytes ); 921 } 922 923 upValue = Strings.utf8ToString( bytes ); 924 } 925 926 // Read the prepared String if any 927 if ( in.readBoolean() ) 928 { 929 normValue = in.readUTF(); 930 } 931 } 932 else 933 { 934 if ( in.readBoolean() ) 935 { 936 int length = in.readInt(); 937 bytes = new byte[length]; 938 939 if ( length != 0 ) 940 { 941 in.readFully( bytes ); 942 } 943 } 944 } 945 946 hashCode(); 947 } 948 949 950 /** 951 * {@inheritDoc} 952 */ 953 @Override 954 public void writeExternal( ObjectOutput out ) throws IOException 955 { 956 // Write a boolean for the HR flag 957 out.writeBoolean( isHR ); 958 959 if ( isHR ) 960 { 961 // Write the value if any 962 out.writeBoolean( upValue != null ); 963 964 if ( upValue != null ) 965 { 966 // Write the value 967 out.writeInt( bytes.length ); 968 969 if ( bytes.length > 0 ) 970 { 971 out.write( bytes ); 972 } 973 } 974 975 // Write the prepared value if any 976 out.writeBoolean( normValue != null ); 977 978 if ( normValue != null ) 979 { 980 // Write the value 981 out.writeUTF( normValue ); 982 } 983 } 984 else 985 { 986 // Just write the bytes if not null 987 out.writeBoolean( bytes != null ); 988 989 if ( bytes != null ) 990 { 991 out.writeInt( bytes.length ); 992 993 if ( bytes.length > 0 ) 994 { 995 out.write( bytes ); 996 } 997 } 998 } 999 1000 // and flush the data 1001 out.flush(); 1002 } 1003 1004 1005 /** 1006 * Compare the current value with a String. 1007 * 1008 * @param other the String we want to compare the current value with 1009 * @return a positive value if the current value is above the provided String, a negative value 1010 * if it's below, 0 if they are equal. 1011 * @throws IllegalStateException on failures to extract the comparator, or the 1012 * normalizers needed to perform the required comparisons based on the schema 1013 */ 1014 public int compareTo( String other ) 1015 { 1016 if ( !isHR ) 1017 { 1018 String msg = I18n.err( I18n.ERR_13224_FAILED_TO_COMPARE_NORM_VALUES, this, other ); 1019 LOG.error( msg ); 1020 throw new IllegalStateException( msg ); 1021 } 1022 1023 // Check if both value are null 1024 if ( bytes == null ) 1025 { 1026 if ( other == null ) 1027 { 1028 return 0; 1029 } 1030 else 1031 { 1032 return -1; 1033 } 1034 } 1035 else if ( other == null ) 1036 { 1037 return 1; 1038 } 1039 1040 // We have HR values. We may have an attributeType for the base Value 1041 // It actually does not matter if the second value has an attributeType 1042 // which is different 1043 try 1044 { 1045 if ( attributeType != null ) 1046 { 1047 // No normalization. Use the base AttributeType to normalize 1048 // the other value 1049 String normalizedOther = attributeType.getEquality().getNormalizer().normalize( other ); 1050 1051 return normValue.compareTo( normalizedOther ); 1052 } 1053 else 1054 { 1055 // No AtributeType... Compare the normValue 1056 return normValue.compareTo( other ); 1057 } 1058 } 1059 catch ( LdapException le ) 1060 { 1061 return -1; 1062 } 1063 } 1064 1065 1066 /** 1067 * Compare two values. We compare the stored bytes 1068 * 1069 * @param other the byte[] we want to compare the current value with 1070 * @return a positive value if the current value is above the provided byte[], a negative value 1071 * if it's below, 0 if they are equal. 1072 * @throws IllegalStateException on failures to extract the comparator, or the 1073 * normalizers needed to perform the required comparisons based on the schema 1074 */ 1075 public int compareTo( byte[] other ) 1076 { 1077 if ( isHR ) 1078 { 1079 String msg = I18n.err( I18n.ERR_13224_FAILED_TO_COMPARE_NORM_VALUES, this, other ); 1080 LOG.error( msg ); 1081 throw new IllegalStateException( msg ); 1082 } 1083 1084 // Check if both value are null 1085 if ( bytes == null ) 1086 { 1087 if ( other == null ) 1088 { 1089 return 0; 1090 } 1091 else 1092 { 1093 return -1; 1094 } 1095 } 1096 else if ( other == null ) 1097 { 1098 return 1; 1099 } 1100 1101 // Default : compare the bytes 1102 return Strings.compare( bytes, other ); 1103 } 1104 1105 1106 /** 1107 * Compare two values. We either compare the stored bytes, or we use the 1108 * AttributeType Comparator, if we have an Ordered MatchingRule. 1109 * 1110 * @param other The other Value we want to compare the current value with 1111 * @return a positive value if the current value is above the provided value, a negative value 1112 * if it's below, 0 if they are equal. 1113 * @throws IllegalStateException on failures to extract the comparator, or the 1114 * normalizers needed to perform the required comparisons based on the schema 1115 */ 1116 @Override 1117 public int compareTo( Value other ) 1118 { 1119 // The two values must have the same type 1120 if ( isHR != other.isHR ) 1121 { 1122 String msg = I18n.err( I18n.ERR_13224_FAILED_TO_COMPARE_NORM_VALUES, this, other ); 1123 LOG.error( msg ); 1124 throw new IllegalStateException( msg ); 1125 } 1126 1127 // Check if both value are null 1128 if ( bytes == null ) 1129 { 1130 if ( other.bytes == null ) 1131 { 1132 return 0; 1133 } 1134 else 1135 { 1136 return -1; 1137 } 1138 } 1139 else if ( other.bytes == null ) 1140 { 1141 return 1; 1142 } 1143 1144 // Ok, neither this nor the other have null values. 1145 1146 // Shortcut when the value are not HR 1147 if ( !isHR ) 1148 { 1149 return Strings.compare( bytes, other.bytes ); 1150 } 1151 1152 // We have HR values. We may have an attributeType for the base Value 1153 // It actually does not matter if the second value has an attributeType 1154 // which is different 1155 try 1156 { 1157 if ( attributeType != null ) 1158 { 1159 // Check if the other value has been normalized or not 1160 if ( other.attributeType == null ) 1161 { 1162 // No normalization. Use the base AttributeType to normalize 1163 // the other value 1164 String normalizedOther = attributeType.getEquality().getNormalizer().normalize( other.upValue ); 1165 1166 return normValue.compareTo( normalizedOther ); 1167 } 1168 else 1169 { 1170 return normValue.compareTo( other.normValue ); 1171 } 1172 } 1173 else 1174 { 1175 if ( other.attributeType != null ) 1176 { 1177 // Normalize the current value with the other value normalizer 1178 String normalizedThis = other.attributeType.getEquality().getNormalizer().normalize( upValue ); 1179 1180 return normalizedThis.compareTo( other.normValue ); 1181 } 1182 else 1183 { 1184 // No AtributeType... Compare the normValue 1185 return normValue.compareTo( other.normValue ); 1186 } 1187 } 1188 } 1189 catch ( LdapException le ) 1190 { 1191 return -1; 1192 } 1193 } 1194 1195 1196 /** 1197 * We compare two values using their Comparator, if any. 1198 * 1199 * @see Object#equals(Object) 1200 */ 1201 @Override 1202 public boolean equals( Object obj ) 1203 { 1204 if ( this == obj ) 1205 { 1206 return true; 1207 } 1208 1209 if ( obj instanceof String ) 1210 { 1211 String other = ( String ) obj; 1212 1213 if ( !isHR ) 1214 { 1215 return false; 1216 } 1217 1218 if ( attributeType == null ) 1219 { 1220 if ( upValue != null ) 1221 { 1222 return upValue.equals( other ); 1223 } 1224 else 1225 { 1226 return obj == null; 1227 } 1228 } 1229 else 1230 { 1231 // Use the comparator 1232 // We have an AttributeType, we use the associated comparator 1233 try 1234 { 1235 LdapComparator<String> comparator = ( LdapComparator<String> ) getLdapComparator(); 1236 1237 Normalizer normalizer = null; 1238 1239 if ( attributeType.getEquality() != null ) 1240 { 1241 normalizer = attributeType.getEquality().getNormalizer(); 1242 } 1243 1244 if ( normalizer == null ) 1245 { 1246 if ( comparator == null ) 1247 { 1248 return normValue.equals( other ); 1249 } 1250 else 1251 { 1252 return comparator.compare( normValue, other ) == 0; 1253 } 1254 } 1255 1256 String thisNormValue = normValue; 1257 String otherNormValue = normalizer.normalize( other ); 1258 1259 // Compare normalized values 1260 if ( comparator == null ) 1261 { 1262 return thisNormValue.equals( otherNormValue ); 1263 } 1264 else 1265 { 1266 return comparator.compare( thisNormValue, otherNormValue ) == 0; 1267 } 1268 } 1269 catch ( LdapException ne ) 1270 { 1271 return false; 1272 } 1273 } 1274 } 1275 1276 if ( !( obj instanceof Value ) ) 1277 { 1278 return false; 1279 } 1280 1281 Value other = ( Value ) obj; 1282 1283 // Check if the values aren't of the same type 1284 if ( isHR != other.isHR ) 1285 { 1286 // Both values must be HR or not HR 1287 return false; 1288 } 1289 1290 if ( !isHR ) 1291 { 1292 // Shortcut for binary values 1293 return Arrays.equals( bytes, other.bytes ); 1294 } 1295 1296 // HR values 1297 if ( bytes == null ) 1298 { 1299 return other.bytes == null; 1300 } 1301 1302 // Special case 1303 if ( other.bytes == null ) 1304 { 1305 return false; 1306 } 1307 1308 // Not null, but empty. We try to avoid a spurious String Preparation 1309 if ( bytes.length == 0 ) 1310 { 1311 return other.bytes.length == 0; 1312 } 1313 else if ( other.bytes.length == 0 ) 1314 { 1315 return false; 1316 } 1317 1318 // Ok, now, let's see if we have an AttributeType at all. If both have one, 1319 // and if they aren't equal, then we get out. If one of them has an AttributeType and 1320 // not the other, we will assume that this is the AttributeType to use. 1321 MatchingRule equalityMR; 1322 1323 if ( attributeType == null ) 1324 { 1325 if ( other.attributeType != null ) 1326 { 1327 // Use the Other value AT 1328 equalityMR = other.attributeType.getEquality(); 1329 1330 // We may not have an Equality MR, and in tjis case, we compare the bytes 1331 if ( equalityMR == null ) 1332 { 1333 return Arrays.equals( bytes, other.bytes ); 1334 } 1335 1336 LdapComparator<Object> ldapComparator = equalityMR.getLdapComparator(); 1337 1338 if ( ldapComparator == null ) 1339 { 1340 // This is an error ! 1341 LOG.error( I18n.err( I18n.ERR_13249_NO_COMPARATOR_FOR_AT, other.attributeType ) ); 1342 1343 return false; 1344 } 1345 1346 return ldapComparator.compare( normValue, other.normValue ) == 0; 1347 } 1348 else 1349 { 1350 // Both are null. We will compare the prepared String if we have one, 1351 // or the bytes otherwise. 1352 if ( upValue != null ) 1353 { 1354 return upValue.equals( other.upValue ); 1355 } 1356 else 1357 { 1358 return Arrays.equals( bytes, other.bytes ); 1359 } 1360 } 1361 } 1362 else 1363 { 1364 if ( other.attributeType != null ) 1365 { 1366 // Both attributeType must be equal 1367 if ( !attributeType.equals( other.attributeType ) ) 1368 { 1369 return false; 1370 } 1371 1372 // Use the comparator 1373 // We have an AttributeType, we use the associated comparator 1374 LdapComparator<String> comparator = ( LdapComparator<String> ) getLdapComparator(); 1375 1376 if ( other.attributeType.getEquality() == null ) 1377 { 1378 // No equality ? Default to comparing using a String comparator 1379 return stringComparator.compare( normValue, other.normValue ) == 0; 1380 } 1381 1382 1383 // Compare normalized values 1384 if ( comparator == null ) 1385 { 1386 return normValue.equals( other.normValue ); 1387 } 1388 else 1389 { 1390 return comparator.compare( normValue, other.normValue ) == 0; 1391 } 1392 } 1393 1394 // No attributeType 1395 if ( normValue == null ) 1396 { 1397 return other.normValue == null; 1398 } 1399 else 1400 { 1401 return normValue.equals( other.normValue ); 1402 } 1403 } 1404 } 1405 1406 1407 /** 1408 * @see Object#hashCode() 1409 * @return the instance's hashcode 1410 */ 1411 @Override 1412 public int hashCode() 1413 { 1414 if ( h == 0 ) 1415 { 1416 // return zero if the value is null so only one null value can be 1417 // stored in an attribute - the binary version does the same 1418 if ( isHR ) 1419 { 1420 if ( normValue != null ) 1421 { 1422 h = normValue.hashCode(); 1423 } 1424 else 1425 { 1426 h = 0; 1427 } 1428 } 1429 else 1430 { 1431 h = Arrays.hashCode( bytes ); 1432 } 1433 } 1434 1435 return h; 1436 } 1437 1438 1439 /** 1440 * @see Object#toString() 1441 */ 1442 @Override 1443 public String toString() 1444 { 1445 if ( isHR ) 1446 { 1447 return upValue == null ? "null" : upValue; 1448 } 1449 else 1450 { 1451 // Dumps binary in hex with label. 1452 if ( bytes == null ) 1453 { 1454 return "null"; 1455 } 1456 else if ( bytes.length > 16 ) 1457 { 1458 // Just dump the first 16 bytes... 1459 byte[] copy = new byte[16]; 1460 1461 System.arraycopy( bytes, 0, copy, 0, 16 ); 1462 1463 return Strings.dumpBytes( copy ) + "..."; 1464 } 1465 else 1466 { 1467 return Strings.dumpBytes( bytes ); 1468 } 1469 } 1470 } 1471}