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