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