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