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