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