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