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   *  https://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  package org.apache.directory.api.ldap.model.entry;
20  
21  
22  import java.io.IOException;
23  import java.io.ObjectInput;
24  import java.io.ObjectOutput;
25  import java.nio.charset.StandardCharsets;
26  import java.util.ArrayList;
27  import java.util.Base64;
28  import java.util.Collection;
29  import java.util.Collections;
30  import java.util.HashMap;
31  import java.util.Iterator;
32  import java.util.List;
33  import java.util.Map;
34  
35  import org.apache.directory.api.i18n.I18n;
36  import org.apache.directory.api.ldap.model.constants.SchemaConstants;
37  import org.apache.directory.api.ldap.model.exception.LdapException;
38  import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
39  import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
40  import org.apache.directory.api.ldap.model.ldif.LdapLdifException;
41  import org.apache.directory.api.ldap.model.ldif.LdifAttributesReader;
42  import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
43  import org.apache.directory.api.ldap.model.name.Dn;
44  import org.apache.directory.api.ldap.model.schema.AttributeType;
45  import org.apache.directory.api.ldap.model.schema.SchemaManager;
46  import org.apache.directory.api.util.Strings;
47  import org.slf4j.Logger;
48  import org.slf4j.LoggerFactory;
49  
50  
51  /**
52   * A default implementation of a ServerEntry which should suite most
53   * use cases.<br>
54   * <br>
55   * This class is final, it should not be extended.
56   *
57   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
58   */
59  public final class DefaultEntry implements Entry
60  {
61      /** Used for serialization */
62      private static final long serialVersionUID = 2L;
63  
64      /** The logger for this class */
65      private static final Logger LOG = LoggerFactory.getLogger( DefaultEntry.class );
66  
67      /** The Dn for this entry */
68      private Dn dn;
69  
70      /** A map containing all the attributes for this entry */
71      private Map<String, Attribute> attributes = new HashMap<>();
72  
73      /** A speedup to get the ObjectClass attribute */
74      private static AttributeType objectClassAttributeType;
75  
76      /** The SchemaManager */
77      private transient SchemaManager schemaManager;
78  
79      /** The computed hashcode. We don't want to compute it each time the hashcode() method is called */
80      private volatile int h;
81  
82      /** A mutex to manage synchronization*/
83      private static final Object MUTEX = new Object();
84  
85  
86      //-------------------------------------------------------------------------
87      // Constructors
88      //-------------------------------------------------------------------------
89      /**
90       * Creates a new instance of DefaultEntry.
91       * <p>
92       * This entry <b>must</b> be initialized before being used !
93       */
94      public DefaultEntry()
95      {
96          this( ( SchemaManager ) null );
97      }
98  
99  
100     /**
101      * <p>
102      * Creates a new instance of DefaultEntry, schema aware.
103      * </p>
104      * <p>
105      * No attributes will be created.
106      * </p>
107      *
108      * @param schemaManager The reference to the schemaManager
109      */
110     public DefaultEntry( SchemaManager schemaManager )
111     {
112         this.schemaManager = schemaManager;
113         dn = Dn.EMPTY_DN;
114 
115         // Initialize the ObjectClass object
116         if ( schemaManager != null )
117         {
118             initObjectClassAT();
119         }
120     }
121 
122 
123     /**
124      * Creates a new instance of DefaultEntry, with a Dn.
125      *
126      * @param dn The String Dn for this serverEntry. Can be null.
127      * @throws LdapInvalidDnException If the Dn is invalid
128      */
129     public DefaultEntry( String dn ) throws LdapInvalidDnException
130     {
131         this.dn = new Dn( dn );
132     }
133 
134 
135     /**
136      * Creates a new instance of DefaultEntry, with a Dn.
137      *
138      * @param dn The Dn for this serverEntry. Can be null.
139      */
140     public DefaultEntry( Dn dn )
141     {
142         this.dn = dn;
143     }
144 
145 
146     /**
147      * <p>
148      * Creates a new instance of DefaultEntry, schema aware.
149      * </p>
150      * <p>
151      * No attributes will be created.
152      * </p>
153      *
154      * @param schemaManager The reference to the schemaManager
155      * @param dn The String Dn for this serverEntry. Can be null.
156      * @throws LdapInvalidDnException If the Dn is invalid
157      */
158     public DefaultEntry( SchemaManager schemaManager, String dn ) throws LdapInvalidDnException
159     {
160         this.schemaManager = schemaManager;
161 
162         if ( Strings.isEmpty( dn ) )
163         {
164             this.dn = Dn.EMPTY_DN;
165         }
166         else
167         {
168             this.dn = new Dn( schemaManager, dn );
169         }
170 
171         // Initialize the ObjectClass object
172         initObjectClassAT();
173     }
174 
175 
176     /**
177      * <p>
178      * Creates a new instance of DefaultEntry, schema aware.
179      * </p>
180      * <p>
181      * No attributes will be created.
182      * </p>
183      *
184      * @param schemaManager The reference to the schemaManager
185      * @param dn The Dn for this serverEntry. Can be null.
186      */
187     public DefaultEntry( SchemaManager schemaManager, Dn dn )
188     {
189         this.schemaManager = schemaManager;
190 
191         if ( dn == null )
192         {
193             this.dn = Dn.EMPTY_DN;
194         }
195         else
196         {
197             this.dn = normalizeDn( dn );
198         }
199 
200         // Initialize the ObjectClass object
201         initObjectClassAT();
202     }
203 
204 
205     /**
206      * Creates a new instance of DefaultEntry, with a
207      * Dn and a list of IDs.
208      *
209      * @param dn The Dn for this serverEntry. Can be null.
210      * @param elements The list of elements to inject in the entry
211      * @throws LdapException If the elements are invalid
212      * @throws LdapException If the provided Dn or elements are invalid
213      */
214     public DefaultEntry( String dn, Object... elements ) throws LdapException
215     {
216         this( null, dn, elements );
217     }
218 
219 
220     /**
221      * Creates a new instance of DefaultEntry, with a
222      * Dn and a list of IDs.
223      *
224      * @param dn The Dn for this serverEntry. Can be null.
225      * @param elements The list of attributes to create.
226      * @throws LdapException If the provided Dn or elements are invalid
227      */
228     public DefaultEntry( Dn dn, Object... elements ) throws LdapException
229     {
230         this( null, dn, elements );
231     }
232 
233 
234     /**
235      * Creates a new instance of DefaultEntry, with a
236      * Dn and a list of IDs.
237      *
238      * @param schemaManager The SchemaManager
239      * @param dn The Dn for this serverEntry. Can be null.
240      * @param elements The list of attributes to create.
241      * @throws LdapException If the provided Dn or elements are invalid
242      */
243     public DefaultEntry( SchemaManager schemaManager, String dn, Object... elements ) throws LdapException
244     {
245         this( schemaManager, new Dn( schemaManager, dn ), elements );
246     }
247 
248 
249     /**
250      * Creates a new instance of DefaultEntry, with a
251      * Dn and a list of IDs.
252      *
253      * @param schemaManager The reference to the schemaManager
254      * @param dn The Dn for this serverEntry. Can be null.
255      * @param elements The list of attributes to create.
256      * @throws LdapException If the provided Dn or Elements are invalid
257      */
258     public DefaultEntry( SchemaManager schemaManager, Dn dn, Object... elements ) throws LdapException
259     {
260         DefaultEntry entry = ( DefaultEntry ) createEntry( schemaManager, elements );
261 
262         this.dn = dn;
263         this.attributes = entry.attributes;
264         this.schemaManager = schemaManager;
265 
266         if ( schemaManager != null )
267         {
268             if ( !dn.isSchemaAware() )
269             {
270                 this.dn = new Dn( schemaManager, dn );
271             }
272 
273             initObjectClassAT();
274         }
275     }
276 
277 
278     /**
279      * <p>
280      * Creates a new instance of DefaultEntry, copying
281      * another entry.
282      * </p>
283      * <p>
284      * No attributes will be created.
285      * </p>
286      *
287      * @param schemaManager The reference to the schemaManager
288      * @param entry the entry to copy
289      * @throws LdapException If the provided entry is invalid
290      */
291     public DefaultEntry( SchemaManager schemaManager, Entry entry ) throws LdapException
292     {
293         this.schemaManager = schemaManager;
294 
295         // Initialize the ObjectClass object
296         initObjectClassAT();
297 
298         // We will clone the existing entry, because it may be normalized
299         if ( entry.getDn() != null )
300         {
301             dn = normalizeDn( entry.getDn() );
302         }
303         else
304         {
305             dn = Dn.EMPTY_DN;
306         }
307 
308         // Init the attributes map
309         attributes = new HashMap<>( entry.size() );
310 
311         // and copy all the attributes
312         for ( Attribute attribute : entry )
313         {
314             try
315             {
316                 // First get the AttributeType
317                 AttributeType attributeType = attribute.getAttributeType();
318 
319                 if ( attributeType == null )
320                 {
321                     attributeType = schemaManager.lookupAttributeTypeRegistry( attribute.getId() );
322                 }
323 
324                 // Create a new ServerAttribute.
325                 Attribute serverAttribute = new DefaultAttribute( attributeType, attribute );
326 
327                 // And store it
328                 add( serverAttribute );
329             }
330             catch ( Exception ne )
331             {
332                 // Just log a warning
333                 if ( LOG.isWarnEnabled() )
334                 {
335                     LOG.warn( I18n.msg( I18n.MSG_13200_CANT_STORE_ATTRIBUTE, attribute.getId() ) );
336                 }
337 
338                 throw ne;
339             }
340         }
341     }
342 
343 
344     //-------------------------------------------------------------------------
345     // Helper methods
346     //-------------------------------------------------------------------------
347     private Entry createEntry( SchemaManager schemaManager, Object... elements )
348         throws LdapInvalidAttributeValueException, LdapLdifException
349     {
350         StringBuilder sb = new StringBuilder();
351         int pos = 0;
352         boolean valueExpected = false;
353 
354         for ( Object element : elements )
355         {
356             if ( !valueExpected )
357             {
358                 if ( !( element instanceof String ) )
359                 {
360                     throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, I18n.err(
361                         I18n.ERR_13233_ATTRIBUTE_ID_MUST_BE_A_STRING, pos + 1 ) );
362                 }
363 
364                 String attribute = ( String ) element;
365                 sb.append( attribute );
366 
367                 if ( attribute.indexOf( ':' ) != -1 )
368                 {
369                     sb.append( '\n' );
370                 }
371                 else
372                 {
373                     valueExpected = true;
374                 }
375             }
376             else
377             {
378                 if ( element instanceof String )
379                 {
380                     sb.append( ": " ).append( ( String ) element ).append( '\n' );
381                 }
382                 else if ( element instanceof byte[] )
383                 {
384                     sb.append( ":: " );
385                     sb.append( new String( Base64.getEncoder().encode( ( byte[] ) element ), StandardCharsets.UTF_8 ) );
386                     sb.append( '\n' );
387                 }
388                 else
389                 {
390                     throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, I18n.err(
391                         I18n.ERR_13234_ATTRIBUTE_VAL_STRING_OR_BYTE, pos + 1 ) );
392                 }
393 
394                 valueExpected = false;
395             }
396         }
397 
398         if ( valueExpected )
399         {
400             throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, I18n
401                 .err( I18n.ERR_13250_VALUE_MISSING_AT_THE_END ) );
402         }
403 
404         try ( LdifAttributesReader reader = new LdifAttributesReader() )
405         {
406             return reader.parseEntry( schemaManager, sb.toString() );
407         }
408         catch ( IOException e )
409         {
410             throw new LdapLdifException( I18n.err( I18n.ERR_13248_CANNOT_READ_ENTRY ), e );
411         }
412     }
413 
414 
415     /**
416      * Get the trimmed and lower cased entry ID
417      *
418      * @param upId The ID
419      * @return The retrieved ID
420      */
421     private String getId( String upId )
422     {
423         String id = Strings.trim( Strings.toLowerCaseAscii( upId ) );
424 
425         // If empty, throw an error
426         if ( Strings.isEmpty( id ) )
427         {
428             String message = I18n.err( I18n.ERR_13216_AT_ID_NULL );
429             LOG.error( message );
430             throw new IllegalArgumentException( message );
431         }
432 
433         return id;
434     }
435 
436 
437     /**
438      * Get the UpId if it is null.
439      *
440      * @param upId The ID
441      * @param attributeType The AttributeType to retrieve
442      * @return the retrieved ID
443      */
444     private String getUpId( String upId, AttributeType attributeType )
445     {
446         String normUpId = Strings.trim( upId );
447 
448         if ( attributeType == null )
449         {
450             if ( Strings.isEmpty( normUpId ) )
451             {
452                 String message = I18n.err( I18n.ERR_13226_CANNOT_ADD_ATTRIBUTE_NO_ID );
453                 LOG.error( message );
454                 throw new IllegalArgumentException( message );
455             }
456 
457             return upId;
458         }
459         else if ( Strings.isEmpty( normUpId ) )
460         {
461             String id = attributeType.getName();
462 
463             if ( Strings.isEmpty( id ) )
464             {
465                 id = attributeType.getOid();
466             }
467 
468             return id;
469         }
470         else
471         {
472             return upId;
473         }
474     }
475 
476 
477     /**
478      * This method is used to initialize the OBJECT_CLASS_AT attributeType.
479      *
480      * We want to do it only once, so it's a synchronized method. Note that
481      * the alternative would be to call the lookup() every time, but this won't
482      * be very efficient, as it will get the AT from a map, which is also
483      * synchronized, so here, we have a very minimal cost.
484      *
485      * We can't do it once as a static part in the body of this class, because
486      * the access to the registries is mandatory to get back the AttributeType.
487      */
488     private void initObjectClassAT()
489     {
490         if ( schemaManager == null )
491         {
492             return;
493         }
494 
495         try
496         {
497             synchronized ( MUTEX )
498             {
499                 if ( objectClassAttributeType == null )
500                 {
501                     objectClassAttributeType = schemaManager
502                         .lookupAttributeTypeRegistry( SchemaConstants.OBJECT_CLASS_AT );
503                 }
504             }
505         }
506         catch ( LdapException ne )
507         {
508             // do nothing...
509         }
510     }
511 
512 
513     /**
514      * Normalizes the given Dn if it was not already normalized
515      *
516      * @param dn the Dn to be normalized
517      * @return The normalized Dn
518      */
519     private Dn normalizeDn( Dn dn )
520     {
521         if ( !dn.isSchemaAware() )
522         {
523             try
524             {
525                 // The dn must be normalized
526                 return new Dn( schemaManager, dn );
527             }
528             catch ( LdapException ne )
529             {
530                 if ( LOG.isWarnEnabled() )
531                 {
532                     LOG.warn( I18n.msg( I18n.MSG_13201_DN_CANT_BE_NORMALIZED, dn ) );
533                 }
534 
535                 return dn;
536             }
537         }
538         else
539         {
540             return dn;
541         }
542     }
543 
544 
545     /**
546      * A helper method to recompute the hash code
547      */
548     private void rehash()
549     {
550         int hTmp = 37;
551         h = hTmp * 17 + dn.hashCode();
552     }
553 
554 
555     /**
556      * Add a new EntryAttribute, with its upId. If the upId is null,
557      * default to the AttributeType name.
558      *
559      * Updates the AttributeMap.
560      *
561      * @param upId The user provided ID for the attribute to create
562      * @param attributeType The AttributeType to use
563      * @param values The values to add to this attribute
564      * @throws LdapInvalidAttributeValueException If one of the value is incorrect
565      */
566     protected void createAttribute( String upId, AttributeType attributeType, byte[]... values )
567         throws LdapInvalidAttributeValueException
568     {
569         Attribute attribute = new DefaultAttribute( attributeType, values );
570         attribute.setUpId( upId, attributeType );
571         attributes.put( attributeType.getOid(), attribute );
572     }
573 
574 
575     /**
576      * Add a new EntryAttribute, with its upId. If the upId is null,
577      * default to the AttributeType name.
578      *
579      * Updates the AttributeMap.
580      *
581      * @param upId The user provided ID for the attribute to create
582      * @param attributeType The AttributeType to use
583      * @param values The values to add to this attribute
584      * @throws LdapInvalidAttributeValueException If one of the value is incorrect
585      */
586     protected void createAttribute( String upId, AttributeType attributeType, String... values )
587         throws LdapInvalidAttributeValueException
588     {
589         Attribute attribute = new DefaultAttribute( attributeType, values );
590         attribute.setUpId( upId, attributeType );
591         attributes.put( attributeType.getOid(), attribute );
592     }
593 
594 
595     /**
596      * Add a new EntryAttribute, with its upId. If the upId is null,
597      * default to the AttributeType name.
598      *
599      * Updates the AttributeMap.
600      *
601      * @param upId The user provided ID for the attribute to create
602      * @param attributeType The AttributeType to use
603      * @param values The values to add to this attribute
604      * @throws LdapInvalidAttributeValueException If one of the value is incorrect
605      */
606     protected void createAttribute( String upId, AttributeType attributeType, Value... values )
607         throws LdapInvalidAttributeValueException
608     {
609         Attribute attribute = new DefaultAttribute( attributeType, values );
610         attribute.setUpId( upId, attributeType );
611         attributes.put( attributeType.getOid(), attribute );
612     }
613 
614 
615     /**
616      * Returns the attributeType from an Attribute ID.
617      *
618      * @param upId The ID we are looking for
619      * @return The found attributeType
620      * @throws LdapException If the lookup failed
621      */
622     protected AttributeType getAttributeType( String upId ) throws LdapException
623     {
624         if ( Strings.isEmpty( Strings.trim( upId ) ) )
625         {
626             String message = I18n.err( I18n.ERR_13204_NULL_ATTRIBUTE_ID );
627             LOG.error( message );
628             throw new IllegalArgumentException( message );
629         }
630 
631         return schemaManager.lookupAttributeTypeRegistry( upId );
632     }
633 
634 
635     //-------------------------------------------------------------------------
636     // Entry methods
637     //-------------------------------------------------------------------------
638     /**
639      * {@inheritDoc}
640      */
641     @Override
642     public Entry add( AttributeType attributeType, byte[]... values ) throws LdapException
643     {
644         if ( attributeType == null )
645         {
646             String message = I18n.err( I18n.ERR_13203_ATTRIBUTE_TYPE_NULL_NOT_ALLOWED );
647             LOG.error( message );
648             throw new IllegalArgumentException( message );
649         }
650 
651         if ( ( values == null ) || ( values.length == 0 ) )
652         {
653             String message = I18n.err( I18n.ERR_13232_NO_VALUE_NOT_ALLOWED );
654             LOG.error( message );
655             throw new IllegalArgumentException( message );
656         }
657 
658         // ObjectClass with binary values are not allowed
659         if ( attributeType.equals( objectClassAttributeType ) )
660         {
661             String message = I18n.err( I18n.ERR_13227_NON_STRING_VALUE_NOT_ALLOWED );
662             LOG.error( message );
663             throw new UnsupportedOperationException( message );
664         }
665 
666         Attribute attribute = attributes.get( attributeType.getOid() );
667 
668         if ( attribute != null )
669         {
670             // This Attribute already exist, we add the values
671             // into it
672             attribute.add( values );
673         }
674         else
675         {
676             // We have to create a new Attribute and set the values.
677             // The upId, which is set to null, will be setup by the
678             // createAttribute method
679             createAttribute( null, attributeType, values );
680         }
681 
682         return this;
683     }
684 
685 
686     /**
687      * {@inheritDoc}
688      */
689     @Override
690     public Entry add( AttributeType attributeType, String... values ) throws LdapException
691     {
692         if ( attributeType == null )
693         {
694             String message = I18n.err( I18n.ERR_13203_ATTRIBUTE_TYPE_NULL_NOT_ALLOWED );
695             LOG.error( message );
696             throw new IllegalArgumentException( message );
697         }
698 
699         Attribute attribute = attributes.get( attributeType.getOid() );
700 
701         if ( attribute != null )
702         {
703             // This Attribute already exist, we add the values
704             // into it
705             attribute.add( values );
706         }
707         else
708         {
709             // We have to create a new Attribute and set the values.
710             // The upId, which is set to null, will be setup by the
711             // createAttribute method
712             createAttribute( null, attributeType, values );
713         }
714 
715         return this;
716     }
717 
718 
719     /**
720      * {@inheritDoc}
721      */
722     @Override
723     public Entry add( AttributeType attributeType, Value... values ) throws LdapException
724     {
725         if ( attributeType == null )
726         {
727             String message = I18n.err( I18n.ERR_13203_ATTRIBUTE_TYPE_NULL_NOT_ALLOWED );
728             LOG.error( message );
729             throw new IllegalArgumentException( message );
730         }
731 
732         Attribute attribute = attributes.get( attributeType.getOid() );
733 
734         if ( attribute != null )
735         {
736             // This Attribute already exist, we add the values
737             // into it
738             attribute.add( values );
739         }
740         else
741         {
742             // We have to create a new Attribute and set the values.
743             // The upId, which is set to null, will be setup by the
744             // createAttribute method
745             createAttribute( null, attributeType, values );
746         }
747 
748         return this;
749     }
750 
751 
752     /**
753      * {@inheritDoc}
754      */
755     @Override
756     public Entry add( String upId, AttributeType attributeType, byte[]... values ) throws LdapException
757     {
758         // ObjectClass with binary values are not allowed
759         if ( attributeType.equals( objectClassAttributeType ) )
760         {
761             String message = I18n.err( I18n.ERR_13227_NON_STRING_VALUE_NOT_ALLOWED );
762             LOG.error( message );
763             throw new UnsupportedOperationException( message );
764         }
765 
766         Attribute attribute = attributes.get( attributeType.getOid() );
767 
768         String id = getUpId( upId, attributeType );
769 
770         if ( attribute != null )
771         {
772             // This Attribute already exist, we add the values
773             // into it
774             attribute.add( values );
775             attribute.setUpId( id, attributeType );
776         }
777         else
778         {
779             // We have to create a new Attribute and set the values
780             // and the upId
781             createAttribute( id, attributeType, values );
782         }
783 
784         return this;
785     }
786 
787 
788     /**
789      * {@inheritDoc}
790      */
791     @Override
792     public Entry add( String upId, AttributeType attributeType, Value... values ) throws LdapException
793     {
794         if ( attributeType == null )
795         {
796             String message = I18n.err( I18n.ERR_13203_ATTRIBUTE_TYPE_NULL_NOT_ALLOWED );
797             LOG.error( message );
798             throw new IllegalArgumentException( message );
799         }
800 
801         String id = getUpId( upId, attributeType );
802 
803         Attribute attribute = attributes.get( attributeType.getOid() );
804 
805         if ( attribute != null )
806         {
807             // This Attribute already exist, we add the values
808             // into it
809             attribute.add( values );
810             attribute.setUpId( id, attributeType );
811         }
812         else
813         {
814             createAttribute( id, attributeType, values );
815         }
816 
817         return this;
818     }
819 
820 
821     /**
822      * {@inheritDoc}
823      */
824     @Override
825     public Entry add( String upId, AttributeType attributeType, String... values ) throws LdapException
826     {
827         if ( attributeType == null )
828         {
829             String message = I18n.err( I18n.ERR_13203_ATTRIBUTE_TYPE_NULL_NOT_ALLOWED );
830             LOG.error( message );
831             throw new IllegalArgumentException( message );
832         }
833 
834         String id = getUpId( upId, attributeType );
835 
836         Attribute attribute = attributes.get( attributeType.getOid() );
837 
838         if ( attribute != null )
839         {
840             // This Attribute already exist, we add the values
841             // into it
842             attribute.add( values );
843             attribute.setUpId( id, attributeType );
844         }
845         else
846         {
847             // We have to create a new Attribute and set the values
848             // and the upId
849             createAttribute( id, attributeType, values );
850         }
851 
852         return this;
853     }
854 
855 
856     /**
857      * {@inheritDoc}
858      */
859     @Override
860     public Entry add( Attribute... attributes ) throws LdapException
861     {
862         // Loop on all the added attributes
863         for ( Attribute attribute : attributes )
864         {
865             AttributeType attributeType = attribute.getAttributeType();
866 
867             if ( attributeType != null )
868             {
869                 String oid = attributeType.getOid();
870 
871                 if ( this.attributes.containsKey( oid ) )
872                 {
873                     // We already have an attribute with the same AttributeType
874                     // Just add the new values into it.
875                     Attribute existingAttribute = this.attributes.get( oid );
876 
877                     for ( Value value : attribute )
878                     {
879                         existingAttribute.add( value );
880                     }
881 
882                     // And update the upId
883                     existingAttribute.setUpId( attribute.getUpId() );
884                 }
885                 else
886                 {
887                     // The attributeType does not exist, add it
888                     this.attributes.put( oid, attribute );
889                 }
890             }
891             else
892             {
893                 // If the attribute already exist, we will add the new values.
894                 if ( contains( attribute ) )
895                 {
896                     Attribute existingAttribute = get( attribute.getId() );
897 
898                     // Loop on all the values, and add them to the existing attribute
899                     for ( Value value : attribute )
900                     {
901                         existingAttribute.add( value );
902                     }
903                 }
904                 else
905                 {
906                     // Stores the attribute into the entry
907                     this.attributes.put( attribute.getId(), attribute );
908                 }
909             }
910         }
911 
912         return this;
913     }
914 
915 
916     /**
917      * {@inheritDoc}
918      */
919     @Override
920     public Entry add( String upId, byte[]... values ) throws LdapException
921     {
922         if ( Strings.isEmpty( upId ) )
923         {
924             String message = I18n.err( I18n.ERR_13204_NULL_ATTRIBUTE_ID );
925             LOG.error( message );
926             throw new IllegalArgumentException( message );
927         }
928 
929         // First, transform the upID to a valid ID
930         String id = getId( upId );
931 
932         if ( schemaManager != null )
933         {
934             add( upId, schemaManager.lookupAttributeTypeRegistry( id ), values );
935         }
936         else
937         {
938             // Now, check to see if we already have such an attribute
939             Attribute attribute = attributes.get( id );
940 
941             if ( attribute != null )
942             {
943                 // This Attribute already exist, we add the values
944                 // into it. (If the values already exists, they will
945                 // not be added, but this is done in the add() method)
946                 attribute.add( values );
947                 attribute.setUpId( upId );
948             }
949             else
950             {
951                 // We have to create a new Attribute and set the values
952                 // and the upId
953                 attributes.put( id, new DefaultAttribute( upId, values ) );
954             }
955         }
956 
957         return this;
958     }
959 
960 
961     /**
962      * {@inheritDoc}
963      */
964     @Override
965     public Entry add( String upId, String... values ) throws LdapException
966     {
967         if ( Strings.isEmpty( upId ) )
968         {
969             String message = I18n.err( I18n.ERR_13204_NULL_ATTRIBUTE_ID );
970             LOG.error( message );
971             throw new IllegalArgumentException( message );
972         }
973 
974         // First, transform the upID to a valid ID
975         String id = getId( upId );
976 
977         if ( schemaManager != null )
978         {
979             add( upId, schemaManager.lookupAttributeTypeRegistry( upId ), values );
980         }
981         else
982         {
983             // Now, check to see if we already have such an attribute
984             Attribute attribute = attributes.get( id );
985 
986             if ( attribute != null )
987             {
988                 // This Attribute already exist, we add the values
989                 // into it. (If the values already exists, they will
990                 // not be added, but this is done in the add() method)
991                 attribute.add( values );
992                 attribute.setUpId( upId );
993             }
994             else
995             {
996                 // We have to create a new Attribute and set the values
997                 // and the upId
998                 attributes.put( id, new DefaultAttribute( upId, values ) );
999             }
1000         }
1001 
1002         return this;
1003     }
1004 
1005 
1006     /**
1007      * {@inheritDoc}
1008      */
1009     @Override
1010     public Entry add( String upId, Value... values ) throws LdapException
1011     {
1012         if ( Strings.isEmpty( upId ) )
1013         {
1014             String message = I18n.err( I18n.ERR_13204_NULL_ATTRIBUTE_ID );
1015             LOG.error( message );
1016             throw new IllegalArgumentException( message );
1017         }
1018 
1019         // First, transform the upID to a valid ID
1020         String id = getId( upId );
1021 
1022         if ( schemaManager != null )
1023         {
1024             add( upId, schemaManager.lookupAttributeTypeRegistry( upId ), values );
1025         }
1026         else
1027         {
1028             // Now, check to see if we already have such an attribute
1029             Attribute attribute = attributes.get( id );
1030 
1031             if ( attribute != null )
1032             {
1033                 // This Attribute already exist, we add the values
1034                 // into it. (If the values already exists, they will
1035                 // not be added, but this is done in the add() method)
1036                 attribute.add( values );
1037                 attribute.setUpId( upId );
1038             }
1039             else
1040             {
1041                 // We have to create a new Attribute and set the values
1042                 // and the upId
1043                 attributes.put( id, new DefaultAttribute( upId, values ) );
1044             }
1045         }
1046 
1047         return this;
1048     }
1049 
1050 
1051     /**
1052      * Clone an entry. All the element are duplicated, so a modification on
1053      * the original object won't affect the cloned object, as a modification
1054      * on the cloned object has no impact on the original object
1055      */
1056     @Override
1057     public Entry clone()
1058     {
1059         // First, clone the structure
1060         DefaultEntry clone = ( DefaultEntry ) shallowClone();
1061 
1062         // now clone all the attributes
1063         clone.attributes.clear();
1064 
1065         if ( schemaManager != null )
1066         {
1067             for ( Attribute attribute : attributes.values() )
1068             {
1069                 String oid = attribute.getAttributeType().getOid();
1070                 clone.attributes.put( oid, attribute.clone() );
1071             }
1072         }
1073         else
1074         {
1075             for ( Attribute attribute : attributes.values() )
1076             {
1077                 clone.attributes.put( attribute.getId(), attribute.clone() );
1078             }
1079 
1080         }
1081 
1082         // We are done !
1083         return clone;
1084 
1085         /*
1086         // First, clone the structure
1087         //DefaultEntry clone = ( DefaultEntry ) shallowClone();
1088         try
1089         {
1090             DefaultEntry clone = ( DefaultEntry ) super.clone();
1091             clone.attributes = new HashMap<>( attributes.size() );
1092 
1093             // now clone all the attributes
1094             //clone.attributes.clear();
1095 
1096             if ( schemaManager != null )
1097             {
1098                 for ( Attribute attribute : attributes.values() )
1099                 {
1100                     String oid = attribute.getAttributeType().getOid();
1101                     clone.attributes.put( oid, attribute.clone() );
1102                 }
1103             }
1104             else
1105             {
1106                 for ( Attribute attribute : attributes.values() )
1107                 {
1108                     clone.attributes.put( attribute.getId(), attribute.clone() );
1109                 }
1110             }
1111 
1112             // We are done !
1113             return clone;
1114         }
1115         catch ( CloneNotSupportedException cnse )
1116         {
1117             return this;
1118         }
1119         */
1120     }
1121 
1122 
1123     /**
1124      * Shallow clone an entry. We don't clone the Attributes
1125      */
1126     @SuppressWarnings("unchecked")
1127     @Override
1128     public Entry shallowClone()
1129     {
1130         try
1131         {
1132             // First, clone the structure
1133             DefaultEntry clone = ( DefaultEntry ) super.clone();
1134 
1135             // An Entry has a Dn and many attributes.
1136             // note that Dn is immutable now
1137             clone.dn = dn;
1138 
1139             // then clone the ClientAttribute Map.
1140             clone.attributes = ( Map<String, Attribute> ) ( ( ( HashMap<String, Attribute> ) attributes )
1141                 .clone() );
1142 
1143             // We are done !
1144             return clone;
1145         }
1146         catch ( CloneNotSupportedException cnse )
1147         {
1148             return null;
1149         }
1150     }
1151 
1152 
1153     /**
1154      * {@inheritDoc}
1155      */
1156     @Override
1157     public boolean contains( Attribute... attributes )
1158     {
1159         if ( schemaManager == null )
1160         {
1161             for ( Attribute attribute : attributes )
1162             {
1163                 if ( attribute == null )
1164                 {
1165                     return this.attributes.size() == 0;
1166                 }
1167 
1168                 if ( !this.attributes.containsKey( attribute.getId() ) )
1169                 {
1170                     return false;
1171                 }
1172             }
1173         }
1174         else
1175         {
1176             for ( Attribute entryAttribute : attributes )
1177             {
1178                 if ( entryAttribute == null )
1179                 {
1180                     return this.attributes.size() == 0;
1181                 }
1182 
1183                 AttributeType attributeType = entryAttribute.getAttributeType();
1184 
1185                 if ( ( attributeType == null ) || !this.attributes.containsKey( attributeType.getOid() ) )
1186                 {
1187                     return false;
1188                 }
1189             }
1190         }
1191 
1192         return true;
1193     }
1194 
1195 
1196     /**
1197      * {@inheritDoc}
1198      */
1199     @Override
1200     public boolean containsAttribute( String... attributes )
1201     {
1202         if ( schemaManager == null )
1203         {
1204             for ( String attribute : attributes )
1205             {
1206                 String id = getId( attribute );
1207 
1208                 if ( !this.attributes.containsKey( id ) )
1209                 {
1210                     return false;
1211                 }
1212             }
1213 
1214             return true;
1215         }
1216         else
1217         {
1218             for ( String attribute : attributes )
1219             {
1220                 try
1221                 {
1222                     if ( !containsAttribute( schemaManager.lookupAttributeTypeRegistry( attribute ) ) )
1223                     {
1224                         return false;
1225                     }
1226                 }
1227                 catch ( LdapException ne )
1228                 {
1229                     return false;
1230                 }
1231             }
1232 
1233             return true;
1234         }
1235     }
1236 
1237 
1238     /**
1239      * {@inheritDoc}
1240      */
1241     @Override
1242     public boolean containsAttribute( AttributeType attributeType )
1243     {
1244         if ( attributeType == null )
1245         {
1246             return false;
1247         }
1248 
1249         return attributes.containsKey( attributeType.getOid() );
1250     }
1251 
1252 
1253     /**
1254      * {@inheritDoc}
1255      */
1256     @Override
1257     public boolean contains( AttributeType attributeType, byte[]... values )
1258     {
1259         if ( attributeType == null )
1260         {
1261             return false;
1262         }
1263 
1264         Attribute attribute = attributes.get( attributeType.getOid() );
1265 
1266         if ( attribute != null )
1267         {
1268             return attribute.contains( values );
1269         }
1270         else
1271         {
1272             return false;
1273         }
1274     }
1275 
1276 
1277     /**
1278      * {@inheritDoc}
1279      */
1280     @Override
1281     public boolean contains( AttributeType attributeType, String... values )
1282     {
1283         if ( attributeType == null )
1284         {
1285             return false;
1286         }
1287 
1288         Attribute attribute = attributes.get( attributeType.getOid() );
1289 
1290         if ( attribute != null )
1291         {
1292             return attribute.contains( values );
1293         }
1294         else
1295         {
1296             return false;
1297         }
1298     }
1299 
1300 
1301     /**
1302      * {@inheritDoc}
1303      */
1304     @Override
1305     public boolean contains( AttributeType attributeType, Value... values )
1306     {
1307         if ( attributeType == null )
1308         {
1309             return false;
1310         }
1311 
1312         Attribute attribute = attributes.get( attributeType.getOid() );
1313 
1314         if ( attribute != null )
1315         {
1316             return attribute.contains( values );
1317         }
1318         else
1319         {
1320             return false;
1321         }
1322     }
1323 
1324 
1325     /**
1326      * {@inheritDoc}
1327      */
1328     @Override
1329     public boolean contains( String upId, byte[]... values )
1330     {
1331         if ( Strings.isEmpty( upId ) )
1332         {
1333             return false;
1334         }
1335 
1336         String id = getId( upId );
1337 
1338         if ( schemaManager != null )
1339         {
1340             try
1341             {
1342                 return contains( schemaManager.lookupAttributeTypeRegistry( id ), values );
1343             }
1344             catch ( LdapException le )
1345             {
1346                 return false;
1347             }
1348         }
1349 
1350         Attribute attribute = attributes.get( id );
1351 
1352         if ( attribute == null )
1353         {
1354             return false;
1355         }
1356 
1357         return attribute.contains( values );
1358     }
1359 
1360 
1361     /**
1362      * {@inheritDoc}
1363      */
1364     @Override
1365     public boolean contains( String upId, String... values )
1366     {
1367         if ( Strings.isEmpty( upId ) )
1368         {
1369             return false;
1370         }
1371 
1372         String id = getId( upId );
1373 
1374         if ( schemaManager != null )
1375         {
1376             try
1377             {
1378                 return contains( schemaManager.lookupAttributeTypeRegistry( id ), values );
1379             }
1380             catch ( LdapException le )
1381             {
1382                 return false;
1383             }
1384         }
1385 
1386         Attribute attribute = attributes.get( id );
1387 
1388         if ( attribute == null )
1389         {
1390             return false;
1391         }
1392 
1393         return attribute.contains( values );
1394     }
1395 
1396 
1397     /**
1398      * {@inheritDoc}
1399      */
1400     @Override
1401     public boolean contains( String upId, Value... values )
1402     {
1403         if ( Strings.isEmpty( upId ) )
1404         {
1405             return false;
1406         }
1407 
1408         String id = getId( upId );
1409 
1410         if ( schemaManager != null )
1411         {
1412             try
1413             {
1414                 return contains( schemaManager.lookupAttributeTypeRegistry( id ), values );
1415             }
1416             catch ( LdapException le )
1417             {
1418                 return false;
1419             }
1420         }
1421 
1422         Attribute attribute = attributes.get( id );
1423 
1424         if ( attribute == null )
1425         {
1426             return false;
1427         }
1428 
1429         return attribute.contains( values );
1430     }
1431 
1432 
1433     /**
1434      * {@inheritDoc}
1435      */
1436     @Override
1437     public Attribute get( String alias )
1438     {
1439         try
1440         {
1441             String id = getId( alias );
1442 
1443             if ( schemaManager != null )
1444             {
1445                 try
1446                 {
1447                     AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( id );
1448 
1449                     return attributes.get( attributeType.getOid() );
1450                 }
1451                 catch ( LdapException ne )
1452                 {
1453                     String message = ne.getLocalizedMessage();
1454                     LOG.error( message );
1455                     return null;
1456                 }
1457             }
1458             else
1459             {
1460                 return attributes.get( id );
1461             }
1462         }
1463         catch ( IllegalArgumentException iea )
1464         {
1465             LOG.error( I18n.err( I18n.ERR_13217_FAILED_LOOKUP_AT, alias ) );
1466             return null;
1467         }
1468     }
1469 
1470 
1471     /**
1472      * {@inheritDoc}
1473      */
1474     @Override
1475     public Attribute get( AttributeType attributeType )
1476     {
1477         if ( attributeType != null )
1478         {
1479             return attributes.get( attributeType.getOid() );
1480         }
1481         else
1482         {
1483             return null;
1484         }
1485     }
1486 
1487 
1488     /**
1489      * {@inheritDoc}
1490      */
1491     @Override
1492     public Collection<Attribute> getAttributes()
1493     {
1494         return Collections.unmodifiableMap( attributes ).values();
1495     }
1496 
1497 
1498     /**
1499      * {@inheritDoc}
1500      */
1501     @Override
1502     public Attribute put( String upId, byte[]... values )
1503     {
1504         if ( Strings.isEmpty( upId ) )
1505         {
1506             String message = I18n.err( I18n.ERR_13204_NULL_ATTRIBUTE_ID );
1507             LOG.error( message );
1508             throw new IllegalArgumentException( message );
1509         }
1510 
1511         if ( schemaManager == null )
1512         {
1513             // Get the normalized form of the ID
1514             String id = getId( upId );
1515 
1516             // Create a new attribute
1517             Attribute clientAttribute = new DefaultAttribute( upId, values );
1518 
1519             // Replace the previous one, and return it back
1520             return attributes.put( id, clientAttribute );
1521         }
1522         else
1523         {
1524             try
1525             {
1526                 return put( upId, getAttributeType( upId ), values );
1527             }
1528             catch ( LdapException ne )
1529             {
1530                 String message = I18n.err( I18n.ERR_13212_ERROR_ADDING_VALUE, upId, ne.getLocalizedMessage() );
1531                 LOG.error( message );
1532                 throw new IllegalArgumentException( message, ne );
1533             }
1534         }
1535     }
1536 
1537 
1538     /**
1539      * {@inheritDoc}
1540      */
1541     @Override
1542     public Attribute put( String upId, String... values )
1543     {
1544         if ( Strings.isEmpty( upId ) )
1545         {
1546             String message = I18n.err( I18n.ERR_13204_NULL_ATTRIBUTE_ID );
1547             LOG.error( message );
1548             throw new IllegalArgumentException( message );
1549         }
1550 
1551         if ( schemaManager == null )
1552         {
1553             // Get the normalized form of the ID
1554             String id = getId( upId );
1555 
1556             // Create a new attribute
1557             Attribute clientAttribute = new DefaultAttribute( upId, values );
1558 
1559             // Replace the previous one, and return it back
1560             return attributes.put( id, clientAttribute );
1561         }
1562         else
1563         {
1564             try
1565             {
1566                 return put( upId, getAttributeType( upId ), values );
1567             }
1568             catch ( LdapException ne )
1569             {
1570                 String message = I18n.err( I18n.ERR_13212_ERROR_ADDING_VALUE, upId, ne.getLocalizedMessage() );
1571                 LOG.error( message );
1572                 throw new IllegalArgumentException( message, ne );
1573             }
1574         }
1575     }
1576 
1577 
1578     /**
1579      * {@inheritDoc}
1580      */
1581     @Override
1582     public Attribute put( String upId, Value... values )
1583     {
1584         if ( Strings.isEmpty( upId ) )
1585         {
1586             String message = I18n.err( I18n.ERR_13204_NULL_ATTRIBUTE_ID );
1587             LOG.error( message );
1588             throw new IllegalArgumentException( message );
1589         }
1590 
1591         if ( schemaManager == null )
1592         {
1593             // Get the normalized form of the ID
1594             String id = getId( upId );
1595 
1596             // Create a new attribute
1597             Attribute clientAttribute = new DefaultAttribute( upId, values );
1598 
1599             // Replace the previous one, and return it back
1600             return attributes.put( id, clientAttribute );
1601         }
1602         else
1603         {
1604             try
1605             {
1606                 return put( upId, getAttributeType( upId ), values );
1607             }
1608             catch ( LdapException ne )
1609             {
1610                 String message = I18n.err( I18n.ERR_13212_ERROR_ADDING_VALUE, upId, ne.getLocalizedMessage() );
1611                 LOG.error( message );
1612                 throw new IllegalArgumentException( message, ne );
1613             }
1614         }
1615     }
1616 
1617 
1618     /**
1619      * {@inheritDoc}
1620      */
1621     @Override
1622     public List<Attribute> put( Attribute... attributes ) throws LdapException
1623     {
1624         // First, get the existing attributes
1625         List<Attribute> previous = new ArrayList<>();
1626 
1627         if ( schemaManager == null )
1628         {
1629             for ( Attribute attribute : attributes )
1630             {
1631                 String id = attribute.getId();
1632 
1633                 if ( containsAttribute( id ) )
1634                 {
1635                     // Store the attribute and remove it from the list
1636                     previous.add( get( id ) );
1637                     this.attributes.remove( id );
1638                 }
1639 
1640                 // add the new one
1641                 this.attributes.put( id, attribute );
1642             }
1643         }
1644         else
1645         {
1646             for ( Attribute attribute : attributes )
1647             {
1648                 if ( attribute == null )
1649                 {
1650                     String message = I18n.err( I18n.ERR_13228_AT_LIST_NULL_ELEMENTS );
1651                     LOG.error( message );
1652                     throw new IllegalArgumentException( message );
1653                 }
1654 
1655                 if ( attribute.getAttributeType() == null )
1656                 {
1657                     AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( attribute.getId() );
1658                     attribute.apply( attributeType );
1659                 }
1660 
1661                 Attribute removed = this.attributes.put( attribute.getAttributeType().getOid(), attribute );
1662 
1663                 if ( removed != null )
1664                 {
1665                     previous.add( removed );
1666                 }
1667             }
1668         }
1669 
1670         // return the previous attributes
1671         return previous;
1672     }
1673 
1674 
1675     /**
1676      * {@inheritDoc}
1677      */
1678     @Override
1679     public Attribute put( AttributeType attributeType, byte[]... values ) throws LdapException
1680     {
1681         return put( null, attributeType, values );
1682     }
1683 
1684 
1685     /**
1686      * {@inheritDoc}
1687      */
1688     @Override
1689     public Attribute put( AttributeType attributeType, String... values ) throws LdapException
1690     {
1691         return put( null, attributeType, values );
1692     }
1693 
1694 
1695     /**
1696      * {@inheritDoc}
1697      */
1698     @Override
1699     public Attribute put( AttributeType attributeType, Value... values ) throws LdapException
1700     {
1701         return put( null, attributeType, values );
1702     }
1703 
1704 
1705     /**
1706      * {@inheritDoc}
1707      */
1708     @Override
1709     public Attribute put( String upId, AttributeType attributeType, byte[]... values ) throws LdapException
1710     {
1711         if ( attributeType == null )
1712         {
1713             try
1714             {
1715                 attributeType = getAttributeType( upId );
1716             }
1717             catch ( Exception e )
1718             {
1719                 String message = I18n.err( I18n.ERR_13231_NO_VALID_AT_FOR_THIS_ID );
1720                 LOG.error( message );
1721                 throw new IllegalArgumentException( message, e );
1722             }
1723         }
1724         else
1725         {
1726             if ( !Strings.isEmpty( upId ) )
1727             {
1728                 AttributeType tempAT = getAttributeType( upId );
1729 
1730                 if ( !tempAT.equals( attributeType ) )
1731                 {
1732                     String message = I18n.err( I18n.ERR_13229_ID_INCOMPATIBLE_WITH_AT, upId, attributeType );
1733                     LOG.error( message );
1734                     throw new IllegalArgumentException( message );
1735                 }
1736             }
1737             else
1738             {
1739                 upId = getUpId( upId, attributeType );
1740             }
1741         }
1742 
1743         if ( attributeType.equals( objectClassAttributeType ) )
1744         {
1745             String message = I18n.err( I18n.ERR_13227_NON_STRING_VALUE_NOT_ALLOWED );
1746             LOG.error( message );
1747             throw new UnsupportedOperationException( message );
1748         }
1749 
1750         Attribute attribute = new DefaultAttribute( upId, attributeType, values );
1751 
1752         return attributes.put( attributeType.getOid(), attribute );
1753     }
1754 
1755 
1756     /**
1757      * {@inheritDoc}
1758      */
1759     @Override
1760     public Attribute put( String upId, AttributeType attributeType, String... values ) throws LdapException
1761     {
1762         if ( attributeType == null )
1763         {
1764             try
1765             {
1766                 attributeType = getAttributeType( upId );
1767             }
1768             catch ( Exception e )
1769             {
1770                 String message = I18n.err( I18n.ERR_13231_NO_VALID_AT_FOR_THIS_ID );
1771                 LOG.error( message );
1772                 throw new IllegalArgumentException( message, e );
1773             }
1774         }
1775         else
1776         {
1777             if ( !Strings.isEmpty( upId ) )
1778             {
1779                 AttributeType tempAT = getAttributeType( upId );
1780 
1781                 if ( !tempAT.equals( attributeType ) )
1782                 {
1783                     String message = I18n.err( I18n.ERR_13229_ID_INCOMPATIBLE_WITH_AT, upId, attributeType );
1784                     LOG.error( message );
1785                     throw new IllegalArgumentException( message );
1786                 }
1787             }
1788             else
1789             {
1790                 upId = getUpId( upId, attributeType );
1791             }
1792         }
1793 
1794         Attribute attribute = new DefaultAttribute( upId, attributeType, values );
1795 
1796         return attributes.put( attributeType.getOid(), attribute );
1797     }
1798 
1799 
1800     /**
1801      * {@inheritDoc}
1802      */
1803     @Override
1804     public Attribute put( String upId, AttributeType attributeType, Value... values ) throws LdapException
1805     {
1806         if ( attributeType == null )
1807         {
1808             try
1809             {
1810                 attributeType = getAttributeType( upId );
1811             }
1812             catch ( Exception e )
1813             {
1814                 String message = I18n.err( I18n.ERR_13231_NO_VALID_AT_FOR_THIS_ID );
1815                 LOG.error( message );
1816                 throw new IllegalArgumentException( message, e );
1817             }
1818         }
1819         else
1820         {
1821             if ( !Strings.isEmpty( upId ) )
1822             {
1823                 AttributeType tempAT = getAttributeType( upId );
1824 
1825                 if ( !tempAT.equals( attributeType ) )
1826                 {
1827                     String message = I18n.err( I18n.ERR_13229_ID_INCOMPATIBLE_WITH_AT, upId, attributeType );
1828                     LOG.error( message );
1829                     throw new IllegalArgumentException( message );
1830                 }
1831             }
1832             else
1833             {
1834                 upId = getUpId( upId, attributeType );
1835             }
1836         }
1837 
1838         Attribute attribute = new DefaultAttribute( upId, attributeType, values );
1839 
1840         return attributes.put( attributeType.getOid(), attribute );
1841     }
1842 
1843 
1844     /**
1845      * {@inheritDoc}
1846      */
1847     @Override
1848     public List<Attribute> remove( Attribute... attributes ) throws LdapException
1849     {
1850         List<Attribute> removedAttributes = new ArrayList<>();
1851 
1852         if ( schemaManager == null )
1853         {
1854             for ( Attribute attribute : attributes )
1855             {
1856                 if ( containsAttribute( attribute.getId() ) )
1857                 {
1858                     this.attributes.remove( attribute.getId() );
1859                     removedAttributes.add( attribute );
1860                 }
1861             }
1862         }
1863         else
1864         {
1865             for ( Attribute attribute : attributes )
1866             {
1867                 AttributeType attributeType = attribute.getAttributeType();
1868 
1869                 if ( attributeType == null )
1870                 {
1871                     String message = I18n.err( I18n.ERR_13203_ATTRIBUTE_TYPE_NULL_NOT_ALLOWED );
1872                     LOG.error( message );
1873                     throw new IllegalArgumentException( message );
1874                 }
1875 
1876                 if ( this.attributes.containsKey( attributeType.getOid() ) )
1877                 {
1878                     this.attributes.remove( attributeType.getOid() );
1879                     removedAttributes.add( attribute );
1880                 }
1881             }
1882         }
1883 
1884         return removedAttributes;
1885     }
1886 
1887 
1888     /**
1889      * {@inheritDoc}
1890      */
1891     @Override
1892     public boolean remove( AttributeType attributeType, byte[]... values ) throws LdapException
1893     {
1894         if ( attributeType == null )
1895         {
1896             return false;
1897         }
1898 
1899         try
1900         {
1901             Attribute attribute = attributes.get( attributeType.getOid() );
1902 
1903             if ( attribute == null )
1904             {
1905                 // Can't remove values from a not existing attribute !
1906                 return false;
1907             }
1908 
1909             int nbOldValues = attribute.size();
1910 
1911             // Remove the values
1912             attribute.remove( values );
1913 
1914             if ( attribute.size() == 0 )
1915             {
1916                 // No mare values, remove the attribute
1917                 attributes.remove( attributeType.getOid() );
1918 
1919                 return true;
1920             }
1921 
1922             return nbOldValues != attribute.size();
1923         }
1924         catch ( IllegalArgumentException iae )
1925         {
1926             LOG.error( I18n.err( I18n.ERR_13205_CANNOT_REMOVE_VAL_MISSING_ATTR, attributeType ) );
1927             return false;
1928         }
1929     }
1930 
1931 
1932     /**
1933      * {@inheritDoc}
1934      */
1935     @Override
1936     public boolean remove( AttributeType attributeType, String... values ) throws LdapException
1937     {
1938         if ( attributeType == null )
1939         {
1940             return false;
1941         }
1942 
1943         try
1944         {
1945             Attribute attribute = attributes.get( attributeType.getOid() );
1946 
1947             if ( attribute == null )
1948             {
1949                 // Can't remove values from a not existing attribute !
1950                 return false;
1951             }
1952 
1953             int nbOldValues = attribute.size();
1954 
1955             // Remove the values
1956             attribute.remove( values );
1957 
1958             if ( attribute.size() == 0 )
1959             {
1960                 // No mare values, remove the attribute
1961                 attributes.remove( attributeType.getOid() );
1962 
1963                 return true;
1964             }
1965 
1966             return nbOldValues != attribute.size();
1967         }
1968         catch ( IllegalArgumentException iae )
1969         {
1970             LOG.error( I18n.err( I18n.ERR_13205_CANNOT_REMOVE_VAL_MISSING_ATTR, attributeType ) );
1971             return false;
1972         }
1973     }
1974 
1975 
1976     /**
1977      * {@inheritDoc}
1978      */
1979     @Override
1980     public boolean remove( AttributeType attributeType, Value... values ) throws LdapException
1981     {
1982         if ( attributeType == null )
1983         {
1984             return false;
1985         }
1986 
1987         try
1988         {
1989             Attribute attribute = attributes.get( attributeType.getOid() );
1990 
1991             if ( attribute == null )
1992             {
1993                 // Can't remove values from a not existing attribute !
1994                 return false;
1995             }
1996 
1997             int nbOldValues = attribute.size();
1998 
1999             // Remove the values
2000             attribute.remove( values );
2001 
2002             if ( attribute.size() == 0 )
2003             {
2004                 // No mare values, remove the attribute
2005                 attributes.remove( attributeType.getOid() );
2006 
2007                 return true;
2008             }
2009 
2010             return nbOldValues != attribute.size();
2011         }
2012         catch ( IllegalArgumentException iae )
2013         {
2014             LOG.error( I18n.err( I18n.ERR_13205_CANNOT_REMOVE_VAL_MISSING_ATTR, attributeType ) );
2015             return false;
2016         }
2017     }
2018 
2019 
2020     /**
2021      * <p>
2022      * Removes the attribute with the specified AttributeTypes.
2023      * </p>
2024      * <p>
2025      * The removed attribute are returned by this method.
2026      * </p>
2027      * <p>
2028      * If there is no attribute with the specified AttributeTypes,
2029      * the return value is <code>null</code>.
2030      * </p>
2031      *
2032      * @param attributes the AttributeTypes to be removed
2033      */
2034     @Override
2035     public void removeAttributes( AttributeType... attributes )
2036     {
2037         if ( ( attributes == null ) || ( attributes.length == 0 ) || ( schemaManager == null ) )
2038         {
2039             return;
2040         }
2041 
2042         for ( AttributeType attributeType : attributes )
2043         {
2044             if ( attributeType == null )
2045             {
2046                 continue;
2047             }
2048 
2049             this.attributes.remove( attributeType.getOid() );
2050         }
2051     }
2052 
2053 
2054     /**
2055      * {@inheritDoc}
2056      */
2057     @Override
2058     public void removeAttributes( String... attributes )
2059     {
2060         if ( attributes.length == 0 )
2061         {
2062             return;
2063         }
2064 
2065         if ( schemaManager == null )
2066         {
2067             for ( String attribute : attributes )
2068             {
2069                 Attribute attr = get( attribute );
2070 
2071                 if ( attr != null )
2072                 {
2073                     this.attributes.remove( attr.getId() );
2074                 }
2075                 else
2076                 {
2077                     if ( LOG.isWarnEnabled() )
2078                     {
2079                         LOG.warn( I18n.err( I18n.ERR_13218_AT_DOES_NOT_EXIST, attribute ) );
2080                     }
2081                 }
2082             }
2083         }
2084         else
2085         {
2086             for ( String attribute : attributes )
2087             {
2088                 AttributeType attributeType = null;
2089 
2090                 try
2091                 {
2092                     attributeType = schemaManager.lookupAttributeTypeRegistry( attribute );
2093                 }
2094                 catch ( LdapException ne )
2095                 {
2096                     if ( LOG.isWarnEnabled() )
2097                     {
2098                         LOG.warn( I18n.msg( I18n.MSG_13203_MISSING_ATTRIBUTE_IN_ENTRY, attribute ) );
2099                     }
2100 
2101                     continue;
2102                 }
2103 
2104                 this.attributes.remove( attributeType.getOid() );
2105             }
2106         }
2107     }
2108 
2109 
2110     /**
2111      * <p>
2112      * Removes the specified binary values from an attribute.
2113      * </p>
2114      * <p>
2115      * If at least one value is removed, this method returns <code>true</code>.
2116      * </p>
2117      * <p>
2118      * If there is no more value after having removed the values, the attribute
2119      * will be removed too.
2120      * </p>
2121      * <p>
2122      * If the attribute does not exist, nothing is done and the method returns
2123      * <code>false</code>
2124      * </p>
2125      *
2126      * @param upId The attribute ID
2127      * @param values the values to be removed
2128      * @return <code>true</code> if at least a value is removed, <code>false</code>
2129      * if not all the values have been removed or if the attribute does not exist.
2130      */
2131     @Override
2132     public boolean remove( String upId, byte[]... values ) throws LdapException
2133     {
2134         if ( Strings.isEmpty( upId ) )
2135         {
2136             if ( LOG.isInfoEnabled() )
2137             {
2138                 LOG.info( I18n.err( I18n.ERR_13204_NULL_ATTRIBUTE_ID ) );
2139             }
2140 
2141             return false;
2142         }
2143 
2144         if ( schemaManager == null )
2145         {
2146             String id = getId( upId );
2147 
2148             Attribute attribute = get( id );
2149 
2150             if ( attribute == null )
2151             {
2152                 // Can't remove values from a not existing attribute !
2153                 return false;
2154             }
2155 
2156             int nbOldValues = attribute.size();
2157 
2158             // Remove the values
2159             attribute.remove( values );
2160 
2161             if ( attribute.size() == 0 )
2162             {
2163                 // No mare values, remove the attribute
2164                 attributes.remove( id );
2165 
2166                 return true;
2167             }
2168 
2169             return nbOldValues != attribute.size();
2170         }
2171         else
2172         {
2173             try
2174             {
2175                 AttributeType attributeType = getAttributeType( upId );
2176 
2177                 return remove( attributeType, values );
2178             }
2179             catch ( LdapException ne )
2180             {
2181                 LOG.error( I18n.err( I18n.ERR_13205_CANNOT_REMOVE_VAL_MISSING_ATTR, upId ) );
2182                 return false;
2183             }
2184             catch ( IllegalArgumentException iae )
2185             {
2186                 LOG.error( I18n.err( I18n.ERR_13206_CANNOT_REMOVE_VAL_BAD_ATTR, upId ) );
2187                 return false;
2188             }
2189         }
2190 
2191     }
2192 
2193 
2194     /**
2195      * <p>
2196      * Removes the specified String values from an attribute.
2197      * </p>
2198      * <p>
2199      * If at least one value is removed, this method returns <code>true</code>.
2200      * </p>
2201      * <p>
2202      * If there is no more value after having removed the values, the attribute
2203      * will be removed too.
2204      * </p>
2205      * <p>
2206      * If the attribute does not exist, nothing is done and the method returns
2207      * <code>false</code>
2208      * </p>
2209      *
2210      * @param upId The attribute ID
2211      * @param values the attributes to be removed
2212      * @return <code>true</code> if at least a value is removed, <code>false</code>
2213      * if not all the values have been removed or if the attribute does not exist.
2214      */
2215     @Override
2216     public boolean remove( String upId, String... values ) throws LdapException
2217     {
2218         if ( Strings.isEmpty( upId ) )
2219         {
2220             if ( LOG.isInfoEnabled() )
2221             {
2222                 LOG.info( I18n.err( I18n.ERR_13204_NULL_ATTRIBUTE_ID ) );
2223             }
2224 
2225             return false;
2226         }
2227 
2228         if ( schemaManager == null )
2229         {
2230             String id = getId( upId );
2231 
2232             Attribute attribute = get( id );
2233 
2234             if ( attribute == null )
2235             {
2236                 // Can't remove values from a not existing attribute !
2237                 return false;
2238             }
2239 
2240             int nbOldValues = attribute.size();
2241 
2242             // Remove the values
2243             attribute.remove( values );
2244 
2245             if ( attribute.size() == 0 )
2246             {
2247                 // No mare values, remove the attribute
2248                 attributes.remove( id );
2249 
2250                 return true;
2251             }
2252 
2253             return nbOldValues != attribute.size();
2254         }
2255         else
2256         {
2257             try
2258             {
2259                 AttributeType attributeType = getAttributeType( upId );
2260 
2261                 return remove( attributeType, values );
2262             }
2263             catch ( LdapException ne )
2264             {
2265                 LOG.error( I18n.err( I18n.ERR_13205_CANNOT_REMOVE_VAL_MISSING_ATTR, upId ) );
2266                 return false;
2267             }
2268             catch ( IllegalArgumentException iae )
2269             {
2270                 LOG.error( I18n.err( I18n.ERR_13206_CANNOT_REMOVE_VAL_BAD_ATTR, upId ) );
2271                 return false;
2272             }
2273         }
2274     }
2275 
2276 
2277     /**
2278      * <p>
2279      * Removes the specified values from an attribute.
2280      * </p>
2281      * <p>
2282      * If at least one value is removed, this method returns <code>true</code>.
2283      * </p>
2284      * <p>
2285      * If there is no more value after having removed the values, the attribute
2286      * will be removed too.
2287      * </p>
2288      * <p>
2289      * If the attribute does not exist, nothing is done and the method returns
2290      * <code>false</code>
2291      * </p>
2292      *
2293      * @param upId The attribute ID
2294      * @param values the attributes to be removed
2295      * @return <code>true</code> if at least a value is removed, <code>false</code>
2296      * if not all the values have been removed or if the attribute does not exist.
2297      */
2298     @Override
2299     public boolean remove( String upId, Value... values ) throws LdapException
2300     {
2301         if ( Strings.isEmpty( upId ) )
2302         {
2303             if ( LOG.isInfoEnabled() )
2304             {
2305                 LOG.info( I18n.err( I18n.ERR_13204_NULL_ATTRIBUTE_ID ) );
2306             }
2307 
2308             return false;
2309         }
2310 
2311         if ( schemaManager == null )
2312         {
2313             String id = getId( upId );
2314 
2315             Attribute attribute = get( id );
2316 
2317             if ( attribute == null )
2318             {
2319                 // Can't remove values from a not existing attribute !
2320                 return false;
2321             }
2322 
2323             int nbOldValues = attribute.size();
2324 
2325             // Remove the values
2326             attribute.remove( values );
2327 
2328             if ( attribute.size() == 0 )
2329             {
2330                 // No mare values, remove the attribute
2331                 attributes.remove( id );
2332 
2333                 return true;
2334             }
2335 
2336             return nbOldValues != attribute.size();
2337         }
2338         else
2339         {
2340             try
2341             {
2342                 AttributeType attributeType = getAttributeType( upId );
2343 
2344                 return remove( attributeType, values );
2345             }
2346             catch ( LdapException ne )
2347             {
2348                 LOG.error( I18n.err( I18n.ERR_13205_CANNOT_REMOVE_VAL_MISSING_ATTR, upId ) );
2349                 return false;
2350             }
2351             catch ( IllegalArgumentException iae )
2352             {
2353                 LOG.error( I18n.err( I18n.ERR_13206_CANNOT_REMOVE_VAL_BAD_ATTR, upId ) );
2354                 return false;
2355             }
2356         }
2357     }
2358 
2359 
2360     /**
2361      * Get this entry's Dn.
2362      *
2363      * @return The entry's Dn
2364      */
2365     @Override
2366     public Dn getDn()
2367     {
2368         return dn;
2369     }
2370 
2371 
2372     /**
2373      * {@inheritDoc}
2374      */
2375     @Override
2376     public void setDn( Dn dn )
2377     {
2378         this.dn = dn;
2379 
2380         // Rehash the object
2381         rehash();
2382     }
2383 
2384 
2385     /**
2386      * {@inheritDoc}
2387      */
2388     @Override
2389     public void setDn( String dn ) throws LdapInvalidDnException
2390     {
2391         setDn( new Dn( dn ) );
2392     }
2393 
2394 
2395     /**
2396      * Remove all the attributes for this entry. The Dn is not reset
2397      */
2398     @Override
2399     public void clear()
2400     {
2401         attributes.clear();
2402     }
2403 
2404 
2405     /**
2406      * Returns an enumeration containing the zero or more attributes in the
2407      * collection. The behavior of the enumeration is not specified if the
2408      * attribute collection is changed.
2409      *
2410      * @return an enumeration of all contained attributes
2411      */
2412     @Override
2413     public Iterator<Attribute> iterator()
2414     {
2415         return attributes.values().iterator();
2416     }
2417 
2418 
2419     /**
2420      * Returns the number of attributes.
2421      *
2422      * @return the number of attributes
2423      */
2424     @Override
2425     public int size()
2426     {
2427         return attributes.size();
2428     }
2429 
2430 
2431     /**
2432      * This is the place where we serialize entries, and all theirs
2433      * elements.
2434      * <br>
2435      * The structure used to store the entry is the following :
2436      * <ul>
2437      *   <li>
2438      *     <b>[Dn]</b> : If it's null, stores an empty Dn
2439      *   </li>
2440      *   <li>
2441      *     <b>[attributes number]</b> : the number of attributes.
2442      *   </li>
2443      *   <li>
2444      *     <b>[attribute]*</b> : each attribute, if we have some
2445      *   </li>
2446      * </ul>
2447      *
2448      * {@inheritDoc}
2449      */
2450     @Override
2451     public void writeExternal( ObjectOutput out ) throws IOException
2452     {
2453         // First, the Dn
2454         if ( dn == null )
2455         {
2456             // Write an empty Dn
2457             Dn.EMPTY_DN.writeExternal( out );
2458         }
2459         else
2460         {
2461             // Write the Dn
2462             dn.writeExternal( out );
2463         }
2464 
2465         // Then the attributes.
2466         // Store the attributes' nulber first
2467         out.writeInt( attributes.size() );
2468 
2469         // Iterate through the keys.
2470         for ( Attribute attribute : attributes.values() )
2471         {
2472             // Store the attribute
2473             attribute.writeExternal( out );
2474         }
2475 
2476         out.flush();
2477     }
2478 
2479 
2480     /**
2481      * {@inheritDoc}
2482      */
2483     @Override
2484     public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
2485     {
2486         // Read the Dn
2487         dn = new Dn( schemaManager );
2488         dn.readExternal( in );
2489 
2490         // Read the number of attributes
2491         int nbAttributes = in.readInt();
2492 
2493         // Read the attributes
2494         for ( int i = 0; i < nbAttributes; i++ )
2495         {
2496             // Read each attribute
2497             Attribute attribute = new DefaultAttribute();
2498             attribute.readExternal( in );
2499 
2500             if ( schemaManager != null )
2501             {
2502                 try
2503                 {
2504                     AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( attribute.getId() );
2505                     attribute.apply( attributeType );
2506 
2507                     attributes.put( attributeType.getOid(), attribute );
2508                 }
2509                 catch ( LdapException le )
2510                 {
2511                     String message = le.getLocalizedMessage();
2512                     LOG.error( message );
2513                     throw new IOException( message, le );
2514                 }
2515             }
2516             else
2517             {
2518                 attributes.put( attribute.getId(), attribute );
2519             }
2520         }
2521     }
2522 
2523 
2524     /**
2525      * Get the hash code of this ClientEntry. The Attributes will be sorted
2526      * before the comparison can be done.
2527      *
2528      * @see java.lang.Object#hashCode()
2529      * @return the instance's hash code
2530      */
2531     @Override
2532     public int hashCode()
2533     {
2534         if ( h == 0 )
2535         {
2536             rehash();
2537         }
2538 
2539         return h;
2540     }
2541 
2542 
2543     /**
2544      * {@inheritDoc}
2545      */
2546     @Override
2547     public boolean hasObjectClass( String... objectClasses )
2548     {
2549         if ( ( objectClasses == null ) || ( objectClasses.length == 0 ) || ( objectClasses[0] == null ) )
2550         {
2551             return false;
2552         }
2553 
2554         for ( String objectClass : objectClasses )
2555         {
2556             if ( schemaManager != null )
2557             {
2558                 if ( !contains( objectClassAttributeType, objectClass ) )
2559                 {
2560                     return false;
2561                 }
2562             }
2563             else
2564             {
2565                 if ( !contains( "objectclass", objectClass ) )
2566                 {
2567                     return false;
2568                 }
2569             }
2570         }
2571 
2572         return true;
2573     }
2574 
2575 
2576     /**
2577      * {@inheritDoc}
2578      */
2579     @Override
2580     public boolean hasObjectClass( Attribute... objectClasses )
2581     {
2582         if ( ( objectClasses == null ) || ( objectClasses.length == 0 ) || ( objectClasses[0] == null ) )
2583         {
2584             return false;
2585         }
2586 
2587         for ( Attribute objectClass : objectClasses )
2588         {
2589             // We have to check that we are checking the ObjectClass attributeType
2590             if ( !objectClass.getAttributeType().equals( objectClassAttributeType ) )
2591             {
2592                 return false;
2593             }
2594 
2595             Attribute attribute = attributes.get( objectClassAttributeType.getOid() );
2596 
2597             if ( attribute == null )
2598             {
2599                 // The entry does not have an ObjectClass attribute
2600                 return false;
2601             }
2602 
2603             for ( Value value : objectClass )
2604             {
2605                 // Loop on all the values, and check if they are present
2606                 if ( !attribute.contains( value.getString() ) )
2607                 {
2608                     return false;
2609                 }
2610             }
2611         }
2612 
2613         return true;
2614     }
2615 
2616 
2617     /**
2618      * {@inheritDoc}
2619      */
2620     @Override
2621     public boolean isSchemaAware()
2622     {
2623         return schemaManager != null;
2624     }
2625 
2626 
2627     /**
2628      * @see Object#equals(Object)
2629      */
2630     @Override
2631     public boolean equals( Object o )
2632     {
2633         // Short circuit
2634         if ( this == o )
2635         {
2636             return true;
2637         }
2638 
2639         if ( !( o instanceof Entry ) )
2640         {
2641             return false;
2642         }
2643 
2644         Entry other = ( Entry ) o;
2645 
2646         // Both Dn must be equal
2647         if ( dn == null )
2648         {
2649             if ( other.getDn() != null )
2650             {
2651                 return false;
2652             }
2653         }
2654         else
2655         {
2656             if ( !dn.equals( other.getDn() ) )
2657             {
2658                 return false;
2659             }
2660         }
2661 
2662         // They must have the same number of attributes
2663         if ( size() != other.size() )
2664         {
2665             return false;
2666         }
2667 
2668         // Each attribute must be equal
2669         for ( Attribute attribute : other )
2670         {
2671             if ( !attribute.equals( this.get( attribute.getId() ) ) )
2672             {
2673                 return false;
2674             }
2675         }
2676 
2677         return true;
2678     }
2679 
2680 
2681     /**
2682      * @see Object#toString()
2683      */
2684     @Override
2685     public String toString()
2686     {
2687         return toString( "" );
2688     }
2689 
2690 
2691     /**
2692      * {@inheritDoc}
2693      */
2694     @Override
2695     public String toString( String tabs )
2696     {
2697         StringBuilder sb = new StringBuilder();
2698 
2699         sb.append( tabs ).append( "Entry\n" );
2700         sb.append( tabs ).append( "    dn" );
2701 
2702         if ( dn.isSchemaAware() )
2703         {
2704             sb.append( "[n]" );
2705         }
2706 
2707         sb.append( ": " );
2708         sb.append( dn.getName() );
2709         sb.append( '\n' );
2710 
2711         // First dump the ObjectClass attribute
2712         if ( schemaManager != null )
2713         {
2714             // First dump the ObjectClass attribute
2715             if ( containsAttribute( objectClassAttributeType.getOid() ) )
2716             {
2717                 Attribute objectClass = get( objectClassAttributeType );
2718 
2719                 sb.append( objectClass.toString( tabs + "    " ) );
2720             }
2721         }
2722         else
2723         {
2724             Attribute objectClass = get( "objectclass" );
2725 
2726             if ( objectClass != null )
2727             {
2728                 sb.append( objectClass.toString( tabs + "    " ) );
2729             }
2730         }
2731 
2732         sb.append( '\n' );
2733 
2734         if ( attributes.size() != 0 )
2735         {
2736             for ( Attribute attribute : attributes.values() )
2737             {
2738                 String id = attribute.getId();
2739 
2740                 if ( schemaManager != null )
2741                 {
2742                     AttributeType attributeType = schemaManager.getAttributeType( id );
2743 
2744                     if ( attributeType == null )
2745                     {
2746                         sb.append( tabs ).append( "id: " ).append( id );
2747                     }
2748                     else if ( !attributeType.equals( objectClassAttributeType ) )
2749                     {
2750                         sb.append( attribute.toString( tabs + "    " ) );
2751                         sb.append( '\n' );
2752                     }
2753                 }
2754                 else
2755                 {
2756                     if ( !id.equalsIgnoreCase( SchemaConstants.OBJECT_CLASS_AT )
2757                         && !id.equals( SchemaConstants.OBJECT_CLASS_AT_OID ) )
2758                     {
2759                         sb.append( attribute.toString( tabs + "    " ) );
2760                         sb.append( '\n' );
2761                     }
2762                 }
2763             }
2764         }
2765 
2766         return sb.toString();
2767     }
2768 }