001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.directory.api.ldap.model.ldif; 021 022 023import java.io.Externalizable; 024import java.io.IOException; 025import java.io.ObjectInput; 026import java.io.ObjectOutput; 027import java.util.HashMap; 028import java.util.Iterator; 029import java.util.LinkedList; 030import java.util.List; 031import java.util.Map; 032import java.util.concurrent.ConcurrentHashMap; 033 034import org.apache.directory.api.i18n.I18n; 035import org.apache.directory.api.ldap.model.entry.Attribute; 036import org.apache.directory.api.ldap.model.entry.DefaultAttribute; 037import org.apache.directory.api.ldap.model.entry.DefaultEntry; 038import org.apache.directory.api.ldap.model.entry.DefaultModification; 039import org.apache.directory.api.ldap.model.entry.Entry; 040import org.apache.directory.api.ldap.model.entry.Modification; 041import org.apache.directory.api.ldap.model.entry.ModificationOperation; 042import org.apache.directory.api.ldap.model.entry.Value; 043import org.apache.directory.api.ldap.model.exception.LdapException; 044import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException; 045import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException; 046import org.apache.directory.api.ldap.model.message.Control; 047import org.apache.directory.api.ldap.model.message.ResultCodeEnum; 048import org.apache.directory.api.ldap.model.name.Dn; 049import org.apache.directory.api.ldap.model.name.Rdn; 050import org.apache.directory.api.ldap.model.schema.SchemaManager; 051import org.apache.directory.api.util.Base64; 052import org.apache.directory.api.util.Strings; 053 054 055/** 056 * A entry to be populated by an ldif parser. 057 * 058 * We will have different kind of entries : 059 * <ul> 060 * <li>added entries</li> 061 * <li>deleted entries</li> 062 * <li>modified entries</li> 063 * <li>Rdn modified entries</li> 064 * <li>Dn modified entries</li> 065 * </ul> 066 * 067 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 068 */ 069public class LdifEntry implements Cloneable, Externalizable, Iterable<Attribute> 070{ 071 /** Used in toArray() */ 072 public static final Modification[] EMPTY_MODS = new Modification[0]; 073 074 /** the change type */ 075 private ChangeType changeType; 076 077 /** the modification item list */ 078 private List<Modification> modificationList; 079 080 /** The map containing all the modifications */ 081 private Map<String, Modification> modifications; 082 083 /** The new superior */ 084 private String newSuperior; 085 086 /** The new rdn */ 087 private String newRdn; 088 089 /** The delete old rdn flag */ 090 private boolean deleteOldRdn; 091 092 /** the entry */ 093 private Entry entry; 094 095 /** the DN */ 096 private Dn entryDn; 097 098 /** The controls */ 099 private Map<String, LdifControl> controls; 100 101 /** The lengthBeforeParsing of the entry at the time of parsing. This includes 102 * the lengthBeforeParsing of the comments present in entry at the time of parsing 103 * so this lengthBeforeParsing may not always match with the lengthBeforeParsing of the entry 104 * data present in memory. 105 */ 106 private int lengthBeforeParsing = 0; 107 108 /** the position of the entry in the file or given input string*/ 109 private long offset = 0; 110 111 112 /** 113 * Creates a new LdifEntry object. 114 */ 115 public LdifEntry() 116 { 117 // Default LDIF content 118 changeType = ChangeType.None; 119 modificationList = new LinkedList<>(); 120 modifications = new HashMap<>(); 121 entry = new DefaultEntry( ( Dn ) null ); 122 entryDn = null; 123 controls = null; 124 } 125 126 127 /** 128 * Creates a new schema aware LdifEntry object. 129 * 130 * @param schemaManager The SchemaManager 131 */ 132 public LdifEntry( SchemaManager schemaManager ) 133 { 134 // Default LDIF content 135 changeType = ChangeType.None; 136 modificationList = new LinkedList<>(); 137 modifications = new HashMap<>(); 138 entry = new DefaultEntry( schemaManager, ( Dn ) null ); 139 entryDn = null; 140 controls = null; 141 } 142 143 144 /** 145 * Creates a new LdifEntry object, storing an Entry 146 * 147 * @param entry The entry to encapsulate 148 */ 149 public LdifEntry( Entry entry ) 150 { 151 // Default LDIF content 152 changeType = ChangeType.None; 153 modificationList = new LinkedList<>(); 154 modifications = new HashMap<>(); 155 this.entry = entry; 156 entryDn = entry.getDn(); 157 controls = null; 158 } 159 160 161 /** 162 * Creates a LdifEntry using a list of strings representing the Ldif element 163 * 164 * @param dn The LdifEntry DN 165 * @param avas The Ldif to convert to an LdifEntry 166 * @throws LdapInvalidAttributeValueException If either the AttributeType or the associated value 167 * is incorrect 168 * @throws LdapLdifException If we get any other exception 169 */ 170 public LdifEntry( Dn dn, Object... avas ) throws LdapInvalidAttributeValueException, LdapLdifException 171 { 172 // First, convert the arguments to a full LDIF 173 StringBuilder sb = new StringBuilder(); 174 int pos = 0; 175 boolean valueExpected = false; 176 String dnStr = null; 177 178 if ( dn == null ) 179 { 180 dnStr = ""; 181 } 182 else 183 { 184 dnStr = dn.getName(); 185 } 186 187 if ( LdifUtils.isLDIFSafe( dnStr ) ) 188 { 189 sb.append( "dn: " ).append( dnStr ).append( '\n' ); 190 } 191 else 192 { 193 sb.append( "dn:: " ).append( Base64.encode( Strings.getBytesUtf8( dnStr ) ) ).append( '\n' ); 194 } 195 196 for ( Object ava : avas ) 197 { 198 if ( !valueExpected ) 199 { 200 if ( !( ava instanceof String ) ) 201 { 202 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, I18n.err( 203 I18n.ERR_13233_ATTRIBUTE_ID_MUST_BE_A_STRING, pos + 1 ) ); 204 } 205 206 String attribute = ( String ) ava; 207 sb.append( attribute ); 208 209 if ( attribute.indexOf( ':' ) != -1 ) 210 { 211 sb.append( '\n' ); 212 } 213 else 214 { 215 valueExpected = true; 216 } 217 } 218 else 219 { 220 if ( ava instanceof String ) 221 { 222 sb.append( ": " ).append( ( String ) ava ).append( '\n' ); 223 } 224 else if ( ava instanceof byte[] ) 225 { 226 sb.append( ":: " ); 227 sb.append( new String( Base64.encode( ( byte[] ) ava ) ) ); 228 sb.append( '\n' ); 229 } 230 else 231 { 232 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, I18n.err( 233 I18n.ERR_13234_ATTRIBUTE_VAL_STRING_OR_BYTE, pos + 1 ) ); 234 } 235 236 valueExpected = false; 237 } 238 } 239 240 if ( valueExpected ) 241 { 242 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, I18n 243 .err( I18n.ERR_13250_VALUE_MISSING_AT_THE_END ) ); 244 } 245 246 // Now, parse the Ldif and convert it to a LdifEntry 247 List<LdifEntry> ldifEntries = null; 248 249 try ( LdifReader reader = new LdifReader() ) 250 { 251 ldifEntries = reader.parseLdif( sb.toString() ); 252 } 253 catch ( IOException e ) 254 { 255 e.printStackTrace(); 256 } 257 258 if ( ( ldifEntries != null ) && ( ldifEntries.size() == 1 ) ) 259 { 260 LdifEntry ldifEntry = ldifEntries.get( 0 ); 261 262 changeType = ldifEntry.getChangeType(); 263 controls = ldifEntry.getControls(); 264 entryDn = ldifEntry.getDn(); 265 266 switch ( ldifEntry.getChangeType() ) 267 { 268 case Add: 269 // Fallback 270 case None: 271 entry = ldifEntry.getEntry(); 272 break; 273 274 case Delete: 275 break; 276 277 case ModDn: 278 case ModRdn: 279 newRdn = ldifEntry.getNewRdn(); 280 newSuperior = ldifEntry.getNewSuperior(); 281 deleteOldRdn = ldifEntry.isDeleteOldRdn(); 282 break; 283 284 case Modify: 285 modificationList = ldifEntry.getModifications(); 286 modifications = new HashMap<>(); 287 288 for ( Modification modification : modificationList ) 289 { 290 modifications.put( modification.getAttribute().getId(), modification ); 291 } 292 293 break; 294 295 default: 296 throw new IllegalArgumentException( I18n.err( I18n.ERR_13431_UNEXPECTED_CHANGETYPE, changeType ) ); 297 } 298 } 299 } 300 301 302 /** 303 * Creates a LdifEntry using a list of strings representing the Ldif element 304 * 305 * @param dn The LdifEntry DN 306 * @param strings The Ldif attributes and values to convert to an LdifEntry 307 * @throws LdapInvalidDnException If the Dn is invalid 308 * @throws LdapInvalidAttributeValueException If either the AttributeType or the associated value 309 * is incorrect 310 * @throws LdapLdifException If we get any other exception 311 */ 312 public LdifEntry( String dn, Object... strings ) 313 throws LdapInvalidAttributeValueException, LdapLdifException, LdapInvalidDnException 314 { 315 this( new Dn( dn ), strings ); 316 } 317 318 319 /** 320 * Set the Distinguished Name 321 * 322 * @param dn The Distinguished Name 323 */ 324 public void setDn( Dn dn ) 325 { 326 entryDn = dn; 327 entry.setDn( dn ); 328 } 329 330 331 /** 332 * Set the Distinguished Name 333 * 334 * @param dn The Distinguished Name 335 * @throws LdapInvalidDnException If the Dn is invalid 336 */ 337 public void setDn( String dn ) throws LdapInvalidDnException 338 { 339 entryDn = new Dn( dn ); 340 entry.setDn( entryDn ); 341 } 342 343 344 /** 345 * Set the modification type 346 * 347 * @param changeType The change type 348 * 349 */ 350 public void setChangeType( ChangeType changeType ) 351 { 352 this.changeType = changeType; 353 } 354 355 356 /** 357 * Set the change type 358 * 359 * @param changeType The change type 360 */ 361 public void setChangeType( String changeType ) 362 { 363 if ( "add".equals( changeType ) ) 364 { 365 this.changeType = ChangeType.Add; 366 } 367 else if ( "modify".equals( changeType ) ) 368 { 369 this.changeType = ChangeType.Modify; 370 } 371 else if ( "moddn".equals( changeType ) ) 372 { 373 this.changeType = ChangeType.ModDn; 374 } 375 else if ( "modrdn".equals( changeType ) ) 376 { 377 this.changeType = ChangeType.ModRdn; 378 } 379 else if ( "delete".equals( changeType ) ) 380 { 381 this.changeType = ChangeType.Delete; 382 } 383 } 384 385 386 /** 387 * Add a modification item (used by modify operations) 388 * 389 * @param modification The modification to be added 390 */ 391 public void addModification( Modification modification ) 392 { 393 if ( changeType == ChangeType.Modify ) 394 { 395 modificationList.add( modification ); 396 modifications.put( modification.getAttribute().getId(), modification ); 397 } 398 } 399 400 401 /** 402 * Add a modification item (used by modify operations) 403 * 404 * @param modOp The operation. One of : 405 * <ul> 406 * <li>ModificationOperation.ADD_ATTRIBUTE</li> 407 * <li>ModificationOperation.REMOVE_ATTRIBUTE</li> 408 * <li>ModificationOperation.REPLACE_ATTRIBUTE</li> 409 * <li>ModificationOperation.INCREMENT_ATTRIBUTE</li> 410 * </ul> 411 * 412 * @param attr The attribute to be added 413 */ 414 public void addModification( ModificationOperation modOp, Attribute attr ) 415 { 416 if ( changeType == ChangeType.Modify ) 417 { 418 Modification item = new DefaultModification( modOp, attr ); 419 modificationList.add( item ); 420 modifications.put( attr.getId(), item ); 421 } 422 } 423 424 425 /** 426 * Add a modification with no value 427 * 428 * @param modOp The modification operation value. One of : 429 * <ul> 430 * <li>ModificationOperation.ADD_ATTRIBUTE</li> 431 * <li>ModificationOperation.REMOVE_ATTRIBUTE</li> 432 * <li>ModificationOperation.REPLACE_ATTRIBUTE</li> 433 * <li>ModificationOperation.INCREMENT_ATTRIBUTE</li> 434 * </ul> 435 * 436 * @param id The attribute's ID 437 */ 438 public void addModification( ModificationOperation modOp, String id ) 439 { 440 if ( changeType == ChangeType.Modify ) 441 { 442 Attribute attr = new DefaultAttribute( id ); 443 444 Modification item = new DefaultModification( modOp, attr ); 445 modificationList.add( item ); 446 modifications.put( id, item ); 447 } 448 } 449 450 451 /** 452 * Add a modification 453 * 454 * @param modOp The modification operation value. One of : 455 * <ul> 456 * <li>ModificationOperation.ADD_ATTRIBUTE</li> 457 * <li>ModificationOperation.REMOVE_ATTRIBUTE</li> 458 * <li>ModificationOperation.REPLACE_ATTRIBUTE</li> 459 * <li>ModificationOperation.INCREMENT_ATTRIBUTE</li> 460 * </ul> 461 * 462 * @param id The attribute's ID 463 * @param value The attribute's value 464 */ 465 public void addModification( ModificationOperation modOp, String id, Object value ) 466 { 467 if ( changeType == ChangeType.Modify ) 468 { 469 Attribute attr; 470 471 if ( value == null ) 472 { 473 value = new Value( ( String ) null ); 474 attr = new DefaultAttribute( id, ( Value ) value ); 475 } 476 else 477 { 478 attr = ( Attribute ) value; 479 } 480 481 Modification item = new DefaultModification( modOp, attr ); 482 modificationList.add( item ); 483 modifications.put( id, item ); 484 } 485 } 486 487 488 /** 489 * Add an attribute to the entry 490 * 491 * @param attr The attribute to be added 492 * @throws org.apache.directory.api.ldap.model.exception.LdapException if something went wrong 493 */ 494 public void addAttribute( Attribute attr ) throws LdapException 495 { 496 entry.put( attr ); 497 } 498 499 500 /** 501 * Add an attribute to the entry 502 * 503 * @param id The attribute ID 504 * 505 * @param values The attribute values 506 * @throws LdapException if something went wrong 507 */ 508 public void addAttribute( String id, Object... values ) throws LdapException 509 { 510 Attribute attribute = entry.get( id ); 511 Boolean isHR = null; 512 513 if ( attribute != null ) 514 { 515 isHR = attribute.isHumanReadable(); 516 } 517 518 if ( values != null ) 519 { 520 for ( Object value : values ) 521 { 522 if ( value instanceof String ) 523 { 524 if ( isHR != null ) 525 { 526 if ( isHR ) 527 { 528 entry.add( id, ( String ) value ); 529 } 530 else 531 { 532 entry.add( id, Strings.getBytesUtf8( ( String ) value ) ); 533 } 534 } 535 else 536 { 537 entry.add( id, ( String ) value ); 538 } 539 } 540 else 541 { 542 if ( isHR != null ) 543 { 544 if ( isHR ) 545 { 546 entry.add( id, Strings.utf8ToString( ( byte[] ) value ) ); 547 } 548 else 549 { 550 entry.add( id, ( byte[] ) value ); 551 } 552 } 553 else 554 { 555 entry.add( id, ( byte[] ) value ); 556 } 557 } 558 } 559 } 560 else 561 { 562 entry.add( id, ( Value ) null ); 563 } 564 } 565 566 567 /** 568 * Remove a list of Attributes from the LdifEntry 569 * 570 * @param ids The Attributes to remove 571 */ 572 public void removeAttribute( String... ids ) 573 { 574 if ( entry.containsAttribute( ids ) ) 575 { 576 entry.removeAttributes( ids ); 577 } 578 } 579 580 581 /** 582 * Add an attribute value to an existing attribute 583 * 584 * @param id The attribute ID 585 * 586 * @param value The attribute value 587 * @throws org.apache.directory.api.ldap.model.exception.LdapException if something went wrong 588 */ 589 public void putAttribute( String id, Object value ) throws LdapException 590 { 591 if ( value instanceof String ) 592 { 593 entry.add( id, ( String ) value ); 594 } 595 else 596 { 597 entry.add( id, ( byte[] ) value ); 598 } 599 } 600 601 602 /** 603 * Get the change type 604 * 605 * @return The change type. One of : 606 * <ul> 607 * <li>ADD</li> 608 * <li>MODIFY</li> 609 * <li>MODDN</li> 610 * <li>MODRDN</li> 611 * <li>DELETE</li> 612 * <li>NONE</li> 613 * </ul> 614 */ 615 public ChangeType getChangeType() 616 { 617 return changeType; 618 } 619 620 621 /** 622 * @return The list of modification items 623 */ 624 public List<Modification> getModifications() 625 { 626 return modificationList; 627 } 628 629 630 /** 631 * Gets the modification items as an array. 632 * 633 * @return modification items as an array. 634 */ 635 public Modification[] getModificationArray() 636 { 637 return modificationList.toArray( EMPTY_MODS ); 638 } 639 640 641 /** 642 * @return The entry Distinguished name 643 */ 644 public Dn getDn() 645 { 646 return entryDn; 647 } 648 649 650 /** 651 * @return The number of entry modifications 652 */ 653 public int size() 654 { 655 return modificationList.size(); 656 } 657 658 659 /** 660 * Returns a attribute given it's id 661 * 662 * @param attributeId The attribute Id 663 * @return The attribute if it exists 664 */ 665 public Attribute get( String attributeId ) 666 { 667 if ( "dn".equalsIgnoreCase( attributeId ) ) 668 { 669 return new DefaultAttribute( "dn", entry.getDn().getName() ); 670 } 671 672 return entry.get( attributeId ); 673 } 674 675 676 /** 677 * Get the entry's entry 678 * 679 * @return the stored Entry 680 */ 681 public Entry getEntry() 682 { 683 if ( isEntry() ) 684 { 685 return entry; 686 } 687 else 688 { 689 return null; 690 } 691 } 692 693 694 /** 695 * @return True, if the old Rdn should be deleted. 696 */ 697 public boolean isDeleteOldRdn() 698 { 699 return deleteOldRdn; 700 } 701 702 703 /** 704 * Set the deleteOldRdn flag 705 * 706 * @param deleteOldRdn True if the old Rdn should be deleted 707 */ 708 public void setDeleteOldRdn( boolean deleteOldRdn ) 709 { 710 this.deleteOldRdn = deleteOldRdn; 711 } 712 713 714 /** 715 * @return The new Rdn 716 */ 717 public String getNewRdn() 718 { 719 return newRdn; 720 } 721 722 723 /** 724 * Set the new Rdn 725 * 726 * @param newRdn The new Rdn 727 */ 728 public void setNewRdn( String newRdn ) 729 { 730 this.newRdn = newRdn; 731 } 732 733 734 /** 735 * @return The new superior 736 */ 737 public String getNewSuperior() 738 { 739 return newSuperior; 740 } 741 742 743 /** 744 * Set the new superior 745 * 746 * @param newSuperior The new Superior 747 */ 748 public void setNewSuperior( String newSuperior ) 749 { 750 this.newSuperior = newSuperior; 751 } 752 753 754 /** 755 * @return True if this is a content ldif 756 */ 757 public boolean isLdifContent() 758 { 759 return changeType == ChangeType.None; 760 } 761 762 763 /** 764 * @return True if there is this is a change ldif 765 */ 766 public boolean isLdifChange() 767 { 768 return changeType != ChangeType.None; 769 } 770 771 772 /** 773 * @return True if the entry is an ADD entry 774 */ 775 public boolean isChangeAdd() 776 { 777 return changeType == ChangeType.Add; 778 } 779 780 781 /** 782 * @return True if the entry is a DELETE entry 783 */ 784 public boolean isChangeDelete() 785 { 786 return changeType == ChangeType.Delete; 787 } 788 789 790 /** 791 * @return True if the entry is a MODDN entry 792 */ 793 public boolean isChangeModDn() 794 { 795 return changeType == ChangeType.ModDn; 796 } 797 798 799 /** 800 * @return True if the entry is a MODRDN entry 801 */ 802 public boolean isChangeModRdn() 803 { 804 return changeType == ChangeType.ModRdn; 805 } 806 807 808 /** 809 * @return True if the entry is a MODIFY entry 810 */ 811 public boolean isChangeModify() 812 { 813 return changeType == ChangeType.Modify; 814 } 815 816 817 /** 818 * Tells if the current entry is a added one 819 * 820 * @return <code>true</code> if the entry is added 821 */ 822 public boolean isEntry() 823 { 824 return ( changeType == ChangeType.None ) || ( changeType == ChangeType.Add ); 825 } 826 827 828 /** 829 * @return true if the entry has some controls 830 */ 831 public boolean hasControls() 832 { 833 return controls != null; 834 } 835 836 837 /** 838 * @return The set of controls for this entry 839 */ 840 public Map<String, LdifControl> getControls() 841 { 842 return controls; 843 } 844 845 846 /** 847 * @param oid The control's OID 848 * @return The associated control, if any 849 */ 850 public LdifControl getControl( String oid ) 851 { 852 if ( controls != null ) 853 { 854 return controls.get( oid ); 855 } 856 857 return null; 858 } 859 860 861 /** 862 * Add a control to the entry 863 * 864 * @param controls The added controls 865 */ 866 public void addControl( Control... controls ) 867 { 868 if ( controls == null ) 869 { 870 throw new IllegalArgumentException( I18n.err( I18n.ERR_13432_NULL_ADDED_CONTROL ) ); 871 } 872 873 for ( Control control : controls ) 874 { 875 if ( changeType == ChangeType.None ) 876 { 877 changeType = ChangeType.Add; 878 } 879 880 if ( this.controls == null ) 881 { 882 this.controls = new ConcurrentHashMap<>(); 883 } 884 885 if ( control instanceof LdifControl ) 886 { 887 this.controls.put( control.getOid(), ( LdifControl ) control ); 888 } 889 else 890 { 891 LdifControl ldifControl = new LdifControl( control.getOid() ); 892 ldifControl.setCritical( control.isCritical() ); 893 this.controls.put( control.getOid(), new LdifControl( control.getOid() ) ); 894 } 895 } 896 } 897 898 899 /** 900 * Clone method 901 * @return a clone of the current instance 902 * @exception CloneNotSupportedException If there is some problem while cloning the instance 903 */ 904 @Override 905 public LdifEntry clone() throws CloneNotSupportedException 906 { 907 LdifEntry clone = ( LdifEntry ) super.clone(); 908 909 if ( modificationList != null ) 910 { 911 for ( Modification modif : modificationList ) 912 { 913 Modification modifClone = new DefaultModification( modif.getOperation(), 914 modif.getAttribute().clone() ); 915 clone.modificationList.add( modifClone ); 916 } 917 } 918 919 if ( modifications != null ) 920 { 921 for ( Map.Entry<String, Modification> modificationEntry : modifications.entrySet() ) 922 { 923 Modification modif = modificationEntry.getValue(); 924 Modification modifClone = new DefaultModification( modif.getOperation(), 925 modif.getAttribute().clone() ); 926 clone.modifications.put( modificationEntry.getKey(), modifClone ); 927 } 928 929 } 930 931 if ( entry != null ) 932 { 933 clone.entry = entry.clone(); 934 } 935 936 return clone; 937 } 938 939 940 /** 941 * Returns the lengthBeforeParsing of the entry at the time of parsing. This includes 942 * the lengthBeforeParsing of the comments present in entry at the time of parsing 943 * so this lengthBeforeParsing may not always match with the lengthBeforeParsing of the entry 944 * data present in memory. 945 * 946 * @return The entry length, comments included 947 */ 948 public int getLengthBeforeParsing() 949 { 950 return lengthBeforeParsing; 951 } 952 953 954 /** 955 * @param length the lengthBeforeParsing to set 956 */ 957 /*No qualifier*/ void setLengthBeforeParsing( int length ) 958 { 959 this.lengthBeforeParsing = length; 960 } 961 962 963 /** 964 * @return the offset 965 */ 966 public long getOffset() 967 { 968 return offset; 969 } 970 971 972 /** 973 * @param offset the offset to set 974 */ 975 /*No qualifier*/ void setOffset( long offset ) 976 { 977 this.offset = offset; 978 } 979 980 981 /** 982 * Returns an enumeration containing the zero or more attributes in the 983 * collection. The behavior of the enumeration is not specified if the 984 * attribute collection is changed. 985 * 986 * @return an enumeration of all contained attributes 987 */ 988 @Override 989 public Iterator<Attribute> iterator() 990 { 991 return entry.iterator(); 992 } 993 994 995 /** 996 * @return a String representing the Entry, as a LDIF 997 */ 998 @Override 999 public String toString() 1000 { 1001 try 1002 { 1003 return LdifUtils.convertToLdif( this ); 1004 } 1005 catch ( LdapException ne ) 1006 { 1007 return ""; 1008 } 1009 } 1010 1011 1012 /** 1013 * @see Object#hashCode() 1014 * 1015 * @return the instance's hash code 1016 */ 1017 @Override 1018 public int hashCode() 1019 { 1020 int result = 37; 1021 1022 if ( entry != null && entry.getDn() != null ) 1023 { 1024 result = result * 17 + entry.getDn().hashCode(); 1025 } 1026 1027 if ( changeType != null ) 1028 { 1029 result = result * 17 + changeType.hashCode(); 1030 1031 // Check each different cases 1032 switch ( changeType ) 1033 { 1034 case None: 1035 // Fall through 1036 case Add: 1037 // Checks the attributes 1038 if ( entry != null ) 1039 { 1040 result = result * 17 + entry.hashCode(); 1041 } 1042 1043 break; 1044 1045 case Delete: 1046 // Nothing to compute 1047 break; 1048 1049 case Modify: 1050 if ( modificationList != null ) 1051 { 1052 result = result * 17 + modificationList.hashCode(); 1053 1054 for ( Modification modification : modificationList ) 1055 { 1056 result = result * 17 + modification.hashCode(); 1057 } 1058 } 1059 1060 break; 1061 1062 case ModDn: 1063 case ModRdn: 1064 result = result * 17; 1065 1066 if ( deleteOldRdn ) 1067 { 1068 result++; 1069 } 1070 else 1071 { 1072 result--; 1073 } 1074 1075 if ( newRdn != null ) 1076 { 1077 result = result * 17 + newRdn.hashCode(); 1078 } 1079 1080 if ( newSuperior != null ) 1081 { 1082 result = result * 17 + newSuperior.hashCode(); 1083 } 1084 1085 break; 1086 1087 default: 1088 // do nothing 1089 break; 1090 } 1091 } 1092 1093 if ( controls != null ) 1094 { 1095 for ( String control : controls.keySet() ) 1096 { 1097 result = result * 17 + control.hashCode(); 1098 } 1099 } 1100 1101 return result; 1102 } 1103 1104 1105 /** 1106 * {@inheritDoc} 1107 */ 1108 @Override 1109 public boolean equals( Object o ) 1110 { 1111 // Basic equals checks 1112 if ( this == o ) 1113 { 1114 return true; 1115 } 1116 1117 if ( o == null ) 1118 { 1119 return false; 1120 } 1121 1122 if ( !( o instanceof LdifEntry ) ) 1123 { 1124 return false; 1125 } 1126 1127 LdifEntry otherEntry = ( LdifEntry ) o; 1128 1129 // Check the Dn 1130 Dn thisDn = entryDn; 1131 Dn dnEntry = otherEntry.getDn(); 1132 1133 if ( !thisDn.equals( dnEntry ) ) 1134 { 1135 return false; 1136 } 1137 1138 // Check the changeType 1139 if ( changeType != otherEntry.changeType ) 1140 { 1141 return false; 1142 } 1143 1144 // Check each different cases 1145 switch ( changeType ) 1146 { 1147 case None: 1148 // Fall through 1149 case Add: 1150 // Checks the attributes 1151 if ( entry.size() != otherEntry.entry.size() ) 1152 { 1153 return false; 1154 } 1155 1156 if ( !entry.equals( otherEntry.entry ) ) 1157 { 1158 return false; 1159 } 1160 1161 break; 1162 1163 case Delete: 1164 // Nothing to do, if the DNs are equals 1165 break; 1166 1167 case Modify: 1168 // Check the modificationItems list 1169 1170 // First, deal with special cases 1171 if ( modificationList == null ) 1172 { 1173 if ( otherEntry.modificationList != null ) 1174 { 1175 return false; 1176 } 1177 else 1178 { 1179 break; 1180 } 1181 } 1182 1183 if ( otherEntry.modificationList == null ) 1184 { 1185 return false; 1186 } 1187 1188 if ( modificationList.size() != otherEntry.modificationList.size() ) 1189 { 1190 return false; 1191 } 1192 1193 // Now, compares the contents 1194 int i = 0; 1195 1196 for ( Modification modification : modificationList ) 1197 { 1198 if ( !modification.equals( otherEntry.modificationList.get( i ) ) ) 1199 { 1200 return false; 1201 } 1202 1203 i++; 1204 } 1205 1206 break; 1207 1208 case ModDn: 1209 case ModRdn: 1210 // Check the deleteOldRdn flag 1211 if ( deleteOldRdn != otherEntry.deleteOldRdn ) 1212 { 1213 return false; 1214 } 1215 1216 // Check the newRdn value 1217 try 1218 { 1219 Rdn thisNewRdn = new Rdn( newRdn ); 1220 Rdn entryNewRdn = new Rdn( otherEntry.newRdn ); 1221 1222 if ( !thisNewRdn.equals( entryNewRdn ) ) 1223 { 1224 return false; 1225 } 1226 } 1227 catch ( LdapInvalidDnException ine ) 1228 { 1229 return false; 1230 } 1231 1232 // Check the newSuperior value 1233 try 1234 { 1235 Dn thisNewSuperior = new Dn( newSuperior ); 1236 Dn entryNewSuperior = new Dn( otherEntry.newSuperior ); 1237 1238 if ( !thisNewSuperior.equals( entryNewSuperior ) ) 1239 { 1240 return false; 1241 } 1242 } 1243 catch ( LdapInvalidDnException ine ) 1244 { 1245 return false; 1246 } 1247 1248 break; 1249 1250 default: 1251 // do nothing 1252 break; 1253 } 1254 1255 if ( controls != null ) 1256 { 1257 Map<String, LdifControl> otherControls = otherEntry.controls; 1258 1259 if ( otherControls == null ) 1260 { 1261 return false; 1262 } 1263 1264 if ( controls.size() != otherControls.size() ) 1265 { 1266 return false; 1267 } 1268 1269 for ( Map.Entry<String, LdifControl> controlEntry : controls.entrySet() ) 1270 { 1271 String controlOid = controlEntry.getKey(); 1272 1273 if ( !otherControls.containsKey( controlOid ) ) 1274 { 1275 return false; 1276 } 1277 1278 Control thisControl = controlEntry.getValue(); 1279 Control otherControl = otherControls.get( controlOid ); 1280 1281 if ( thisControl == null ) 1282 { 1283 if ( otherControl != null ) 1284 { 1285 return false; 1286 } 1287 } 1288 else 1289 { 1290 if ( !thisControl.equals( otherControl ) ) 1291 { 1292 return false; 1293 } 1294 } 1295 } 1296 1297 return true; 1298 } 1299 else 1300 { 1301 return otherEntry.controls == null; 1302 } 1303 } 1304 1305 1306 /** 1307 * @see Externalizable#readExternal(ObjectInput) 1308 * 1309 * @param in The stream from which the LdifEntry is read 1310 * @throws IOException If the stream can't be read 1311 * @throws ClassNotFoundException If the LdifEntry can't be created 1312 */ 1313 @Override 1314 public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException 1315 { 1316 // Read the changeType 1317 int type = in.readInt(); 1318 changeType = ChangeType.getChangeType( type ); 1319 1320 // Read the modification 1321 switch ( changeType ) 1322 { 1323 case Add: 1324 case None: 1325 // Read the entry 1326 entry.readExternal( in ); 1327 entryDn = entry.getDn(); 1328 1329 break; 1330 1331 case Delete: 1332 // Read the Dn 1333 entryDn = new Dn(); 1334 entryDn.readExternal( in ); 1335 1336 break; 1337 1338 case ModDn: 1339 // Fallback 1340 case ModRdn: 1341 // Read the Dn 1342 entryDn = new Dn(); 1343 entryDn.readExternal( in ); 1344 1345 deleteOldRdn = in.readBoolean(); 1346 1347 if ( in.readBoolean() ) 1348 { 1349 newRdn = in.readUTF(); 1350 } 1351 1352 if ( in.readBoolean() ) 1353 { 1354 newSuperior = in.readUTF(); 1355 } 1356 1357 break; 1358 1359 case Modify: 1360 // Read the Dn 1361 entryDn = new Dn(); 1362 entryDn.readExternal( in ); 1363 1364 // Read the modifications 1365 int nbModifs = in.readInt(); 1366 1367 for ( int i = 0; i < nbModifs; i++ ) 1368 { 1369 Modification modification = new DefaultModification(); 1370 modification.readExternal( in ); 1371 1372 addModification( modification ); 1373 } 1374 1375 break; 1376 1377 default: 1378 throw new IllegalArgumentException( I18n.err( I18n.ERR_13431_UNEXPECTED_CHANGETYPE, changeType ) ); 1379 } 1380 1381 int nbControls = in.readInt(); 1382 1383 // We have at least a control 1384 if ( nbControls > 0 ) 1385 { 1386 controls = new ConcurrentHashMap<>( nbControls ); 1387 1388 for ( int i = 0; i < nbControls; i++ ) 1389 { 1390 LdifControl control = new LdifControl(); 1391 1392 control.readExternal( in ); 1393 1394 controls.put( control.getOid(), control ); 1395 } 1396 } 1397 } 1398 1399 1400 /** 1401 * @see Externalizable#readExternal(ObjectInput) 1402 * @param out The stream in which the ChangeLogEvent will be serialized. 1403 * @throws IOException If the serialization fail 1404 */ 1405 @Override 1406 public void writeExternal( ObjectOutput out ) throws IOException 1407 { 1408 // Write the changeType 1409 out.writeInt( changeType.getChangeType() ); 1410 1411 // Write the data 1412 switch ( changeType ) 1413 { 1414 case Add: 1415 case None: 1416 entry.writeExternal( out ); 1417 break; 1418 1419 // Fallback 1420 case Delete: 1421 // we write the Dn 1422 entryDn.writeExternal( out ); 1423 break; 1424 1425 case ModDn: 1426 // Fallback 1427 case ModRdn: 1428 // Write the Dn 1429 entryDn.writeExternal( out ); 1430 1431 out.writeBoolean( deleteOldRdn ); 1432 1433 if ( newRdn == null ) 1434 { 1435 out.writeBoolean( false ); 1436 } 1437 else 1438 { 1439 out.writeBoolean( true ); 1440 out.writeUTF( newRdn ); 1441 } 1442 1443 if ( newSuperior != null ) 1444 { 1445 out.writeBoolean( true ); 1446 out.writeUTF( newSuperior ); 1447 } 1448 else 1449 { 1450 out.writeBoolean( false ); 1451 } 1452 break; 1453 1454 case Modify: 1455 // Write the Dn 1456 entryDn.writeExternal( out ); 1457 1458 // Write the modifications 1459 out.writeInt( modificationList.size() ); 1460 1461 for ( Modification modification : modificationList ) 1462 { 1463 modification.writeExternal( out ); 1464 } 1465 1466 break; 1467 1468 default: 1469 throw new IllegalArgumentException( I18n.err( I18n.ERR_13431_UNEXPECTED_CHANGETYPE, changeType ) ); 1470 } 1471 1472 // The controls 1473 if ( controls != null ) 1474 { 1475 // Write the control 1476 out.writeInt( controls.size() ); 1477 1478 for ( LdifControl control : controls.values() ) 1479 { 1480 control.writeExternal( out ); 1481 } 1482 } 1483 else 1484 { 1485 // No control, write -1 1486 out.writeInt( -1 ); 1487 } 1488 1489 // and flush the result 1490 out.flush(); 1491 } 1492}