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 2006/01/06 00:18:02 UTC

svn commit: r366335 - in /directory/trunk/ldap-common/src: main/java/org/apache/ldap/common/codec/search/controls/ test/java/org/apache/ldap/common/codec/search/controls/

Author: elecharny
Date: Thu Jan  5 15:17:52 2006
New Revision: 366335

URL: http://svn.apache.org/viewcvs?rev=366335&view=rev
Log:
- Fixed the grammar
- Added some checks
- Fixed the encoder
- Added some testCases

Added:
    directory/trunk/ldap-common/src/test/java/org/apache/ldap/common/codec/search/controls/EntryChangeControlTest.java
Modified:
    directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/codec/search/controls/EntryChangeControl.java
    directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/codec/search/controls/EntryChangeControlGrammar.java
    directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/codec/search/controls/EntryChangeControlStatesEnum.java

Modified: directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/codec/search/controls/EntryChangeControl.java
URL: http://svn.apache.org/viewcvs/directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/codec/search/controls/EntryChangeControl.java?rev=366335&r1=366334&r2=366335&view=diff
==============================================================================
--- directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/codec/search/controls/EntryChangeControl.java (original)
+++ directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/codec/search/controls/EntryChangeControl.java Thu Jan  5 15:17:52 2006
@@ -98,19 +98,26 @@
      */
     public int computeLength()
     {
-        int changeTypesLength = 1 + 1 + Value.getNbBytes( changeType.getValue() );
+        int changeTypesLength = 1 + 1 + 1;
+
         int previousDnLength = 0;
         int changeNumberLength = 0;
         
-        if ( previousDn != null ) previousDnLength += previousDn.getNbBytes();
-        if ( changeNumber != UNDEFINED_CHANGE_NUMBER ) changeNumberLength += Value.getNbBytes( changeNumber );
+        if ( previousDn != null ) 
+        {
+            previousDnLength = 1 + Length.getNbBytes( previousDn.getNbBytes() ) + previousDn.getNbBytes();
+        }
+
+        if ( changeNumber != UNDEFINED_CHANGE_NUMBER ) 
+        {
+            changeNumberLength = 1 + 1 + Value.getNbBytes( changeNumber );
+        }
 
         eccSeqLength = changeTypesLength + previousDnLength + changeNumberLength;
         
         return  1 + Length.getNbBytes( eccSeqLength ) + eccSeqLength;
     }
 
-
     /**
      * Encodes the entry change control.
      * 
@@ -125,7 +132,10 @@
         bb.put( UniversalTag.SEQUENCE_TAG );
         bb.put( Length.getBytes( eccSeqLength ) );
 
-        Value.encode( bb, changeType.getValue() );
+        bb.put( UniversalTag.ENUMERATED_TAG );
+        bb.put( (byte)1 );
+        bb.put( Value.getBytes( changeType.getValue() ) );
+        
         if ( previousDn != null )
         {
             Value.encode( bb, previousDn.getBytes() );
@@ -174,7 +184,7 @@
     
     public String getPreviousDn()
     {
-        return previousDn.getString();
+    	return previousDn == null ? "" : previousDn.getString();
     }
     
     

Modified: directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/codec/search/controls/EntryChangeControlGrammar.java
URL: http://svn.apache.org/viewcvs/directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/codec/search/controls/EntryChangeControlGrammar.java?rev=366335&r1=366334&r2=366335&view=diff
==============================================================================
--- directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/codec/search/controls/EntryChangeControlGrammar.java (original)
+++ directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/codec/search/controls/EntryChangeControlGrammar.java Thu Jan  5 15:17:52 2006
@@ -17,6 +17,8 @@
 package org.apache.ldap.common.codec.search.controls;
 
 
+import javax.naming.InvalidNameException;
+
 import org.apache.asn1.ber.IAsn1Container;
 import org.apache.asn1.ber.grammar.AbstractGrammar;
 import org.apache.asn1.ber.grammar.GrammarAction;
@@ -27,9 +29,8 @@
 import org.apache.asn1.codec.DecoderException;
 import org.apache.asn1.util.IntegerDecoder;
 import org.apache.asn1.util.IntegerDecoderException;
-import org.apache.ldap.common.codec.LdapStatesEnum;
-import org.apache.ldap.common.codec.util.LdapString;
-import org.apache.ldap.common.codec.util.LdapStringEncodingException;
+import org.apache.ldap.common.codec.util.LdapDN;
+import org.apache.ldap.common.util.StringTools;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -60,10 +61,22 @@
         // Create the transitions table
         super.transitions = new GrammarTransition[EntryChangeControlStatesEnum.LAST_EC_STATE][256];
 
+        //============================================================================================
+        // Entry Change Control
+        //============================================================================================
+        // EntryChangeNotification ::= SEQUENCE { (Tag)
+        //    ...
+        // Nothing to do
         super.transitions[EntryChangeControlStatesEnum.EC_SEQUENCE_TAG][UniversalTag.SEQUENCE_TAG] =
             new GrammarTransition( EntryChangeControlStatesEnum.EC_SEQUENCE_TAG,
                 EntryChangeControlStatesEnum.EC_SEQUENCE_VALUE, null );
 
+        //============================================================================================
+        // Entry Change Control
+        //============================================================================================
+        // EntryChangeNotification ::= SEQUENCE { (Value)
+        //    ...
+        // Initialization of the structure
         super.transitions[EntryChangeControlStatesEnum.EC_SEQUENCE_VALUE][UniversalTag.SEQUENCE_TAG] =
             new GrammarTransition( EntryChangeControlStatesEnum.EC_SEQUENCE_VALUE,
                 EntryChangeControlStatesEnum.CHANGE_TYPE_TAG, 
@@ -71,34 +84,67 @@
                 {
                     public void action( IAsn1Container container ) 
                     {
-                        EntryChangeControlContainer EntryChangeContainer = ( EntryChangeControlContainer ) container;
+                        EntryChangeControlContainer entryChangeContainer = ( EntryChangeControlContainer ) container;
                         EntryChangeControl control = new EntryChangeControl();
-                        EntryChangeContainer.setEntryChangeControl( control );
+                        entryChangeContainer.setEntryChangeControl( control );
                     }
                 }
             );
 
+        //============================================================================================
+        // Change Type
+        //============================================================================================
+        // EntryChangeNotification ::= SEQUENCE {
+        //    changeType ENUMERATED { (Tag) },
+        //    ...
+        //
+        // Nothing to do
         super.transitions[EntryChangeControlStatesEnum.CHANGE_TYPE_TAG][UniversalTag.ENUMERATED_TAG] =
             new GrammarTransition( EntryChangeControlStatesEnum.CHANGE_TYPE_TAG,
                 EntryChangeControlStatesEnum.CHANGE_TYPE_VALUE, null );
 
+        //============================================================================================
+        // Change Type
+        //============================================================================================
+        // EntryChangeNotification ::= SEQUENCE {
+        //    changeType ENUMERATED { (Value)  },
+        //    ...
+        //
+        // Evaluates the changeType
+        
+        // Action associated with the ChangeType transition
         GrammarAction setChangeTypeAction = new GrammarAction( "Set EntryChangeControl changeType" )
         {
             public void action( IAsn1Container container ) throws DecoderException
             {
-                EntryChangeControlContainer EntryChangeContainer = ( EntryChangeControlContainer ) container;
-                Value value = EntryChangeContainer.getCurrentTLV().getValue();
+                EntryChangeControlContainer entryChangeContainer = ( EntryChangeControlContainer ) container;
+                Value value = entryChangeContainer.getCurrentTLV().getValue();
                 
                 try
                 {
-                    ChangeType changeType = ChangeType.getChangeType( IntegerDecoder.parse( value ) );
-                    
-                    if ( log.isDebugEnabled() )
-                    {
-                        log.debug( "changeType = " + changeType );
-                    }
-                    
-                    EntryChangeContainer.getEntryChangeControl().setChangeType( changeType );
+                	int change = IntegerDecoder.parse( value, 1, 8 );
+                	
+                	switch ( change )
+                	{
+	                	case ChangeType.ADD_VALUE :
+	                	case ChangeType.DELETE_VALUE :
+	                	case ChangeType.MODDN_VALUE :
+	                	case ChangeType.MODIFY_VALUE :
+	                        ChangeType changeType = ChangeType.getChangeType( change );
+	                        
+	                        if ( log.isDebugEnabled() )
+	                        {
+	                            log.debug( "changeType = " + changeType );
+	                        }
+	                        
+	                        entryChangeContainer.getEntryChangeControl().setChangeType( changeType );
+	                        break;
+
+	                	default :
+	                        String msg = "failed to decode the changeType for EntryChangeControl";
+	                		log.error( msg );
+	                    	throw new DecoderException( msg );
+                	}
                 }
                 catch ( IntegerDecoderException e )
                 {
@@ -109,96 +155,147 @@
             }
         };
         
-        // transition for when we have a previousDN value
-        super.transitions[EntryChangeControlStatesEnum.CHANGE_TYPE_VALUE][UniversalTag.ENUMERATED_TAG] =
-            new GrammarTransition( EntryChangeControlStatesEnum.CHANGE_TYPE_VALUE, 
-                EntryChangeControlStatesEnum.PREVIOUS_DN_TAG, setChangeTypeAction );
-
-        // transition for when we do not have a previousDN value but we do have a changeNumber
-        super.transitions[EntryChangeControlStatesEnum.CHANGE_TYPE_VALUE][UniversalTag.ENUMERATED_TAG] =
-            new GrammarTransition( EntryChangeControlStatesEnum.CHANGE_TYPE_VALUE, 
-                EntryChangeControlStatesEnum.CHANGE_NUMBER_TAG, setChangeTypeAction );
-        
-        // transition for when we do not have a previousDN value nor do we have a changeNumber
+        // ChangeType Transition 
         super.transitions[EntryChangeControlStatesEnum.CHANGE_TYPE_VALUE][UniversalTag.ENUMERATED_TAG] =
             new GrammarTransition( EntryChangeControlStatesEnum.CHANGE_TYPE_VALUE, 
-                EntryChangeControlStatesEnum.GRAMMAR_END, setChangeTypeAction );
+                EntryChangeControlStatesEnum.CHANGE_NUMBER_OR_PREVIOUS_DN_TAG, setChangeTypeAction );
 
-        super.transitions[EntryChangeControlStatesEnum.PREVIOUS_DN_TAG][UniversalTag.OCTET_STRING_TAG] =
-            new GrammarTransition( EntryChangeControlStatesEnum.PREVIOUS_DN_TAG,
+        //============================================================================================
+        // Previous DN (We have a OCTET_STRING Tag)
+        //============================================================================================
+        // EntryChangeNotification ::= SEQUENCE {
+        //    ...
+        //    previousDN   LDAPDN OPTIONAL, (Tag)
+        //    ...
+        //
+        // Nothing to do
+        super.transitions[EntryChangeControlStatesEnum.CHANGE_NUMBER_OR_PREVIOUS_DN_TAG][UniversalTag.OCTET_STRING_TAG] =
+            new GrammarTransition( EntryChangeControlStatesEnum.CHANGE_NUMBER_OR_PREVIOUS_DN_TAG,
                 EntryChangeControlStatesEnum.PREVIOUS_DN_VALUE, null );
 
+        //============================================================================================
+        // Previous DN 
+        //============================================================================================
+        // EntryChangeNotification ::= SEQUENCE {
+        //    ...
+        //    previousDN   LDAPDN OPTIONAL, (Value)
+        //    ...
+        //
+        // Set the previousDN into the structure. We first check that it's a valid DN
+
+        // Action associated with the PreviousDN transition
         GrammarAction setPreviousDnAction = new GrammarAction( "Set EntryChangeControl previousDN" )
         {
             public void action( IAsn1Container container ) throws DecoderException
             {
-                EntryChangeControlContainer EntryChangeContainer = ( EntryChangeControlContainer ) container;
-                Value value = EntryChangeContainer.getCurrentTLV().getValue();
-                LdapString previousDn;
-                try
-                {
-                    previousDn = new LdapString( value.getData() );
-                }
-                catch ( LdapStringEncodingException e )
+                EntryChangeControlContainer entryChangeContainer = ( EntryChangeControlContainer ) container;
+                
+                ChangeType changeType = entryChangeContainer.getEntryChangeControl().getChangeType();
+                
+                if ( changeType != ChangeType.MODDN )
                 {
-                    throw new DecoderException( "failed to encode string data" );
+                	log.error( "The previousDN field should not contain anything if the changeType is not MODDN" );
+                	throw new DecoderException( "Previous DN is not allowed for this change type" );
                 }
-                
-                if ( log.isDebugEnabled() )
+                else
                 {
-                    log.debug( "previousDN = " + previousDn );
+	                Value value = entryChangeContainer.getCurrentTLV().getValue();
+	                String previousDn;
+	                
+	                try
+	                {
+	                	previousDn = StringTools.utf8ToString( value.getData() );
+	                    new LdapDN( previousDn );
+	                }
+	                catch ( InvalidNameException ine )
+	                {
+	                	log.error( "Bad Previous DN : '" + StringTools.dumpBytes( value.getData() ) ); 
+	                    throw new DecoderException( "failed to decode the previous DN" );
+	                }
+	                
+	                if ( log.isDebugEnabled() )
+	                {
+	                    log.debug( "previousDN = " + previousDn );
+	                }
+	                
+	                entryChangeContainer.getEntryChangeControl().setPreviousDn( previousDn );
                 }
-                
-                EntryChangeContainer.getEntryChangeControl().setPreviousDn( previousDn );
             }
         };
 
-        // transition if we do have an optional changeNumber after this previousDN
+        // PreviousDN transition 
         super.transitions[EntryChangeControlStatesEnum.PREVIOUS_DN_VALUE][UniversalTag.OCTET_STRING_TAG] =
             new GrammarTransition( EntryChangeControlStatesEnum.PREVIOUS_DN_VALUE, 
                 EntryChangeControlStatesEnum.CHANGE_NUMBER_TAG, setPreviousDnAction );
 
-        // transition if we do *NOT* have an optional changeNumber after this previousDN
-        super.transitions[EntryChangeControlStatesEnum.PREVIOUS_DN_VALUE][UniversalTag.OCTET_STRING_TAG] =
-            new GrammarTransition( EntryChangeControlStatesEnum.PREVIOUS_DN_VALUE, 
-                EntryChangeControlStatesEnum.GRAMMAR_END, setPreviousDnAction );
+        //============================================================================================
+        // Change Number from Change Type 
+        //============================================================================================
+        // EntryChangeNotification ::= SEQUENCE {
+        //    ...
+        // changeNumber INTEGER OPTIONAL (Tag)
+        // }
+        //
+        // Nothing to do
+        super.transitions[EntryChangeControlStatesEnum.CHANGE_NUMBER_OR_PREVIOUS_DN_TAG][UniversalTag.INTEGER_TAG] =
+            new GrammarTransition( EntryChangeControlStatesEnum.CHANGE_NUMBER_OR_PREVIOUS_DN_TAG,
+                EntryChangeControlStatesEnum.CHANGE_NUMBER_VALUE, null );
 
-        // transition for processing changeNumber
+        //============================================================================================
+        // Change Number from PreviousDN
+        //============================================================================================
+        // EntryChangeNotification ::= SEQUENCE {
+        //    ...
+        // changeNumber INTEGER OPTIONAL (Tag)
+        // }
+        //
+        // Nothing to do
         super.transitions[EntryChangeControlStatesEnum.CHANGE_NUMBER_TAG][UniversalTag.INTEGER_TAG] =
             new GrammarTransition( EntryChangeControlStatesEnum.CHANGE_NUMBER_TAG,
                 EntryChangeControlStatesEnum.CHANGE_NUMBER_VALUE, null );
 
-        // transition to finish grammar and set the changeNumber
-        super.transitions[EntryChangeControlStatesEnum.CHANGE_NUMBER_VALUE][UniversalTag.INTEGER_TAG] =
-            new GrammarTransition( EntryChangeControlStatesEnum.CHANGE_NUMBER_VALUE, 
-                LdapStatesEnum.GRAMMAR_END, 
-                new GrammarAction( "Set EntryChangeControl changeNumber" )
+        //============================================================================================
+        // Change Number
+        //============================================================================================
+        // EntryChangeNotification ::= SEQUENCE {
+        //    ...
+        // changeNumber INTEGER OPTIONAL (Value)
+        // }
+        //
+        // Set the changeNumber into the structure
+        
+        // Change Number action
+        GrammarAction setChangeNumberAction = new GrammarAction( "Set EntryChangeControl changeNumber" )
+        {
+            public void action( IAsn1Container container ) throws DecoderException
+            {
+                EntryChangeControlContainer entryChangeContainer = ( EntryChangeControlContainer ) container;
+                Value value = entryChangeContainer.getCurrentTLV().getValue();
+                
+                try
                 {
-                    public void action( IAsn1Container container ) throws DecoderException
+                    int changeNumber = IntegerDecoder.parse( value );
+                    
+                    if ( log.isDebugEnabled() )
                     {
-                        EntryChangeControlContainer EntryChangeContainer = ( EntryChangeControlContainer ) container;
-                        Value value = EntryChangeContainer.getCurrentTLV().getValue();
-                        
-                        try
-                        {
-                            int changeNumber = IntegerDecoder.parse( value );
-                            
-                            if ( log.isDebugEnabled() )
-                            {
-                                log.debug( "changeNumber = " + changeNumber );
-                            }
-                            
-                            EntryChangeContainer.getEntryChangeControl().setChangeNumber( changeNumber );
-                        }
-                        catch ( IntegerDecoderException e )
-                        {
-                            String msg = "failed to decode the changeNumber for EntryChangeControl";
-                            log.error( msg, e );
-                            throw new DecoderException( msg );
-                        }
+                        log.debug( "changeNumber = " + changeNumber );
                     }
+                    
+                    entryChangeContainer.getEntryChangeControl().setChangeNumber( changeNumber );
                 }
-            );
+                catch ( IntegerDecoderException e )
+                {
+                    String msg = "failed to decode the changeNumber for EntryChangeControl";
+                    log.error( msg, e );
+                    throw new DecoderException( msg );
+                }
+            }
+        };
+
+        // Transition
+        super.transitions[EntryChangeControlStatesEnum.CHANGE_NUMBER_VALUE][UniversalTag.INTEGER_TAG] =
+            new GrammarTransition( EntryChangeControlStatesEnum.CHANGE_NUMBER_VALUE,
+                EntryChangeControlStatesEnum.GRAMMAR_END, setChangeNumberAction );
     }
 
     /**

Modified: directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/codec/search/controls/EntryChangeControlStatesEnum.java
URL: http://svn.apache.org/viewcvs/directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/codec/search/controls/EntryChangeControlStatesEnum.java?rev=366335&r1=366334&r2=366335&view=diff
==============================================================================
--- directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/codec/search/controls/EntryChangeControlStatesEnum.java (original)
+++ directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/codec/search/controls/EntryChangeControlStatesEnum.java Thu Jan  5 15:17:52 2006
@@ -48,7 +48,7 @@
     public static int CHANGE_TYPE_VALUE = 3;
 
     /** previousDN Tag */
-    public static int PREVIOUS_DN_TAG = 4;
+    public static int CHANGE_NUMBER_OR_PREVIOUS_DN_TAG = 4;
 
     /** previousDN Value */
     public static int PREVIOUS_DN_VALUE = 5;
@@ -90,7 +90,7 @@
         "EC_SEQUENCE_VALUE",
         "CHANGE_TYPE_TAG",
         "CHANGE_TYPE_VALUE",
-        "PREVIOUS_DN_TAG",
+        "CHANGE_NUMBER_OR_PREVIOUS_DN_TAG",
         "PREVIOUS_DN_VALUE",
         "CHANGE_NUMBER_TAG",
         "CHANGE_NUMBER_VALUE"

Added: directory/trunk/ldap-common/src/test/java/org/apache/ldap/common/codec/search/controls/EntryChangeControlTest.java
URL: http://svn.apache.org/viewcvs/directory/trunk/ldap-common/src/test/java/org/apache/ldap/common/codec/search/controls/EntryChangeControlTest.java?rev=366335&view=auto
==============================================================================
--- directory/trunk/ldap-common/src/test/java/org/apache/ldap/common/codec/search/controls/EntryChangeControlTest.java (added)
+++ directory/trunk/ldap-common/src/test/java/org/apache/ldap/common/codec/search/controls/EntryChangeControlTest.java Thu Jan  5 15:17:52 2006
@@ -0,0 +1,242 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed 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.ldap.common.codec.search.controls;
+
+import java.nio.ByteBuffer;
+
+import javax.naming.NamingException;
+
+import org.apache.asn1.codec.DecoderException;
+import org.apache.asn1.ber.Asn1Decoder;
+import org.apache.ldap.common.codec.LdapDecoder;
+import org.apache.ldap.common.util.StringTools;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+/**
+ * Test the EntryChangeControlTest codec
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class EntryChangeControlTest extends TestCase {
+    /**
+     * Test the decoding of a EntryChangeControl
+     */
+    public void testDecodeEntryChangeControlSuccess() throws NamingException
+    {
+        Asn1Decoder ldapDecoder = new LdapDecoder();
+        ByteBuffer bb = ByteBuffer.allocate( 0x0D );
+        bb.put( new byte[]
+            {
+                0x30, 0x0B, 				// EntryChangeNotification ::= SEQUENCE {
+				0x0A, 0x01, 0x08, 			//     changeType ENUMERATED {
+											//         modDN           (8)
+											//     }
+				0x04, 0x03, 'a', '=', 'b',	//     previousDN   LDAPDN OPTIONAL,     -- modifyDN ops. only
+                0x02, 0x01, 0x10    		//     changeNumber INTEGER OPTIONAL     -- if supported
+                                    		// }
+            } );
+        bb.flip();
+
+        EntryChangeControlContainer container = new EntryChangeControlContainer();
+        try
+        {
+            ldapDecoder.decode( bb, container );
+        }
+        catch ( DecoderException de )
+        {
+            de.printStackTrace();
+            Assert.fail( de.getMessage() );
+        }
+        
+        EntryChangeControl entryChange = container.getEntryChangeControl();
+        assertEquals( ChangeType.MODDN, entryChange.getChangeType() );
+        assertEquals( "a=b", entryChange.getPreviousDn() );
+        assertEquals( 16, entryChange.getChangeNumber() );
+    }
+
+    /**
+     * Test the decoding of a EntryChangeControl with a add
+     * and a change number
+     */
+    public void testDecodeEntryChangeControlWithADDAndChangeNumber() throws NamingException
+    {
+        Asn1Decoder ldapDecoder = new LdapDecoder();
+        ByteBuffer bb = ByteBuffer.allocate( 0x08 );
+        bb.put( new byte[]
+            {
+                0x30, 0x06, 				// EntryChangeNotification ::= SEQUENCE {
+				0x0A, 0x01, 0x01, 			//     changeType ENUMERATED {
+											//         Add           (1)
+											//     }
+                0x02, 0x01, 0x10    		//     changeNumber INTEGER OPTIONAL     -- if supported
+                                    		// }
+            } );
+        bb.flip();
+
+        EntryChangeControlContainer container = new EntryChangeControlContainer();
+        try
+        {
+            ldapDecoder.decode( bb, container );
+        }
+        catch ( DecoderException de )
+        {
+            de.printStackTrace();
+            Assert.fail( de.getMessage() );
+        }
+        
+        EntryChangeControl entryChange = container.getEntryChangeControl();
+        assertEquals( ChangeType.ADD, entryChange.getChangeType() );
+        assertEquals( "", entryChange.getPreviousDn() );
+        assertEquals( 16, entryChange.getChangeNumber() );
+    }
+
+    /**
+     * Test the decoding of a EntryChangeControl with a add
+     * so we should not have a PreviousDN
+     */
+    public void testDecodeEntryChangeControlWithADDAndPreviousDNBad() throws NamingException
+    {
+        Asn1Decoder ldapDecoder = new LdapDecoder();
+        ByteBuffer bb = ByteBuffer.allocate( 0x0D );
+        bb.put( new byte[]
+            {
+                0x30, 0x0B, 				// EntryChangeNotification ::= SEQUENCE {
+				0x0A, 0x01, 0x01, 			//     changeType ENUMERATED {
+											//         ADD           (1)
+											//     }
+				0x04, 0x03, 'a', '=', 'b',	//     previousDN   LDAPDN OPTIONAL,     -- modifyDN ops. only
+                0x02, 0x01, 0x10    		//     changeNumber INTEGER OPTIONAL     -- if supported
+                                    		// }
+            } );
+        bb.flip();
+
+        EntryChangeControlContainer container = new EntryChangeControlContainer();
+        
+        try
+        {
+            ldapDecoder.decode( bb, container );
+        }
+        catch ( DecoderException de )
+        {
+        	// We should fail, because we have a previousDN with a ADD
+        	assertTrue( true );
+        	return;
+        }
+        
+        Assert.fail( "A ADD operation should not have a PreviousDN" );
+    }
+
+    /**
+     * Test the decoding of a EntryChangeControl with a add
+     * and nothing else
+     */
+    public void testDecodeEntryChangeControlWithADD() throws NamingException
+    {
+        Asn1Decoder ldapDecoder = new LdapDecoder();
+        ByteBuffer bb = ByteBuffer.allocate( 0x05 );
+        bb.put( new byte[]
+            {
+                0x30, 0x03, 				// EntryChangeNotification ::= SEQUENCE {
+				0x0A, 0x01, 0x01, 			//     changeType ENUMERATED {
+											//         ADD           (1)
+											//     }
+                                    		// }
+            } );
+        bb.flip();
+
+        EntryChangeControlContainer container = new EntryChangeControlContainer();
+        
+        try
+        {
+            ldapDecoder.decode( bb, container );
+        }
+        catch ( DecoderException de )
+        {
+            de.printStackTrace();
+            Assert.fail( de.getMessage() );
+        }
+        
+        EntryChangeControl entryChange = container.getEntryChangeControl();
+        assertEquals( ChangeType.ADD, entryChange.getChangeType() );
+        assertEquals( "", entryChange.getPreviousDn() );
+        assertEquals( EntryChangeControl.UNDEFINED_CHANGE_NUMBER, entryChange.getChangeNumber() );
+    }
+
+    /**
+     * Test the decoding of a EntryChangeControl with a worng changeType
+     * and nothing else
+     */
+    public void testDecodeEntryChangeControlWithWrongChangeType() throws NamingException
+    {
+        Asn1Decoder ldapDecoder = new LdapDecoder();
+        ByteBuffer bb = ByteBuffer.allocate( 0x05 );
+        bb.put( new byte[]
+            {
+                0x30, 0x03, 				// EntryChangeNotification ::= SEQUENCE {
+				0x0A, 0x01, 0x03, 			//     changeType ENUMERATED {
+											//         BAD Change Type
+											//     }
+                                    		// }
+            } );
+        bb.flip();
+
+        EntryChangeControlContainer container = new EntryChangeControlContainer();
+        
+        try
+        {
+            ldapDecoder.decode( bb, container );
+        }
+        catch ( DecoderException de )
+        {
+        	// We should fail because the ChangeType is not known 
+        	assertTrue( true );
+        	return;
+        }
+        
+        Assert.fail( "The changeType is unknown" );
+    }
+
+    /**
+     * Test encoding of a EntryChangeControl.
+     */
+    public void testEncodeEntryChangeControl() throws Exception
+    {
+        ByteBuffer bb = ByteBuffer.allocate( 0x0D );
+        bb.put( new byte[]
+        {
+                0x30, 0x0B, 				// EntryChangeNotification ::= SEQUENCE {
+				0x0A, 0x01, 0x08, 			//     changeType ENUMERATED {
+											//         modDN           (8)
+											//     }
+				0x04, 0x03, 'a', '=', 'b',	//     previousDN   LDAPDN OPTIONAL,     -- modifyDN ops. only
+                0x02, 0x01, 0x10    		//     changeNumber INTEGER OPTIONAL     -- if supported
+        } );
+
+        String expected = StringTools.dumpBytes( bb.array() );
+        bb.flip();
+        
+        EntryChangeControl entry = new EntryChangeControl();
+        entry.setChangeType( ChangeType.MODDN);
+        entry.setChangeNumber( 16 );
+        entry.setPreviousDn( "a=b" );
+        bb = entry.encode( null );
+        String decoded = StringTools.dumpBytes( bb.array() );
+        assertEquals( expected, decoded );
+    }
+}