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