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