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