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