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