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