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     */
020    
021    package org.apache.directory.shared.ldap.ldif;
022    
023    import java.io.Externalizable;
024    import java.io.IOException;
025    import java.io.ObjectInput;
026    import java.io.ObjectOutput;
027    import java.util.HashMap;
028    import java.util.LinkedList;
029    import java.util.List;
030    import java.util.Map;
031    
032    import javax.naming.InvalidNameException;
033    import javax.naming.NamingException;
034    
035    import org.apache.directory.shared.ldap.entry.Entry;
036    import org.apache.directory.shared.ldap.entry.EntryAttribute;
037    import org.apache.directory.shared.ldap.entry.Modification;
038    import org.apache.directory.shared.ldap.entry.ModificationOperation;
039    import org.apache.directory.shared.ldap.entry.Value;
040    import org.apache.directory.shared.ldap.entry.client.ClientEntry;
041    import org.apache.directory.shared.ldap.entry.client.ClientModification;
042    import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
043    import org.apache.directory.shared.ldap.entry.client.DefaultClientAttribute;
044    import org.apache.directory.shared.ldap.entry.client.DefaultClientEntry;
045    import org.apache.directory.shared.ldap.message.control.Control;
046    import org.apache.directory.shared.ldap.name.DN;
047    import org.apache.directory.shared.ldap.name.RDN;
048    import org.apache.directory.shared.ldap.util.StringTools;
049    
050    
051    /**
052     * A entry to be populated by an ldif parser.
053     * 
054     * We will have different kind of entries : 
055     * - added entries 
056     * - deleted entries 
057     * - modified entries 
058     * - RDN modified entries 
059     * - DN modified entries
060     * 
061     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
062     * @version $Rev$, $Date$
063     */
064    public class LdifEntry implements Cloneable, Externalizable
065    {
066        private static final long serialVersionUID = 2L;
067        
068        /** Used in toArray() */
069        public static final Modification[] EMPTY_MODS = new Modification[0];
070    
071        /** the change type */
072        private ChangeType changeType;
073    
074        /** the modification item list */
075        private List<Modification> modificationList;
076    
077        private Map<String, Modification> modificationItems;
078    
079        /** The new superior */
080        private String newSuperior;
081    
082        /** The new rdn */
083        private String newRdn;
084    
085        /** The delete old rdn flag */
086        private boolean deleteOldRdn;
087    
088        /** the entry */
089        private ClientEntry entry;
090    
091        
092        /** The control */
093        private Control control;
094    
095        /**
096         * Creates a new Entry object.
097         */
098        public LdifEntry()
099        {
100            changeType = ChangeType.Add; // Default LDIF content
101            modificationList = new LinkedList<Modification>();
102            modificationItems = new HashMap<String, Modification>();
103            entry = new DefaultClientEntry( null );
104            control = null;
105        }
106    
107        
108        /**
109         * Set the Distinguished Name
110         * 
111         * @param dn
112         *            The Distinguished Name
113         */
114        public void setDn( DN dn )
115        {
116            entry.setDn( (DN)dn.clone() );
117        }
118    
119        
120        /**
121         * Set the Distinguished Name
122         * 
123         * @param dn The Distinguished Name
124         */
125        public void setDn( String dn ) throws InvalidNameException
126        {
127            entry.setDn( new DN( dn ) );
128        }
129    
130    
131        /**
132         * Set the modification type
133         * 
134         * @param changeType
135         *            The change type
136         * 
137         */
138        public void setChangeType( ChangeType changeType )
139        {
140            this.changeType = changeType;
141        }
142    
143        /**
144         * Set the change type
145         * 
146         * @param changeType
147         *            The change type
148         */
149        public void setChangeType( String changeType )
150        {
151            if ( "add".equals( changeType ) )
152            {
153                this.changeType = ChangeType.Add;
154            }
155            else if ( "modify".equals( changeType ) )
156            {
157                this.changeType = ChangeType.Modify;
158            }
159            else if ( "moddn".equals( changeType ) )
160            {
161                this.changeType = ChangeType.ModDn;
162            }
163            else if ( "modrdn".equals( changeType ) )
164            {
165                this.changeType = ChangeType.ModRdn;
166            }
167            else if ( "delete".equals( changeType ) )
168            {
169                this.changeType = ChangeType.Delete;
170            }
171        }
172    
173        /**
174         * Add a modification item (used by modify operations)
175         * 
176         * @param modification The modification to be added
177         */
178        public void addModificationItem( Modification modification )
179        {
180            if ( changeType == ChangeType.Modify )
181            {
182                modificationList.add( modification );
183                modificationItems.put( modification.getAttribute().getId(), modification );
184            }
185        }
186    
187        /**
188         * Add a modification item (used by modify operations)
189         * 
190         * @param modOp The operation. One of : 
191         * - ModificationOperation.ADD_ATTRIBUTE
192         * - ModificationOperation.REMOVE_ATTRIBUTE 
193         * - ModificationOperation.REPLACE_ATTRIBUTE
194         * 
195         * @param attr The attribute to be added
196         */
197        public void addModificationItem( ModificationOperation modOp, EntryAttribute attr )
198        {
199            if ( changeType == ChangeType.Modify )
200            {
201                Modification item = new ClientModification( modOp, attr );
202                modificationList.add( item );
203                modificationItems.put( attr.getId(), item );
204            }
205        }
206    
207    
208        /**
209         * Add a modification item
210         * 
211         * @param modOp The operation. One of : 
212         *  - ModificationOperation.ADD_ATTRIBUTE
213         *  - ModificationOperation.REMOVE_ATTRIBUTE 
214         *  - ModificationOperation.REPLACE_ATTRIBUTE
215         * 
216         * @param modOp The modification operation value
217         * @param id The attribute's ID
218         * @param value The attribute's value
219         */
220        public void addModificationItem( ModificationOperation modOp, String id, Object value )
221        {
222            if ( changeType == ChangeType.Modify )
223            {
224                EntryAttribute attr =  null;
225                
226                if ( value == null )
227                {
228                    value = new ClientStringValue( null );
229                    attr = new DefaultClientAttribute( id, (Value<?>)value );
230                }
231                else
232                {
233                    attr = (EntryAttribute)value;
234                }
235    
236                Modification item = new ClientModification( modOp, attr );
237                modificationList.add( item );
238                modificationItems.put( id, item );
239            }
240        }
241    
242    
243        /**
244         * Add an attribute to the entry
245         * 
246         * @param attr
247         *            The attribute to be added
248         */
249        public void addAttribute( EntryAttribute attr ) throws NamingException
250        {
251            entry.put( attr );
252        }
253    
254        /**
255         * Add an attribute to the entry
256         * 
257         * @param id
258         *            The attribute ID
259         * 
260         * @param value
261         *            The attribute value
262         * 
263         */
264        public void addAttribute( String id, Object value ) throws NamingException
265        {
266            if ( value instanceof String )
267            {
268                entry.add( id, (String)value );
269            }
270            else
271            {
272                entry.add( id, (byte[])value );
273            }
274        }
275        
276        
277        /**
278         * Remove a list of Attributes from the LdifEntry
279         *
280         * @param ids The Attributes to remove
281         * @return The list of removed EntryAttributes
282         */
283        public List<EntryAttribute> removeAttribute( String... ids )
284        {
285            if ( entry.containsAttribute( ids ) )
286            {
287                return entry.removeAttributes( ids );
288            }
289            else
290            {
291                return null;
292            }
293        }
294    
295        /**
296         * Add an attribute value to an existing attribute
297         * 
298         * @param id
299         *            The attribute ID
300         * 
301         * @param value
302         *            The attribute value
303         * 
304         */
305        public void putAttribute( String id, Object value ) throws NamingException
306        {
307            if ( value instanceof String )
308            {
309                entry.add( id, (String)value );
310            }
311            else
312            {
313                entry.add( id, (byte[])value );
314            }
315        }
316    
317        /**
318         * Get the change type
319         * 
320         * @return The change type. One of : ADD = 0; MODIFY = 1; MODDN = 2; MODRDN =
321         *         3; DELETE = 4;
322         */
323        public ChangeType getChangeType()
324        {
325            return changeType;
326        }
327    
328        /**
329         * @return The list of modification items
330         */
331        public List<Modification> getModificationItems()
332        {
333            return modificationList;
334        }
335    
336    
337        /**
338         * Gets the modification items as an array.
339         *
340         * @return modification items as an array.
341         */
342        public Modification[] getModificationItemsArray()
343        {
344            return modificationList.toArray( EMPTY_MODS );
345        }
346    
347    
348        /**
349         * @return The entry Distinguished name
350         */
351        public DN getDn()
352        {
353            return entry.getDn();
354        }
355    
356        /**
357         * @return The number of entry modifications
358         */
359        public int size()
360        {
361            return modificationList.size();
362        }
363    
364        /**
365         * Returns a attribute given it's id
366         * 
367         * @param attributeId
368         *            The attribute Id
369         * @return The attribute if it exists
370         */
371        public EntryAttribute get( String attributeId )
372        {
373            if ( "dn".equalsIgnoreCase( attributeId ) )
374            {
375                return new DefaultClientAttribute( "dn", entry.getDn().getName() );
376            }
377    
378            return entry.get( attributeId );
379        }
380    
381        /**
382         * Get the entry's entry
383         * 
384         * @return the stored Entry
385         */
386        public Entry getEntry()
387        {
388            if ( isEntry() )
389            {
390                return entry;
391            }
392            else
393            {
394                return null;
395            }
396        }
397    
398        /**
399         * @return True, if the old RDN should be deleted.
400         */
401        public boolean isDeleteOldRdn()
402        {
403            return deleteOldRdn;
404        }
405    
406        /**
407         * Set the flage deleteOldRdn
408         * 
409         * @param deleteOldRdn
410         *            True if the old RDN should be deleted
411         */
412        public void setDeleteOldRdn( boolean deleteOldRdn )
413        {
414            this.deleteOldRdn = deleteOldRdn;
415        }
416    
417        /**
418         * @return The new RDN
419         */
420        public String getNewRdn()
421        {
422            return newRdn;
423        }
424    
425        /**
426         * Set the new RDN
427         * 
428         * @param newRdn
429         *            The new RDN
430         */
431        public void setNewRdn( String newRdn )
432        {
433            this.newRdn = newRdn;
434        }
435    
436        /**
437         * @return The new superior
438         */
439        public String getNewSuperior()
440        {
441            return newSuperior;
442        }
443    
444        /**
445         * Set the new superior
446         * 
447         * @param newSuperior
448         *            The new Superior
449         */
450        public void setNewSuperior( String newSuperior )
451        {
452            this.newSuperior = newSuperior;
453        }
454    
455        /**
456         * @return True if the entry is an ADD entry
457         */
458        public boolean isChangeAdd()
459        {
460            return changeType == ChangeType.Add;
461        }
462    
463        /**
464         * @return True if the entry is a DELETE entry
465         */
466        public boolean isChangeDelete()
467        {
468            return changeType == ChangeType.Delete;
469        }
470    
471        /**
472         * @return True if the entry is a MODDN entry
473         */
474        public boolean isChangeModDn()
475        {
476            return changeType == ChangeType.ModDn;
477        }
478    
479        /**
480         * @return True if the entry is a MODRDN entry
481         */
482        public boolean isChangeModRdn()
483        {
484            return changeType == ChangeType.ModRdn;
485        }
486    
487        /**
488         * @return True if the entry is a MODIFY entry
489         */
490        public boolean isChangeModify()
491        {
492            return changeType == ChangeType.Modify;
493        }
494    
495        /**
496         * Tells if the current entry is a added one
497         *
498         * @return <code>true</code> if the entry is added
499         */
500        public boolean isEntry()
501        {
502            return changeType == ChangeType.Add;
503        }
504    
505        /**
506         * @return The associated control, if any
507         */
508        public Control getControl()
509        {
510            return control;
511        }
512    
513        /**
514         * Add a control to the entry
515         * 
516         * @param control
517         *            The control
518         */
519        public void setControl( Control control )
520        {
521            this.control = control;
522        }
523    
524        /**
525         * Clone method
526         * @return a clone of the current instance
527         * @exception CloneNotSupportedException If there is some problem while cloning the instance
528         */
529        public LdifEntry clone() throws CloneNotSupportedException
530        {
531            LdifEntry clone = (LdifEntry) super.clone();
532    
533            if ( modificationList != null )
534            {
535                for ( Modification modif:modificationList )
536                {
537                    Modification modifClone = new ClientModification( modif.getOperation(), 
538                        (EntryAttribute) modif.getAttribute().clone() );
539                    clone.modificationList.add( modifClone );
540                }
541            }
542    
543            if ( modificationItems != null )
544            {
545                for ( String key:modificationItems.keySet() )
546                {
547                    Modification modif = modificationItems.get( key );
548                    Modification modifClone = new ClientModification( modif.getOperation(), 
549                        (EntryAttribute) modif.getAttribute().clone() );
550                    clone.modificationItems.put( key, modifClone );
551                }
552    
553            }
554    
555            if ( entry != null )
556            {
557                clone.entry = (ClientEntry)entry.clone();
558            }
559    
560            return clone;
561        }
562        
563        /**
564         * Dumps the attributes
565         * @return A String representing the attributes
566         */
567        private String dumpAttributes()
568        {
569            StringBuffer sb = new StringBuffer();
570            
571            for ( EntryAttribute attribute:entry )
572            {
573                if ( attribute == null )
574                {
575                    sb.append( "        Null attribute\n" );
576                    continue;
577                }
578                
579                sb.append( "        ").append( attribute.getId() ).append( ":\n" );
580                
581                for ( Value<?> value:attribute )
582                {
583                    if ( !value.isBinary() )
584                    {
585                        sb.append(  "            " ).append( value.getString() ).append('\n' );
586                    }
587                    else
588                    {
589                        sb.append(  "            " ).append( StringTools.dumpBytes( value.getBytes() ) ).append('\n' );
590                    }
591                }
592            }
593            
594            return sb.toString();
595        }
596        
597        /**
598         * Dumps the modifications
599         * @return A String representing the modifications
600         */
601        private String dumpModificationItems()
602        {
603            StringBuffer sb = new StringBuffer();
604            
605            for ( Modification modif:modificationList )
606            {
607                sb.append( "            Operation: " );
608                
609                switch ( modif.getOperation() )
610                {
611                    case ADD_ATTRIBUTE :
612                        sb.append( "ADD\n" );
613                        break;
614                        
615                    case REMOVE_ATTRIBUTE :
616                        sb.append( "REMOVE\n" );
617                        break;
618                        
619                    case REPLACE_ATTRIBUTE :
620                        sb.append( "REPLACE \n" );
621                        break;
622                        
623                    default :
624                        break; // Do nothing
625                }
626                
627                EntryAttribute attribute = modif.getAttribute();
628                
629                sb.append( "                Attribute: " ).append( attribute.getId() ).append( '\n' );
630                
631                if ( attribute.size() != 0 )
632                {
633                    for ( Value<?> value:attribute )
634                    {
635                        if ( !value.isBinary() )
636                        {
637                            sb.append(  "                " ).append( value.getString() ).append('\n' );
638                        }
639                        else
640                        {
641                            sb.append(  "                " ).append( StringTools.dumpBytes( value.getBytes() ) ).append('\n' );
642                        }
643                    }
644                }
645            }
646            
647            return sb.toString();
648        }
649    
650        
651        /**
652         * @return a String representing the Entry, as a LDIF 
653         */
654        public String toString()
655        {
656            try
657            {
658                return LdifUtils.convertToLdif( this );
659            }
660            catch ( NamingException ne )
661            {
662                return null;
663            }
664        }
665        
666        
667        /**
668         * @see Object#hashCode()
669         * 
670         * @return the instance's hash code
671         */
672        public int hashCode()
673        {
674            int result = 37;
675    
676            if ( entry.getDn() != null )
677            {
678                result = result*17 + entry.getDn().hashCode();
679            }
680            
681            if ( changeType != null )
682            {
683                result = result*17 + changeType.hashCode();
684                
685                // Check each different cases
686                switch ( changeType )
687                {
688                    case Add :
689                        // Checks the attributes
690                        if ( entry != null )
691                        {
692                            result = result * 17 + entry.hashCode();
693                        }
694                        
695                        break;
696    
697                    case Delete :
698                        // Nothing to compute
699                        break;
700                        
701                    case Modify :
702                        if ( modificationList != null )
703                        {
704                            result = result * 17 + modificationList.hashCode();
705                            
706                            for ( Modification modification:modificationList )
707                            {
708                                result = result * 17 + modification.hashCode();
709                            }
710                        }
711                        
712                        break;
713                        
714                    case ModDn :
715                    case ModRdn :
716                        result = result * 17 + ( deleteOldRdn ? 1 : -1 ); 
717                        
718                        if ( newRdn != null )
719                        {
720                            result = result*17 + newRdn.hashCode();
721                        }
722                        
723                        if ( newSuperior != null )
724                        {
725                            result = result*17 + newSuperior.hashCode();
726                        }
727                        
728                        break;
729                        
730                    default :
731                        break; // do nothing
732                }
733            }
734    
735            if ( control != null )
736            {
737                result = result * 17 + control.hashCode();
738            }
739    
740            return result;
741        }
742        
743        /**
744         * @see Object#equals(Object)
745         * @return <code>true</code> if both values are equal
746         */
747        public boolean equals( Object o )
748        {
749            // Basic equals checks
750            if ( this == o )
751            {
752                return true;
753            }
754            
755            if ( o == null )
756            {
757               return false;
758            }
759            
760            if ( ! (o instanceof LdifEntry ) )
761            {
762                return false;
763            }
764            
765            LdifEntry otherEntry = (LdifEntry)o;
766            
767            // Check the DN
768            DN thisDn = entry.getDn();
769            DN dnEntry = otherEntry.getDn();
770            
771            if ( !thisDn.equals( dnEntry ) )
772            {
773                return false;
774            }
775    
776            
777            // Check the changeType
778            if ( changeType != otherEntry.changeType )
779            {
780                return false;
781            }
782            
783            // Check each different cases
784            switch ( changeType )
785            {
786                case Add :
787                    // Checks the attributes
788                    if ( entry == null )
789                    {
790                        if ( otherEntry.entry != null )
791                        {
792                            return false;
793                        }
794                        else
795                        {
796                            break;
797                        }
798                    }
799                    
800                    if ( otherEntry.entry == null )
801                    {
802                        return false;
803                    }
804                    
805                    if ( entry.size() != otherEntry.entry.size() )
806                    {
807                        return false;
808                    }
809                    
810                    if ( !entry.equals( otherEntry.entry ) )
811                    {
812                        return false;
813                    }
814                    
815                    break;
816    
817                case Delete :
818                    // Nothing to do, if the DNs are equals
819                    break;
820                    
821                case Modify :
822                    // Check the modificationItems list
823    
824                    // First, deal with special cases
825                    if ( modificationList == null )
826                    {
827                        if ( otherEntry.modificationList != null )
828                        {
829                            return false;
830                        }
831                        else
832                        {
833                            break;
834                        }
835                    }
836                    
837                    if ( otherEntry.modificationList == null )
838                    {
839                        return false;
840                    }
841                    
842                    if ( modificationList.size() != otherEntry.modificationList.size() )
843                    {
844                        return false;
845                    }
846                    
847                    // Now, compares the contents
848                    int i = 0;
849                    
850                    for ( Modification modification:modificationList )
851                    {
852                        if ( ! modification.equals( otherEntry.modificationList.get( i ) ) )
853                        {
854                            return false;
855                        }
856                        
857                        i++;
858                    }
859                    
860                    break;
861                    
862                case ModDn :
863                case ModRdn :
864                    // Check the deleteOldRdn flag
865                    if ( deleteOldRdn != otherEntry.deleteOldRdn )
866                    {
867                        return false;
868                    }
869                    
870                    // Check the newRdn value
871                    try
872                    {
873                        RDN thisNewRdn = new RDN( newRdn );
874                        RDN entryNewRdn = new RDN( otherEntry.newRdn );
875    
876                        if ( !thisNewRdn.equals( entryNewRdn ) )
877                        {
878                            return false;
879                        }
880                    }
881                    catch ( InvalidNameException ine )
882                    {
883                        return false;
884                    }
885                    
886                    // Check the newSuperior value
887                    try
888                    {
889                        DN thisNewSuperior = new DN( newSuperior );
890                        DN entryNewSuperior = new DN( otherEntry.newSuperior );
891                        
892                        if ( ! thisNewSuperior.equals(  entryNewSuperior ) )
893                        {
894                            return false;
895                        }
896                    }
897                    catch ( InvalidNameException ine )
898                    {
899                        return false;
900                    }
901                    
902                    break;
903                    
904                default :
905                    break; // do nothing
906            }
907            
908            if ( control != null )
909            {
910                return control.equals( otherEntry.control );
911            }
912            else 
913            {
914                return otherEntry.control == null;
915            }
916        }
917    
918    
919        /**
920         * @see Externalizable#readExternal(ObjectInput)
921         * 
922         * @param in The stream from which the LdifEntry is read
923         * @throws IOException If the stream can't be read
924         * @throws ClassNotFoundException If the LdifEntry can't be created 
925         */
926        public void readExternal( ObjectInput in ) throws IOException , ClassNotFoundException
927        {
928            // Read the changeType
929            int type = in.readInt();
930            changeType = ChangeType.getChangeType( type );
931            entry = (ClientEntry)in.readObject();
932            
933            switch ( changeType )
934            {
935                case Add :
936                    // Fallback
937                case Delete :
938                    // we don't have anything to read, but the control
939                    break;
940    
941                case ModDn :
942                    // Fallback
943                case ModRdn :
944                    deleteOldRdn = in.readBoolean();
945                    
946                    if ( in.readBoolean() )
947                    {
948                        newRdn = in.readUTF();
949                    }
950                    
951                    if ( in.readBoolean() )
952                    {
953                        newSuperior = in.readUTF();
954                    }
955                    
956                    break;
957                    
958                case Modify :
959                    // Read the modification
960                    int nbModifs = in.readInt();
961                    
962                    
963                    for ( int i = 0; i < nbModifs; i++ )
964                    {
965                        int operation = in.readInt();
966                        String modStr = in.readUTF();
967                        DefaultClientAttribute value = (DefaultClientAttribute)in.readObject();
968                        
969                        addModificationItem( ModificationOperation.getOperation( operation ), modStr, value );
970                    }
971                    
972                    break;
973            }
974            
975            if ( in.available() > 0 )
976            {
977                // We have a control
978                control = (Control)in.readObject();
979            }
980        }
981    
982    
983        /**
984         * @see Externalizable#readExternal(ObjectInput)<p>
985         *
986         *@param out The stream in which the ChangeLogEvent will be serialized. 
987         *
988         *@throws IOException If the serialization fail
989         */
990        public void writeExternal( ObjectOutput out ) throws IOException
991        {
992            // Write the changeType
993            out.writeInt( changeType.getChangeType() );
994            
995            // Write the entry
996            out.writeObject( entry );
997            
998            // Write the data
999            switch ( changeType )
1000            {
1001                case Add :
1002                    // Fallback
1003                case Delete :
1004                    // we don't have anything to write, but the control
1005                    break;
1006    
1007                case ModDn :
1008                    // Fallback
1009                case ModRdn :
1010                    out.writeBoolean( deleteOldRdn );
1011                    
1012                    if ( newRdn != null )
1013                    {
1014                        out.writeBoolean( true );
1015                        out.writeUTF( newRdn );
1016                    }
1017                    else
1018                    {
1019                        out.writeBoolean( false );
1020                    }
1021                    
1022                    if ( newSuperior != null )
1023                    {
1024                        out.writeBoolean( true );
1025                        out.writeUTF( newSuperior );
1026                    }
1027                    else
1028                    {
1029                        out.writeBoolean( false );
1030                    }
1031                    break;
1032                    
1033                case Modify :
1034                    // Read the modification
1035                    out.writeInt( modificationList.size() );
1036                    
1037                    for ( Modification modification:modificationList )
1038                    {
1039                        out.writeInt( modification.getOperation().getValue() );
1040                        out.writeUTF( modification.getAttribute().getId() );
1041                        
1042                        EntryAttribute attribute = modification.getAttribute();
1043                        out.writeObject( attribute );
1044                    }
1045                    
1046                    break;
1047            }
1048            
1049            if ( control != null )
1050            {
1051                // Write the control
1052                out.writeObject( control );
1053                
1054            }
1055            
1056            // and flush the result
1057            out.flush();
1058        }
1059    }