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 */ 019package org.apache.directory.api.ldap.model.entry; 020 021 022import java.io.IOException; 023import java.io.ObjectInput; 024import java.io.ObjectOutput; 025import java.util.Iterator; 026import java.util.LinkedHashSet; 027import java.util.Set; 028 029import org.apache.directory.api.asn1.util.Oid; 030import org.apache.directory.api.i18n.I18n; 031import org.apache.directory.api.ldap.model.exception.LdapException; 032import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException; 033import org.apache.directory.api.ldap.model.message.ResultCodeEnum; 034import org.apache.directory.api.ldap.model.schema.AttributeType; 035import org.apache.directory.api.ldap.model.schema.LdapSyntax; 036import org.apache.directory.api.ldap.model.schema.SyntaxChecker; 037import org.apache.directory.api.util.Strings; 038import org.slf4j.Logger; 039import org.slf4j.LoggerFactory; 040 041 042/** 043 * An LDAP attribute.<p> 044 * To define the kind of data stored, the client must set the isHR flag, or inject an AttributeType. 045 * 046 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 047 */ 048public class DefaultAttribute implements Attribute, Cloneable 049{ 050 /** logger for reporting errors that might not be handled properly upstream */ 051 private static final Logger LOG = LoggerFactory.getLogger( DefaultAttribute.class ); 052 053 /** The associated AttributeType */ 054 private AttributeType attributeType; 055 056 /** The set of contained values */ 057 private Set<Value<?>> values = new LinkedHashSet<>(); 058 059 /** The User provided ID */ 060 private String upId; 061 062 /** The normalized ID (will be the OID if we have a AttributeType) */ 063 private String id; 064 065 /** Tells if the attribute is Human Readable or not. When not set, 066 * this flag is null. */ 067 private Boolean isHR; 068 069 /** The computed hashcode. We don't want to compute it each time the hashcode() method is called */ 070 private volatile int h; 071 072 073 //------------------------------------------------------------------------- 074 // Constructors 075 //------------------------------------------------------------------------- 076 // maybe have some additional convenience constructors which take 077 // an initial value as a string or a byte[] 078 /** 079 * Create a new instance of a Attribute, without ID nor value. 080 * Used by the serializer 081 */ 082 /* No protection*/DefaultAttribute() 083 { 084 } 085 086 087 /** 088 * Create a new instance of a schema aware Attribute, without ID nor value. 089 * Used by the serializer 090 */ 091 /* No protection*/DefaultAttribute( AttributeType attributeType, String upId, String normId, boolean isHR, 092 int hashCode, Value<?>... values ) 093 { 094 this.attributeType = attributeType; 095 this.upId = upId; 096 this.id = normId; 097 this.isHR = isHR; 098 this.h = hashCode; 099 100 if ( values != null ) 101 { 102 for ( Value<?> value : values ) 103 { 104 this.values.add( value ); 105 } 106 } 107 } 108 109 110 /** 111 * Create a new instance of a schema aware Attribute, without ID nor value. 112 * 113 * @param attributeType the attributeType for the empty attribute added into the entry 114 */ 115 public DefaultAttribute( AttributeType attributeType ) 116 { 117 if ( attributeType != null ) 118 { 119 try 120 { 121 apply( attributeType ); 122 } 123 catch ( LdapInvalidAttributeValueException liave ) 124 { 125 // Do nothing, it can't happen, there is no value 126 } 127 } 128 } 129 130 131 /** 132 * Create a new instance of an Attribute, without value. 133 * @param upId The user provided ID 134 */ 135 public DefaultAttribute( String upId ) 136 { 137 setUpId( upId ); 138 } 139 140 141 /** 142 * Create a new instance of an Attribute, without value. 143 * @param upId The user provided ID 144 */ 145 public DefaultAttribute( byte[] upId ) 146 { 147 setUpId( upId ); 148 } 149 150 151 /** 152 * Create a new instance of a schema aware Attribute, without value. 153 * 154 * @param upId the ID for the added attributeType 155 * @param attributeType the added AttributeType 156 */ 157 public DefaultAttribute( String upId, AttributeType attributeType ) 158 { 159 if ( attributeType == null ) 160 { 161 String message = I18n.err( I18n.ERR_04460_ATTRIBUTE_TYPE_NULL_NOT_ALLOWED ); 162 LOG.error( message ); 163 throw new IllegalArgumentException( message ); 164 } 165 166 try 167 { 168 apply( attributeType ); 169 } 170 catch ( LdapInvalidAttributeValueException liave ) 171 { 172 // Do nothing, it can't happen, there is no value 173 } 174 175 setUpId( upId, attributeType ); 176 } 177 178 179 /** 180 * Create a new instance of an Attribute, with some values, and a user provided ID.<br> 181 * If the value does not correspond to the same attributeType, then it's 182 * wrapped value is copied into a new ClientValue which uses the specified 183 * attributeType. 184 * <p> 185 * Otherwise, the value is stored, but as a reference. It's not a copy. 186 * </p> 187 * @param upId the attributeType ID 188 * @param vals an initial set of values for this attribute 189 */ 190 public DefaultAttribute( String upId, Value<?>... vals ) 191 { 192 // The value can be null, this is a valid value. 193 if ( vals[0] == null ) 194 { 195 add( new StringValue( ( String ) null ) ); 196 } 197 else 198 { 199 for ( Value<?> val : vals ) 200 { 201 if ( ( val instanceof StringValue ) || ( !val.isHumanReadable() ) ) 202 { 203 add( val ); 204 } 205 else 206 { 207 String message = I18n.err( I18n.ERR_04129, val.getClass().getName() ); 208 LOG.error( message ); 209 throw new IllegalStateException( message ); 210 } 211 } 212 } 213 214 setUpId( upId ); 215 } 216 217 218 /** 219 * Create a new instance of a schema aware Attribute, without ID but with some values. 220 * 221 * @param attributeType The attributeType added on creation 222 * @param vals The added value for this attribute 223 * @throws LdapInvalidAttributeValueException If any of the 224 * added values is not valid 225 */ 226 public DefaultAttribute( AttributeType attributeType, String... vals ) throws LdapInvalidAttributeValueException 227 { 228 this( null, attributeType, vals ); 229 } 230 231 232 /** 233 * Create a new instance of a schema aware Attribute, with some values, and a user provided ID. 234 * 235 * @param upId the ID for the created attribute 236 * @param attributeType The attributeType added on creation 237 * @param vals the added values for this attribute 238 * @throws LdapInvalidAttributeValueException If any of the 239 * added values is not valid 240 */ 241 public DefaultAttribute( String upId, AttributeType attributeType, String... vals ) 242 throws LdapInvalidAttributeValueException 243 { 244 if ( attributeType == null ) 245 { 246 String message = I18n.err( I18n.ERR_04460_ATTRIBUTE_TYPE_NULL_NOT_ALLOWED ); 247 LOG.error( message ); 248 throw new IllegalArgumentException( message ); 249 } 250 251 apply( attributeType ); 252 253 if ( ( vals != null ) && ( vals.length > 0 ) ) 254 { 255 add( vals ); 256 } 257 258 setUpId( upId, attributeType ); 259 } 260 261 262 /** 263 * Create a new instance of a schema aware Attribute, with some values, and a user provided ID.<br> 264 * If the value does not correspond to the same attributeType, then it's 265 * wrapped value is copied into a new Value which uses the specified 266 * attributeType. 267 * <p> 268 * Otherwise, the value is stored, but as a reference. It's not a copy. 269 * </p> 270 * @param upId the ID of the created attribute 271 * @param attributeType the attribute type according to the schema 272 * @param vals an initial set of values for this attribute 273 * @throws LdapInvalidAttributeValueException If any of the 274 * added values is not valid 275 */ 276 public DefaultAttribute( String upId, AttributeType attributeType, Value<?>... vals ) 277 throws LdapInvalidAttributeValueException 278 { 279 if ( attributeType == null ) 280 { 281 String message = I18n.err( I18n.ERR_04460_ATTRIBUTE_TYPE_NULL_NOT_ALLOWED ); 282 LOG.error( message ); 283 throw new IllegalArgumentException( message ); 284 } 285 286 apply( attributeType ); 287 setUpId( upId, attributeType ); 288 add( vals ); 289 } 290 291 292 /** 293 * Create a new instance of a schema aware Attribute, with some values. 294 * <p> 295 * If the value does not correspond to the same attributeType, then it's 296 * wrapped value is copied into a new Value which uses the specified 297 * attributeType. 298 * </p> 299 * @param attributeType the attribute type according to the schema 300 * @param vals an initial set of values for this attribute 301 * @throws LdapInvalidAttributeValueException If one the values are invalid 302 */ 303 public DefaultAttribute( AttributeType attributeType, Value<?>... vals ) throws LdapInvalidAttributeValueException 304 { 305 this( null, attributeType, vals ); 306 } 307 308 309 /** 310 * Create a new instance of an Attribute, with some String values, and a user provided ID. 311 * 312 * @param upId the ID of the created attribute 313 * @param vals an initial set of String values for this attribute 314 */ 315 public DefaultAttribute( String upId, String... vals ) 316 { 317 try 318 { 319 add( vals ); 320 } 321 catch ( LdapInvalidAttributeValueException liave ) 322 { 323 // Do nothing, it can't happen 324 } 325 326 setUpId( upId ); 327 } 328 329 330 /** 331 * Create a new instance of an Attribute, with some binary values, and a user provided ID. 332 * 333 * @param upId the ID of the created attribute 334 * @param vals an initial set of binary values for this attribute 335 */ 336 public DefaultAttribute( String upId, byte[]... vals ) 337 { 338 try 339 { 340 add( vals ); 341 } 342 catch ( LdapInvalidAttributeValueException liave ) 343 { 344 // Do nothing, this can't happen 345 } 346 347 setUpId( upId ); 348 } 349 350 351 /** 352 * Create a new instance of a schema aware Attribute, with some byte[] values. 353 * 354 * @param attributeType The attributeType added on creation 355 * @param vals The added binary values 356 * @throws LdapInvalidAttributeValueException If any of the 357 * added values is not valid 358 */ 359 public DefaultAttribute( AttributeType attributeType, byte[]... vals ) throws LdapInvalidAttributeValueException 360 { 361 this( null, attributeType, vals ); 362 } 363 364 365 /** 366 * Create a new instance of a schema aware Attribute, with some byte[] values, and 367 * a user provided ID. 368 * 369 * @param upId the ID for the added attribute 370 * @param attributeType the AttributeType to be added 371 * @param vals the binary values for the added attribute 372 * @throws LdapInvalidAttributeValueException If any of the 373 * added values is not valid 374 */ 375 public DefaultAttribute( String upId, AttributeType attributeType, byte[]... vals ) 376 throws LdapInvalidAttributeValueException 377 { 378 if ( attributeType == null ) 379 { 380 throw new IllegalArgumentException( I18n.err( I18n.ERR_04460_ATTRIBUTE_TYPE_NULL_NOT_ALLOWED ) ); 381 } 382 383 apply( attributeType ); 384 add( vals ); 385 setUpId( upId, attributeType ); 386 } 387 388 389 /** 390 * Creates a new instance of schema aware Attribute, by copying another attribute. 391 * If the initial Attribute is not schema aware, the copy will be if the attributeType 392 * argument is not null. 393 * 394 * @param attributeType The attribute's type 395 * @param attribute The attribute to be copied 396 * @throws LdapException If we weren't able to create an instance 397 */ 398 public DefaultAttribute( AttributeType attributeType, Attribute attribute ) throws LdapException 399 { 400 // Copy the common values. isHR is only available on a ServerAttribute 401 this.attributeType = attributeType; 402 this.id = attribute.getId(); 403 this.upId = attribute.getUpId(); 404 405 if ( attributeType == null ) 406 { 407 isHR = attribute.isHumanReadable(); 408 409 // Copy all the values 410 for ( Value<?> value : attribute ) 411 { 412 add( value.clone() ); 413 } 414 415 if ( attribute.getAttributeType() != null ) 416 { 417 apply( attribute.getAttributeType() ); 418 } 419 } 420 else 421 { 422 423 isHR = attributeType.getSyntax().isHumanReadable(); 424 425 // Copy all the values 426 for ( Value<?> clientValue : attribute ) 427 { 428 Value<?> serverValue = null; 429 430 // We have to convert the value first 431 if ( clientValue instanceof StringValue ) 432 { 433 if ( isHR ) 434 { 435 serverValue = new StringValue( attributeType, clientValue.getString() ); 436 } 437 else 438 { 439 // We have to convert the value to a binary value first 440 serverValue = new BinaryValue( attributeType, 441 clientValue.getBytes() ); 442 } 443 } 444 else if ( clientValue instanceof BinaryValue ) 445 { 446 if ( isHR ) 447 { 448 // We have to convert the value to a String value first 449 serverValue = new StringValue( attributeType, 450 clientValue.getString() ); 451 } 452 else 453 { 454 serverValue = new BinaryValue( attributeType, clientValue.getBytes() ); 455 } 456 } 457 458 add( serverValue ); 459 } 460 } 461 } 462 463 464 //------------------------------------------------------------------------- 465 // Helper methods 466 //------------------------------------------------------------------------- 467 private Value<String> createStringValue( AttributeType attributeType, String value ) 468 { 469 Value<String> stringValue; 470 471 if ( attributeType != null ) 472 { 473 try 474 { 475 stringValue = new StringValue( attributeType, value ); 476 } 477 catch ( LdapInvalidAttributeValueException iae ) 478 { 479 return null; 480 } 481 } 482 else 483 { 484 stringValue = new StringValue( value ); 485 } 486 487 return stringValue; 488 } 489 490 491 private Value<byte[]> createBinaryValue( AttributeType attributeType, byte[] value ) 492 throws LdapInvalidAttributeValueException 493 { 494 Value<byte[]> binaryValue; 495 496 if ( attributeType != null ) 497 { 498 binaryValue = new BinaryValue( attributeType, value ); 499 } 500 else 501 { 502 binaryValue = new BinaryValue( value ); 503 } 504 505 return binaryValue; 506 } 507 508 509 /** 510 * {@inheritDoc} 511 */ 512 @Override 513 public byte[] getBytes() throws LdapInvalidAttributeValueException 514 { 515 Value<?> value = get(); 516 517 if ( !isHR && ( value != null ) ) 518 { 519 return value.getBytes(); 520 } 521 522 String message = I18n.err( I18n.ERR_04130 ); 523 LOG.error( message ); 524 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message ); 525 } 526 527 528 /** 529 * {@inheritDoc} 530 */ 531 @Override 532 public String getString() throws LdapInvalidAttributeValueException 533 { 534 Value<?> value = get(); 535 536 if ( isHR && ( value != null ) ) 537 { 538 return value.getString(); 539 } 540 541 String message = I18n.err( I18n.ERR_04131 ); 542 LOG.error( message ); 543 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message ); 544 } 545 546 547 /** 548 * {@inheritDoc} 549 */ 550 @Override 551 public String getId() 552 { 553 return id; 554 } 555 556 557 /** 558 * {@inheritDoc} 559 */ 560 @Override 561 public String getUpId() 562 { 563 return upId; 564 } 565 566 567 /** 568 * {@inheritDoc} 569 */ 570 @Override 571 public void setUpId( String upId ) 572 { 573 setUpId( upId, attributeType ); 574 } 575 576 577 /** 578 * Sets the User Provided ID as a byte[] 579 * 580 * @param upId The User Provided ID 581 */ 582 public void setUpId( byte[] upId ) 583 { 584 setUpId( upId, attributeType ); 585 } 586 587 588 /** 589 * Check that the upId is either a name or the OID of a given AT 590 */ 591 private boolean areCompatible( String id, AttributeType attributeType ) 592 { 593 // First, get rid of the options, if any 594 int optPos = id.indexOf( ';' ); 595 String idNoOption = id; 596 597 if ( optPos != -1 ) 598 { 599 idNoOption = id.substring( 0, optPos ); 600 } 601 602 // Check that we find the ID in the AT names 603 for ( String name : attributeType.getNames() ) 604 { 605 if ( name.equalsIgnoreCase( idNoOption ) ) 606 { 607 return true; 608 } 609 } 610 611 // Not found in names, check the OID 612 return Oid.isOid( id ) && attributeType.getOid().equals( id ); 613 } 614 615 616 /** 617 * {@inheritDoc} 618 */ 619 @Override 620 public void setUpId( String upId, AttributeType attributeType ) 621 { 622 String trimmed = Strings.trim( upId ); 623 624 if ( Strings.isEmpty( trimmed ) && ( attributeType == null ) ) 625 { 626 throw new IllegalArgumentException( "Cannot set a null ID with a null AttributeType" ); 627 } 628 629 String newId = Strings.toLowerCaseAscii( trimmed ); 630 631 setUpIdInternal( upId, newId, attributeType ); 632 } 633 634 635 /** 636 * Sets the User Provided ID as a byte[] 637 * 638 * @param upId The User Provided ID 639 * @param attributeType The asscoiated AttributeType 640 */ 641 public void setUpId( byte[] upId, AttributeType attributeType ) 642 { 643 byte[] trimmed = Strings.trim( upId ); 644 645 if ( Strings.isEmpty( trimmed ) && ( attributeType == null ) ) 646 { 647 throw new IllegalArgumentException( "Cannot set a null ID with a null AttributeType" ); 648 } 649 650 String newId = Strings.toLowerCase( trimmed ); 651 652 setUpIdInternal( Strings.utf8ToString( upId ), newId, attributeType ); 653 } 654 655 656 private void setUpIdInternal( String upId, String newId, AttributeType attributeType ) 657 { 658 if ( attributeType == null ) 659 { 660 if ( this.attributeType == null ) 661 { 662 this.upId = upId; 663 this.id = newId; 664 665 // Compute the hashCode 666 rehash(); 667 668 return; 669 } 670 else 671 { 672 if ( areCompatible( newId, this.attributeType ) ) 673 { 674 this.upId = upId; 675 this.id = this.attributeType.getOid(); 676 677 // Compute the hashCode 678 rehash(); 679 680 return; 681 } 682 else 683 { 684 return; 685 } 686 } 687 } 688 689 if ( Strings.isEmpty( newId ) ) 690 { 691 this.attributeType = attributeType; 692 this.upId = attributeType.getName(); 693 this.id = attributeType.getOid(); 694 695 // Compute the hashCode 696 rehash(); 697 698 return; 699 } 700 701 if ( areCompatible( newId, attributeType ) ) 702 { 703 this.upId = upId; 704 this.id = attributeType.getOid(); 705 this.attributeType = attributeType; 706 707 // Compute the hashCode 708 rehash(); 709 710 return; 711 } 712 713 throw new IllegalArgumentException( "ID '" + id + "' and AttributeType '" + attributeType.getName() 714 + "' are not compatible " ); 715 } 716 717 718 /** 719 * {@inheritDoc} 720 */ 721 @Override 722 public boolean isHumanReadable() 723 { 724 return isHR != null ? isHR : false; 725 } 726 727 728 /** 729 * {@inheritDoc} 730 */ 731 @Override 732 public boolean isValid( AttributeType attributeType ) throws LdapInvalidAttributeValueException 733 { 734 LdapSyntax syntax = attributeType.getSyntax(); 735 736 if ( syntax == null ) 737 { 738 return false; 739 } 740 741 SyntaxChecker syntaxChecker = syntax.getSyntaxChecker(); 742 743 if ( syntaxChecker == null ) 744 { 745 return false; 746 } 747 748 // Check that we can have no value for this attributeType 749 if ( values.isEmpty() ) 750 { 751 return syntaxChecker.isValidSyntax( null ); 752 } 753 754 // Check that we can't have more than one value if the AT is single-value 755 if ( ( attributeType.isSingleValued() ) && ( values.size() > 1 ) ) 756 { 757 return false; 758 } 759 760 // Now check the values 761 for ( Value<?> value : values ) 762 { 763 try 764 { 765 if ( !value.isValid( syntaxChecker ) ) 766 { 767 return false; 768 } 769 } 770 catch ( LdapException le ) 771 { 772 return false; 773 } 774 } 775 776 return true; 777 } 778 779 780 /** 781 * {@inheritDoc} 782 */ 783 @Override 784 public int add( Value<?>... vals ) 785 { 786 int nbAdded = 0; 787 BinaryValue nullBinaryValue = null; 788 StringValue nullStringValue = null; 789 boolean nullValueAdded = false; 790 Value<?>[] valArray = vals; 791 792 if ( vals == null ) 793 { 794 valArray = new Value[0]; 795 } 796 797 if ( attributeType != null ) 798 { 799 for ( Value<?> val : valArray ) 800 { 801 if ( attributeType.getSyntax().isHumanReadable() ) 802 { 803 if ( ( val == null ) || val.isNull() ) 804 { 805 try 806 { 807 Value<String> nullSV = new StringValue( attributeType, ( String ) null ); 808 809 if ( values.add( nullSV ) ) 810 { 811 nbAdded++; 812 } 813 } 814 catch ( LdapInvalidAttributeValueException iae ) 815 { 816 continue; 817 } 818 } 819 else if ( val instanceof StringValue ) 820 { 821 StringValue stringValue = ( StringValue ) val; 822 823 try 824 { 825 if ( stringValue.getAttributeType() == null ) 826 { 827 stringValue.apply( attributeType ); 828 } 829 830 if ( values.contains( val ) ) 831 { 832 // Replace the value 833 values.remove( val ); 834 values.add( val ); 835 } 836 else if ( values.add( val ) ) 837 { 838 nbAdded++; 839 } 840 } 841 catch ( LdapInvalidAttributeValueException iae ) 842 { 843 continue; 844 } 845 } 846 else 847 { 848 String message = I18n.err( I18n.ERR_04451 ); 849 LOG.error( message ); 850 } 851 } 852 else 853 { 854 if ( val == null ) 855 { 856 if ( attributeType.getSyntax().getSyntaxChecker().isValidSyntax( val ) ) 857 { 858 try 859 { 860 Value<byte[]> nullSV = new BinaryValue( attributeType, ( byte[] ) null ); 861 862 if ( values.add( nullSV ) ) 863 { 864 nbAdded++; 865 } 866 } 867 catch ( LdapInvalidAttributeValueException iae ) 868 { 869 continue; 870 } 871 } 872 else 873 { 874 String message = I18n.err( I18n.ERR_04452 ); 875 LOG.error( message ); 876 } 877 } 878 else 879 { 880 if ( val instanceof BinaryValue ) 881 { 882 BinaryValue binaryValue = ( BinaryValue ) val; 883 884 try 885 { 886 if ( binaryValue.getAttributeType() == null ) 887 { 888 binaryValue = new BinaryValue( attributeType, val.getBytes() ); 889 } 890 891 if ( values.add( binaryValue ) ) 892 { 893 nbAdded++; 894 } 895 } 896 catch ( LdapInvalidAttributeValueException iae ) 897 { 898 continue; 899 } 900 } 901 else 902 { 903 String message = I18n.err( I18n.ERR_04452 ); 904 LOG.error( message ); 905 } 906 } 907 } 908 } 909 } 910 else 911 { 912 for ( Value<?> val : valArray ) 913 { 914 if ( val == null ) 915 { 916 // We have a null value. If the HR flag is not set, we will consider 917 // that the attribute is not HR. We may change this later 918 if ( isHR == null ) 919 { 920 // This is the first value. Add both types, as we 921 // don't know yet the attribute type's, but we may 922 // know later if we add some new value. 923 // We have to do that because we are using a Set, 924 // and we can't remove the first element of the Set. 925 nullBinaryValue = new BinaryValue( ( byte[] ) null ); 926 nullStringValue = new StringValue( ( String ) null ); 927 928 values.add( nullBinaryValue ); 929 values.add( nullStringValue ); 930 nullValueAdded = true; 931 nbAdded++; 932 } 933 else if ( !isHR ) 934 { 935 // The attribute type is binary. 936 nullBinaryValue = new BinaryValue( ( byte[] ) null ); 937 938 // Don't add a value if it already exists. 939 if ( !values.contains( nullBinaryValue ) ) 940 { 941 values.add( nullBinaryValue ); 942 nbAdded++; 943 } 944 945 } 946 else 947 { 948 // The attribute is HR 949 nullStringValue = new StringValue( ( String ) null ); 950 951 // Don't add a value if it already exists. 952 if ( !values.contains( nullStringValue ) ) 953 { 954 values.add( nullStringValue ); 955 } 956 } 957 } 958 else 959 { 960 // Let's check the value type. 961 if ( val instanceof StringValue ) 962 { 963 // We have a String value 964 if ( isHR == null ) 965 { 966 // The attribute type will be set to HR 967 isHR = true; 968 values.add( val ); 969 nbAdded++; 970 } 971 else if ( !isHR ) 972 { 973 // The attributeType is binary, convert the 974 // value to a BinaryValue 975 BinaryValue bv = new BinaryValue( val.getBytes() ); 976 977 if ( !contains( bv ) ) 978 { 979 values.add( bv ); 980 nbAdded++; 981 } 982 } 983 else 984 { 985 // The attributeType is HR, simply add the value 986 if ( !contains( val ) ) 987 { 988 values.add( val ); 989 nbAdded++; 990 } 991 } 992 } 993 else 994 { 995 // We have a Binary value 996 if ( isHR == null ) 997 { 998 // The attribute type will be set to binary 999 isHR = false; 1000 values.add( val ); 1001 nbAdded++; 1002 } 1003 else if ( !isHR ) 1004 { 1005 // The attributeType is not HR, simply add the value if it does not already exist 1006 if ( !contains( val ) ) 1007 { 1008 values.add( val ); 1009 nbAdded++; 1010 } 1011 } 1012 else 1013 { 1014 // The attribute Type is HR, convert the 1015 // value to a StringValue 1016 StringValue sv = new StringValue( val.getString() ); 1017 1018 if ( !contains( sv ) ) 1019 { 1020 values.add( sv ); 1021 nbAdded++; 1022 } 1023 } 1024 } 1025 } 1026 } 1027 } 1028 1029 // Last, not least, if a nullValue has been added, and if other 1030 // values are all String, we have to keep the correct nullValue, 1031 // and to remove the other 1032 if ( nullValueAdded ) 1033 { 1034 if ( isHR ) 1035 { 1036 // Remove the Binary value 1037 values.remove( nullBinaryValue ); 1038 } 1039 else 1040 { 1041 // Remove the String value 1042 values.remove( nullStringValue ); 1043 } 1044 } 1045 1046 return nbAdded; 1047 } 1048 1049 1050 /** 1051 * {@inheritDoc} 1052 */ 1053 @Override 1054 public int add( String... vals ) throws LdapInvalidAttributeValueException 1055 { 1056 int nbAdded = 0; 1057 String[] valArray = vals; 1058 1059 if ( vals == null ) 1060 { 1061 valArray = new String[0]; 1062 } 1063 1064 // First, if the isHR flag is not set, we assume that the 1065 // attribute is HR, because we are asked to add some strings. 1066 if ( isHR == null ) 1067 { 1068 isHR = true; 1069 } 1070 1071 // Check the attribute type. 1072 if ( attributeType == null ) 1073 { 1074 if ( isHR ) 1075 { 1076 for ( String val : valArray ) 1077 { 1078 Value<String> value = createStringValue( attributeType, val ); 1079 1080 if ( value == null ) 1081 { 1082 // The value can't be normalized : we don't add it. 1083 LOG.error( I18n.err( I18n.ERR_04449, val ) ); 1084 continue; 1085 } 1086 1087 // Call the add(Value) method, if not already present 1088 if ( add( value ) == 1 ) 1089 { 1090 nbAdded++; 1091 } 1092 else 1093 { 1094 LOG.warn( I18n.err( I18n.ERR_04486_VALUE_ALREADY_EXISTS, val, upId ) ); 1095 } 1096 } 1097 } 1098 else 1099 { 1100 // The attribute is binary. Transform the String to byte[] 1101 for ( String val : valArray ) 1102 { 1103 byte[] valBytes = null; 1104 1105 if ( val != null ) 1106 { 1107 valBytes = Strings.getBytesUtf8( val ); 1108 } 1109 1110 Value<byte[]> value = createBinaryValue( attributeType, valBytes ); 1111 1112 // Now call the add(Value) method 1113 if ( add( value ) == 1 ) 1114 { 1115 nbAdded++; 1116 } 1117 } 1118 } 1119 } 1120 else 1121 { 1122 if ( attributeType.isSingleValued() && ( values.size() + valArray.length > 1 ) ) 1123 { 1124 LOG.error( I18n.err( I18n.ERR_04487_ATTRIBUTE_IS_SINGLE_VALUED, attributeType.getName() ) ); 1125 return 0; 1126 } 1127 1128 if ( isHR ) 1129 { 1130 for ( String val : valArray ) 1131 { 1132 Value<String> value = createStringValue( attributeType, val ); 1133 1134 if ( value == null ) 1135 { 1136 // The value can't be normalized : we don't add it. 1137 LOG.error( I18n.err( I18n.ERR_04449, val ) ); 1138 continue; 1139 } 1140 1141 // Call the add(Value) method, if not already present 1142 if ( add( value ) == 1 ) 1143 { 1144 nbAdded++; 1145 } 1146 else 1147 { 1148 LOG.warn( I18n.err( I18n.ERR_04486_VALUE_ALREADY_EXISTS, val, upId ) ); 1149 } 1150 } 1151 } 1152 else 1153 { 1154 // The attribute is binary. Transform the String to byte[] 1155 for ( String val : valArray ) 1156 { 1157 byte[] valBytes = null; 1158 1159 if ( val != null ) 1160 { 1161 valBytes = Strings.getBytesUtf8( val ); 1162 } 1163 1164 Value<byte[]> value = createBinaryValue( attributeType, valBytes ); 1165 1166 // Now call the add(Value) method 1167 if ( add( value ) == 1 ) 1168 { 1169 nbAdded++; 1170 } 1171 } 1172 } 1173 } 1174 1175 return nbAdded; 1176 } 1177 1178 1179 /** 1180 * {@inheritDoc} 1181 */ 1182 public int add( byte[]... vals ) throws LdapInvalidAttributeValueException 1183 { 1184 int nbAdded = 0; 1185 byte[][] valArray = vals; 1186 1187 if ( vals == null ) 1188 { 1189 valArray = new byte[0][]; 1190 } 1191 1192 // First, if the isHR flag is not set, we assume that the 1193 // attribute is not HR, because we are asked to add some byte[]. 1194 if ( isHR == null ) 1195 { 1196 isHR = false; 1197 } 1198 1199 if ( !isHR ) 1200 { 1201 for ( byte[] val : valArray ) 1202 { 1203 Value<byte[]> value; 1204 1205 if ( attributeType == null ) 1206 { 1207 value = new BinaryValue( val ); 1208 } 1209 else 1210 { 1211 value = createBinaryValue( attributeType, val ); 1212 } 1213 1214 if ( add( value ) != 0 ) 1215 { 1216 nbAdded++; 1217 } 1218 else 1219 { 1220 LOG.warn( I18n.err( I18n.ERR_04486_VALUE_ALREADY_EXISTS, Strings.dumpBytes( val ), upId ) ); 1221 } 1222 } 1223 } 1224 else 1225 { 1226 // We can't add Binary values into a String Attribute 1227 LOG.info( I18n.err( I18n.ERR_04451 ) ); 1228 return 0; 1229 } 1230 1231 return nbAdded; 1232 } 1233 1234 1235 /** 1236 * {@inheritDoc} 1237 */ 1238 @Override 1239 public void clear() 1240 { 1241 values.clear(); 1242 } 1243 1244 1245 /** 1246 * {@inheritDoc} 1247 */ 1248 @Override 1249 public boolean contains( Value<?>... vals ) 1250 { 1251 if ( isHR == null ) 1252 { 1253 // If this flag is null, then there is no values. 1254 return false; 1255 } 1256 1257 if ( attributeType == null ) 1258 { 1259 if ( isHR ) 1260 { 1261 // Iterate through all the values, convert the Binary values 1262 // to String values, and quit id any of the values is not 1263 // contained in the object 1264 for ( Value<?> val : vals ) 1265 { 1266 if ( val instanceof StringValue ) 1267 { 1268 if ( !values.contains( val ) ) 1269 { 1270 return false; 1271 } 1272 } 1273 else 1274 { 1275 byte[] binaryVal = val.getBytes(); 1276 1277 // We have to convert the binary value to a String 1278 if ( !values.contains( new StringValue( Strings.utf8ToString( binaryVal ) ) ) ) 1279 { 1280 return false; 1281 } 1282 } 1283 } 1284 } 1285 else 1286 { 1287 // Iterate through all the values, convert the String values 1288 // to binary values, and quit id any of the values is not 1289 // contained in the object 1290 for ( Value<?> val : vals ) 1291 { 1292 if ( val.isHumanReadable() ) 1293 { 1294 String stringVal = val.getString(); 1295 1296 // We have to convert the binary value to a String 1297 if ( !values.contains( new BinaryValue( Strings.getBytesUtf8( stringVal ) ) ) ) 1298 { 1299 return false; 1300 } 1301 } 1302 else 1303 { 1304 if ( !values.contains( val ) ) 1305 { 1306 return false; 1307 } 1308 } 1309 } 1310 } 1311 } 1312 else 1313 { 1314 // Iterate through all the values, and quit if we 1315 // don't find one in the values. We have to separate the check 1316 // depending on the isHR flag value. 1317 if ( isHR ) 1318 { 1319 for ( Value<?> val : vals ) 1320 { 1321 if ( val instanceof StringValue ) 1322 { 1323 StringValue stringValue = ( StringValue ) val; 1324 1325 try 1326 { 1327 if ( stringValue.getAttributeType() == null ) 1328 { 1329 stringValue.apply( attributeType ); 1330 } 1331 } 1332 catch ( LdapInvalidAttributeValueException liave ) 1333 { 1334 return false; 1335 } 1336 1337 if ( !values.contains( val ) ) 1338 { 1339 return false; 1340 } 1341 } 1342 else 1343 { 1344 // Not a String value 1345 return false; 1346 } 1347 } 1348 } 1349 else 1350 { 1351 for ( Value<?> val : vals ) 1352 { 1353 if ( val instanceof BinaryValue ) 1354 { 1355 if ( !values.contains( val ) ) 1356 { 1357 return false; 1358 } 1359 } 1360 else 1361 { 1362 // Not a Binary value 1363 return false; 1364 } 1365 } 1366 } 1367 } 1368 1369 return true; 1370 } 1371 1372 1373 /** 1374 * {@inheritDoc} 1375 */ 1376 @Override 1377 public boolean contains( String... vals ) 1378 { 1379 if ( isHR == null ) 1380 { 1381 // If this flag is null, then there is no values. 1382 return false; 1383 } 1384 1385 if ( attributeType == null ) 1386 { 1387 if ( isHR ) 1388 { 1389 for ( String val : vals ) 1390 { 1391 try 1392 { 1393 if ( !contains( new StringValue( val ) ) ) 1394 { 1395 return false; 1396 } 1397 } 1398 catch ( IllegalArgumentException iae ) 1399 { 1400 return false; 1401 } 1402 } 1403 } 1404 else 1405 { 1406 // As the attribute type is binary, we have to convert 1407 // the values before checking for them in the values 1408 // Iterate through all the values, and quit if we 1409 // don't find one in the values 1410 for ( String val : vals ) 1411 { 1412 byte[] binaryVal = Strings.getBytesUtf8( val ); 1413 1414 if ( !contains( new BinaryValue( binaryVal ) ) ) 1415 { 1416 return false; 1417 } 1418 } 1419 } 1420 } 1421 else 1422 { 1423 if ( isHR ) 1424 { 1425 // Iterate through all the values, and quit if we 1426 // don't find one in the values 1427 for ( String val : vals ) 1428 { 1429 try 1430 { 1431 StringValue value = new StringValue( attributeType, val ); 1432 1433 if ( !values.contains( value ) ) 1434 { 1435 return false; 1436 } 1437 } 1438 catch ( LdapInvalidAttributeValueException liave ) 1439 { 1440 return false; 1441 } 1442 } 1443 1444 return true; 1445 } 1446 else 1447 { 1448 return false; 1449 } 1450 } 1451 1452 return true; 1453 } 1454 1455 1456 /** 1457 * {@inheritDoc} 1458 */ 1459 public boolean contains( byte[]... vals ) 1460 { 1461 if ( isHR == null ) 1462 { 1463 // If this flag is null, then there is no values. 1464 return false; 1465 } 1466 1467 if ( attributeType == null ) 1468 { 1469 if ( !isHR ) 1470 { 1471 // Iterate through all the values, and quit if we 1472 // don't find one in the values 1473 for ( byte[] val : vals ) 1474 { 1475 if ( !contains( new BinaryValue( val ) ) ) 1476 { 1477 return false; 1478 } 1479 } 1480 } 1481 else 1482 { 1483 // As the attribute type is String, we have to convert 1484 // the values before checking for them in the values 1485 // Iterate through all the values, and quit if we 1486 // don't find one in the values 1487 for ( byte[] val : vals ) 1488 { 1489 String stringVal = Strings.utf8ToString( val ); 1490 1491 if ( !contains( new StringValue( stringVal ) ) ) 1492 { 1493 return false; 1494 } 1495 } 1496 } 1497 } 1498 else 1499 { 1500 if ( !isHR ) 1501 { 1502 // Iterate through all the values, and quit if we 1503 // don't find one in the values 1504 for ( byte[] val : vals ) 1505 { 1506 try 1507 { 1508 BinaryValue value = new BinaryValue( attributeType, val ); 1509 1510 if ( !values.contains( value ) ) 1511 { 1512 return false; 1513 } 1514 } 1515 catch ( LdapInvalidAttributeValueException liave ) 1516 { 1517 return false; 1518 } 1519 } 1520 1521 return true; 1522 } 1523 else 1524 { 1525 return false; 1526 } 1527 } 1528 1529 return true; 1530 } 1531 1532 1533 /** 1534 * {@inheritDoc} 1535 */ 1536 @Override 1537 public Value<?> get() 1538 { 1539 if ( values.isEmpty() ) 1540 { 1541 return null; 1542 } 1543 1544 return values.iterator().next(); 1545 } 1546 1547 1548 /** 1549 * {@inheritDoc} 1550 */ 1551 @Override 1552 public int size() 1553 { 1554 return values.size(); 1555 } 1556 1557 1558 /** 1559 * {@inheritDoc} 1560 */ 1561 @Override 1562 public boolean remove( Value<?>... vals ) 1563 { 1564 if ( ( isHR == null ) || values.isEmpty() ) 1565 { 1566 // Trying to remove a value from an empty list will fail 1567 return false; 1568 } 1569 1570 boolean removed = true; 1571 1572 if ( attributeType == null ) 1573 { 1574 if ( isHR ) 1575 { 1576 for ( Value<?> val : vals ) 1577 { 1578 if ( val instanceof StringValue ) 1579 { 1580 removed &= values.remove( val ); 1581 } 1582 else 1583 { 1584 // Convert the binary value to a string value 1585 byte[] binaryVal = val.getBytes(); 1586 removed &= values.remove( new StringValue( Strings.utf8ToString( binaryVal ) ) ); 1587 } 1588 } 1589 } 1590 else 1591 { 1592 for ( Value<?> val : vals ) 1593 { 1594 removed &= values.remove( val ); 1595 } 1596 } 1597 } 1598 else 1599 { 1600 // Loop through all the values to remove. If one of 1601 // them is not present, the method will return false. 1602 // As the attribute may be HR or not, we have two separated treatments 1603 if ( isHR ) 1604 { 1605 for ( Value<?> val : vals ) 1606 { 1607 if ( val instanceof StringValue ) 1608 { 1609 StringValue stringValue = ( StringValue ) val; 1610 1611 try 1612 { 1613 if ( stringValue.getAttributeType() == null ) 1614 { 1615 stringValue.apply( attributeType ); 1616 } 1617 1618 removed &= values.remove( stringValue ); 1619 } 1620 catch ( LdapInvalidAttributeValueException liave ) 1621 { 1622 removed = false; 1623 } 1624 } 1625 else 1626 { 1627 removed = false; 1628 } 1629 } 1630 } 1631 else 1632 { 1633 for ( Value<?> val : vals ) 1634 { 1635 if ( val instanceof BinaryValue ) 1636 { 1637 try 1638 { 1639 BinaryValue binaryValue = ( BinaryValue ) val; 1640 1641 if ( binaryValue.getAttributeType() == null ) 1642 { 1643 binaryValue.apply( attributeType ); 1644 } 1645 1646 removed &= values.remove( binaryValue ); 1647 } 1648 catch ( LdapInvalidAttributeValueException liave ) 1649 { 1650 removed = false; 1651 } 1652 } 1653 else 1654 { 1655 removed = false; 1656 } 1657 } 1658 } 1659 } 1660 1661 return removed; 1662 } 1663 1664 1665 /** 1666 * {@inheritDoc} 1667 */ 1668 public boolean remove( byte[]... vals ) 1669 { 1670 if ( ( isHR == null ) || values.isEmpty() ) 1671 { 1672 // Trying to remove a value from an empty list will fail 1673 return false; 1674 } 1675 1676 boolean removed = true; 1677 1678 if ( attributeType == null ) 1679 { 1680 if ( !isHR ) 1681 { 1682 // The attribute type is not HR, we can directly process the values 1683 for ( byte[] val : vals ) 1684 { 1685 BinaryValue value = new BinaryValue( val ); 1686 removed &= values.remove( value ); 1687 } 1688 } 1689 else 1690 { 1691 // The attribute type is String, we have to convert the values 1692 // to String before removing them 1693 for ( byte[] val : vals ) 1694 { 1695 StringValue value = new StringValue( Strings.utf8ToString( val ) ); 1696 removed &= values.remove( value ); 1697 } 1698 } 1699 } 1700 else 1701 { 1702 if ( !isHR ) 1703 { 1704 try 1705 { 1706 for ( byte[] val : vals ) 1707 { 1708 BinaryValue value = new BinaryValue( attributeType, val ); 1709 removed &= values.remove( value ); 1710 } 1711 } 1712 catch ( LdapInvalidAttributeValueException liave ) 1713 { 1714 removed = false; 1715 } 1716 } 1717 else 1718 { 1719 removed = false; 1720 } 1721 } 1722 1723 return removed; 1724 } 1725 1726 1727 /** 1728 * {@inheritDoc} 1729 */ 1730 @Override 1731 public boolean remove( String... vals ) 1732 { 1733 if ( ( isHR == null ) || values.isEmpty() ) 1734 { 1735 // Trying to remove a value from an empty list will fail 1736 return false; 1737 } 1738 1739 boolean removed = true; 1740 1741 if ( attributeType == null ) 1742 { 1743 if ( isHR ) 1744 { 1745 // The attribute type is HR, we can directly process the values 1746 for ( String val : vals ) 1747 { 1748 StringValue value = new StringValue( val ); 1749 removed &= values.remove( value ); 1750 } 1751 } 1752 else 1753 { 1754 // The attribute type is binary, we have to convert the values 1755 // to byte[] before removing them 1756 for ( String val : vals ) 1757 { 1758 BinaryValue value = new BinaryValue( Strings.getBytesUtf8( val ) ); 1759 removed &= values.remove( value ); 1760 } 1761 } 1762 } 1763 else 1764 { 1765 if ( isHR ) 1766 { 1767 for ( String val : vals ) 1768 { 1769 try 1770 { 1771 StringValue value = new StringValue( attributeType, val ); 1772 removed &= values.remove( value ); 1773 } 1774 catch ( LdapInvalidAttributeValueException liave ) 1775 { 1776 removed = false; 1777 } 1778 } 1779 } 1780 else 1781 { 1782 removed = false; 1783 } 1784 } 1785 1786 return removed; 1787 } 1788 1789 1790 /** 1791 * An iterator on top of the stored values. 1792 * 1793 * @return an iterator over the stored values. 1794 */ 1795 @Override 1796 public Iterator<Value<?>> iterator() 1797 { 1798 return values.iterator(); 1799 } 1800 1801 1802 /** 1803 * {@inheritDoc} 1804 */ 1805 @Override 1806 public AttributeType getAttributeType() 1807 { 1808 return attributeType; 1809 } 1810 1811 1812 /** 1813 * {@inheritDoc} 1814 */ 1815 @Override 1816 public void apply( AttributeType attributeType ) throws LdapInvalidAttributeValueException 1817 { 1818 if ( attributeType == null ) 1819 { 1820 throw new IllegalArgumentException( "The AttributeType parameter should not be null" ); 1821 } 1822 1823 this.attributeType = attributeType; 1824 this.id = attributeType.getOid(); 1825 1826 if ( Strings.isEmpty( this.upId ) ) 1827 { 1828 this.upId = attributeType.getName(); 1829 } 1830 else 1831 { 1832 if ( !areCompatible( this.upId, attributeType ) ) 1833 { 1834 this.upId = attributeType.getName(); 1835 } 1836 } 1837 1838 if ( values != null ) 1839 { 1840 Set<Value<?>> newValues = new LinkedHashSet<>( values.size() ); 1841 1842 for ( Value<?> value : values ) 1843 { 1844 if ( value instanceof StringValue ) 1845 { 1846 newValues.add( new StringValue( attributeType, value.getString() ) ); 1847 } 1848 else 1849 { 1850 newValues.add( new BinaryValue( attributeType, value.getBytes() ) ); 1851 } 1852 } 1853 1854 values = newValues; 1855 } 1856 1857 isHR = attributeType.getSyntax().isHumanReadable(); 1858 1859 // Compute the hashCode 1860 rehash(); 1861 } 1862 1863 1864 /** 1865 * {@inheritDoc} 1866 */ 1867 @Override 1868 public boolean isInstanceOf( AttributeType attributeType ) throws LdapInvalidAttributeValueException 1869 { 1870 return ( attributeType != null ) 1871 && ( this.attributeType.equals( attributeType ) || this.attributeType.isDescendantOf( attributeType ) ); 1872 } 1873 1874 1875 //------------------------------------------------------------------------- 1876 // Overloaded Object classes 1877 //------------------------------------------------------------------------- 1878 /** 1879 * A helper method to rehash the hashCode 1880 */ 1881 private void rehash() 1882 { 1883 h = 37; 1884 1885 if ( isHR != null ) 1886 { 1887 h = h * 17 + isHR.hashCode(); 1888 } 1889 1890 if ( id != null ) 1891 { 1892 h = h * 17 + id.hashCode(); 1893 } 1894 1895 if ( attributeType != null ) 1896 { 1897 h = h * 17 + attributeType.hashCode(); 1898 } 1899 } 1900 1901 1902 /** 1903 * The hashCode is based on the id, the isHR flag and 1904 * on the internal values. 1905 * 1906 * @see Object#hashCode() 1907 * @return the instance's hashcode 1908 */ 1909 @Override 1910 public int hashCode() 1911 { 1912 if ( h == 0 ) 1913 { 1914 rehash(); 1915 } 1916 1917 return h; 1918 } 1919 1920 1921 /** 1922 * @see Object#equals(Object) 1923 */ 1924 @Override 1925 public boolean equals( Object obj ) 1926 { 1927 if ( obj == this ) 1928 { 1929 return true; 1930 } 1931 1932 if ( !( obj instanceof Attribute ) ) 1933 { 1934 return false; 1935 } 1936 1937 Attribute other = ( Attribute ) obj; 1938 1939 if ( id == null ) 1940 { 1941 if ( other.getId() != null ) 1942 { 1943 return false; 1944 } 1945 } 1946 else 1947 { 1948 if ( other.getId() == null ) 1949 { 1950 return false; 1951 } 1952 else 1953 { 1954 if ( attributeType != null ) 1955 { 1956 if ( !attributeType.equals( other.getAttributeType() ) ) 1957 { 1958 return false; 1959 } 1960 } 1961 else if ( !id.equals( other.getId() ) ) 1962 { 1963 return false; 1964 } 1965 } 1966 } 1967 1968 if ( isHumanReadable() != other.isHumanReadable() ) 1969 { 1970 return false; 1971 } 1972 1973 if ( values.size() != other.size() ) 1974 { 1975 return false; 1976 } 1977 1978 for ( Value<?> val : values ) 1979 { 1980 if ( !other.contains( val ) ) 1981 { 1982 return false; 1983 } 1984 } 1985 1986 if ( attributeType == null ) 1987 { 1988 return other.getAttributeType() == null; 1989 } 1990 1991 return attributeType.equals( other.getAttributeType() ); 1992 } 1993 1994 1995 /** 1996 * {@inheritDoc} 1997 */ 1998 @Override 1999 public Attribute clone() 2000 { 2001 try 2002 { 2003 DefaultAttribute attribute = ( DefaultAttribute ) super.clone(); 2004 2005 if ( this.attributeType != null ) 2006 { 2007 attribute.id = attributeType.getOid(); 2008 attribute.attributeType = attributeType; 2009 } 2010 2011 attribute.values = new LinkedHashSet<>( values.size() ); 2012 2013 for ( Value<?> value : values ) 2014 { 2015 // No need to clone the value, it will never be changed 2016 attribute.values.add( value ); 2017 } 2018 2019 return attribute; 2020 } 2021 catch ( CloneNotSupportedException cnse ) 2022 { 2023 return null; 2024 } 2025 } 2026 2027 2028 /** 2029 * This is the place where we serialize attributes, and all theirs 2030 * elements. 2031 * 2032 * {@inheritDoc} 2033 */ 2034 @Override 2035 public void writeExternal( ObjectOutput out ) throws IOException 2036 { 2037 // Write the UPId (the id will be deduced from the upID) 2038 out.writeUTF( upId ); 2039 2040 // Write the HR flag, if not null 2041 if ( isHR != null ) 2042 { 2043 out.writeBoolean( true ); 2044 out.writeBoolean( isHR ); 2045 } 2046 else 2047 { 2048 out.writeBoolean( false ); 2049 } 2050 2051 // Write the number of values 2052 out.writeInt( size() ); 2053 2054 if ( size() > 0 ) 2055 { 2056 // Write each value 2057 for ( Value<?> value : values ) 2058 { 2059 // Write the value 2060 value.writeExternal( out ); 2061 } 2062 } 2063 2064 out.flush(); 2065 } 2066 2067 2068 /** 2069 * {@inheritDoc} 2070 */ 2071 @Override 2072 public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException 2073 { 2074 // Read the ID and the UPId 2075 upId = in.readUTF(); 2076 2077 // Compute the id 2078 setUpId( upId ); 2079 2080 // Read the HR flag, if not null 2081 if ( in.readBoolean() ) 2082 { 2083 isHR = in.readBoolean(); 2084 } 2085 2086 // Read the number of values 2087 int nbValues = in.readInt(); 2088 2089 if ( nbValues > 0 ) 2090 { 2091 for ( int i = 0; i < nbValues; i++ ) 2092 { 2093 Value<?> value; 2094 2095 if ( isHR ) 2096 { 2097 value = new StringValue( attributeType ); 2098 } 2099 else 2100 { 2101 value = new BinaryValue( attributeType ); 2102 } 2103 2104 value.readExternal( in ); 2105 2106 values.add( value ); 2107 } 2108 } 2109 } 2110 2111 2112 /** 2113 * @see Object#toString() 2114 */ 2115 @Override 2116public String toString() 2117 { 2118 return toString( "" ); 2119 } 2120 2121 2122 /** 2123 * {@inheritDoc} 2124 */ 2125 @Override 2126 public String toString( String tabs ) 2127 { 2128 StringBuilder sb = new StringBuilder(); 2129 2130 if ( ( values != null ) && !values.isEmpty() ) 2131 { 2132 boolean isFirst = true; 2133 2134 for ( Value<?> value : values ) 2135 { 2136 if ( isFirst ) 2137 { 2138 isFirst = false; 2139 } 2140 else 2141 { 2142 sb.append( '\n' ); 2143 } 2144 2145 sb.append( tabs ).append( upId ).append( ": " ); 2146 2147 if ( value.isNull() ) 2148 { 2149 sb.append( "''" ); 2150 } 2151 else 2152 { 2153 sb.append( value ); 2154 } 2155 } 2156 } 2157 else 2158 { 2159 sb.append( tabs ).append( upId ).append( ": (null)" ); 2160 } 2161 2162 return sb.toString(); 2163 } 2164}