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 2026/05/13 14:27:12 UTC

(directory-ldap-api) branch master updated (14e9b4564 -> b1583e4a4)

This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/directory-ldap-api.git


    from 14e9b4564 Merge pull request #281 from apache/dependabot/maven/org.junit.platform-junit-platform-runner-1.14.4
     new 7fafde7fb Removed the last antlr parser: DN.
     new b1583e4a4 merged the pom

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 Jenkinsfile                                        |    3 +-
 distribution/pom.xml                               |   10 -
 distribution/src/main/assembly/anon.xml            |    1 -
 distribution/src/main/release/NOTICE               |    2 -
 .../src/main/release/licenses/antlr-LICENSE.txt    |   19 -
 .../java/org/apache/directory/api/i18n/I18n.java   |    8 +-
 .../apache/directory/api/i18n/errors.properties    |    8 +-
 .../directory/api/osgi/ApiLdapModelOsgiTest.java   |    4 +-
 .../apache/directory/api/osgi/ApiOsgiTestBase.java |    7 -
 .../model/name/{DnTest.java => DnTestInteg.java}   |   34 +-
 .../directory/api/ldap/model/name/NameUtil.java    |  293 +++
 .../api/ldap/model/name/SchemaAwareRdnTest.java    |   45 +-
 .../api/ldap/codec/bind/BindRequestPerfTest.java   |    2 +-
 ldap/model/pom.xml                                 |   55 -
 ldap/model/src/checkstyle/suppressions.xml         |    4 +-
 ldap/model/src/main/antlr/distinguishedName.g      | 1129 -----------
 .../directory/api/ldap/model/entry/Value.java      |    2 +-
 .../apache/directory/api/ldap/model/name/Ava.java  |   21 +-
 .../api/ldap/model/name/ComplexDnParser.java       |   86 -
 .../apache/directory/api/ldap/model/name/Dn.java   |  191 +-
 .../directory/api/ldap/model/name/DnParser.java    | 2063 ++++++++++++++++++++
 .../api/ldap/model/name/FastDnParser.java          |  671 -------
 .../apache/directory/api/ldap/model/name/Rdn.java  |   25 +-
 .../model/subtree/SubtreeSpecificationParser.java  |    5 +-
 .../api/ldap/model/name/ComplexDnParserTest.java   |  449 +++++
 .../api/ldap/model/name/DnParserTest.java          |   40 +-
 .../directory/api/ldap/model/name/NameUtil.java    |  293 +++
 .../{FastRdnParserTest.java => RdnParserTest.java} |    4 +-
 .../directory/api/ldap/model/name/RdnTest.java     |   18 +-
 ...stDnParserTest.java => SimpleDnParserTest.java} |  173 +-
 pom.xml                                            |   27 -
 .../appended-resources/supplemental-models.xml     |   40 -
 .../org/apache/directory/api/util/ParserUtil.java  |   52 +-
 .../org/apache/directory/api/util/Position.java    |   98 +
 34 files changed, 3562 insertions(+), 2320 deletions(-)
 delete mode 100644 distribution/src/main/release/licenses/antlr-LICENSE.txt
 rename integ/src/test/java/org/apache/directory/api/ldap/model/name/{DnTest.java => DnTestInteg.java} (98%)
 create mode 100644 integ/src/test/java/org/apache/directory/api/ldap/model/name/NameUtil.java
 delete mode 100644 ldap/model/src/main/antlr/distinguishedName.g
 delete mode 100644 ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/ComplexDnParser.java
 create mode 100644 ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/DnParser.java
 delete mode 100644 ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/FastDnParser.java
 create mode 100644 ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/ComplexDnParserTest.java
 create mode 100644 ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/NameUtil.java
 rename ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/{FastRdnParserTest.java => RdnParserTest.java} (99%)
 rename ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/{FastDnParserTest.java => SimpleDnParserTest.java} (76%)
 delete mode 100644 src/main/appended-resources/supplemental-models.xml


(directory-ldap-api) 01/02: Removed the last antlr parser: DN.

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/directory-ldap-api.git

commit 7fafde7fbb8385883a3e3cee674df9a1609cf4b9
Author: Emmanuel Lécharny <em...@worteks.com>
AuthorDate: Wed May 13 16:23:32 2026 +0200

    Removed the last antlr parser: DN.
---
 Jenkinsfile                                        |    3 +-
 distribution/pom.xml                               |   10 -
 distribution/src/main/assembly/anon.xml            |    1 -
 distribution/src/main/release/NOTICE               |    2 -
 .../src/main/release/licenses/antlr-LICENSE.txt    |   19 -
 .../java/org/apache/directory/api/i18n/I18n.java   |    8 +-
 .../apache/directory/api/i18n/errors.properties    |    8 +-
 .../directory/api/osgi/ApiLdapModelOsgiTest.java   |    4 +-
 .../apache/directory/api/osgi/ApiOsgiTestBase.java |    7 -
 .../model/name/{DnTest.java => DnTestInteg.java}   |   34 +-
 .../directory/api/ldap/model/name/NameUtil.java    |  293 +++
 .../api/ldap/model/name/SchemaAwareRdnTest.java    |   45 +-
 .../api/ldap/codec/bind/BindRequestPerfTest.java   |    2 +-
 ldap/model/pom.xml                                 |   55 -
 ldap/model/src/checkstyle/suppressions.xml         |    4 +-
 ldap/model/src/main/antlr/distinguishedName.g      | 1129 -----------
 .../directory/api/ldap/model/entry/Value.java      |    2 +-
 .../apache/directory/api/ldap/model/name/Ava.java  |   21 +-
 .../api/ldap/model/name/ComplexDnParser.java       |   86 -
 .../apache/directory/api/ldap/model/name/Dn.java   |  191 +-
 .../directory/api/ldap/model/name/DnParser.java    | 2063 ++++++++++++++++++++
 .../api/ldap/model/name/FastDnParser.java          |  671 -------
 .../apache/directory/api/ldap/model/name/Rdn.java  |   25 +-
 .../model/subtree/SubtreeSpecificationParser.java  |    5 +-
 .../api/ldap/model/name/ComplexDnParserTest.java   |  449 +++++
 .../api/ldap/model/name/DnParserTest.java          |   40 +-
 .../directory/api/ldap/model/name/NameUtil.java    |  293 +++
 .../{FastRdnParserTest.java => RdnParserTest.java} |    4 +-
 .../directory/api/ldap/model/name/RdnTest.java     |   18 +-
 ...stDnParserTest.java => SimpleDnParserTest.java} |  173 +-
 pom.xml                                            |   27 -
 .../appended-resources/supplemental-models.xml     |   40 -
 .../org/apache/directory/api/util/ParserUtil.java  |   52 +-
 .../org/apache/directory/api/util/Position.java    |  100 +-
 34 files changed, 3563 insertions(+), 2321 deletions(-)

diff --git a/Jenkinsfile b/Jenkinsfile
index ceacac5fd..560bca051 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -78,7 +78,7 @@ pipeline {
         sh 'mvn clean install'
       }
     }
-/** Suspended Java 21 and 25 build as we need to move to antlr4
+
     stage('Build JDK 21 Linux') {
       tools {
         jdk "jdk_21_latest"
@@ -102,6 +102,5 @@ pipeline {
         sh 'mvn clean install -Pserial -Djava.security.manager=allow'
       }
     }
-*/
   }
 }
diff --git a/distribution/pom.xml b/distribution/pom.xml
index ffd4ffa1a..48b7a5dea 100644
--- a/distribution/pom.xml
+++ b/distribution/pom.xml
@@ -75,11 +75,6 @@
       <artifactId>api-all</artifactId>
     </dependency>
 
-    <dependency>
-      <groupId>antlr</groupId>
-      <artifactId>antlr</artifactId>
-    </dependency>
-
     <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-collections4</artifactId>
@@ -123,11 +118,6 @@
                       <artifactId>slf4j-api</artifactId>
                     </artifactItem>
                     
-                    <artifactItem>
-                      <groupId>antlr</groupId>
-                      <artifactId>antlr</artifactId>
-                    </artifactItem>
-                    
                     <artifactItem>
                       <groupId>org.apache.commons</groupId>
                       <artifactId>commons-collections4</artifactId>
diff --git a/distribution/src/main/assembly/anon.xml b/distribution/src/main/assembly/anon.xml
index ff54f492d..c64cc35c8 100644
--- a/distribution/src/main/assembly/anon.xml
+++ b/distribution/src/main/assembly/anon.xml
@@ -41,7 +41,6 @@
         <include>org.apache.directory.api:api-util:jar:${project.version}</include>
 
         <!-- The external dependencies -->
-        <include>antlr:antlr:jar:${antlr.version}</include>
         <include>org.apache.commons:commons-collections4:jar:${commons.collections.version}</include>
         <include>org.slf4j:slf4j-api:jar:${slf4j.api.version}</include>
       </includes>
diff --git a/distribution/src/main/release/NOTICE b/distribution/src/main/release/NOTICE
index cc962e75c..f28971484 100644
--- a/distribution/src/main/release/NOTICE
+++ b/distribution/src/main/release/NOTICE
@@ -17,8 +17,6 @@ This product uses Apache Maven (https://maven.apache.org/).
 
 This product includes/uses Apache Mina (https://mina.apache.org/).
 
-This product includes/uses software, AntLR Parser Generator (http://www.antlr.org/).
-
 This product uses software, JUnit (http://junit.org),
 developed by JUnit (http://www.junit.org)
 
diff --git a/distribution/src/main/release/licenses/antlr-LICENSE.txt b/distribution/src/main/release/licenses/antlr-LICENSE.txt
deleted file mode 100644
index afe00dc63..000000000
--- a/distribution/src/main/release/licenses/antlr-LICENSE.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-ANTLR 2 (http://www.antlr2.org/license.html) :
-
-ANTLR 2 License
-
-We reserve no legal rights to the ANTLR--it is fully in the public domain. An 
-individual or company may do whatever they wish with source code distributed 
-with ANTLR or the code generated by ANTLR, including the incorporation of ANTLR, 
-or its output, into commerical software.
-
-We encourage users to develop software with ANTLR. However, we do ask that 
-credit is given to us for developing ANTLR. By "credit", we mean that if you 
-use ANTLR or incorporate any source code into one of your programs (commercial 
-product, research project, or otherwise) that you acknowledge this fact 
-somewhere in the documentation, research report, etc... If you like ANTLR and 
-have developed a nice tool with the output, please mention that you developed it 
-using ANTLR. In addition, we ask that the headers remain intact in our source 
-code. As long as these guidelines are kept, we expect to continue enhancing this 
-system and expect to make other tools available as they are completed. 
-
diff --git a/i18n/src/main/java/org/apache/directory/api/i18n/I18n.java b/i18n/src/main/java/org/apache/directory/api/i18n/I18n.java
index f5a746fd9..643eafad6 100644
--- a/i18n/src/main/java/org/apache/directory/api/i18n/I18n.java
+++ b/i18n/src/main/java/org/apache/directory/api/i18n/I18n.java
@@ -750,7 +750,13 @@ public enum I18n
     ERR_13627_NEGATIVE_NUMBER_OF_RDNS( "ERR_13627_NEGATIVE_NUMBER_OF_RDNS" ),
     ERR_13628_NEGATIVE_NUMBER_OF_AVAS( "ERR_13628_NEGATIVE_NUMBER_OF_AVAS" ),
     ERR_13629_NO_ATTRIBUTE_TYPE( "ERR_13629_NO_ATTRIBUTE_TYPE" ),
-
+    ERR_13630_BAD_OID_ATTRIBUTE_TYPE( "ERR_13630_BAD_OID_ATTRIBUTE_TYPE" ),
+    ERR_13631_WRONG_ESCAPED_SEQUENCE( "ERR_13631_WRONG_ESCAPED_SEQUENCE" ),
+    ERR_13632_WRONG_HEX_PAIR( "ERR_13632_WRONG_HEX_PAIR" ),
+    ERR_13633_INVALID_UTFMB( "ERR_13633_INVALID_UTFMB" ),
+    ERR_13634_INCOMPLETE_UTFMB( "ERR_13634_INCOMPLETE_UTFMB"),
+    ERR_13635_INVALID_VALUE_CHARACTER( "ERR_13635_INVALID_VALUE_CHARACTER" ),
+    
     //     schema                           13700 - 13899
     ERR_13700_CANNOT_MODIFY_LOCKED_SCHEMA_OBJECT( "ERR_13700_CANNOT_MODIFY_LOCKED_SCHEMA_OBJECT" ),
     ERR_13701_CANNOT_MODIFY_AT_REGISTRY_COPY( "ERR_13701_CANNOT_MODIFY_AT_REGISTRY_COPY" ),
diff --git a/i18n/src/main/resources/org/apache/directory/api/i18n/errors.properties b/i18n/src/main/resources/org/apache/directory/api/i18n/errors.properties
index 28bb5f0ee..d65168152 100644
--- a/i18n/src/main/resources/org/apache/directory/api/i18n/errors.properties
+++ b/i18n/src/main/resources/org/apache/directory/api/i18n/errors.properties
@@ -468,7 +468,6 @@ ERR_11008_INVALID_DN=The TriggerSpecifciation operation requires a valid DN, got
 ERR_11009_MISSING_AFTER=The TriggerSpecifciation operation requires a time action AFTER, got ''{0}''
 ERR_11010_MISSING_MODIFY_DN_OPERATION=The TriggerSpecifciation ModififyDN operation requires either ''export'', ''import'' or ''rename'' , got ''{0}''
 
-
 # api-ldap-extra-utils <> 12000-12999
 ERR_12000_CANNOT_PROCESS_EMPTY_DN=Cannot process an empty Dn
 ERR_12001_CANNOT_ADD_NODE_CHILD_EXISTS=Cannot add a node to a node already having an element
@@ -741,6 +740,13 @@ ERR_13626_INVALID_RDN_DUPLICATE_AVA=Invalid RDN: the {0} is already present in t
 ERR_13627_NEGATIVE_NUMBER_OF_RDNS=The number of RDNs is negative
 ERR_13628_NEGATIVE_NUMBER_OF_AVAS=The number of AVAs is negative
 ERR_13629_NO_ATTRIBUTE_TYPE=The AttributeType is not present
+ERR_13630_BAD_OID_ATTRIBUTE_TYPE=The parsed attributeType is incorrect, we should have an OID at {0}
+ERR_13631_WRONG_ESCAPED_SEQUENCE=This value contains some invalid escaped sequence
+ERR_13632_WRONG_HEX_PAIR=This value contains some invalid hex pair, it should have 2 consecutive hex chars
+ERR_13633_INVALID_UTFMB=The UTFMB byte sequence {0} is invalid
+ERR_13634_INCOMPLETE_UTFMB=The UTFMB byte sequence is missing a mandatory byte
+ERR_13635_INVALID_VALUE_CHARACTER=The Value character ''{0}'' is invalid
+
 
 # api-ldap-model schema          13700-13899
 ERR_13700_CANNOT_MODIFY_LOCKED_SCHEMA_OBJECT=Cannot modify the SchemaObject {0}, it has been locked
diff --git a/integ-osgi/src/test/java/org/apache/directory/api/osgi/ApiLdapModelOsgiTest.java b/integ-osgi/src/test/java/org/apache/directory/api/osgi/ApiLdapModelOsgiTest.java
index a8a3ecfbc..c9b9e0647 100644
--- a/integ-osgi/src/test/java/org/apache/directory/api/osgi/ApiLdapModelOsgiTest.java
+++ b/integ-osgi/src/test/java/org/apache/directory/api/osgi/ApiLdapModelOsgiTest.java
@@ -47,8 +47,8 @@ public class ApiLdapModelOsgiTest extends ApiOsgiTestBase
     @Override
     protected void useBundleClasses() throws Exception
     {
-        new Dn( "dc=example,dc=com" ); // uses FastDnParser
-        new Dn( "cn=a+sn=b,dc=example,dc=com" ); // uses ComplexDnparser (antlr based)
+        new Dn( "dc=example,dc=com" );
+        new Dn( "cn=a+sn=b,dc=example,dc=com" );
         new Value( "foo" );
         new DefaultAttribute( "cn" );
         new DefaultEntry();
diff --git a/integ-osgi/src/test/java/org/apache/directory/api/osgi/ApiOsgiTestBase.java b/integ-osgi/src/test/java/org/apache/directory/api/osgi/ApiOsgiTestBase.java
index d1c3db20c..437542c1a 100644
--- a/integ-osgi/src/test/java/org/apache/directory/api/osgi/ApiOsgiTestBase.java
+++ b/integ-osgi/src/test/java/org/apache/directory/api/osgi/ApiOsgiTestBase.java
@@ -57,17 +57,10 @@ import org.osgi.framework.BundleContext;
 @ExamReactorStrategy(PerClass.class)
 public abstract class ApiOsgiTestBase
 {
-
     @Inject
     protected BundleContext context;
 
     private static Set<String> skips = new HashSet<String>();
-//    static
-//    {
-//        skips.add( "antlr-2.7.7.jar" );
-//        skips.add( "xpp3-1.1.4c.jar" );
-//        skips.add( "dom4j-1.6.1.jar" );
-//    }
 
 
     @Configuration
diff --git a/integ/src/test/java/org/apache/directory/api/ldap/model/name/DnTest.java b/integ/src/test/java/org/apache/directory/api/ldap/model/name/DnTestInteg.java
similarity index 98%
rename from integ/src/test/java/org/apache/directory/api/ldap/model/name/DnTest.java
rename to integ/src/test/java/org/apache/directory/api/ldap/model/name/DnTestInteg.java
index 4cba13b30..2d36964f4 100644
--- a/integ/src/test/java/org/apache/directory/api/ldap/model/name/DnTest.java
+++ b/integ/src/test/java/org/apache/directory/api/ldap/model/name/DnTestInteg.java
@@ -53,7 +53,7 @@ import org.junit.jupiter.api.parallel.ExecutionMode;
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
 @Execution( ExecutionMode.CONCURRENT )
-public class DnTest
+public class DnTestInteg
 {
     private static SchemaManager schemaManager;
 
@@ -1845,11 +1845,12 @@ public class DnTest
 
         Dn result = new Dn( schemaManager, name );
 
-        assertEquals( result,
-                "0.9.2342.19200300.100.1.25=and some animals+ou=some people,0.9.2342.19200300.100.1.25=eXample,dc=cOm" );
+        assertEquals( 
+            "0.9.2342.19200300.100.1.25= and  some  animals +2.5.4.11= some  people ,0.9.2342.19200300.100.1.25= example ,0.9.2342.19200300.100.1.25= com ",
+            result.getNormName() );
         assertEquals(
-                "ou= Some   People   + 0.9.2342.19200300.100.1.25=  And   Some anImAls,0.9.2342.19200300.100.1.25 = eXample,dc= cOm",
-                result.getName() );
+            "ou= Some   People   + 0.9.2342.19200300.100.1.25=  And   Some anImAls,0.9.2342.19200300.100.1.25 = eXample,dc= cOm",
+            result.getName() );
     }
 
 
@@ -2034,11 +2035,12 @@ public class DnTest
 
 
     /**
-     * Test for DIRSERVER-642
+     * Test for DIRSERVER-642. Disabled as we don't support double quoted values
      *
      * @throws LdapException if anything goes wrong.
      */
     @Test
+    @Disabled
     public void testDoubleQuoteInNameDIRSERVER_642() throws LdapException
     {
         Dn name1 = new Dn( "cn=\"Kylie Minogue\",dc=example,dc=com" );
@@ -2063,6 +2065,7 @@ public class DnTest
      * @throws LdapException if anything goes wrong.
      */
     @Test
+    @Disabled
     public void testDoubleQuoteInNameDIRSERVER_642_1() throws LdapException
     {
         Dn dn = new Dn( "cn=\" Kylie Minogue \",dc=example,dc=com" );
@@ -2078,6 +2081,7 @@ public class DnTest
      * @throws LdapException if anything goes wrong.
      */
     @Test
+    @Disabled
     public void testDoubleQuoteWithSpecialCharsInNameDIRSERVER_250() throws LdapException
     {
         Dn dn = new Dn( "a=\"b,c\"" );
@@ -2345,14 +2349,14 @@ public class DnTest
     @Test
     public void testTrimAtavs() throws LdapException
     {
-        // antlr parser: string value with trailing spaces
+        // string value with trailing spaces
         Dn dn1 = new Dn( " cn = Amos\\,Tori , ou=system " );
         assertEquals( " cn = Amos\\,Tori ", dn1.getRdn().getName() );
         Ava atav1 = dn1.getRdn().getAva();
         assertEquals( "cn", atav1.getType() );
         assertEquals( "Amos,Tori", atav1.getValue().getString() );
 
-        // antlr parser: hexstring with trailing spaces
+        // hexstring with trailing spaces
         Dn dn3 = new Dn( " cn = #414243 , ou=system " );
         assertEquals( " cn = #414243 ", dn3.getRdn().getName() );
         Ava atav3 = dn3.getRdn().getAva();
@@ -2360,21 +2364,14 @@ public class DnTest
         assertTrue( Arrays.equals( Strings.getBytesUtf8( "ABC" ), atav3.getValue().getBytes() ) );
         assertTrue( Arrays.equals( Strings.getBytesUtf8( "ABC" ), atav3.getValue().getBytes() ) );
 
-        // antlr parser:
+        // DnParser
         Dn dn4 = new Dn( " cn = \\41\\42\\43 , ou=system " );
         assertEquals( " cn = \\41\\42\\43 ", dn4.getRdn().getName() );
         Ava atav4 = dn4.getRdn().getAva();
         assertEquals( "cn", atav4.getType() );
         assertEquals( "ABC", atav4.getValue().getString() );
 
-        // antlr parser: quotestring with trailing spaces
-        Dn dn5 = new Dn( " cn = \"ABC\" , ou=system " );
-        assertEquals( " cn = \"ABC\" ", dn5.getRdn().getName() );
-        Ava atav5 = dn5.getRdn().getAva();
-        assertEquals( "cn", atav5.getType() );
-        assertEquals( "ABC", atav5.getValue() .getString());
-
-        // fast parser: string value with trailing spaces
+        // DnParser: string value with trailing spaces
         Dn dn2 = new Dn( " cn = Amos Tori , ou=system " );
         assertEquals( " cn = Amos Tori ", dn2.getRdn().getName() );
         Ava atav2 = dn2.getRdn().getAva();
@@ -2466,6 +2463,7 @@ public class DnTest
         Dn dn = new Dn( "  ou  =  Example ,  ou  =  COM " );
 
         new Dn( schemaManager, dn );
+        
         assertEquals( "ou=Example,ou=COM", dn.getEscaped() );
         assertEquals( "  ou  =  Example ,  ou  =  COM ", dn.getName() );
 
@@ -2485,7 +2483,7 @@ public class DnTest
         assertEquals( "ou", atav.getType() );
         assertEquals( "Example", atav.getValue().getString() );
 
-        assertEquals( "ou=Example", atav.getName() );
+        assertEquals( "  ou  =  Example ", atav.getName() );
     }
 
 
diff --git a/integ/src/test/java/org/apache/directory/api/ldap/model/name/NameUtil.java b/integ/src/test/java/org/apache/directory/api/ldap/model/name/NameUtil.java
new file mode 100644
index 000000000..bae28af2b
--- /dev/null
+++ b/integ/src/test/java/org/apache/directory/api/ldap/model/name/NameUtil.java
@@ -0,0 +1,293 @@
+/*
+ *  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
+ *  
+ *    https://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.api.ldap.model.name;
+
+import java.util.List;
+
+import org.apache.directory.api.ldap.model.entry.Value;
+import org.apache.directory.api.util.Strings;
+
+/**
+ * Utility class used to dump the DN/RDN/AVA/Value elements
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class NameUtil 
+{
+    private static String dumpValue( String tabs, Value value )
+    {
+        StringBuilder sb = new StringBuilder();
+        
+        if ( value  == null )
+        {
+            return tabs + "No value" + "\n";
+        }
+        else
+        {
+            sb.append( tabs ).append( "Value:\n" );
+            tabs = tabs + "    ";
+        }
+        
+        // The upValue
+        sb.append( tabs ).append( "UpValue: '" ).append( value.getUpValue() ).append( "'\n" );
+        
+        // The normValue
+        sb.append( tabs ).append( "NormValue: '" ).append( value.getNormalized() ).append( "'\n" );
+        
+        // The bytes
+        sb.append( tabs ).append( "Bytes: '" ).append( Strings.dumpBytes( value.getBytes() ) ).append( "'\n" );
+        
+        // The attributeType
+        if ( value.getAttributeType() == null )
+        {
+            sb.append( tabs ).append( "No attributeType" ).append( '\n' ); 
+        }
+        else
+        {
+            sb.append( tabs ).append( "AttributeType" ).append( value.getAttributeType() ).append( '\n' );  
+        }
+        
+        // The HR flag
+        if ( value.isHumanReadable() )
+        {
+            sb.append( tabs ).append( "HR: true\n" );
+        }
+        else
+        {
+            sb.append( tabs ).append( "HR: false\n" );
+        }
+        
+        // The hashcode
+        sb.append( tabs ).append( "H: " ).append( value.hashCode() ).append( '\n' );
+        
+        return sb.toString();
+    }
+    
+ 
+    public static void dumpValue( Value value )
+    {
+        System.out.println( dumpValue( "    ", value ));
+    }
+    
+    
+    private static String dumpAva( String tabs, Ava ava )
+    {
+        if ( ava == null )
+        {
+            return tabs + "null";
+        }
+        
+        StringBuilder sb = new StringBuilder();
+        
+        // The upName
+        sb.append( tabs ).append( "upName: '" ).append( ava.upName ).append( "'\n" );
+        
+        // The attributeType
+        sb.append( tabs ).append( "attributeType: '" ).append( ava.attributeType ).append( "'\n" );
+
+        // The upType
+        sb.append( tabs ).append( "upType: '" ).append( ava.upType ).append( "'\n" );
+
+        // The normType
+        sb.append( tabs ).append( "normType: '" ).append( ava.normType ).append( "'\n" );
+
+        // The schemaManager
+        if ( ava.isSchemaAware() )
+        {
+            sb.append( tabs ).append( "Schema aware\n" );
+        }
+        else
+        {
+            sb.append( tabs ).append( "No schema\n" );
+        }
+        
+        // The value
+        sb.append( dumpValue( tabs, ava.value ) ).appendCodePoint( '\n' );
+        
+        return sb.toString();
+    }
+
+    public static void dumpAva( Ava ava )
+    {
+        System.out.println( dumpAva( "    ", ava ) );
+    }
+
+    private static String dumpRdn( String tabs, Rdn rdn )
+    {
+        StringBuilder sb = new StringBuilder();
+
+        if ( rdn == null )
+        {
+            sb.append( tabs ).append( "RDN: null\n" );
+        }
+        
+        if ( rdn.nbAvas == 0 )
+        {
+            sb.append( tabs ).append( "RDN: no AVAs\n" );
+        }
+        else
+        {
+            sb.append( tabs ).append( "RDN:\n" );
+        }
+        
+        tabs = tabs + "    ";
+        
+        // The schema
+        if ( rdn.isSchemaAware() )
+        {
+            sb.append( tabs ).append( "Schema aware\n" );
+        }
+        else
+        {
+            sb.append( tabs ).append( "No schema\n" );
+        }
+        
+        // The upName
+        if ( Strings.isEmpty( rdn.upName ) )
+        {
+            sb.append( tabs ).append( "UpName is empty\n" );
+        }
+        else
+        {
+            sb.append( tabs ).append( "UpName: '" ).append( rdn.upName ).append( "'\n" );
+        }
+  
+        // The normName
+        if ( Strings.isEmpty( rdn.normName ) )
+        {
+            sb.append( tabs ).append( "NormName is empty\n" );
+        }
+        else
+        {
+            sb.append( tabs ).append( "NormName: '" ).append( rdn.normName ).append( "'\n" );
+        }
+        
+        // The hash code
+        sb.append( tabs ).append( "h:" ).append( rdn.hashCode() ).append( '\n' );
+
+        // The AVAs
+        sb.append( tabs ).append( "Nb AVAs: " ).append( rdn.nbAvas ).append( '\n' );
+        
+        switch ( rdn.nbAvas )
+        {
+            case 0:
+                sb.append( tabs ).append( "No AVAs\n" );
+                break;
+                
+            case 1:
+                sb.append( tabs + "    " ).append( "AVA:\n" );
+                sb.append( dumpAva( tabs + "        ", rdn.ava ) );
+                break;
+                
+            default:
+                List<Ava> avas = rdn.avas;
+                
+                for ( int i = 0; i < rdn.nbAvas; i++ )
+                {
+                    sb.append( tabs + "    " ).append( "AVA[" ).append( i ).append( "]:\n" );
+                    sb.append( dumpAva( tabs + "        ", avas.get( i ) ) );
+                }
+        }
+        
+        return sb.toString();
+    }
+
+    public static void dumpRdn( Rdn rdn )
+    {
+        String tabs = "    ";
+        
+        System.out.println( dumpRdn( tabs, rdn ) );
+    }
+
+    public static void dumpDn( Dn dn )
+    {
+        String tabs = "    ";
+        
+        if ( dn == null )
+        {
+            System.out.println( "DN: null" );
+        }
+        
+        if ( dn.isEmpty() )
+        {
+            System.out.println( "DN: empty" );
+        }
+        
+        StringBuilder sb = new StringBuilder();
+        
+        // The schema
+        if ( dn.isSchemaAware() )
+        {
+            sb.append( tabs ).append( "Schema aware\n" );
+        }
+        else
+        {
+            sb.append( tabs ).append( "No schema\n" );
+        }
+        
+        // The upname
+        if ( Strings.isEmpty( dn.getName() ) )
+        {
+            sb.append( tabs ).append( "UpName is empty\n" );
+        }
+        else
+        {
+            sb.append( tabs ).append( "UpName: '" ).append( dn.getName() ).append( "'\n" );
+        }
+        
+        // The normname
+        if ( Strings.isEmpty( dn.getNormName() ) )
+        {
+            sb.append( tabs ).append( "NormName is empty\n" );
+        }
+        else
+        {
+            sb.append( tabs ).append( "NormName: '" ).append( dn.getNormName() ).append( "'\n" );
+        }
+        
+        // The RDNs
+        List<Rdn> rdns = dn.getRdns();
+        
+        sb.append( tabs ).append( "RDNs:\n" );
+        
+        tabs = tabs + "    ";
+        
+        if ( rdns.isEmpty() )
+        {
+            sb.append( tabs ).append( "null\n" );
+        }
+        else
+        {
+            int i = 0;
+            
+            for ( Rdn rdn : rdns )
+            {
+                sb.append( tabs ).append( "rdn[" + i + "]:\n" );
+                
+                sb.append( dumpRdn( tabs + "    ", rdn ) );
+                
+                i++;
+            }
+        }
+        
+        System.out.println( "DN: ");
+        System.out.println( sb.toString() );
+    }
+}
diff --git a/integ/src/test/java/org/apache/directory/api/ldap/model/name/SchemaAwareRdnTest.java b/integ/src/test/java/org/apache/directory/api/ldap/model/name/SchemaAwareRdnTest.java
index 99b12dfd8..119ba191f 100644
--- a/integ/src/test/java/org/apache/directory/api/ldap/model/name/SchemaAwareRdnTest.java
+++ b/integ/src/test/java/org/apache/directory/api/ldap/model/name/SchemaAwareRdnTest.java
@@ -177,7 +177,7 @@ public class SchemaAwareRdnTest
     @Test
     public void testRdnOidUpper() throws LdapException
     {
-        assertEquals( "2.5.4.3=azerty", new Rdn( schemaManager, "OID.2.5.4.3 =  azerty" ).getEscaped() );
+        assertEquals( "OID.2.5.4.3=azerty", new Rdn( schemaManager, "OID.2.5.4.3 =  azerty" ).getEscaped() );
     }
 
 
@@ -877,38 +877,9 @@ public class SchemaAwareRdnTest
     public void testRdnWithSpaces() throws LdapException
     {
         Rdn rdn = new Rdn( schemaManager, "cn=a\\ b\\ c" );
+        
         assertEquals( "cn=a b c", rdn.getEscaped() );
     }
-    
-    /*
-    @Test
-        public void testEscapedSpaceInValue() throws LdapException
-    {
-        Rdn rdn1 = new Rdn( schemaManager, "cn=a b c" );
-        Rdn rdn2 = new Rdn( schemaManager, "cn=a\\ b\\ c" );
-        assertEquals( "2.5.4.3=a b c", rdn1.getEscaped() );
-        assertEquals( "2.5.4.3=a b c", rdn2.getEscaped() );
-        assertTrue( rdn1.equals( rdn2 ) );
-
-        Rdn rdn3 = new Rdn( schemaManager, "cn=\\ a b c\\ " );
-        Rdn rdn4 = new Rdn( schemaManager, "cn=\\ a\\ b\\ c\\ " );
-        assertEquals( "2.5.4.3= a b c ", rdn3.getEscaped() );
-        assertEquals( "cn=\\ a b c\\ ", rdn3.getName() );
-        assertEquals( "2.5.4.3=\\ a b c\\ ", rdn4.getEscaped() );
-        assertEquals( "cn=\\ a\\ b\\ c\\ ", rdn4.getName() );
-        assertTrue( rdn3.equals( rdn4 ) );
-    }
-    */
-    
-    /*
-    public void testEscapedSpaceInValue2() throws LdapException
-    {
-        Rdn rdn = new Rdn( schemaManager, "cn=\\ a\\ " );
-
-        assertEquals( "cn=\\ a\\ ", rdn.getName() );
-        assertEquals( "2.5.4.3=\\ a\\ ", rdn.getEscaped() );
-    }
-    */
 
 
     @Test
@@ -1253,9 +1224,8 @@ public class SchemaAwareRdnTest
     public void testRdnWithEmptyValue() throws LdapException
     {
         assertTrue( Rdn.isValid( "dc=" ) );
-        assertTrue( Rdn.isValid( "dc=\"\"" ) );
+        assertFalse( Rdn.isValid( "dc=\"\"" ) );
         assertEquals( "dc=", new Rdn( schemaManager, "dc=" ).getEscaped() );
-        assertEquals( "dc=", new Rdn( schemaManager, "dc=\"\"" ).getEscaped() );
     }
 
 
@@ -1270,14 +1240,7 @@ public class SchemaAwareRdnTest
         assertTrue( Rdn.isValid( "cn=b\\,c" ) );
         assertEquals( "cn=b\\,c", new Rdn( schemaManager, "cn=b\\,c" ).getEscaped() );
 
-        assertTrue( Rdn.isValid( "cn=\"b,c\"" ) );
-        assertEquals( "cn=b\\,c", new Rdn( schemaManager, "cn=\"b,c\"" ).getEscaped() );
-        assertEquals( "cn=\"b,c\"", new Rdn( schemaManager, "cn=\"b,c\"" ).getName() );
-
-        assertTrue( Rdn.isValid( "cn=\"b\\,c\"" ) );
-        Rdn rdn = new Rdn( schemaManager, "cn=\"b\\,c\"" );
-        assertEquals( "cn=\"b\\,c\"", rdn.getName() );
-        assertEquals( "cn=b\\,c", rdn.getEscaped() );
+        assertFalse( Rdn.isValid( "cn=\"b,c\"" ) );
     }
 
 
diff --git a/ldap/codec/core/src/test/java/org/apache/directory/api/ldap/codec/bind/BindRequestPerfTest.java b/ldap/codec/core/src/test/java/org/apache/directory/api/ldap/codec/bind/BindRequestPerfTest.java
index a0655cce7..4ff68c69f 100644
--- a/ldap/codec/core/src/test/java/org/apache/directory/api/ldap/codec/bind/BindRequestPerfTest.java
+++ b/ldap/codec/core/src/test/java/org/apache/directory/api/ldap/codec/bind/BindRequestPerfTest.java
@@ -36,7 +36,7 @@ import org.junit.jupiter.api.parallel.ExecutionMode;
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
 @Execution( ExecutionMode.CONCURRENT)
-//@Ignore("Ignore performance tests: should not be with integration tests")
+//@Disabled("Ignore performance tests: should not be with integration tests")
 public class BindRequestPerfTest extends AbstractCodecServiceTest
 {
     /**
diff --git a/ldap/model/pom.xml b/ldap/model/pom.xml
index b4dd0065c..b1b593e21 100644
--- a/ldap/model/pom.xml
+++ b/ldap/model/pom.xml
@@ -68,21 +68,6 @@
       <artifactId>mina-core</artifactId>
     </dependency>
 
-  <!-- The original antlr artifact is needed by the antlr-maven-plugin which 
-    checks for its existence within the classpath. Use scope provided to avoid 
-    propagation to dependent projects. Choosen artifact is a valid OSGi bundle 
-    repackaged by ServiceMix team, kudos to them. -->
-    <dependency>
-      <groupId>antlr</groupId>
-      <artifactId>antlr</artifactId>
-      <scope>provided</scope>
-    </dependency>
-    
-    <dependency>
-      <groupId>org.apache.servicemix.bundles</groupId>
-      <artifactId>org.apache.servicemix.bundles.antlr</artifactId>
-    </dependency>
-
     <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-lang3</artifactId>
@@ -106,44 +91,6 @@
 
   <build>
     <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-javadoc-plugin</artifactId>
-        <configuration>
-          <sourceFileExcludes>
-            <sourceFileExclude>**/Antlr*</sourceFileExclude>
-          </sourceFileExcludes>
-        </configuration>
-      </plugin>
-      
-      <plugin>
-        <artifactId>maven-antrun-plugin</artifactId>
-        <executions>
-          <execution>
-            <phase>generate-sources</phase>
-            <configuration />
-            <goals>
-              <goal>run</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-
-      <plugin>
-        <groupId>org.codehaus.mojo</groupId>
-        <artifactId>antlr-maven-plugin</artifactId>
-        <configuration>
-          <grammars>*.g</grammars>
-        </configuration>
-        <executions>
-           <execution>
-              <goals>
-                 <goal>generate</goal>
-              </goals>
-           </execution>
-        </executions>
-      </plugin>
-
       <plugin>
         <artifactId>maven-surefire-plugin</artifactId>
         <configuration>
@@ -207,8 +154,6 @@
               org.apache.directory.api.ldap.model.url;version=${project.version};-noimport:=true
             </Export-Package>
             <Import-Package>
-              antlr;version=${antlr.version},
-              antlr.collections.impl;version=${antlr.version},
               com.github.benmanes.caffeine.cache;bundle-version=${caffeine.version},
               javax.crypto,
               javax.crypto.spec,
diff --git a/ldap/model/src/checkstyle/suppressions.xml b/ldap/model/src/checkstyle/suppressions.xml
index b75a310a7..f87d00495 100644
--- a/ldap/model/src/checkstyle/suppressions.xml
+++ b/ldap/model/src/checkstyle/suppressions.xml
@@ -49,6 +49,7 @@
     <suppress files="org.apache.directory.api.ldap.model.message.ResultCodeEnum" checks="FileLength" />
     <suppress files="org.apache.directory.api.ldap.model.schema.PrepareString" checks="FileLength" />
     <suppress files="org.apache.directory.api.ldap.model.name.Ava" checks="FileLength" />
+    <suppress files="org.apache.directory.api.ldap.model.name.DnParser" checks="FileLength" />
     <suppress files="org.apache.directory.api.ldap.model.name.Rdn" checks="FileLength" />
     <suppress files="org.apache.directory.api.ldap.model.url.LdapUrl" checks="FileLength" />
     <suppress files="org.apache.directory.api.ldap.model.schema.registries.Registries" checks="FileLength" />
@@ -63,7 +64,4 @@
 
     <!-- Contol inferfaces that don't declare any new method -->
     <suppress files="org.apache.directory.api.ldap.model.message.controls" checks="InterfaceIsType" />
-
-    <!-- Exclude Antlr generated sources -->
-    <suppress files="[\\/]generated-sources[\\/]" checks="[a-zA-Z0-9]*"/>
 </suppressions>
diff --git a/ldap/model/src/main/antlr/distinguishedName.g b/ldap/model/src/main/antlr/distinguishedName.g
deleted file mode 100644
index cfd34e569..000000000
--- a/ldap/model/src/main/antlr/distinguishedName.g
+++ /dev/null
@@ -1,1129 +0,0 @@
-header {
-/*
- *  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
- *  
- *    https://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.api.ldap.model.name;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
-import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
-import org.apache.directory.api.ldap.model.entry.Value;
-import org.apache.directory.api.ldap.model.schema.SchemaManager;
-import org.apache.directory.api.ldap.model.schema.AttributeType;
-import org.apache.directory.api.util.ExpansibleByteBuffer;
-import org.apache.directory.api.util.Strings;
-import org.apache.directory.api.util.Unicode;
-}
-
-/**
- * An antlr generated Dn lexer.
- *
- * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
- */
-class AntlrDnLexer extends Lexer;
-
-options    {
-    k = 3 ;
-    exportVocab=AntlrDn ;
-    charVocabulary = '\u0000'..'\uFFFE';
-    caseSensitive = false ;
-    defaultErrorHandler = false ;
-}
-
-COMMA : ',' ;
-EQUALS : '=' ;
-PLUS : '+' ;
-HYPHEN : '-' ;
-UNDERSCORE : '_' ;
-DQUOTE : '"' ;
-SEMI : ';' ;
-LANGLE : '<' ;
-RANGLE : '>' ;
-SPACE : ' ' ;
-
-NUMERICOID_OR_ALPHA_OR_DIGIT 
-    : ( NUMERICOID ) => NUMERICOID { $setType(NUMERICOID); }
-    | ( DIGIT ) => DIGIT { $setType(DIGIT); }
-    | ( ALPHA ) => ALPHA { $setType(ALPHA); }
-    ;
-protected NUMERICOID : ( "oid." )? NUMBER ( DOT NUMBER )+ ;
-protected DOT: '.' ;
-protected NUMBER: DIGIT | ( LDIGIT ( DIGIT )+ ) ;
-protected LDIGIT : '1'..'9' ;
-protected DIGIT : '0'..'9' ;
-protected ALPHA : 'a'..'z' ;
-
-HEXPAIR_OR_ESCESC_ESCSHARP_OR_ESC 
-    : (ESC HEX HEX) => HEXPAIR { $setType(HEXPAIR); }
-    | ESCESC { $setType(ESCESC); }
-    | ESCSHARP { $setType(ESCSHARP); }
-    | ESC { $setType(ESC); }
-    ;
-protected HEXPAIR : ESC! HEX HEX ;
-protected ESC : '\\';
-protected ESCESC : ESC ESC;
-protected ESCSHARP : ESC SHARP;
-protected HEX: DIGIT | 'a'..'f' ;
-
-HEXVALUE_OR_SHARP
-    : (SHARP ( HEX HEX )+) => HEXVALUE { $setType(HEXVALUE); }
-    | SHARP { $setType(SHARP); }
-    ;
-protected HEXVALUE : SHARP! ( HEX HEX )+ ;
-protected SHARP: '#' ;
-
-UTFMB : '\u0080'..'\uFFFE' ;
-
-/**
- * RFC 4514, Section 3:
- * <pre>
- * LUTF1 = %x01-1F / %x21 / %x24-2A / %x2D-3A /
- *    %x3D / %x3F-5B / %x5D-7F
- *
- * To avoid nondeterminism the following 
- * rules are excluded. These rules are 
- * explicitly added in the productions.
- *   EQUALS (0x3D)
- *   HYPHEN (0x2D)
- *   UNDERSCORE (0x5F)
- *   DIGIT (0x30-0x39)
- *   ALPHA (0x41-0x5A and 0x61-0x7A)
- * </pre>
- *
- * @param _createToken If a Token is to be to created
- * @throws RecognitionException If the token is invalid
- * @throws TokenStreamException When we weren't able to fetch a token
- * @throws CharStreamException If we can't process the stream
- */
-CHAR_REST : 
-    '\u0001'..'\u001F' |
-    '\u0021' |
-    '\u0024'..'\u002A' |
-    '\u002E'..'\u002F' |
-    '\u003A' |
-    '\u003F'..'\u0040' |
-    '\u005B' |
-    '\u005D'..'\u005E' | 
-    '\u0060' | 
-    '\u007B'..'\u007F' 
-    ;
-
-
-/**
- * An antlr generated Dn parser.
- *
- * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
- */
-class AntlrDnParser extends Parser;
-options    {
-    k = 3 ;
-    defaultErrorHandler = false ;
-    //buildAST=true ;
-}
-
-{
-    private void matchedProduction( String msg )
-    {
-    }
-
-    /**
-     * This class is used to store the decoded value
-     */
-    private static class UpAndNormValue
-    {
-        // The value as a byte array
-        ExpansibleByteBuffer bytes = new ExpansibleByteBuffer();
-
-        // The user provided value
-        StringBuilder upValue = new StringBuilder();
-
-        // A flag set to false if we have a binary value
-        boolean isHR = true;
-    }
-
-
-    private String createNormAva( Ava ava )
-    {
-        StringBuilder rdnNormStr = new StringBuilder();
-        Value value = ava.getValue(); 
-        //AttributeType attributeType = ava.getAttributeType();
-        rdnNormStr.append( ava.getNormType() );
-        rdnNormStr.append( '=' );
-
-        if ( value != null )
-        {
-            if ( value.getNormalized() != null )
-            {
-                rdnNormStr.append( value.getNormalized() );
-            }
-            else
-            {
-                // We can't tell if the value is HR or not. 
-                // Use the Value User Provided value
-                rdnNormStr.append( value.getUpValue() );
-            }
-        }
-
-        return rdnNormStr.toString();
-    }
-}
-
-
-/**
- * Parses a Dn string.
- *
- * RFC 4514, Section 3
- * <pre>
- * distinguishedName = [ relativeDistinguishedName
- *     *( COMMA relativeDistinguishedName ) ]
- * </pre>
- *
- * RFC 2253, Section 3
- * <pre>
- * distinguishedName = [name] 
- * name       = name-component *("," name-component)
- * </pre>
- *
- * RFC 1779, Section 2.3
- * <pre>
- * &lt;name&gt; ::= &lt;name-component&gt; ( &lt;spaced-separator&gt; )
- *        | &lt;name-component&gt; &lt;spaced-separator&gt; &lt;name&gt;
- * &lt;spaced-separator&gt; ::= &lt;optional-space&gt;
- *             &lt;separator&gt;
- *             &lt;optional-space&gt;
- * &lt;separator&gt; ::=  "," | ";"
- * &lt;optional-space&gt; ::= ( &lt;CR&gt; ) *( " " )
- * </pre>
- *
- * @param schemaManager The SchemaManager
- * @param dn The Dn to update
- * @throws RecognitionException If the token is invalid
- * @throws TokenStreamException When we weren't able to fetch a token
- */
-distinguishedName [SchemaManager schemaManager, Dn dn]
-    {
-        matchedProduction( "distinguishedName()" );
-        Rdn rdn = new Rdn( schemaManager );
-    }
-    :
-    (
-        relativeDistinguishedName[schemaManager, rdn] 
-        { 
-            try
-            { 
-                dn.add( rdn );
-                
-            }
-            catch ( LdapInvalidDnException lide )
-            {
-                // Do nothing, can't get an exception here
-            } 
-        }
-        (
-            ( COMMA | SEMI )
-            {
-                rdn = new Rdn( schemaManager );
-            }
-            relativeDistinguishedName[schemaManager, rdn] 
-            { 
-                try
-                { 
-                    dn.add( rdn ); 
-                }
-                catch ( LdapInvalidDnException lide )
-                {
-                    // Do nothing, can't get an exception here
-                } 
-            }
-        )*
-        EOF
-    )?
-    ;
-
-
-/**
- * Parses a Dn string.
- *
- * RFC 4514, Section 3
- * <pre>
- * distinguishedName = [ relativeDistinguishedName
- *     *( COMMA relativeDistinguishedName ) ]
- * </pre>
- *
- * RFC 2253, Section 3
- * <pre>
- * distinguishedName = [name] 
- * name       = name-component *("," name-component)
- * </pre>
- *
- * RFC 1779, Section 2.3
- * <pre>
- * &lt;name&gt; ::= &lt;name-component&gt; ( &lt;spaced-separator&gt; )
- *        | &lt;name-component&gt; &lt;spaced-separator&gt; &lt;name&gt;
- * &lt;spaced-separator&gt; ::= &lt;optional-space&gt;
- *             &lt;separator&gt;
- *             &lt;optional-space&gt;
- * &lt;separator&gt; ::=  "," | ";"
- * &lt;optional-space&gt; ::= ( &lt;CR&gt; ) *( " " )
- * </pre>
- *
- * @param schemaManager The SchemaManager
- * @param rdns The list of Rdns to update
- * @return The normalized Dn
- * @throws RecognitionException If the token is invalid
- * @throws TokenStreamException When we weren't able to fetch a token
- */
-relativeDistinguishedNames [SchemaManager schemaManager, List<Rdn> rdns] returns [String normNameStr]
-    {
-        matchedProduction( "relativeDistinguishedNames()" );
-        Rdn rdn = new Rdn( schemaManager );
-        StringBuilder dnNormSb = new StringBuilder();
-    }
-    :
-    (
-        relativeDistinguishedName[ schemaManager, rdn] 
-        { 
-            rdns.add( rdn );
-            dnNormSb.append( rdn.getNormName() );
-            rdn = new Rdn( schemaManager );
-        }
-        (
-            ( COMMA | SEMI )
-            relativeDistinguishedName[schemaManager, rdn] 
-            { 
-                rdns.add( rdn ); 
-                dnNormSb.append( ',' );
-                dnNormSb.append( rdn.getNormName() );
-                rdn = new Rdn( schemaManager );
-                }
-        )*
-        EOF
-    )?
-    {
-        normNameStr = dnNormSb.toString();
-    }
-    ;
-
-
-/**
- * Parses a Rdn string.
- *
- * RFC 4514, Section 3
- * <pre>
- * relativeDistinguishedName = attributeTypeAndValue
- *     *( PLUS attributeTypeAndValue )
- * </pre>
- *
- * RFC 2253, Section 3
- * <pre>
- * name-component = attributeTypeAndValue *("+" attributeTypeAndValue)
- * </pre>
- *
- * RFC 1779, Section 2.3
- * <pre>
- * &lt;name-component&gt; ::= &lt;attribute&gt;
- *     | &lt;attribute&gt; &lt;optional-space&gt; "+"
- *       &lt;optional-space&gt; &lt;name-component&gt;
- * </pre>
- *
- * @param schemaManager The SchemaManager
- * @param rdn The Rdn to update
- * @throws RecognitionException If the token is invalid
- * @throws TokenStreamException When we weren't able to fetch a token
- */
-relativeDistinguishedName [SchemaManager schemaManager, Rdn rdn]
-    {
-        matchedProduction( "relativeDistinguishedName()" );
-
-        // The rdnStr variable is used to gather the full RDN string
-        // as provided
-        StringBuilder rdnUpStr = new StringBuilder();
-        StringBuilder rdnNormStr = new StringBuilder();
-        Value val;
-        Ava ava = new Ava( schemaManager);
-        String upAva;
-
-        // The list of parsed Ava for a later post-processing
-        List<Ava> avas = new ArrayList<>();
-    }
-    :
-    (
-        // The first AVA
-        upAva = attributeTypeAndValue[schemaManager, ava] 
-        {
-            ava.hashCode();
-            rdnUpStr.append( upAva );
-            avas.add( ava );
-        }
-        (
-            PLUS 
-            {
-                ava = new Ava( schemaManager);
-            }
-            upAva = attributeTypeAndValue[schemaManager, ava] 
-            {
-                ava.hashCode();
-                rdnUpStr.append( '+' ).append( upAva );
-
-                try 
-                {
-                    Rdn.addOrdered( avas, ava );
-                }
-                catch ( LdapInvalidDnException lide )
-                {
-                    throw new SemanticException( lide.getMessage() );
-                }
-            }
-        )*
-    )
-    {
-        // Now, build the Rdn
-        switch ( avas.size() )
-        {
-            case 0:
-                // Can't be...
-            case 1:
-                // One single Ava
-                rdn.upName = rdnUpStr.toString();
-                rdn.normName = createNormAva( ava );
-                rdn.ava = ava;
-                rdn.avaType = ava.getType();
-                rdn.nbAvas = 1; 
-                break;
-
-            default:
-                rdn.nbAvas = avas.size();
-                rdn.avaTypes = new HashMap<String, List<Ava>>();
-                boolean isFirst = true;
-
-                for ( Ava parsedAva : avas )
-                {
-                    if ( isFirst  )
-                    {
-                        isFirst = false;
-                    }
-                    else
-                    {
-                        rdnNormStr.append( '+' );
-                    }
-
-                    String type;
-
-                    if ( schemaManager != null )
-                    {
-                        type = parsedAva.getAttributeType().getOid();
-                        rdnNormStr.append( type );
-                    }
-                    else
-                    {
-                        type = parsedAva.normType;
-                        rdnNormStr.append( type );
-                    }
-
-                    rdnNormStr.append( '=' );
-
-                    val = parsedAva.getValue();
-
-                    if ( ( val != null ) && ( val.getNormalized() != null ) )
-                    {
-                        rdnNormStr.append( val.getNormalized() );
-                    }
-                    else
-                    {
-                        rdnNormStr.append( val.getUpValue() );
-                    }
-
-                    List<Ava> avaList = rdn.avaTypes.get( type );
-
-                    if ( avaList == null )
-                    {
-                        avaList = new ArrayList<>();
-                    }
-
-                    avaList.add( parsedAva );
-                    rdn.avaTypes.put( type, avaList );
-                }
-
-                rdn.upName = rdnUpStr.toString();
-                rdn.normName = rdnNormStr.toString();
-                rdn.avas = avas;
-
-                break;
-        }
-
-        rdn.hashCode();
-    }
-    ;
-    
-
-/**
- * RFC 4514, Section 3
- * <pre>
- * attributeTypeAndValue = attributeType EQUALS attributeValue
- * </pre>
- *
- * RFC 2253, Section 3
- * <pre>
- * attributeTypeAndValue = attributeType "=" attributeValue
- * </pre>
- *
- * @param schemaManager The SchemaManager
- * @param ava The parsed Ava
- * @return The user provided Ava
- * @throws RecognitionException If the token is invalid
- * @throws TokenStreamException When we weren't able to fetch a token
- */
-attributeTypeAndValue [SchemaManager schemaManager, Ava ava] returns [String upNameStr]
-    {
-        matchedProduction( "attributeTypeAndValue()" );
-        String type = null;
-        UpAndNormValue value = new UpAndNormValue();
-        StringBuilder rdnUpName = new StringBuilder();
-    }
-    :
-    (
-        ( SPACE { rdnUpName.append( ' ' ); } )*
-        type = attributeType { rdnUpName.append( type ); }
-        ( SPACE { rdnUpName.append( ' ' ); } )*
-        EQUALS { rdnUpName.append( '=' ); }
-        ( SPACE  { rdnUpName.append( ' ' ); } )*
-        attributeValue[value] 
-        {
-            try
-            {
-                // We have to remove the ending spaces that may have been added, as the tutf1 rule
-                // cannot be processed
-                rdnUpName.append( value.upValue );
-                AttributeType attributeType = null;
-
-                if ( schemaManager != null )
-                {
-                    if ( ( type.startsWith( "oid." ) ) || ( type.startsWith( "OID." ) ) )
-                    {
-                        type = type.substring( 4 );
-                    }
-
-                    attributeType = schemaManager.getAttributeType( type );
-                    ava.attributeType = attributeType;
-                    ava.upType = type;
-                    ava.normType = attributeType.getOid();
-                }
-                else
-                {
-                    ava.upType = type;
-                    ava.normType = Strings.lowerCaseAscii( Strings.trim( type ) );
-                }
-
-                if ( ( ( attributeType != null ) && attributeType.isHR() ) || value.isHR )
-                {
-                    int valueLength = value.upValue.length();
-                    int pos = value.bytes.position();
-                    
-                    for ( int i = valueLength - 1; i >= 0; i-- )
-                    {
-                        if ( value.upValue.charAt( i ) == ' ' ) 
-                        {
-                            if ( i == 0 )
-                            {
-                                // The value is empty
-                                ava = new Ava( schemaManager, type, rdnUpName.toString(), ( String ) null );
-                                break;
-                            }
-                            else if ( value.upValue.charAt( i - 1 ) != '\\' )
-                            {
-                                // This is a trailing space, get rid of it
-                                value.upValue.deleteCharAt( i );
-                                pos--;
-                                value.bytes.position( pos );
-                            }
-                            else
-                            {
-                                // This is an escaped space, get out
-                                break;
-                            }
-                        }
-                        else
-                        {
-                            break;
-                        }
-                    }
-
-                    if ( attributeType != null )
-                    {
-                        try 
-                        {
-                            ava.upName = rdnUpName.toString();
-                            ava.value = new Value( attributeType, Strings.utf8ToString( value.bytes.copyOfUsedBytes() ) );
-                        }
-                        catch ( LdapInvalidAttributeValueException liave )
-                        {
-                            throw new SemanticException( liave.getMessage() );
-                        }
-                    }
-                    else
-                    {
-                        ava.upName = rdnUpName.toString();
-                        ava.value = new Value( Strings.utf8ToString( value.bytes.copyOfUsedBytes() ) );
-                    }
-                }
-                else
-                {
-                    ava.upName = rdnUpName.toString();
-                    ava.value = new Value( value.bytes.copyOfUsedBytes() );
-                }
-            }
-            catch ( LdapInvalidDnException e )
-            {
-                throw new SemanticException( e.getMessage() );
-            } 
-        }
-        ( SPACE { rdnUpName.append( ' ' ); } )*
-    )
-    {
-        upNameStr = rdnUpName.toString();
-    }
-    ;
-    
-
-/**
- * RFC 4514 Section 3
- *
- * <pre>
- * attributeType = descr / numericoid
- * </pre>
- *
- * @return The AttributeType
- * @throws RecognitionException If the token is invalid
- * @throws TokenStreamException When we weren't able to fetch a token
- */
-attributeType returns [String attributeType]
-    {
-        matchedProduction( "attributeType()" );
-    }
-    :
-    (
-        attributeType = descr
-        |
-        attributeType = numericoid
-    )
-    ;
-
-
-/**
- * RFC 4512 Section 1.4
- *
- * <pre>
- * descr = keystring
- * keystring = leadkeychar *keychar
- * leadkeychar = ALPHA
- * keychar = ALPHA / DIGIT / HYPHEN
- * </pre>
- *
- * We additionally add UNDERSCORE because some servers allow them.
- *
- * @return The description
- * @throws RecognitionException If the token is invalid
- * @throws TokenStreamException When we weren't able to fetch a token
- */
-descr returns [String descr]
-    {
-        matchedProduction( "descr()" );
-        StringBuilder descrSb = new StringBuilder();
-    }
-    :
-    leadkeychar:ALPHA { descrSb.append( leadkeychar.getText() ); }
-    (
-        alpha:ALPHA { descrSb.append( alpha.getText() ); }
-        |
-        digit:DIGIT { descrSb.append( digit.getText() ); }
-        |
-        HYPHEN { descrSb.append( '-' ); }
-        |
-        UNDERSCORE { descrSb.append( '_' ); }
-    )*
-    {
-        descr = descrSb.toString();
-    }
-    ;
-
-
-/**
- * RFC 4512 Section 1.4
- *
- * <pre>
- * numericoid = number 1*( DOT number )
- * number  = DIGIT / ( LDIGIT 1*DIGIT )
- * DIGIT   = %x30 / LDIGIT       ; "0"-"9"
- * LDIGIT  = %x31-39             ; "1"-"9"
- * </pre>
- *
- * @return The numeric OID
- * @throws RecognitionException If the token is invalid
- * @throws TokenStreamException When we weren't able to fetch a token
- */
-numericoid returns [String numericoid = ""]
-    {
-        matchedProduction( "numericoid()" );
-    }
-    :
-    noid:NUMERICOID { numericoid = noid.getText(); }
-    ;
-
-
-/**
- * RFC 4514, Section 3
- * <pre>
- * attributeValue = string / hexstring
- * </pre>
- *
- * RFC 2253, Section 3
- * <pre>
- * attributeValue = string
- * string     = *( stringchar / pair )
- *              / "#" hexstring
- *              / QUOTATION *( quotechar / pair ) QUOTATION ; only from v2
- *
- * We still accept both forms, which means we can have a value surrounded by '"'
- * </pre>
- *
- * @param value The value to update
- * @throws RecognitionException If the token is invalid
- * @throws TokenStreamException When we weren't able to fetch a token
- */
-attributeValue [UpAndNormValue value] 
-    {
-        matchedProduction( "attributeValue()" );
-    }
-    :
-    (
-        // Special for RFC 2253
-        quotestring [value]
-        |
-        string [value]
-        |
-        hexstring [value]
-    )?
-    ;
-
-
-/**
- * RFC 2253, Section 3
- * <pre>
- *              / QUOTATION *( quotechar / pair ) QUOTATION ; only from v2
- * quotechar     = &lt;any character except "\" or QUOTATION &gt;
- * </pre>
- *
- * @param value The value to update
- * @throws RecognitionException If the token is invalid
- * @throws TokenStreamException When we weren't able to fetch a token
- */
-quotestring [UpAndNormValue value] 
-    {
-        matchedProduction( "quotestring()" );
-    }
-    :
-    (
-        DQUOTE { value.upValue.append( '"' ); }
-        (
-            (
-                s:~(DQUOTE|ESC|ESCESC|ESCSHARP|HEXPAIR) 
-                {
-                    value.upValue.append( s.getText() );
-                    value.bytes.append( Strings.getBytesUtf8( s.getText() ) );
-                }
-            )
-            |
-            pair [value] 
-        )*
-        DQUOTE { value.upValue.append( '"' ); }
-    )
-    ;
-
-
-/**
- * RFC 4514 Section 3
- *
- * <pre>
- * hexstring = SHARP 1*hexpair
- *
- * If in &lt;hexstring&gt; form, a BER representation can be obtained from
- * converting each &lt;hexpair&gt; of the &lt;hexstring&gt; to the octet indicated
- * by the &lt;hexpair&gt;.
- * </pre>
- *
- * @param value The value to update
- * @throws RecognitionException If the token is invalid
- * @throws TokenStreamException When we weren't able to fetch a token
- */ 
-hexstring [UpAndNormValue value]
-    {
-        matchedProduction( "hexstring()" );
-    }
-    :
-    hexValue:HEXVALUE
-    {
-        String hexStr = hexValue.getText();
-        value.upValue.append( '#' ).append( hexStr );
-        value.bytes.append( Strings.toByteArray( hexStr ) );
-        value.isHR = false; 
-    }
-    ;
-
-
-/**
- * RFC 4514 Section 3
- *
- * <pre>
- * ; The following characters are to be escaped when they appear
- * ; in the value to be encoded: ESC, one of &lt;escaped&gt;, &lt;leading&gt;
- * ; SHARP or SPACE, trailing SPACE, and NULL.
- * string =   [ ( leadchar / pair ) [ *( stringchar / pair ) ( trailchar / pair ) ] ]
- * leadchar = LUTF1 | UTFMB
- * stringchar = SUTF1 / UTFMB
- * trailchar = TUTF1 / UTFMB
- * </pre>
- *
- * @param value The value to update
- * @throws RecognitionException If the token is invalid
- * @throws TokenStreamException When we weren't able to fetch a token
- */ 
-string [UpAndNormValue value]
-    {
-        matchedProduction( "string()" );
-    }
-    :
-    (
-        // Note that we don't distinguish between sutf1 and tutf1, as it would be ambiguous.
-        // The final spaces will be handled later.
-        ( lutf1 [value] | utfmb [value] | pair [value] ) ( sutf1 [value] | utfmb [value] | pair [value] )*
-    )
-    ;
-
-
-/**
- * RFC 4514, Section 3:
- * <pre>
- * LUTF1 = %x01-1F / %x21 / %x24-2A / %x2D-3A /
- *    %x3D / %x3F-5B / %x5D-7F
- *
- * The rule CHAR_REST doesn't contain the following charcters,
- * so we must check them additionally
- *   EQUALS (0x3D)
- *   HYPHEN (0x2D)
- *   UNDERSCORE (0x5F)
- *   DIGIT (0x30-0x39)
- *   ALPHA (0x41-0x5A and 0x61-0x7A)
- * </pre>
- *
- * @param value The value to update
- * @throws RecognitionException If the token is invalid
- * @throws TokenStreamException When we weren't able to fetch a token
- */
-lutf1 [UpAndNormValue value]
-    {
-        matchedProduction( "lutf1()" );
-    }
-    :
-    rest:CHAR_REST 
-    { 
-        char c = rest.getText().charAt( 0 );
-        value.upValue.append( c );
-        value.bytes.append( ( byte ) c );
-    }
-    |
-    EQUALS
-    { 
-        value.upValue.append( '=' );
-        value.bytes.append( '=' );
-    }
-    |
-    HYPHEN
-    { 
-        value.upValue.append( '-' );
-        value.bytes.append( '-' );
-    }
-    |
-    UNDERSCORE
-    { 
-        value.upValue.append( '_' );
-        value.bytes.append( '_' );
-    }
-    |
-    digit:DIGIT
-    { 
-        char c = digit.getText().charAt( 0 );
-        value.upValue.append( c );
-        value.bytes.append( ( byte ) c );
-    }
-    |
-    alpha:ALPHA
-    { 
-        char c = alpha.getText().charAt( 0 );
-        value.upValue.append( c );
-        value.bytes.append( ( byte ) c  );
-    }
-    | 
-    // Another hack : having a String like 127.0.0.1 in the value
-    // will not match a DIGIT, because it's swallowed by the NUMERICOID
-    // token
-    numericoid:NUMERICOID  
-    {
-        String number = numericoid.getText();
-        value.upValue.append( number );
-        value.bytes.append( Strings.getBytesUtf8( number ) );
-    }
-    ;
-
-
-/**
- * RFC 4514, Section 3:
- * <pre>
- * SUTF1 = %x01-21 / %x23-2A / %x2D-3A /
- *    %x3D / %x3F-5B / %x5D-7F
- *
- * The rule CHAR_REST doesn't contain the following charcters,
- * so we must check them additionally
- *   EQUALS (0x3D)
- *   HYPHEN (0x2D)
- *   UNDERSCORE (0x5F)
- *   DIGIT (0x30-0x39)
- *   ALPHA (0x41-0x5A and 0x61-0x7A)
- *   SHARP (0x23)
- *   SPACE (0x20)
- * </pre>
- *
- * @param value The value to update
- * @throws RecognitionException If the token is invalid
- * @throws TokenStreamException When we weren't able to fetch a token
- */
-sutf1 [UpAndNormValue value]
-    {
-        matchedProduction( "sutf1()" );
-    }
-    :
-    rest:CHAR_REST
-    { 
-        char c = rest.getText().charAt( 0 );
-        value.upValue.append( c );
-        value.bytes.append( ( byte ) c );
-    }
-    |
-    EQUALS
-    { 
-        value.upValue.append( '=' );
-        value.bytes.append( '=' );
-    }
-    |
-    HYPHEN
-    { 
-        value.upValue.append( '-' );
-        value.bytes.append( '-' );
-    }
-    |
-    UNDERSCORE
-    { 
-        value.upValue.append( '_' );
-        value.bytes.append( '_' );
-    }
-    |
-    digit:DIGIT
-    { 
-        char c = digit.getText().charAt( 0 );
-        value.upValue.append( c );
-        value.bytes.append( ( byte ) c );
-    }
-    |
-    alpha:ALPHA
-    { 
-        char c = alpha.getText().charAt( 0 );
-        value.upValue.append( c );
-        value.bytes.append( ( byte ) c );
-    }
-    |
-    SHARP
-    { 
-        value.upValue.append( '#' );
-        value.bytes.append( '#' );
-    }
-    | 
-    SPACE
-    { 
-        value.upValue.append( ' ' );
-        value.bytes.append( ' ' );
-    }
-    | 
-    // This is a hack to deal with #NN included into the value, due to 
-    // some collision with the HEXVALUE token. In this case, we should
-    // consider that a hex value is in fact a String
-    hex:HEXVALUE
-    {
-        String hexStr = hex.getText();
-        value.upValue.append( '#' ).append( hexStr );
-        value.bytes.append( '#' );
-        value.bytes.append( Strings.getBytesUtf8( hexStr ) );
-    }
-    | 
-    // Another hack : having a String like 127.0.0.1 in the value
-    // will not match a DIGIT, because it's swallowed by the NUMERICOID
-    // token
-    numericoid:NUMERICOID  
-    {
-        String number = numericoid.getText();
-        value.upValue.append( number );
-        value.bytes.append( Strings.getBytesUtf8( number ) );
-    }
-    ;
-
-/**
- * Process a UTFMB char
- *
- * @param value The value to update
- * @throws RecognitionException If the token is invalid
- * @throws TokenStreamException When we weren't able to fetch a token
- */
-utfmb [UpAndNormValue value]
-    {
-        matchedProduction( "utfmb()" );
-    }
-    :
-    s:UTFMB
-    { 
-        char c = s.getText().charAt( 0 );
-        value.upValue.append( c );
-        value.bytes.append( Unicode.charToBytes( c ) );
-    }
-    ;
-
-
-/**
- * RFC 4514, Section 3
- * <pre>
- * pair = ESC ( ESC / special / hexpair )
- * special = escaped / SPACE / SHARP / EQUALS
- * escaped = DQUOTE / PLUS / COMMA / SEMI / LANGLE / RANGLE
- * hexpair = HEX HEX
- *
- * If in &lt;string&gt; form, a LDAP string representation asserted value can
- * be obtained by replacing (left to right, non-recursively) each &lt;pair&gt;
- * appearing in the &lt;string&gt; as follows:
- *   replace &lt;ESC&gt;&lt;ESC&gt; with &lt;ESC&gt;;
- *   replace &lt;ESC&gt;&lt;special&gt; with &lt;special&gt;;
- *   replace &lt;ESC&gt;&lt;hexpair&gt; with the octet indicated by the &lt;hexpair&gt;.
- * </pre>
- * 
- * RFC 2253, Section 3
- * <pre>
- * pair       = "\" ( special / "\" / QUOTATION / hexpair )
- * special    = "," / "=" / "+" / "&lt;" /  "&gt;" / "#" / ";"
- * </pre>
- * 
- * RFC 1779, Section 2.3
- * <pre>
- * &lt;pair&gt; ::= "\" ( &lt;special&gt; | "\" | '"')
- * &lt;special&gt; ::= "," | "=" | &lt;CR&gt; | "+" | "&lt;" |  "&gt;"
- *           | "#" | ";"
- * </pre>
- * 
- * @param value The value to update
- * @throws RecognitionException If the token is invalid
- * @throws TokenStreamException When we weren't able to fetch a token
- */ 
-pair [UpAndNormValue value]
-    {
-        matchedProduction( "pair()" );
-        char specialChar;
-    }
-    :
-    (
-        ESCESC 
-        { 
-            value.upValue.append( "\\\\" );
-            value.bytes.append( '\\' );
-        } 
-    )
-    |
-    (
-        ESCSHARP 
-        { 
-            value.upValue.append( "\\#" );
-            value.bytes.append( '#' );
-        } 
-    )
-    |
-    ( 
-        ESC
-        specialChar = special 
-        { 
-            value.upValue.append( '\\' ).append( specialChar );
-            value.bytes.append( specialChar );
-        }
-    )
-    |
-    (
-        // A String like "\C4", corresponding to the hex value 0xC4. 
-        hexpair:HEXPAIR
-        {
-            value.upValue.append( '\\' ).append( hexpair.getText() );
-            value.bytes.append( Strings.toByteArray( hexpair.getText() ) );
-        }
-    )
-    ;
-
-
-/**
- * RFC 4514 Section 3
- * 
- * <pre>
- * special = escaped / SPACE / SHARP / EQUALS
- * escaped = DQUOTE / PLUS / COMMA / SEMI / LANGLE / RANGLE
- * </pre>
- *
- * @return The special char
- * @throws RecognitionException If the token is invalid
- * @throws TokenStreamException When we weren't able to fetch a token
- */
-special returns [char special]
-    {
-        matchedProduction( "()" );
-    }
-    :
-    (
-        DQUOTE { special = '"'; }
-        |
-        PLUS { special = '+'; }
-        |
-        COMMA { special = ','; }
-        |
-        SEMI { special = ';'; }
-        |
-        LANGLE { special = '<'; }
-        |
-        RANGLE { special = '>'; }
-        |
-        SPACE { special = ' '; }
-        |
-        SHARP { special = '#'; }
-        |
-        EQUALS { special = '='; }
-    )
-    ;
-
diff --git a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/entry/Value.java b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/entry/Value.java
index b9f0293cc..ae5228b49 100644
--- a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/entry/Value.java
+++ b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/entry/Value.java
@@ -324,7 +324,7 @@ public class Value implements Cloneable, Externalizable, Comparable<Value>
         
         this.normValue = normValue;
         
-        if ( !attributeType.isRelaxed() )
+        if ( ( attributeType != null ) && !attributeType.isRelaxed() )
         {
             // Check the value
             if ( attributeType.getSyntax().getSyntaxChecker() != null )
diff --git a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Ava.java b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Ava.java
index 996b36d6d..4bcd10828 100644
--- a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Ava.java
+++ b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Ava.java
@@ -455,8 +455,8 @@ public class Ava implements Externalizable, Cloneable, Comparable<Ava>
     // WARNING : The protection level is left unspecified intentionally.
     // We need this method to be visible from the DnParser class, but not
     // from outside this package.
-    /* Unspecified protection */Ava( AttributeType attributeType, String upType, String normType, Value value, String upName )
-        throws LdapInvalidDnException
+    /* Unspecified protection */Ava( AttributeType attributeType, String upType, String normType, Value value,
+            String upName ) throws LdapInvalidDnException
     {
         this.attributeType = attributeType;
         String upTypeTrimmed = Strings.trim( upType );
@@ -521,7 +521,17 @@ public class Ava implements Externalizable, Cloneable, Comparable<Ava>
         StringBuilder sb = new StringBuilder();
 
         this.upType = upType;
-        this.normType = normType;
+        
+        // Special case for oid attributeType
+        if ( ( normType != null ) && normType.startsWith( "oid." ) )
+        {
+            this.normType = normType.substring( 4 );
+        }
+        else
+        {
+            this.normType = normType;
+        }
+        
         this.value = value;
         
         sb.append( upType );
@@ -670,7 +680,7 @@ public class Ava implements Externalizable, Cloneable, Comparable<Ava>
                 }
             }
 
-            if ( this.attributeType == tmpAttributeType )
+            if ( ( this.attributeType != null ) && this.attributeType.equals( tmpAttributeType ) )
             {
                 // No need to normalize again
                 return;
@@ -689,6 +699,9 @@ public class Ava implements Externalizable, Cloneable, Comparable<Ava>
                 String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY );
                 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le );
             }
+            
+            // Update the normalized type
+            this.normType = this.attributeType.getOid();
 
             hashCode();
         }
diff --git a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/ComplexDnParser.java b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/ComplexDnParser.java
deleted file mode 100644
index f941cc2a7..000000000
--- a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/ComplexDnParser.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- *  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
- *  
- *    https://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.api.ldap.model.name;
-
-
-import java.io.StringReader;
-import java.util.List;
-
-import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
-import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
-import org.apache.directory.api.ldap.model.schema.SchemaManager;
-
-
-/**
- * A Dn parser that is able to parse complex DNs. This is an Antlr based parser.
- * 
- * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
- */
-/* No protection*/class ComplexDnParser
-{
-
-    /**
-     * Parses an Dn.
-     * 
-     * @param schemaManager The SchemaManager
-     * @param name the string representation of the distinguished name
-     * @param rdns the (empty) list where parsed RDNs are put to
-     * @return The normalized Dn
-     * 
-     * @throws LdapInvalidDnException the invalid name exception
-     */
-    /* No protection*/String parseDn( SchemaManager schemaManager, String name, List<Rdn> rdns ) throws LdapInvalidDnException
-    {
-        AntlrDnParser dnParser = new AntlrDnParser( new AntlrDnLexer( new StringReader( name ) ) );
-
-        try
-        {
-            return dnParser.relativeDistinguishedNames( schemaManager, rdns );
-        }
-        catch ( Exception e )
-        {
-            throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, e.getMessage(), e );
-        }
-    }
-
-
-    /**
-     * Parses an Rdn.
-     * 
-     * @param schemaManager The SchemaManager
-     * @param name the string representation of the relative distinguished name
-     * @param rdn the (empty) Rdn where parsed ATAVs are put into
-     * 
-     * @throws LdapInvalidDnException the invalid name exception
-     */
-    /* No protection*/void parseRdn( SchemaManager schemaManager, String name, Rdn rdn ) throws LdapInvalidDnException
-    {
-        AntlrDnParser dnParser = new AntlrDnParser( new AntlrDnLexer( new StringReader( name ) ) );
-
-        try
-        {
-            dnParser.relativeDistinguishedName( schemaManager, rdn );
-        }
-        catch ( Exception e )
-        {
-            throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, e.getMessage(), e );
-        }
-    }
-}
diff --git a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Dn.java b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Dn.java
index 6424b6939..52d6e82ec 100644
--- a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Dn.java
+++ b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Dn.java
@@ -196,12 +196,29 @@ public class Dn implements Iterable<Rdn>, Externalizable
             return;
         }
 
+        StringBuilder sb = new StringBuilder();
+        boolean isFirst = true;
+        
         for ( Rdn rdn : dn.rdns )
         {
-            this.rdns.add( new Rdn( schemaManager, rdn ) );
+            Rdn normalizedRdn = new Rdn( schemaManager, rdn );
+            
+            this.rdns.add( normalizedRdn );
+            
+            if ( isFirst )
+            {
+                isFirst = false;
+            }
+            else
+            {
+                sb.append( ',' );
+            }
+            
+            sb.append( normalizedRdn.getNormName() );
         }
 
         upName = toUpName();
+        normName = sb.toString();
     }
 
 
@@ -209,7 +226,7 @@ public class Dn implements Iterable<Rdn>, Externalizable
      * Creates a new instance of Dn, using varargs to declare the RDNs. Each
      * String is either a full Rdn, or a couple of AttributeType DI and a value.
      * If the String contains a '=' symbol, the the constructor will assume that
-     * the String arg contains afull Rdn, otherwise, it will consider that the
+     * the String arg contains a full Rdn, otherwise, it will consider that the
      * following arg is the value.<br>
      * The created Dn is Schema aware.
      * <br><br>
@@ -235,10 +252,12 @@ public class Dn implements Iterable<Rdn>, Externalizable
 
     /**
      * Creates a new instance of schema aware Dn, using varargs to declare the RDNs. Each
-     * String is either a full Rdn, or a couple of AttributeType DI and a value.
+     * String is either a full Rdn, or a couple of AttributeType and a value.
+     * <br><br>
      * If the String contains a '=' symbol, the the constructor will assume that
-     * the String arg contains afull Rdn, otherwise, it will consider that the
-     * following arg is the value.<br>
+     * the String arg contains a full Rdn, otherwise, it will consider that the
+     * following arg is the value.
+     * <br><br>
      * The created Dn is Schema aware.
      * <br><br>
      * An example of usage would be :
@@ -251,6 +270,16 @@ public class Dn implements Iterable<Rdn>, Externalizable
      *     "ou", exampleName,
      *     baseDn);
      * </pre>
+     * 
+     * which will create the "cn=Test,ou=example" Dn.
+     * <br><br>
+     * If one single String is passed as an argument, then it will be considered as
+     * a full Dn.
+     * <br><br>
+     * Note that the method expect to receive a full Dn (one single value has been passed),
+     * many RDNs (in a &lt;type&gt; = &lt;value&gt; form) or pairs of attributeType/values.
+     * <br><br>
+     * It does not support receiving an incorrect RDN like "cn=" or "cn=test,dc="
      *
      * @param schemaManager the schema manager
      * @param upRdns The list of String composing the Dn
@@ -258,56 +287,111 @@ public class Dn implements Iterable<Rdn>, Externalizable
      */
     public Dn( SchemaManager schemaManager, String... upRdns ) throws LdapInvalidDnException
     {
-        StringBuilder sbUpName = new StringBuilder();
-        boolean valueExpected = false;
-        boolean isFirst = true;
         this.schemaManager = schemaManager;
 
-        for ( String upRdn : upRdns )
+        if ( ( upRdns == null ) || ( upRdns.length == 0 ) )
         {
-            if ( Strings.isEmpty( upRdn ) )
-            {
-                continue;
-            }
-
-            if ( isFirst )
-            {
-                isFirst = false;
-            }
-            else if ( !valueExpected )
-            {
-                sbUpName.append( ',' );
-            }
-
-            if ( !valueExpected )
+            // Special case: the empty Dn
+            upName = "";
+            normName = "";
+            
+            return;
+        }
+        
+        // Compute the upName
+        if ( upRdns.length == 1 )
+        {
+            // Another special case: the provided value is expected to be a full DN
+            upName = upRdns[0];
+        }
+        else
+        {
+            StringBuilder sbUpName = new StringBuilder();
+            boolean valueExpected = false;
+            boolean isFirst = true;
+        
+            // Reconstruct the upName from each RDN's upName.
+            // as we may get many forms of Rdns, 
+            for ( String upRdn : upRdns )
             {
-                sbUpName.append( upRdn );
-
-                if ( upRdn.indexOf( '=' ) == -1 )
+                if ( Strings.isEmpty( upRdn ) )
                 {
-                    valueExpected = true;
+                    continue;
+                }
+        
+                if ( valueExpected )
+                {
+                    sbUpName.append( "=" ).append( upRdn );
+                    
+                    valueExpected = false;
+                }
+                else
+                {
+                    if ( isFirst )
+                    {
+                        isFirst = false;
+                    }
+                    else
+                    {
+                        sbUpName.append( ',' );
+                    }
+                    
+                    sbUpName.append( upRdn );
+        
+                    if ( upRdn.indexOf( '=' ) == -1 )
+                    {
+                        valueExpected = true;
+                    }
                 }
             }
-            else
+        
+            if ( !isFirst && valueExpected )
             {
-                sbUpName.append( "=" ).append( upRdn );
-
-                valueExpected = false;
+                throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, I18n.err( I18n.ERR_13611_VALUE_MISSING_ON_RDN ) );
             }
+        
+            // Stores the representations of a Dn : internal (as a string and as a
+            // byte[]) and external.
+            upName = sbUpName.toString();
         }
 
-        if ( !isFirst && valueExpected )
+        // Now parse the Dn and construct the normName
+        try
         {
-            throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, I18n.err( I18n.ERR_13611_VALUE_MISSING_ON_RDN ) );
+            normName = parseInternal( schemaManager, upName, rdns );
         }
+        catch ( LdapInvalidDnException e )
+        {
+            if ( schemaManager == null || !schemaManager.isRelaxed() )
+            {
+                throw e;
+            }
+            // Ignore invalid DN formats in relaxed mode.
+            // This is needed to support unbelievably insane
+            // DN formats such as <GUI=abcd...> format used by
+            // Active Directory
+        }
+    }
 
-        // Stores the representations of a Dn : internal (as a string and as a
-        // byte[]) and external.
-        upName = sbUpName.toString();
-
+    
+    /**
+     * Creates a new instance of schema aware Dn, from a byte[] representation of an 
+     * UTF-8 String.
+     * 
+     * @param schemaManager the schema manager
+     * @param bytes The byte[] containing the DN as an UTF-8 encoded value
+     * @throws LdapInvalidDnException If the resulting Dn is invalid
+     */
+    public Dn( SchemaManager schemaManager, byte[] bytes ) throws LdapInvalidDnException
+    {
+        this.schemaManager = schemaManager;
+        
+        // Convert the byte array to a String
+        upName = Strings.utf8ToString( bytes );
+        
         try
         {
-            normName = parseInternal( schemaManager, upName, rdns );
+            normName = parseInternal( schemaManager, bytes, rdns );
         }
         catch ( LdapInvalidDnException e )
         {
@@ -322,7 +406,6 @@ public class Dn implements Iterable<Rdn>, Externalizable
         }
     }
 
-
     /**
      * Creates a Dn from a list of Rdns.
      *
@@ -1248,26 +1331,32 @@ public class Dn implements Iterable<Rdn>, Externalizable
     }
 
 
+    /**
+     * Parse a Dn.
+     *
+     * @param schemaManager The SchemaManager
+     * @param bytes, The Dn to be parsed, as a byte[] of UTF-8 encoded chars
+     * @param rdns The list that will contain the RDNs
+     * @return The normalized Dn
+     * @throws LdapInvalidDnException If the Dn is invalid
+     */
+    private static String parseInternal( SchemaManager schemaManager, byte[] bytes, List<Rdn> rdns ) throws LdapInvalidDnException
+    {
+        return DnParser.parseDn( schemaManager, bytes, rdns );
+    }
+
     /**
      * Parse a Dn.
      *
      * @param schemaManager The SchemaManager
      * @param name The Dn to be parsed
      * @param rdns The list that will contain the RDNs
-     * @return The nromalized Dn
+     * @return The normalized Dn
      * @throws LdapInvalidDnException If the Dn is invalid
      */
-    private static String parseInternal( SchemaManager schemaManager, String name, List<Rdn> rdns ) throws LdapInvalidDnException
+    private static String parseInternal( SchemaManager schemaManager, String upName, List<Rdn> rdns ) throws LdapInvalidDnException
     {
-        try
-        {
-            return FastDnParser.parseDn( schemaManager, name, rdns );
-        }
-        catch ( TooComplexDnException e )
-        {
-            rdns.clear();
-            return new ComplexDnParser().parseDn( schemaManager, name, rdns );
-        }
+        return DnParser.parseDn( schemaManager, upName, rdns );
     }
 
 
diff --git a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/DnParser.java b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/DnParser.java
new file mode 100644
index 000000000..f338e571c
--- /dev/null
+++ b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/DnParser.java
@@ -0,0 +1,2063 @@
+/*
+ *  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
+ *  
+ *    https://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.api.ldap.model.name;
+
+
+import java.nio.charset.StandardCharsets;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+import org.apache.directory.api.i18n.I18n;
+import org.apache.directory.api.ldap.model.entry.Value;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
+import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
+import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
+import org.apache.directory.api.ldap.model.schema.AttributeType;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.api.util.Position;
+import org.apache.directory.api.util.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.directory.api.util.ParserUtil.isMatchChar;
+import static org.apache.directory.api.util.ParserUtil.hasMoreChars;
+import static org.apache.directory.api.util.ParserUtil.matchChar;
+
+import static org.apache.directory.api.util.ParserUtil.COMMA;
+import static org.apache.directory.api.util.ParserUtil.DOT;
+import static org.apache.directory.api.util.ParserUtil.DQUOTE;
+import static org.apache.directory.api.util.ParserUtil.EQUAL;
+import static org.apache.directory.api.util.ParserUtil.ESC;
+import static org.apache.directory.api.util.ParserUtil.LANGLE;
+import static org.apache.directory.api.util.ParserUtil.PLUS;
+import static org.apache.directory.api.util.ParserUtil.RANGLE;
+import static org.apache.directory.api.util.ParserUtil.SEMI_COLON;
+import static org.apache.directory.api.util.ParserUtil.SHARP;
+import static org.apache.directory.api.util.ParserUtil.SPACE;
+
+/**
+ * A Dn parser that is able to parse complex DNs. 
+ * 
+ * This is an hand written parser.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+/* No protection*/final class DnParser
+{
+    /** The LoggerFactory used by this class */
+    protected static final Logger LOG = LoggerFactory.getLogger( DnParser.class );
+    
+    /** A private enum listing the AttributeType state machine states */
+    private enum AttributTypeStates 
+    {
+        START,
+        STATE_1,
+        STATE_2,
+        STATE_3,
+        STATE_4,
+        STATE_5,
+        STATE_6,
+        STATE_7,
+        STATE_8,
+        STATE_9,
+        STATE_10,
+        END
+    }
+    
+    /** A private enum listing the AttributeValue state machine states */
+    private enum AttributeValuestates 
+    {
+        START,
+        STRING_PAIR,
+        STRING_PAIR_HEX1,
+        STRING_MIDDLE,
+        STRING_PAIR_MIDDLE,
+        STRING_PAIR_HEX1_MIDDLE,
+        END
+    }
+    
+    /**
+     * A flag used to tell which part of a hex value we are dealing with
+     */
+    private static final boolean EVEN = true;
+    private static final boolean ODD = false;   
+
+    /** No constructor allowed */
+    private DnParser() 
+    {
+        throw new UnsupportedOperationException( "This is a utility class and cannot be instantiated" );
+    }
+    
+    
+    /**
+     * Create a normalized AVA
+     */
+    private static String createNormAva( Ava ava )
+    {
+        StringBuilder rdnNormStr = new StringBuilder();
+        Value value = ava.getValue(); 
+
+        rdnNormStr.append( ava.getNormType() );
+        rdnNormStr.append( EQUAL );
+
+        if ( value != null )
+        {
+            if ( value.getNormalized() != null )
+            {
+                rdnNormStr.append( value.getNormalized() );
+            }
+            else
+            {
+                // We can't tell if the value is HR or not. 
+                // Use the Value User Provided value
+                rdnNormStr.append( value.getUpValue() );
+            }
+        }
+
+        return rdnNormStr.toString();
+    }
+
+    
+    /**
+     * Parse a HexString:
+     * 
+     * <pre>
+     * hexstring ::= SHARP hexpair hexpair*
+     * hexpair ::= HEX HEX
+     * </pre>
+     */
+    private static byte[] parseHexString( Position pos )
+            throws ParseException
+    {
+        // We should at least have one hexpair. The '#' has already been read.
+        // We start with a NONE value: we haven't seen any yet
+        boolean hexPair = EVEN;
+        int hexValue = 0;
+        
+        // The maximum length, divided by two. We allocate a byte array 
+        // of this size, just in case
+        int maxLength = ( pos.length - pos.start ) >> 1;
+        byte[] result = new byte[maxLength];
+                
+        // Keep a track of the first hexPair
+        int start = pos.start;
+        int current = 0;
+        
+        // The position of any space after the value: we should keep them
+        // in the upValue, but ignore them for the normValue
+        int spaceStart = -1;
+        
+        // A flag used to exit the loop
+        boolean valueRead = false; 
+
+        while ( hasMoreChars( pos ) && !valueRead )
+        {
+            byte b = pos.getByte();
+            
+            switch ( b )
+            {
+                case '0': case '1': case '2': case '3':
+                case '4': case '5': case '6': case '7':
+                case '8': case '9': 
+                    if ( hexPair == EVEN )
+                    {
+                        // A new hexPair
+                        hexValue = b - '0';
+                        hexPair = ODD;
+                    }
+                    else
+                    {
+                        // Ok, we have an hex pair, let's convert it
+                        result[current++] = ( byte ) ( ( hexValue << 4 ) + b - '0' );
+                        
+                        hexPair = EVEN;
+                    }
+
+                    pos.start++;
+                    break;
+
+                case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+                    if ( hexPair == EVEN )
+                    {
+                        hexValue = b - 'a' + 10;
+                        hexPair = ODD;
+                    }
+                    else
+                    {
+                        // Ok, we have an hex pair, let's convert it
+                        result[current++] = ( byte ) ( ( hexValue << 4 ) + b - 'a' + 10 );
+                        hexPair = EVEN;
+                    }
+                    
+                    pos.start++;
+                    break;
+                    
+                case 'A': case 'B': case 'C': case 'D':case 'E':case 'F':
+                    if ( hexPair == EVEN )
+                    {
+                        hexValue = b - 'A' + 10;
+                        hexPair = ODD;
+                    }
+                    else
+                    {
+                        // Ok, we have an hex pair, let's convert it
+                        result[current++] = ( byte ) ( ( hexValue << 4 ) + b - 'A' + 10 );
+                        hexPair = EVEN;
+                    }
+                    
+                    pos.start++;
+                    break;
+                    
+                case SPACE:
+                    // A special case: we have to ignore them
+                    if ( spaceStart == -1 )
+                    {
+                        spaceStart = pos.start;
+                    }
+                    
+                    pos.start++;
+                    break;
+                    
+                case PLUS: case COMMA: case SEMI_COLON:
+                    // The end, we should have either one or more space,
+                    // a '+', a ',' a ';' or nothing. But here, we just stop
+                    // Copy the bytes read
+                    valueRead  = true;
+                    break;
+
+                default:
+                    // This is an error
+                    throw new ParseException( I18n.err( I18n.ERR_13613_VALUE_NOT_IN_HEX_FORM_ODD_NUMBER ), pos.start );
+            }
+        }
+        
+        // Ok, we are done. Check that we have had only hex pairs
+        if ( ( hexPair == ODD ) || ( current == 0 ) )
+        {
+            // This is an error, we have an even number of chars.
+            throw new ParseException( I18n.err( I18n.ERR_13613_VALUE_NOT_IN_HEX_FORM_ODD_NUMBER ), pos.start );
+        }
+        
+        // Deal with spaces at the end of the value. We should not copy them
+        int realSize = pos.start;
+        
+        if ( spaceStart > 0 )
+        {
+            realSize = spaceStart;
+        }
+        
+        realSize = ( realSize - start ) >> 1;
+        
+        byte[] valueBytes = Arrays.copyOf( result, realSize );
+        
+        return valueBytes;
+    }
+    
+    
+    /**
+     * Parse a hex pair 
+     */
+    private static void parsePair( Position pos, StringBuilder upValue, byte[] result, int[] bPos )
+            throws ParseException
+    {
+        // We have either a couple of hex chars, or some special char
+        if ( !hasMoreChars( pos ) )
+        {
+            throw new ParseException( I18n.err( I18n.ERR_13631_WRONG_ESCAPED_SEQUENCE ), pos.start );
+        }
+        
+        // Let's process the first state
+        byte b = pos.getByte();
+        int firstHex = 0;
+
+        switch ( b )
+        {
+            case ESC: 
+            case DQUOTE: 
+            case PLUS: 
+            case COMMA: 
+            case SEMI_COLON: 
+            case LANGLE:
+            case RANGLE:
+            case SPACE:
+            case SHARP:
+            case EQUAL:
+                // An escaped special char
+                upValue.append( ESC );
+                upValue.append( ( char ) b );
+                result[bPos[0]++] = b;
+                pos.start++;
+
+                return;
+            
+            case '0': case '1': case '2': case '3':  case '4': 
+            case '5': case '6': case '7': case '8': case '9': 
+                firstHex = b - '0';
+                break;
+                
+            case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': 
+                firstHex = b - 'a' + 10;
+                break;
+                
+            case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': 
+                firstHex = b - 'A' + 10;
+                break;
+                
+            default:
+                // An error: we are supposed to have an escaped sequence
+                throw new ParseException( I18n.err( I18n.ERR_13631_WRONG_ESCAPED_SEQUENCE ), pos.start );
+        }
+        
+        // Let's check the second char now
+        pos.start++;
+        
+        if ( hasMoreChars( pos ) )
+        {
+            b = pos.getByte();
+            byte bb = 0;
+            
+            switch ( b )
+            {
+                case '0': case '1': case '2': case '3':  case '4': 
+                case '5': case '6': case '7': case '8': case '9': 
+                    bb = ( byte ) ( ( firstHex << 4 ) + b - '0' );
+                    
+                    break;
+                    
+                case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': 
+                    bb = ( byte ) ( ( firstHex << 4 ) + b - 'a' + 10 );
+
+                    break;
+                    
+                case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': 
+                    bb = ( byte ) ( ( firstHex << 4 ) + b - 'A' + 10 );
+
+                    break;
+                    
+                default:
+                    // An error: we are supposed to have an escaped sequence
+                    throw new ParseException( I18n.err( I18n.ERR_13631_WRONG_ESCAPED_SEQUENCE ), pos.start );
+            }
+            
+            result[bPos[0]++] = bb;
+            pos.start++;
+        }
+        else
+        {
+            // This is an error, we must have a second char
+            throw new ParseException( I18n.err( I18n.ERR_13632_WRONG_HEX_PAIR ), pos.start );
+        }
+        
+        return;
+    }
+    
+    
+    /**
+     * Get an UTF0 byte (0x80-0xBF)
+     */
+    private static byte getUTF0( Position pos, byte[] result, int[] bPos ) throws ParseException
+    {
+        if ( hasMoreChars( pos ) )
+        {
+            byte b = pos.getByte();
+            
+            // Must be an UTF0 (0x80-0xBF)
+            if ( ( b >= ( byte ) 0x0080 ) && ( b <= ( byte ) 0x00BF ) )
+            {
+                pos.start++;
+                result[bPos[0]++] = b;
+                
+                return b;
+            }
+            else
+            {
+                // Error
+                byte[] wrongBytes = new byte[] { b };
+                throw new ParseException( I18n.err( I18n.ERR_13633_INVALID_UTFMB, 
+                        Strings.dumpBytes( wrongBytes ) ), pos.start );
+            }
+        }
+        else
+        {
+            // Error
+            throw new ParseException( I18n.err( I18n.ERR_13634_INCOMPLETE_UTFMB ), pos.start );
+        }
+    }
+
+    
+    /**
+     * Parse the following char:
+     * 
+     * <pre>
+     * UTF2 = %xC2-DF UTF0
+     * </pre>
+     */
+    private static char parseUTF2( Position pos, byte u1, byte[] result, int[] bPos ) throws ParseException
+    {
+        result[bPos[0]++] = u1;
+
+        // UTF2, get the next byte, which must be between 0x80-0xBF
+        byte u2 = getUTF0( pos, result, bPos );
+
+        // Convert to unicode: 
+        // C2 -> DF: 110[0 0010] - 110[1 1111], mask is 0x1F
+        // 80 -> BF: 10[00 0000] - 10[11 1111], mask is 0x3F
+        char c = ( char ) ( ( ( u1 & 0x001F ) << 6 ) | ( u2 & 0x003F ) );
+        
+        return c;
+    }
+
+    
+    /**
+     * Parse the following char:
+     * 
+     * <pre>
+     * UTF3 = %xE0 %xA0-BF UTF0
+     * </pre>
+     */
+    private static char parseUTF3First( Position pos, byte u1, byte[] result, int[] bPos ) throws ParseException
+    {
+        result[bPos[0]++] = u1;
+
+        // UTF3, get the next byte, which must be between 0xA0-0xBF
+        if ( hasMoreChars( pos ) )
+        {
+            byte u2 = pos.getByte();
+            
+            if ( ( u2 >= ( byte ) 0x00A0 ) && ( u2 <= ( byte ) 0x00BF ) )
+            {
+                pos.start++;
+                result[bPos[0]++] = u2;
+
+                
+                // The third byte must be an UTF-0
+                byte u3 = getUTF0( pos, result, bPos );
+                    
+                // Convert to unicode: 
+                // E0 -> 1110 [0000] mask is 0x0F
+                // A0 -> BF: 10[10 0000] - 10[11 1111], mask is 0x3F
+                // 80 -> BF: 10[00 0000] - 10[11 1111], mask is 0x3F
+                char c = ( char ) ( ( ( u1 & 0x0010F ) << 12 ) 
+                        | ( ( u2 & 0x003F ) << 6 ) 
+                        | ( u3 & 0x003F ) );
+                
+                return c;
+            }
+            else
+            {
+                // Error
+                byte[] wrongBytes = new byte[] { u1, u2 };
+                throw new ParseException( I18n.err( I18n.ERR_13633_INVALID_UTFMB, 
+                        Strings.dumpBytes( wrongBytes ) ), pos.start );
+            }
+        }
+        else
+        {
+            // Error
+            byte[] wrongBytes = new byte[] { u1 };
+            throw new ParseException( I18n.err( I18n.ERR_13633_INVALID_UTFMB, 
+                    Strings.dumpBytes( wrongBytes ) ), pos.start );
+        }
+    }
+    
+    
+    /**
+     * Parse the following char:
+     * 
+     * <pre>
+     * UTF3 = %xE1-EC 2(UTF0)
+     * </pre>
+     */
+    private static char parseUTF3Second( Position pos, byte u1, byte[] result, int[] bPos ) throws ParseException
+    {
+        result[bPos[0]++] = u1;
+
+        // UTF3, get the next byte, which must be between 0x80-0xBF
+        byte u2 = getUTF0( pos, result, bPos );
+        byte u3 = getUTF0( pos, result, bPos );
+        
+        // Convert to unicode: 
+        // E1 -> EC: 1110 [0001] - 1110 [1100], mask is 0x0F
+        // 80 -> BF: 10[00 0000] - 10[11 1111], mask is 0x3F
+        // 80 -> BF: 10[00 0000] - 10[11 1111], mask is 0x3F
+        char c = ( char ) ( ( ( u1 & 0x000F ) << 12 ) 
+                | ( ( u2 & 0x003F ) << 6 ) 
+                | ( u3 & 0x003F ) );
+        
+        return c;
+    }
+
+    
+    /**
+     * Parse the following char:
+     * 
+     * <pre>
+     * UTF3 = %xED %x80-9F UTF0
+     * </pre>
+     */
+    private static char parseUTF3Third( Position pos, byte u1, byte[] result, int[] bPos ) throws ParseException
+    {
+        result[bPos[0]++] = u1;
+
+        // UTF3, get the next byte, which must be between 0x80-0x9F
+        if ( hasMoreChars( pos ) )
+        {
+            byte u2 = pos.getByte();
+            
+            if ( ( u2 >= ( byte ) 0x0080 ) && ( u2 <= ( byte ) 0x009F ) )
+            {
+                pos.start++;
+                result[bPos[0]++] = u2;
+
+                
+                // The third byte must be an UTF-0 (0x80-0xBF)
+                byte u3 = getUTF0( pos, result, bPos );
+
+                // Convert to unicode: 
+                // E0 -> 1110 [1101] Value 0x0D
+                // A0 -> BF: 100[0 0000] - 100[1 1111], mask is 0x1F
+                // 80 -> BF: 10[00 0000] - 10[11 1111], mask is 0x3F
+                char c = ( char ) ( ( ( 0x000D ) << 12 ) 
+                        | ( ( u2 & 0x001F ) << 6 ) 
+                        | ( u3 & 0x003F ) );
+                
+                return c;
+            }
+            else
+            {
+                // Error
+                byte[] wrongBytes = new byte[] { u1, u2 };
+                throw new ParseException( I18n.err( I18n.ERR_13633_INVALID_UTFMB, 
+                        Strings.dumpBytes( wrongBytes ) ), pos.start );
+            }
+        }
+        else
+        {
+            // Error
+            byte[] wrongBytes = new byte[] { u1 };
+            throw new ParseException( I18n.err( I18n.ERR_13633_INVALID_UTFMB, 
+                    Strings.dumpBytes( wrongBytes ) ), pos.start );
+        }
+    }
+    
+    
+    /**
+     * Parse the following char:
+     * 
+     * <pre>
+     * UTF3 = %xEE-EF 2(UTF0)
+     * </pre>
+     */
+    private static char parseUTF3Forth( Position pos, byte u1, byte[] result, int[] bPos ) throws ParseException
+    {
+        result[bPos[0]++] = u1;
+
+        // UTF3, get the next byte, which must be between 0x80-0xBF
+        byte u2 = getUTF0( pos, result, bPos );
+        byte u3 = getUTF0( pos, result, bPos );
+
+        // Convert to unicode: 
+        // EE -> EF: 1110 [1110] - 1110 [1111], mask is 0x0F
+        // 80 -> BF: 10[00 0000] - 10[11 1111], mask is 0x3F
+        // 80 -> BF: 10[00 0000] - 10[11 1111], mask is 0x3F
+        char c = ( char ) ( ( ( u1 & 0x000F ) << 12 ) 
+                | ( ( u2 & 0x003F ) << 6 ) 
+                | ( u3 & 0x003F ) );
+        
+        return c;
+    }
+    
+    
+    /**
+     * As we have 20 bits to deal with, and as a char can only contains 16 bits, the value is
+     * split in two 10 bits values:
+     * 
+     * <pre>
+     *   byte 1     byte 2     byte 3     byte 4
+     * [1111 0]xab  [10]01 0000  [10]00 0000  [10]00 0000   Min
+     *              [10]11 1111  [10]11 1111  [10]11 1111   Max
+     *          <>      <----->      <----->      <----->   Value bits from 0 to 19 (20 bits)
+     *          11      11 1111      11 0000      00 0000 
+     *          98      76 5432      10 9876      54 3210
+     *          [   High surrogate    ] [ Low Surrogate ]
+     * </pre>
+     * 
+     * <ul>
+     *   <li>The Low Surrogate uses bits from 0 to 9</li>
+     *   <li>The High Surrigate uses bits from 10 to 19</li>
+     * </ul>
+     * 
+     * Now, we add 0xD800 to the high surrogate (1101 1000  0000 0000)
+     * and 0xDC00 to the low surrogate (1101 1100 0000 0000)
+     * 
+     * That gives two values, from 0xD800 to 0xDBFF (high surrogate) and from 0xDC00 to 0xDFFF (low surrogate).
+     * 
+     * The returned int will contains those two values, as (High Surrogate << 16) & Low Surrogate
+     */
+    private static int computeSurrogates( int value )
+    {
+        // Remove 0x10000
+        value -= 0x10000;
+        
+        // Split it in two surrogates, using 10 high bits (mask 0x000F FC00)
+        // and 10 low bits (mask 0x000003FF)
+        int highSurrogate = ( ( value & 0x000F_FC00 ) >> 10 ) | 0xD800;
+        int lowSurrogate  = ( value & 0x0000_03FF ) | 0xDC00;
+        
+        // And combine both
+        return ( highSurrogate << 16 ) | lowSurrogate;
+    }
+    
+    
+    /**
+     * Parse the following char:
+     * 
+     * <pre>
+     * UTF4    = %xF0 %x90-BF 2(UTF0)
+     * </pre>
+     * 
+     * It will be encoded as an int, using a high surrogate and a low surrogate.
+     * The reason is that we can't encode an unicode value above 0xFFFF into a char, as a char
+     * only contains an UTF-16 value.
+     * 
+     * The bits layout for an UTF4 is the following:
+     * <pre>
+     *   byte 1     byte 2     byte 3     byte 4
+     * [1111 0]x00  [10]01 0000  [10]00 0000  [10]00 0000   Min
+     *              [10]11 1111  [10]11 1111  [10]11 1111   Max
+     *          <>      <----->      <----->      <----->   Value bits from 0 to 19 (20 bits)
+     *          11      11 1111      11 0000      00 0000 
+     *          98      76 5432      10 9876      54 3210
+     * </pre>
+     */
+    private static int parseUTF4First( Position pos, byte u1, byte[] result, int[] bPos ) throws ParseException
+    {
+        result[bPos[0]++] = u1;
+
+        // UTF4, get the next byte, which must be between 0x90-0xBF
+        if ( hasMoreChars( pos ) )
+        {
+            byte u2 = pos.getByte();
+            
+            if ( ( u2 >= ( byte ) 0x0090 ) && ( u2 <= ( byte ) 0x00BF ) )
+            {
+                pos.start++;
+                result[bPos[0]++] = u2;
+
+                
+                byte u3 = getUTF0( pos, result, bPos );
+                byte u4 = getUTF0( pos, result, bPos );
+                    
+                // Convert to unicode: 
+                // F0 -> 1111 00[00] Value 0x00
+                // 90 -> BF: 10[01 0000] - 10[11 1111], mask is 0x3F
+                // 80 -> BF: 10[00 0000] - 10[11 1111], mask is 0x3F
+                // 80 -> BF: 10[00 0000] - 10[11 1111], mask is 0x3F
+                int i = ( int ) ( ( ( u2 & 0x003F ) << 12 ) 
+                        | ( ( u3 & 0x003F ) << 6 ) 
+                        | ( u4 & 0x003F ) );
+                
+                // And convert to surrogates
+                return computeSurrogates( i );
+            }
+            else
+            {
+                // Error
+                byte[] wrongBytes = new byte[] { u1, u2 };
+                throw new ParseException( I18n.err( I18n.ERR_13633_INVALID_UTFMB, 
+                        Strings.dumpBytes( wrongBytes ) ), pos.start );
+            }
+        }
+        else
+        {
+            // Error
+            byte[] wrongBytes = new byte[] { u1 };
+            throw new ParseException( I18n.err( I18n.ERR_13633_INVALID_UTFMB, 
+                    Strings.dumpBytes( wrongBytes ) ), pos.start );
+        }
+    }
+    
+    
+    /**
+     * Parse the following char:
+     * 
+     * <pre>
+     * UTF4    = %xF1-F3 3(UTF0)
+     * </pre>
+     * 
+     * Same that the parseUTF4_1 method, the result will be encoded as an int
+     */
+    private static int parseUTF4Second( Position pos, byte u1, byte[] result, int[] bPos ) throws ParseException
+    {
+        result[bPos[0]++] = u1;
+
+        // UTF4, get the next byte, which must be between 0x90-0xBF
+        byte u2 = getUTF0( pos, result, bPos );
+        byte u3 = getUTF0( pos, result, bPos );
+        byte u4 = getUTF0( pos, result, bPos );
+
+        // Convert to unicode: 
+        // F1-F3:    1111 0[001] - 1111 0[011], mask is 0x07
+        // 80 -> BF: 10[00 0000] - 10[11 1111], mask is 0x3F
+        // 80 -> BF: 10[00 0000] - 10[11 1111], mask is 0x3F
+        // 80 -> BF: 10[00 0000] - 10[11 1111], mask is 0x3F
+        int i = ( int ) ( ( ( u1 & 0x0007 ) << 18 ) 
+                | ( ( u2 & 0x003F ) << 12 ) 
+                | ( ( u3 & 0x003F ) << 6 ) 
+                | ( u4 & 0x003F ) );
+        
+        // And convert to surrogates
+        return computeSurrogates( i );
+    }
+    
+    
+    /**
+     * Parse the following char:
+     * 
+     * <pre>
+     * UTF4    =  %xF4 %x80-8F 2(UTF0)
+     * </pre>
+     */
+    private static int parseUTF4Third( Position pos, byte u1, byte[] result, int[] bPos ) throws ParseException
+    {
+        result[bPos[0]++] = u1;
+
+        // UTF4, get the next byte, which must be between 0x80-0xBF
+        if ( hasMoreChars( pos ) )
+        {
+            byte u2 = pos.getByte();
+            
+            if ( ( u2 >= ( byte ) 0x0080 ) && ( u2 <= ( byte ) 0x00BF ) )
+            {
+                pos.start++;
+                result[bPos[0]++] = u2;
+
+                
+                byte u3 = getUTF0( pos, result, bPos );
+                byte u4 = getUTF0( pos, result, bPos );
+            
+                // Convert to unicode: 
+                // F4:       1111 0100, Value is 0x04
+                // 80 -> 8F: 10[00 0000] - 10[00 1111], mask is 0x0F
+                // 80 -> BF: 10[00 0000] - 10[11 1111], mask is 0x3F
+                // 80 -> BF: 10[00 0000] - 10[11 1111], mask is 0x3F
+                int i = ( int ) ( ( ( 0x0004 ) << 18 ) 
+                        | ( ( u2 & 0x000F ) << 12 ) 
+                        | ( ( u3 & 0x003F ) << 6 ) 
+                        | ( u4 & 0x003F ) );
+                
+                return computeSurrogates( i );
+            }
+            else
+            {
+                // Error
+                byte[] wrongBytes = new byte[] { u1, u2 };
+                throw new ParseException( I18n.err( I18n.ERR_13633_INVALID_UTFMB, 
+                        Strings.dumpBytes( wrongBytes ) ), pos.start );
+            }
+        }
+        else
+        {
+            // Error
+            byte[] wrongBytes = new byte[] { u1 };
+            throw new ParseException( I18n.err( I18n.ERR_13633_INVALID_UTFMB, 
+                    Strings.dumpBytes( wrongBytes ) ), pos.start );
+        }
+    }
+
+    
+    /**
+     * Parse an UTFMB char:
+     * 
+     * <pre>
+     * UTFMB   = UTF2 / UTF3 / UTF4
+     * UTF0    = %x80-BF
+     * UTF1    = %x00-7F
+     * UTF2    = %xC2-DF UTF0
+     * UTF3    = %xE0 %xA0-BF UTF0 / %xE1-EC 2(UTF0) / %xED %x80-9F UTF0 / %xEE-EF 2(UTF0)
+     * UTF4    = %xF0 %x90-BF 2(UTF0) / %xF1-F3 3(UTF0) / %xF4 %x80-8F 2(UTF0)
+     * </pre> 
+     * 
+     * Basically:
+     * 
+     * <ul>
+     *   <li>UTF1 is used for chars between 0x0000 and 0x007F</li>
+     *   <li>UTF2 is used for chars between 0x0080 and 0x07FF</li>
+     *   <li>UTF3 is used for chars between 0x0800 and 0xFFFF</li>
+     *   <li>UTF4 is used for chars between 0x10000 and 0x10FFFF, and will be stored in 2 chars</li>
+     * </ul>
+     * 
+     * Note, from RFC 3629:
+     * 
+     * <pre>
+     * The first octet values C0, C1, F5 to FF never appear.
+     * C0 (1100 0000) followed by [80-BF] (1000 0000 to 1011 1111) would encode for 0000 0000 
+     * to 0011 1111 (0x00 - 0x3F), so are covered by UTF1.
+     * C1 (1100 0001) followed by [80-BF] (1000 0000 to 1011 1111) would encode for 0100 0000 
+     * to 0111 1111 (0x40 - 0x7F), so are covered by UTF1.
+     * </pre>
+     * 
+     * This can translate into one (UTF2/UTF3) or two (UTF4) chars, so we return an int instead.
+     */
+    private static int parseUTFMB( Position pos, byte[] result, int[] bPos ) throws ParseException
+    {
+        byte u1 = pos.getByte();
+        pos.start++;
+
+        switch ( u1 )
+        {
+            // UTF2    = %xC2-DF UTF0
+            case ( byte ) 0xC2: case ( byte ) 0xC3: case ( byte ) 0xC4: case ( byte ) 0xC5:
+            case ( byte ) 0xC6: case ( byte ) 0xC7: case ( byte ) 0xC8: case ( byte ) 0xC9:
+            case ( byte ) 0xCA: case ( byte ) 0xCB: case ( byte ) 0xCC: case ( byte ) 0xCD:
+            case ( byte ) 0xCE: case ( byte ) 0xCF: case ( byte ) 0xD0: case ( byte ) 0xD1:
+            case ( byte ) 0xD2: case ( byte ) 0xD3: case ( byte ) 0xD4: case ( byte ) 0xD5:
+            case ( byte ) 0xD6: case ( byte ) 0xD7: case ( byte ) 0xD8: case ( byte ) 0xD9:
+            case ( byte ) 0xDA: case ( byte ) 0xDB: case ( byte ) 0xDC: case ( byte ) 0xDD:
+            case ( byte ) 0xDE: case ( byte ) 0xDF:
+                char c = parseUTF2( pos, u1, result, bPos );
+            
+                return c;
+                
+            // UTF3-1    = %xE0 %xA0-BF UTF0
+            case ( byte ) 0xE0:
+                return parseUTF3First( pos, u1, result, bPos );
+
+            // UTF3-2    = %xE1-EC 2(UTF0)
+            case ( byte ) 0xE1: case ( byte ) 0xE2: case ( byte ) 0xE3: case ( byte ) 0xE4:
+            case ( byte ) 0xE5: case ( byte ) 0xE6: case ( byte ) 0xE7: case ( byte ) 0xE8:
+            case ( byte ) 0xE9: case ( byte ) 0xEA: case ( byte ) 0xEB: case ( byte ) 0xEC:
+                return parseUTF3Second( pos, u1, result, bPos );
+
+            // UTF3    = %xED %x80-9F UTF0
+            case ( byte ) 0xED:
+                return parseUTF3Third( pos, u1, result, bPos );
+ 
+            // UTF3    = %xEE-EF 2(UTF0)
+            case ( byte ) 0xEE: case ( byte ) 0xEF:
+                return parseUTF3Forth( pos, u1, result, bPos );
+
+            // UTF4    = %xF0 %x90-BF 2(UTF0)
+            case ( byte ) 0xF0:
+                return parseUTF4First( pos, u1, result, bPos );
+
+            // UTF4    = %xF1-F3 3(UTF0)
+            case ( byte ) 0xF1: case ( byte ) 0xF2: case ( byte ) 0xF3:
+                return parseUTF4Second( pos, u1, result, bPos );
+
+            // UTF4    = %xF4 %x80-8F 2(UTF0)
+            case ( byte ) 0xF4:
+                return parseUTF4Third( pos, u1, result, bPos );
+            
+            default:
+                // This is an error
+                byte[] wrongBytes = new byte[] { u1 };
+                throw new ParseException( I18n.err( I18n.ERR_13633_INVALID_UTFMB, 
+                        Strings.dumpBytes( wrongBytes ) ), pos.start );
+        }
+    }
+
+    
+    /**
+     * Parse an attributeValue. The RFC 4514 grammar is:
+     * 
+     * <pre>
+     * attributeValue ::= string? | hexstring
+     * hexstring ::= SHARP hexpair hexpair*
+     * string ::= ( lutf1 | utfmb | pair ) ( sutf1 | utfmb | pair )* (tutf1 | utfmb | pair)?
+     * hexpair ::= HEX HEX
+     * 
+     * lutf1 ::= '\u0001'..'\u001F' | '\u0021' | '\u0024'..'\u002A' | '\u002D'..'\u003A' |
+     *           '\u003D' | '\u003F'..'\u005B' | '\u005D'..'\u007F'
+     * sutf1 ::= \u0001'..'\u0021' | '\u0023'..'\u002A' | '\u002D'..'\u003A' |
+     *           '\u003D' | '\u003F'..'\u005B' | '\u005D'..'\u007F' 
+     * tutf1 ::= '\u0001'..'\u001F' | '\u0021' | '\u0023'..'\u002A' | '\u002D'..'\u003A' |
+     *           '\u003D' | '\u003F'..'\u005B' | '\u005D'..'\u007F' 
+     * utfmb ::= '\u0080'..'\uFFFE'
+     * pair ::= ESCESC | ESCSHARP | ESC special | HEXPAIR
+     * ESCESC ::= ESC ESC
+     * ESCSHARP ::= ESC SHARP
+     * special ::= DQUOTE | PLUS | COMMA | SEMI | LANGLE | RANGLE | SPACE | SHARP | EQUALS
+     * HEXPAIR ::= ESC HEX HEX
+     * HEXVALUE ::= SHARP ( HEX HEX )+
+     * HEX ::= DIGIT | 'a'..'f' | 'A'..'F';
+     * NUMBER ::= DIGIT | ( LDIGIT ( DIGIT )+ )
+     * DIGIT ::= '0'..'9'
+     * LDIGIT ::= '1'..'9'
+     * COMMA ::= ',' ('\u002C')
+     * DOT ::= '.' ('\u002E')
+     * DQUOTE ::= '"' ('\u0022')
+     * EQUALS ::= '=' ('\u003D')
+     * ESC ::= '\\' ('\u005C')
+     * HYPHEN ::= '-' ('\u002D')
+     * LANGLE ::= '<' ('\u003C')
+     * PLUS ::= '+' ('\u002B')
+     * RANGLE ::= '>' ('\u003E')
+     * SEMI ::= ';' ('\u003B')
+     * SHARP = '#' ('\u0023')
+     * SPACE ::= ' ' ('\u0020')
+     * UNDERSCORE = '_' ('\u005F')
+     * </pre>
+     * 
+     * Actually, in Java, the String is already converted to an array of unicode
+     * chars, so we can simplify the grammar to:
+     * 
+     * <pre>
+     * attributeValue = string | SHARP HEX HEX *(HEX HEX) | e
+     * string ::= [(LUTF1/Unicode/pair)[*(SUTF1/Unicode>/pair)(TUTF1/Unicode/pair)]]
+     * LUTF1 ::= %x01-1F / %x21 / %x24-2A / %x2D-3A / %x3D / %x3F-5B / %x5D-7F
+     * SUTF1 ::= %x01-21 /        %x23-2A / %x2D-3A / %x3D / %x3F-5B / %x5D-7F
+     * TUTF1 ::= %x01-1F / %x21 / %x23-2A / %x2D-3A / %x3D / %x3F-5B / %x5D-7F
+     * pair ::= ESC(ESC/DQUOTE/PLUS/COMMA/SEMI/LANGLE/RANGLE/SPACE/SHARP/EQUALS/HEX HEX)
+     *
+     * HEX ::= DIGIT | 'a'..'f' | 'A'..'F';
+     * COMMA ::= ',' ('\u002C')
+     * DIGIT ::= '0'..'9'
+     * DQUOTE ::= '"' ('\u0022')
+     * EQUALS ::= '=' ('\u003D')
+     * ESC ::= '\\' ('\u005C')
+     * LANGLE ::= '<' ('\u003C')
+     * PLUS ::= '+' ('\u002B')
+     * RANGLE ::= '>' ('\u003E')
+     * SEMI ::= ';' ('\u003B')
+     * SHARP = '#' ('\u0023')
+     * SPACE ::= ' ' ('\u0020')
+     * </pre>
+     */
+    private static Value parseAttributeValue( SchemaManager schemaManager1, AttributeType attributeType,
+            Position pos ) throws ParseException, LdapInvalidAttributeValueException
+    {
+        AttributeValuestates state = AttributeValuestates.START;
+                
+        // Used to keep a track of the last suite of spaces in the value.
+        int spaceNb = 0;
+        
+        // If the value starts with a '#', we will only have hex pairs
+        if ( isMatchChar( ( byte ) SHARP, pos ) ) 
+        {
+            // It's a hexstring. We can only have pairs of hex
+            byte[] valueHex = parseHexString( pos );
+            //String upValue = Strings.utf8ToString( pos.getBytes(), start, pos.start - start );
+            Value value = null;
+            
+            if ( attributeType.getSyntax() != null )
+            {
+                if ( attributeType.isHR() )
+                {
+                    value = new Value( attributeType, Strings.utf8ToString( valueHex ) );
+                }
+                else
+                {
+                    value = new Value( attributeType, valueHex );
+                }
+            }
+            else
+            {
+                value = new Value( valueHex );
+            }
+            
+            return value;
+        }
+        
+        boolean noValue = true;
+        StringBuilder upValue = new StringBuilder();
+
+        byte[] result = new byte[ pos.length << 1 ];
+        int[] bPos = new int[1];
+        bPos[0] = 0;
+        boolean completed = false;
+        
+        // Not a hexstring, it can be either a string or nothing (empty value)
+        while ( hasMoreChars( pos ) && !completed )
+        {
+            switch ( state )
+            {
+                case START:
+                    byte b = pos.getByte();
+                    
+                    if ( ( b >= 0 ) && ( b < 0x0080 ) )
+                    {
+                        // Pure ascii. Some chars aren't allowed in START state
+                        switch ( b )
+                        {
+                            case '\0':
+                            case SPACE:
+                                // Special case: we skip spaces at the beginning of a value
+                                upValue.append( SPACE );
+                                result[bPos[0]++] = b;
+                                pos.start++;
+                                break;
+
+                            case DQUOTE:
+                            case SHARP:
+                            case LANGLE:
+                            case RANGLE:
+                                // Forbidden, we will assume the Value is invalid
+                                throw new ParseException( I18n.err( 
+                                        I18n.ERR_13635_INVALID_VALUE_CHARACTER, ( char ) b ), pos.start );
+
+                            case PLUS:
+                            case COMMA:
+                            case SEMI_COLON:
+                                // Forbidden, we will assume the value is null
+                                Value value = new Value( Strings.EMPTY_STRING );
+
+                                return value;
+                                
+                            case ESC:
+                                // stringPair
+                                pos.start++;
+                                parsePair( pos, upValue, result, bPos );
+                                state = AttributeValuestates.STRING_MIDDLE; 
+                                
+                                break;
+
+                            default:
+                                upValue.append( ( char ) b );
+                                result[bPos[0]++] = b;
+                                state = AttributeValuestates.STRING_MIDDLE;
+                                pos.start++;
+                                break;
+                        }
+                    }
+                    else
+                    {
+                        // unicode, parse the UTFMB
+                        int utfmb = parseUTFMB( pos, result, bPos );
+                        
+                        // Can be 2 chars
+                        if ( utfmb > 0x0000FFFF )
+                        {
+                            // 2 chars
+                            char c1 = ( char ) ( utfmb >> 16 );
+                            char c2 = ( char ) ( utfmb & 0x0000FFFF );
+                            
+                            upValue.append( c1 );
+                            upValue.append( c2 );
+                        }
+                        else
+                        {
+                            // 1 char
+                            char c = ( char ) ( utfmb & 0x0000FFFF );
+                            upValue.append( c );
+                        }
+
+                        state = AttributeValuestates.STRING_MIDDLE;
+                    }
+                    
+                    noValue = false;
+                    break;
+                    
+                case STRING_MIDDLE:
+                    // Pretty much the same as for the START state, except that '#' is accepted, so is space
+                    b = pos.getByte();
+                    
+                    if ( ( b >= 0 ) && ( b < 0x0080 ) )
+                    {
+                        // Pure ascii. Some chars aren't allowed in STRING_MIDDLE state
+                        switch ( b )
+                        {
+                            case ' ':
+                                // Special case: we keep track of where we have seen spaces
+                                // so that we can eliminate them at the end of the value
+                                // parsing, because they are not significant. We just want to
+                                // keep track of the first space in a suite of spaces.
+                                spaceNb++;
+                                pos.start++;
+                                
+                                break;
+                                
+                            case '\0':
+                            case DQUOTE:
+                            case PLUS:
+                            case COMMA:
+                            case SEMI_COLON:
+                            case LANGLE:
+                            case RANGLE:
+                                // Forbidden, we will assume this is the end of the value
+                                completed = true;
+                                break;
+                                
+                            case ESC:
+                                // Add the previous spaces if needed
+                                if ( spaceNb > 0 )
+                                {
+                                    for ( int i = 0; i < spaceNb; i++ )
+                                    {
+                                        upValue.append( SPACE );
+                                        result[bPos[0]++] = ( byte ) SPACE;
+                                    }
+                                        
+                                    spaceNb = 0;
+                                }
+
+                                // stringPair in the middle of a String
+                                pos.start++;
+                                parsePair( pos, upValue, result, bPos );
+                                
+                                break;
+
+                            default:
+                                // Add the previous spaces if needed
+                                if ( spaceNb > 0 )
+                                {
+                                    for ( int i = 0; i < spaceNb; i++ )
+                                    {
+                                        upValue.append( SPACE );
+                                        result[bPos[0]++] = ( byte ) SPACE;
+                                    }
+                                        
+                                    spaceNb = 0;
+                                }
+
+                                upValue.append( ( char ) b );
+                                result[bPos[0]++] = b;
+                                pos.start++;
+
+                                break;
+                        }
+                    }
+                    else
+                    {
+                        // UTFMB, get the character in Unicode
+                        int utfmb = parseUTFMB( pos, result, bPos );
+                        
+                        // Add the previous spaces if needed
+                        if ( spaceNb > 0 )
+                        {
+                            for ( int i = 0; i < spaceNb; i++ )
+                            {
+                                upValue.append( SPACE );
+                                result[bPos[0]++] = ( byte ) SPACE;
+                            }
+                                
+                            spaceNb = 0;
+                        }
+
+                        // Can be 2 chars
+                        if ( utfmb > 0x0000FFFF )
+                        {
+                            // 2 chars
+                            char c1 = ( char ) ( utfmb >> 16 );
+                            char c2 = ( char ) ( utfmb & 0x0000FFFF );
+                            
+                            upValue.append( c1 );
+                            upValue.append( c2 );
+                        }
+                        else
+                        {
+                            // 1 char
+                            char c = ( char ) ( utfmb & 0x0000FFFF );
+                            upValue.append( c );
+                        }
+                        
+                        state = AttributeValuestates.STRING_MIDDLE;
+                    }
+                    
+                    noValue = false;
+                    
+                    break;
+                    
+                default:
+                    // can't be...
+            }
+        }
+
+        switch ( state )
+        {
+            case STRING_PAIR_HEX1:
+                // We are supposed to have a second hex char
+                throw new ParseException( I18n.err( I18n.ERR_13632_WRONG_HEX_PAIR ), pos.start );
+
+            case STRING_PAIR:
+                // We are supposed to have some escaped char
+                throw new ParseException( I18n.err( I18n.ERR_13631_WRONG_ESCAPED_SEQUENCE ), pos.start );
+                
+            default:
+                // Nothing to do...
+        }
+        
+        if ( noValue )
+        {
+            if ( ( attributeType != null ) && ( attributeType.getSyntaxOid() != null ) )
+            {
+                if ( attributeType.isHR() )
+                {
+                    // A String attribute
+                    return new Value( attributeType, Strings.EMPTY_STRING );
+                }
+                else
+                {
+                    return new Value( attributeType, Strings.EMPTY_BYTES );
+                }
+            }
+            else
+            {
+                // By default, we will consider teh value as HR if we don't have an AttributeType
+                return new Value( Strings.EMPTY_STRING );
+            }
+        }
+        else
+        {
+            //String upValueStr = Strings.utf8ToString( pos.getBytes(), start, pos.start - start );
+            byte[] realBytes = new byte[ bPos[0] ];
+            System.arraycopy( result, 0, realBytes, 0, bPos[0] );
+
+            if ( ( attributeType != null ) && ( attributeType.getSyntaxOid() != null ) )
+            {
+                if ( attributeType.isHR() )
+                {
+                    // A String attribute
+                    return new Value( attributeType, Strings.utf8ToString( realBytes ) );
+                }
+                else
+                {
+                    return new Value( attributeType, realBytes );
+                }
+            }
+            else
+            {
+                // By default, we will consider teh value as HR if we don't have an AttributeType
+                return new Value( Strings.utf8ToString( realBytes ) );
+            }
+        }
+    }
+
+    
+    /**
+     * Parse an attributeType. The grammar is the following:
+     * 
+     * <pre>
+     * attributeType ::= descr | numericoid
+     * descr ::= ALPHA ( ALPHA | DIGIT | HYPHEN | UNDERSCORE )*
+     * numericoid ::= NUMERICOID 
+     * NUMERICOID ::= ( "oid." )? NUMBER ( DOT NUMBER )+
+     * DOT::= '.'
+     * NUMBER ::= DIGIT | ( LDIGIT ( DIGIT )+ )
+     * LDIGIT ::= '1'..'9'
+     * ALPHA ::= 'a'..'z', ‘A’..’Z’
+     * DIGIT ::= '0'..'9'
+     * HYPHEN ::= '-'555567
+     * UNDERSCORE ::= '_'
+     * </pre>
+     * 
+     * It will use the following state machine:
+     * <pre>
+     * START --[a..zA..Z] / [oO]--> S1
+     * START --[1..9]--> S2
+     * START --'0'--> S3
+     * START --[oO]--> S4
+     * S1 --[a..zA..Z0..9-_]--> S1
+     * S1 --![a..zA..Z0..9-_]--> END
+     * S2 --[0..9]--> S2
+     * S2 --![0..9]--> error
+     * S2 --'.'--> S7
+     * S3 --'.'--> S7
+     * S3 --!'.'--> error
+     * S4 --[iI]--> S5
+     * S4 --[a..zA..Z0..9-_]/[iI]--> S1
+     * S4 --![a..zA..Z0..9-_]--> END
+     * S5 --[dD]--> S6
+     * S5 --[a..zA..Z0..9-_]/[dD]--> S1
+     * S5 --![a..zA..Z0..9-_]--> END
+     * S6 --'.'--> S7
+     * S6 --[a..zA..Z0..9-_]/'.'--> S1
+     * S6 --![a..zA..Z0..9-_.]--> END
+     * S7 --'0'-->S8
+     * S7 --[1..9]--> S9
+     * S7 --![0..9]--> error
+     * S8 --'.'--> S7
+     * S8 --!'.'--> error
+     * S9 --[0..9] -> S9
+     * S9 --'.'--> S7
+     * S9 --![0..9.]--> end
+     * </pre>
+     */
+    private static String parseAttributeType( Position pos ) throws ParseException
+    {
+        AttributTypeStates state = AttributTypeStates.START;
+        
+        int start = pos.start;
+        
+        while ( hasMoreChars( pos ) )
+        {
+            switch ( state )
+            {
+                case START:
+                    // We may have either a 'O', 'o' or an ALPHA (but 'o' or 'O') 
+                    // or a NUMBER
+                    byte b = pos.getByte();
+                    
+                    switch ( b )
+                    {
+                        case '0':
+                            // A numeric OID
+                            state = AttributTypeStates.STATE_3;
+                            pos.start++;
+                            
+                            break;
+
+                        case '1': case '2': case '3': case '4': case '5': 
+                        case '6': case '7': case '8': case '9':
+                            // Numeric oid again
+                            state = AttributTypeStates.STATE_2;
+                            pos.start++;
+                            
+                            break;
+
+                        case 'o': case 'O':
+                            // An 'oid.' start?
+                            state = AttributTypeStates.STATE_4;
+                            pos.start++;
+                            
+                            break;
+                            
+                        case 'a': case 'b': case 'c': case 'd': case 'e': 
+                        case 'f': case 'g': case 'h': case 'i': case 'j': 
+                        case 'k': case 'l': case 'm': /* no 'o' */case 'n':
+                        case 'p': case 'q': case 'r': case 's': case 't': 
+                        case 'u': case 'v': case 'w': case 'x': case 'y': 
+                        case 'z': case 'A': case 'B': case 'C': case 'D': 
+                        case 'E': case 'F': case 'G': case 'H': case 'I': 
+                        case 'J': case 'K': case 'L': case 'M': case 'N': 
+                        /* no 'O' */case 'P': case 'Q': case 'R': case 'S': 
+                        case 'T': case 'U': case 'V': case 'W': case 'X': 
+                        case 'Y': case 'Z': case '-': case '_':
+                            // A descr
+                            state = AttributTypeStates.STATE_1;
+                            pos.start++;
+                            
+                            break;
+                        
+                        default:
+                            // Not an attributeType...
+                            LOG.debug( "Cannot parse an attributeType at {}", pos );
+                            
+                            return "";
+                    }
+                    
+                    break;
+                    
+                case STATE_1:
+                    // We may have either an ALPHA or a NUMBER, or '-' or '_'
+                    b = pos.getByte();
+                    
+                    switch ( b )
+                    {
+                        case '0': case '1': case '2': case '3': case '4': 
+                        case '5': case '6': case '7': case '8': case '9':
+                        case 'a': case 'b': case 'c': case 'd': case 'e': 
+                        case 'f': case 'g': case 'h': case 'i': case 'j': 
+                        case 'k': case 'l': case 'm': case 'o': case 'n':
+                        case 'p': case 'q': case 'r': case 's': case 't': 
+                        case 'u': case 'v': case 'w': case 'x': case 'y': 
+                        case 'z': case 'A': case 'B': case 'C': case 'D': 
+                        case 'E': case 'F': case 'G': case 'H': case 'I': 
+                        case 'J': case 'K': case 'L': case 'M': case 'N': 
+                        case 'O': case 'P': case 'Q': case 'R': case 'S': 
+                        case 'T': case 'U': case 'V': case 'W': case 'X': 
+                        case 'Y': case 'Z': case '-': case '_':
+                            // A descr continuation
+                            state = AttributTypeStates.STATE_1;
+                            pos.start++;
+                            
+                            break;
+                        
+                        default:
+                            // The end, return the found attributeType
+                            String attributType = Strings.utf8ToString( pos.getBytes(), start, pos.start - start );
+                            LOG.debug( "Parsed attributeType '{}'", attributType );
+                            
+                            return attributType;
+                    }
+                    
+                    break;
+
+                case STATE_2:
+                    b = pos.getByte();
+                    
+                    switch ( b )
+                    {
+                        case '0': case '1': case '2': case '3': case '4': 
+                        case '5': case '6': case '7': case '8': case '9':
+                            // Numeric oid or descr, we don't know yet
+                            state = AttributTypeStates.STATE_2;
+                            pos.start++;
+                            
+                            break;
+                            
+                        case '.':
+                            // Definitively numericOid
+                            state = AttributTypeStates.STATE_7;
+                            pos.start++;
+                            
+                            break;
+                            
+                        default:
+                            // We should have at least another '.' and number
+                            String attributType = Strings.getString( 
+                                    pos.getBytes(), start, pos.start - start, StandardCharsets.UTF_8 );
+                            LOG.error( "Wrong attributeType '{}' at {}", attributType, pos );
+                            
+                            throw new ParseException( 
+                                    I18n.err( I18n.ERR_13630_BAD_OID_ATTRIBUTE_TYPE, pos ), pos.start );
+                    }
+                    
+                    break;
+
+                case STATE_3:
+                    b = pos.getByte();
+                    pos.start++;
+                    
+                    if ( b == '.' )
+                    {
+                        // An NUMERICOID starting with '0.'
+                        state = AttributTypeStates.STATE_7;
+                    }
+                    else
+                    {
+                        // We should have at least another '.' and number
+                        String attributType = Strings.getString( 
+                                pos.getBytes(), start, pos.start - start, StandardCharsets.UTF_8 );
+                        LOG.error( "Wrong attributeType '{}' at {}", attributType, pos );
+                        
+                        throw new ParseException( 
+                                I18n.err( I18n.ERR_13630_BAD_OID_ATTRIBUTE_TYPE, pos ), pos.start );
+                    }
+                    
+                    break;
+                    
+                case STATE_4:
+                    // We may have either an ALPHA but 'i' or 'I',  or a NUMBER, 
+                    // or '-' or '_' or 'i' or 'I'
+                    b = pos.getByte();
+                    
+                    switch ( b )
+                    {
+                        case 'i': case 'I':
+                            // An 'oid.' start?
+                            state = AttributTypeStates.STATE_5;
+                            pos.start++;
+                            
+                            break;
+                    
+                        case '0': case '1': case '2': case '3': case '4': 
+                        case '5': case '6': case '7': case '8': case '9':
+                        case 'a': case 'b': case 'c': case 'd': case 'e': 
+                        case 'f': case 'g': case 'h': /* not 'i' */ case 'j': 
+                        case 'k': case 'l': case 'm': case 'o': case 'n':
+                        case 'p': case 'q': case 'r': case 's': case 't': 
+                        case 'u': case 'v': case 'w': case 'x': case 'y': 
+                        case 'z': case 'A': case 'B': case 'C': case 'D': 
+                        case 'E': case 'F': case 'G': case 'H': /* not 'I' */ 
+                        case 'J': case 'K': case 'L': case 'M': case 'N': 
+                        case 'O': case 'P': case 'Q': case 'R': case 'S': 
+                        case 'T': case 'U': case 'V': case 'W': case 'X': 
+                        case 'Y': case 'Z': case '-': case '_':
+                            // A descr continuation
+                            state = AttributTypeStates.STATE_1;
+                            pos.start++;
+                            
+                            break;
+                        
+                        default:
+                            // the descr is just 'o' or 'O'
+                            String attributType = Strings.getString( 
+                                    pos.getBytes(), start, pos.start - start, StandardCharsets.UTF_8 );
+                            LOG.debug( "Parsed attributeType '{}'", attributType );
+                            
+                            return attributType;
+                    }
+                    
+                    break;
+
+                case STATE_5:
+                    // We may have either an ALPHA but 'd' or 'D',  or a NUMBER, 
+                    // or '-' or '_', or 'd' or 'D'
+                    b = pos.getByte();
+                    
+                    switch ( b )
+                    {
+                        case 'd': case 'D':
+                            // An 'oid.' start?
+                            state = AttributTypeStates.STATE_6;
+                            pos.start++;
+                            
+                            break;
+                    
+                        case '0': case '1': case '2': case '3': case '4': 
+                        case '5': case '6': case '7': case '8': case '9':
+                        case 'a': case 'b': case 'c': /* not 'd' */ case 'e': 
+                        case 'f': case 'g': case 'h': case 'i': case 'j': 
+                        case 'k': case 'l': case 'm': case 'o': case 'n':
+                        case 'p': case 'q': case 'r': case 's': case 't': 
+                        case 'u': case 'v': case 'w': case 'x': case 'y': 
+                        case 'z': case 'A': case 'B': case 'C': /* not 'D' */ 
+                        case 'E': case 'F': case 'G': case 'H': case 'I':
+                        case 'J': case 'K': case 'L': case 'M': case 'N': 
+                        case 'O': case 'P': case 'Q': case 'R': case 'S': 
+                        case 'T': case 'U': case 'V': case 'W': case 'X': 
+                        case 'Y': case 'Z': case '-': case '_':
+                            // A descr continuation
+                            state = AttributTypeStates.STATE_1;
+                            pos.start++;
+                            
+                            break;
+                        
+                        default:
+                            // the descr is just '[oO][iI]' 
+                            String attributType = Strings.getString( 
+                                    pos.getBytes(), start, pos.start - start, StandardCharsets.UTF_8 );
+                            LOG.debug( "Parsed attributeType '{}'", attributType );
+                            
+                            return attributType;
+                    }
+                    
+                    break;
+
+                case STATE_6:
+                    // We may have either an ALPHA or a NUMBER, 
+                    // or '-' or '_', or '.'
+                    b = pos.getByte();
+                    
+                    switch ( b )
+                    {
+                        case '.':
+                            // An 'oid.' start?
+                            state = AttributTypeStates.STATE_7;
+                            pos.start++;
+                            
+                            break;
+                    
+                        case '0': case '1': case '2': case '3': case '4': 
+                        case '5': case '6': case '7': case '8': case '9':
+                        case 'a': case 'b': case 'c': case 'd': case 'e': 
+                        case 'f': case 'g': case 'h': case 'i': case 'j': 
+                        case 'k': case 'l': case 'm': case 'o': case 'n':
+                        case 'p': case 'q': case 'r': case 's': case 't': 
+                        case 'u': case 'v': case 'w': case 'x': case 'y': 
+                        case 'z': case 'A': case 'B': case 'C': case 'D': 
+                        case 'E': case 'F': case 'G': case 'H': case 'I':
+                        case 'J': case 'K': case 'L': case 'M': case 'N': 
+                        case 'O': case 'P': case 'Q': case 'R': case 'S': 
+                        case 'T': case 'U': case 'V': case 'W': case 'X': 
+                        case 'Y': case 'Z': case '-': case '_':
+                            // A descr continuation
+                            state = AttributTypeStates.STATE_1;
+                            pos.start++;
+                            
+                            break;
+                        
+                        default:
+                            // the descr is just '[oO][iI][dD]' 
+                            String attributType = Strings.getString( 
+                                    pos.getBytes(), start, pos.start - start, StandardCharsets.UTF_8 );
+                            LOG.debug( "Parsed attributeType '{}'", attributType );
+                            
+                            return attributType;
+                    }
+                    
+                    break;
+
+                case STATE_7:
+                    b = pos.getByte();
+                    
+                    switch ( b )
+                    {
+                        case '0': 
+                            state = AttributTypeStates.STATE_8;
+                            pos.start++;
+                            
+                            break;
+
+                        case '1': case '2': case '3': case '4': case '5': 
+                        case '6': case '7': case '8': case '9':
+                            state = AttributTypeStates.STATE_9;
+                            pos.start++;
+                            
+                            break;
+                            
+                        default:
+                            // We should have at least another '.' and number
+                            String attributType = Strings.getString( 
+                                    pos.getBytes(), start, pos.start - start, StandardCharsets.UTF_8 );
+                            LOG.error( "Wrong attributeType '{}' at {}", attributType, pos );
+                            
+                            throw new ParseException( 
+                                    I18n.err( I18n.ERR_13630_BAD_OID_ATTRIBUTE_TYPE, pos ), pos.start );
+                    }
+                    
+                    break;
+
+                case STATE_8:
+                    b = pos.getByte();
+
+                    if ( b == DOT )
+                    {
+                        state = AttributTypeStates.STATE_7;
+                        pos.start++;
+                        
+                        break;
+                    }
+                    else
+                    {
+                        // Then end, the numericOID is complete
+                        String attributType = Strings.getString( 
+                                pos.getBytes(), start, pos.start - start, StandardCharsets.UTF_8 );
+                        LOG.debug( "Parsed attributeType '{}'", attributType );
+                        
+                        return attributType;
+                    }
+                    
+                case STATE_9:
+                    b = pos.getByte();
+                    
+                    switch ( b )
+                    {
+                        case '0': case '1': case '2': case '3': case '4': 
+                        case '5': case '6': case '7': case '8': case '9':
+                            state = AttributTypeStates.STATE_9;
+                            pos.start++;
+                            
+                            break;
+                            
+                        case '.':
+                            state = AttributTypeStates.STATE_7;
+                            pos.start++;
+                            
+                            break;
+                            
+                        default:
+                            // The end, the numericOID is complete
+                            String attributType = Strings.getString( 
+                                    pos.getBytes(), start, pos.start - start, StandardCharsets.UTF_8 );
+                            LOG.debug( "Parsed attributeType '{}'", attributType );
+                            
+                            return attributType;
+                    }
+                    
+                    break;
+                    
+                default: 
+                    // Can't be...
+            }
+        }
+        
+        // Depending on the current state, the end of stream might correspond to an error
+        switch ( state )
+        {
+            case START:
+                // Not an attributeType...
+                LOG.debug( "Cannot parse an attributeType at {}", pos );
+                return "";
+
+            case STATE_1:
+            case STATE_4:
+            case STATE_5:
+            case STATE_6:
+            case STATE_9:
+            case STATE_8:
+               // The end, return the found attributeType
+                String attributType = Strings.getString( 
+                        pos.getBytes(), start, pos.start - start, StandardCharsets.UTF_8 );
+                LOG.debug( "Parsed attributeType '{}'", attributType );
+                
+                return attributType;
+
+            case STATE_2:
+            case STATE_3:
+                // We should have at least another '.' and number
+                attributType = Strings.getString( 
+                        pos.getBytes(), start, pos.start - start, StandardCharsets.UTF_8 );
+                LOG.error( "Wrong attributeType '{}' at {}", attributType, pos );
+                
+                throw new ParseException( I18n.err( I18n.ERR_13630_BAD_OID_ATTRIBUTE_TYPE, attributType ), pos.start );
+
+            case STATE_7:
+                // We should have at least another '.' and number
+                attributType = Strings.getString( 
+                        pos.getBytes(), start, pos.start - start, StandardCharsets.UTF_8 );
+                LOG.error( "Wrong attributeType '{}' at {}", attributType, pos );
+                
+                throw new ParseException( 
+                        I18n.err( I18n.ERR_13630_BAD_OID_ATTRIBUTE_TYPE, pos ), pos.start );
+
+            default:
+                return "";
+        }
+    }
+
+    
+    /**
+     * Parse an AttributeType and Value. The grammar is the following:
+     * 
+     * <pre>
+     * attributeTypeAndValue ::=
+     *   ( SPACE )* attributeType ( SPACE )* EQUALS ( SPACE )* attributeValue ( SPACE )*
+     * </pre>
+     */
+    private static Ava parseAttributeTypeAndValue( SchemaManager schemaManager, byte[] bytes,
+            Position pos ) throws LdapInvalidDnException, ParseException, LdapInvalidAttributeValueException
+    {
+        AttributeType attributeType = null;
+        StringBuilder upName = new StringBuilder();
+        
+        int start = pos.start;
+
+        // ( SPACE )*
+        while ( isMatchChar( ( byte ) SPACE, pos ) )
+        {
+            upName.append( SPACE );
+        }
+        
+        // attributeType
+        String type = parseAttributeType( pos );
+        
+        if ( Strings.isEmpty( type ) )
+        {
+            // If we don't have a type, it's an error
+            throw new LdapInvalidDnException( I18n.err( I18n.ERR_13622_DN_OR_RDN_NULL, pos ) );
+        }
+                    
+        upName.append( type );
+        
+        if ( schemaManager != null )
+        {
+            String realType = type;
+            
+            // Special case for oid.xxx attributeType
+            if ( Strings.toLowerCaseAscii( type ).startsWith( "oid." ) )
+            {
+                realType = type.substring( 4 );
+            }
+
+            attributeType = schemaManager.getAttributeType( realType );
+        }
+        else
+        {
+            attributeType = new AttributeType( type );
+        }
+        
+        // ( SPACE )*
+        while ( isMatchChar( ( byte ) SPACE, pos ) )
+        {
+            upName.append( SPACE );
+        }
+        
+        // EQUALS
+        matchChar( bytes, ( byte ) EQUAL, pos );
+        upName.append( EQUAL );
+                
+        // ( SPACE )*
+        while ( isMatchChar( ( byte ) SPACE, pos ) )
+        {
+            upName.append( SPACE );
+        }
+
+        start = pos.start;
+
+        // And now, the value
+        Value value = parseAttributeValue( schemaManager, attributeType, pos );
+
+        String upValue = Strings.utf8ToString( bytes, start, pos.start - start );
+        
+        Ava ava = new Ava( schemaManager, type,  Strings.lowerCaseAscii( Strings.trim( type ) ), value );
+        upName.append( upValue );
+        ava.upName = upName.toString();
+        
+        return ava;
+    }
+
+
+    /**
+     * Parse a RDN. The grammar is the following:
+     * <pre>
+     * relativeDistinguishedName ::=
+     *     attributeTypeAndValue ( PLUS attributeTypeAndValue )*
+     * </pre>
+     */
+    private static void parseRelativeDistinguishedName( SchemaManager schemaManager, byte[] bytes, Rdn rdn, 
+            Position pos ) throws LdapInvalidDnException, ParseException, LdapInvalidAttributeValueException
+    {
+        StringBuilder rdnUpStr = new StringBuilder();
+        StringBuilder rdnNormStr = new StringBuilder();
+
+        // The list of parsed Ava for a later post-processing, if needed
+        boolean isFirst = true;
+        Ava ava = null;
+
+        while ( hasMoreChars( pos ) )
+        {
+            if ( isFirst )
+            {
+                isFirst = false;
+            }
+            else
+            {
+                if ( isMatchChar( ( byte ) PLUS, pos ) )
+                {
+                    rdnUpStr.append( PLUS );
+                    rdn.avas = Rdn.addOrdered( rdn.avas, ava );
+                    ava = null;
+                }
+                else
+                {
+                    // No more AVA
+                    break;
+                }
+            }
+            
+            ava = parseAttributeTypeAndValue( schemaManager, bytes, pos );
+            
+            rdn.nbAvas++;
+            rdnUpStr.append( ava.getName() );
+        }
+        
+        if ( rdn.nbAvas > 1 )
+        {
+            rdn.avas = Rdn.addOrdered( rdn.avas, ava );
+        }
+        else
+        {
+            rdn.ava = ava;
+        }
+        
+        // Now, build the Rdn
+        switch ( rdn.nbAvas )
+        {
+            case 0:
+                // Can't be...
+                
+            case 1:
+                // One single Ava
+                rdn.upName = rdnUpStr.toString();
+                rdn.normName = createNormAva( rdn.ava );
+                rdn.avaType = rdn.ava.getType();
+                break;
+
+            default:
+                rdn.ava = null;                
+                rdn.upName = rdnUpStr.toString();
+                rdn.avaTypes = new HashMap<String, List<Ava>>();
+                isFirst = true;
+
+                for ( Ava parsedAva : rdn.avas )
+                {
+                    if ( isFirst  )
+                    {
+                        isFirst = false;
+                    }
+                    else
+                    {
+                        rdnNormStr.append( PLUS );
+                    }
+
+                    String type;
+
+                    if ( schemaManager != null )
+                    {
+                        // TODO: what if parsedAva.getAttributeType() returns null?
+                        type = parsedAva.getAttributeType().getOid();
+                    }
+                    else
+                    {
+                        type = parsedAva.normType;
+                    }
+
+                    rdnNormStr.append( createNormAva( parsedAva ) );
+
+                    List<Ava> avaList = rdn.avaTypes.get( type );
+
+                    if ( avaList == null )
+                    {
+                        avaList = new ArrayList<>();
+                    }
+
+                    avaList.add( parsedAva );
+                    rdn.avaTypes.put( type, avaList );
+                }
+
+                rdn.normName = rdnNormStr.toString();
+
+                break;
+        }
+
+        rdn.hashCode();
+    }
+
+
+    /**
+     * Parses an Rdn.
+     * 
+     * @param schemaManager The SchemaManager
+     * @param name the string representation of the relative distinguished name
+     * @param rdn the (empty) Rdn where parsed ATAVs are put into
+     * 
+     * @throws LdapInvalidDnException the invalid name exception
+     */
+    /* No protection*/static void parseRdn( SchemaManager schemaManager, String name, Rdn rdn ) throws LdapInvalidDnException
+    {
+        if ( Strings.isNotEmpty( name ) )
+        {
+            try
+            {
+                // First convert the String to an UTF-8 byte[]
+                byte[] bytes = Strings.getBytesUtf8( name );
+                
+                parseRdn( schemaManager, bytes, rdn );
+            }
+            catch ( Exception e )
+            {
+                throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, e.getMessage(), e );
+            }
+        }
+    }
+
+
+    /**
+     * Parses an Rdn.
+     * 
+     * @param schemaManager The SchemaManager
+     * @param name the string representation of the relative distinguished name
+     * @param rdn the (empty) Rdn where parsed ATAVs are put into
+     * 
+     * @throws LdapInvalidDnException the invalid name exception
+     */
+    /* No protection*/static void parseRdn( SchemaManager schemaManager, byte[] bytes, Rdn rdn ) throws LdapInvalidDnException
+    {
+        try
+        {
+            Position pos = new Position( bytes );
+            pos.length = bytes.length;
+            
+            // We must have a following RDN, let's parse it
+            parseRelativeDistinguishedName( schemaManager, bytes, rdn, pos );
+        }
+        catch ( Exception e )
+        {
+            throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, e.getMessage(), e );
+        }
+    }
+    
+    
+    /**
+     * Parse the following grammar:
+     * <pre>
+     * distinguishedName :== [ relativeDistinguishedName
+          *( COMMA relativeDistinguishedName ) ]
+     * </pre>
+     * 
+     * @param schemaManager The SchemaManager instance. May be null
+     * @param bytes The DN to parse, stored in a byte[]
+     * @param rdns 
+     * @return
+     * @throws LdapInvalidDnException
+     */
+    /* no protection */static String parseDn( SchemaManager schemaManager, byte[] bytes, List<Rdn> rdns ) 
+            throws LdapInvalidDnException
+    {
+        if ( Strings.isEmpty( bytes ) )
+        {
+            return Strings.EMPTY_STRING;
+        }
+        
+        try
+        {
+            Position pos = new Position( bytes );
+            pos.length = bytes.length;
+            
+            StringBuilder dnNormSb = new StringBuilder();
+            boolean isFirst = true;
+            
+            while ( hasMoreChars( pos ) )
+            {
+                if ( isFirst )
+                {
+                    isFirst = false;
+                }
+                else
+                {
+                    // We accept ',' (RFC 2253/4514 form) or ';' (RFC 1779 form) 
+                    // as a separator between RDNs
+                    if ( isMatchChar( ( byte ) COMMA, pos ) || isMatchChar( ( byte ) SEMI_COLON, pos ) )
+                    {
+                        // Add the parsed RDN to the RDN list
+                        dnNormSb.append( COMMA );
+                    }
+                    else
+                    {
+                        // The end...
+                        break;
+                    }
+                }
+
+                // We must have a following RDN, let's parse it
+                Rdn rdn = new Rdn( schemaManager );
+                parseRelativeDistinguishedName( schemaManager, bytes, rdn, pos );
+                
+                rdns.add( rdn ); 
+                dnNormSb.append( rdn.getNormName() );
+            }
+            
+            // No more char, return the normalized dn
+            return dnNormSb.toString();
+        }
+        catch ( Exception e )
+        {
+            throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, e.getMessage(), e );
+        }
+    }
+
+    
+    /* no protection */static String parseDn( SchemaManager schemaManager, String upName, List<Rdn> rdns ) 
+            throws LdapInvalidDnException
+    {
+        if ( ( upName == null ) || Strings.isEmpty( upName.trim() ) )
+        {
+            return Strings.EMPTY_STRING;
+        }
+        
+        byte[] bytes = Strings.getBytesUtf8( upName );
+        
+        return parseDn( schemaManager, bytes, rdns );
+    }
+
+    
+    /**
+     * Parses a Dn from a String
+     *
+     * @param name The Dn to parse
+     * @return A valid Dn
+     * @throws org.apache.directory.api.ldap.model.exception.LdapException If the Dn was invalid
+     */
+    /* No protection*/static Dn parseDn( String name ) throws LdapException
+    {
+        Dn dn = new Dn();
+        String normName = parseDn( null, name, dn.rdns );
+        dn.setUpName( name );
+        dn.setNormName( normName );
+        
+        return dn;
+    }
+}
diff --git a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/FastDnParser.java b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/FastDnParser.java
deleted file mode 100644
index 2f9c8d95b..000000000
--- a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/FastDnParser.java
+++ /dev/null
@@ -1,671 +0,0 @@
-/*
- *  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
- *  
- *    https://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.api.ldap.model.name;
-
-
-import java.util.List;
-
-import org.apache.directory.api.i18n.I18n;
-import org.apache.directory.api.ldap.model.exception.LdapException;
-import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
-import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
-import org.apache.directory.api.ldap.model.schema.AttributeType;
-import org.apache.directory.api.ldap.model.schema.SchemaManager;
-import org.apache.directory.api.util.Position;
-import org.apache.directory.api.util.Strings;
-
-
-/**
- * A fast LDAP Dn parser that handles only simple DNs. If the Dn contains
- * any special character an {@link LdapInvalidDnException} is thrown.
- *
- * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
- */
-/* No protection*/enum FastDnParser
-{
-    INSTANCE;
-
-    /**
-     * Parses a Dn from a String
-     *
-     * @param name The Dn to parse
-     * @return A valid Dn
-     * @throws org.apache.directory.api.ldap.model.exception.LdapException If the Dn was invalid
-     */
-    /* No protection*/static Dn parse( String name ) throws LdapException
-    {
-        Dn dn = new Dn();
-        parseDn( null, name, dn );
-        
-        return dn;
-    }
-
-
-    /**
-     * Parses a Dn from a String
-     *
-     * @param schemaManager The SchemaManager
-     * @param name The Dn to parse
-     * @return A valid Dn
-     * @throws LdapException If the Dn was invalid
-     */
-    /* No protection*/static Dn parse( SchemaManager schemaManager, String name ) throws LdapException
-    {
-        Dn dn = new Dn( schemaManager );
-        parseDn( schemaManager, name, dn );
-        
-        return dn;
-    }
-
-
-    /**
-     * Parses the given name string and fills the given Dn object.
-     * 
-     * @param schemaManager The SchemaManager
-     * @param name the name to parse
-     * @param dn the Dn to fill
-     * 
-     * @throws LdapInvalidDnException the invalid name exception
-     */
-    /* No protection*/static void parseDn( SchemaManager schemaManager, String name, Dn dn ) throws LdapInvalidDnException
-    {
-        String normName = parseDn( schemaManager, name, dn.rdns );
-        dn.setUpName( name );
-        dn.setNormName( normName );
-    }
-
-
-    /* No protection*/static String parseDn( SchemaManager schemaManager, String name, List<Rdn> rdns ) throws LdapInvalidDnException
-    {
-        if ( ( name == null ) || ( name.trim().length() == 0 ) )
-        {
-            // We have an empty Dn, just get out of the function.
-            return "";
-        }
-
-        Position pos = new Position();
-        char[] chars = name.toCharArray();
-
-        pos.start = 0;
-        pos.length = chars.length;
-        StringBuilder sb = new StringBuilder();
-
-        while ( true )
-        {
-            Rdn rdn = new Rdn( schemaManager );
-            parseRdnInternal( schemaManager, name, pos, rdn );
-            sb.append( rdn.getNormName() );
-            rdns.add( rdn );
-
-            if ( !hasMoreChars( pos ) )
-            {
-                // end of line reached
-                break;
-            }
-
-            char c = nextChar( chars, pos, true );
-
-            switch ( c )
-            {
-                case ',':
-                case ';':
-                    // another Rdn to parse
-                    sb.append( ',' );
-                    break;
-
-                default:
-                    throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, I18n.err( I18n.ERR_13601_EXPECTED_COMMA_SEMI_COLON, c,
-                        pos.start ) );
-            }
-        }
-        
-        return sb.toString();
-    }
-
-
-    /**
-     * Parses the given name string and fills the given Rdn object.
-     * 
-     * @param schemaManager The SchemaManager
-     * @param name the name to parse
-     * @param rdn the Rdn to fill
-     * 
-     * @throws LdapInvalidDnException the invalid name exception
-     */
-    /* No protection*/static void parseRdn( SchemaManager schemaManager, String name, Rdn rdn ) throws LdapInvalidDnException
-    {
-        if ( Strings.isEmpty( name ) )
-        {
-            throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, I18n.err( I18n.ERR_13602_RDN_EMPTY ) );
-        }
-
-        Position pos = new Position();
-        pos.start = 0;
-        pos.length = name.length();
-
-        parseRdnInternal( schemaManager, name, pos, rdn );
-    }
-
-
-    private static void parseRdnInternal( SchemaManager schemaManager, String name, Position pos, Rdn rdn ) throws LdapInvalidDnException
-    {
-        StringBuilder sbNormName = new StringBuilder();
-        int rdnStart = pos.start;
-        char[] chars = name.toCharArray();
-
-        // SPACE*
-        matchSpaces( chars, pos );
-
-        // attributeType: ALPHA (ALPHA|DIGIT|HYPEN) | NUMERICOID
-        String type = matchAttributeType( chars, pos );
-
-        // SPACE*
-        matchSpaces( chars, pos );
-
-        // EQUALS
-        matchEquals( chars, pos );
-
-        // SPACE*
-        matchSpaces( chars, pos );
-
-        // here we only match "simple" values
-        // stops at \ + # " -> Too Complex Exception
-        String upValue = matchValue( chars, pos );
-
-        if ( rdn != null )
-        {
-            String upName = name.substring( rdnStart, pos.start );
-            Ava ava = new Ava( schemaManager, type, upValue );
-            
-            rdn.addAVA( schemaManager, ava );
-        
-            if ( schemaManager != null )
-            {
-                AttributeType attributeType = ava.getAttributeType();
-                
-                if ( attributeType != null )
-                {
-                    sbNormName.append( ava.getNormType() );
-                    sbNormName.append( '=' );
-                    sbNormName.append( ava.getValue().getNormalized() );
-                }
-                else
-                {
-                    sbNormName.append( type );
-                    sbNormName.append( '=' );
-                    sbNormName.append( upValue );
-                }
-            }
-            else
-            {
-                sbNormName.append( Strings.toLowerCaseAscii( type ) );
-                sbNormName.append( '=' );
-                sbNormName.append( upValue );
-            }
-
-            rdn.setUpName( upName );
-            rdn.setNormName( sbNormName.toString() );
-            rdn.hashCode();
-        }
-    }
-
-
-    /**
-     * Matches and forgets optional spaces.
-     * 
-     * @param name the name
-     * @param pos the pos
-     * @throws LdapInvalidDnException If some invalid chars are found 
-     */
-    private static void matchSpaces( char[] name, Position pos ) throws LdapInvalidDnException
-    {
-        while ( hasMoreChars( pos ) )
-        {
-            char c = nextChar( name, pos, true );
-
-            if ( c != ' ' )
-            {
-                pos.start--;
-                break;
-            }
-        }
-    }
-
-
-    /**
-     * Matches attribute type.
-     * 
-     * @param name the name
-     * @param pos the pos
-     * 
-     * @return the matched attribute type
-     * 
-     * @throws LdapInvalidDnException the invalid name exception
-     */
-    private static String matchAttributeType( char[] name, Position pos ) throws LdapInvalidDnException
-    {
-        char c = nextChar( name, pos, false );
-
-        switch ( c )
-        {
-            case 'A':
-            case 'B':
-            case 'C':
-            case 'D':
-            case 'E':
-            case 'F':
-            case 'G':
-            case 'H':
-            case 'I':
-            case 'J':
-            case 'K':
-            case 'L':
-            case 'M':
-            case 'N':
-            case 'O':
-            case 'P':
-            case 'Q':
-            case 'R':
-            case 'S':
-            case 'T':
-            case 'U':
-            case 'V':
-            case 'W':
-            case 'X':
-            case 'Y':
-            case 'Z':
-            case 'a':
-            case 'b':
-            case 'c':
-            case 'd':
-            case 'e':
-            case 'f':
-            case 'g':
-            case 'h':
-            case 'i':
-            case 'j':
-            case 'k':
-            case 'l':
-            case 'm':
-            case 'n':
-            case 'o':
-            case 'p':
-            case 'q':
-            case 'r':
-            case 's':
-            case 't':
-            case 'u':
-            case 'v':
-            case 'w':
-            case 'x':
-            case 'y':
-            case 'z':
-                // descr
-                return matchAttributeTypeDescr( name, pos );
-
-            case '0':
-            case '1':
-            case '2':
-            case '3':
-            case '4':
-            case '5':
-            case '6':
-            case '7':
-            case '8':
-            case '9':
-                // numericoid
-                return matchAttributeTypeNumericOid( name, pos );
-
-            default:
-                // error
-                throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, I18n.err( I18n.ERR_13604_START_AT_EXPECTED, c,
-                    pos.start ) );
-        }
-    }
-
-
-    /**
-     * Matches attribute type descr.
-     * 
-     * @param name the name
-     * @param pos the pos
-     * 
-     * @return the attribute type descr
-     * 
-     * @throws LdapInvalidDnException the invalid name exception
-     */
-    private static String matchAttributeTypeDescr( char[] name, Position pos ) throws LdapInvalidDnException
-    {
-        int start = pos.start;
-
-        while ( hasMoreChars( pos ) )
-        {
-            char c = nextChar( name, pos, true );
-
-            switch ( c )
-            {
-                case 'A':
-                case 'B':
-                case 'C':
-                case 'D':
-                case 'E':
-                case 'F':
-                case 'G':
-                case 'H':
-                case 'I':
-                case 'J':
-                case 'K':
-                case 'L':
-                case 'M':
-                case 'N':
-                case 'O':
-                case 'P':
-                case 'Q':
-                case 'R':
-                case 'S':
-                case 'T':
-                case 'U':
-                case 'V':
-                case 'W':
-                case 'X':
-                case 'Y':
-                case 'Z':
-                case 'a':
-                case 'b':
-                case 'c':
-                case 'd':
-                case 'e':
-                case 'f':
-                case 'g':
-                case 'h':
-                case 'i':
-                case 'j':
-                case 'k':
-                case 'l':
-                case 'm':
-                case 'n':
-                case 'o':
-                case 'p':
-                case 'q':
-                case 'r':
-                case 's':
-                case 't':
-                case 'u':
-                case 'v':
-                case 'w':
-                case 'x':
-                case 'y':
-                case 'z':
-                case '0':
-                case '1':
-                case '2':
-                case '3':
-                case '4':
-                case '5':
-                case '6':
-                case '7':
-                case '8':
-                case '9':
-                case '-':
-                case '_':
-                    // Violation of the RFC, just because those idiots at Microsoft decided to support it...
-                    break;
-
-                case ' ':
-                case '=':
-                    pos.start--;
-                    return new String( name, start, pos.start - start );
-
-                case '.':
-                    // occurs for RDNs of form "oid.1.2.3=test"
-                    throw TooComplexDnException.INSTANCE;
-
-                default:
-                    // error
-                    throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, I18n.err( I18n.ERR_13605_START_AT_DESCR_EXPECTED, c,
-                        pos.start ) );
-            }
-        }
-
-        return new String( name, start, pos.start - start );
-    }
-
-
-    /**
-     * Matches attribute type numeric OID.
-     * 
-     * @param name the name
-     * @param pos the pos
-     * 
-     * @return the attribute type OID
-     * 
-     * @throws org.apache.directory.api.ldap.model.exception.LdapInvalidDnException the invalid name exception
-     */
-    private static String matchAttributeTypeNumericOid( char[] name, Position pos ) throws LdapInvalidDnException
-    {
-        int dotCount = 0;
-        int start = pos.start;
-
-        while ( true )
-        {
-            char c = nextChar( name, pos, true );
-
-            switch ( c )
-            {
-                case '0':
-                    // leading '0', no other digit may follow!
-                    c = nextChar( name, pos, true );
-
-                    switch ( c )
-                    {
-                        case '.':
-                            dotCount++;
-                            break;
-
-                        case ' ':
-                        case '=':
-                            pos.start--;
-                            break;
-
-                        default:
-                            throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, I18n.err(
-                                I18n.ERR_13606_EXPECTED_NUMERICOID, c, pos.start ) );
-                    }
-
-                    break;
-
-                case '1':
-                case '2':
-                case '3':
-                case '4':
-                case '5':
-                case '6':
-                case '7':
-                case '8':
-                case '9':
-                    boolean inInnerLoop = true;
-
-                    while ( inInnerLoop )
-                    {
-                        c = nextChar( name, pos, true );
-
-                        switch ( c )
-                        {
-                            case ' ':
-                            case '=':
-                                inInnerLoop = false;
-                                pos.start--;
-                                break;
-
-                            case '.':
-                                inInnerLoop = false;
-                                dotCount++;
-                                break;
-                                
-                            case '0':
-                            case '1':
-                            case '2':
-                            case '3':
-                            case '4':
-                            case '5':
-                            case '6':
-                            case '7':
-                            case '8':
-                            case '9':
-                                break;
-
-                            default:
-                                throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, I18n.err(
-                                    I18n.ERR_13606_EXPECTED_NUMERICOID, c, pos.start ) );
-                        }
-                    }
-
-                    break;
-
-                case ' ':
-                case '=':
-                    pos.start--;
-
-                    if ( dotCount > 0 )
-                    {
-                        return new String( name, start, pos.start - start );
-                    }
-                    else
-                    {
-                        throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, I18n.err( I18n.ERR_13607_DOT_MISSING_IN_OID ) );
-                    }
-
-                default:
-                    throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, I18n.err( I18n.ERR_13608_START_AT_NUMERICOID_EXPECTED, c,
-                        pos.start ) );
-            }
-        }
-    }
-
-
-    /**
-     * Matches the equals character.
-     * 
-     * @param name the name
-     * @param pos the pos
-     * 
-     * @throws LdapInvalidDnException the invalid name exception
-     */
-    private static void matchEquals( char[] name, Position pos ) throws LdapInvalidDnException
-    {
-        char c = nextChar( name, pos, true );
-
-        if ( c != '=' )
-        {
-            throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, I18n.err( I18n.ERR_13609_EQUAL_EXPECTED, c, pos.start ) );
-        }
-    }
-
-
-    /**
-     * Matches the assertion value. This method only handles simple values.
-     * If we find any special character (BACKSLASH, PLUS, SHARP or DQUOTE),
-     * a LdapInvalidDnException will be thrown.
-     * 
-     * @param name the name
-     * @param pos the pos
-     * 
-     * @return the string
-     * 
-     * @throws LdapInvalidDnException the invalid name exception
-     */
-    private static String matchValue( char[] name, Position pos ) throws LdapInvalidDnException
-    {
-        int start = pos.start;
-        int numTrailingSpaces = 0;
-
-        while ( true )
-        {
-            if ( !hasMoreChars( pos ) )
-            {
-                return new String( name, start, pos.start - numTrailingSpaces - start );
-            }
-
-            char c = nextChar( name, pos, true );
-
-            switch ( c )
-            {
-                case '\\':
-                case '+':
-                case '#':
-                case '"':
-                    throw TooComplexDnException.INSTANCE;
-
-                case ',':
-                case ';':
-                    pos.start--;
-                    return new String( name, start, pos.start - numTrailingSpaces - start );
-
-                case ' ':
-                    numTrailingSpaces++;
-                    break;
-
-                default:
-                    numTrailingSpaces = 0;
-            }
-        }
-    }
-
-
-    /**
-     * Gets the next character.
-     * 
-     * @param name the name
-     * @param pos the pos
-     * @param increment true to increment the position
-     * 
-     * @return the character
-     * @throws LdapInvalidDnException If no more characters are available
-     */
-    private static char nextChar( char[] name, Position pos, boolean increment ) throws LdapInvalidDnException
-    {
-        if ( !hasMoreChars( pos ) )
-        {
-            throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, I18n.err( I18n.ERR_13610_NO_MORE_CHAR_AVAILABLE, pos.start ) );
-        }
-
-        char c = name[pos.start];
-
-        if ( increment )
-        {
-            pos.start++;
-        }
-
-        return c;
-    }
-
-
-    /**
-     * Checks if there are more characters.
-     * 
-     * @param pos the pos
-     * 
-     * @return true, if more characters are available
-     */
-    private static boolean hasMoreChars( Position pos )
-    {
-        return pos.start < pos.length;
-    }
-}
diff --git a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Rdn.java b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Rdn.java
index e038e4c42..d7f9c3ddf 100644
--- a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Rdn.java
+++ b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Rdn.java
@@ -1708,23 +1708,16 @@ public class Rdn implements Cloneable, Externalizable, Iterable<Ava>, Comparable
      */
     private static void parse( SchemaManager schemaManager, String rdnStr, Rdn rdn ) throws LdapInvalidDnException
     {
-        try
+        if ( rdn != null )
         {
-            FastDnParser.parseRdn( schemaManager, rdnStr, rdn );
+            rdn.clear();
         }
-        catch ( TooComplexDnException e )
+        else
         {
-            if ( rdn != null )
-            {
-                rdn.clear();
-            }
-            else
-            {
-                rdn = new Rdn();
-            }
-            
-            new ComplexDnParser().parseRdn( schemaManager, rdnStr, rdn );
+            rdn = new Rdn();
         }
+
+        DnParser.parseRdn( schemaManager, rdnStr, rdn );
     }
 
 
@@ -1739,7 +1732,7 @@ public class Rdn implements Cloneable, Externalizable, Iterable<Ava>, Comparable
     {
         if ( h == 0 )
         {
-            int hTmp = 37;
+            int h = 37;
 
             switch ( nbAvas )
             {
@@ -1749,7 +1742,7 @@ public class Rdn implements Cloneable, Externalizable, Iterable<Ava>, Comparable
 
                 case 1:
                     // We have a single Ava
-                    h = hTmp * 17 + ava.hashCode();
+                    h = h * 17 + ava.hashCode();
                     break;
 
                 default:
@@ -1757,7 +1750,7 @@ public class Rdn implements Cloneable, Externalizable, Iterable<Ava>, Comparable
 
                     for ( Ava ata : avas )
                     {
-                        h = hTmp * 17 + ata.hashCode();
+                        h = h * 17 + ata.hashCode();
                     }
 
                     break;
diff --git a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/subtree/SubtreeSpecificationParser.java b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/subtree/SubtreeSpecificationParser.java
index c42b4c027..5383a2676 100644
--- a/ldap/model/src/main/java/org/apache/directory/api/ldap/model/subtree/SubtreeSpecificationParser.java
+++ b/ldap/model/src/main/java/org/apache/directory/api/ldap/model/subtree/SubtreeSpecificationParser.java
@@ -61,9 +61,8 @@ import static org.apache.directory.api.util.ParserUtil.ZERO_N;
 
 
 /**
- * A reusable wrapper around the antlr generated parser for an LDAP subtree
- * specification as defined by <a href="http://www.faqs.org/rfcs/rfc3672.html">
- * RFC 3672</a>. 
+ * A LDAP subtree parser base on the  specification as defined by 
+ * <a href="http://www.faqs.org/rfcs/rfc3672.html">RFC 3672</a>. 
  * 
  * The parsed grammar is:
  * 
diff --git a/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/ComplexDnParserTest.java b/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/ComplexDnParserTest.java
new file mode 100644
index 000000000..ca89de6bd
--- /dev/null
+++ b/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/ComplexDnParserTest.java
@@ -0,0 +1,449 @@
+/*
+ *  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
+ * 
+ *    https://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.api.ldap.model.name;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.text.ParseException;
+
+import org.apache.directory.api.util.Position;
+import org.apache.directory.api.util.Strings;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test the class AttributeTypeAndValue
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+class ComplexDnParserTest {
+    
+    private static Method getPrivateMethod( String methodName, Class<?> clazz ) throws IllegalAccessException, IllegalArgumentException, 
+        InvocationTargetException, NoSuchMethodException, SecurityException 
+    {
+        Method method = DnParser.class.getDeclaredMethod( methodName, clazz, Position.class );
+        method.setAccessible( true );
+
+        return method;
+    }
+
+    /**
+     * test for a simple AT like "objectClass"
+     */
+    @Test
+    void testParseSimpelAttributeType()
+    {
+        try {
+            Method method = getPrivateMethod( "parseAttributeType", byte[].class );
+            
+            // Check a simple attributeType
+            // Transition Start -> 1
+            String at = "objectClass";
+            Position pos = new Position( at );
+            pos.length = at.length(); 
+            
+            String attributeType = ( String ) method.invoke( null, Strings.getBytesUtf8( at ), pos );
+            
+            assertEquals( at, attributeType );
+        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    /**
+     * test for a not terminated numericOID AT like "oid.1.2.3"
+     */
+    @Test
+    void testParseOidNumericOidAttributeType()
+    {
+        try {
+            Method method = getPrivateMethod( "parseAttributeType", byte[].class );
+            
+            // Check a simple attributeType
+            // Transition Start -> 1
+            String at = "oid.1.2.3";
+            Position pos = new Position( at );
+            pos.length = at.length(); 
+            
+            String attributeType = ( String ) method.invoke( null, Strings.getBytesUtf8( at ), pos );
+            
+            assertEquals( at, attributeType );
+        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    /**
+     * test for a bad  numericOID AT like "123"
+     */
+    @Test
+    void testParseBadNumericOidAttributeType()
+    {
+        try {
+            Method method = getPrivateMethod( "parseAttributeType", byte[].class );
+            
+            // Check a wrong OID
+            String at = "123";
+            Position pos = new Position( at );
+            pos.length = at.length(); 
+            
+            try 
+            {
+                method.invoke( null, Strings.getBytesUtf8( at ), pos );
+            }
+            catch ( Exception e )
+            {
+                assertTrue( e.getCause().getMessage().startsWith("ERR_13630_BAD_OID_ATTRIBUTE_TYPE" ) );
+            }
+        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    /**
+     * test for a not terminated numericOID AT like "123.2."
+     */
+    @Test
+    void testParseNotTerminatedNumericOidAttributeType()
+    {
+        try {
+            Method method = getPrivateMethod( "parseAttributeType", byte[].class );
+            
+            // Check a wrong OID
+            String at = "123.2.";
+            Position pos = new Position( at );
+            pos.length = at.length(); 
+            pos.end = pos.length;
+            
+            try 
+            {
+                method.invoke( null, Strings.getBytesUtf8( at ), pos );
+            }
+            catch ( Exception e )
+            {
+                assertTrue( e.getCause().getMessage().startsWith("ERR_13630_BAD_OID_ATTRIBUTE_TYPE" ) );
+            }
+        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            e.printStackTrace();
+        }
+    }
+    
+    
+    @Test
+    void testParseUTFMB2_00A3() throws ParseException
+    {
+        try {
+            Method method = getPrivateMethod( "parseUTFMB", byte[].class );
+
+            byte[] bytes = new byte[] { ( byte ) 0xC2, ( byte ) 0xA3 };
+            Position pos  = new Position( bytes );
+            pos.length = bytes.length;
+            
+            int c = ( int ) method.invoke( null, bytes, pos );
+            
+            assertEquals( '\u00A3' /*'£'*/, c );
+            assertEquals( '£', c );
+        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            e.printStackTrace();
+        }
+   }
+    
+    
+    /**
+     * %xE0 %xA0-BF UTF0
+     */
+    @Test
+    void testParseUTFMB3_1_0920()
+    {
+        try {
+            Method method = getPrivateMethod( "parseUTFMB", byte[].class );
+
+            byte[] bytes = new byte[] { ( byte ) 0xE0, ( byte ) 0xA4, ( byte ) 0xA0 };
+            Position pos  = new Position( bytes );
+            pos.length = bytes.length;
+            
+            int c = ( int ) method.invoke( null, bytes, pos );
+            
+            assertEquals( '\u0920' /*'ठ'*/, c );
+            assertEquals( 'ठ', c );
+        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            e.printStackTrace();
+        }
+    }
+    
+    
+    /**
+     * %xE1-EC 2(UTF0)
+     */
+    @Test
+    void testParseUTFMB3_2_1200()
+    {
+        try {
+            Method method = getPrivateMethod( "parseUTFMB", byte[].class );
+
+            byte[] bytes = new byte[] { ( byte ) 0xE1, ( byte ) 0x88, ( byte ) 0x80 };
+            Position pos  = new Position( bytes );
+            pos.length = bytes.length;
+            
+            int c = ( int ) method.invoke( null, bytes, pos );
+            
+            assertEquals( '\u1200' /*'ሀ'*/ , c);
+            assertEquals( 'ሀ', c );
+        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            e.printStackTrace();
+        }
+    }
+    
+    
+    /**
+     * %xE1-EC 2(UTF0)
+     */
+    @Test
+    void testParseWrongUTFMB3_2()
+    {
+        try {
+            Method method = getPrivateMethod( "parseUTFMB", byte[].class );
+
+            byte[] bytes = new byte[] { ( byte ) 0xE1, ( byte ) 0x45, ( byte ) 0x80 };
+            Position pos  = new Position( bytes );
+            pos.length = bytes.length;
+            
+            assertThrows( InvocationTargetException.class, () -> method.invoke( null, bytes, pos ) );
+            
+        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException |
+                InvocationTargetException e ) {
+            e.printStackTrace();
+        }
+    }
+      
+    
+    /**
+     * %xED %x80-9F UTF0
+     */
+    @Test
+    void testParseUTFMB3_3_D000()
+    {
+        try {
+            Method method = getPrivateMethod( "parseUTFMB", byte[].class );
+
+            byte[] bytes = new byte[] { ( byte ) 0xED, ( byte ) 0x80, ( byte ) 0x80 };
+            Position pos  = new Position( bytes );
+            pos.length = bytes.length;
+            
+            int c = ( int ) method.invoke( null, bytes, pos );
+            
+            assertEquals( '\uD000' /*'퀀'*/, c );
+            assertEquals( '퀀' /*'퀀'*/, c );
+        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            e.printStackTrace();
+        }
+    }
+    
+    /**
+     * %xED %x80-9F UTF0
+     */
+    @Test
+    void testParseUTFMB3_3_D7FB()
+    {
+        try {
+            Method method = getPrivateMethod( "parseUTFMB", byte[].class );
+
+            byte[] bytes = new byte[] { ( byte ) 0xED, ( byte ) 0x9F, ( byte ) 0xBB };
+            Position pos  = new Position( bytes );
+            pos.length = bytes.length;
+            
+            int c = ( int ) method.invoke( null, bytes, pos );
+            
+            assertEquals( '\uD7FB' /*'ퟻ'*/, c );
+            assertEquals( 'ퟻ', c );
+        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            e.printStackTrace();
+        }
+    }
+    
+    
+    /**
+     * %xEE-EF 2(UTF0)
+     */
+    @Test
+    void testParseUTFMB3_4_E000()
+    {
+        try {
+            Method method = getPrivateMethod( "parseUTFMB", byte[].class );
+
+            byte[] bytes = new byte[] { ( byte ) 0xEE, ( byte ) 0x80, ( byte ) 0x80 };
+            Position pos  = new Position( bytes );
+            pos.length = bytes.length;
+            
+            int c = ( int ) method.invoke( null, bytes, pos );
+            
+            assertEquals( '\uE000' /*''*/, c );
+            assertEquals( '', c );
+        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            e.printStackTrace();
+        }
+    }
+    
+    
+    /**
+     * %xEE-EF 2(UTF0)
+     */
+    @Test
+    void testParseUTFMB3_4_FFFD()
+    {
+        try {
+            Method method = getPrivateMethod( "parseUTFMB", byte[].class );
+
+            byte[] bytes = new byte[] { ( byte ) 0xEF, ( byte ) 0xBF, ( byte ) 0xBD };
+            Position pos  = new Position( bytes );
+            pos.length = bytes.length;
+            
+            int c = ( int ) method.invoke( null, bytes, pos );
+            
+            assertEquals( '\uFFFD' /*'�'*/, c );
+            assertEquals( '�', c );
+        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            e.printStackTrace();
+        }
+    }
+    
+    
+    /**
+     * %xF0 %x90-BF 2(UTF0)
+     */
+    @Test
+    void testParseUTFMB4_1_10000()
+    {
+        try {
+            Method method = getPrivateMethod( "parseUTFMB", byte[].class );
+
+            byte[] bytes = new byte[] { ( byte ) 0xF0, ( byte ) 0x90, ( byte ) 0x80, ( byte ) 0x80 };
+            Position pos  = new Position( bytes );
+            pos.length = bytes.length;
+            
+            int c = ( int ) method.invoke( null, bytes, pos );
+            char[] chars = new char[] { ( char )( c >> 16 ), ( char ) ( c & 0xFFFF ) };
+            String result = new String( chars );
+            assertEquals( "\uD800\uDC00" /*'𐀀'*/, result );
+            assertEquals( "𐀀", result );
+        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            e.printStackTrace();
+        }
+    }
+    
+    
+    /**
+     * %xF0 %x90-BF 2(UTF0)
+     */
+    @Test
+    void testParseUTFMB4_1_3D05F()
+    {
+        try {
+            Method method = getPrivateMethod( "parseUTFMB", byte[].class );
+
+            byte[] bytes = new byte[] { ( byte ) 0xF0, ( byte ) 0xBD, ( byte ) 0x81, ( byte ) 0x9F };
+            Position pos  = new Position( bytes );
+            pos.length = bytes.length;
+            
+            int c = ( int ) method.invoke( null, bytes, pos );
+            char[] chars = new char[] { ( char )( c >> 16 ), ( char ) ( c & 0xFFFF ) };
+            String result = new String( chars );
+            assertEquals( "\uD8B4\uDC5F" /*'𽁟'*/, result );
+            assertEquals( "𽁟", result );
+        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            e.printStackTrace();
+        }
+    }
+    
+    
+    /**
+     * %xF1-F3 3(UTF0)
+     */
+    @Test
+    void testParseUTFMB4_2_E0052()
+    {
+        try {
+            Method method = getPrivateMethod( "parseUTFMB", byte[].class );
+
+            byte[] bytes = new byte[] { ( byte ) 0xF3, ( byte ) 0xA0, ( byte ) 0x81, ( byte ) 0x92 };
+            Position pos  = new Position( bytes );
+            pos.length = bytes.length;
+            
+            int c = ( int ) method.invoke( null, bytes, pos );
+            char[] chars = new char[] { ( char )( c >> 16 ), ( char ) ( c & 0xFFFF ) };
+            String result = new String( chars );
+            assertEquals( "\uDB40\uDC52", result );
+        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            e.printStackTrace();
+        }
+    }
+    
+    
+    /**
+     * %xF4 %x80-8F 2(UTF0)
+     */
+    @Test
+    void testParseUTFMB4_3_100000()
+    {
+        try {
+            Method method = getPrivateMethod( "parseUTFMB", byte[].class );
+
+            byte[] bytes = new byte[] { ( byte ) 0xF4, ( byte ) 0x80, ( byte ) 0x80, ( byte ) 0x80 };
+            Position pos  = new Position( bytes );
+            pos.length = bytes.length;
+            
+            int c = ( int ) method.invoke( null, bytes, pos );
+            char[] chars = new char[] { ( char )( c >> 16 ), ( char ) ( c & 0xFFFF ) };
+            String result = new String( chars );
+            assertEquals( "\uDBC0\uDC00", result );
+        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            e.printStackTrace();
+        }
+    }
+    
+    
+    /**
+     * %xF4 %x80-8F 2(UTF0)
+     */
+    @Test
+    void testParseUTFMB4_3_10FFFF()
+    {
+        try {
+            Method method = getPrivateMethod( "parseUTFMB", byte[].class );
+
+            byte[] bytes = new byte[] { ( byte ) 0xF4, ( byte ) 0x80, ( byte ) 0x80, ( byte ) 0x80 };
+            Position pos  = new Position( bytes );
+            pos.length = bytes.length;
+            
+            int c = ( int ) method.invoke( null, bytes, pos );
+            char[] chars = new char[] { ( char )( c >> 16 ), ( char ) ( c & 0xFFFF ) };
+            String result = new String( chars );
+            assertEquals( "\uDBC0\uDC00", result );
+        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/DnParserTest.java b/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/DnParserTest.java
index 922042768..c3bfb0b8c 100644
--- a/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/DnParserTest.java
+++ b/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/DnParserTest.java
@@ -72,6 +72,20 @@ public class DnParserTest
     }
 
 
+    /**
+     * test a simple Dn : a = b
+     * 
+     * @throws LdapException If the test failed
+     */
+    @Test
+    public void testLdapDNSimpleNoValue() throws LdapException
+    {
+        Dn dn = new Dn( "1.2.3.4.5 =  " );
+
+        assertEquals( "1.2.3.4.5 =  ", dn.getName() );
+    }
+
+
     /**
      * test a composite Dn : a = b, d = e
      * 
@@ -131,7 +145,7 @@ public class DnParserTest
 
 
     /**
-     * Test DN with '_' in value, because of special handling in Antlr grammar.
+     * Test DN with '_' in value.
      * 
      * @throws LdapException If the test failed
      */
@@ -239,7 +253,11 @@ public class DnParserTest
     @Test
     public void testLdapDNPairCharAttributeValue() throws LdapException
     {
-        Dn dn = new Dn( "a = \\,\\=\\+\\<\\>\\#\\;\\\\\\\"\\C3\\A9" );
+        Dn dn = new Dn( "a = \\C3\\A9" );
+        assertEquals( "a=\u00e9", dn.getEscaped() );
+        assertEquals( "a = \\C3\\A9", dn.getName() );
+
+        dn = new Dn( "a = \\,\\=\\+\\<\\>\\#\\;\\\\\\\"\\C3\\A9" );
         assertEquals( "a=\\,=\\+\\<\\>#\\;\\\\\\\"\u00e9", dn.getEscaped() );
         assertEquals( "a = \\,\\=\\+\\<\\>\\#\\;\\\\\\\"\\C3\\A9", dn.getName() );
 
@@ -336,7 +354,7 @@ public class DnParserTest
             { 'C', '=', ' ', 'E', ( byte ) 0xc3, ( byte ) 0xa9, 'c' } );
 
         Dn name = new Dn( dn );
-
+        
         assertEquals( "C= E\u00e9c", name.getName() );
         assertEquals( "C=E\u00e9c", name.getEscaped() );
     }
@@ -541,6 +559,7 @@ public class DnParserTest
     @Test
     public void testNameFrenchChars() throws Exception
     {
+        // cn=Jérôme
         String cn = new String( new byte[]
             { 'c', 'n', '=', 0x4A, ( byte ) 0xC3, ( byte ) 0xA9, 0x72, ( byte ) 0xC3, ( byte ) 0xB4, 0x6D, 0x65 },
             StandardCharsets.UTF_8 );
@@ -628,6 +647,21 @@ public class DnParserTest
     }
 
 
+    /**
+     * Test to check that even with a non escaped char, the Dn is parsed ok
+     * or at least an error is generated.
+     * @throws LdapInvalidDnException 
+     */
+    @Test
+    public final void testEqualInValue() throws LdapInvalidDnException
+    {
+        String input = "ou==,ou=test";
+
+        Dn dn = new Dn( input );
+        System.out.println(dn);
+    }
+
+
     /**
      * Test the Dn.get( int ) method
      * 
diff --git a/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/NameUtil.java b/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/NameUtil.java
new file mode 100644
index 000000000..bae28af2b
--- /dev/null
+++ b/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/NameUtil.java
@@ -0,0 +1,293 @@
+/*
+ *  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
+ *  
+ *    https://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.api.ldap.model.name;
+
+import java.util.List;
+
+import org.apache.directory.api.ldap.model.entry.Value;
+import org.apache.directory.api.util.Strings;
+
+/**
+ * Utility class used to dump the DN/RDN/AVA/Value elements
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class NameUtil 
+{
+    private static String dumpValue( String tabs, Value value )
+    {
+        StringBuilder sb = new StringBuilder();
+        
+        if ( value  == null )
+        {
+            return tabs + "No value" + "\n";
+        }
+        else
+        {
+            sb.append( tabs ).append( "Value:\n" );
+            tabs = tabs + "    ";
+        }
+        
+        // The upValue
+        sb.append( tabs ).append( "UpValue: '" ).append( value.getUpValue() ).append( "'\n" );
+        
+        // The normValue
+        sb.append( tabs ).append( "NormValue: '" ).append( value.getNormalized() ).append( "'\n" );
+        
+        // The bytes
+        sb.append( tabs ).append( "Bytes: '" ).append( Strings.dumpBytes( value.getBytes() ) ).append( "'\n" );
+        
+        // The attributeType
+        if ( value.getAttributeType() == null )
+        {
+            sb.append( tabs ).append( "No attributeType" ).append( '\n' ); 
+        }
+        else
+        {
+            sb.append( tabs ).append( "AttributeType" ).append( value.getAttributeType() ).append( '\n' );  
+        }
+        
+        // The HR flag
+        if ( value.isHumanReadable() )
+        {
+            sb.append( tabs ).append( "HR: true\n" );
+        }
+        else
+        {
+            sb.append( tabs ).append( "HR: false\n" );
+        }
+        
+        // The hashcode
+        sb.append( tabs ).append( "H: " ).append( value.hashCode() ).append( '\n' );
+        
+        return sb.toString();
+    }
+    
+ 
+    public static void dumpValue( Value value )
+    {
+        System.out.println( dumpValue( "    ", value ));
+    }
+    
+    
+    private static String dumpAva( String tabs, Ava ava )
+    {
+        if ( ava == null )
+        {
+            return tabs + "null";
+        }
+        
+        StringBuilder sb = new StringBuilder();
+        
+        // The upName
+        sb.append( tabs ).append( "upName: '" ).append( ava.upName ).append( "'\n" );
+        
+        // The attributeType
+        sb.append( tabs ).append( "attributeType: '" ).append( ava.attributeType ).append( "'\n" );
+
+        // The upType
+        sb.append( tabs ).append( "upType: '" ).append( ava.upType ).append( "'\n" );
+
+        // The normType
+        sb.append( tabs ).append( "normType: '" ).append( ava.normType ).append( "'\n" );
+
+        // The schemaManager
+        if ( ava.isSchemaAware() )
+        {
+            sb.append( tabs ).append( "Schema aware\n" );
+        }
+        else
+        {
+            sb.append( tabs ).append( "No schema\n" );
+        }
+        
+        // The value
+        sb.append( dumpValue( tabs, ava.value ) ).appendCodePoint( '\n' );
+        
+        return sb.toString();
+    }
+
+    public static void dumpAva( Ava ava )
+    {
+        System.out.println( dumpAva( "    ", ava ) );
+    }
+
+    private static String dumpRdn( String tabs, Rdn rdn )
+    {
+        StringBuilder sb = new StringBuilder();
+
+        if ( rdn == null )
+        {
+            sb.append( tabs ).append( "RDN: null\n" );
+        }
+        
+        if ( rdn.nbAvas == 0 )
+        {
+            sb.append( tabs ).append( "RDN: no AVAs\n" );
+        }
+        else
+        {
+            sb.append( tabs ).append( "RDN:\n" );
+        }
+        
+        tabs = tabs + "    ";
+        
+        // The schema
+        if ( rdn.isSchemaAware() )
+        {
+            sb.append( tabs ).append( "Schema aware\n" );
+        }
+        else
+        {
+            sb.append( tabs ).append( "No schema\n" );
+        }
+        
+        // The upName
+        if ( Strings.isEmpty( rdn.upName ) )
+        {
+            sb.append( tabs ).append( "UpName is empty\n" );
+        }
+        else
+        {
+            sb.append( tabs ).append( "UpName: '" ).append( rdn.upName ).append( "'\n" );
+        }
+  
+        // The normName
+        if ( Strings.isEmpty( rdn.normName ) )
+        {
+            sb.append( tabs ).append( "NormName is empty\n" );
+        }
+        else
+        {
+            sb.append( tabs ).append( "NormName: '" ).append( rdn.normName ).append( "'\n" );
+        }
+        
+        // The hash code
+        sb.append( tabs ).append( "h:" ).append( rdn.hashCode() ).append( '\n' );
+
+        // The AVAs
+        sb.append( tabs ).append( "Nb AVAs: " ).append( rdn.nbAvas ).append( '\n' );
+        
+        switch ( rdn.nbAvas )
+        {
+            case 0:
+                sb.append( tabs ).append( "No AVAs\n" );
+                break;
+                
+            case 1:
+                sb.append( tabs + "    " ).append( "AVA:\n" );
+                sb.append( dumpAva( tabs + "        ", rdn.ava ) );
+                break;
+                
+            default:
+                List<Ava> avas = rdn.avas;
+                
+                for ( int i = 0; i < rdn.nbAvas; i++ )
+                {
+                    sb.append( tabs + "    " ).append( "AVA[" ).append( i ).append( "]:\n" );
+                    sb.append( dumpAva( tabs + "        ", avas.get( i ) ) );
+                }
+        }
+        
+        return sb.toString();
+    }
+
+    public static void dumpRdn( Rdn rdn )
+    {
+        String tabs = "    ";
+        
+        System.out.println( dumpRdn( tabs, rdn ) );
+    }
+
+    public static void dumpDn( Dn dn )
+    {
+        String tabs = "    ";
+        
+        if ( dn == null )
+        {
+            System.out.println( "DN: null" );
+        }
+        
+        if ( dn.isEmpty() )
+        {
+            System.out.println( "DN: empty" );
+        }
+        
+        StringBuilder sb = new StringBuilder();
+        
+        // The schema
+        if ( dn.isSchemaAware() )
+        {
+            sb.append( tabs ).append( "Schema aware\n" );
+        }
+        else
+        {
+            sb.append( tabs ).append( "No schema\n" );
+        }
+        
+        // The upname
+        if ( Strings.isEmpty( dn.getName() ) )
+        {
+            sb.append( tabs ).append( "UpName is empty\n" );
+        }
+        else
+        {
+            sb.append( tabs ).append( "UpName: '" ).append( dn.getName() ).append( "'\n" );
+        }
+        
+        // The normname
+        if ( Strings.isEmpty( dn.getNormName() ) )
+        {
+            sb.append( tabs ).append( "NormName is empty\n" );
+        }
+        else
+        {
+            sb.append( tabs ).append( "NormName: '" ).append( dn.getNormName() ).append( "'\n" );
+        }
+        
+        // The RDNs
+        List<Rdn> rdns = dn.getRdns();
+        
+        sb.append( tabs ).append( "RDNs:\n" );
+        
+        tabs = tabs + "    ";
+        
+        if ( rdns.isEmpty() )
+        {
+            sb.append( tabs ).append( "null\n" );
+        }
+        else
+        {
+            int i = 0;
+            
+            for ( Rdn rdn : rdns )
+            {
+                sb.append( tabs ).append( "rdn[" + i + "]:\n" );
+                
+                sb.append( dumpRdn( tabs + "    ", rdn ) );
+                
+                i++;
+            }
+        }
+        
+        System.out.println( "DN: ");
+        System.out.println( sb.toString() );
+    }
+}
diff --git a/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/FastRdnParserTest.java b/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/RdnParserTest.java
similarity index 99%
rename from ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/FastRdnParserTest.java
rename to ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/RdnParserTest.java
index b4d813345..d4da0c7c6 100644
--- a/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/FastRdnParserTest.java
+++ b/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/RdnParserTest.java
@@ -45,7 +45,7 @@ import org.junit.jupiter.api.parallel.ExecutionMode;
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
 @Execution(ExecutionMode.CONCURRENT)
-public class FastRdnParserTest
+public class RdnParserTest
 {
     /** A null schemaManager used in tests */
     SchemaManager schemaManager = null;
@@ -458,7 +458,7 @@ public class FastRdnParserTest
         
         for ( int i = 0; i < 10000000; i++ )
         {
-            new Rdn( "cn=test"+ i );
+            new Rdn( "cn=test"+ i + " + gn=toto" );
         }
         
         long t1 = System.currentTimeMillis();
diff --git a/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/RdnTest.java b/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/RdnTest.java
index a9f158d1b..a0057d10b 100644
--- a/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/RdnTest.java
+++ b/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/RdnTest.java
@@ -606,7 +606,7 @@ public class RdnTest
     public void testGetValue() throws LdapException
     {
         Rdn rdn = new Rdn( " a = b + b = f + g = h + c = d " );
-
+        
         assertEquals( "b", rdn.getValue() );
     }
 
@@ -661,7 +661,7 @@ public class RdnTest
     public void testEquals() throws LdapException
     {
         Rdn rdn = new Rdn( "a=b + c=d + e=f" );
-
+        
         assertFalse( rdn.equals( null ) );
         assertFalse( rdn.equals( "test" ) );
         assertFalse( rdn.equals( new Rdn( "a=c + c=d + e=f" ) ) );
@@ -871,6 +871,7 @@ public class RdnTest
 
         Rdn rdn3 = new Rdn( "cn= \\ a b c\\  " );
         Rdn rdn4 = new Rdn( "cn=\\ a\\ b\\ c\\ " );
+
         assertEquals( "cn= \\ a b c\\  ", rdn3.getName() );
         assertEquals( "cn=\\ a b c\\ ", rdn3.getEscaped() );
         assertTrue( rdn3.equals( rdn4 ) );
@@ -1160,8 +1161,7 @@ public class RdnTest
     public void testRdnWithEmptyValue() throws LdapException
     {
         assertTrue( Rdn.isValid( "a=" ) );
-        assertTrue( Rdn.isValid( "a=\"\"" ) );
-        assertEquals( "a=\"\"", new Rdn( "a=\"\"" ).getName() );
+        assertFalse( Rdn.isValid( "a=\"\"" ) );
         assertEquals( "a=", new Rdn( "a=" ).getName() );
     }
 
@@ -1177,15 +1177,7 @@ public class RdnTest
         assertTrue( Rdn.isValid( "a=b\\,c" ) );
         assertEquals( "a=b\\,c", new Rdn( "a=b\\,c" ).getName() );
 
-        assertTrue( Rdn.isValid( "a=\"b,c\"" ) );
-        assertEquals( "a=\"b,c\"", new Rdn( "a=\"b,c\"" ).getName() );
-        assertEquals( "a=b\\,c", new Rdn( "a=\"b,c\"" ).getEscaped() );
-        assertEquals( "a=\"b,c\"", new Rdn( "a=\"b,c\"" ).getName() );
-
-        assertTrue( Rdn.isValid( "a=\"b\\,c\"" ) );
-        Rdn rdn = new Rdn( "a=\"b\\,c\"" );
-        assertEquals( "a=\"b\\,c\"", rdn.getName() );
-        assertEquals( "a=b\\,c", rdn.getEscaped() );
+        assertFalse( Rdn.isValid( "a=\"b,c\"" ) );
     }
 
 
diff --git a/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/FastDnParserTest.java b/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/SimpleDnParserTest.java
similarity index 76%
rename from ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/FastDnParserTest.java
rename to ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/SimpleDnParserTest.java
index e3cd6c954..ba641ecc2 100644
--- a/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/FastDnParserTest.java
+++ b/ldap/model/src/test/java/org/apache/directory/api/ldap/model/name/SimpleDnParserTest.java
@@ -28,6 +28,7 @@ import static org.junit.jupiter.api.Assertions.fail;
 import java.nio.charset.StandardCharsets;
 
 import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
 import org.apache.directory.api.util.Strings;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.parallel.Execution;
@@ -43,7 +44,7 @@ import org.junit.jupiter.api.parallel.ExecutionMode;
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
 @Execution(ExecutionMode.CONCURRENT)
-public class FastDnParserTest
+public class SimpleDnParserTest
 {
 
     /**
@@ -54,7 +55,7 @@ public class FastDnParserTest
     @Test
     public void testLdapDNEmpty() throws LdapException
     {
-        assertEquals( "", FastDnParser.parse( "" ).getName() );
+        assertEquals( "", DnParser.parseDn( "" ).getName() );
     }
 
 
@@ -68,12 +69,12 @@ public class FastDnParserTest
     public void testLdapDNIncomplete() throws LdapException
     {
         // empty Dn is ok
-        FastDnParser.parse( " " );
+        DnParser.parseDn( " " );
 
         // test DNs starting with an descr
         try
         {
-            FastDnParser.parse( " a" );
+            DnParser.parseDn( " a" );
             fail();
         }
         catch ( LdapException ine )
@@ -82,7 +83,7 @@ public class FastDnParserTest
         }
         try
         {
-            FastDnParser.parse( " a " );
+            DnParser.parseDn( " a " );
             fail();
         }
         catch ( LdapException ine )
@@ -91,21 +92,21 @@ public class FastDnParserTest
         }
         try
         {
-            FastDnParser.parse( " a- " );
+            DnParser.parseDn( " a- " );
             fail();
         }
         catch ( LdapException ine )
         {
             // expected
         }
-        FastDnParser.parse( " a =" );
-        FastDnParser.parse( " a = " );
-        FastDnParser.parse( " a = b" );
+        DnParser.parseDn( " a =" );
+        DnParser.parseDn( " a = " );
+        DnParser.parseDn( " a = b" );
 
         // test DNs starting with an OID
         try
         {
-            FastDnParser.parse( " 1 = b " );
+            DnParser.parseDn( " 1 = b " );
             fail( "OID must contain at least on dot." );
         }
         catch ( LdapException ine )
@@ -114,7 +115,7 @@ public class FastDnParserTest
         }
         try
         {
-            FastDnParser.parse( " 0" );
+            DnParser.parseDn( " 0" );
             fail();
         }
         catch ( LdapException ine )
@@ -123,7 +124,7 @@ public class FastDnParserTest
         }
         try
         {
-            FastDnParser.parse( " 0." );
+            DnParser.parseDn( " 0." );
             fail();
         }
         catch ( LdapException ine )
@@ -132,7 +133,7 @@ public class FastDnParserTest
         }
         try
         {
-            FastDnParser.parse( " 0.5" );
+            DnParser.parseDn( " 0.5" );
             fail();
         }
         catch ( LdapException ine )
@@ -141,7 +142,7 @@ public class FastDnParserTest
         }
         try
         {
-            FastDnParser.parse( " 0.5 " );
+            DnParser.parseDn( " 0.5 " );
             fail();
         }
         catch ( LdapException ine )
@@ -149,9 +150,9 @@ public class FastDnParserTest
             // expected
         }
 
-        FastDnParser.parse( " 0.5=" );
-        FastDnParser.parse( " 0.5 = " );
-        FastDnParser.parse( " 0.5 = b" );
+        DnParser.parseDn( " 0.5=" );
+        DnParser.parseDn( " 0.5 = " );
+        DnParser.parseDn( " 0.5 = b" );
     }
 
 
@@ -163,7 +164,7 @@ public class FastDnParserTest
     @Test
     public void testLdapDNSimple() throws LdapException
     {
-        Dn dn = FastDnParser.parse( "a = b" );
+        Dn dn = DnParser.parseDn( "a = b" );
 
         assertEquals( "a = b", dn.getName() );
         assertEquals( "a=b", dn.getEscaped() );
@@ -172,7 +173,7 @@ public class FastDnParserTest
         assertEquals( "a = b", dn.getRdn().getName() );
         assertEquals( "a=b", dn.getRdn().getEscaped() );
 
-        assertEquals( "a=b", dn.getRdn().getAva().getName() );
+        assertEquals( "a = b", dn.getRdn().getAva().getName() );
         assertEquals( "a=b", dn.getRdn().getAva().getEscaped() );
 
         assertEquals( "a", dn.getRdn().getAva().getType() );
@@ -190,7 +191,7 @@ public class FastDnParserTest
     @Test
     public void testLdapDNComposite() throws LdapException
     {
-        Dn dn = FastDnParser.parse( "a = b, c = d" );
+        Dn dn = DnParser.parseDn( "a = b, c = d" );
         assertEquals( "a=b,c=d", dn.getEscaped() );
         assertEquals( "a = b, c = d", dn.getName() );
     }
@@ -204,7 +205,7 @@ public class FastDnParserTest
     @Test
     public void testLdapDNCompositeWithSpace() throws LdapException
     {
-        Dn dn = FastDnParser.parse( "a=b, a =b, a= b, a = b, a  =  b" );
+        Dn dn = DnParser.parseDn( "a=b, a =b, a= b, a = b, a  =  b" );
         assertEquals( "a=b,a=b,a=b,a=b,a=b", dn.getEscaped() );
         assertEquals( "a=b, a =b, a= b, a = b, a  =  b", dn.getName() );
     }
@@ -219,21 +220,21 @@ public class FastDnParserTest
     @Test
     public void testLdapDNCompositeSepators() throws LdapException
     {
-        Dn dn = FastDnParser.parse( "a=b;c=d,e=f" );
+        Dn dn = DnParser.parseDn( "a=b;c=d,e=f" );
         assertEquals( "a=b,c=d,e=f", dn.getEscaped() );
         assertEquals( "a=b;c=d,e=f", dn.getName() );
     }
 
     /**
-     * Test an attributeType with '_' (Microsoft morons support...)
+     * Test an attributeType with '_' (Microsoft morons support.)
      * 
      * @throws LdapException If the test failed
      */
     @Test
     public void testAttributeTypeWithUnderscore() throws LdapException
     {
-        Dn dn = FastDnParser.parse( "microsoft_developpers=morons" );
-        assertEquals( "microsoft_developpers=morons", dn.getEscaped() );
+        Dn dn = DnParser.parseDn( "microsoft_developers=morons" );
+        assertEquals( "microsoft_developers=morons", dn.getEscaped() );
     }
 
     
@@ -245,10 +246,7 @@ public class FastDnParserTest
     @Test
     public void testLdapDNSimpleMultivaluedAttribute() throws LdapException
     {
-        assertThrows( TooComplexDnException.class, () -> 
-        {
-            FastDnParser.parse( "a = b + c = d" );
-        } );
+        DnParser.parseDn( "a = b + c = d" );
     }
 
 
@@ -261,10 +259,7 @@ public class FastDnParserTest
     @Test
     public void testLdapDNCompositeMultivaluedAttribute() throws LdapException
     {
-        assertThrows( TooComplexDnException.class, () -> 
-        {
-            FastDnParser.parse( "a=b+c=d, e=f + g=h + i=j" );
-        } );
+        DnParser.parseDn( "a=b+c=d, e=f + g=h + i=j" );
     }
 
 
@@ -276,10 +271,7 @@ public class FastDnParserTest
     @Test
     public void testLdapDNOidUpper() throws LdapException
     {
-        assertThrows( TooComplexDnException.class, () -> 
-        {
-            FastDnParser.parse( "OID.12.34.56 = azerty" );
-        } );
+        DnParser.parseDn( "OID.12.34.56 = azerty" );
     }
 
 
@@ -291,10 +283,7 @@ public class FastDnParserTest
     @Test
     public void testLdapDNOidLower() throws LdapException
     {
-        assertThrows( TooComplexDnException.class, () -> 
-        {
-            FastDnParser.parse( "oid.12.34.56 = azerty" );
-        } );
+        DnParser.parseDn( "oid.12.34.56 = azerty" );
     }
 
 
@@ -307,7 +296,7 @@ public class FastDnParserTest
     @Test
     public void testLdapDNOidWithoutPrefix() throws LdapException
     {
-        Dn dn = FastDnParser.parse( "12.34.56 = azerty" );
+        Dn dn = DnParser.parseDn( "12.34.56 = azerty" );
         assertEquals( "12.34.56=azerty", dn.getEscaped() );
         assertEquals( "12.34.56 = azerty", dn.getName() );
     }
@@ -322,7 +311,7 @@ public class FastDnParserTest
     @Test
     public void testLdapDNCompositeOidWithoutPrefix() throws LdapException
     {
-        Dn dn = FastDnParser.parse( "12.34.56 = azerty; 7.8 = test" );
+        Dn dn = DnParser.parseDn( "12.34.56 = azerty; 7.8 = test" );
         assertEquals( "12.34.56=azerty,7.8=test", dn.getEscaped() );
         assertEquals( "12.34.56 = azerty; 7.8 = test", dn.getName() );
     }
@@ -336,10 +325,7 @@ public class FastDnParserTest
     @Test
     public void testLdapDNPairCharAttributeValue() throws LdapException
     {
-        assertThrows( TooComplexDnException.class, () -> 
-        {
-            FastDnParser.parse( "a = \\,\\=\\+\\<\\>\\#\\;\\\\\\\"\\C3\\A9" );
-        } );
+        DnParser.parseDn( "a = \\,\\=\\+\\<\\>\\#\\;\\\\\\\"\\C3\\A9" );
     }
 
 
@@ -351,10 +337,7 @@ public class FastDnParserTest
     @Test
     public void testLdapDNHexStringAttributeValue() throws LdapException
     {
-        assertThrows( TooComplexDnException.class, () -> 
-        {
-            FastDnParser.parse( "a = #0010A0AAFF" );
-        } );
+        DnParser.parseDn( "a = #0010A0AAFF" );
     }
 
 
@@ -366,9 +349,9 @@ public class FastDnParserTest
     @Test
     public void testBadLdapDNHexStringAttributeValue() throws LdapException
     {
-        assertThrows( TooComplexDnException.class, () -> 
+        assertThrows( LdapInvalidDnException.class, () -> 
         {
-            FastDnParser.parse( "a=#zz" );
+            DnParser.parseDn( "a=#zz" );
         } );
     }
 
@@ -381,10 +364,7 @@ public class FastDnParserTest
     @Test
     public void testLdapDNQuotedAttributeValue() throws LdapException
     {
-        assertThrows( TooComplexDnException.class, () -> 
-        {
-            FastDnParser.parse( "a = quoted \\\"value" );
-        } );
+        DnParser.parseDn( "a = quoted \\\"value" );
     }
 
 
@@ -395,7 +375,7 @@ public class FastDnParserTest
             { 'C', 'N', ' ', '=', ' ', 'E', 'm', 'm', 'a', 'n', 'u', 'e', 'l', ' ', ' ', 'L', ( byte ) 0xc3,
                 ( byte ) 0xa9, 'c', 'h', 'a', 'r', 'n', 'y' } );
 
-        Dn name = FastDnParser.parse( dn );
+        Dn name = DnParser.parseDn( dn );
 
         assertEquals( "CN = Emmanuel  L\u00e9charny", name.getName() );
         assertEquals( "CN=Emmanuel  L\u00e9charny", name.getEscaped() );
@@ -408,7 +388,7 @@ public class FastDnParserTest
         String dn = Strings.utf8ToString( new byte[]
             { 'C', '=', ' ', 'E', ( byte ) 0xc3, ( byte ) 0xa9, 'c' } );
 
-        Dn name = FastDnParser.parse( dn );
+        Dn name = DnParser.parseDn( dn );
 
         assertEquals( "C= E\u00e9c", name.getName() );
         assertEquals( "C=E\u00e9c", name.getEscaped() );
@@ -418,11 +398,8 @@ public class FastDnParserTest
     @Test
     public void testVsldapExtras() throws LdapException
     {
-        assertThrows( TooComplexDnException.class, () -> 
-        {
-            FastDnParser
-                .parse( "cn=Billy Bakers, OID.2.5.4.11=Corporate Tax, ou=Fin-Accounting, ou=Americas, ou=Search, o=IMC, c=US" );
-        } );
+        DnParser
+            .parseDn( "cn=Billy Bakers, OID.2.5.4.11=Corporate Tax, ou=Fin-Accounting, ou=Americas, ou=Search, o=IMC, c=US" );
     }
 
 
@@ -434,7 +411,7 @@ public class FastDnParserTest
     @Test
     public final void testParseStringEmpty() throws LdapException
     {
-        Dn nameEmpty = FastDnParser.parse( "" );
+        Dn nameEmpty = DnParser.parseDn( "" );
 
         assertNotNull( nameEmpty );
     }
@@ -448,7 +425,7 @@ public class FastDnParserTest
     @Test
     public final void testParseStringNull() throws LdapException
     {
-        Dn nameNull = FastDnParser.parse( null );
+        Dn nameNull = DnParser.parseDn( null );
 
         assertEquals( "", nameNull.toString(), "Null Dn are legal : " );
     }
@@ -462,8 +439,8 @@ public class FastDnParserTest
     @Test
     public final void testParseStringRFC1779_1() throws LdapException
     {
-        Dn nameRFC1779_1 = FastDnParser
-            .parse( "CN=Marshall T. Rose, O=Dover Beach Consulting, L=Santa Clara, ST=California, C=US" );
+        Dn nameRFC1779_1 = DnParser
+            .parseDn( "CN=Marshall T. Rose, O=Dover Beach Consulting, L=Santa Clara, ST=California, C=US" );
 
         assertEquals( 
             "CN=Marshall T. Rose, O=Dover Beach Consulting, L=Santa Clara, ST=California, C=US",
@@ -481,7 +458,7 @@ public class FastDnParserTest
     @Test
     public final void testParseStringRFC2253_1() throws LdapException
     {
-        Dn nameRFC2253_1 = FastDnParser.parse( "CN=Steve Kille,O=Isode limited,C=GB" );
+        Dn nameRFC2253_1 = DnParser.parseDn( "CN=Steve Kille,O=Isode limited,C=GB" );
 
         assertEquals( "CN=Steve Kille,O=Isode limited,C=GB", nameRFC2253_1.getName(), "RFC2253_1 : " );
     }
@@ -495,10 +472,7 @@ public class FastDnParserTest
     @Test
     public final void testParseStringRFC2253_2() throws LdapException
     {
-        assertThrows( TooComplexDnException.class, () -> 
-        {
-            FastDnParser.parse( "CN = Sales + CN =   J. Smith , O = Widget Inc. , C = US" );
-        } );
+        DnParser.parseDn( "CN = Sales + CN =   J. Smith , O = Widget Inc. , C = US" );
     }
 
 
@@ -510,10 +484,7 @@ public class FastDnParserTest
     @Test
     public final void testParseStringRFC2253_3() throws LdapException
     {
-        assertThrows( TooComplexDnException.class, () -> 
-        {
-            FastDnParser.parse( "CN=L. Eagle,   O=Sue\\, Grabbit and Runn, C=GB" );
-        } );
+        DnParser.parseDn( "CN=L. Eagle,   O=Sue\\, Grabbit and Runn, C=GB" );
     }
 
 
@@ -525,10 +496,7 @@ public class FastDnParserTest
     @Test
     public final void testParseStringRFC2253_4() throws LdapException
     {
-        assertThrows( TooComplexDnException.class, () -> 
-        {
-            FastDnParser.parse( "CN=Before\\0DAfter,O=Test,C=GB" );
-        } );
+        DnParser.parseDn( "CN=Before\\0DAfter,O=Test,C=GB" );
     }
 
 
@@ -540,10 +508,7 @@ public class FastDnParserTest
     @Test
     public final void testParseStringRFC2253_5() throws LdapException
     {
-        assertThrows( TooComplexDnException.class, () -> 
-        {
-            FastDnParser.parse( "1.3.6.1.4.1.1466.0=#04024869,O=Test,C=GB" );
-        } );
+        DnParser.parseDn( "1.3.6.1.4.1.1466.0=#04024869,O=Test,C=GB" );
     }
 
 
@@ -555,10 +520,7 @@ public class FastDnParserTest
     @Test
     public final void testParseStringRFC2253_6() throws LdapException
     {
-        assertThrows( TooComplexDnException.class, () -> 
-        {
-            FastDnParser.parse( "SN=Lu\\C4\\8Di\\C4\\87" );
-        } );
+        DnParser.parseDn( "SN=Lu\\C4\\8Di\\C4\\87" );
     }
 
 
@@ -570,7 +532,7 @@ public class FastDnParserTest
     {
         try
         {
-            FastDnParser.parse( "&#347;=&#347;rasulu,dc=example,dc=com" );
+            DnParser.parseDn( "&#347;=&#347;rasulu,dc=example,dc=com" );
             fail( "the invalid name should never succeed in a parse" );
         }
         catch ( LdapException e )
@@ -593,10 +555,7 @@ public class FastDnParserTest
     {
         String input = "ou=some test\\,  something else";
 
-        assertThrows( TooComplexDnException.class, () -> 
-        {
-            FastDnParser.parse( input ).toString();
-        } );
+        DnParser.parseDn( input ).toString();
     }
 
 
@@ -606,10 +565,7 @@ public class FastDnParserTest
         // '\' should be escaped as stated in RFC 2253
         String path = "windowsFilePath=C:\\\\cygwin";
 
-        assertThrows( TooComplexDnException.class, () -> 
-        {
-            FastDnParser.parse( path );
-        } );
+        DnParser.parseDn( path );
     }
 
 
@@ -620,7 +576,7 @@ public class FastDnParserTest
             { 'c', 'n', '=', 0x4A, ( byte ) 0xC3, ( byte ) 0xA9, 0x72, ( byte ) 0xC3, ( byte ) 0xB4, 0x6D, 0x65 },
             StandardCharsets.UTF_8 );
 
-        String result = FastDnParser.parse( cn ).toString();
+        String result = DnParser.parseDn( cn ).toString();
 
         assertEquals( "cn=J\u00e9r\u00f4me", result );
     }
@@ -634,7 +590,7 @@ public class FastDnParserTest
                 ( byte ) 0xC3, ( byte ) 0x9F, ( byte ) 0xC3, ( byte ) 0xA4, ( byte ) 0xC3, ( byte ) 0xB6,
                 ( byte ) 0xC3, ( byte ) 0xBC }, StandardCharsets.UTF_8 );
 
-        String result = FastDnParser.parse( cn ).toString();
+        String result = DnParser.parseDn( cn ).toString();
 
         assertEquals( "cn=\u00C4\u00D6\u00DC\u00DF\u00E4\u00F6\u00FC", result );
     }
@@ -655,7 +611,7 @@ public class FastDnParserTest
                 ( byte ) 0xC3, ( byte ) 0x9C, ( byte ) 0xC3, ( byte ) 0xBC, ( byte ) 0xC4, ( byte ) 0x9E,
                 ( byte ) 0xC4, ( byte ) 0x9F }, StandardCharsets.UTF_8 );
 
-        String result = FastDnParser.parse( cn ).toString();
+        String result = DnParser.parseDn( cn ).toString();
 
         assertEquals( "cn=\u0130\u0131\u015E\u015F\u00D6\u00F6\u00DC\u00FC\u011E\u011F", result );
 
@@ -672,9 +628,9 @@ public class FastDnParserTest
         String cn = new String( new byte[]
             { 'c', 'n', '=', ( byte ) 0xC3, ( byte ) 0x84, 0x5C, 0x32, 0x42 }, StandardCharsets.UTF_8 );
 
-        assertThrows( TooComplexDnException.class, () -> 
+        assertThrows( LdapInvalidDnException.class, () -> 
         {
-            FastDnParser.parse( cn ).toString();
+            DnParser.parseDn( null, cn, null ).toString();
         } );
     }
 
@@ -684,15 +640,12 @@ public class FastDnParserTest
      * fast DN parser
      */
     @Test
-    public void testAUmlautPlusChar()
+    public void testAUmlautPlusChar() throws LdapException
     {
         String cn = new String( new byte[]
             { 'c', 'n', '=', ( byte ) 0xC3, ( byte ) 0x84, '\\', '+' }, StandardCharsets.UTF_8 );
 
-        assertThrows( TooComplexDnException.class, () -> 
-        {
-            FastDnParser.parse( cn ).toString();
-        } );
+        DnParser.parseDn( cn ).toString();
     }
 
 
@@ -705,9 +658,9 @@ public class FastDnParserTest
     {
         String input = "ou=ou+test";
 
-        assertThrows( TooComplexDnException.class, () -> 
+        assertThrows( LdapInvalidDnException.class, () -> 
         {
-            FastDnParser.parse( input ).toString();
+            DnParser.parseDn( input ).toString();
         } );
     }
 }
diff --git a/pom.xml b/pom.xml
index 2477f6f59..564461e40 100644
--- a/pom.xml
+++ b/pom.xml
@@ -66,7 +66,6 @@
     <org.apache.directory.checkstyle-configuration.version>2.0.2</org.apache.directory.checkstyle-configuration.version>
     
     <!-- Set versions for depending jars -->
-    <antlr.version>2.7.7</antlr.version>
     <caffeine.version>2.9.3</caffeine.version>
     <commons.codec.version>1.21.0</commons.codec.version>
     <commons.collections.version>4.5.0</commons.collections.version>
@@ -275,19 +274,6 @@
                   </action>
                 </pluginExecution>
                 
-                <pluginExecution>
-                  <pluginExecutionFilter>
-                    <groupId>org.codehaus.mojo</groupId>
-                    <artifactId>antlr-maven-plugin</artifactId>
-                    <versionRange>[2.7.7,)</versionRange>
-                    <goals>
-                      <goal>generate</goal>
-                    </goals>
-                  </pluginExecutionFilter>
-                  <action>
-                    <ignore />
-                  </action>
-                </pluginExecution>
                 <pluginExecution>
                     <pluginExecutionFilter>
                         <groupId>org.apache.maven.plugins</groupId>
@@ -609,19 +595,6 @@
       </dependency>
 
       <!-- Other dependencies -->
-
-      <dependency>
-        <groupId>antlr</groupId>
-        <artifactId>antlr</artifactId>
-        <version>${antlr.version}</version>
-      </dependency>
-
-      <dependency>
-        <groupId>org.apache.servicemix.bundles</groupId>
-        <artifactId>org.apache.servicemix.bundles.antlr</artifactId>
-        <version>${antlr.version}_5</version>
-      </dependency>
-
       <dependency>
         <groupId>org.apache.servicemix.bundles</groupId>
         <artifactId>org.apache.servicemix.bundles.dom4j</artifactId>
diff --git a/src/main/appended-resources/supplemental-models.xml b/src/main/appended-resources/supplemental-models.xml
deleted file mode 100644
index 032fade10..000000000
--- a/src/main/appended-resources/supplemental-models.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  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
-
-  https://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.
--->
-
-<supplementalDataModels>
-  <supplement>
-    <project>
-      <groupId>antlr</groupId>
-      <artifactId>antlr</artifactId>
-      <name>ANTLR Parser Generator</name>
-      <organization>
-        <name>ANTLR</name>
-        <url>http://www.antlr.org/</url>
-      </organization>
-      <licenses>
-        <license>
-          <name>ANTLR 2 License (public domain)</name>
-          <url>http://www.antlr2.org/license.html</url>
-        </license>
-      </licenses>
-    </project>
-  </supplement>
-
-</supplementalDataModels>
diff --git a/util/src/main/java/org/apache/directory/api/util/ParserUtil.java b/util/src/main/java/org/apache/directory/api/util/ParserUtil.java
index 958770138..10c351638 100644
--- a/util/src/main/java/org/apache/directory/api/util/ParserUtil.java
+++ b/util/src/main/java/org/apache/directory/api/util/ParserUtil.java
@@ -37,17 +37,24 @@ public final class ParserUtil
 
     /** The various found characters */
     public static final char COLON      = ':';
+    public static final char COMMA      = ',';
     public static final char DOLLAR     = '$';
     public static final char DOT        = '.';
     public static final char DQUOTE     = '"';
     public static final char EQUAL      = '=';
+    public static final char ESC        = '\\';
     public static final char HYPHEN     = '-';
+    public static final char LANGLE     = '<';
     public static final char LCURLY     = '{';
     public static final char LPAREN     = '(';
+    public static final char PLUS       = '+';
+    public static final char RANGLE     = '>';
     public static final char RCURLY     = '}';
     public static final char RPAREN     = ')';
     public static final char SEMI_COLON = ';';
     public static final char SEP        = ',';
+    public static final char SHARP      = '#';
+    public static final char SPACE      = ' ';
     public static final char USCORE     = '_';
     public static final char ZERO       = '0';
 
@@ -224,11 +231,31 @@ public final class ParserUtil
     }
     
     
+    /**
+     * Match a specific character at the current position in the string.
+     * If present, move forward the current position.
+     * 
+     * @param bytes The byte[] to process
+     * @param b The expected byte
+     * @param pos The current position in the string
+     * @throws ParseException If the required character is not found at current position
+     */
+    public static void matchChar( byte[] bytes, byte b, Position pos ) throws ParseException
+    {
+        if ( !hasMoreChars( pos ) || bytes[ pos.start ] != b )
+        {
+            throw new ParseException( I18n.err( I18n.ERR_17073_CHAR_REQUIRED, b ), pos.start );
+        }
+        
+        pos.start++;
+    }
+    
+    
     /**
      * Check if a specific character is present at the current position in the string.
      * If the character is found, the position will be moved forward.
      * 
-     * @param schema The string to process
+     * @param str The string to process
      * @param c The expected character
      * @param pos The current position in the string
      * @return <code>true</code> if the character is present at the current position,
@@ -245,6 +272,29 @@ public final class ParserUtil
         
         return true;
     }
+    
+    
+    /**
+     * Check if a specific character is present at the current position in the string.
+     * If the character is found, the position will be moved forward.
+     * 
+     * @param bytes The byte[] to process
+     * @param c The expected character
+     * @param pos The current position in the string
+     * @return <code>true</code> if the character is present at the current position,
+     * <code>false</code> if the character is not present or if we are at the end of the string.
+     */
+    public static boolean isMatchChar( byte c, Position pos )
+    {
+        if ( !hasMoreChars( pos ) || ( pos.getByte() != c ) )
+        {
+            return false;
+        }
+        
+        pos.start++;
+        
+        return true;
+    }
 
 
     /**
diff --git a/util/src/main/java/org/apache/directory/api/util/Position.java b/util/src/main/java/org/apache/directory/api/util/Position.java
index 1e19f5d93..ff4fb7edf 100644
--- a/util/src/main/java/org/apache/directory/api/util/Position.java
+++ b/util/src/main/java/org/apache/directory/api/util/Position.java
@@ -40,6 +40,9 @@ public class Position
     /** The parsed text. It's only used by the toString function */
     private String text;
     
+    /** The parsed text as a UTF-8 byte array */
+    byte[] bytes;
+    
     /**
      * A public constructor
      */
@@ -48,6 +51,7 @@ public class Position
         // Nothing to do
     }
     
+    
     /**
      * A public constructor
      */
@@ -55,6 +59,33 @@ public class Position
     {
         this.text = text;
     }
+    
+    
+    /**
+     * A public constructor
+     */
+    public Position( byte[] bytes )
+    {
+        this.bytes = bytes;
+    }
+    
+    
+    /**
+     * @return the byte at the current position
+     */
+    public byte getByte()
+    {
+        return bytes[start];
+    }
+    
+    
+    /**
+     * @return The interned byte[]
+     */
+    public byte[] getBytes()
+    {
+        return bytes;
+    }
 
 
     /**
@@ -68,7 +99,7 @@ public class Position
             String head = text.substring( Math.max( start - 10, 0 ), start );
             String tail = text.substring( Math.min( start + 1, length ), Math.min( start + 10, length ) );
             
-            if ( start == length)
+            if ( start == length )
             {
                 return "'..." + head + "'";
             }
@@ -78,6 +109,73 @@ public class Position
                         + "...' [" + start + ", " + end + ", " + length + "]";
             }
         }
+        else if ( bytes != null )
+        {
+            int head = Math.max( start - 10, 0 );
+            int tail = Math.min( start + 1, length );
+            
+            if ( start == length )
+            {
+                return "'..." + ( char ) bytes[start - 1] + "[]'";
+            }
+            else
+            {
+                StringBuilder sb = new StringBuilder();
+                sb.append( "'" );
+                
+                for ( int i = head; i < start; i++ )
+                {
+                    byte b = bytes[i];
+                    
+                    if ( ( b >= 0x20 ) && ( b <= 0x7F ) )
+                    {
+                        sb.append( ( char ) b );
+                    }
+                    else
+                    {
+                        sb.append( Strings.byteToString( b ) );
+                    }
+                }
+
+                sb.append( "'[" );
+                byte b = bytes[start];
+                
+                if ( ( b >= 0x20 ) && ( b <= 0x7F ) )
+                {
+                    sb.append( ( char ) b );
+                }
+                else
+                {
+                    sb.append( Strings.byteToString( b ) );
+                }
+
+                sb.append( "]'" );
+
+                for ( int i = start + 1; i < tail; i++ )
+                {
+                    b = bytes[i];
+                    
+                    if ( ( b >= 0x20 ) && ( b <= 0x7F ) )
+                    {
+                        sb.append( ( char ) b );
+                    }
+                    else
+                    {
+                        sb.append( Strings.byteToString( b ) );
+                    }
+                }
+                
+                sb.append( "...' [" ).
+                    append( start ).
+                    append( ", " ).
+                    append( end ).
+                    append( ", " ).
+                    append( length ).
+                    append( "]" );
+                
+                return sb.toString();
+            }
+        }
         
         return "[" + start + ", " + end + ", " + length + "]";
     }


(directory-ldap-api) 02/02: merged the pom

Posted by el...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/directory-ldap-api.git

commit b1583e4a49b41b21eabedf511e4c587dcd8f5111
Merge: 7fafde7fb 14e9b4564
Author: Emmanuel Lécharny <em...@worteks.com>
AuthorDate: Wed May 13 16:26:43 2026 +0200

    merged the pom

 .github/workflows/codeql-analysis.yml              |   6 +-
 .github/workflows/scorecards.yml                   |   4 +-
 doap_ldap-api.rdf                                  |   2 +-
 ldap/client/api/pom.xml                            |   8 +-
 .../ldap/client/api/LdapConnectionConfig.java      |  29 +++
 .../ldap/client/api/LdapNetworkConnection.java     |   8 +-
 .../client/api/LdapNetworkConnectionSslTest.java   | 273 +++++++++++++++++++++
 .../ldap/client/api/LdapNetworkConnectionTest.java |  43 ++++
 .../client/api/NoVerificationTrustManager.java     |   0
 pom.xml                                            |   9 +-
 10 files changed, 369 insertions(+), 13 deletions(-)

diff --cc pom.xml
index 564461e40,590d343d7..7b1fa6966
--- a/pom.xml
+++ b/pom.xml
@@@ -66,8 -66,10 +66,9 @@@
      <org.apache.directory.checkstyle-configuration.version>2.0.2</org.apache.directory.checkstyle-configuration.version>
      
      <!-- Set versions for depending jars -->
 -    <antlr.version>2.7.7</antlr.version>
+     <bouncycastle.version>1.84</bouncycastle.version>
      <caffeine.version>2.9.3</caffeine.version>
-     <commons.codec.version>1.21.0</commons.codec.version>
+     <commons.codec.version>1.22.0</commons.codec.version>
      <commons.collections.version>4.5.0</commons.collections.version>
      <commons.lang.version>3.20.0</commons.lang.version>
      <commons.pool.version>2.13.1</commons.pool.version>