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

svn commit: r638228 [4/20] - in /directory/sandbox/akarasulu/bigbang/apacheds: ./ apacheds-xbean-spring/src/site/ benchmarks/src/site/ bootstrap-extract/src/site/ bootstrap-partition/src/site/ bootstrap-plugin/src/main/java/org/apache/directory/server/...

Modified: directory/sandbox/akarasulu/bigbang/apacheds/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerEntryUtils.java
URL: http://svn.apache.org/viewvc/directory/sandbox/akarasulu/bigbang/apacheds/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerEntryUtils.java?rev=638228&r1=638227&r2=638228&view=diff
==============================================================================
--- directory/sandbox/akarasulu/bigbang/apacheds/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerEntryUtils.java (original)
+++ directory/sandbox/akarasulu/bigbang/apacheds/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerEntryUtils.java Mon Mar 17 23:12:41 2008
@@ -18,7 +18,12 @@
  */
 package org.apache.directory.server.core.entry;
 
+import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Set;
 
 import javax.naming.NamingEnumeration;
 import javax.naming.NamingException;
@@ -26,15 +31,23 @@
 import javax.naming.directory.Attributes;
 import javax.naming.directory.BasicAttribute;
 import javax.naming.directory.BasicAttributes;
-import javax.naming.directory.DirContext;
 import javax.naming.directory.InvalidAttributeIdentifierException;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchResult;
 
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
 import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.entry.Value;
 import org.apache.directory.shared.ldap.message.AttributeImpl;
 import org.apache.directory.shared.ldap.message.AttributesImpl;
 import org.apache.directory.shared.ldap.message.ModificationItemImpl;
 import org.apache.directory.shared.ldap.name.LdapDN;
 import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.util.EmptyEnumeration;
+import org.apache.directory.shared.ldap.util.StringTools;
 
 /**
  * A helper class used to manipulate Entries, Attributes and Values.
@@ -62,13 +75,23 @@
 
         for ( AttributeType attributeType:entry.getAttributeTypes() )
         {
-            Attribute attribute = new AttributeImpl( attributeType.getName() );
-            
             ServerAttribute attr = entry.get( attributeType );
             
-            for ( Iterator<ServerValue<?>> iter = attr.iterator(); iter.hasNext();)
+            // Deal with a special case : an entry without any ObjectClass
+            if ( attributeType.getOid() == SchemaConstants.OBJECT_CLASS_AT_OID )
+            {
+                if ( attr.size() == 0 )
+                {
+                    // We don't have any objectClass, just dismiss this element
+                    continue;
+                }
+            }
+            
+            Attribute attribute = new AttributeImpl( attributeType.getName() );
+            
+            for ( Iterator<Value<?>> iter = attr.iterator(); iter.hasNext();)
             {
-                ServerValue<?> value = iter.next();
+                Value<?> value = iter.next();
                 attribute.add( value.get() );
             }
             
@@ -105,17 +128,35 @@
             {
                 Object value = values.nextElement();
                 
-                if ( value instanceof String )
+                if ( serverAttribute.isHR() )
                 {
-                    serverAttribute.add( (String)value );
-                }
-                else if ( value instanceof byte[] )
-                {
-                    serverAttribute.add( (byte[])value );
+                    if ( value instanceof String )
+                    {
+                        serverAttribute.add( (String)value );
+                    }
+                    else if ( value instanceof byte[] )
+                    {
+                        serverAttribute.add( StringTools.utf8ToString( (byte[])value ) );
+                    }
+                    else
+                    {
+                        return null;
+                    }
                 }
                 else
                 {
-                    return null;
+                    if ( value instanceof String )
+                    {
+                        serverAttribute.add( StringTools.getBytesUtf8( (String)value ) );
+                    }
+                    else if ( value instanceof byte[] )
+                    {
+                        serverAttribute.add( (byte[])value );
+                    }
+                    else
+                    {
+                        return null;
+                    }
                 }
             }
             
@@ -151,7 +192,11 @@
                 {
                     Attribute attr = attrs.nextElement();
 
-                    AttributeType attributeType = registries.getAttributeTypeRegistry().lookup( attr.getID() );
+                    String attributeId = attr.getID();
+                    String id = stripOptions( attributeId );
+                    Set<String> options = getOptions( attributeId );
+                    // TODO : handle options.
+                    AttributeType attributeType = registries.getAttributeTypeRegistry().lookup( id );
                     ServerAttribute serverAttribute = ServerEntryUtils.toServerAttribute( attr, attributeType );
                     
                     if ( serverAttribute != null )
@@ -191,9 +236,9 @@
             
             ServerAttribute attr = entry.get( attributeType );
             
-            for ( Iterator<ServerValue<?>> iter = attr.iterator(); iter.hasNext();)
+            for ( Iterator<Value<?>> iter = attr.iterator(); iter.hasNext();)
             {
-                ServerValue<?> value = iter.next();
+                Value<?> value = iter.next();
                 attribute.add( value );
             }
             
@@ -213,7 +258,7 @@
     {
         Attribute attribute = new BasicAttribute( attr.getUpId(), false );
 
-        for ( ServerValue<?> value:attr )
+        for ( Value<?> value:attr )
         {
             attribute.add( value.get() );
         }
@@ -231,7 +276,7 @@
     {
         Attribute attribute = new AttributeImpl( attr.getUpId() );
 
-        for ( ServerValue<?> value:attr )
+        for ( Value<?> value:attr )
         {
             attribute.add( value.get() );
         }
@@ -249,21 +294,21 @@
      * @return the resultant entry after the modification has taken place
      * @throws NamingException if there are problems accessing attributes
      */
-    public static ServerEntry getTargetEntry( ModificationItemImpl mod, ServerEntry entry, Registries registries ) throws NamingException
+    public static ServerEntry getTargetEntry( Modification mod, ServerEntry entry, Registries registries ) throws NamingException
     {
         ServerEntry targetEntry = ( ServerEntry ) entry.clone();
-        int modOp = mod.getModificationOp();
-        String id = mod.getAttribute().getID();
+        ModificationOperation modOp = mod.getOperation();
+        String id = mod.getAttribute().getId();
         AttributeType attributeType = registries.getAttributeTypeRegistry().lookup( id );
         
         switch ( modOp )
         {
-            case ( DirContext.REPLACE_ATTRIBUTE  ):
-                targetEntry.put( toServerAttribute( mod.getAttribute(), attributeType ) );
+            case REPLACE_ATTRIBUTE :
+                targetEntry.put( (ServerAttribute)mod.getAttribute() );
                 break;
                 
-            case ( DirContext.REMOVE_ATTRIBUTE  ):
-                ServerAttribute toBeRemoved = toServerAttribute( mod.getAttribute(), attributeType );
+            case REMOVE_ATTRIBUTE :
+                ServerAttribute toBeRemoved = (ServerAttribute)mod.getAttribute();
 
                 if ( toBeRemoved.size() == 0 )
                 {
@@ -275,7 +320,7 @@
 
                     if ( existing != null )
                     {
-                        for ( ServerValue<?> value:toBeRemoved )
+                        for ( Value<?> value:toBeRemoved )
                         {
                             existing.remove( value );
                         }
@@ -283,20 +328,20 @@
                 }
                 break;
                 
-            case ( DirContext.ADD_ATTRIBUTE  ):
+            case ADD_ATTRIBUTE :
                 ServerAttribute combined = new DefaultServerAttribute( id, attributeType );
-                ServerAttribute toBeAdded = toServerAttribute( mod.getAttribute(), attributeType );
+                ServerAttribute toBeAdded = (ServerAttribute)mod.getAttribute();
                 ServerAttribute existing = entry.get( id );
 
                 if ( existing != null )
                 {
-                    for ( ServerValue<?> value:existing )
+                    for ( Value<?> value:existing )
                     {
                         combined.add( value );
                     }
                 }
 
-                for ( ServerValue<?> value:toBeAdded )
+                for ( Value<?> value:toBeAdded )
                 {
                     combined.add( value );
                 }
@@ -309,5 +354,326 @@
         }
 
         return targetEntry;
+    }
+
+
+    /**
+     * Creates a new attribute which contains the values representing the union
+     * of two attributes. If one attribute is null then the resultant attribute
+     * returned is a copy of the non-null attribute. If both are null then we
+     * cannot determine the attribute ID and an {@link IllegalArgumentException}
+     * is raised.
+     * 
+     * @param attr0 the first attribute
+     * @param attr1 the second attribute
+     * @return a new attribute with the union of values from both attribute
+     *         arguments
+     * @throws NamingException if there are problems accessing attribute values
+     */
+    public static ServerAttribute getUnion( ServerAttribute attr0, ServerAttribute attr1 ) throws NamingException
+    {
+        if ( attr0 == null && attr1 == null )
+        {
+            throw new IllegalArgumentException( "Cannot figure out attribute ID if both args are null" );
+        }
+        else if ( attr0 == null )
+        {
+            return (ServerAttribute)attr1.clone();
+        }
+        else if ( attr1 == null )
+        {
+            return (ServerAttribute)attr0.clone();
+        }
+        else if ( !attr0.getAttributeType().equals( attr1.getAttributeType() ) )
+        {
+            throw new IllegalArgumentException( "Cannot take union of attributes with different IDs!" );
+        }
+
+        ServerAttribute attr = (ServerAttribute)attr0.clone();
+
+        if ( attr0 != null )
+        {
+            for ( Value<?> value:attr1 )
+            {
+                attr.add( value );
+            }
+        }
+
+        return attr;
+    }
+    
+    
+    public static ModificationItemImpl toModificationItemImpl( Modification modification )
+    {
+        ModificationItemImpl modificationItem = new ModificationItemImpl( 
+            modification.getOperation().getValue(),
+            toAttributeImpl( (ServerAttribute)modification.getAttribute() ) ); 
+        
+        return modificationItem;
+        
+    }
+
+
+    public static Modification toModification( ModificationItemImpl modificationImpl, AttributeType attributeType ) throws InvalidAttributeIdentifierException
+    {
+        Modification modification = new ServerModification( 
+            modificationImpl.getModificationOp(),
+            ServerEntryUtils.toServerAttribute( modificationImpl.getAttribute(), attributeType ) ); 
+        
+        return modification;
+        
+    }
+
+
+    public static List<ModificationItemImpl> toModificationItemImpl( List<Modification> modifications )
+    {
+        if ( modifications != null )
+        {
+            List<ModificationItemImpl> modificationItems = new ArrayList<ModificationItemImpl>();
+
+            for ( Modification modification: modifications )
+            {
+                modificationItems.add( toModificationItemImpl( modification ) );
+            }
+        
+            return modificationItems;
+        }
+        else
+        {
+            return null;
+        }
+    }
+    
+    
+    public static List<Modification> toServerModification( List<ModificationItemImpl> modificationImpls, AttributeTypeRegistry atRegistry )
+        throws NamingException
+    {
+        if ( modificationImpls != null )
+        {
+            List<Modification> modifications = new ArrayList<Modification>();
+
+            for ( ModificationItemImpl modificationImpl: modificationImpls )
+            {
+                AttributeType attributeType = atRegistry.lookup( modificationImpl.getAttribute().getID() );
+                modifications.add( toModification( modificationImpl, attributeType ) );
+            }
+        
+            return modifications;
+        }
+        else
+        {
+            return null;
+        }
+    }
+    
+    
+    public static List<Modification> toServerModification( ModificationItem[] modifications, AttributeTypeRegistry atRegistry )
+    throws NamingException
+    {
+	    if ( modifications != null )
+	    {
+	        List<Modification> modificationsList = new ArrayList<Modification>();
+	
+	        for ( ModificationItem modification: modifications )
+	        {
+	            String attributeId = modification.getAttribute().getID();
+                String id = stripOptions( attributeId );
+	            Set<String> options = getOptions( attributeId );
+	            // TODO : handle options
+	            AttributeType attributeType = atRegistry.lookup( id );
+	            modificationsList.add( toModification( (ModificationItemImpl)modification, attributeType ) );
+	        }
+	    
+	        return modificationsList;
+	    }
+	    else
+	    {
+	        return null;
+	    }
+	}
+
+
+    /**
+     * Utility method to extract a modification item from an array of modifications.
+     * 
+     * @param mods the array of ModificationItems to extract the Attribute from.
+     * @param type the attributeType spec of the Attribute to extract
+     * @return the modification item on the attributeType specified
+     */
+    public static final Modification getModificationItem( List<Modification> mods, AttributeType type )
+    {
+        for ( Modification modification:mods )
+        {
+            ServerAttribute attribute = (ServerAttribute)modification.getAttribute();
+            
+            if ( attribute.getAttributeType() == type )
+            {
+                return modification;
+            }
+        }
+        
+        return null;
+    }
+    
+    
+    /**
+     * Utility method to extract an attribute from a list of modifications.
+     * 
+     * @param mods the list of ModificationItems to extract the Attribute from.
+     * @param type the attributeType spec of the Attribute to extract
+     * @return the extract Attribute or null if no such attribute exists
+     */
+    public static ServerAttribute getAttribute( List<Modification> mods, AttributeType type )
+    {
+        Modification mod = getModificationItem( mods, type );
+        
+        if ( mod != null )
+        {
+            return (ServerAttribute)mod.getAttribute();
+        }
+        
+        return null;
+    }
+    
+    
+    /**
+     * Encapsulate a ServerSearchResult enumeration into a SearchResult enumeration
+     * @param result The ServerSearchResult enumeration
+     * @return A SearchResultEnumeration
+     */
+    public static NamingEnumeration<SearchResult> toSearchResultEnum( final NamingEnumeration<ServerSearchResult> result )
+    {
+    	if ( result instanceof EmptyEnumeration<?> )
+    	{
+    		return new EmptyEnumeration<SearchResult>();
+    	}
+    	
+    	return new NamingEnumeration<SearchResult> ()
+    	{
+    	    public void close() throws NamingException
+    	    {
+    	        result.close();
+    	    }
+
+
+    	    /**
+    	     * @see javax.naming.NamingEnumeration#hasMore()
+    	     */
+    	    public boolean hasMore() throws NamingException
+    	    {
+    	        return result.hasMore();
+    	    }
+
+
+    	    /**
+    	     * @see javax.naming.NamingEnumeration#next()
+    	     */
+    	    public SearchResult next() throws NamingException
+    	    {
+    	        ServerSearchResult rec = result.next();
+    	        
+    	        SearchResult searchResult = new SearchResult( 
+    	        		rec.getDn().getUpName(), 
+    	        		rec.getObject(), 
+    	        		toAttributesImpl( rec.getServerEntry() ), 
+    	        		rec.isRelative() );
+    	        
+    	        return searchResult;
+    	    }
+    	    
+    	    
+    	    /**
+    	     * @see java.util.Enumeration#hasMoreElements()
+    	     */
+    	    public boolean hasMoreElements()
+    	    {
+    	        return result.hasMoreElements();
+    	    }
+
+
+    	    /**
+    	     * @see java.util.Enumeration#nextElement()
+    	     */
+    	    public SearchResult nextElement()
+    	    {
+    	    	try
+    	    	{
+	    	    	ServerSearchResult rec = result.next();
+	
+	    	        SearchResult searchResult = new SearchResult( 
+	    	        		rec.getDn().getUpName(), 
+	    	        		rec.getObject(), 
+	    	        		toAttributesImpl( rec.getServerEntry() ), 
+	    	        		rec.isRelative() );
+	    	        
+	    	        return searchResult;
+    	    	}
+    	    	catch ( NamingException ne )
+    	    	{
+    	            NoSuchElementException nsee = 
+    	                new NoSuchElementException( "Encountered NamingException on underlying enumeration." );
+    	            nsee.initCause( ne );
+    	            throw nsee;
+    	    	}
+    	    }
+    	};
+    }
+    
+    
+    /**
+     * Remove the options from the attributeType, and returns the ID.
+     * 
+     * RFC 4512 :
+     * attributedescription = attributetype options
+     * attributetype = oid
+     * options = *( SEMI option )
+     * option = 1*keychar
+     */
+    private static String stripOptions( String attributeId )
+    {
+        int optionsPos = attributeId.indexOf( ";" ); 
+        
+        if ( optionsPos != -1 )
+        {
+            return attributeId.substring( 0, optionsPos );
+        }
+        else
+        {
+            return attributeId;
+        }
+    }
+    
+    /**
+     * Get the options from the attributeType.
+     * 
+     * For instance, given :
+     * jpegphoto;binary;lang=jp
+     * 
+     * your get back a set containing { "binary", "lang=jp" }
+     */
+    private static Set<String> getOptions( String attributeId )
+    {
+        int optionsPos = attributeId.indexOf( ";" ); 
+
+        if ( optionsPos != -1 )
+        {
+            Set<String> options = new HashSet<String>();
+            
+            String[] res = attributeId.substring( optionsPos + 1 ).split( ";" );
+            
+            for ( String option:res )
+            {
+                if ( !StringTools.isEmpty( option ) )
+                {
+                    options.add( option );
+                }
+            }
+            
+            return options;
+        }
+        else
+        {
+            return null;
+        }
     }
 }

Modified: directory/sandbox/akarasulu/bigbang/apacheds/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerModification.java
URL: http://svn.apache.org/viewvc/directory/sandbox/akarasulu/bigbang/apacheds/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerModification.java?rev=638228&r1=638227&r2=638228&view=diff
==============================================================================
--- directory/sandbox/akarasulu/bigbang/apacheds/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerModification.java (original)
+++ directory/sandbox/akarasulu/bigbang/apacheds/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerModification.java Mon Mar 17 23:12:41 2008
@@ -19,10 +19,9 @@
  */
 package org.apache.directory.server.core.entry;
 
-import java.io.Serializable;
-
 import javax.naming.directory.DirContext;
 
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
 import org.apache.directory.shared.ldap.entry.Modification;
 import org.apache.directory.shared.ldap.entry.ModificationOperation;
 
@@ -33,7 +32,7 @@
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  * @version $Rev$, $Date$
  */
-public class ServerModification<T extends ServerAttribute> implements Modification<T>, Serializable
+public class ServerModification implements Modification
 {
     public static final long serialVersionUID = 1L;
     
@@ -41,9 +40,23 @@
     private ModificationOperation operation;
     
     /** The attribute which contains the modification */
-    private T attribute;
+    private ServerAttribute attribute;
  
     
+    public ServerModification( ModificationOperation operation, ServerAttribute attribute )
+    {
+        this.operation = operation;
+        this.attribute = attribute;
+    }
+    
+    
+    public ServerModification( int operation, ServerAttribute attribute )
+    {
+        setOperation( operation );
+        this.attribute = attribute;
+    }
+    
+    
     /**
      *  @return the operation
      */
@@ -64,12 +77,15 @@
         {
             case DirContext.ADD_ATTRIBUTE :
                 this.operation = ModificationOperation.ADD_ATTRIBUTE;
+                break;
 
             case DirContext.REPLACE_ATTRIBUTE :
                 this.operation = ModificationOperation.REPLACE_ATTRIBUTE;
+                break;
             
             case DirContext.REMOVE_ATTRIBUTE :
                 this.operation = ModificationOperation.REMOVE_ATTRIBUTE;
+                break;
         }
     }
 
@@ -88,7 +104,7 @@
     /**
      * @return the attribute containing the modifications
      */
-    public T getAttribute()
+    public EntryAttribute getAttribute()
     {
         return attribute;
     }
@@ -99,9 +115,9 @@
      *
      * @param attribute The modified attribute 
      */
-    public void setAttribute( T attribute )
+    public void setAttribute( EntryAttribute attribute )
     {
-        this.attribute = attribute;
+        this.attribute = (ServerAttribute)attribute;
     }
     
     
@@ -118,6 +134,21 @@
         return h;
     }
     
+    
+    public ServerModification clone()
+    {
+        try
+        {
+            ServerModification clone = (ServerModification)super.clone();
+            
+            clone.attribute = (ServerAttribute)this.attribute.clone();
+            return clone;
+        }
+        catch ( CloneNotSupportedException cnse )
+        {
+            return null;
+        }
+    }
     
     /**
      * @see Object#toString()

Modified: directory/sandbox/akarasulu/bigbang/apacheds/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerStringValue.java
URL: http://svn.apache.org/viewvc/directory/sandbox/akarasulu/bigbang/apacheds/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerStringValue.java?rev=638228&r1=638227&r2=638228&view=diff
==============================================================================
--- directory/sandbox/akarasulu/bigbang/apacheds/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerStringValue.java (original)
+++ directory/sandbox/akarasulu/bigbang/apacheds/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerStringValue.java Mon Mar 17 23:12:41 2008
@@ -20,7 +20,8 @@
 
 
 import org.apache.directory.shared.ldap.NotImplementedException;
-import org.apache.directory.shared.ldap.entry.AbstractStringValue;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
 import org.apache.directory.shared.ldap.schema.AttributeType;
 import org.apache.directory.shared.ldap.schema.MatchingRule;
 import org.apache.directory.shared.ldap.schema.Normalizer;
@@ -45,7 +46,7 @@
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  * @version $Rev$, $Date$
  */
-public class ServerStringValue extends AbstractStringValue implements ServerValue<String>, Externalizable
+public class ServerStringValue extends ClientStringValue
 {
     /** Used for serialization */
     public static final long serialVersionUID = 2L;
@@ -56,13 +57,50 @@
     /** reference to the attributeType which is not serialized */
     private transient AttributeType attributeType;
 
-    /** the canonical representation of the wrapped String value */
-    private String normalizedValue;
 
-    /** cached results of the isValid() method call */
-    private transient Boolean valid;
+    // -----------------------------------------------------------------------
+    // utility methods
+    // -----------------------------------------------------------------------
+    /**
+     * Utility method to get some logs if an assert fails
+     */
+    protected String logAssert( String message )
+    {
+        LOG.error(  message );
+        return message;
+    }
 
+    
+    /**
+     *  Check the attributeType member. It should not be null, 
+     *  and it should contains a syntax.
+     */
+    protected String checkAttributeType( AttributeType attributeType )
+    {
+        try
+        {
+            if ( attributeType == null )
+            {
+                return "The AttributeType parameter should not be null";
+            }
+            
+            if ( attributeType.getSyntax() == null )
+            {
+                return "There is no Syntax associated with this attributeType";
+            }
 
+            return null;
+        }
+        catch ( NamingException ne )
+        {
+            return "This AttributeType is incorrect";
+        }
+    }
+
+    
+    // -----------------------------------------------------------------------
+    // Constructors
+    // -----------------------------------------------------------------------
     /**
      * Creates a ServerStringValue without an initial wrapped value.
      *
@@ -70,7 +108,8 @@
      */
     public ServerStringValue( AttributeType attributeType )
     {
-        assert checkAttributeType( attributeType) == null : logAssert( checkAttributeType( attributeType ) );
+        super();
+        assert checkAttributeType( attributeType ) == null : logAssert( checkAttributeType( attributeType ) );
 
         try
         {
@@ -98,36 +137,42 @@
     public ServerStringValue( AttributeType attributeType, String wrapped )
     {
         this( attributeType );
-        super.set( wrapped );
+        this.wrapped = wrapped;
     }
 
 
-    // -----------------------------------------------------------------------
-    // Value<String> Methods
-    // -----------------------------------------------------------------------
-
-
     /**
-     * Sets the wrapped String value.  Has the side effect of setting the
-     * normalizedValue and the valid flags to null if the wrapped value is
-     * different than what is already set.  These cached values must be
-     * recomputed to be correct with different values.
+     * Creates a ServerStringValue with an initial wrapped String value and
+     * a normalized value.
      *
-     * @see ServerValue#set(Object)
+     * @param attributeType the schema type associated with this ServerStringValue
+     * @param wrapped the value to wrap which can be null
+     * @param normalizedValue the normalized value
      */
-    public final void set( String wrapped )
+    /** No protection */ ServerStringValue( AttributeType attributeType, String wrapped, String normalizedValue, boolean valid )
     {
-        // Why should we invalidate the normalized value if it's we're setting the
-        // wrapper to it's current value?
-        if ( ( wrapped != null ) && wrapped.equals( get() ) )
-        {
-            return;
-        }
+        super( wrapped );
+        this.normalized = true;
+        this.attributeType = attributeType;
+        this.normalizedValue = normalizedValue;
+        this.valid = valid;
+    }
+
 
-        normalizedValue = null;
-        valid = null;
-        super.set( wrapped );
+    // -----------------------------------------------------------------------
+    // Value<String> Methods, overloaded
+    // -----------------------------------------------------------------------
+    /**
+     * @return a copy of the current value
+     */
+    public ServerStringValue clone()
+    {
+        ServerStringValue clone = (ServerStringValue)super.clone();
+        
+        return clone;
     }
+    
+    
 
 
     // -----------------------------------------------------------------------
@@ -141,17 +186,26 @@
      */
     public void normalize() throws NamingException
     {
+        // If the value is already normalized, get out.
+        if ( normalized )
+        {
+            return;
+        }
+        
         Normalizer normalizer = getNormalizer();
 
         if ( normalizer == null )
         {
-            normalizedValue = get();
+            normalizedValue = wrapped;
         }
         else
         {
-            normalizedValue = ( String ) normalizer.normalize( get() );
+            normalizedValue = ( String ) normalizer.normalize( wrapped );
         }
+
+        normalized = true;
     }
+    
 
     /**
      * Gets the normalized (canonical) representation for the wrapped string.
@@ -164,16 +218,26 @@
      * @return gets the normalized value
      * @throws NamingException if the value cannot be properly normalized
      */
-    public String getNormalized() throws NamingException
+    public String getNormalizedValue() 
     {
         if ( isNull() )
         {
+            normalized = true;
             return null;
         }
 
-        if ( normalizedValue == null )
+        if ( !normalized )
         {
-            normalize();
+            try
+            {
+                normalize();
+            }
+            catch ( NamingException ne )
+            {
+                String message = "Cannot normalize the value :" + ne.getMessage();
+                LOG.warn( message );
+                normalized = false;
+            }
         }
 
         return normalizedValue;
@@ -187,26 +251,36 @@
      * change. Syntax checks only result on the first check, and when the wrapped
      * value changes.
      *
-     * @see ServerValue#isValid()
+     * @see Value<T>#isValid()
      */
-    public final boolean isValid() throws NamingException
+    public final boolean isValid()
     {
         if ( valid != null )
         {
             return valid;
         }
 
-        valid = attributeType.getSyntax().getSyntaxChecker().isValidSyntax( get() );
+        try
+        {
+            valid = attributeType.getSyntax().getSyntaxChecker().isValidSyntax( get() );
+        }
+        catch ( NamingException ne )
+        {
+            String message = "Cannot check the syntax : " + ne.getMessage();
+            LOG.error( message );
+            valid = false;
+        }
+        
         return valid;
     }
 
 
     /**
-     * @see ServerValue#compareTo(ServerValue)
+     * @see Value<T>#compareTo(ServerValue)
      * @throws IllegalStateException on failures to extract the comparator, or the
      * normalizers needed to perform the required comparisons based on the schema
      */
-    public int compareTo( ServerValue<String> value )
+    public int compareTo( Value<String> value )
     {
         if ( isNull() )
         {
@@ -227,23 +301,52 @@
         if ( value instanceof ServerStringValue )
         {
             ServerStringValue stringValue = ( ServerStringValue ) value;
+            
+            // Normalizes the compared value
+            try
+            {
+                stringValue.normalize();
+            }
+            catch ( NamingException ne )
+            {
+                String message = "Cannot normalize the wrapped value " + stringValue; 
+                LOG.error( message );
+            }
+            
+            // Normalizes the value
+            try
+            {
+                normalize();
+            }
+            catch ( NamingException ne )
+            {
+                String message = "Cannot normalize the wrapped value " + this;
+                LOG.error( message );
+            }
+
             try
             {
                 //noinspection unchecked
-                return getComparator().compare( getNormalized(), stringValue.getNormalized() );
+                return getComparator().compare( getNormalizedValue(), stringValue.getNormalizedValue() );
             }
             catch ( NamingException e )
             {
-                String msg = "Failed to compare normalized values for " + get() + " and " + value;
+                String msg = "Failed to compare normalized values for " + this + " and " + value;
                 LOG.error( msg, e );
                 throw new IllegalStateException( msg, e );
             }
         }
 
-        throw new NotImplementedException( "I don't know what to do if value is not a ServerStringValue" );
+        String message = "I don't know what to do if value is not a ServerStringValue";
+        LOG.error( message );
+        throw new NotImplementedException( message );
     }
 
 
+    /**
+     * Get the associated AttributeType
+     * @return The AttributeType
+     */
     public AttributeType getAttributeType()
     {
         return attributeType;
@@ -251,7 +354,15 @@
 
 
     /**
-     * @see ServerValue#instanceOf(AttributeType)
+     * Check if the value is stored into an instance of the given 
+     * AttributeType, or one of its ascendant.
+     * 
+     * For instance, if the Value is associated with a CommonName,
+     * checking for Name will match.
+     * 
+     * @param attributeType The AttributeType we are looking at
+     * @return <code>true</code> if the value is associated with the given
+     * attributeType or one of its ascendant
      */
     public boolean instanceOf( AttributeType attributeType ) throws NamingException
     {
@@ -267,40 +378,16 @@
     // -----------------------------------------------------------------------
     // Object Methods
     // -----------------------------------------------------------------------
-
-
-    /**
-     * @see Object#hashCode()
-     * @throws IllegalStateException on failures to extract the comparator, or the
-     * normalizers needed to perform the required comparisons based on the schema
-     */
-    public int hashCode()
-    {
-        // return zero if the value is null so only one null value can be
-        // stored in an attribute - the binary version does the same 
-        if ( isNull() )
-        {
-            return 0;
-        }
-
-        try
-        {
-            return getNormalized().hashCode();
-        }
-        catch ( NamingException e )
-        {
-            String msg = "Failed to normalize \"" + get() + "\" while trying to get hashCode()";
-            LOG.error( msg, e );
-            throw new IllegalStateException( msg, e );
-        }
-    }
-
-
     /**
      * Checks to see if this ServerStringValue equals the supplied object.
      *
      * This equals implementation overrides the StringValue implementation which
      * is not schema aware.
+     * 
+     * Two ServerStringValues are equal if they have the same AttributeType,
+     * they are both null, their value are equal or their normalized value 
+     * are equal. If the AttributeType has a comparator, we use it to
+     * compare both values.
      * @throws IllegalStateException on failures to extract the comparator, or the
      * normalizers needed to perform the required comparisons based on the schema
      */
@@ -310,7 +397,7 @@
         {
             return true;
         }
-
+        
         if ( ! ( obj instanceof ServerStringValue ) )
         {
             return false;
@@ -318,27 +405,44 @@
 
         ServerStringValue other = ( ServerStringValue ) obj;
         
-        if ( isNull() && other.isNull() )
+        if ( !attributeType.equals( other.attributeType ) )
         {
-            return true;
+            return false;
         }
-
-        if ( isNull() != other.isNull() )
+        
+        if ( isNull() )
         {
-            return false;
+            return other.isNull();
         }
 
-        // now unlike regular values we have to compare the normalized values
-        try
+        // Shortcut : compare the values without normalization
+        // If they are equal, we may avoid a normalization.
+        // Note : if two values are equal, then their normalized
+        // value are equal too if their attributeType are equal. 
+        if ( get().equals( other.get() ) )
         {
-            return getNormalized().equals( other.getNormalized() );
+            return true;
         }
-        catch ( NamingException e )
+        else 
         {
-            String msg = "Failed to normalize while testing for equality on String values: \"";
-            msg += get() + "\"" + " and \"" + other.get() + "\"" ;
-            LOG.error( msg, e );
-            throw new IllegalStateException( msg, e );
+            try
+            {
+                Comparator<String> comparator = getComparator();
+
+                // Compare normalized values
+                if ( comparator == null )
+                {
+                    return getNormalizedValue().equals( other.getNormalizedValue() );
+                }
+                else
+                {
+                    return comparator.compare( getNormalizedValue(), other.getNormalizedValue() ) == 0;
+                }
+            }
+            catch ( NamingException ne )
+            {
+                return false;
+            }
         }
     }
 
@@ -396,6 +500,39 @@
 
 
     /**
+     * Implement the hashCode method.
+     * 
+     * @see Object#hashCode()
+     * @throws IllegalStateException on failures to extract the comparator, or the
+     * normalizers needed to perform the required comparisons based on the schema
+     */
+    public int hashCode()
+    {
+        // return the OID hashcode if the value is null. 
+        if ( isNull() )
+        {
+            return attributeType.getOid().hashCode();
+        }
+
+        // If the normalized value is null, will default to wrapped
+        // which cannot be null at this point.
+        int h = 17;
+        
+        String normalized = getNormalizedValue();
+        
+        if ( normalized != null )
+        {
+            h = h*37 + normalized.hashCode();
+        }
+        
+        // Add the OID hashcode
+        h = h*37 + attributeType.getOid().hashCode();
+        
+        return h;
+    }
+
+    
+    /**
      * Gets a comparator using getMatchingRule() to resolve the matching
      * that the comparator is extracted from.
      *
@@ -416,22 +553,6 @@
     
     
     /**
-     * @return a copy of the current value
-     */
-    public ServerStringValue clone()
-    {
-        try
-        {
-            return (ServerStringValue)super.clone();
-        }
-        catch ( CloneNotSupportedException cnse )
-        {
-            return null;
-        }
-    }
-    
-    
-    /**
      * @see Externalizable#writeExternal(ObjectOutput)
      * 
      * We will write the value and the normalized value, only
@@ -448,7 +569,16 @@
         {
             out.writeUTF( get() );
             
-            if ( normalizedValue.equals( get() ) )
+            try
+            {
+                normalize();
+            }
+            catch ( NamingException ne )
+            {
+                normalizedValue = null;
+            }
+            
+            if ( get().equals( normalizedValue ) )
             {
                 // If the normalized value is equal to the UP value,
                 // don't save it
@@ -486,6 +616,11 @@
             {
                 // In this case, the normalized value is equal to the UP value
                 normalizedValue = wrapped;
+                setNormalized( true );
+            }
+            else
+            {
+                setNormalized( false );
             }
         }
     }