You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by el...@apache.org on 2008/03/31 18:20:08 UTC

svn commit: r643046 [7/8] - in /directory: apacheds/branches/bigbang/benchmarks/ apacheds/branches/bigbang/core-constants/src/main/java/org/apache/directory/server/constants/ apacheds/branches/bigbang/core-entry/src/main/java/org/apache/directory/serve...

Modified: directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/Entry.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/Entry.java?rev=643046&r1=643045&r2=643046&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/Entry.java (original)
+++ directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/Entry.java Mon Mar 31 09:19:45 2008
@@ -20,7 +20,6 @@
 
 
 import org.apache.directory.shared.ldap.name.LdapDN;
-import org.apache.directory.shared.ldap.schema.AttributeType;
 
 import javax.naming.NamingException;
 
@@ -29,49 +28,109 @@
 
 
 /**
+ * <p>
  * This interface represent a LDAP entry. An LDAP entry contains :
- * - A distinguished name (DN)
- * - A list of attributes
- * 
+ * <li> A distinguished name (DN)</li>
+ * <li> A list of attributes</li>
+ * </p>
+ * <p>
  * The available methods on this object are described in this interface.
- * 
+ * </p>
+ * <p>
  * This interface is used by the serverEntry and clientEntry interfaces.
- *
+ *</p>
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  * @version $Rev$, $Date$
  */
-public interface Entry<T extends EntryAttribute> extends Cloneable, Iterable<T>
+public interface Entry extends Cloneable, Iterable<EntryAttribute>
 {
     /**
-     * Removes all the attributes.
+     * Remove all the attributes for this entry. The DN is not reset
      */
     void clear();
 
 
     /**
+     * Clone the current entry
+     */
+    Entry clone();
+
+
+    /**
      * Get this entry's DN.
      *
-     * @return The entry DN
+     * @return The entry's DN
      */
     LdapDN getDn();
 
 
     /**
+     * Tells if an entry as a specific ObjectClass value
+     * 
+     * @param objectClass The ObjectClassw we want to check
+     * @return <code>true</code> if the ObjectClass value is present 
+     * in the ObjectClass attribute
+     */
+    boolean hasObjectClass( String objectClass );
+
+
+    /**
+     * <p>
+     * Returns the attribute with the specified alias. The return value
+     * is <code>null</code> if no match is found.  
+     * </p>
+     * <p>An Attribute with an id different from the supplied alias may 
+     * be returned: for example a call with 'cn' may in some implementations 
+     * return an Attribute whose getId() field returns 'commonName'.
+     * </p>
+     *
+     * @param alias an aliased name of the attribute identifier
+     * @return the attribute associated with the alias
+     */
+    EntryAttribute get( String alias );
+
+
+    /**
+     * <p>
+     * Put some new ClientAttribute using the User Provided ID. 
+     * No value is inserted. 
+     * </p>
+     * <p>
+     * If an existing Attribute is found, it will be replaced by an
+     * empty attribute, and returned to the caller.
+     * </p>
+     * 
+     * @param upIds The user provided IDs of the AttributeTypes to add.
+     * @return A list of replaced Attributes.
+     */
+    List<EntryAttribute> set( String... upIds );
+
+
+    /**
      * Set this entry's DN.
      *
-     * @param dn The LdapdN associated with this entry
+     * @param dn The DN associated with this entry
      */
-    void setDn( LdapDN dn);
+    void setDn( LdapDN dn );
 
 
     /**
      * Returns an enumeration containing the zero or more attributes in the
-     * collection. The behaviour of the enumeration is not specified if the
+     * collection. The behavior of the enumeration is not specified if the
      * attribute collection is changed.
      *
      * @return an enumeration of all contained attributes
      */
-    Iterator<T> iterator();
+    Iterator<EntryAttribute> iterator();
+
+
+    /**
+     * Add some Attributes to the current Entry.
+     *
+     * @param attributes The attributes to add
+     * @throws NamingException If we can't add any of the attributes
+     */
+    void add( EntryAttribute... attributes ) throws NamingException;
 
 
     /**
@@ -80,45 +139,97 @@
      * @param upId The user provided ID of the attribute we want to add 
      * some values to
      * @param values The list of String values to add
-     * @throws NamingException
+     * @throws NamingException If we can't add any of the values
      */
     void add( String upId, String... values ) throws NamingException;
 
-    
+
     /**
      * Add some binary values to the current Entry.
      *
      * @param upId The user provided ID of the attribute we want to add 
      * some values to
      * @param values The list of binary values to add
-     * @throws NamingException
+     * @throws NamingException If we can't add any of the values
      */
     void add( String upId, byte[]... values ) throws NamingException;
 
-    
+
     /**
      * Add some Values to the current Entry.
      *
      * @param upId The user provided ID of the attribute we want to add 
      * some values to
      * @param values The list of Values to add
-     * @throws NamingException
+     * @throws NamingException If we can't add any of the values
      */
     void add( String upId, Value<?>... values ) throws NamingException;
-    
-    
+
+
     /**
-     * Places non-null attributes in the attribute collection. If there is
-     * already an attribute with the same OID as any of the new attributes, 
-     * the old ones are removed from the collection and are returned by this 
-     * method. If there was no attribute with the same OID the return value 
-     * is <code>null</code>.
+     * <p>
+     * Places attributes in the attribute collection. 
+     * </p>
+     * <p>If there is already an attribute with the same ID as any of the 
+     * new attributes, the old ones are removed from the collection and 
+     * are returned by this method. If there was no attribute with the 
+     * same ID the return value is <code>null</code>.
+     *</p>
      *
      * @param attributes the attributes to be put
      * @return the old attributes with the same OID, if exist; otherwise
      *         <code>null</code>
+     * @exception NamingException if the operation fails
+     */
+    List<EntryAttribute> put( EntryAttribute... attributes ) throws NamingException;
+
+
+    /**
+     * <p>
+     * Put an attribute (represented by its ID and some binary values) into an entry. 
+     * </p>
+     * <p> 
+     * If the attribute already exists, the previous attribute will be 
+     * replaced and returned.
+     * </p>
+     *
+     * @param upId The attribute ID
+     * @param values The list of binary values to put. It can be empty.
+     * @return The replaced attribute
+     */
+    EntryAttribute put( String upId, byte[]... values );
+
+
+    /**
+     * <p>
+     * Put an attribute (represented by its ID and some String values) into an entry. 
+     * </p>
+     * <p> 
+     * If the attribute already exists, the previous attribute will be 
+     * replaced and returned.
+     * </p>
+     *
+     * @param upId The attribute ID
+     * @param values The list of String values to put. It can be empty.
+     * @return The replaced attribute
+     */
+    EntryAttribute put( String upId, String... values );
+
+
+    /**
+     * <p>
+     * Put an attribute (represented by its ID and some values) into an entry. 
+     * </p>
+     * <p> 
+     * If the attribute already exists, the previous attribute will be 
+     * replaced and returned.
+     * </p>
+     *
+     * @param upId The attribute ID
+     * @param values The list of values to put. It can be empty.
+     * @return The replaced attribute
      */
-    List<T> put( T... attributes ) throws NamingException;
+    EntryAttribute put( String upId, Value<?>... values );
 
 
     /**
@@ -129,57 +240,162 @@
       * @param attributes the attributes to be removed
       * @return the removed attribute, if exists; otherwise <code>null</code>
       */
-    List<T> remove( T... attributes ) throws NamingException;
+    List<EntryAttribute> remove( EntryAttribute... attributes ) throws NamingException;
 
 
     /**
-     * Checks if an entry contains an attribute with a given value.
+     * <p>
+     * Removes the specified binary values from an attribute.
+     * </p>
+     * <p>
+     * If at least one value is removed, this method returns <code>true</code>.
+     * </p>
+     * <p>
+     * If there is no more value after having removed the values, the attribute
+     * will be removed too.
+     * </p>
+     * <p>
+     * If the attribute does not exist, nothing is done and the method returns 
+     * <code>false</code>
+     * </p> 
+     *
+     * @param upId The attribute ID  
+     * @param attributes the attributes to be removed
+     * @return <code>true</code> if at least a value is removed, <code>false</code>
+     * if not all the values have been removed or if the attribute does not exist. 
+     */
+    boolean remove( String upId, byte[]... values ) throws NamingException;
+
+
+    /**
+     * <p>
+     * Removes the specified String values from an attribute.
+     * </p>
+     * <p>
+     * If at least one value is removed, this method returns <code>true</code>.
+     * </p>
+     * <p>
+     * If there is no more value after havong removed the values, the attribute
+     * will be removed too.
+     * </p>
+     * <p>
+     * If the attribute does not exist, nothing is done and the method returns 
+     * <code>false</code>
+     * </p> 
+     *
+     * @param upId The attribute ID  
+     * @param attributes the attributes to be removed
+     * @return <code>true</code> if at least a value is removed, <code>false</code>
+     * if no values have been removed or if the attribute does not exist. 
+     */
+    boolean remove( String upId, String... values ) throws NamingException;
+
+
+    /**
+     * <p>
+     * Removes the specified values from an attribute.
+     * </p>
+     * <p>
+     * If at least one value is removed, this method returns <code>true</code>.
+     * </p>
+     * <p>
+     * If there is no more value after having removed the values, the attribute
+     * will be removed too.
+     * </p>
+     * <p>
+     * If the attribute does not exist, nothing is done and the method returns 
+     * <code>false</code>
+     * </p> 
+     *
+     * @param upId The attribute ID  
+     * @param attributes the attributes to be removed
+     * @return <code>true</code> if at least a value is removed, <code>false</code>
+     * if not all the values have been removed or if the attribute does not exist. 
+     */
+    boolean remove( String upId, Value<?>... values ) throws NamingException;
+
+
+    /**
+      * <p>
+      * Removes the attribute with the specified alias. 
+      * </p>
+      * <p>
+      * The removed attribute are returned by this method. 
+      * </p>
+      * <p>
+      * If there is no attribute with the specified alias,
+      * the return value is <code>null</code>.
+      * </p>
+      *
+      * @param attributes an aliased name of the attribute to be removed
+      * @return the removed attributes, if any, as a list; otherwise <code>null</code>
+      */
+    List<EntryAttribute> removeAttributes( String... attributes );
+
+
+    /**
+     * <p>
+     * Checks if an entry contains a list of attributes.
+     * </p>
+     * <p>
+     * If the list is null or empty, this method will return <code>true</code>
+     * if the entry has no attribute, <code>false</code> otherwise.
+     * </p>
      *
-     * @param attributeType The Attribute type we are looking for
-     * @param value The searched value
-     * @return <code>true</code> if the value is found within the attribute
+     * @param attributes The Attributes to look for
+     * @return <code>true</code> if all the attributes are found within 
+     * the entry, <code>false</code> if at least one of them is not present.
      * @throws NamingException If the attribute does not exist
      */
-    boolean contains( AttributeType attributeType, Value<?> value ) throws NamingException;
-    
-    
+    boolean contains( EntryAttribute... attributes ) throws NamingException;
+
+
     /**
-     * Checks if an entry contains an attribute with a given value.
+     * Checks if an entry contains an attribute with some binary values.
      *
-     * @param id The Attribute ID we are looking for
-     * @param value The searched value
-     * @return <code>true</code> if the value is found within the attribute
-     * @throws NamingException If the attribute does not exist
+     * @param id The Attribute we are looking for.
+     * @param values The searched values.
+     * @return <code>true</code> if all the values are found within the attribute,
+     * false if at least one value is not present or if the ID is not valid. 
      */
-    boolean contains( String id, Value<?> value ) throws NamingException;
-    
-    
+    boolean contains( String upId, byte[]... values );
+
+
     /**
-     * Checks if an entry contains an attribute with a given value.
+     * Checks if an entry contains an attribute with some String values.
      *
-     * @param id The Attribute ID we are looking for
-     * @param value The searched value
-     * @return <code>true</code> if the value is found within the attribute
-     * @throws NamingException If the attribute does not exist
+     * @param id The Attribute we are looking for.
+     * @param values The searched values.
+     * @return <code>true</code> if all the values are found within the attribute,
+     * false if at least one value is not present or if the ID is not valid. 
      */
-    boolean contains( String id, String value ) throws NamingException;
+    boolean contains( String upId, String... values );
+
 
-    
     /**
-     * Checks if an entry contains an attribute with a given value.
+     * Checks if an entry contains an attribute with some values.
      *
-     * @param id The Attribute ID we are looking for
-     * @param value The searched value
-     * @return <code>true</code> if the value is found within the attribute
-     * @throws NamingException If the attribute does not exist
+     * @param id The Attribute we are looking for.
+     * @param values The searched values.
+     * @return <code>true</code> if all the values are found within the attribute,
+     * false if at least one value is not present or if the ID is not valid. 
      */
-    boolean contains( String id, byte[] value ) throws NamingException;
+    boolean contains( String upId, Value<?>... values );
+
+
+    /**
+     * Checks if an entry contains some specific attributes.
+     *
+     * @param attributes The Attributes to look for.
+     * @return <code>true</code> if the attributes are all found within the entry.
+     */
+    boolean containsAttribute( String... attributes );
 
     
     /**
-      * Returns the number of attributes.
-      *
-      * @return the number of attributes
-      */
+     * Returns the number of attributes.
+     *
+     * @return the number of attributes
+     */
     int size();
 }

Modified: directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/Value.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/Value.java?rev=643046&r1=643045&r2=643046&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/Value.java (original)
+++ directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/Value.java Mon Mar 31 09:19:45 2008
@@ -19,7 +19,7 @@
  */
 package org.apache.directory.shared.ldap.entry;
 
-import java.io.Serializable;
+import java.io.Externalizable;
 
 import javax.naming.NamingException;
 
@@ -34,7 +34,7 @@
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  * @version $Rev$, $Date$
  */
-public interface Value<T> extends Cloneable, Serializable, Comparable<Value<T>>
+public interface Value<T> extends Cloneable, Externalizable, Comparable<Value<T>>
 {
     
     Value<T> clone();

Modified: directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/client/ClientBinaryValue.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/client/ClientBinaryValue.java?rev=643046&r1=643045&r2=643046&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/client/ClientBinaryValue.java (original)
+++ directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/client/ClientBinaryValue.java Mon Mar 31 09:19:45 2008
@@ -29,6 +29,11 @@
 import org.slf4j.LoggerFactory;
 
 import javax.naming.NamingException;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
 import java.util.Arrays;
 
 
@@ -335,6 +340,25 @@
     }
 
 
+    /**
+     * @see Externalizable#readExternal(ObjectInput)
+     */
+    public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
+    {
+        // TODO implement this method
+        return;
+    }
+
+    
+    /**
+     * @see Externalizable#writeExternal(ObjectOutput)
+     */
+    public void writeExternal( ObjectOutput out ) throws IOException
+    {
+        // TODO Implement this method
+    }
+    
+    
     /**
      * Dumps binary in hex with label.
      *

Modified: directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/client/ClientEntry.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/client/ClientEntry.java?rev=643046&r1=643045&r2=643046&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/client/ClientEntry.java (original)
+++ directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/client/ClientEntry.java Mon Mar 31 09:19:45 2008
@@ -20,7 +20,6 @@
 
 
 import org.apache.directory.shared.ldap.entry.Entry;
-import org.apache.directory.shared.ldap.entry.Value;
 
 
 /**
@@ -34,85 +33,4 @@
  */
 public interface ClientEntry extends Entry
 {
-    /**
-     * Returns the attribute with the specified alias. The return value
-     * is <code>null</code> if no match is found.  An Attribute with an id
-     * different from the supplied alias may be returned: for example a call
-     * with 'cn' may in some implementations return a ClientAttribute whose
-     * getId() feild returns 'commonName'.
-     *
-     * @param alias an aliased name of the attribute identifier
-     * @return the attribute associated with the alias
-     */
-    ClientAttribute get( String alias );
-
-
-    /**
-     * Places a new attribute with the supplied alias and value into the
-     * attribute collection. If there is already an attribute associated with
-     * the alias, the old one is removed from the collection and is returned
-     * by this method. If there was no attribute associated with the alias the
-     * return value is <code>null</code>. An attribute with an id different
-     * from the supplied alias may be returned: for example a call with 'cn'
-     * may in some implementations return a ClientAttribute whose getId()
-     * feild returns 'commonName'.
-     *
-     * This method provides a mechanism to put an attribute with a <code>null
-     * </code> value: the value of <code>val</code> may be <code>null</code>.
-     *
-     * @param alias an aliased name of the new attribute to be put
-     * @param val the value of the new attribute to be put
-     * @return the old attribute with associated with the specified alias,
-     * if exists; otherwise <code>null</code>
-     */
-    ClientAttribute put( String alias, Value<?> val );
-
-
-    /**
-     * Places a new attribute with the supplied OID and value into the attribute
-     * collection. If there is already an attribute with the same OID, the old
-     * one is removed from the collection and is returned by this method. If
-     * there was no attribute with the same OID the return value is
-     * <code>null</code>.
-     *
-     * This method provides a mechanism to put an attribute with a
-     * <code>null</code> value: the value of <code>obj</code> may be
-     * <code>null</code>.
-     *
-     * @param alias an aliased name of the new attribute to be put
-     * @param val the value of the new attribute to be put
-     * @return the old attribute with the same identifier, if exists; otherwise
-     *         <code>null</code>
-     */
-    ClientAttribute put( String alias, String val );
-
-
-    /**
-     * Places a new attribute with the supplied OID and value into the attribute
-     * collection. If there is already an attribute with the same OID, the old
-     * one is removed from the collection and is returned by this method. If
-     * there was no attribute with the same OID the return value is
-     * <code>null</code>.
-     *
-     * This method provides a mechanism to put an attribute with a
-     * <code>null</code> value: the value of <code>obj</code> may be
-     * <code>null</code>.
-     *
-     * @param alias an aliased of the new attribute to be put
-     * @param val the value of the new attribute to be put
-     * @return the old attribute with the same identifier, if exists; otherwise
-     *         <code>null</code>
-     */
-    ClientAttribute put( String alias, byte[] val );
-
-
-    /**
-     * Removes the attribute with the specified alias. The removed attribute is
-     * returned by this method. If there is no attribute with the specified OID,
-     * the return value is <code>null</code>.
-     *
-     * @param alias an aliased name of the attribute to be removed
-     * @return the removed attribute, if exists; otherwise <code>null</code>
-     */
-    ClientAttribute remove( String alias );
 }

Modified: directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/client/ClientStringValue.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/client/ClientStringValue.java?rev=643046&r1=643045&r2=643046&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/client/ClientStringValue.java (original)
+++ directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/client/ClientStringValue.java Mon Mar 31 09:19:45 2008
@@ -19,6 +19,11 @@
 package org.apache.directory.shared.ldap.entry.client;
 
 
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
 import org.apache.directory.shared.ldap.NotImplementedException;
 import org.apache.directory.shared.ldap.entry.AbstractValue;
 import org.apache.directory.shared.ldap.entry.Value;
@@ -286,6 +291,25 @@
         
         // Test the normalized values
         return this.getNormalizedValue().equals( other.getNormalizedValue() );
+    }
+    
+    
+    /**
+     * @see Externalizable#readExternal(ObjectInput)
+     */
+    public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
+    {
+        // TODO implement this method
+        return;
+    }
+
+    
+    /**
+     * @see Externalizable#writeExternal(ObjectOutput)
+     */
+    public void writeExternal( ObjectOutput out ) throws IOException
+    {
+        // TODO Implement this method
     }
     
     

Modified: directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/client/DefaultClientAttribute.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/client/DefaultClientAttribute.java?rev=643046&r1=643045&r2=643046&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/client/DefaultClientAttribute.java (original)
+++ directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/client/DefaultClientAttribute.java Mon Mar 31 09:19:45 2008
@@ -108,15 +108,9 @@
         {
             for ( Value<?> val:vals )
             {
-                if ( val instanceof ClientStringValue )
-                {
-                    ClientStringValue serverString = ( ClientStringValue ) val;
-                    add( new ClientStringValue( serverString.get() ) );
-                }
-                else if ( val instanceof ClientBinaryValue )
+                if ( ( val instanceof ClientStringValue ) || ( val instanceof ClientBinaryValue ) )
                 {
-                    ClientBinaryValue serverBinary = ( ClientBinaryValue ) val;
-                    add( new ClientBinaryValue( serverBinary.getCopy() ) );
+                    add( val );
                 }
                 else
                 {
@@ -415,6 +409,7 @@
                     values.add( nullBinaryValue );
                     values.add( nullStringValue );
                     nullValueAdded = true;
+                    nbAdded++;
                 }
                 else if ( !isHR )
                 {
@@ -425,6 +420,7 @@
                     if ( !values.contains( nullBinaryValue ) )
                     {
                         values.add( nullBinaryValue );
+                        nbAdded++;
                     }
                     
                 }
@@ -451,6 +447,7 @@
                         // The attribute type will be set to HR
                         isHR = true;
                         values.add( val );
+                        nbAdded++;
                     }
                     else if ( !isHR )
                     {
@@ -459,12 +456,20 @@
                         ClientBinaryValue cbv = new ClientBinaryValue();
                         cbv.set( StringTools.getBytesUtf8( (String)val.get() ) );
                         
-                        values.add( cbv );
+                        if ( !contains( cbv ) )
+                        {
+                            values.add( cbv );
+                            nbAdded++;
+                        }
                     }
                     else
                     {
                         // The attributeType is HR, simply add the value
-                        values.add( val );
+                        if ( !contains( val ) )
+                        {
+                            values.add( val );
+                            nbAdded++;
+                        }
                     }
                 }
                 else
@@ -475,11 +480,16 @@
                         // The attribute type will be set to binary
                         isHR = false;
                         values.add( val );
+                        nbAdded++;
                     }
                     else if ( !isHR )
                     {
-                        // The attributeType is not HR, simply add the value
-                        values.add( val );
+                        // The attributeType is not HR, simply add the value if it does not already exist
+                        if ( !contains( val ) )
+                        {
+                            values.add( val );
+                            nbAdded++;
+                        }
                     }
                     else
                     {
@@ -488,12 +498,14 @@
                         ClientStringValue csv = new ClientStringValue();
                         csv.set( StringTools.utf8ToString( (byte[])val.get() ) );
                         
-                        values.add( csv );
+                        if ( !contains( csv ) )
+                        {
+                            values.add( csv );
+                            nbAdded++;
+                        }
                     }
                 }
             }
-
-            nbAdded++;
         }
 
         // Last, not least, if a nullValue has been added, and if other 
@@ -536,9 +548,13 @@
         {
             for ( String val:vals )
             {
-                if ( add( new ClientStringValue( val ) ) == 1 )
+                // Call the add(Value) method, if not already present
+                if ( !contains( val ) )
                 {
-                    nbAdded++;
+                    if ( add( new ClientStringValue( val ) ) == 1 )
+                    {
+                        nbAdded++;
+                    }
                 }
             }
         }
@@ -618,10 +634,13 @@
                     valString = StringTools.utf8ToString( val );
                 }
                 
-                // Now call the add(Value) method
-                if ( add( new ClientStringValue( valString ) ) == 1 )
+                // Now call the add(Value) method, if not already present
+                if ( !contains( val ) )
                 {
-                    nbAdded++;
+                    if ( add( new ClientStringValue( valString ) ) == 1 )
+                    {
+                        nbAdded++;
+                    }
                 }
             }
         }
@@ -1321,7 +1340,18 @@
         {
             for ( Value<?> value:values )
             {
-                sb.append( "    " ).append( upId ).append( ": " ).append( value ).append( '\n' );
+                sb.append( "    " ).append( upId ).append( ": " );
+                
+                if ( value.isNull() )
+                {
+                    sb.append( "''" );
+                }
+                else
+                {
+                    sb.append( value );
+                }
+                
+                sb.append( '\n' );
             }
         }
         else

Added: directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/client/DefaultClientEntry.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/client/DefaultClientEntry.java?rev=643046&view=auto
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/client/DefaultClientEntry.java (added)
+++ directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/entry/client/DefaultClientEntry.java Mon Mar 31 09:19:45 2008
@@ -0,0 +1,1160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.directory.shared.ldap.entry.client;
+
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.directory.shared.ldap.entry.AbstractEntry;
+import org.apache.directory.shared.ldap.entry.Entry;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.NamingException;
+
+
+/**
+ * A default implementation of a ServerEntry which should suite most
+ * use cases.
+ * 
+ * This class is final, it should not be extended.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public final class DefaultClientEntry extends AbstractEntry<String> implements ClientEntry, Externalizable
+{
+    /** Used for serialization */
+    public static final long serialVersionUID = 2L;
+    
+    /** The logger for this class */
+    private static final Logger LOG = LoggerFactory.getLogger( DefaultClientEntry.class );
+
+    //-------------------------------------------------------------------------
+    // Constructors
+    //-------------------------------------------------------------------------
+    /**
+     * Creates a new instance of DefaultClientEntry. 
+     * <p>
+     * This entry <b>must</b> be initialized before being used !
+     */
+    public DefaultClientEntry()
+    {
+    }
+
+
+    /**
+     * Creates a new instance of DefaultServerEntry, with a 
+     * DN. 
+     * 
+     * @param dn The DN for this serverEntry. Can be null.
+     */
+    public DefaultClientEntry( LdapDN dn )
+    {
+        this.dn = dn;
+    }
+
+
+    /**
+     * Creates a new instance of DefaultServerEntry, with a 
+     * DN and a list of IDs. 
+     * 
+     * @param dn The DN for this serverEntry. Can be null.
+     * @param upIds The list of attributes to create.
+     */
+    public DefaultClientEntry( LdapDN dn, String... upIds )
+    {
+        this.dn = dn;
+
+        for ( String upId:upIds )
+        {
+            // Add a new AttributeType without value
+            set( upId );
+        }
+    }
+
+    
+    /**
+     * <p>
+     * Creates a new instance of DefaultClientEntry, with a 
+     * DN and a list of EntryAttributes.
+     * </p> 
+     * 
+     * @param dn The DN for this serverEntry. Can be null
+     * @param attributes The list of attributes to create
+     */
+    public DefaultClientEntry( LdapDN dn, EntryAttribute... attributes )
+    {
+        this.dn = dn;
+
+        for ( EntryAttribute attribute:attributes )
+        {
+            if ( attribute == null )
+            {
+                continue;
+            }
+            
+            // Store a new ClientAttribute
+            this.attributes.put( attribute.getId(), attribute );
+        }
+    }
+
+    
+    //-------------------------------------------------------------------------
+    // Helper methods
+    //-------------------------------------------------------------------------
+    private String getId( String upId ) throws IllegalArgumentException
+    {
+        String id = StringTools.trim( StringTools.toLowerCase( upId ) );
+        
+        // If empty, throw an error
+        if ( ( id == null ) || ( id.length() == 0 ) ) 
+        {
+            String message = "The attributeType ID should not be null or empty";
+            LOG.error( message );
+            throw new IllegalArgumentException( message );
+        }
+        
+        return id;
+    }
+
+    
+    //-------------------------------------------------------------------------
+    // Entry methods
+    //-------------------------------------------------------------------------
+    /**
+     * Add some Attributes to the current Entry.
+     *
+     * @param attributes The attributes to add
+     * @throws NamingException If we can't add any of the attributes
+     */
+    public void add( EntryAttribute... attributes ) throws NamingException
+    {
+        // Loop on all the added attributes
+        for ( EntryAttribute attribute:attributes )
+        {
+            // If the attribute already exist, we will add the new values.
+            if ( contains( attribute ) )
+            {
+                EntryAttribute existingAttr = get( attribute.getId() );
+                
+                // Loop on all the values, and add them to the existing attribute
+                for ( Value<?> value:attribute )
+                {
+                    existingAttr.add( value );
+                }
+            }
+            else
+            {
+                // Stores the attribute into the entry
+                this.attributes.put( attribute.getId(), (ClientAttribute)attribute );
+            }
+        }
+    }
+
+    
+    /**
+     * Add an attribute (represented by its ID and binary values) into an entry. 
+     *
+     * @param upId The attribute ID
+     * @param values The list of binary values to inject. It can be empty
+     * @throws NamingException If the attribute does not exist
+     */
+    public void add( String upId, byte[]... values ) throws NamingException
+    {
+        // First, transform the upID to a valid ID
+        String id = getId( upId );
+        
+        // Now, check to see if we already have such an attribute
+        EntryAttribute attribute = attributes.get( id );
+        
+        if ( attribute != null )
+        {
+            // This Attribute already exist, we add the values 
+            // into it. (If the values already exists, they will
+            // not be added, but this is done in the add() method)
+            attribute.add( values );
+            attribute.setUpId( upId );
+        }
+        else
+        {
+            // We have to create a new Attribute and set the values
+            // and the upId
+            attributes.put( id, new DefaultClientAttribute( upId, values ) );
+        }
+    }
+
+
+    /**
+     * Add some String values to the current Entry.
+     *
+     * @param upId The user provided ID of the attribute we want to add 
+     * some values to
+     * @param values The list of String values to add
+     * @throws NamingException If we can't add any of the values
+     */
+    public void add( String upId, String... values ) throws NamingException
+    {
+        // First, transform the upID to a valid ID
+        String id = getId( upId );
+
+        // Now, check to see if we already have such an attribute
+        EntryAttribute attribute = attributes.get( id );
+        
+        if ( attribute != null )
+        {
+            // This Attribute already exist, we add the values 
+            // into it. (If the values already exists, they will
+            // not be added, but this is done in the add() method)
+            attribute.add( values );
+            attribute.setUpId( upId );
+        }
+        else
+        {
+            // We have to create a new Attribute and set the values
+            // and the upId
+            attributes.put( id, new DefaultClientAttribute( upId, values ) );
+        }
+    }
+
+
+    /**
+     * Add an attribute (represented by its ID and Value values) into an entry. 
+     *
+     * @param upId The attribute ID
+     * @param values The list of Value values to inject. It can be empty
+     * @throws NamingException If the attribute does not exist
+     */
+    public void add( String upId, Value<?>... values ) throws NamingException
+    {
+        // First, transform the upID to a valid ID
+        String id = getId( upId );
+
+        // Now, check to see if we already have such an attribute
+        EntryAttribute attribute = attributes.get( id );
+        
+        if ( attribute != null )
+        {
+            // This Attribute already exist, we add the values 
+            // into it. (If the values already exists, they will
+            // not be added, but this is done in the add() method)
+            attribute.add( values );
+            attribute.setUpId( upId );
+        }
+        else
+        {
+            // We have to create a new Attribute and set the values
+            // and the upId
+            attributes.put( id, new DefaultClientAttribute( upId, values ) );
+        }
+    }
+
+
+    /**
+     * Clone an entry. All the element are duplicated, so a modification on
+     * the original object won't affect the cloned object, as a modification
+     * on the cloned object has no impact on the original object
+     */
+    public Entry clone()
+    {
+        // First, clone the structure
+        DefaultClientEntry clone = (DefaultClientEntry)super.clone();
+        
+        // Just in case ... Should *never* happen
+        if ( clone == null )
+        {
+            return null;
+        }
+        
+        // An Entry has a DN and many attributes.
+        // First, clone the DN, if not null.
+        if ( dn != null )
+        {
+            clone.setDn( (LdapDN)dn.clone() );
+        }
+        
+        // then clone the ClientAttribute Map.
+        clone.attributes = (Map<String, EntryAttribute>)(((HashMap<String, EntryAttribute>)attributes).clone());
+        
+        // now clone all the attributes
+        clone.attributes.clear();
+        
+        for ( EntryAttribute attribute:attributes.values() )
+        {
+            clone.attributes.put( attribute.getId(), attribute.clone() );
+        }
+        
+        // We are done !
+        return clone;
+    }
+    
+
+    /**
+     * <p>
+     * Checks if an entry contains a list of attributes.
+     * </p>
+     * <p>
+     * If the list is null or empty, this method will return <code>true</code>
+     * if the entry has no attribute, <code>false</code> otherwise.
+     * </p>
+     *
+     * @param attributes The Attributes to look for
+     * @return <code>true</code> if all the attributes are found within 
+     * the entry, <code>false</code> if at least one of them is not present.
+     * @throws NamingException If the attribute does not exist
+     */
+    public boolean contains( EntryAttribute... attributes ) throws NamingException
+    {
+        for ( EntryAttribute attribute:attributes )
+        {
+            if ( attribute == null )
+            {
+                return this.attributes.size() == 0;
+            }
+            
+            if ( !this.attributes.containsKey( attribute.getId() ) )
+            {
+                return false;
+            }
+        }
+        
+        return true;
+    }
+    
+    
+    /**
+     * Checks if an entry contains a specific attribute
+     *
+     * @param attributes The Attributes to look for
+     * @return <code>true</code> if the attributes are found within the entry
+     * @throws NamingException If the attribute does not exist
+     */
+    public boolean contains( String upId ) throws NamingException
+    {
+        String id = getId( upId );
+        
+        return attributes.containsKey( id );
+    }
+
+    
+    /**
+     * Checks if an entry contains an attribute with some binary values.
+     *
+     * @param id The Attribute we are looking for.
+     * @param values The searched values.
+     * @return <code>true</code> if all the values are found within the attribute,
+     * false if at least one value is not present or if the ID is not valid. 
+     */
+    public boolean contains( String upId, byte[]... values )
+    {
+        String id = getId( upId );
+        
+        EntryAttribute attribute = attributes.get( id );
+        
+        if ( attribute == null )
+        {
+            return false;
+        }
+        
+        return attribute.contains( values );
+    }
+    
+    
+    /**
+     * Checks if an entry contains an attribute with some String values.
+     *
+     * @param id The Attribute we are looking for.
+     * @param values The searched values.
+     * @return <code>true</code> if all the values are found within the attribute,
+     * false if at least one value is not present or if the ID is not valid. 
+     */
+    public boolean contains( String upId, String... values )
+    {
+        String id = getId( upId );
+        
+        EntryAttribute attribute = attributes.get( id );
+        
+        if ( attribute == null )
+        {
+            return false;
+        }
+        
+        return attribute.contains( values );
+    }
+    
+    
+    /**
+     * Checks if an entry contains an attribute with some values.
+     *
+     * @param id The Attribute we are looking for.
+     * @param values The searched values.
+     * @return <code>true</code> if all the values are found within the attribute,
+     * false if at least one value is not present or if the ID is not valid. 
+     */
+    public boolean contains( String upId, Value<?>... values )
+    {
+        String id = getId( upId );
+        
+        EntryAttribute attribute = attributes.get( id );
+        
+        if ( attribute == null )
+        {
+            return false;
+        }
+        
+        return attribute.contains( values );
+    }
+    
+    
+    /**
+     * Checks if an entry contains some specific attributes.
+     *
+     * @param attributes The Attributes to look for.
+     * @return <code>true</code> if the attributes are all found within the entry.
+     */
+    public boolean containsAttribute( String... attributes )
+    {
+        for ( String attribute:attributes )
+        {
+            String id = getId( attribute );
+    
+            if ( !this.attributes.containsKey( id ) )
+            {
+                return false;
+            }
+        }
+        
+        return true;
+    }
+
+    
+    /**
+     * <p>
+     * Returns the attribute with the specified alias. The return value
+     * is <code>null</code> if no match is found.  
+     * </p>
+     * <p>An Attribute with an id different from the supplied alias may 
+     * be returned: for example a call with 'cn' may in some implementations 
+     * return an Attribute whose getId() field returns 'commonName'.
+     * </p>
+     *
+     * @param alias an aliased name of the attribute identifier
+     * @return the attribute associated with the alias
+     */
+    public EntryAttribute get( String alias )
+    {
+        try
+        {
+            String id = getId( alias );
+            
+            return attributes.get( id );
+        }
+        catch( IllegalArgumentException iea )
+        {
+            LOG.error( "An exception has been raised while looking for attribute id {}''", alias );
+            return null;
+        }
+    }
+
+
+    /**
+     * <p>
+     * Put an attribute (represented by its ID and some binary values) into an entry. 
+     * </p>
+     * <p> 
+     * If the attribute already exists, the previous attribute will be 
+     * replaced and returned.
+     * </p>
+     *
+     * @param upId The attribute ID
+     * @param values The list of binary values to put. It can be empty.
+     * @return The replaced attribute
+     */
+    public EntryAttribute put( String upId, byte[]... values )
+    {
+        // Get the normalized form of the ID
+        String id = getId( upId );
+        
+        // Create a new attribute
+        ClientAttribute clientAttribute = new DefaultClientAttribute( upId, values );
+
+        // Replace the previous one, and return it back
+        return attributes.put( id, clientAttribute );
+    }
+
+
+    /**
+     * <p>
+     * Put an attribute (represented by its ID and some String values) into an entry. 
+     * </p>
+     * <p> 
+     * If the attribute already exists, the previous attribute will be 
+     * replaced and returned.
+     * </p>
+     *
+     * @param upId The attribute ID
+     * @param values The list of String values to put. It can be empty.
+     * @return The replaced attribute
+     */
+    public EntryAttribute put( String upId, String... values )
+    {
+        // Get the normalized form of the ID
+        String id = getId( upId );
+        
+        // Create a new attribute
+        ClientAttribute clientAttribute = new DefaultClientAttribute( upId, values );
+
+        // Replace the previous one, and return it back
+        return attributes.put( id, clientAttribute );
+    }
+
+
+    /**
+     * <p>
+     * Put an attribute (represented by its ID and some values) into an entry. 
+     * </p>
+     * <p> 
+     * If the attribute already exists, the previous attribute will be 
+     * replaced and returned.
+     * </p>
+     *
+     * @param upId The attribute ID
+     * @param values The list of values to put. It can be empty.
+     * @return The replaced attribute
+     */
+    public EntryAttribute put( String upId, Value<?>... values )
+    {
+        // Get the normalized form of the ID
+        String id = getId( upId );
+        
+        // Create a new attribute
+        ClientAttribute clientAttribute = new DefaultClientAttribute( upId, values );
+
+        // Replace the previous one, and return it back
+        return attributes.put( id, clientAttribute );
+    }
+
+
+    /**
+     * <p>
+     * Put some new ClientAttribute using the User Provided ID. 
+     * No value is inserted. 
+     * </p>
+     * <p>
+     * If an existing Attribute is found, it will be replaced by an
+     * empty attribute, and returned to the caller.
+     * </p>
+     * 
+     * @param upIds The user provided IDs of the AttributeTypes to add.
+     * @return A list of replaced Attributes.
+     */
+    public List<EntryAttribute> set( String... upIds )
+    {
+        if ( upIds == null )
+        {
+            String message = "The AttributeType list should not be null";
+            LOG.error( message );
+            throw new IllegalArgumentException( message );
+        }
+        
+        List<EntryAttribute> returnedClientAttributes = new ArrayList<EntryAttribute>();
+        
+        // Now, loop on all the attributeType to add
+        for ( String upId:upIds )
+        {
+            String id = StringTools.trim( StringTools.toLowerCase( upId ) );
+            
+            if ( id == null )
+            {
+                String message = "The AttributeType list should not contain null values";
+                LOG.error( message );
+                throw new IllegalArgumentException( message );
+            }
+            
+            if ( attributes.containsKey( id ) )
+            {
+                // Add the removed serverAttribute to the list
+                returnedClientAttributes.add( attributes.remove( id ) );
+            }
+
+            ClientAttribute newAttribute = new DefaultClientAttribute( upId );
+            attributes.put( id, newAttribute );
+        }
+        
+        return returnedClientAttributes;
+    }
+
+    
+    /**
+     * <p>
+     * Places attributes in the attribute collection. 
+     * </p>
+     * <p>If there is already an attribute with the same ID as any of the 
+     * new attributes, the old ones are removed from the collection and 
+     * are returned by this method. If there was no attribute with the 
+     * same ID the return value is <code>null</code>.
+     *</p>
+     *
+     * @param attributes the attributes to be put
+     * @return the old attributes with the same OID, if exist; otherwise
+     *         <code>null</code>
+     * @exception NamingException if the operation fails
+     */
+    public List<EntryAttribute> put( EntryAttribute... attributes ) throws NamingException
+    {
+        // First, get the existing attributes
+        List<EntryAttribute> previous = new ArrayList<EntryAttribute>();
+        
+        for ( EntryAttribute attribute:attributes )
+        {
+            String id = attribute.getId();
+            
+            if ( contains( id ) )
+            {
+                // Store the attribute and remove it from the list
+                previous.add( get( id ) );
+                this.attributes.remove( id );
+            }
+            
+            // add the new one
+            this.attributes.put( id, (ClientAttribute)attribute );            
+        }
+        
+        // return the previous attributes
+        return previous;
+    }
+
+
+    public List<EntryAttribute> remove( EntryAttribute... attributes ) throws NamingException
+    {
+        List<EntryAttribute> removedAttributes = new ArrayList<EntryAttribute>();
+        
+        for ( EntryAttribute attribute:attributes )
+        {
+            if ( contains( attribute.getId() ) )
+            {
+                this.attributes.remove( attribute.getId() );
+                removedAttributes.add( attribute );
+            }
+        }
+        
+        return removedAttributes;
+    }
+
+
+    /**
+     * <p>
+     * Removes the attribute with the specified alias. 
+     * </p>
+     * <p>
+     * The removed attribute are returned by this method. 
+     * </p>
+     * <p>
+     * If there is no attribute with the specified alias,
+     * the return value is <code>null</code>.
+     * </p>
+     *
+     * @param attributes an aliased name of the attribute to be removed
+     * @return the removed attributes, if any, as a list; otherwise <code>null</code>
+     */
+    public List<EntryAttribute> removeAttributes( String... attributes )
+    {
+        if ( attributes.length == 0 )
+        {
+            return null;
+        }
+        
+        List<EntryAttribute> removed = new ArrayList<EntryAttribute>( attributes.length );
+        
+        for ( String attribute:attributes )
+        {
+            EntryAttribute attr = get( attribute );
+            
+            if ( attr != null )
+            {
+                removed.add( this.attributes.remove( attr.getId() ) );
+            }
+            else
+            {
+                String message = "The attribute '" + attribute + "' does not exist in the entry";
+                LOG.warn( message );
+                continue;
+            }
+        }
+        
+        if ( removed.size() == 0 )
+        {
+            return null;
+        }
+        else
+        {
+            return removed;
+        }
+    }
+
+
+    /**
+     * <p>
+     * Removes the specified binary values from an attribute.
+     * </p>
+     * <p>
+     * If at least one value is removed, this method returns <code>true</code>.
+     * </p>
+     * <p>
+     * If there is no more value after having removed the values, the attribute
+     * will be removed too.
+     * </p>
+     * <p>
+     * If the attribute does not exist, nothing is done and the method returns 
+     * <code>false</code>
+     * </p> 
+     *
+     * @param upId The attribute ID  
+     * @param values the values to be removed
+     * @return <code>true</code> if at least a value is removed, <code>false</code>
+     * if not all the values have been removed or if the attribute does not exist. 
+     */
+    public boolean remove( String upId, byte[]... values ) throws NamingException
+    {
+        try
+        {
+            String id = getId( upId );
+            
+            EntryAttribute attribute = get( id );
+            
+            if ( attribute == null )
+            {
+                // Can't remove values from a not existing attribute !
+                return false;
+            }
+            
+            int nbOldValues = attribute.size();
+            
+            // Remove the values
+            attribute.remove( values );
+            
+            if ( attribute.size() == 0 )
+            {
+                // No mare values, remove the attribute
+                attributes.remove( id );
+                
+                return true;
+            }
+            
+            if ( nbOldValues != attribute.size() )
+            {
+                // At least one value have been removed, return true.
+                return true;
+            }
+            else
+            {
+                // No values have been removed, return false.
+                return false;
+            }
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            LOG.error( "The removal of values for the missing '{}' attribute is not possible", upId );
+            return false;
+        }
+    }
+
+
+    /**
+     * <p>
+     * Removes the specified String values from an attribute.
+     * </p>
+     * <p>
+     * If at least one value is removed, this method returns <code>true</code>.
+     * </p>
+     * <p>
+     * If there is no more value after having removed the values, the attribute
+     * will be removed too.
+     * </p>
+     * <p>
+     * If the attribute does not exist, nothing is done and the method returns 
+     * <code>false</code>
+     * </p> 
+     *
+     * @param upId The attribute ID  
+     * @param attributes the attributes to be removed
+     * @return <code>true</code> if at least a value is removed, <code>false</code>
+     * if not all the values have been removed or if the attribute does not exist. 
+     */
+    public boolean remove( String upId, String... values ) throws NamingException
+    {
+        try
+        {
+            String id = getId( upId );
+            
+            EntryAttribute attribute = get( id );
+            
+            if ( attribute == null )
+            {
+                // Can't remove values from a not existing attribute !
+                return false;
+            }
+            
+            int nbOldValues = attribute.size();
+            
+            // Remove the values
+            attribute.remove( values );
+            
+            if ( attribute.size() == 0 )
+            {
+                // No mare values, remove the attribute
+                attributes.remove( id );
+                
+                return true;
+            }
+            
+            if ( nbOldValues != attribute.size() )
+            {
+                // At least one value have been removed, return true.
+                return true;
+            }
+            else
+            {
+                // No values have been removed, return false.
+                return false;
+            }
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            LOG.error( "The removal of values for the missing '{}' attribute is not possible", upId );
+            return false;
+        }
+    }
+
+
+    /**
+     * <p>
+     * Removes the specified values from an attribute.
+     * </p>
+     * <p>
+     * If at least one value is removed, this method returns <code>true</code>.
+     * </p>
+     * <p>
+     * If there is no more value after having removed the values, the attribute
+     * will be removed too.
+     * </p>
+     * <p>
+     * If the attribute does not exist, nothing is done and the method returns 
+     * <code>false</code>
+     * </p> 
+     *
+     * @param upId The attribute ID  
+     * @param attributes the attributes to be removed
+     * @return <code>true</code> if at least a value is removed, <code>false</code>
+     * if not all the values have been removed or if the attribute does not exist. 
+     */
+    public boolean remove( String upId, Value<?>... values ) throws NamingException
+    {
+        try
+        {
+            String id = getId( upId );
+            
+            EntryAttribute attribute = get( id );
+            
+            if ( attribute == null )
+            {
+                // Can't remove values from a not existing attribute !
+                return false;
+            }
+            
+            int nbOldValues = attribute.size();
+            
+            // Remove the values
+            attribute.remove( values );
+            
+            if ( attribute.size() == 0 )
+            {
+                // No mare values, remove the attribute
+                attributes.remove( id );
+                
+                return true;
+            }
+            
+            if ( nbOldValues != attribute.size() )
+            {
+                // At least one value have been removed, return true.
+                return true;
+            }
+            else
+            {
+                // No values have been removed, return false.
+                return false;
+            }
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            LOG.error( "The removal of values for the missing '{}' attribute is not possible", upId );
+            return false;
+        }
+    }
+
+
+    public Iterator<EntryAttribute> iterator()
+    {
+        return Collections.unmodifiableMap( attributes ).values().iterator();
+    }
+
+
+    /**
+     * @see Externalizable#writeExternal(ObjectOutput)<p>
+     * 
+     * This is the place where we serialize entries, and all theirs
+     * elements. the reason why we don't call the underlying methods
+     * (<code>ClientAttribute.write(), Value.write()</code>) is that we need
+     * access to the registries to read back the values.
+     * <p>
+     * The structure used to store the entry is the following :
+     * <li><b>[DN length]</b> : can be -1 if we don't have a DN, 0 if the 
+     * DN is empty, otherwise contains the DN's length.<p> 
+     * <b>NOTE :</b>This should be unnecessary, as the DN should always exists
+     * <p>
+     * </li>
+     * <li>
+     * <b>DN</b> : The entry's DN. Can be empty (rootDSE=<p>
+     * </li>
+     * We have to store the UPid, and all the values, if any.
+     */
+    public void writeExternal( ObjectOutput out ) throws IOException
+    {
+        // First, the DN
+        if ( dn == null )
+        {
+            // Write an empty DN
+            LdapDN.EMPTY_LDAPDN.writeExternal( out );
+        }
+        else
+        {
+            // Write the DN
+            out.writeObject( dn );
+        }
+        
+        // Then the attributes. 
+        if ( attributes == null )
+        {
+            // A negative number denotes no attributes
+            out.writeInt( -1 );
+        }
+        else
+        {
+            // Store the attributes' nulber first
+            out.writeInt( attributes.size() );
+            
+            // Iterate through the keys. We store the Attribute
+            // here, to be able to restore it in the readExternal :
+            // we need access to the registries, which are not available
+            // in the ClientAttribute class.
+            for ( EntryAttribute attribute:attributes.values() )
+            {
+                // Store the UP id
+                out.writeUTF( attribute.getUpId() );
+                
+                // The number of values
+                int nbValues = attribute.size();
+                
+                if ( nbValues == 0 ) 
+                {
+                    out.writeInt( 0 );
+                }
+                else 
+                {
+                    out.writeInt( nbValues );
+
+                    for ( Value<?> value:attribute )
+                    {
+                        out.writeObject( value );
+                    }
+                }
+            }
+        }
+        
+        out.flush();
+    }
+
+    
+    /**
+     * @see Externalizable#readExternal(ObjectInput)
+     */
+    public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
+    {
+        // Read the DN
+        LdapDN dn = (LdapDN)in.readObject();
+        
+        // Read the number of attributes
+        int nbAttributes = in.readInt();
+        
+        attributes = new HashMap<String, EntryAttribute>();
+
+        // Read the attributes
+        for ( int i = 0; i < nbAttributes; i++ )
+        {
+            String upId = in.readUTF();
+            
+            EntryAttribute attribute = new DefaultClientAttribute( upId );
+            
+            // Read the number of values
+            int nbValues = in.readInt();
+            
+            for ( int j = 0; j < nbValues; j++ )
+            {
+                Value<?> value = (Value<?>)in.readObject();
+                attribute.add( value );
+            }
+            
+            attributes.put( attribute.getId(), attribute );
+        }
+    }
+    
+    
+    /**
+    * Get the hashcode of this ClientEntry.
+    *
+    * @see java.lang.Object#hashCode()
+     */
+    public int hashCode()
+    {
+        int result = 37;
+        
+        result = result*17 + dn.hashCode();
+        
+        for ( EntryAttribute attribute:attributes.values() )
+        {
+            result = result*17 + attribute.hashCode();
+        }
+
+        return result;
+    }
+
+    
+    /**
+     * Tells if an entry has a specific ObjectClass value
+     * 
+     * @param objectClass The ObjectClass we want to check
+     * @return <code>true</code> if the ObjectClass value is present 
+     * in the ObjectClass attribute
+     */
+    public boolean hasObjectClass( String objectClass )
+    {
+        return contains( "objectclass", objectClass );
+    }
+
+
+    /**
+     * @see Object#equals(Object)
+     */
+    public boolean equals( Object o )
+    {
+        // Short circuit
+
+        if ( this == o )
+        {
+            return true;
+        }
+        
+        if ( ! ( o instanceof DefaultClientEntry ) )
+        {
+            return false;
+        }
+        
+        DefaultClientEntry other = (DefaultClientEntry)o;
+        
+        // Both DN must be equal
+        if ( dn == null )
+        {
+            if ( other.getDn() != null )
+            {
+                return false;
+            }
+        }
+        else
+        {
+            if ( !dn.equals( other.getDn() ) )
+            {
+                return false;
+            }
+        }
+        
+        // They must have the same number of attributes
+        if ( size() != other.size() )
+        {
+            return false;
+        }
+        
+        // Each attribute must be equal
+        for ( EntryAttribute attribute:other )
+        {
+            if ( !attribute.equals( this.get( attribute.getId() ) ) )
+            {
+                return false;
+            }
+        }
+        
+        return true;
+    }
+        
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder();
+        
+        sb.append( "ClientEntry\n" );
+        sb.append( "    dn: " ).append( dn ).append( '\n' );
+        
+        // First dump the ObjectClass attribute
+        if ( containsAttribute( "objectClass" ) )
+        {
+            EntryAttribute objectClass = get( "objectclass" );
+            
+            sb.append( objectClass );
+        }
+        
+        if ( attributes.size() != 0 )
+        {
+            for ( EntryAttribute attribute:attributes.values() )
+            {
+                if ( !attribute.getId().equals( "objectclass" ) )
+                {
+                    sb.append( attribute );
+                }
+            }
+        }
+        
+        return sb.toString();
+    }
+}

Modified: directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/entry/client/DefaultClientAttributeTest.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/entry/client/DefaultClientAttributeTest.java?rev=643046&r1=643045&r2=643046&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/entry/client/DefaultClientAttributeTest.java (original)
+++ directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/entry/client/DefaultClientAttributeTest.java Mon Mar 31 09:19:45 2008
@@ -537,6 +537,15 @@
         assertFalse( attr6.isHR() );
         assertTrue( attr6.contains( new byte[]{'a'} ) );
         assertTrue( attr6.contains( (byte[])null ) );
+        
+        EntryAttribute attr7 = new DefaultClientAttribute( "test" );
+        
+        attr7.add( "a", "b" );
+        assertEquals( 2, attr7.size() );
+        
+        assertEquals( 1, attr7.add( "b", "c" ) );
+        assertEquals( 3, attr7.size() );
+        assertTrue( attr7.contains( "a", "b", "c" ) );
     }