Index: shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserMatchingRuleUseDescriptionTest.java
===================================================================
--- shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserMatchingRuleUseDescriptionTest.java	(Revision 487885)
+++ shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserMatchingRuleUseDescriptionTest.java	(Arbeitskopie)
@@ -52,25 +52,25 @@
 
     public void testNumericOid() throws Exception
     {
-        SchemaParserTestUtils.testNumericOid( parser );
+        SchemaParserTestUtils.testNumericOid( parser, "APPLIES 1.1" );
     }
 
 
     public void testNames() throws Exception
     {
-        SchemaParserTestUtils.testNames( parser );
+        SchemaParserTestUtils.testNames( parser, "1.1", "APPLIES 1.1" );
     }
 
 
     public void testDescription() throws ParseException
     {
-        SchemaParserTestUtils.testDescription( parser );
+        SchemaParserTestUtils.testDescription( parser, "1.1", "APPLIES 1.1" );
     }
 
 
     public void testObsolete() throws ParseException
     {
-        SchemaParserTestUtils.testObsolete( parser );
+        SchemaParserTestUtils.testObsolete( parser, "1.1", "APPLIES 1.1" );
     }
 
 
@@ -80,11 +80,6 @@
         String value = null;
         MatchingRuleUseDescription mrud = null;
 
-        // no APPLIES
-        value = "( 1.1 )";
-        mrud = parser.parseMatchingRuleUseDescription( value );
-        assertEquals( 0, mrud.getApplicableAttributes().size() );
-
         // APPLIES simple numericoid
         value = "( 1.1 APPLIES 1.2.3.4.5.6.7.8.9.0 )";
         mrud = parser.parseMatchingRuleUseDescription( value );
@@ -220,12 +215,36 @@
         {
             // expected
         }
+        
+        // APPLIES is required
+        value = "( 1.1 )";
+        try
+        {
+            mrud = parser.parseMatchingRuleUseDescription( value );
+            fail( "Exception expected, APPLIES is required" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // APPLIES must only appear once
+        value = "( 1.1 APPLIES test1 APPLIES test2 )";
+        try
+        {
+            mrud = parser.parseMatchingRuleUseDescription( value );
+            fail( "Exception expected, APPLIES appears twice" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
     }
 
 
     public void testExtensions() throws ParseException
     {
-        SchemaParserTestUtils.testExtensions( parser );
+        SchemaParserTestUtils.testExtensions( parser, "1.1", "APPLIES 1.1" );
     }
 
 
@@ -259,6 +278,53 @@
     }
 
 
+    /**
+     * Test unique elements.
+     * 
+     * @throws ParseException
+     */
+    public void testUniqueElements() throws ParseException
+    {
+        String[] testValues = new String[]
+            { 
+                "( 1.1 APPLIES 1.1 NAME 'test1' NAME 'test2' )",
+                "( 1.1 APPLIES 1.1 DESC 'test1' DESC 'test2' )",
+                "( 1.1 APPLIES 1.1 OBSOLETE OBSOLETE )", 
+                "( 1.1 APPLIES 1.1 APPLIES test1 APPLIES test2 )",
+                "( 1.1 APPLIES 1.1 X-TEST 'test1' X-TEST 'test2' )" 
+            };
+        SchemaParserTestUtils.testUnique( parser, testValues );
+    }    
+    
+    
+    /**
+     * Test required elements.
+     * 
+     * @throws ParseException
+     */
+    public void testRequiredElements() throws ParseException
+    {
+        String value = null;
+        MatchingRuleUseDescription mrud = null;
+
+        value = "( 1.2.3.4.5.6.7.8.9.0 APPLIES a )";
+        mrud = parser.parseMatchingRuleUseDescription( value );
+        assertEquals( 1, mrud.getApplicableAttributes().size() );
+
+        value = "( 1.2.3.4.5.6.7.8.9.0 )";
+        try
+        {
+            mrud = parser.parseMatchingRuleUseDescription( value );
+            fail( "Exception expected, APPLIES is required" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+    }
+    
+    
     ////////////////////////////////////////////////////////////////
     //       Some real-world matching rule use descriptons        //
     ////////////////////////////////////////////////////////////////
@@ -287,7 +353,7 @@
     {
         String[] testValues = new String[]
             {
-                "( 1.1 )",
+                "( 1.1 APPLIES 1.1 )",
                 "( 2.5.13.17 NAME 'octetStringMatch' APPLIES ( javaSerializedData $ userPassword ) )",
                 "( 2.5.13.1 NAME 'distinguishedNameMatch' APPLIES ( memberOf $ dITRedirect $ associatedName $ secretary $ documentAuthor $ manager $ seeAlso $ roleOccupant $ owner $ member $ distinguishedName $ aliasedObjectName $ namingContexts $ subschemaSubentry $ modifiersName $ creatorsName ) )",
                 "( 1.2.3.4.5.6.7.8.9.0 NAME ( 'abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789' 'test' ) DESC 'Descripton äöüß 部長' OBSOLETE APPLIES ( 0.1.2.3.4.5.6.7.8.9 $ abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 ) X-TEST-a ('test1-1' 'test1-2') X-TEST-b ('test2-1' 'test2-2') )" };
Index: shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/NameFormDescriptionSyntaxCheckerTest.java
===================================================================
--- shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/NameFormDescriptionSyntaxCheckerTest.java	(Revision 0)
+++ shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/NameFormDescriptionSyntaxCheckerTest.java	(Revision 0)
@@ -0,0 +1,84 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+package org.apache.directory.shared.ldap.schema.syntax;
+
+
+import junit.framework.TestCase;
+
+
+/**
+ * Test cases for NameFormDescriptionSyntaxChecker.
+ * 
+ * There are also many test cases in SchemaParserNameFormDescriptionTest.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class NameFormDescriptionSyntaxCheckerTest extends TestCase
+{
+    private NameFormDescriptionSyntaxChecker checker = new NameFormDescriptionSyntaxChecker();
+
+    public void testValid()
+    {
+        assertTrue( checker.isValidSyntax( "( 2.5.15.3 OC o MUST m )" ) );
+        assertTrue( checker.isValidSyntax( "( 2.5.15.3 OC o MUST m NAME 'orgNameForm' )" ) );
+        assertTrue( checker.isValidSyntax( "( 2.5.15.3 OC o MUST m NAME 'orgNameForm' DESC 'orgNameForm' )" ) );
+        assertTrue( checker.isValidSyntax( "( 2.5.15.3 MUST m NAME 'orgNameForm' DESC 'orgNameForm' OC organization )" ) );
+        assertTrue( checker.isValidSyntax( "( 2.5.15.3 NAME 'orgNameForm' DESC 'orgNameForm' OC organization MUST o )" ) );
+        assertTrue( checker.isValidSyntax( "( 2.5.15.3 NAME 'orgNameForm' DESC 'orgNameForm' OC organization MUST ( o ) MAY ( ou $ cn ) )" ) );
+        assertTrue( checker.isValidSyntax( "( 2.5.15.3 NAME 'orgNameForm' DESC 'orgNameForm' OC organization MUST ( o ) MAY ( ou $ cn ) )" ) );
+       
+
+        assertTrue( checker.isValidSyntax( "(2.5.15.3 OC o MUST m)" ) );
+        assertTrue( checker.isValidSyntax( "(   2.5.15.3   NAME   'orgNameForm'    DESC    'orgNameForm'   OC   organization   MUST   (o)   MAY   (ou$cn))" ) );
+    }
+
+    public void testInvalid()
+    {
+        // null 
+        assertFalse( checker.isValidSyntax( null ) );
+        
+        // empty 
+        assertFalse( checker.isValidSyntax( "" ) );
+        
+        // missing/invalid OID
+        assertFalse( checker.isValidSyntax( "()" ) );
+        assertFalse( checker.isValidSyntax( "(  )" ) );
+        assertFalse( checker.isValidSyntax( "( . )" ) );
+        assertFalse( checker.isValidSyntax( "( 1 )" ) );
+        assertFalse( checker.isValidSyntax( "( 1. )" ) );
+        assertFalse( checker.isValidSyntax( "( 1.2. )" ) );
+        assertFalse( checker.isValidSyntax( "( 1.A )" ) );
+        assertFalse( checker.isValidSyntax( "( A.B )" ) );
+
+        // missing right parenthesis
+        assertFalse( checker.isValidSyntax( "( 2.5.15.3 NAME 'orgNameForm'" ) );
+
+        // missing quotes
+        assertFalse( checker.isValidSyntax( "( 2.5.15.3 NAME orgNameForm )" ) );
+
+        // lowercase NAME, DESC, AUX
+        assertFalse( checker.isValidSyntax( "( 2.5.15.3 name 'orgNameForm' desc 'orgNameForm' oc o )" ) );
+
+        assertFalse( checker.isValidSyntax( "( 2.5.15.3 )" ) );
+        assertFalse( checker.isValidSyntax( "( 2.5.15.3 NAME 'orgNameForm' )" ) );
+    }
+
+}
Index: shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserAttributeTypeDescriptionTest.java
===================================================================
--- shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserAttributeTypeDescriptionTest.java	(Revision 487885)
+++ shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserAttributeTypeDescriptionTest.java	(Arbeitskopie)
@@ -58,7 +58,7 @@
      */
     public void testNumericOid() throws ParseException
     {
-        SchemaParserTestUtils.testNumericOid( parser );
+        SchemaParserTestUtils.testNumericOid( parser, "SYNTAX 1.1" );
     }
 
 
@@ -69,7 +69,7 @@
      */
     public void testNames() throws ParseException
     {
-        SchemaParserTestUtils.testNames( parser );
+        SchemaParserTestUtils.testNames( parser, "1.1", "SYNTAX 1.1" );
     }
 
 
@@ -80,7 +80,7 @@
      */
     public void testDescription() throws ParseException
     {
-        SchemaParserTestUtils.testDescription( parser );
+        SchemaParserTestUtils.testDescription( parser, "1.1", "SYNTAX 1.1" );
     }
 
 
@@ -91,7 +91,7 @@
      */
     public void testObsolete() throws ParseException
     {
-        SchemaParserTestUtils.testObsolete( parser );
+        SchemaParserTestUtils.testObsolete( parser, "1.1", "SYNTAX 1.1" );
     }
 
 
@@ -106,22 +106,22 @@
         AttributeTypeDescription atd = null;
 
         // no SUP
-        value = "( 1.1 )";
+        value = "( 1.1 SYNTAX 1.1 )";
         atd = parser.parseAttributeTypeDescription( value );
         assertNull( atd.getSuperType() );
 
         // SUP numericoid
-        value = "( 1.1 SUP 1.2.3.4.5.6.7.8.9.0 )";
+        value = "( 1.1 SYNTAX 1.1 SUP 1.2.3.4.5.6.7.8.9.0 )";
         atd = parser.parseAttributeTypeDescription( value );
         assertEquals( "1.2.3.4.5.6.7.8.9.0", atd.getSuperType() );
 
         // SUP descr
-        value = "( 1.1 SUP abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 )";
+        value = "( 1.1 SYNTAX 1.1 SUP abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 )";
         atd = parser.parseAttributeTypeDescription( value );
         assertEquals( "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789", atd.getSuperType() );
 
         // no quote allowed
-        value = "( 1.1 SUP 'name' )";
+        value = "( 1.1 SYNTAX 1.1 SUP 'name' )";
         try
         {
             atd = parser.parseAttributeTypeDescription( value );
@@ -133,7 +133,7 @@
         }
 
         // no quote allowed
-        value = "( 1.1 SUP '1.2.3.4' )";
+        value = "( 1.1 SYNTAX 1.1 SUP '1.2.3.4' )";
         try
         {
             atd = parser.parseAttributeTypeDescription( value );
@@ -145,7 +145,7 @@
         }
 
         // invalid character
-        value = "( 1.1 SUP 1.2.3.4.A )";
+        value = "( 1.1 SYNTAX 1.1 SUP 1.2.3.4.A )";
         try
         {
             atd = parser.parseAttributeTypeDescription( value );
@@ -157,7 +157,7 @@
         }
 
         // only single SUP allowed
-        value = "( 1.1 SUP ( name1 $ name2 ) )";
+        value = "( 1.1 SYNTAX 1.1 SUP ( name1 $ name2 ) )";
         try
         {
             atd = parser.parseAttributeTypeDescription( value );
@@ -169,7 +169,7 @@
         }
 
         // empty sup
-        value = "( 1.1 SUP )";
+        value = "( 1.1 SYNTAX 1.1 SUP )";
         try
         {
             atd = parser.parseAttributeTypeDescription( value );
@@ -194,22 +194,22 @@
         AttributeTypeDescription atd = null;
 
         // no EQUALITY
-        value = "( 1.1 )";
+        value = "( 1.1 SYNTAX 1.1 )";
         atd = parser.parseAttributeTypeDescription( value );
         assertNull( atd.getEqualityMatchingRule() );
 
         // EQUALITY numericoid
-        value = "( 1.1 EQUALITY 1.2.3.4567.8.9.0 )";
+        value = "( 1.1 SYNTAX 1.1 EQUALITY 1.2.3.4567.8.9.0 )";
         atd = parser.parseAttributeTypeDescription( value );
         assertEquals( "1.2.3.4567.8.9.0", atd.getEqualityMatchingRule() );
 
         // EQUALITY descr
-        value = "( 1.1 EQUALITY abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 )";
+        value = "( 1.1 SYNTAX 1.1 EQUALITY abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 )";
         atd = parser.parseAttributeTypeDescription( value );
         assertEquals( "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789", atd.getEqualityMatchingRule() );
 
         // no quote allowed
-        value = "( 1.1 EQUALITY 'caseExcactMatch' )";
+        value = "( 1.1 SYNTAX 1.1 EQUALITY 'caseExcactMatch' )";
         try
         {
             atd = parser.parseAttributeTypeDescription( value );
@@ -234,22 +234,22 @@
         AttributeTypeDescription atd = null;
 
         // no EQUALITY
-        value = "( 1.1 )";
+        value = "( 1.1 SYNTAX 1.1 )";
         atd = parser.parseAttributeTypeDescription( value );
         assertNull( atd.getOrderingMatchingRule() );
 
         // EQUALITY numericoid
-        value = "( 1.1 ORDERING 1.2.3.4567.8.9.0 )";
+        value = "( 1.1 SYNTAX 1.1 ORDERING 1.2.3.4567.8.9.0 )";
         atd = parser.parseAttributeTypeDescription( value );
         assertEquals( "1.2.3.4567.8.9.0", atd.getOrderingMatchingRule() );
 
         // EQUALITY descr
-        value = "( 1.1 ORDERING abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 )";
+        value = "( 1.1 SYNTAX 1.1 ORDERING abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 )";
         atd = parser.parseAttributeTypeDescription( value );
         assertEquals( "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789", atd.getOrderingMatchingRule() );
 
         // no quote allowed
-        value = "( 1.1 ORDERING 'generalizedTimeOrderingMatch' )";
+        value = "( 1.1 SYNTAX 1.1 ORDERING 'generalizedTimeOrderingMatch' )";
         try
         {
             atd = parser.parseAttributeTypeDescription( value );
@@ -274,23 +274,23 @@
         AttributeTypeDescription atd = null;
 
         // no EQUALITY
-        value = "( 1.1 )";
+        value = "( 1.1 SYNTAX 1.1 )";
         atd = parser.parseAttributeTypeDescription( value );
         assertNull( atd.getSubstringsMatchingRule() );
 
         // EQUALITY numericoid
-        value = "( 1.1 SUBSTR 1.2.3.4567.8.9.0 )";
+        value = "( 1.1 SYNTAX 1.1 SUBSTR 1.2.3.4567.8.9.0 )";
         atd = parser.parseAttributeTypeDescription( value );
         assertEquals( "1.2.3.4567.8.9.0", atd.getSubstringsMatchingRule() );
 
         // EQUALITY descr
-        value = "( 1.1 SUBSTR abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 )";
+        value = "( 1.1 SYNTAX 1.1 SUBSTR abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 )";
         atd = parser.parseAttributeTypeDescription( value );
         assertEquals( "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789", atd
             .getSubstringsMatchingRule() );
 
         // no quote allowed
-        value = "( 1.1 SUBSTR 'caseIgnoreSubstringsMatch' )";
+        value = "( 1.1 SYNTAX 1.1 SUBSTR 'caseIgnoreSubstringsMatch' )";
         try
         {
             atd = parser.parseAttributeTypeDescription( value );
@@ -314,7 +314,7 @@
         AttributeTypeDescription atd = null;
 
         // no SYNTAX
-        value = "( 1.1 )";
+        value = "( 1.1 SUP 1.1 )";
         atd = parser.parseAttributeTypeDescription( value );
         assertNull( atd.getSyntax() );
         assertEquals( 0, atd.getSyntaxLength() );
@@ -423,22 +423,22 @@
         AttributeTypeDescription atd = null;
 
         // not single-value
-        value = "( 1.1 NAME 'test' DESC 'Descripton' )";
+        value = "( 1.1 SYNTAX 1.1 NAME 'test' DESC 'Descripton' )";
         atd = parser.parseAttributeTypeDescription( value );
         assertFalse( atd.isSingleValued() );
 
         // single-value
-        value = "(1.1 NAME 'test' DESC 'Descripton' SINGLE-VALUE)";
+        value = "(1.1 SYNTAX 1.1 NAME 'test' DESC 'Descripton' SINGLE-VALUE)";
         atd = parser.parseAttributeTypeDescription( value );
         assertTrue( atd.isSingleValued() );
 
         // single-value 
-        value = "(1.1 SINGLE-VALUE)";
+        value = "(1.1 SYNTAX 1.1 SINGLE-VALUE)";
         atd = parser.parseAttributeTypeDescription( value );
         assertTrue( atd.isSingleValued() );
 
         // ivalid
-        value = "(1.1 NAME 'test' DESC 'Descripton' SINGLE-VALU )";
+        value = "(1.1 SYNTAX 1.1 NAME 'test' DESC 'Descripton' SINGLE-VALU )";
         try
         {
             atd = parser.parseAttributeTypeDescription( value );
@@ -462,22 +462,22 @@
         AttributeTypeDescription atd = null;
 
         // not collective
-        value = "( 1.1 NAME 'test' DESC 'Descripton' )";
+        value = "( 1.1 SYNTAX 1.1 NAME 'test' DESC 'Descripton' )";
         atd = parser.parseAttributeTypeDescription( value );
         assertFalse( atd.isCollective() );
 
         // single-value
-        value = "(1.1 NAME 'test' DESC 'Descripton' COLLECTIVE )";
+        value = "(1.1 SYNTAX 1.1 NAME 'test' DESC 'Descripton' COLLECTIVE )";
         atd = parser.parseAttributeTypeDescription( value );
         assertTrue( atd.isCollective() );
 
         // single-value 
-        value = "(1.1 COLLECTIVE)";
+        value = "(1.1 SYNTAX 1.1 COLLECTIVE)";
         atd = parser.parseAttributeTypeDescription( value );
         assertTrue( atd.isCollective() );
 
         // ivalid
-        value = "(1.1 NAME 'test' DESC 'Descripton' COLLECTIV )";
+        value = "(1.1 SYNTAX 1.1 NAME 'test' DESC 'Descripton' COLLECTIV )";
         try
         {
             atd = parser.parseAttributeTypeDescription( value );
@@ -501,26 +501,26 @@
         AttributeTypeDescription atd = null;
 
         // not NO-USER-MODIFICATION
-        value = "( 1.1 NAME 'test' DESC 'Descripton' )";
+        value = "( 1.1 SYNTAX 1.1 NAME 'test' DESC 'Descripton' )";
         atd = parser.parseAttributeTypeDescription( value );
         assertTrue( atd.isUserModifiable() );
 
         // NO-USER-MODIFICATION
-        value = "(1.1 NAME 'test' DESC 'Descripton' NO-USER-MODIFICATION )";
+        value = "(1.1 SYNTAX 1.1 NAME 'test' DESC 'Descripton' NO-USER-MODIFICATION USAGE directoryOperation )";
         atd = parser.parseAttributeTypeDescription( value );
         assertFalse( atd.isUserModifiable() );
 
         // NO-USER-MODIFICATION 
-        value = "(1.1 NO-USER-MODIFICATION)";
+        value = "(1.1 SYNTAX 1.1 NO-USER-MODIFICATION USAGE directoryOperation )";
         atd = parser.parseAttributeTypeDescription( value );
         assertFalse( atd.isUserModifiable() );
 
         // ivalid
-        value = "(1.1 NAME 'test' DESC 'Descripton' NO-USER-MODIFICATIO )";
+        value = "(1.1 SYNTAX 1.1 NAME 'test' DESC 'Descripton' NO-USER-MODIFICATIO USAGE directoryOperation )";
         try
         {
             atd = parser.parseAttributeTypeDescription( value );
-            fail( "Exception expected, invalid COLLECTIVE value" );
+            fail( "Exception expected, invalid NO-USER-MODIFICATION value" );
         }
         catch ( ParseException pe )
         {
@@ -540,34 +540,34 @@
         AttributeTypeDescription atd = null;
 
         // DEFAULT is userApplications
-        value = "( 1.1 )";
+        value = "( 1.1 SYNTAX 1.1 )";
         atd = parser.parseAttributeTypeDescription( value );
         assertEquals( UsageEnum.USER_APPLICATIONS, atd.getUsage() );
 
         // userApplications
-        value = "( 1.1 USAGE userApplications )";
+        value = "( 1.1 SYNTAX 1.1 USAGE userApplications )";
         atd = parser.parseAttributeTypeDescription( value );
         assertEquals( UsageEnum.USER_APPLICATIONS, atd.getUsage() );
 
         // directoryOperation
-        value = "( 1.1 USAGE directoryOperation )";
+        value = "( 1.1 SYNTAX 1.1 USAGE directoryOperation )";
         atd = parser.parseAttributeTypeDescription( value );
         assertEquals( UsageEnum.DIRECTORY_OPERATION, atd.getUsage() );
 
         // AUXILIARY
-        value = "( 1.1 USAGE distributedOperation )";
+        value = "( 1.1 SYNTAX 1.1 USAGE distributedOperation )";
         atd = parser.parseAttributeTypeDescription( value );
         assertEquals( UsageEnum.DISTRIBUTED_OPERATION, atd.getUsage() );
 
         // STRUCTURAL
-        value = "( 1.1 USAGE dSAOperation )";
+        value = "( 1.1 SYNTAX 1.1 USAGE dSAOperation )";
         atd = parser.parseAttributeTypeDescription( value );
         assertEquals( UsageEnum.DSA_OPERATION, atd.getUsage() );
 
         // TODO: case insensitive?
 
         // ivalid
-        value = "( 1.1 USAGE abc )";
+        value = "( 1.1 SYNTAX 1.1 USAGE abc )";
         try
         {
             atd = parser.parseAttributeTypeDescription( value );
@@ -587,7 +587,7 @@
      */
     public void testExtensions() throws ParseException
     {
-        SchemaParserTestUtils.testExtensions( parser );
+        SchemaParserTestUtils.testExtensions( parser, "1.1", "SYNTAX 1.1" );
     }
 
 
@@ -601,7 +601,7 @@
         String value = null;
         AttributeTypeDescription atd = null;
 
-        value = "( 1.2.3.4.5.6.7.8.9.0 NAME ( 'abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789' 'test' ) DESC 'Descripton äöüß 部長' OBSOLETE SUP abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 EQUALITY 2.3.4.5.6.7.8.9.0.1 ORDERING 3.4.5.6.7.8.9.0.1.2 SUBSTR 4.5.6.7.8.9.0.1.2.3 SYNTAX 5.6.7.8.9.0.1.2.3.4{1234567890} SINGLE-VALUE COLLECTIVE NO-USER-MODIFICATION USAGE dSAOperation X-TEST-a ('test1-1' 'test1-2') X-TEST-b ('test2-1' 'test2-2') )";
+        value = "( 1.2.3.4.5.6.7.8.9.0 NAME ( 'abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789' 'test' ) DESC 'Descripton äöüß 部長' OBSOLETE SUP abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 EQUALITY 2.3.4.5.6.7.8.9.0.1 ORDERING 3.4.5.6.7.8.9.0.1.2 SUBSTR 4.5.6.7.8.9.0.1.2.3 SYNTAX 5.6.7.8.9.0.1.2.3.4{1234567890} SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation X-TEST-a ('test1-1' 'test1-2') X-TEST-b ('test2-1' 'test2-2') )";
         atd = parser.parseAttributeTypeDescription( value );
 
         assertEquals( "1.2.3.4.5.6.7.8.9.0", atd.getNumericOid() );
@@ -618,7 +618,7 @@
         assertEquals( 1234567890, atd.getSyntaxLength() );
 
         assertTrue( atd.isSingleValued() );
-        assertTrue( atd.isCollective() );
+        assertFalse( atd.isCollective() );
         assertFalse( atd.isUserModifiable() );
         assertEquals( UsageEnum.DSA_OPERATION, atd.getUsage() );
 
@@ -633,8 +633,180 @@
         assertEquals( "test2-2", atd.getExtensions().get( "X-TEST-b" ).get( 1 ) );
     }
 
+    
+    /**
+     * Test unique elements.
+     * 
+     * @throws ParseException
+     */
+    public void testUniqueElements() throws ParseException
+    {
+        String[] testValues = new String[]
+            { 
+                "( 1.1 SYNTAX 1.1 NAME 'test1' NAME 'test2' )", 
+                "( 1.1 SYNTAX 1.1 DESC 'test1' DESC 'test2' )",
+                "( 1.1 SYNTAX 1.1 OBSOLETE OBSOLETE )", 
+                "( 1.1 SYNTAX 1.1 SUP test1 SUP test2 )",
+                "( 1.1 SYNTAX 1.1 EQUALITY test1 EQUALITY test2 )",
+                "( 1.1 SYNTAX 1.1 ORDERING test1 ORDERING test2 )",
+                "( 1.1 SYNTAX 1.1 SUBSTR test1 SUBSTR test2 )",
+                "( 1.1 SYNTAX 1.1 SYNTAX 2.2 SYNTAX 3.3 )",
+                "( 1.1 SYNTAX 1.1 SINGLE-VALUE SINGLE-VALUE )",
+                "( 1.1 SYNTAX 1.1 COLLECTIVE COLLECTIVE )", 
+                "( 1.1 SYNTAX 1.1 USAGE directoryOperation NO-USER-MODIFICATION NO-USER-MODIFICATION )", 
+                "( 1.1 SYNTAX 1.1 USAGE directoryOperation USAGE userApplications )", 
+                "( 1.1 SYNTAX 1.1 X-TEST 'test1' X-TEST 'test2' )" 
+            };
+        SchemaParserTestUtils.testUnique( parser, testValues );
+    }
+    
+    
+    /**
+     * Test required elements.
+     * 
+     * @throws ParseException
+     */
+    public void testRequiredElements() throws ParseException
+    {
+        String value = null;
+        AttributeTypeDescription atd = null;
 
+        value = "( 1.2.3.4.5.6.7.8.9.0 SYNTAX 1.1 SUP 1.1 )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertNotNull( atd.getSyntax() );
+        assertNotNull( atd.getSuperType() );
+        
+        value = "( 1.2.3.4.5.6.7.8.9.0 SYNTAX 1.1 )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertNotNull( atd.getSyntax() );
+        assertNull( atd.getSuperType() );
+        
+        value = "( 1.2.3.4.5.6.7.8.9.0 SUP 1.1 )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertNull( atd.getSyntax() );
+        assertNotNull( atd.getSuperType() );
+
+        value = "( 1.2.3.4.5.6.7.8.9.0 )";
+        try
+        {
+            parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, SYNTAX or SUP is required" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+    }
+    
     /**
+     * Test collective constraint:
+     * COLLECTIVE requires USAGE userApplications
+     * 
+     * @throws ParseException
+     */
+    public void testCollecitveConstraint() throws ParseException
+    {
+        String value = null;
+        AttributeTypeDescription atd = null;
+
+        value = "( 1.1 SYNTAX 1.1 COLLECTIVE )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertTrue( atd.isCollective() );
+        assertEquals( UsageEnum.USER_APPLICATIONS , atd.getUsage() );
+        
+        value = "( 1.1 SYNTAX 1.1 COLLECTIVE USAGE userApplications )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertTrue( atd.isCollective() );
+        assertEquals( UsageEnum.USER_APPLICATIONS , atd.getUsage() );
+        
+        value = "( 1.1 SYNTAX 1.1 COLLECTIVE USAGE directoryOperation )";
+        try
+        {
+            parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, COLLECTIVE requires USAGE userApplications" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+        
+        value = "( 1.1 SYNTAX 1.1 COLLECTIVE USAGE dSAOperation )";
+        try
+        {
+            parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, COLLECTIVE requires USAGE userApplications" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+        
+        value = "( 1.1 SYNTAX 1.1 COLLECTIVE USAGE distributedOperation )";
+        try
+        {
+            parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, COLLECTIVE requires USAGE userApplications" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+        
+    }
+    
+    
+    /**
+     * Test no-user-modification constraint:
+     * NO-USER-MODIFICATION requires an operational USAGE
+     * 
+     * @throws ParseException
+     */
+    public void testNoUserModificatonConstraint() throws ParseException
+    {
+        String value = null;
+        AttributeTypeDescription atd = null;
+        
+        value = "( 1.1 SYNTAX 1.1 NO-USER-MODIFICATION USAGE directoryOperation )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertFalse( atd.isUserModifiable() );
+        assertEquals( UsageEnum.DIRECTORY_OPERATION , atd.getUsage() );
+        
+        value = "( 1.1 SYNTAX 1.1 NO-USER-MODIFICATION USAGE dSAOperation )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertFalse( atd.isUserModifiable() );
+        assertEquals( UsageEnum.DSA_OPERATION , atd.getUsage() );
+        
+        value = "( 1.1 SYNTAX 1.1 NO-USER-MODIFICATION USAGE distributedOperation )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertFalse( atd.isUserModifiable() );
+        assertEquals( UsageEnum.DISTRIBUTED_OPERATION , atd.getUsage() );
+        
+        value = "( 1.1 SYNTAX 1.1 NO-USER-MODIFICATION USAGE userApplications )";
+        try
+        {
+            parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, NO-USER-MODIFICATION requires an operational USAGE" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+        
+        value = "( 1.1 SYNTAX 1.1 NO-USER-MODIFICATION )";
+        try
+        {
+            parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, NO-USER-MODIFICATION requires an operational USAGE" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+    }
+    
+    
+    /**
      * Ensure that element order is ignored
      * 
      * @throws ParseException
@@ -697,12 +869,11 @@
     {
         String[] testValues = new String[]
             {
-                "( 1.1 )",
+                "( 1.1 SYNTAX 1.1 )",
                 "( 2.5.4.41 NAME 'name' DESC 'RFC2256: common supertype of name attributes'  EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} USAGE userApplications )",
                 "( 2.5.4.3 NAME ( 'cn' 'commonName' ) DESC 'RFC2256: common name(s) for which the entity is known by'  SUP name EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 USAGE userApplications )",
-                "( 1.2.3.4.5.6.7.8.9.0 NAME ( 'abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789' 'test' ) DESC 'Descripton äöüß 部長' OBSOLETE SUP abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 EQUALITY 2.3.4.5.6.7.8.9.0.1 ORDERING 3.4.5.6.7.8.9.0.1.2 SUBSTR 4.5.6.7.8.9.0.1.2.3 SYNTAX 5.6.7.8.9.0.1.2.3.4{1234567890} SINGLE-VALUE COLLECTIVE NO-USER-MODIFICATION USAGE dSAOperation X-TEST-a ('test1-1' 'test1-2') X-TEST-b ('test2-1' 'test2-2') )" };
+                "( 1.2.3.4.5.6.7.8.9.0 NAME ( 'abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789' 'test' ) DESC 'Descripton äöüß 部長' OBSOLETE SUP abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 EQUALITY 2.3.4.5.6.7.8.9.0.1 ORDERING 3.4.5.6.7.8.9.0.1.2 SUBSTR 4.5.6.7.8.9.0.1.2.3 SYNTAX 5.6.7.8.9.0.1.2.3.4{1234567890} SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation X-TEST-a ('test1-1' 'test1-2') X-TEST-b ('test2-1' 'test2-2') )" };
         SchemaParserTestUtils.testMultiThreaded( parser, testValues );
-
     }
 
 }
Index: shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserMatchingRuleDescriptionTest.java
===================================================================
--- shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserMatchingRuleDescriptionTest.java	(Revision 487885)
+++ shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserMatchingRuleDescriptionTest.java	(Arbeitskopie)
@@ -51,25 +51,25 @@
 
     public void testNumericOid() throws Exception
     {
-        SchemaParserTestUtils.testNumericOid( parser );
+        SchemaParserTestUtils.testNumericOid( parser, "SYNTAX 1.1" );
     }
 
 
     public void testNames() throws Exception
     {
-        SchemaParserTestUtils.testNames( parser );
+        SchemaParserTestUtils.testNames( parser, "1.1", "SYNTAX 1.1" );
     }
 
 
     public void testDescription() throws ParseException
     {
-        SchemaParserTestUtils.testDescription( parser );
+        SchemaParserTestUtils.testDescription( parser, "1.1", "SYNTAX 1.1" );
     }
 
 
     public void testObsolete() throws ParseException
     {
-        SchemaParserTestUtils.testObsolete( parser );
+        SchemaParserTestUtils.testObsolete( parser, "1.1", "SYNTAX 1.1" );
     }
 
 
@@ -106,12 +106,36 @@
             // expected
         }
 
+        // SYNTAX is required
+        value = "( 1.1 )";
+        try
+        {
+            mrd = parser.parseMatchingRuleDescription( value );
+            fail( "Exception expected, SYNTAX is required" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // OC must only appear once
+        value = "( 1.1 SYNTAX 2.2 SYNTAX 3.3 )";
+        try
+        {
+            mrd = parser.parseMatchingRuleDescription( value );
+            fail( "Exception expected, SYNTAX appears twice" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+        
     }
 
 
     public void testExtensions() throws ParseException
     {
-        SchemaParserTestUtils.testExtensions( parser );
+        SchemaParserTestUtils.testExtensions( parser, "1.1", "SYNTAX 1.1" );
     }
 
 
@@ -140,8 +164,55 @@
         assertEquals( "test2-1", mrd.getExtensions().get( "X-TEST-b" ).get( 0 ) );
         assertEquals( "test2-2", mrd.getExtensions().get( "X-TEST-b" ).get( 1 ) );
     }
+    
+    
+    /**
+     * Test unique elements.
+     * 
+     * @throws ParseException
+     */
+    public void testUniqueElements() throws ParseException
+    {
+        String[] testValues = new String[]
+            { 
+                "( 1.1 SYNTAX 1.1 NAME 'test1' NAME 'test2' )", 
+                "( 1.1 SYNTAX 1.1 DESC 'test1' DESC 'test2' )",
+                "( 1.1 SYNTAX 1.1 OBSOLETE OBSOLETE )", 
+                "( 1.1 SYNTAX 1.1 SYNTAX 2.2 SYNTAX 3.3 )",
+                "( 1.1 SYNTAX 1.1 X-TEST 'test1' X-TEST 'test2' )" 
+            };
+        SchemaParserTestUtils.testUnique( parser, testValues );
+    }
 
 
+    /**
+     * Test required elements.
+     * 
+     * @throws ParseException
+     */
+    public void testRequiredElements() throws ParseException
+    {
+        String value = null;
+        MatchingRuleDescription mrd = null;
+
+        value = "( 1.2.3.4.5.6.7.8.9.0 SYNTAX 1.1 )";
+        mrd = parser.parseMatchingRuleDescription( value );
+        assertNotNull( mrd.getSyntax() );
+
+        value = "( 1.2.3.4.5.6.7.8.9.0 )";
+        try
+        {
+            mrd = parser.parseMatchingRuleDescription( value );
+            fail( "Exception expected, SYNTAX is required" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+    }
+    
+    
     ////////////////////////////////////////////////////////////////
     //         Some real-world matching rule descriptons          //
     ////////////////////////////////////////////////////////////////
@@ -203,7 +274,7 @@
     {
         String[] testValues = new String[]
             {
-                "( 1.1 )",
+                "( 1.1 SYNTAX 1.1 )",
                 "( 2.5.13.5 NAME 'caseExactMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
                 "( 2.5.13.5 NAME 'caseExactMatch' DESC 'Case Exact Matching on Directory String [defined in X.520]' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
                 "( 1.2.3.4.5.6.7.8.9.0 NAME ( 'abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789' 'test' ) DESC 'Descripton äöüß 部長' OBSOLETE SYNTAX 0.1.2.3.4.5.6.7.8.9 X-TEST-a ('test1-1' 'test1-2') X-TEST-b ('test2-1' 'test2-2') )" };
Index: shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserLdapSyntaxDescriptionTest.java
===================================================================
--- shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserLdapSyntaxDescriptionTest.java	(Revision 487885)
+++ shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserLdapSyntaxDescriptionTest.java	(Arbeitskopie)
@@ -57,7 +57,7 @@
      */
     public void testNumericOid() throws ParseException
     {
-        SchemaParserTestUtils.testNumericOid( parser );
+        SchemaParserTestUtils.testNumericOid( parser, "" );
     }
 
 
@@ -68,7 +68,7 @@
      */
     public void testDescription() throws ParseException
     {
-        SchemaParserTestUtils.testDescription( parser );
+        SchemaParserTestUtils.testDescription( parser, "1.1", "" );
     }
 
 
@@ -79,7 +79,7 @@
      */
     public void testExtensions() throws ParseException
     {
-        SchemaParserTestUtils.testExtensions( parser );
+        SchemaParserTestUtils.testExtensions( parser, "1.1", "" );
     }
 
 
@@ -111,6 +111,22 @@
     }
 
 
+    /**
+     * Test unique elements.
+     * 
+     * @throws ParseException
+     */
+    public void testUniqueElements() throws ParseException
+    {
+        String[] testValues = new String[]
+            { 
+                "( 1.1 DESC 'test1' DESC 'test2' )",
+                "( 1.1 X-TEST 'test1' X-TEST 'test2' )" 
+            };
+        SchemaParserTestUtils.testUnique( parser, testValues );
+    }
+    
+
     ////////////////////////////////////////////////////////////////
     //         Some real-world attribute type definitions         //
     ////////////////////////////////////////////////////////////////
Index: shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/DITStructureRuleDescriptionSyntaxCheckerTest.java
===================================================================
--- shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/DITStructureRuleDescriptionSyntaxCheckerTest.java	(Revision 0)
+++ shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/DITStructureRuleDescriptionSyntaxCheckerTest.java	(Revision 0)
@@ -0,0 +1,82 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+package org.apache.directory.shared.ldap.schema.syntax;
+
+
+import junit.framework.TestCase;
+
+
+/**
+ * Test cases for DITStructureRuleDescriptionSyntaxChecker.
+ * 
+ * There are also many test cases in SchemaParserDITStructureRuleDescriptionTest.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DITStructureRuleDescriptionSyntaxCheckerTest extends TestCase
+{
+    private DITStructureRuleDescriptionSyntaxChecker checker = new DITStructureRuleDescriptionSyntaxChecker();
+
+    public void testValid()
+    {
+        assertTrue( checker.isValidSyntax( "( 2 FORM 2.5.15.3 )" ) );
+        assertTrue( checker.isValidSyntax( "( 2 NAME 'organization' FORM 2.5.15.3 )" ) );
+        assertTrue( checker.isValidSyntax( "( 2 NAME 'organization' DESC 'organization structure rule' FORM 2.5.15.3 )" ) );
+        assertTrue( checker.isValidSyntax( "( 2 NAME 'organization' DESC 'organization structure rule' OBSOLETE FORM 2.5.15.3 )" ) );
+        assertTrue( checker.isValidSyntax( "( 2 NAME 'organization' DESC 'organization structure rule' OBSOLETE FORM 2.5.15.3 SUP 1 )" ) );
+        assertTrue( checker.isValidSyntax( "( 2 NAME 'organization' DESC 'organization structure rule' OBSOLETE FORM 2.5.15.3 SUP ( 1 ) )" ) );
+        assertTrue( checker.isValidSyntax( "( 2 NAME 'organization' DESC 'organization structure rule' OBSOLETE FORM 2.5.15.3 SUP ( 1 1234567890 5 ) )" ) );
+
+        assertTrue( checker.isValidSyntax( "(2 FORM 2.5.15.3)" ) );
+        assertTrue( checker.isValidSyntax( "(   2   NAME    'organization'    DESC    'organization structure rule'    OBSOLETE   FORM   2.5.15.3    SUP   (1 1234567890        5   ))" ) );
+    }
+
+    public void testInvalid()
+    {
+        // null 
+        assertFalse( checker.isValidSyntax( null ) );
+        
+        // empty 
+        assertFalse( checker.isValidSyntax( "" ) );
+        
+        // missing/invalid ruleid
+        assertFalse( checker.isValidSyntax( "()" ) );
+        assertFalse( checker.isValidSyntax( "(  )" ) );
+        assertFalse( checker.isValidSyntax( "( . )" ) );
+        assertFalse( checker.isValidSyntax( "( 1 )" ) );
+        assertFalse( checker.isValidSyntax( "( 1.2 )" ) );
+        assertFalse( checker.isValidSyntax( "( A )" ) );
+        assertFalse( checker.isValidSyntax( "( A.B )" ) );
+
+        // missing right parenthesis
+        assertFalse( checker.isValidSyntax( "( 2 FORM 2.5.15.3" ) );
+
+        // missing quotes
+        assertFalse( checker.isValidSyntax( "( 2 NAME organization FORM 2.5.15.3 )" ) );
+
+        // lowercase NAME, DESC, FORM
+        assertFalse( checker.isValidSyntax( "( 2 name 'organization' desc 'organization structure rule' form 2.5.15.3 )" ) );
+
+        // missing FORM
+        assertFalse( checker.isValidSyntax( "( 2 NAME 'organization' DESC 'organization structure rule' )" ) );
+    }
+
+}
Index: shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserDITStructureRuleDescriptionTest.java
===================================================================
--- shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserDITStructureRuleDescriptionTest.java	(Revision 0)
+++ shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserDITStructureRuleDescriptionTest.java	(Revision 0)
@@ -0,0 +1,434 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+package org.apache.directory.shared.ldap.schema.syntax;
+
+
+import java.text.ParseException;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.shared.ldap.schema.syntax.parser.DITStructureRuleDescriptionSchemaParser;
+
+
+/**
+ * Tests the DITStructureRuleDescriptionSchemaParser class.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class SchemaParserDITStructureRuleDescriptionTest extends TestCase
+{
+    /** the parser instance */
+    private DITStructureRuleDescriptionSchemaParser parser;
+
+
+    protected void setUp() throws Exception
+    {
+        parser = new DITStructureRuleDescriptionSchemaParser();
+    }
+
+
+    protected void tearDown() throws Exception
+    {
+        parser = null;
+    }
+
+
+    /**
+     * Test ruleid
+     * 
+     * @throws ParseException
+     */
+    public void testNumericRuleId() throws ParseException
+    {
+        String value = null;
+        DITStructureRuleDescription dsrd = null;
+
+        // null test
+        value = null;
+        try
+        {
+            parser.parseDITStructureRuleDescription( value );
+            fail( "Exception expected, null" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // no ruleid
+        value = "( )";
+        try
+        {
+            parser.parseDITStructureRuleDescription( value );
+            fail( "Exception expected, no ruleid" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // simple
+        value = "( 1 FORM 1.1 )";
+        dsrd = parser.parseDITStructureRuleDescription( value );
+        assertEquals( new Integer( 1 ), dsrd.getRuleId() );
+
+        // simple
+        value = "( 1234567890 FORM 1.1 )";
+        dsrd = parser.parseDITStructureRuleDescription( value );
+        assertEquals( new Integer( 1234567890 ), dsrd.getRuleId() );
+
+        // simple with spaces
+        value = "(      1234567890   FORM   1.1     )";
+        dsrd = parser.parseDITStructureRuleDescription( value );
+        assertEquals( new Integer( 1234567890 ), dsrd.getRuleId() );
+
+        // non-numeric not allowed
+        value = "( test FORM 1.1 )";
+        try
+        {
+            parser.parseDITStructureRuleDescription( value );
+            fail( "Exception expected, invalid ruleid test (non-numeric)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // oid not allowed
+        value = "( 1.2.3.4 FORM 1.1 )";
+        try
+        {
+            parser.parseDITStructureRuleDescription( value );
+            fail( "Exception expected, invalid ruleid 1.2.3.4 (oid)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // quotes not allowed
+        value = "( '1234567890' FORM 1.1 )";
+        try
+        {
+            parser.parse( value );
+            TestCase.fail( "Exception expected, invalid ruleid '1234567890' (quoted)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+    }
+
+
+    /**
+     * Tests NAME and its values
+     * 
+     * @throws ParseException
+     */
+    public void testNames() throws ParseException
+    {
+        SchemaParserTestUtils.testNames( parser, "1", "FORM 1.1" );
+    }
+
+
+    /**
+     * Tests DESC
+     * 
+     * @throws ParseException
+     */
+    public void testDescription() throws ParseException
+    {
+        SchemaParserTestUtils.testDescription( parser, "1", "FORM 1.1" );
+    }
+
+
+    /**
+     * Tests OBSOLETE
+     * 
+     * @throws ParseException
+     */
+    public void testObsolete() throws ParseException
+    {
+        SchemaParserTestUtils.testObsolete( parser, "1", "FORM 1.1" );
+    }
+
+
+    /**
+     * Tests FORM
+     * 
+     * @throws ParseException
+     */
+    public void testForm() throws ParseException
+    {
+        String value = null;
+        DITStructureRuleDescription dsrd = null;
+
+        // numeric oid
+        value = "( 1 FORM 1.2.3.4.5.6.7.8.9.0 )";
+        dsrd = parser.parseDITStructureRuleDescription( value );
+        assertEquals( "1.2.3.4.5.6.7.8.9.0", dsrd.getForm() );
+
+        // numeric oid
+        value = "(   1    FORM    123.4567.890    )";
+        dsrd = parser.parseDITStructureRuleDescription( value );
+        assertEquals( "123.4567.890", dsrd.getForm() );
+
+        // descr
+        value = "( 1 FORM abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 )";
+        dsrd = parser.parseDITStructureRuleDescription( value );
+        assertEquals( "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789", dsrd.getForm() );
+
+        // no quote allowed
+        value = "( 1 FORM '1.2.3.4.5.6.7.8.9.0' )";
+        try
+        {
+            dsrd = parser.parseDITStructureRuleDescription( value );
+            fail( "Exception expected, invalid FORM '1.2.3.4.5.6.7.8.9.0' (quoted)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // no quote allowed
+        value = "( 1 FORM 'test' )";
+        try
+        {
+            dsrd = parser.parseDITStructureRuleDescription( value );
+            fail( "Exception expected, invalid FORM 'test' (quoted)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // invalid character
+        value = "( 1 FORM 1.2.3.4.A )";
+        try
+        {
+            dsrd = parser.parseDITStructureRuleDescription( value );
+            fail( "Exception expected, invalid FORM 1.2.3.4.A (invalid character)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // invalid start
+        value = "( 1 FORM -test ) )";
+        try
+        {
+            dsrd = parser.parseDITStructureRuleDescription( value );
+            fail( "Exception expected, invalid FORM '-test' (starts with hypen)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // no multi value
+        value = "( 1 FORM ( test1 test2 ) )";
+        try
+        {
+            dsrd = parser.parseDITStructureRuleDescription( value );
+            fail( "Exception expected, FORM must be single valued" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+    }
+
+
+    /**
+     * Tests SUP
+     * 
+     * @throws ParseException
+     */
+    public void testSup() throws ParseException
+    {
+        String value = null;
+        DITStructureRuleDescription dsrd = null;
+
+        // no SUP
+        value = "( 1 FORM 1.1 )";
+        dsrd = parser.parseDITStructureRuleDescription( value );
+        assertEquals( 0, dsrd.getSuperRules().size() );
+
+        // SUP simple number
+        value = "( 1 FORM 1.1 SUP 1 )";
+        dsrd = parser.parseDITStructureRuleDescription( value );
+        assertEquals( 1, dsrd.getSuperRules().size() );
+        assertEquals( new Integer( 1 ), dsrd.getSuperRules().get( 0 ) );
+
+        // SUP single number
+        value = "( 1 FORM 1.1 SUP ( 1 ) )";
+        dsrd = parser.parseDITStructureRuleDescription( value );
+        assertEquals( 1, dsrd.getSuperRules().size() );
+        assertEquals( new Integer( 1 ), dsrd.getSuperRules().get( 0 ) );
+
+        // SUP multi number
+        value = "( 1 FORM 1.1 SUP (12345 67890) )";
+        dsrd = parser.parseDITStructureRuleDescription( value );
+        assertEquals( 2, dsrd.getSuperRules().size() );
+        assertEquals( new Integer( 12345 ), dsrd.getSuperRules().get( 0 ) );
+        assertEquals( new Integer( 67890 ), dsrd.getSuperRules().get( 1 ) );
+
+        // non-numeric not allowed
+        value = "( 1 FORM 1.1 SUP test )";
+        try
+        {
+            parser.parseDITStructureRuleDescription( value );
+            fail( "Exception expected, invalid SUP test (non-numeric)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // oid not allowed
+        value = "( 1 FORM 1.1 SUP 1.2.3.4 )";
+        try
+        {
+            parser.parseDITStructureRuleDescription( value );
+            fail( "Exception expected, invalid SUP 1.2.3.4 (oid)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+    }
+
+
+    /**
+     * Test extensions.
+     * 
+     * @throws ParseException
+     */
+    public void testExtensions() throws ParseException
+    {
+        SchemaParserTestUtils.testExtensions( parser, "1", "FORM 1.1" );
+
+    }
+
+
+    /**
+     * Test full object class description.
+     * 
+     * @throws ParseException
+     */
+    public void testFull() throws ParseException
+    {
+        String value = null;
+        DITStructureRuleDescription dsrd = null;
+
+        value = "( 1234567890 NAME ( 'abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789' 'test' ) DESC 'Descripton äöüß 部長' OBSOLETE FORM 2.3.4.5.6.7.8.9.0.1 SUP ( 1 1234567890 5 ) X-TEST-a ('test1-1' 'test1-2') X-TEST-b ('test2-1' 'test2-2') )";
+        dsrd = parser.parseDITStructureRuleDescription( value );
+
+        assertEquals( new Integer( 1234567890 ), dsrd.getRuleId() );
+        assertEquals( 2, dsrd.getNames().size() );
+        assertEquals( "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789", dsrd.getNames().get( 0 ) );
+        assertEquals( "test", dsrd.getNames().get( 1 ) );
+        assertEquals( "Descripton äöüß 部長", dsrd.getDescription() );
+        assertTrue( dsrd.isObsolete() );
+        assertEquals( "2.3.4.5.6.7.8.9.0.1", dsrd.getForm() );
+        assertEquals( 3, dsrd.getSuperRules().size() );
+        assertEquals( new Integer(1), dsrd.getSuperRules().get( 0 ) );
+        assertEquals( new Integer(1234567890), dsrd.getSuperRules().get( 1 ) );
+        assertEquals( new Integer(5), dsrd.getSuperRules().get( 2 ) );
+        assertEquals( 2, dsrd.getExtensions().size() );
+        assertNotNull( dsrd.getExtensions().get( "X-TEST-a" ) );
+        assertEquals( 2, dsrd.getExtensions().get( "X-TEST-a" ).size() );
+        assertEquals( "test1-1", dsrd.getExtensions().get( "X-TEST-a" ).get( 0 ) );
+        assertEquals( "test1-2", dsrd.getExtensions().get( "X-TEST-a" ).get( 1 ) );
+        assertNotNull( dsrd.getExtensions().get( "X-TEST-b" ) );
+        assertEquals( 2, dsrd.getExtensions().get( "X-TEST-b" ).size() );
+        assertEquals( "test2-1", dsrd.getExtensions().get( "X-TEST-b" ).get( 0 ) );
+        assertEquals( "test2-2", dsrd.getExtensions().get( "X-TEST-b" ).get( 1 ) );
+    }
+
+    
+    /**
+     * Test unique elements.
+     * 
+     * @throws ParseException
+     */
+    public void testUniqueElements() throws ParseException
+    {
+        String[] testValues = new String[]
+            { 
+                "( 1 FORM 1.1 NAME 'test1' NAME 'test2' )", 
+                "( 1 FORM 1.1 DESC 'test1' DESC 'test2' )",
+                "( 1 FORM 1.1 OBSOLETE OBSOLETE )", 
+                "( 1 FORM 1.1 FORM test1 FORM test2 )",
+                "( 1 FORM 1.1 SUP 1 SUP 2 )",
+                "( 1 FORM 1.1 X-TEST 'test1' X-TEST 'test2' )" 
+            };
+        SchemaParserTestUtils.testUnique( parser, testValues );
+    }
+    
+    
+    /**
+     * Test required elements.
+     * 
+     * @throws ParseException
+     */
+    public void testRequiredElements() throws ParseException
+    {
+        String value = null;
+        DITStructureRuleDescription dsrd = null;
+
+        value = "( 1 FORM 1.1 )";
+        dsrd = parser.parseDITStructureRuleDescription( value );
+        assertNotNull( dsrd.getForm() );
+
+        value = "( 1 )";
+        try
+        {
+            dsrd = parser.parseDITStructureRuleDescription( value );
+            fail( "Exception expected, FORM is required" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+        
+    }
+
+
+    /**
+     * Tests the multithreaded use of a single parser.
+     */
+    public void testMultiThreaded() throws Exception
+    {
+        String[] testValues = new String[]
+            {
+                "( 1 FORM 1.1 )",
+                "( 2 DESC 'organization structure rule' FORM 2.5.15.3 )",
+                "( 2 DESC 'organization structure rule' FORM 2.5.15.3 )",
+                "( 1234567890 NAME ( 'abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789' 'test' ) DESC 'Descripton äöüß 部長' OBSOLETE FORM 2.3.4.5.6.7.8.9.0.1 SUP ( 1 1234567890 5 ) X-TEST-a ('test1-1' 'test1-2') X-TEST-b ('test2-1' 'test2-2') )" };
+        SchemaParserTestUtils.testMultiThreaded( parser, testValues );
+
+    }
+
+}
Index: shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserTestUtils.java
===================================================================
--- shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserTestUtils.java	(Revision 487885)
+++ shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserTestUtils.java	(Arbeitskopie)
@@ -18,7 +18,7 @@
      * 
      * @throws ParseException
      */
-    public static void testNumericOid( AbstractSchemaParser parser ) throws ParseException
+    public static void testNumericOid( AbstractSchemaParser parser, String required ) throws ParseException
     {
         String value = null;
         AbstractSchemaDescription asd = null;
@@ -48,22 +48,22 @@
         }
 
         // simple
-        value = "( 0.1.2.3.4.5.6.7.8.9 )";
+        value = "( 0.1.2.3.4.5.6.7.8.9 "+required+" )";
         asd = parser.parse( value );
         TestCase.assertEquals( "0.1.2.3.4.5.6.7.8.9", asd.getNumericOid() );
 
         // simple
-        value = "( 123.4567.890 )";
+        value = "( 123.4567.890 "+required+")";
         asd = parser.parse( value );
         TestCase.assertEquals( "123.4567.890", asd.getNumericOid() );
         
         // simple with spaces
-        value = "(          0.1.2.3.4.5.6.7.8.9          )";
+        value = "(          0.1.2.3.4.5.6.7.8.9         "+required+" )";
         asd = parser.parse( value );
         TestCase.assertEquals( "0.1.2.3.4.5.6.7.8.9", asd.getNumericOid() );
 
         // non-numeric not allowed
-        value = "( test )";
+        value = "( test "+required+" )";
         try
         {
             parser.parse( value );
@@ -75,7 +75,7 @@
         }
 
         // to short
-        value = "( 1 )";
+        value = "( 1 "+required+" )";
         try
         {
             parser.parse( value );
@@ -87,7 +87,7 @@
         }
 
         // dot only
-        value = "( . )";
+        value = "( . "+required+" )";
         try
         {
             parser.parse( value );
@@ -99,7 +99,7 @@
         }
 
         // ends with dot
-        value = "( 1.1. )";
+        value = "( 1.1. "+required+" )";
         try
         {
             parser.parse( value );
@@ -111,7 +111,7 @@
         }
 
         // quotes not allowed
-        value = "( '1.1' )";
+        value = "( '1.1' "+required+" )";
         try
         {
             parser.parse( value );
@@ -123,7 +123,7 @@
         }
 
         // leading 0 not allowed
-        value = "( 01.1 )";
+        value = "( 01.1 "+required+" )";
         try
         {
             parser.parse( value );
@@ -135,7 +135,7 @@
         }
 
         // alpha not allowed
-        value = "( 1.2.a.4 )";
+        value = "( 1.2.a.4 "+required+" )";
         try
         {
             parser.parse( value );
@@ -153,44 +153,44 @@
      * 
      * @throws ParseException
      */
-    public static void testNames( AbstractSchemaParser parser ) throws ParseException
+    public static void testNames( AbstractSchemaParser parser, String oid, String required ) throws ParseException
     {
         String value = null;
         AbstractSchemaDescription asd = null;
 
         // alpha
-        value = "( 1.1 NAME 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' )";
+        value = "( "+oid+" "+required+" NAME 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' )";
         asd = parser.parse( value );
         TestCase.assertEquals( 1, asd.getNames().size() );
         TestCase.assertEquals( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", asd.getNames().get( 0 ) );
 
         // alpha-num-hypen
-        value = "( 1.1 NAME 'abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789' )";
+        value = "( "+oid+" "+required+" NAME 'abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789' )";
         asd = parser.parse( value );
         TestCase.assertEquals( 1, asd.getNames().size() );
         TestCase.assertEquals( "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789", asd.getNames().get( 0 ) );
 
         // with parentheses
-        value = "( 1.1 NAME ( 'a-z-0-9' ) )";
+        value = "( "+oid+" "+required+" NAME ( 'a-z-0-9' ) )";
         asd = parser.parse( value );
         TestCase.assertEquals( 1, asd.getNames().size() );
         TestCase.assertEquals( "a-z-0-9", asd.getNames().get( 0 ) );
 
         // with parentheses, without space
-        value = "( 1.1 NAME ('a-z-0-9') )";
+        value = "( "+oid+" "+required+" NAME ('a-z-0-9') )";
         asd = parser.parse( value );
         TestCase.assertEquals( 1, asd.getNames().size() );
         TestCase.assertEquals( "a-z-0-9", asd.getNames().get( 0 ) );
 
         // multi with space
-        value = "( 1.1 NAME ( 'test1' 'test2' ) )";
+        value = "( "+oid+" "+required+" NAME ( 'test1' 'test2' ) )";
         asd = parser.parse( value );
         TestCase.assertEquals( 2, asd.getNames().size() );
         TestCase.assertEquals( "test1", asd.getNames().get( 0 ) );
         TestCase.assertEquals( "test2", asd.getNames().get( 1 ) );
 
         // multi without space
-        value = "( 1.1 NAME ('test1' 'test2' 'test3') )";
+        value = "( "+oid+" "+required+" NAME ('test1' 'test2' 'test3') )";
         asd = parser.parse( value );
         TestCase.assertEquals( 3, asd.getNames().size() );
         TestCase.assertEquals( "test1", asd.getNames().get( 0 ) );
@@ -198,7 +198,7 @@
         TestCase.assertEquals( "test3", asd.getNames().get( 2 ) );
 
         // multi with many spaces
-        value = "(          1.1          NAME          (          'test1'          'test2'          'test3'          )          )";
+        value = "(          "+oid+" "+required+"          NAME          (          'test1'          'test2'          'test3'          )          )";
         asd = parser.parse( value );
         TestCase.assertEquals( 3, asd.getNames().size() );
         TestCase.assertEquals( "test1", asd.getNames().get( 0 ) );
@@ -206,7 +206,7 @@
         TestCase.assertEquals( "test3", asd.getNames().get( 2 ) );
 
         // lowercase
-        value = "( 1.1 name 'test' )";
+        value = "( "+oid+" "+required+" name 'test' )";
         try
         {
             parser.parse( value );
@@ -218,7 +218,7 @@
         }
 
         // unquoted
-        value = "( 1.1 NAME test )";
+        value = "( "+oid+" "+required+" NAME test )";
         try
         {
             parser.parse( value );
@@ -230,7 +230,7 @@
         }
 
         // start with number
-        value = "( 1.1 NAME '1test' )";
+        value = "( "+oid+" "+required+" NAME '1test' )";
         try
         {
             parser.parse( value );
@@ -242,7 +242,7 @@
         }
 
         // start with hypen
-        value = "( 1.1 NAME '-test' )";
+        value = "( "+oid+" "+required+" NAME '-test' )";
         try
         {
             parser.parse( value );
@@ -254,7 +254,7 @@
         }
 
         // invalid character
-        value = "( 1.1 NAME 'te_st' )";
+        value = "( "+oid+" "+required+" NAME 'te_st' )";
         try
         {
             parser.parse( value );
@@ -266,7 +266,7 @@
         }
 
         // NAM unknown
-        value = "( 1.1 NAM 'test' )";
+        value = "( "+oid+" "+required+" NAM 'test' )";
         try
         {
             parser.parse( value );
@@ -278,7 +278,7 @@
         }
 
         // one valid, one invalid
-        value = "( 1.1 NAME ( 'test' 'te_st' ) )";
+        value = "( "+oid+" "+required+" NAME ( 'test' 'te_st' ) )";
         try
         {
             parser.parse( value );
@@ -290,7 +290,7 @@
         }
 
         // no space between values
-        value = "( 1.1 NAME ( 'test1''test2' ) )";
+        value = "( "+oid+" "+required+" NAME ( 'test1''test2' ) )";
         try
         {
             asd = parser.parse( value );
@@ -308,23 +308,23 @@
      * 
      * @throws ParseException
      */
-    public static void testDescription( AbstractSchemaParser parser ) throws ParseException
+    public static void testDescription( AbstractSchemaParser parser, String oid, String required ) throws ParseException
     {
         String value = null;
         AbstractSchemaDescription asd = null;
 
         // simple
-        value = "(1.1 DESC 'Descripton')";
+        value = "("+oid+" "+required+" DESC 'Descripton')";
         asd = parser.parse( value );
         TestCase.assertEquals( "Descripton", asd.getDescription() );
 
         // unicode
-        value = "( 1.1 DESC 'Descripton äöüß 部長' )";
+        value = "( "+oid+" "+required+" DESC 'Descripton äöüß 部長' )";
         asd = parser.parse( value );
         TestCase.assertEquals( "Descripton äöüß 部長", asd.getDescription() );
 
         // lowercase
-        value = "( 1.1 desc 'Descripton' )";
+        value = "( "+oid+" "+required+" desc 'Descripton' )";
         try
         {
             parser.parse( value );
@@ -343,18 +343,18 @@
      * 
      * @throws ParseException
      */
-    public static void testExtensions( AbstractSchemaParser parser ) throws ParseException
+    public static void testExtensions( AbstractSchemaParser parser, String oid, String required ) throws ParseException
     {
         String value = null;
         AbstractSchemaDescription asd = null;
 
         // no extension
-        value = "( 1.1 )";
+        value = "( "+oid+" "+required+" )";
         asd = parser.parse( value );
         TestCase.assertEquals( 0, asd.getExtensions().size() );
 
         // single extension with one value
-        value = "( 1.1 X-TEST 'test' )";
+        value = "( "+oid+" "+required+" X-TEST 'test' )";
         asd = parser.parse( value );
         TestCase.assertEquals( 1, asd.getExtensions().size() );
         TestCase.assertNotNull( asd.getExtensions().get( "X-TEST" ) );
@@ -362,7 +362,7 @@
         TestCase.assertEquals( "test", asd.getExtensions().get( "X-TEST" ).get( 0 ) );
 
         // single extension with multiple values
-        value = "( 1.1 X-TEST-ABC ('test1' 'test äöüß'       'test 部長' ) )";
+        value = "( "+oid+" "+required+" X-TEST-ABC ('test1' 'test äöüß'       'test 部長' ) )";
         asd = parser.parse( value );
         TestCase.assertEquals( 1, asd.getExtensions().size() );
         TestCase.assertNotNull( asd.getExtensions().get( "X-TEST-ABC" ) );
@@ -372,7 +372,7 @@
         TestCase.assertEquals( "test 部長", asd.getExtensions().get( "X-TEST-ABC" ).get( 2 ) );
 
         // multiple extensions
-        value = "(1.1 X-TEST-a ('test1-1' 'test1-2') X-TEST-b ('test2-1' 'test2-2'))";
+        value = "("+oid+" "+required+" X-TEST-a ('test1-1' 'test1-2') X-TEST-b ('test2-1' 'test2-2'))";
         asd = parser.parse( value );
         TestCase.assertEquals( 2, asd.getExtensions().size() );
         TestCase.assertNotNull( asd.getExtensions().get( "X-TEST-a" ) );
@@ -385,7 +385,7 @@
         TestCase.assertEquals( "test2-2", asd.getExtensions().get( "X-TEST-b" ).get( 1 ) );
 
         // invalid extension, no number allowed
-        value = "( 1.1 X-TEST1 'test' )";
+        value = "( "+oid+" "+required+" X-TEST1 'test' )";
         try
         {
             asd = parser.parse( value );
@@ -404,33 +404,33 @@
      * 
      * @throws ParseException
      */
-    public static void testObsolete( AbstractSchemaParser parser ) throws ParseException
+    public static void testObsolete( AbstractSchemaParser parser, String oid, String required ) throws ParseException
     {
         String value = null;
         AbstractSchemaDescription asd = null;
 
         // not obsolete
-        value = "( 1.1 )";
+        value = "( "+oid+" "+required+" )";
         asd = parser.parse( value );
         TestCase.assertFalse( asd.isObsolete() );
 
         // not obsolete
-        value = "( 1.1 NAME 'test' DESC 'Descripton' )";
+        value = "( "+oid+" "+required+" NAME 'test' DESC 'Descripton' )";
         asd = parser.parse( value );
         TestCase.assertFalse( asd.isObsolete() );
         
         // obsolete
-        value = "(1.1 NAME 'test' DESC 'Descripton' OBSOLETE)";
+        value = "("+oid+" "+required+" NAME 'test' DESC 'Descripton' OBSOLETE)";
         asd = parser.parse( value );
         TestCase.assertTrue( asd.isObsolete() );
 
         // obsolete 
-        value = "(1.1 OBSOLETE)";
+        value = "("+oid+" "+required+" OBSOLETE)";
         asd = parser.parse( value );
         TestCase.assertTrue( asd.isObsolete() );
 
         // ivalid
-        value = "(1.1 NAME 'test' DESC 'Descripton' OBSOLET )";
+        value = "("+oid+" "+required+" NAME 'test' DESC 'Descripton' OBSOLET )";
         try
         {
             asd = parser.parse( value );
@@ -442,7 +442,7 @@
         }
         
         // trailing value not allowed
-        value = "(1.1 NAME 'test' DESC 'Descripton' OBSOLETE 'true' )";
+        value = "("+oid+" "+required+" NAME 'test' DESC 'Descripton' OBSOLETE 'true' )";
         try
         {
             asd = parser.parse( value );
@@ -458,6 +458,30 @@
     
     
     /**
+     * Test extensions.
+     * 
+     * @throws ParseException
+     */
+    public static void testUnique( AbstractSchemaParser parser, String[] testValues ) throws ParseException
+    {
+        for ( int i = 0; i < testValues.length; i++ )
+        {
+            String testValue = testValues[i];
+            try
+            {
+                parser.parse( testValue );
+                TestCase.fail( "Exception expected, element appears twice in "+testValue );
+            }
+            catch ( ParseException pe )
+            {
+                // expected
+            }
+        }
+        
+    }
+    
+    
+    /**
      * Tests the multithreaded use of a single parser.
      */
     public static void testMultiThreaded( AbstractSchemaParser parser, String[] testValues ) throws ParseException
Index: shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/DITContentRuleDescriptionSyntaxCheckerTest.java
===================================================================
--- shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/DITContentRuleDescriptionSyntaxCheckerTest.java	(Revision 0)
+++ shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/DITContentRuleDescriptionSyntaxCheckerTest.java	(Revision 0)
@@ -0,0 +1,83 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+package org.apache.directory.shared.ldap.schema.syntax;
+
+
+import junit.framework.TestCase;
+
+
+/**
+ * Test cases for DITContentRuleDescriptionSyntaxChecker.
+ * 
+ * There are also many test cases in SchemaParserDITContentRuleDescriptionTest.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DITContentRuleDescriptionSyntaxCheckerTest extends TestCase
+{
+    private DITContentRuleDescriptionSyntaxChecker checker = new DITContentRuleDescriptionSyntaxChecker();
+
+    public void testValid()
+    {
+        assertTrue( checker.isValidSyntax( "( 2.5.6.4 )" ) );
+        assertTrue( checker.isValidSyntax( "( 2.5.6.4 NAME 'organization' )" ) );
+        assertTrue( checker.isValidSyntax( "( 2.5.6.4 NAME 'organization' DESC 'content rule for organization' )" ) );
+        assertTrue( checker.isValidSyntax( "( 2.5.6.4 NAME 'organization' DESC 'content rule for organization' OBSOLETE )" ) );
+        assertTrue( checker.isValidSyntax( "( 2.5.6.4 NAME 'organization' DESC 'content rule for organization' OBSOLETE AUX ( pilotOrganization $  2.5.6.5 ) )" ) );
+        assertTrue( checker.isValidSyntax( "( 2.5.6.4 NAME 'organization' DESC 'content rule for organization' OBSOLETE AUX ( pilotOrganization $  2.5.6.5 ) MUST ( objectClass $ o ) )" ) );
+        assertTrue( checker.isValidSyntax( "( 2.5.6.4 NAME 'organization' DESC 'content rule for organization' OBSOLETE AUX ( pilotOrganization $  2.5.6.5 ) MUST ( objectClass $ o ) MAY ( l $ st )  )" ) );
+        assertTrue( checker.isValidSyntax( "( 2.5.6.4 NAME 'organization' DESC 'content rule for organization' OBSOLETE AUX ( pilotOrganization $  2.5.6.5 ) MUST ( objectClass $ o ) MAY ( l $ st ) NOT ( 1.2.3.4.5.6.7.8.9.0 $ ou ) )" ) );
+        
+
+        assertTrue( checker.isValidSyntax( "(2.5.6.4)" ) );
+        assertTrue( checker.isValidSyntax( "(   2.5.6.4     NAME   'organization'   DESC   'content rule for organization' OBSOLETE AUX ( pilotOrganization $  2.5.6.5 ) MUST ( objectClass $ o )     MAY    (    l   $   st   ) NOT (1.2.3.4.5.6.7.8.9.0 $ ou))" ) );
+    }
+
+    public void testInvalid()
+    {
+        // null 
+        assertFalse( checker.isValidSyntax( null ) );
+        
+        // empty 
+        assertFalse( checker.isValidSyntax( "" ) );
+        
+        // missing/invalid OID
+        assertFalse( checker.isValidSyntax( "()" ) );
+        assertFalse( checker.isValidSyntax( "(  )" ) );
+        assertFalse( checker.isValidSyntax( "( . )" ) );
+        assertFalse( checker.isValidSyntax( "( 1 )" ) );
+        assertFalse( checker.isValidSyntax( "( 1. )" ) );
+        assertFalse( checker.isValidSyntax( "( 1.2. )" ) );
+        assertFalse( checker.isValidSyntax( "( 1.A )" ) );
+        assertFalse( checker.isValidSyntax( "( A.B )" ) );
+
+        // missing right parenthesis
+        assertFalse( checker.isValidSyntax( "( 2.5.6.4 NAME 'organization'" ) );
+
+        // missing quotes
+        assertFalse( checker.isValidSyntax( "( 2.5.6.4 NAME organization )" ) );
+
+        // lowercase NAME, DESC, AUX
+        assertFalse( checker.isValidSyntax( "( 2.5.6.4 name 'organization' desc 'content rule for organization' aux ( pilotOrganization $  2.5.6.5 ) )" ) );
+
+    }
+
+}
Index: shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserDITContentRuleDescriptionTest.java
===================================================================
--- shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserDITContentRuleDescriptionTest.java	(Revision 0)
+++ shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserDITContentRuleDescriptionTest.java	(Revision 0)
@@ -0,0 +1,490 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+package org.apache.directory.shared.ldap.schema.syntax;
+
+
+import java.text.ParseException;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.shared.ldap.schema.syntax.parser.DITContentRuleDescriptionSchemaParser;
+
+
+/**
+ * Tests the DITContentRuleDescriptionSchemaParser class.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class SchemaParserDITContentRuleDescriptionTest extends TestCase
+{
+    /** the parser instance */
+    private DITContentRuleDescriptionSchemaParser parser;
+
+
+    protected void setUp() throws Exception
+    {
+        parser = new DITContentRuleDescriptionSchemaParser();
+    }
+
+
+    protected void tearDown() throws Exception
+    {
+        parser = null;
+    }
+
+
+    /**
+     * Test numericoid
+     * 
+     * @throws ParseException
+     */
+    public void testNumericOid() throws ParseException
+    {
+        SchemaParserTestUtils.testNumericOid( parser, "" );
+    }
+
+
+    /**
+     * Tests NAME and its values
+     * 
+     * @throws ParseException
+     */
+    public void testNames() throws ParseException
+    {
+        SchemaParserTestUtils.testNames( parser, "1.1", "" );
+    }
+
+
+    /**
+     * Tests DESC
+     * 
+     * @throws ParseException
+     */
+    public void testDescription() throws ParseException
+    {
+        SchemaParserTestUtils.testDescription( parser, "1.1", "" );
+    }
+
+
+    /**
+     * Tests OBSOLETE
+     * 
+     * @throws ParseException
+     */
+    public void testObsolete() throws ParseException
+    {
+        SchemaParserTestUtils.testObsolete( parser, "1.1", "" );
+    }
+
+
+    /**
+     * Test AUX and its values.
+     * 
+     * @throws ParseException
+     */
+    public void testAux() throws ParseException
+    {
+        String value = null;
+        DITContentRuleDescription dcrd = null;
+
+        // no AUX
+        value = "( 1.1 )";
+        dcrd = parser.parseDITContentRuleDescription( value );
+        assertEquals( 0, dcrd.getAuxiliaryObjectClasses().size() );
+
+        // AUX simple numericoid
+        value = "( 1.1 AUX 1.2.3 )";
+        dcrd = parser.parseDITContentRuleDescription( value );
+        assertEquals( 1, dcrd.getAuxiliaryObjectClasses().size() );
+        assertEquals( "1.2.3", dcrd.getAuxiliaryObjectClasses().get( 0 ) );
+
+        // AUX simple descr
+        value = "( 1.1 AUX top )";
+        dcrd = parser.parseDITContentRuleDescription( value );
+        assertEquals( 1, dcrd.getAuxiliaryObjectClasses().size() );
+        assertEquals( "top", dcrd.getAuxiliaryObjectClasses().get( 0 ) );
+
+        // AUX single numericoid
+        value = "( 1.1 AUX ( 1.2.3.4.5 ) )";
+        dcrd = parser.parseDITContentRuleDescription( value );
+        assertEquals( 1, dcrd.getAuxiliaryObjectClasses().size() );
+        assertEquals( "1.2.3.4.5", dcrd.getAuxiliaryObjectClasses().get( 0 ) );
+
+        // AUX single descr
+        value = "( 1.1 AUX ( A-Z-0-9 ) )";
+        dcrd = parser.parseDITContentRuleDescription( value );
+        assertEquals( 1, dcrd.getAuxiliaryObjectClasses().size() );
+        assertEquals( "A-Z-0-9", dcrd.getAuxiliaryObjectClasses().get( 0 ) );
+
+        // AUX multi numericoid
+        value = "( 1.1 AUX ( 1.2.3 $ 1.2.3.4.5 ) )";
+        dcrd = parser.parseDITContentRuleDescription( value );
+        assertEquals( 2, dcrd.getAuxiliaryObjectClasses().size() );
+        assertEquals( "1.2.3", dcrd.getAuxiliaryObjectClasses().get( 0 ) );
+        assertEquals( "1.2.3.4.5", dcrd.getAuxiliaryObjectClasses().get( 1 ) );
+
+        // AUX multi descr
+        value = "( 1.1 AUX ( top1 $ top2 ) )";
+        dcrd = parser.parseDITContentRuleDescription( value );
+        assertEquals( 2, dcrd.getAuxiliaryObjectClasses().size() );
+        assertEquals( "top1", dcrd.getAuxiliaryObjectClasses().get( 0 ) );
+        assertEquals( "top2", dcrd.getAuxiliaryObjectClasses().get( 1 ) );
+
+        // AUX multi mixed
+        value = "( 1.1 AUX ( top1 $ 1.2.3.4 $ top2 ) )";
+        dcrd = parser.parseDITContentRuleDescription( value );
+        assertEquals( 3, dcrd.getAuxiliaryObjectClasses().size() );
+        assertEquals( "top1", dcrd.getAuxiliaryObjectClasses().get( 0 ) );
+        assertEquals( "1.2.3.4", dcrd.getAuxiliaryObjectClasses().get( 1 ) );
+        assertEquals( "top2", dcrd.getAuxiliaryObjectClasses().get( 2 ) );
+
+        // AUX multi mixed no space
+        value = "( 1.1 AUX (TOP-1$1.2.3.4$TOP-2) )";
+        dcrd = parser.parseDITContentRuleDescription( value );
+        assertEquals( 3, dcrd.getAuxiliaryObjectClasses().size() );
+        assertEquals( "TOP-1", dcrd.getAuxiliaryObjectClasses().get( 0 ) );
+        assertEquals( "1.2.3.4", dcrd.getAuxiliaryObjectClasses().get( 1 ) );
+        assertEquals( "TOP-2", dcrd.getAuxiliaryObjectClasses().get( 2 ) );
+
+        // AUX multi mixed many spaces
+        value = "(          1.1          AUX          (          top1          $          1.2.3.4$top2          )          )";
+        dcrd = parser.parseDITContentRuleDescription( value );
+        assertEquals( 3, dcrd.getAuxiliaryObjectClasses().size() );
+        assertEquals( "top1", dcrd.getAuxiliaryObjectClasses().get( 0 ) );
+        assertEquals( "1.2.3.4", dcrd.getAuxiliaryObjectClasses().get( 1 ) );
+        assertEquals( "top2", dcrd.getAuxiliaryObjectClasses().get( 2 ) );
+
+        // no quote allowed
+        value = "( 1.1 AUX 'top' )";
+        try
+        {
+            dcrd = parser.parseDITContentRuleDescription( value );
+            fail( "Exception expected, invalid AUX 'top' (quoted)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // no quote allowed
+        value = "( 1.1 AUX '1.2.3.4' )";
+        try
+        {
+            dcrd = parser.parseDITContentRuleDescription( value );
+            fail( "Exception expected, invalid AUX '1.2.3.4' (quoted)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // invalid character
+        value = "( 1.1 AUX 1.2.3.4.A )";
+        try
+        {
+            dcrd = parser.parseDITContentRuleDescription( value );
+            fail( "Exception expected, invalid AUX '1.2.3.4.A' (invalid character)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // invalid start
+        value = "( 1.1 AUX ( top1 $ -top2 ) )";
+        try
+        {
+            dcrd = parser.parseDITContentRuleDescription( value );
+            fail( "Exception expected, invalid AUX '-top' (starts with hypen)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // invalid separator
+        value = "( 1.1 AUX ( top1 top2 ) )";
+        try
+        {
+            dcrd = parser.parseDITContentRuleDescription( value );
+            fail( "Exception expected, invalid separator (no DOLLAR)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // empty AUX
+        value = "( 1.1 AUX )";
+        try
+        {
+            dcrd = parser.parseDITContentRuleDescription( value );
+            fail( "Exception expected, no AUX value" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+    }
+
+
+    /**
+     * Test MUST and its values.
+     * Very similar to AUX, so here are less test cases. 
+     * 
+     * @throws ParseException
+     */
+    public void testMust() throws ParseException
+    {
+        String value = null;
+        DITContentRuleDescription dcrd = null;
+        
+        // no MUST
+        value = "( 1.1 )";
+        dcrd = parser.parseDITContentRuleDescription( value );
+        assertEquals( 0, dcrd.getMustAttributeTypes().size() );
+
+        // MUST simple numericoid
+        value = "( 1.1 MUST 1.2.3 )";
+        dcrd = parser.parseDITContentRuleDescription( value );
+        assertEquals( 1, dcrd.getMustAttributeTypes().size() );
+        assertEquals( "1.2.3", dcrd.getMustAttributeTypes().get( 0 ) );
+
+        // MUST mulitple
+        value = "(1.1 MUST (cn$sn       $11.22.33.44.55         $  objectClass   ))";
+        dcrd = parser.parseDITContentRuleDescription( value );
+        assertEquals( 4, dcrd.getMustAttributeTypes().size() );
+        assertEquals( "cn", dcrd.getMustAttributeTypes().get( 0 ) );
+        assertEquals( "sn", dcrd.getMustAttributeTypes().get( 1 ) );
+        assertEquals( "11.22.33.44.55", dcrd.getMustAttributeTypes().get( 2 ) );
+        assertEquals( "objectClass", dcrd.getMustAttributeTypes().get( 3 ) );
+
+        // invalid value
+        value = "( 1.1 MUST ( c_n ) )";
+        try
+        {
+            dcrd = parser.parseDITContentRuleDescription( value );
+            fail( "Exception expected, invalid value c_n" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // no MUST values
+        value = "( 1.1 MUST )";
+        try
+        {
+            dcrd = parser.parseDITContentRuleDescription( value );
+            fail( "Exception expected, no MUST value" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+    }
+
+
+    /**
+     * Test MAY and its values.
+     * Very similar to AUX, so here are less test cases. 
+     * 
+     * @throws ParseException
+     */
+    public void testMay() throws ParseException
+    {
+        String value = null;
+        DITContentRuleDescription dcrd = null;
+
+        // no MAY
+        value = "( 1.1 )";
+        dcrd = parser.parseDITContentRuleDescription( value );
+        assertEquals( 0, dcrd.getMayAttributeTypes().size() );
+
+        // MAY simple numericoid
+        value = "( 1.1 MAY 1.2.3 )";
+        dcrd = parser.parseDITContentRuleDescription( value );
+        assertEquals( 1, dcrd.getMayAttributeTypes().size() );
+        assertEquals( "1.2.3", dcrd.getMayAttributeTypes().get( 0 ) );
+
+        // MAY mulitple
+        value = "(1.1 MAY (cn$sn       $11.22.33.44.55         $  objectClass   ))";
+        dcrd = parser.parseDITContentRuleDescription( value );
+        assertEquals( 4, dcrd.getMayAttributeTypes().size() );
+        assertEquals( "cn", dcrd.getMayAttributeTypes().get( 0 ) );
+        assertEquals( "sn", dcrd.getMayAttributeTypes().get( 1 ) );
+        assertEquals( "11.22.33.44.55", dcrd.getMayAttributeTypes().get( 2 ) );
+        assertEquals( "objectClass", dcrd.getMayAttributeTypes().get( 3 ) );
+
+        // invalid value
+        value = "( 1.1 MAY ( c_n ) )";
+        try
+        {
+            dcrd = parser.parseDITContentRuleDescription( value );
+            fail( "Exception expected, invalid value c_n" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+    }
+
+    /**
+     * Test NOT and its values.
+     * Very similar to AUX, so here are less test cases. 
+     * 
+     * @throws ParseException
+     */
+    public void testNot() throws ParseException
+    {
+        String value = null;
+        DITContentRuleDescription dcrd = null;
+
+        // no MAY
+        value = "( 1.1 )";
+        dcrd = parser.parseDITContentRuleDescription( value );
+        assertEquals( 0, dcrd.getNotAttributeTypes().size() );
+
+        // MAY simple numericoid
+        value = "( 1.1 NOT 1.2.3 )";
+        dcrd = parser.parseDITContentRuleDescription( value );
+        assertEquals( 1, dcrd.getNotAttributeTypes().size() );
+        assertEquals( "1.2.3", dcrd.getNotAttributeTypes().get( 0 ) );
+
+        // MAY mulitple
+        value = "(1.1 NOT (cn$sn       $11.22.33.44.55         $  objectClass   ))";
+        dcrd = parser.parseDITContentRuleDescription( value );
+        assertEquals( 4, dcrd.getNotAttributeTypes().size() );
+        assertEquals( "cn", dcrd.getNotAttributeTypes().get( 0 ) );
+        assertEquals( "sn", dcrd.getNotAttributeTypes().get( 1 ) );
+        assertEquals( "11.22.33.44.55", dcrd.getNotAttributeTypes().get( 2 ) );
+        assertEquals( "objectClass", dcrd.getNotAttributeTypes().get( 3 ) );
+
+        // invalid value
+        value = "( 1.1 NOT ( c_n ) )";
+        try
+        {
+            dcrd = parser.parseDITContentRuleDescription( value );
+            fail( "Exception expected, invalid value c_n" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+    }
+    
+
+    /**
+     * Test extensions.
+     * 
+     * @throws ParseException
+     */
+    public void testExtensions() throws ParseException
+    {
+        SchemaParserTestUtils.testExtensions( parser, "1.1", "" );
+
+    }
+
+
+    /**
+     * Test full object class description.
+     * 
+     * @throws ParseException
+     */
+    public void testFull() throws ParseException
+    {
+        String value = null;
+        DITContentRuleDescription dcrd = null;
+
+        value = "( 1.2.3.4.5.6.7.8.9.0 NAME ( 'abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789' 'test' ) DESC 'Descripton äöüß 部長' OBSOLETE AUX ( 2.3.4.5.6.7.8.9.0.1 $ abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 ) MUST ( 3.4.5.6.7.8.9.0.1.2 $ abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 ) MAY ( 4.5.6.7.8.9.0.1.2.3 $ abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 ) NOT ( 5.6.7.8.9.0.1.2.3.4 $ abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 ) X-TEST-a ('test1-1' 'test1-2') X-TEST-b ('test2-1' 'test2-2') )";
+        dcrd = parser.parseDITContentRuleDescription( value );
+
+        assertEquals( "1.2.3.4.5.6.7.8.9.0", dcrd.getNumericOid() );
+        assertEquals( 2, dcrd.getNames().size() );
+        assertEquals( "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789", dcrd.getNames().get( 0 ) );
+        assertEquals( "test", dcrd.getNames().get( 1 ) );
+        assertEquals( "Descripton äöüß 部長", dcrd.getDescription() );
+        assertTrue( dcrd.isObsolete() );
+        assertEquals( 2, dcrd.getAuxiliaryObjectClasses().size() );
+        assertEquals( "2.3.4.5.6.7.8.9.0.1", dcrd.getAuxiliaryObjectClasses().get( 0 ) );
+        assertEquals( "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789", dcrd
+            .getAuxiliaryObjectClasses().get( 1 ) );
+        assertEquals( 2, dcrd.getMustAttributeTypes().size() );
+        assertEquals( "3.4.5.6.7.8.9.0.1.2", dcrd.getMustAttributeTypes().get( 0 ) );
+        assertEquals( "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789", dcrd.getMustAttributeTypes()
+            .get( 1 ) );
+        assertEquals( 2, dcrd.getMayAttributeTypes().size() );
+        assertEquals( "4.5.6.7.8.9.0.1.2.3", dcrd.getMayAttributeTypes().get( 0 ) );
+        assertEquals( "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789", dcrd.getMayAttributeTypes()
+            .get( 1 ) );
+        assertEquals( 2, dcrd.getNotAttributeTypes().size() );
+        assertEquals( "5.6.7.8.9.0.1.2.3.4", dcrd.getNotAttributeTypes().get( 0 ) );
+        assertEquals( "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789", dcrd.getNotAttributeTypes()
+            .get( 1 ) );
+        assertEquals( 2, dcrd.getExtensions().size() );
+        assertNotNull( dcrd.getExtensions().get( "X-TEST-a" ) );
+        assertEquals( 2, dcrd.getExtensions().get( "X-TEST-a" ).size() );
+        assertEquals( "test1-1", dcrd.getExtensions().get( "X-TEST-a" ).get( 0 ) );
+        assertEquals( "test1-2", dcrd.getExtensions().get( "X-TEST-a" ).get( 1 ) );
+        assertNotNull( dcrd.getExtensions().get( "X-TEST-b" ) );
+        assertEquals( 2, dcrd.getExtensions().get( "X-TEST-b" ).size() );
+        assertEquals( "test2-1", dcrd.getExtensions().get( "X-TEST-b" ).get( 0 ) );
+        assertEquals( "test2-2", dcrd.getExtensions().get( "X-TEST-b" ).get( 1 ) );
+    }
+
+    
+    /**
+     * Test unique elements.
+     * 
+     * @throws ParseException
+     */
+    public void testUniqueElements() throws ParseException
+    {
+        String[] testValues = new String[]
+            { 
+                "( 1.1 NAME 'test1' NAME 'test2' )", 
+                "( 1.1 DESC 'test1' DESC 'test2' )",
+                "( 1.1 OBSOLETE OBSOLETE )", 
+                "( 1.1 AUX test1 AUX test2 )",
+                "( 1.1 MUST test1 MUST test2 )",
+                "( 1.1 MAY test1 MAY test2 )",
+                "( 1.1 NOT test1 NOT test2 )",
+                "( 1.1 X-TEST 'test1' X-TEST 'test2' )" 
+            };
+        SchemaParserTestUtils.testUnique( parser, testValues );
+    }
+    
+    
+    /**
+     * Tests the multithreaded use of a single parser.
+     */
+    public void testMultiThreaded() throws Exception
+    {
+        String[] testValues = new String[]
+            {
+                "( 1.1 )",
+                "( 2.5.6.4 DESC 'content rule for organization' NOT ( x121Address $ telexNumber ) )",
+                "( 2.5.6.4 DESC 'content rule for organization' NOT ( x121Address $ telexNumber ) )",
+                "( 1.2.3.4.5.6.7.8.9.0 NAME ( 'abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789' 'test' ) DESC 'Descripton äöüß 部長' OBSOLETE AUX ( 2.3.4.5.6.7.8.9.0.1 $ abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 ) MUST ( 3.4.5.6.7.8.9.0.1.2 $ abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 ) MAY ( 4.5.6.7.8.9.0.1.2.3 $ abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 ) NOT ( 5.6.7.8.9.0.1.2.3.4 $ abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 ) X-TEST-a ('test1-1' 'test1-2') X-TEST-b ('test2-1' 'test2-2') )" };
+        SchemaParserTestUtils.testMultiThreaded( parser, testValues );
+
+    }
+
+}
Index: shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserNameFormDescriptionTest.java
===================================================================
--- shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserNameFormDescriptionTest.java	(Revision 0)
+++ shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserNameFormDescriptionTest.java	(Revision 0)
@@ -0,0 +1,512 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+package org.apache.directory.shared.ldap.schema.syntax;
+
+
+import java.text.ParseException;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.shared.ldap.schema.syntax.parser.NameFormDescriptionSchemaParser;
+
+
+/**
+ * Tests the NameFormDescriptionSchemaParser class.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class SchemaParserNameFormDescriptionTest extends TestCase
+{
+    /** the parser instance */
+    private NameFormDescriptionSchemaParser parser;
+
+
+    protected void setUp() throws Exception
+    {
+        parser = new NameFormDescriptionSchemaParser();
+    }
+
+
+    protected void tearDown() throws Exception
+    {
+        parser = null;
+    }
+
+
+    /**
+     * Test numericoid
+     * 
+     * @throws ParseException
+     */
+    public void testNumericOid() throws ParseException
+    {
+        SchemaParserTestUtils.testNumericOid( parser, "OC o MUST m" );
+    }
+
+
+    /**
+     * Tests NAME and its values
+     * 
+     * @throws ParseException
+     */
+    public void testNames() throws ParseException
+    {
+        SchemaParserTestUtils.testNames( parser, "1.1", "OC o MUST m" );
+    }
+
+
+    /**
+     * Tests DESC
+     * 
+     * @throws ParseException
+     */
+    public void testDescription() throws ParseException
+    {
+        SchemaParserTestUtils.testDescription( parser, "1.1", "OC o MUST m" );
+    }
+
+
+    /**
+     * Tests OBSOLETE
+     * 
+     * @throws ParseException
+     */
+    public void testObsolete() throws ParseException
+    {
+        SchemaParserTestUtils.testObsolete( parser, "1.1", "OC o MUST m" );
+    }
+
+
+    /**
+     * Test OC and its value.
+     * 
+     * @throws ParseException
+     */
+    public void testOc() throws ParseException
+    {
+        String value = null;
+        NameFormDescription nfd = null;
+
+        // numeric oid
+        value = "( 1.1 MUST m OC 1.2.3.4.5.6.7.8.9.0 )";
+        nfd = parser.parseNameFormDescription( value );
+        assertEquals( "1.2.3.4.5.6.7.8.9.0", nfd.getStructuralObjectClass() );
+
+        // numeric oid
+        value = "(   1.1 MUST m   OC    123.4567.890    )";
+        nfd = parser.parseNameFormDescription( value );
+        assertEquals( "123.4567.890", nfd.getStructuralObjectClass() );
+
+        // descr
+        value = "( 1.1 MUST m OC abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789 )";
+        nfd = parser.parseNameFormDescription( value );
+        assertEquals( "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789", nfd
+            .getStructuralObjectClass() );
+
+        // no quote allowed
+        value = "( 1.1 MUST m OC '1.2.3.4.5.6.7.8.9.0' )";
+        try
+        {
+            nfd = parser.parseNameFormDescription( value );
+            fail( "Exception expected, invalid FORM '1.2.3.4.5.6.7.8.9.0' (quoted)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // no quote allowed
+        value = "( 1.1 MUST m OC 'test' )";
+        try
+        {
+            nfd = parser.parseNameFormDescription( value );
+            fail( "Exception expected, invalid OC 'test' (quoted)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // invalid character
+        value = "( 1.1 MUST m OC 1.2.3.4.A )";
+        try
+        {
+            nfd = parser.parseNameFormDescription( value );
+            fail( "Exception expected, invalid OC 1.2.3.4.A (invalid character)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // invalid start
+        value = "( 1.1 MUST m OC -test ) )";
+        try
+        {
+            nfd = parser.parseNameFormDescription( value );
+            fail( "Exception expected, invalid OC '-test' (starts with hypen)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // no multi value allowed
+        value = "( 1.1 MUST m OC ( test1 test2 ) )";
+        try
+        {
+            nfd = parser.parseNameFormDescription( value );
+            fail( "Exception expected, OC must be single valued" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // OC is required
+        value = "( 1.1 MUST m )";
+        try
+        {
+            nfd = parser.parseNameFormDescription( value );
+            fail( "Exception expected, OC is required" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // OC must only appear once
+        value = "( 1.1 MUST m OC test1 OC test2 )";
+        try
+        {
+            nfd = parser.parseNameFormDescription( value );
+            fail( "Exception expected, OC appears twice" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+    }
+
+
+    /**
+     * Test MUST and its values.
+     * 
+     * @throws ParseException
+     */
+    public void testMust() throws ParseException
+    {
+        String value = null;
+        NameFormDescription nfd = null;
+
+        // MUST simple numericoid
+        value = "( 1.1 OC o MUST 1.2.3 )";
+        nfd = parser.parseNameFormDescription( value );
+        assertEquals( 1, nfd.getMustAttributeTypes().size() );
+        assertEquals( "1.2.3", nfd.getMustAttributeTypes().get( 0 ) );
+
+        // MUST mulitple
+        value = "(1.1 OC o MUST (cn$sn       $11.22.33.44.55         $  objectClass   ))";
+        nfd = parser.parseNameFormDescription( value );
+        assertEquals( 4, nfd.getMustAttributeTypes().size() );
+        assertEquals( "cn", nfd.getMustAttributeTypes().get( 0 ) );
+        assertEquals( "sn", nfd.getMustAttributeTypes().get( 1 ) );
+        assertEquals( "11.22.33.44.55", nfd.getMustAttributeTypes().get( 2 ) );
+        assertEquals( "objectClass", nfd.getMustAttributeTypes().get( 3 ) );
+
+        // invalid value
+        value = "( 1.1 OC o MUST ( c_n ) )";
+        try
+        {
+            nfd = parser.parseNameFormDescription( value );
+            fail( "Exception expected, invalid value c_n" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // no MUST values
+        value = "( 1.1 OC o MUST )";
+        try
+        {
+            nfd = parser.parseNameFormDescription( value );
+            fail( "Exception expected, no MUST value" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // MUST is required
+        value = "( 1.1 OC o )";
+        try
+        {
+            nfd = parser.parseNameFormDescription( value );
+            fail( "Exception expected, MUST is required" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // MUST must only appear once
+        value = "( 1.1 OC o MUST test1 MUST test2 )";
+        try
+        {
+            nfd = parser.parseNameFormDescription( value );
+            fail( "Exception expected, MUST appears twice" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+    }
+
+
+    /**
+     * Test MAY and its values.
+     * 
+     * @throws ParseException
+     */
+    public void testMay() throws ParseException
+    {
+        String value = null;
+        NameFormDescription nfd = null;
+
+        // no MAY
+        value = "( 1.1 OC o MUST m )";
+        nfd = parser.parseNameFormDescription( value );
+        assertEquals( 0, nfd.getMayAttributeTypes().size() );
+
+        // MAY simple numericoid
+        value = "( 1.1 OC o MUST m MAY 1.2.3 )";
+        nfd = parser.parseNameFormDescription( value );
+        assertEquals( 1, nfd.getMayAttributeTypes().size() );
+        assertEquals( "1.2.3", nfd.getMayAttributeTypes().get( 0 ) );
+
+        // MAY mulitple
+        value = "(1.1 OC o MUST m MAY (cn$sn       $11.22.33.44.55         $  objectClass   ))";
+        nfd = parser.parseNameFormDescription( value );
+        assertEquals( 4, nfd.getMayAttributeTypes().size() );
+        assertEquals( "cn", nfd.getMayAttributeTypes().get( 0 ) );
+        assertEquals( "sn", nfd.getMayAttributeTypes().get( 1 ) );
+        assertEquals( "11.22.33.44.55", nfd.getMayAttributeTypes().get( 2 ) );
+        assertEquals( "objectClass", nfd.getMayAttributeTypes().get( 3 ) );
+
+        // invalid value
+        value = "( 1.1 OC o MUST m MAY ( c_n ) )";
+        try
+        {
+            nfd = parser.parseNameFormDescription( value );
+            fail( "Exception expected, invalid value c_n" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // MAY must only appear once
+        value = "( 1.1 OC o MUST m MAY test1 MAY test2 )";
+        try
+        {
+            nfd = parser.parseNameFormDescription( value );
+            fail( "Exception expected, MAY appears twice" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+    }
+
+
+    /**
+     * Test extensions.
+     * 
+     * @throws ParseException
+     */
+    public void testExtensions() throws ParseException
+    {
+        SchemaParserTestUtils.testExtensions( parser, "1.1", "OC o MUST m" );
+
+    }
+
+
+    /**
+     * Test full object class description.
+     * 
+     * @throws ParseException
+     */
+    public void testFull() throws ParseException
+    {
+        String value = null;
+        NameFormDescription nfd = null;
+
+        value = "( 1.2.3.4.5.6.7.8.9.0 NAME ( 'abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789' 'test' ) DESC 'Descripton äöüß 部長' OBSOLETE OC bcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789a MUST ( 3.4.5.6.7.8.9.0.1.2 $ cdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789ab ) MAY ( 4.5.6.7.8.9.0.1.2.3 $ defghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789abc ) X-TEST-a ('test1-1' 'test1-2') X-TEST-b ('test2-1' 'test2-2') )";
+        nfd = parser.parseNameFormDescription( value );
+
+        assertEquals( "1.2.3.4.5.6.7.8.9.0", nfd.getNumericOid() );
+        assertEquals( 2, nfd.getNames().size() );
+        assertEquals( "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789", nfd.getNames().get( 0 ) );
+        assertEquals( "test", nfd.getNames().get( 1 ) );
+        assertEquals( "Descripton äöüß 部長", nfd.getDescription() );
+        assertTrue( nfd.isObsolete() );
+        assertEquals( "bcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789a", nfd
+            .getStructuralObjectClass() );
+        assertEquals( 2, nfd.getMustAttributeTypes().size() );
+        assertEquals( "3.4.5.6.7.8.9.0.1.2", nfd.getMustAttributeTypes().get( 0 ) );
+        assertEquals( "cdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789ab", nfd.getMustAttributeTypes()
+            .get( 1 ) );
+        assertEquals( 2, nfd.getMayAttributeTypes().size() );
+        assertEquals( "4.5.6.7.8.9.0.1.2.3", nfd.getMayAttributeTypes().get( 0 ) );
+        assertEquals( "defghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789abc", nfd.getMayAttributeTypes()
+            .get( 1 ) );
+        assertEquals( 2, nfd.getExtensions().size() );
+        assertNotNull( nfd.getExtensions().get( "X-TEST-a" ) );
+        assertEquals( 2, nfd.getExtensions().get( "X-TEST-a" ).size() );
+        assertEquals( "test1-1", nfd.getExtensions().get( "X-TEST-a" ).get( 0 ) );
+        assertEquals( "test1-2", nfd.getExtensions().get( "X-TEST-a" ).get( 1 ) );
+        assertNotNull( nfd.getExtensions().get( "X-TEST-b" ) );
+        assertEquals( 2, nfd.getExtensions().get( "X-TEST-b" ).size() );
+        assertEquals( "test2-1", nfd.getExtensions().get( "X-TEST-b" ).get( 0 ) );
+        assertEquals( "test2-2", nfd.getExtensions().get( "X-TEST-b" ).get( 1 ) );
+    }
+
+
+    /**
+     * Test unique elements.
+     * 
+     * @throws ParseException
+     */
+    public void testUniqueElements() throws ParseException
+    {
+        String[] testValues = new String[]
+            { 
+                "( 1.1 OC o MUST m NAME 'test1' NAME 'test2' )", 
+                "( 1.1 OC o MUST m DESC 'test1' DESC 'test2' )",
+                "( 1.1 OC o MUST m OBSOLETE OBSOLETE )", 
+                "( 1.1 OC o MUST m OC test1 OC test2 )",
+                "( 1.1 OC o MUST m MUST test1 MUST test2 )",
+                "( 1.1 OC o MUST m MAY test1 MAY test2 )",
+                "( 1.1 OC o MUST m X-TEST 'test1' X-TEST 'test2' )" 
+            };
+        SchemaParserTestUtils.testUnique( parser, testValues );
+    }
+
+
+    /**
+     * Test required elements.
+     * 
+     * @throws ParseException
+     */
+    public void testRequiredElements() throws ParseException
+    {
+        String value = null;
+        NameFormDescription nfd = null;
+
+        value = "( 1.2.3.4.5.6.7.8.9.0 OC o MUST m )";
+        nfd = parser.parseNameFormDescription( value );
+        assertNotNull( nfd.getStructuralObjectClass() );
+        assertEquals( 1, nfd.getMustAttributeTypes().size() );
+
+        value = "( 1.2.3.4.5.6.7.8.9.0 MUST m )";
+        try
+        {
+            nfd = parser.parseNameFormDescription( value );
+            fail( "Exception expected, OC is required" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        value = "( 1.2.3.4.5.6.7.8.9.0 OC o )";
+        try
+        {
+            nfd = parser.parseNameFormDescription( value );
+            fail( "Exception expected, MUST is required" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+    }
+
+
+//    /**
+//     * Test if MUST and MAY are disjoint.
+//     * 
+//     * Problem: What if MUST is a numeric oid and MAY is a name?
+//     * 
+//     * @throws ParseException
+//     */
+//    public void testDisjoint() throws ParseException
+//    {
+//        String value = null;
+//        NameFormDescription nfd = null;
+//
+//        value = "( 1.2.3.4.5.6.7.8.9.0 OC o MUST test1 MAY test2 )";
+//        nfd = parser.parseNameFormDescription( value );
+//        assertNotNull( nfd.getStructuralObjectClass() );
+//        assertEquals( 1, nfd.getMustAttributeTypes().size() );
+//
+//        value = "( 1.2.3.4.5.6.7.8.9.0 OC o MUST test1 MAY test1 )";
+//        try
+//        {
+//            nfd = parser.parseNameFormDescription( value );
+//            fail( "Exception expected, MUST and MAY must be disjoint" );
+//        }
+//        catch ( ParseException pe )
+//        {
+//            // expected
+//        }
+//
+//        value = "( 1.2.3.4.5.6.7.8.9.0 OC o MUST ( test1 $ test2 ) MAY ( test4 $ test3 $ test2 ) )";
+//        try
+//        {
+//            nfd = parser.parseNameFormDescription( value );
+//            fail( "Exception expected, MUST and MAY must be disjoint" );
+//        }
+//        catch ( ParseException pe )
+//        {
+//            // expected
+//        }
+//
+//    }
+
+
+    /**
+     * Tests the multithreaded use of a single parser.
+     */
+    public void testMultiThreaded() throws Exception
+    {
+        String[] testValues = new String[]
+            {
+                "( 1.1 OC o MUST m )",
+                "( 2.5.15.3 NAME 'orgNameForm' OC organization MUST o )",
+                "( 2.5.15.3 NAME 'orgNameForm' OC organization MUST o )",
+                "( 1.2.3.4.5.6.7.8.9.0 NAME ( 'abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789' 'test' ) DESC 'Descripton äöüß 部長' OBSOLETE OC bcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789a MUST ( 3.4.5.6.7.8.9.0.1.2 $ cdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789ab ) MAY ( 4.5.6.7.8.9.0.1.2.3 $ defghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789abc ) X-TEST-a ('test1-1' 'test1-2') X-TEST-b ('test2-1' 'test2-2') )" };
+        SchemaParserTestUtils.testMultiThreaded( parser, testValues );
+
+    }
+
+}
Index: shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserObjectClassDescriptionTest.java
===================================================================
--- shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserObjectClassDescriptionTest.java	(Revision 487885)
+++ shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserObjectClassDescriptionTest.java	(Arbeitskopie)
@@ -58,7 +58,7 @@
      */
     public void testNumericOid() throws ParseException
     {
-        SchemaParserTestUtils.testNumericOid( parser );
+        SchemaParserTestUtils.testNumericOid( parser, "" );
     }
 
 
@@ -69,7 +69,7 @@
      */
     public void testNames() throws ParseException
     {
-        SchemaParserTestUtils.testNames( parser );
+        SchemaParserTestUtils.testNames( parser, "1.1", "" );
     }
 
 
@@ -80,7 +80,7 @@
      */
     public void testDescription() throws ParseException
     {
-        SchemaParserTestUtils.testDescription( parser );
+        SchemaParserTestUtils.testDescription( parser, "1.1", "" );
     }
 
 
@@ -91,7 +91,7 @@
      */
     public void testObsolete() throws ParseException
     {
-        SchemaParserTestUtils.testObsolete( parser );
+        SchemaParserTestUtils.testObsolete( parser, "1.1", "" );
     }
 
 
@@ -399,7 +399,7 @@
      */
     public void testExtensions() throws ParseException
     {
-        SchemaParserTestUtils.testExtensions( parser );
+        SchemaParserTestUtils.testExtensions( parser, "1.1", "" );
 
     }
 
@@ -449,6 +449,31 @@
 
 
     /**
+     * Test unique elements.
+     * 
+     * @throws ParseException
+     */
+    public void testUniqueElements() throws ParseException
+    {
+        String[] testValues = new String[]
+            { 
+                "( 1.1 NAME 'test1' NAME 'test2' )", 
+                "( 1.1 DESC 'test1' DESC 'test2' )",
+                "( 1.1 OBSOLETE OBSOLETE )", 
+                "( 1.1 SUP test1 SUP test2 )",
+                "( 1.1 STRUCTURAL STRUCTURAL )",
+                "( 1.1 ABSTRACT ABSTRACT )",
+                "( 1.1 AUXILIARY AUXILIARY )",
+                "( 1.1 STRUCTURAL AUXILIARY AUXILIARY )",
+                "( 1.1 MUST test1 MUST test2 )",
+                "( 1.1 MAY test1 MAY test2 )",
+                "( 1.1 X-TEST 'test1' X-TEST 'test2' )" 
+            };
+        SchemaParserTestUtils.testUnique( parser, testValues );
+    }
+    
+    
+    /**
      * Ensure that element order is ignored
      * 
      * @throws ParseException
Index: shared/ldap/src/main/antlr/schema-value.g
===================================================================
--- shared/ldap/src/main/antlr/schema-value.g	(Revision 487871)
+++ shared/ldap/src/main/antlr/schema-value.g	(Arbeitskopie)
@@ -49,15 +49,20 @@
 protected LDIGIT : '1'..'9' ;
 protected DIGIT : '0'..'9' ; 
 protected NUMBER : DIGIT | ( LDIGIT (DIGIT)+ ) ;
+protected NUMERICOID : NUMBER ( '.' NUMBER )+ ;
 
 QUOTE : '\'' ;
 DOLLAR : '$' ;
 LCURLY : '{' ;
 RCURLY : '}' ;
-NUMERICOID : NUMBER ( '.' NUMBER )+ ;
 DESCR : ( 'a'..'z' | 'A'..'Z' ) ( 'a'..'z' | 'A'..'Z' | '0'..'9' | '-' )* ;
 LEN : LCURLY n:NUMBER RCURLY { setText(n.getText()); } ;
 
+NUMBER_OR_NUMERICOID :
+    ( NUMBER '.' ) => NUMERICOID { $setType( NUMERICOID ); }
+    |
+    ( NUMBER ) { $setType( NUMBER ); }
+    ;
 
 /**
  * An antlr generated schema parser. This is a sub-parser used to parse
@@ -194,3 +199,45 @@
     )
     ;
     
+    /**
+     * ruleid = number
+     * number  = DIGIT / ( LDIGIT 1*DIGIT )
+     *
+     */
+ruleid returns [Integer ruleid=null]
+    : 
+    (
+        (SP)? 
+        n:NUMBER { ruleid = Integer.parseInt(n.getText()); }
+    )
+    ;
+
+
+    /**
+     * ruleids = ruleid / ( LPAREN WSP ruleidlist WSP RPAREN )
+     * ruleidlist = ruleid *( SP ruleid )
+     */
+ruleids returns [List<Integer> ruleids]
+    {
+        ruleids = new ArrayList<Integer>();
+        Integer ruleid = null;
+    }
+    :
+    (
+        ( 
+        	ruleid=ruleid { ruleids.add(ruleid); } 
+    	)
+    |
+        ( 
+        	LPAR 
+        	ruleid=ruleid { ruleids.add(ruleid); } 
+        	( 
+        		SP
+        		ruleid=ruleid { ruleids.add(ruleid); } 
+        	)* 
+        	(SP)?
+        	RPAR 
+        )
+    )
+    ;
+    
\ Kein Zeilenvorschub am Ende der Datei
Index: shared/ldap/src/main/antlr/schema.g
===================================================================
--- shared/ldap/src/main/antlr/schema.g	(Revision 487871)
+++ shared/ldap/src/main/antlr/schema.g	(Arbeitskopie)
@@ -69,6 +69,10 @@
 SUP : ( "SUP" WHSP sup:VALUES ) { setText(sup.getText().trim()); } ;
 MUST : ( "MUST" WHSP must:VALUES ) { setText(must.getText().trim()); } ;
 MAY : ( "MAY" WHSP may:VALUES ) { setText(may.getText()); } ;
+AUX : ( "AUX" WHSP aux:VALUES ) { setText(aux.getText()); } ;
+NOT : ( "NOT" WHSP not:VALUES ) { setText(not.getText()); } ;
+FORM : ( "FORM" WHSP form:VALUES ) { setText(form.getText()); } ;
+OC : ( "OC" WHSP oc:VALUES ) { setText(oc.getText()); } ;
 EQUALITY : ( "EQUALITY" WHSP equality:VALUES ) { setText(equality.getText().trim()); } ;
 ORDERING : ( "ORDERING" WHSP ordering:VALUES ) { setText(ordering.getText().trim()); } ;
 SUBSTR : ( "SUBSTR" WHSP substr:VALUES ) { setText(substr.getText().trim()); } ;
@@ -116,6 +120,23 @@
 	    String noid = "";
 	    int len = 0;
 	}
+    static class ElementTracker
+	{
+	    Map<String, Integer> elementMap = new HashMap<String, Integer>();
+	    void track(String element, Token token) throws SemanticException 
+	    {
+	        if(elementMap.containsKey(element))
+	        {
+	            throw new SemanticException( element + " appears twice.", token.getFilename(), token.getLine() , token.getColumn() );
+	        }
+	        elementMap.put(element, new Integer(1));
+	    }
+	    boolean contains(String element) 
+	    {
+	        return elementMap.containsKey(element);
+	    }
+	}
+
 }
 
 
@@ -142,30 +163,34 @@
      * </pre>
     */
 objectClassDescription returns [ObjectClassDescription ocd = new ObjectClassDescription()]
+    {
+        ElementTracker et = new ElementTracker();
+    }
     :
     ( oid:STARTNUMERICOID { ocd.setNumericOid(numericoid(oid.getText())); } )
     (
-	    ( name:NAME { ocd.setNames(qdescrs(name.getText())); } )
+	    ( name:NAME { et.track("NAME", name); ocd.setNames(qdescrs(name.getText())); } )
 	    |
-	    ( desc:DESC { ocd.setDescription(qdstring(desc.getText())); } )
+	    ( desc:DESC { et.track("DESC", desc); ocd.setDescription(qdstring(desc.getText())); } )
 	    |
-	    ( OBSOLETE { ocd.setObsolete( true ); } )
+	    ( obsolete:OBSOLETE { et.track("OBSOLETE", obsolete); ocd.setObsolete( true ); } )
 	    |
-	    ( sup:SUP { ocd.setSuperiorObjectClasses(oids(sup.getText())); } )
+	    ( sup:SUP { et.track("SUP", sup); ocd.setSuperiorObjectClasses(oids(sup.getText())); } )
 	    |
-	    ( ABSTRACT { ocd.setKind( ObjectClassTypeEnum.ABSTRACT ); }
+	    ( kind1:ABSTRACT { et.track("KIND", kind1); ocd.setKind( ObjectClassTypeEnum.ABSTRACT ); }
 	      |
-	      STRUCTURAL { ocd.setKind( ObjectClassTypeEnum.STRUCTURAL ); }
+	      kind2:STRUCTURAL { et.track("KIND", kind2); ocd.setKind( ObjectClassTypeEnum.STRUCTURAL ); }
 	      |
-	      AUXILIARY { ocd.setKind( ObjectClassTypeEnum.AUXILIARY ); } 
+	      kind3:AUXILIARY { et.track("KIND", kind3); ocd.setKind( ObjectClassTypeEnum.AUXILIARY ); } 
 	    )
 	    |
-	    ( must:MUST { ocd.setMustAttributeTypes(oids(must.getText())); } )
+	    ( must:MUST { et.track("MUST", must); ocd.setMustAttributeTypes(oids(must.getText())); } )
 	    |
-	    ( may:MAY { ocd.setMayAttributeTypes(oids(may.getText())); } )
+	    ( may:MAY { et.track("MAY", may); ocd.setMayAttributeTypes(oids(may.getText())); } )
 	    |
 	    ( extension:EXTENSION { 
 	        Extension ex = extension(extension.getText());
+	        et.track(ex.key, extension); 
 	        ocd.addExtension(ex.key, ex.values); 
 	     } )
 	)*    
@@ -204,50 +229,74 @@
      * </pre>
     */
 attributeTypeDescription returns [AttributeTypeDescription atd = new AttributeTypeDescription()]
+    {
+        ElementTracker et = new ElementTracker();
+    }
     :
     ( oid:STARTNUMERICOID { atd.setNumericOid(numericoid(oid.getText())); } )
     (
-	    ( name:NAME { atd.setNames(qdescrs(name.getText())); } )
+	    ( name:NAME { et.track("NAME", name); atd.setNames(qdescrs(name.getText())); } )
 	    |
-	    ( desc:DESC { atd.setDescription(qdstring(desc.getText())); } )
+	    ( desc:DESC { et.track("DESC", desc); atd.setDescription(qdstring(desc.getText())); } )
 	    |
-	    ( OBSOLETE { atd.setObsolete( true ); } )
+	    ( obsolete:OBSOLETE { et.track("OBSOLETE", obsolete); atd.setObsolete( true ); } )
 	    |
-	    ( sup:SUP { atd.setSuperType(oid(sup.getText())); } )
+	    ( sup:SUP { et.track("SUP", sup); atd.setSuperType(oid(sup.getText())); } )
 	    |
-        ( equality:EQUALITY { atd.setEqualityMatchingRule(oid(equality.getText())); } )
+        ( equality:EQUALITY { et.track("EQUALITY", equality); atd.setEqualityMatchingRule(oid(equality.getText())); } )
         |
-        ( ordering:ORDERING { atd.setOrderingMatchingRule(oid(ordering.getText())); } )
+        ( ordering:ORDERING { et.track("ORDERING", ordering); atd.setOrderingMatchingRule(oid(ordering.getText())); } )
         |
-        ( substr:SUBSTR { atd.setSubstringsMatchingRule(oid(substr.getText())); } )
+        ( substr:SUBSTR { et.track("SUBSTR", substr); atd.setSubstringsMatchingRule(oid(substr.getText())); } )
         |
         ( syntax:SYNTAX { 
+           et.track("SYNTAX", syntax); 
             NoidLen noidlen = noidlen(syntax.getText());
             atd.setSyntax(noidlen.noid); 
             atd.setSyntaxLength(noidlen.len);
           } )
         |
-        ( SINGLE_VALUE { atd.setSingleValued( true ); } )
+        ( singleValue:SINGLE_VALUE { et.track("SINGLE_VALUE", singleValue); atd.setSingleValued( true ); } )
         |
-        ( COLLECTIVE { atd.setCollective( true ); } )
+        ( collective:COLLECTIVE { et.track("COLLECTIVE", collective); atd.setCollective( true ); } )
         |
-        ( NO_USER_MODIFICATION { atd.setUserModifiable( false ); } )
+        ( noUserModification:NO_USER_MODIFICATION { et.track("NO_USER_MODIFICATION", noUserModification); atd.setUserModifiable( false ); } )
         |
-	    ( USAGE (WHSP)* USER_APPLICATIONS { atd.setUsage( UsageEnum.USER_APPLICATIONS ); }
+	    ( usage1:USAGE (WHSP)* USER_APPLICATIONS { et.track("USAGE", usage1); atd.setUsage( UsageEnum.USER_APPLICATIONS ); }
 	      |
-	      USAGE DIRECTORY_OPERATION { atd.setUsage( UsageEnum.DIRECTORY_OPERATION ); }
+	      usage2:USAGE DIRECTORY_OPERATION { et.track("USAGE", usage2); atd.setUsage( UsageEnum.DIRECTORY_OPERATION ); }
 	      |
-	      USAGE DISTRIBUTED_OPERATION { atd.setUsage( UsageEnum.DISTRIBUTED_OPERATION ); } 
+	      usage3:USAGE DISTRIBUTED_OPERATION { et.track("USAGE", usage3); atd.setUsage( UsageEnum.DISTRIBUTED_OPERATION ); } 
 	      |
-	      USAGE DSA_OPERATION { atd.setUsage( UsageEnum.DSA_OPERATION ); } 
+	      usage4:USAGE DSA_OPERATION { et.track("USAGE", usage4); atd.setUsage( UsageEnum.DSA_OPERATION ); } 
 	    )
 	    |
 	    ( extension:EXTENSION { 
 	        Extension ex = extension(extension.getText());
+	        et.track(ex.key, extension); 
 	        atd.addExtension(ex.key, ex.values); 
 	     } )
 	)*    
     RPAR
+    {
+        // semantic check: required elements
+        if( !et.contains("SYNTAX") && !et.contains("SUP") ) 
+        {
+            throw new SemanticException( "One of SYNTAX or SUP is required", null, 0, 0 );
+        }
+        
+        // COLLECTIVE requires USAGE userApplications
+        if ( atd.isCollective() && ( atd.getUsage() != UsageEnum.USER_APPLICATIONS ) )
+        {
+            throw new SemanticException( "COLLECTIVE requires USAGE userApplications", null, 0, 0 );
+        }
+        
+        // NO-USER-MODIFICATION requires an operational USAGE.
+        if ( !atd.isUserModifiable() && ( atd.getUsage() == UsageEnum.USER_APPLICATIONS ) )
+        {
+            throw new SemanticException( "NO-USER-MODIFICATION requires an operational USAGE", null, 0, 0 );
+        }
+    }
     ;
 
 
@@ -263,13 +312,17 @@
      * </pre>
     */
 ldapSyntaxDescription returns [LdapSyntaxDescription lsd = new LdapSyntaxDescription()]
-     :
+    {
+        ElementTracker et = new ElementTracker();
+    }
+    :
     ( oid:STARTNUMERICOID { lsd.setNumericOid(numericoid(oid.getText())); } )
     (
-	    ( desc:DESC { lsd.setDescription(qdstring(desc.getText())); } )
+	    ( desc:DESC { et.track("DESC", desc); lsd.setDescription(qdstring(desc.getText())); } )
 	    |
 	    ( extension:EXTENSION { 
 	        Extension ex = extension(extension.getText());
+	        et.track(ex.key, extension); 
 	        lsd.addExtension(ex.key, ex.values); 
 	     } )
     )*
@@ -293,23 +346,33 @@
      * </pre>
     */
 matchingRuleDescription returns [MatchingRuleDescription mrd = new MatchingRuleDescription()]
-     :
+    {
+        ElementTracker et = new ElementTracker();
+    }
+    :
     ( oid:STARTNUMERICOID { mrd.setNumericOid(numericoid(oid.getText())); } )
     (
-	    ( name:NAME { mrd.setNames(qdescrs(name.getText())); } )
+	    ( name:NAME { et.track("NAME", name); mrd.setNames(qdescrs(name.getText())); } )
 	    |
-	    ( desc:DESC { mrd.setDescription(qdstring(desc.getText())); } )
+	    ( desc:DESC { et.track("DESC", desc); mrd.setDescription(qdstring(desc.getText())); } )
 	    |
-	    ( OBSOLETE { mrd.setObsolete( true ); } )
+	    ( obsolete:OBSOLETE { et.track("OBSOLETE", obsolete); mrd.setObsolete( true ); } )
 	    |
-        ( syntax:SYNTAX { mrd.setSyntax(numericoid(syntax.getText())); } )
+        ( syntax:SYNTAX { et.track("SYNTAX", syntax); mrd.setSyntax(numericoid(syntax.getText())); } )
 	    |
 	    ( extension:EXTENSION { 
 	        Extension ex = extension(extension.getText());
+	        et.track(ex.key, extension); 
 	        mrd.addExtension(ex.key, ex.values); 
 	     } )
     )*
     RPAR
+    {
+        // semantic check: required elements
+        if( !et.contains("SYNTAX") ) {
+            throw new SemanticException( "SYNTAX is required", null, 0, 0 );
+        }
+    }
     ;
 
 
@@ -328,28 +391,200 @@
      * </pre>
     */
 matchingRuleUseDescription returns [MatchingRuleUseDescription mrud = new MatchingRuleUseDescription()]
-     :
+    {
+        ElementTracker et = new ElementTracker();
+    }
+    :
     ( oid:STARTNUMERICOID { mrud.setNumericOid(numericoid(oid.getText())); } )
     (
-	    ( name:NAME { mrud.setNames(qdescrs(name.getText())); } )
+	    ( name:NAME { et.track("NAME", name); mrud.setNames(qdescrs(name.getText())); } )
 	    |
-	    ( desc:DESC { mrud.setDescription(qdstring(desc.getText())); } )
+	    ( desc:DESC { et.track("DESC", desc); mrud.setDescription(qdstring(desc.getText())); } )
 	    |
-	    ( OBSOLETE { mrud.setObsolete( true ); } )
+	    ( obsolete:OBSOLETE { et.track("OBSOLETE", obsolete); mrud.setObsolete( true ); } )
 	    |
-        ( applies:APPLIES { mrud.setApplicableAttributes(oids(applies.getText())); } )
+        ( applies:APPLIES { et.track("APPLIES", applies); mrud.setApplicableAttributes(oids(applies.getText())); } )
 	    |
 	    ( extension:EXTENSION { 
 	        Extension ex = extension(extension.getText());
+	        et.track(ex.key, extension); 
 	        mrud.addExtension(ex.key, ex.values); 
 	     } )
     )*
     RPAR
+    {
+        // semantic check: required elements
+        if( !et.contains("APPLIES") ) {
+            throw new SemanticException( "APPLIES is required", null, 0, 0 );
+        }
+    }
     ;
 
 
+    /**
+     * Production for DIT content rule descriptions. It is fault-tolerant
+     * against element ordering.
+     *
+     * <pre>
+     * DITContentRuleDescription = LPAREN WSP
+     *    numericoid                 ; object identifier
+     *    [ SP "NAME" SP qdescrs ]   ; short names (descriptors)
+     *    [ SP "DESC" SP qdstring ]  ; description
+     *    [ SP "OBSOLETE" ]          ; not active
+     *    [ SP "AUX" SP oids ]       ; auxiliary object classes
+     *    [ SP "MUST" SP oids ]      ; attribute types
+     *    [ SP "MAY" SP oids ]       ; attribute types
+     *    [ SP "NOT" SP oids ]       ; attribute types
+     *    extensions WSP RPAREN      ; extensions
+     * </pre>
+    */
+ditContentRuleDescription returns [DITContentRuleDescription dcrd = new DITContentRuleDescription()]
+    {
+        ElementTracker et = new ElementTracker();
+    }
+    :
+    ( oid:STARTNUMERICOID { dcrd.setNumericOid(numericoid(oid.getText())); } )
+    (
+	    ( name:NAME { et.track("NAME", name); dcrd.setNames(qdescrs(name.getText())); } )
+	    |
+	    ( desc:DESC { et.track("DESC", desc); dcrd.setDescription(qdstring(desc.getText())); } )
+	    |
+	    ( obsolete:OBSOLETE { et.track("OBSOLETE", obsolete); dcrd.setObsolete( true ); } )
+	    |
+	    ( aux:AUX { et.track("AUX", aux); dcrd.setAuxiliaryObjectClasses(oids(aux.getText())); } )
+	    |
+	    ( must:MUST { et.track("MUST", must); dcrd.setMustAttributeTypes(oids(must.getText())); } )
+	    |
+	    ( may:MAY { et.track("MAY", may); dcrd.setMayAttributeTypes(oids(may.getText())); } )
+	    |
+	    ( not:NOT { et.track("NOT", not); dcrd.setNotAttributeTypes(oids(not.getText())); } )
+	    |
+	    ( extension:EXTENSION { 
+	        Extension ex = extension(extension.getText());
+	        et.track(ex.key, extension); 
+	        dcrd.addExtension(ex.key, ex.values); 
+	     } )
+    )*
+    RPAR
+    ;
 
 
+    /**
+     * Production for DIT structure rules descriptions. It is fault-tolerant
+     * against element ordering.
+     *
+     * <pre>
+     * DITStructureRuleDescription = LPAREN WSP
+     *   ruleid                     ; rule identifier
+     *   [ SP "NAME" SP qdescrs ]   ; short names (descriptors)
+     *   [ SP "DESC" SP qdstring ]  ; description
+     *   [ SP "OBSOLETE" ]          ; not active
+     *   SP "FORM" SP oid           ; NameForm
+     *   [ SP "SUP" ruleids ]       ; superior rules
+     *   extensions WSP RPAREN      ; extensions
+     *
+     * ruleids = ruleid / ( LPAREN WSP ruleidlist WSP RPAREN )
+     * ruleidlist = ruleid *( SP ruleid )
+     * ruleid = number
+     * </pre>
+    */
+ditStructureRuleDescription returns [DITStructureRuleDescription dsrd = new DITStructureRuleDescription()]
+    {
+        ElementTracker et = new ElementTracker();
+    }
+    :
+    ( ruleid:STARTNUMERICOID { dsrd.setRuleId(ruleid(ruleid.getText())); } )
+    (
+	    ( name:NAME { et.track("NAME", name); dsrd.setNames(qdescrs(name.getText())); } )
+	    |
+	    ( desc:DESC { et.track("DESC", desc); dsrd.setDescription(qdstring(desc.getText())); } )
+	    |
+	    ( obsolete:OBSOLETE { et.track("OBSOLETE", obsolete); dsrd.setObsolete( true ); } )
+	    |
+	    ( form:FORM { et.track("FORM", form); dsrd.setForm(oid(form.getText())); } )
+	    |
+	    ( sup:SUP { et.track("SUP", sup); dsrd.setSuperRules(ruleids(sup.getText())); } )
+	    |
+	    ( extension:EXTENSION { 
+	        Extension ex = extension(extension.getText());
+	        et.track(ex.key, extension); 
+	        dsrd.addExtension(ex.key, ex.values); 
+	     } )
+    )*
+    RPAR
+    {
+        // semantic check: required elements
+        if( !et.contains("FORM") ) {
+            throw new SemanticException( "FORM is required", null, 0, 0 );
+        }
+    }
+    ;
+
+
+    /**
+     * Production for name form descriptions. It is fault-tolerant
+     * against element ordering.
+     *
+     * <pre>
+     * NameFormDescription = LPAREN WSP
+     *    numericoid                 ; object identifier
+     *    [ SP "NAME" SP qdescrs ]   ; short names (descriptors)
+     *    [ SP "DESC" SP qdstring ]  ; description
+     *    [ SP "OBSOLETE" ]          ; not active
+     *    SP "OC" SP oid             ; structural object class
+     *    SP "MUST" SP oids          ; attribute types
+     *    [ SP "MAY" SP oids ]       ; attribute types
+     *    extensions WSP RPAREN      ; extensions
+     * </pre>
+    */
+nameFormDescription returns [NameFormDescription nfd = new NameFormDescription()]
+    {
+        ElementTracker et = new ElementTracker();
+    }
+    :
+    ( oid:STARTNUMERICOID { nfd.setNumericOid(numericoid(oid.getText())); } )
+    (
+	    ( name:NAME { et.track("NAME", name); nfd.setNames(qdescrs(name.getText())); } )
+	    |
+	    ( desc:DESC { et.track("DESC", desc); nfd.setDescription(qdstring(desc.getText())); } )
+	    |
+	    ( obsolete:OBSOLETE { et.track("OBSOLETE", obsolete); nfd.setObsolete( true ); } )
+	    |
+	    ( oc:OC { et.track("OC", oc); nfd.setStructuralObjectClass(oid(oc.getText())); } )
+	    |
+	    ( must:MUST { et.track("MUST", must); nfd.setMustAttributeTypes(oids(must.getText())); } )
+	    |
+	    ( may:MAY { et.track("MAY", may); nfd.setMayAttributeTypes(oids(may.getText())); } )
+	    |
+	    ( extension:EXTENSION { 
+	        Extension ex = extension(extension.getText());
+	        et.track(ex.key, extension); 
+	        nfd.addExtension(ex.key, ex.values); 
+	     } )
+    )*
+    RPAR
+    {
+        // semantic check: required elements
+        if( !et.contains("MUST") ) {
+            throw new SemanticException( "MUST is required", null, 0, 0 );
+        }
+        if( !et.contains("OC") ) {
+            throw new SemanticException( "OC is required", null, 0, 0 );
+        }
+        
+        // semantic check: MUST and MAY must be disjoint
+        //List<String> aList = new ArrayList<String>( nfd.getMustAttributeTypes() );
+        //aList.retainAll( nfd.getMayAttributeTypes() );
+        //if( !aList.isEmpty() ) 
+        //{
+        //    throw new SemanticException( "MUST and MAY must be disjoint, "+aList.get( 0 )+" appears in both", null, 0, 0 );
+        //}
+    }
+    ;
+    
+
+
+
 noidlen [String s] returns [NoidLen noidlen]
     {
         noidlen = new NoidLen();
@@ -435,6 +670,23 @@
     :
     ;
 
-    
+ruleid [String s] returns [Integer ruleid]
+    {
+    	AntlrSchemaValueLexer lexer = new AntlrSchemaValueLexer(new StringReader(s));
+        AntlrSchemaValueParser parser = new AntlrSchemaValueParser(lexer);
+        ruleid = parser.ruleid();
+    }
+    :
+    ;
 
+ruleids [String s] returns [List<Integer> ruleids]
+    {
+    	AntlrSchemaValueLexer lexer = new AntlrSchemaValueLexer(new StringReader(s));
+        AntlrSchemaValueParser parser = new AntlrSchemaValueParser(lexer);
+        ruleids = parser.ruleids();
+    }
+    :
+    ;
+
+
     
\ Kein Zeilenvorschub am Ende der Datei
Index: shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/MatchingRuleUseDescriptionSyntaxChecker.java
===================================================================
--- shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/MatchingRuleUseDescriptionSyntaxChecker.java	(Revision 487885)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/MatchingRuleUseDescriptionSyntaxChecker.java	(Arbeitskopie)
@@ -116,14 +116,7 @@
 
         try
         {
-            MatchingRuleUseDescription mrud = schemaParser.parseMatchingRuleUseDescription( strValue );
-            
-            // APPLIES must be present
-            if ( ( mrud.getApplicableAttributes()== null ) || ( mrud.getApplicableAttributes().isEmpty() ) ) 
-            {
-                return false;
-            }
-                
+            schemaParser.parseMatchingRuleUseDescription( strValue );
             return true;
         }
         catch ( ParseException pe )
Index: shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/NameFormDescriptionSyntaxChecker.java
===================================================================
--- shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/NameFormDescriptionSyntaxChecker.java	(Revision 0)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/NameFormDescriptionSyntaxChecker.java	(Revision 0)
@@ -0,0 +1,128 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+package org.apache.directory.shared.ldap.schema.syntax;
+
+
+import java.text.ParseException;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeValueException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.schema.syntax.parser.NameFormDescriptionSchemaParser;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+
+/**
+ * A SyntaxChecker which verifies that a value follows the
+ * name descripton syntax according to RFC 4512, par 4.2.7.2:
+ * 
+ * <pre>
+ * NameFormDescription = LPAREN WSP
+ *    numericoid                 ; object identifier
+ *    [ SP "NAME" SP qdescrs ]   ; short names (descriptors)
+ *    [ SP "DESC" SP qdstring ]  ; description
+ *    [ SP "OBSOLETE" ]          ; not active
+ *    SP "OC" SP oid             ; structural object class
+ *    SP "MUST" SP oids          ; attribute types
+ *    [ SP "MAY" SP oids ]       ; attribute types
+ *    extensions WSP RPAREN      ; extensions
+ * </pre>
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class NameFormDescriptionSyntaxChecker implements SyntaxChecker
+{
+
+    /** The Syntax OID, according to RFC 4517, par. 3.3.22 */
+    public static final String OID = "1.3.6.1.4.1.1466.115.121.1.35";
+
+    /** The schema parser used to parse the DITContentRuleDescription Syntax */
+    private NameFormDescriptionSchemaParser schemaParser = new NameFormDescriptionSchemaParser();
+
+
+    /**
+     * 
+     * Creates a new instance of DITContentRuleDescriptionSyntaxChecker.
+     *
+     */
+    public NameFormDescriptionSyntaxChecker()
+    {
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.SyntaxChecker#getSyntaxOid()
+     */
+    public String getSyntaxOid()
+    {
+        return OID;
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.SyntaxChecker#assertSyntax(java.lang.Object)
+     */
+    public void assertSyntax( Object value ) throws NamingException
+    {
+        if ( !isValidSyntax( value ) )
+        {
+            throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
+        }
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.SyntaxChecker#isValidSyntax(java.lang.Object)
+     */
+    public boolean isValidSyntax( Object value )
+    {
+        String strValue;
+
+        if ( value == null )
+        {
+            return false;
+        }
+
+        if ( value instanceof String )
+        {
+            strValue = ( String ) value;
+        }
+        else if ( value instanceof byte[] )
+        {
+            strValue = StringTools.utf8ToString( ( byte[] ) value );
+        }
+        else
+        {
+            strValue = value.toString();
+        }
+
+        try
+        {
+            schemaParser.parseNameFormDescription( strValue );
+            return true;
+        }
+        catch ( ParseException pe )
+        {
+            return false;
+        }
+    }
+}
Index: shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/AttributeTypeDescriptionSyntaxChecker.java
===================================================================
--- shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/AttributeTypeDescriptionSyntaxChecker.java	(Revision 487885)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/AttributeTypeDescriptionSyntaxChecker.java	(Arbeitskopie)
@@ -26,7 +26,6 @@
 
 import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeValueException;
 import org.apache.directory.shared.ldap.message.ResultCodeEnum;
-import org.apache.directory.shared.ldap.schema.UsageEnum;
 import org.apache.directory.shared.ldap.schema.syntax.parser.AttributeTypeDescriptionSchemaParser;
 import org.apache.directory.shared.ldap.util.StringTools;
 
@@ -141,26 +140,7 @@
 
         try
         {
-            AttributeTypeDescription atd = schemaParser.parseAttributeTypeDescription( strValue );
-            
-            // SYNTAX or SUP must be contained
-            if ( ( atd.getSyntax() == null ) && ( atd.getSuperType() == null ) ) 
-            {
-                return false;
-            }
-            
-            // COLLECTIVE requires usage userApplications
-            if ( atd.isCollective() && ( atd.getUsage() != UsageEnum.USER_APPLICATIONS ) )
-            {
-                return false;
-            }
-            
-            // NO-USER-MODIFICATION requires an operational usage.
-            if ( !atd.isUserModifiable() && ( atd.getUsage() == UsageEnum.USER_APPLICATIONS ) ) 
-            {
-                return false;
-            }
-                
+            schemaParser.parseAttributeTypeDescription( strValue );
             return true;
         }
         catch ( ParseException pe )
Index: shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/DITContentRuleDescription.java
===================================================================
--- shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/DITContentRuleDescription.java	(Revision 0)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/DITContentRuleDescription.java	(Revision 0)
@@ -0,0 +1,125 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+
+package org.apache.directory.shared.ldap.schema.syntax;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * RFC 4512 - 4.1.6.  DIT Content Rule Description
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class DITContentRuleDescription extends AbstractSchemaDescription
+{
+
+    private List<String> auxiliaryObjectClasses;
+
+    private List<String> mustAttributeTypes;
+
+    private List<String> mayAttributeTypes;
+
+    private List<String> notAttributeTypes;
+
+
+    public DITContentRuleDescription()
+    {
+        auxiliaryObjectClasses = new ArrayList<String>();
+        mustAttributeTypes = new ArrayList<String>();
+        mayAttributeTypes = new ArrayList<String>();
+        notAttributeTypes = new ArrayList<String>();
+    }
+
+
+    public List<String> getMayAttributeTypes()
+    {
+        return mayAttributeTypes;
+    }
+
+
+    public void setMayAttributeTypes( List<String> mayAttributeTypes )
+    {
+        this.mayAttributeTypes = mayAttributeTypes;
+    }
+
+
+    public List<String> getMustAttributeTypes()
+    {
+        return mustAttributeTypes;
+    }
+
+
+    public void setMustAttributeTypes( List<String> mustAttributeTypes )
+    {
+        this.mustAttributeTypes = mustAttributeTypes;
+    }
+
+
+    public List<String> getAuxiliaryObjectClasses()
+    {
+        return auxiliaryObjectClasses;
+    }
+
+
+    public void setAuxiliaryObjectClasses( List<String> auxiliaryObjectClasses )
+    {
+        this.auxiliaryObjectClasses = auxiliaryObjectClasses;
+    }
+
+
+    public List<String> getNotAttributeTypes()
+    {
+        return notAttributeTypes;
+    }
+
+
+    public void setNotAttributeTypes( List<String> notAttributeTypes )
+    {
+        this.notAttributeTypes = notAttributeTypes;
+    }
+
+
+    public void addAuxiliaryAttributeType( String oid )
+    {
+        auxiliaryObjectClasses.add( oid );
+    }
+
+
+    public void addMustAttributeType( String oid )
+    {
+        mustAttributeTypes.add( oid );
+    }
+
+
+    public void addMayAttributeType( String oid )
+    {
+        mayAttributeTypes.add( oid );
+    }
+
+
+    public void addNotAttributeType( String oid )
+    {
+        notAttributeTypes.add( oid );
+    }
+
+}
Index: shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/NameFormDescription.java
===================================================================
--- shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/NameFormDescription.java	(Revision 0)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/NameFormDescription.java	(Revision 0)
@@ -0,0 +1,98 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+
+package org.apache.directory.shared.ldap.schema.syntax;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * RFC 4512 - 4.1.7.2.  Name Form Description
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class NameFormDescription extends AbstractSchemaDescription
+{
+
+    private String structuralObjectClass;
+    
+    private List<String> mustAttributeTypes;
+
+    private List<String> mayAttributeTypes;
+
+
+    public NameFormDescription()
+    {
+        structuralObjectClass = null;
+        mustAttributeTypes = new ArrayList<String>();
+        mayAttributeTypes = new ArrayList<String>();
+    }
+
+
+    public List<String> getMayAttributeTypes()
+    {
+        return mayAttributeTypes;
+    }
+
+
+    public void setMayAttributeTypes( List<String> mayAttributeTypes )
+    {
+        this.mayAttributeTypes = mayAttributeTypes;
+    }
+
+
+    public List<String> getMustAttributeTypes()
+    {
+        return mustAttributeTypes;
+    }
+
+
+    public void setMustAttributeTypes( List<String> mustAttributeTypes )
+    {
+        this.mustAttributeTypes = mustAttributeTypes;
+    }
+
+
+    public void addMustAttributeType( String oid )
+    {
+        mustAttributeTypes.add( oid );
+    }
+
+
+    public void addMayAttributeType( String oid )
+    {
+        mayAttributeTypes.add( oid );
+    }
+
+
+    public String getStructuralObjectClass()
+    {
+        return structuralObjectClass;
+    }
+
+
+    public void setStructuralObjectClass( String structuralObjectClass )
+    {
+        this.structuralObjectClass = structuralObjectClass;
+    }
+
+}
Index: shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/DITStructureRuleDescriptionSyntaxChecker.java
===================================================================
--- shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/DITStructureRuleDescriptionSyntaxChecker.java	(Revision 0)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/DITStructureRuleDescriptionSyntaxChecker.java	(Revision 0)
@@ -0,0 +1,131 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+package org.apache.directory.shared.ldap.schema.syntax;
+
+
+import java.text.ParseException;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeValueException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.schema.syntax.parser.DITStructureRuleDescriptionSchemaParser;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+
+/**
+ * A SyntaxChecker which verifies that a value follows the
+ * DIT structure rule descripton syntax according to RFC 4512, par 4.2.7.1:
+ * 
+ * <pre>
+ * DITStructureRuleDescription = LPAREN WSP
+ *   ruleid                     ; rule identifier
+ *   [ SP "NAME" SP qdescrs ]   ; short names (descriptors)
+ *   [ SP "DESC" SP qdstring ]  ; description
+ *   [ SP "OBSOLETE" ]          ; not active
+ *   SP "FORM" SP oid           ; NameForm
+ *   [ SP "SUP" ruleids ]       ; superior rules
+ *   extensions WSP RPAREN      ; extensions
+ *
+ * ruleids = ruleid / ( LPAREN WSP ruleidlist WSP RPAREN )
+ * ruleidlist = ruleid *( SP ruleid )
+ * ruleid = numbers
+ * </pre>
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DITStructureRuleDescriptionSyntaxChecker implements SyntaxChecker
+{
+
+    /** The Syntax OID, according to RFC 4517, par. 3.3.8 */
+    public static final String OID = "1.3.6.1.4.1.1466.115.121.1.17";
+
+    /** The schema parser used to parse the DITContentRuleDescription Syntax */
+    private DITStructureRuleDescriptionSchemaParser schemaParser = new DITStructureRuleDescriptionSchemaParser();
+
+
+    /**
+     * 
+     * Creates a new instance of DITContentRuleDescriptionSyntaxChecker.
+     *
+     */
+    public DITStructureRuleDescriptionSyntaxChecker()
+    {
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.SyntaxChecker#getSyntaxOid()
+     */
+    public String getSyntaxOid()
+    {
+        return OID;
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.SyntaxChecker#assertSyntax(java.lang.Object)
+     */
+    public void assertSyntax( Object value ) throws NamingException
+    {
+        if ( !isValidSyntax( value ) )
+        {
+            throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
+        }
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.SyntaxChecker#isValidSyntax(java.lang.Object)
+     */
+    public boolean isValidSyntax( Object value )
+    {
+        String strValue;
+
+        if ( value == null )
+        {
+            return false;
+        }
+
+        if ( value instanceof String )
+        {
+            strValue = ( String ) value;
+        }
+        else if ( value instanceof byte[] )
+        {
+            strValue = StringTools.utf8ToString( ( byte[] ) value );
+        }
+        else
+        {
+            strValue = value.toString();
+        }
+
+        try
+        {
+            schemaParser.parseDITStructureRuleDescription( strValue );
+            return true;
+        }
+        catch ( ParseException pe )
+        {
+            return false;
+        }
+    }
+}
Index: shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/ObjectClassDescriptionSyntaxChecker.java
===================================================================
--- shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/ObjectClassDescriptionSyntaxChecker.java	(Revision 487885)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/ObjectClassDescriptionSyntaxChecker.java	(Arbeitskopie)
@@ -32,7 +32,7 @@
 
 /**
  * A SyntaxChecker which verifies that a value follows the
- * object class descripton syntax according to RFC 4512, par 4.4.1:
+ * object class descripton syntax according to RFC 4512, par 4.2.1:
  * 
  * <pre>
  * ObjectClassDescription = LPAREN WSP
@@ -124,7 +124,6 @@
         try
         {
             schemaParser.parseObjectClassDescription( strValue );
-                
             return true;
         }
         catch ( ParseException pe )
Index: shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/DITContentRuleDescriptionSyntaxChecker.java
===================================================================
--- shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/DITContentRuleDescriptionSyntaxChecker.java	(Revision 0)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/DITContentRuleDescriptionSyntaxChecker.java	(Revision 0)
@@ -0,0 +1,129 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+package org.apache.directory.shared.ldap.schema.syntax;
+
+
+import java.text.ParseException;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeValueException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.schema.syntax.parser.DITContentRuleDescriptionSchemaParser;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+
+/**
+ * A SyntaxChecker which verifies that a value follows the
+ * DIT content rule descripton syntax according to RFC 4512, par 4.2.6:
+ * 
+ * <pre>
+ * DITContentRuleDescription = LPAREN WSP
+ *    numericoid                 ; object identifier
+ *    [ SP "NAME" SP qdescrs ]   ; short names (descriptors)
+ *    [ SP "DESC" SP qdstring ]  ; description
+ *    [ SP "OBSOLETE" ]          ; not active
+ *    [ SP "AUX" SP oids ]       ; auxiliary object classes
+ *    [ SP "MUST" SP oids ]      ; attribute types
+ *    [ SP "MAY" SP oids ]       ; attribute types
+ *    [ SP "NOT" SP oids ]       ; attribute types
+ *    extensions WSP RPAREN      ; extensions
+ * </pre>
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DITContentRuleDescriptionSyntaxChecker implements SyntaxChecker
+{
+
+    /** The Syntax OID, according to RFC 4517, par. 3.3.7 */
+    public static final String OID = "1.3.6.1.4.1.1466.115.121.1.16";
+
+    /** The schema parser used to parse the DITContentRuleDescription Syntax */
+    private DITContentRuleDescriptionSchemaParser schemaParser = new DITContentRuleDescriptionSchemaParser();
+
+
+    /**
+     * 
+     * Creates a new instance of DITContentRuleDescriptionSyntaxChecker.
+     *
+     */
+    public DITContentRuleDescriptionSyntaxChecker()
+    {
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.SyntaxChecker#getSyntaxOid()
+     */
+    public String getSyntaxOid()
+    {
+        return OID;
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.SyntaxChecker#assertSyntax(java.lang.Object)
+     */
+    public void assertSyntax( Object value ) throws NamingException
+    {
+        if ( !isValidSyntax( value ) )
+        {
+            throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
+        }
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.SyntaxChecker#isValidSyntax(java.lang.Object)
+     */
+    public boolean isValidSyntax( Object value )
+    {
+        String strValue;
+
+        if ( value == null )
+        {
+            return false;
+        }
+
+        if ( value instanceof String )
+        {
+            strValue = ( String ) value;
+        }
+        else if ( value instanceof byte[] )
+        {
+            strValue = StringTools.utf8ToString( ( byte[] ) value );
+        }
+        else
+        {
+            strValue = value.toString();
+        }
+
+        try
+        {
+            schemaParser.parseDITContentRuleDescription( strValue );
+            return true;
+        }
+        catch ( ParseException pe )
+        {
+            return false;
+        }
+    }
+}
Index: shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/DITContentRuleDescriptionSchemaParser.java
===================================================================
--- shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/DITContentRuleDescriptionSchemaParser.java	(Revision 0)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/DITContentRuleDescriptionSchemaParser.java	(Revision 0)
@@ -0,0 +1,107 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+package org.apache.directory.shared.ldap.schema.syntax.parser;
+
+
+import java.text.ParseException;
+
+import org.apache.directory.shared.ldap.schema.syntax.AbstractSchemaDescription;
+import org.apache.directory.shared.ldap.schema.syntax.DITContentRuleDescription;
+
+import antlr.RecognitionException;
+import antlr.TokenStreamException;
+
+
+/**
+ * A parser for RFC 4512 DIT content rule descriptons
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class DITContentRuleDescriptionSchemaParser extends AbstractSchemaParser
+{
+
+    /**
+     * Creates a schema parser instance.
+     */
+    public DITContentRuleDescriptionSchemaParser()
+    {
+        super();
+    }
+
+
+    /**
+     * Parses a DIT content rule description according to RFC 4512:
+     * 
+     * <pre>
+     * DITContentRuleDescription = LPAREN WSP
+     *    numericoid                 ; object identifier
+     *    [ SP "NAME" SP qdescrs ]   ; short names (descriptors)
+     *    [ SP "DESC" SP qdstring ]  ; description
+     *    [ SP "OBSOLETE" ]          ; not active
+     *    [ SP "AUX" SP oids ]       ; auxiliary object classes
+     *    [ SP "MUST" SP oids ]      ; attribute types
+     *    [ SP "MAY" SP oids ]       ; attribute types
+     *    [ SP "NOT" SP oids ]       ; attribute types
+     *    extensions WSP RPAREN      ; extensions
+     * </pre>
+     * 
+     * @param ditContentRuleDescription the DIT content rule description to be parsed
+     * @return the parsed DITContentRuleDescription bean
+     * @throws ParseException if there are any recognition errors (bad syntax)
+     */
+    public synchronized DITContentRuleDescription parseDITContentRuleDescription( String ditContentRuleDescription )
+        throws ParseException
+    {
+
+        if ( ditContentRuleDescription == null )
+        {
+            throw new ParseException( "Null", 0 );
+        }
+
+        reset( ditContentRuleDescription ); // reset and initialize the parser / lexer pair
+
+        try
+        {
+            DITContentRuleDescription dcrd = parser.ditContentRuleDescription();
+            return dcrd;
+        }
+        catch ( RecognitionException re )
+        {
+            String msg = "Parser failure on DIT content rule description:\n\t" + ditContentRuleDescription;
+            msg += "\nAntlr message: " + re.getMessage();
+            msg += "\nAntlr column: " + re.getColumn();
+            throw new ParseException( msg, re.getColumn() );
+        }
+        catch ( TokenStreamException tse )
+        {
+            String msg = "Parser failure on DIT content rule description:\n\t" + ditContentRuleDescription;
+            msg += "\nAntlr message: " + tse.getMessage();
+            throw new ParseException( msg, 0 );
+        }
+
+    }
+
+
+    public AbstractSchemaDescription parse( String schemaDescription ) throws ParseException
+    {
+        return parseDITContentRuleDescription( schemaDescription );
+    }
+
+}
Index: shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/NameFormDescriptionSchemaParser.java
===================================================================
--- shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/NameFormDescriptionSchemaParser.java	(Revision 0)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/NameFormDescriptionSchemaParser.java	(Revision 0)
@@ -0,0 +1,106 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+package org.apache.directory.shared.ldap.schema.syntax.parser;
+
+
+import java.text.ParseException;
+
+import org.apache.directory.shared.ldap.schema.syntax.AbstractSchemaDescription;
+import org.apache.directory.shared.ldap.schema.syntax.NameFormDescription;
+
+import antlr.RecognitionException;
+import antlr.TokenStreamException;
+
+
+/**
+ * A parser for RFC 4512 name form descriptons
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class NameFormDescriptionSchemaParser extends AbstractSchemaParser
+{
+
+    /**
+     * Creates a schema parser instance.
+     */
+    public NameFormDescriptionSchemaParser()
+    {
+        super();
+    }
+
+
+    /**
+     * Parses a name form description according to RFC 4512:
+     * 
+     * <pre>
+     * NameFormDescription = LPAREN WSP
+     *    numericoid                 ; object identifier
+     *    [ SP "NAME" SP qdescrs ]   ; short names (descriptors)
+     *    [ SP "DESC" SP qdstring ]  ; description
+     *    [ SP "OBSOLETE" ]          ; not active
+     *    SP "OC" SP oid             ; structural object class
+     *    SP "MUST" SP oids          ; attribute types
+     *    [ SP "MAY" SP oids ]       ; attribute types
+     *    extensions WSP RPAREN      ; extensions
+     * </pre>
+     * 
+     * @param nameFormDescription the name form description to be parsed
+     * @return the parsed NameFormDescription bean
+     * @throws ParseException if there are any recognition errors (bad syntax)
+     */
+    public synchronized NameFormDescription parseNameFormDescription( String nameFormDescription )
+        throws ParseException
+    {
+
+        if ( nameFormDescription == null )
+        {
+            throw new ParseException( "Null", 0 );
+        }
+
+        reset( nameFormDescription ); // reset and initialize the parser / lexer pair
+
+        try
+        {
+            NameFormDescription nfd = parser.nameFormDescription();
+            return nfd;
+        }
+        catch ( RecognitionException re )
+        {
+            String msg = "Parser failure on name form description:\n\t" + nameFormDescription;
+            msg += "\nAntlr message: " + re.getMessage();
+            msg += "\nAntlr column: " + re.getColumn();
+            throw new ParseException( msg, re.getColumn() );
+        }
+        catch ( TokenStreamException tse )
+        {
+            String msg = "Parser failure on name form description:\n\t" + nameFormDescription;
+            msg += "\nAntlr message: " + tse.getMessage();
+            throw new ParseException( msg, 0 );
+        }
+
+    }
+
+
+    public AbstractSchemaDescription parse( String schemaDescription ) throws ParseException
+    {
+        return parseNameFormDescription( schemaDescription );
+    }
+
+}
Index: shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/DITStructureRuleDescriptionSchemaParser.java
===================================================================
--- shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/DITStructureRuleDescriptionSchemaParser.java	(Revision 0)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/DITStructureRuleDescriptionSchemaParser.java	(Revision 0)
@@ -0,0 +1,109 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+package org.apache.directory.shared.ldap.schema.syntax.parser;
+
+
+import java.text.ParseException;
+
+import org.apache.directory.shared.ldap.schema.syntax.AbstractSchemaDescription;
+import org.apache.directory.shared.ldap.schema.syntax.DITStructureRuleDescription;
+
+import antlr.RecognitionException;
+import antlr.TokenStreamException;
+
+
+/**
+ * A parser for RFC 4512 DIT structure rule descriptons
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class DITStructureRuleDescriptionSchemaParser extends AbstractSchemaParser
+{
+
+    /**
+     * Creates a schema parser instance.
+     */
+    public DITStructureRuleDescriptionSchemaParser()
+    {
+        super();
+    }
+
+
+    /**
+     * Parses a DIT structure rule description according to RFC 4512:
+     * 
+     * <pre>
+     * DITStructureRuleDescription = LPAREN WSP
+     *   ruleid                     ; rule identifier
+     *   [ SP "NAME" SP qdescrs ]   ; short names (descriptors)
+     *   [ SP "DESC" SP qdstring ]  ; description
+     *   [ SP "OBSOLETE" ]          ; not active
+     *   SP "FORM" SP oid           ; NameForm
+     *   [ SP "SUP" ruleids ]       ; superior rules
+     *   extensions WSP RPAREN      ; extensions
+     *
+     * ruleids = ruleid / ( LPAREN WSP ruleidlist WSP RPAREN )
+     * ruleidlist = ruleid *( SP ruleid )
+     * ruleid = numbers
+     * </pre>
+     * 
+     * @param ditStructureRuleDescription the DIT structure rule description to be parsed
+     * @return the parsed DITStructureRuleDescription bean
+     * @throws ParseException if there are any recognition errors (bad syntax)
+     */
+    public synchronized DITStructureRuleDescription parseDITStructureRuleDescription( String ditStructureRuleDescription )
+        throws ParseException
+    {
+
+        if ( ditStructureRuleDescription == null )
+        {
+            throw new ParseException( "Null", 0 );
+        }
+
+        reset( ditStructureRuleDescription ); // reset and initialize the parser / lexer pair
+
+        try
+        {
+            DITStructureRuleDescription dsrd = parser.ditStructureRuleDescription();
+            return dsrd;
+        }
+        catch ( RecognitionException re )
+        {
+            String msg = "Parser failure on DIT structure rule description:\n\t" + ditStructureRuleDescription;
+            msg += "\nAntlr message: " + re.getMessage();
+            msg += "\nAntlr column: " + re.getColumn();
+            throw new ParseException( msg, re.getColumn() );
+        }
+        catch ( TokenStreamException tse )
+        {
+            String msg = "Parser failure on DIT structure rule description:\n\t" + ditStructureRuleDescription;
+            msg += "\nAntlr message: " + tse.getMessage();
+            throw new ParseException( msg, 0 );
+        }
+
+    }
+
+
+    public AbstractSchemaDescription parse( String schemaDescription ) throws ParseException
+    {
+        return parseDITStructureRuleDescription( schemaDescription );
+    }
+
+}
Index: shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/MatchingRuleDescriptionSyntaxChecker.java
===================================================================
--- shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/MatchingRuleDescriptionSyntaxChecker.java	(Revision 487885)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/MatchingRuleDescriptionSyntaxChecker.java	(Arbeitskopie)
@@ -119,14 +119,7 @@
 
         try
         {
-            MatchingRuleDescription mrd = schemaParser.parseMatchingRuleDescription( strValue );
-            
-            // SYNTAX must be present
-            if ( ( mrd.getSyntax() == null ) ) 
-            {
-                return false;
-            }
-            
+            schemaParser.parseMatchingRuleDescription( strValue );
             return true;
         }
         catch ( ParseException pe )
Index: shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/DITStructureRuleDescription.java
===================================================================
--- shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/DITStructureRuleDescription.java	(Revision 0)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/DITStructureRuleDescription.java	(Revision 0)
@@ -0,0 +1,93 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+
+package org.apache.directory.shared.ldap.schema.syntax;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * RFC 4512 - 4.1.7.  DIT Structure Rule Description
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class DITStructureRuleDescription extends AbstractSchemaDescription
+{
+
+    private Integer ruleId;
+    
+    private String form;
+
+    private List<Integer> superRules;
+
+
+    public DITStructureRuleDescription()
+    {
+        ruleId = 0;
+        form = null;
+        superRules = new ArrayList<Integer>();
+    }
+
+
+    public String getForm()
+    {
+        return form;
+    }
+
+
+    public void setForm( String form )
+    {
+        this.form = form;
+    }
+
+
+
+    public Integer getRuleId()
+    {
+        return ruleId;
+    }
+
+
+    public void setRuleId( Integer ruleId )
+    {
+        this.ruleId = ruleId;
+    }
+
+
+    public List<Integer> getSuperRules()
+    {
+        return superRules;
+    }
+
+
+    public void setSuperRules( List<Integer> superRules )
+    {
+        this.superRules = superRules;
+    }
+
+    
+    public void addSuperRule( Integer superRule )
+    {
+        superRules.add( superRule );
+    }
+
+}
