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