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