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