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