Index: shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/AttributeTypeDescriptionSyntaxCheckerTest.java
===================================================================
--- shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/AttributeTypeDescriptionSyntaxCheckerTest.java	(Revision 0)
+++ shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/AttributeTypeDescriptionSyntaxCheckerTest.java	(Revision 0)
@@ -0,0 +1,126 @@
+/*
+ *  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 AttributeTypeDescriptionSyntaxChecker.
+ * 
+ * There are also many test cases in SchemaParserAttributeTypeDescriptionTest.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class AttributeTypeDescriptionSyntaxCheckerTest extends TestCase
+{
+    AttributeTypeDescriptionSyntaxChecker checker = new AttributeTypeDescriptionSyntaxChecker();
+
+
+    public void testNullString()
+    {
+        assertFalse( checker.isValidSyntax( null ) );
+    }
+
+
+    public void testEmptyString()
+    {
+        assertFalse( checker.isValidSyntax( "" ) );
+    }
+
+
+    public void testOneCharString()
+    {
+        assertFalse( checker.isValidSyntax( "A" ) );
+        assertFalse( checker.isValidSyntax( "1" ) );
+        assertFalse( checker.isValidSyntax( "-" ) );
+        assertFalse( checker.isValidSyntax( "(" ) );
+    }
+
+
+    public void testValid()
+    {
+        assertTrue( checker.isValidSyntax( "( 2.5.4.3 NAME 'cn' SUP name )" ) );
+        assertTrue( checker.isValidSyntax( "( 2.5.4.3 NAME ( 'cn' 'commonName' ) SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )" ) );
+        assertTrue( checker.isValidSyntax( "( 2.5.4.3 NAME ( 'cn' 'commonName' ) DESC 'RFC2256: common name(s) for which the entity is known by' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15  )" ) );
+        assertTrue( checker.isValidSyntax( "( 2.5.4.3 NAME ( 'cn' 'commonName' ) DESC 'RFC2256: common name(s) for which the entity is known by'  SUP name  )" ) );
+        assertTrue( checker.isValidSyntax( "( 2.5.4.3 NAME ( 'cn' 'commonName' ) DESC 'RFC2256: common name(s) for which the entity is known by'  SUP name EQUALITY caseIgnoreMatch  )" ) );
+        assertTrue( checker.isValidSyntax( "( 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  )" ) );
+        assertTrue( checker.isValidSyntax( "( 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 )" ) );
+        assertTrue( checker.isValidSyntax( "( 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 )" ) );
+
+        // spaces
+        assertTrue( checker.isValidSyntax( "(2.5.4.3 SUP name)" ) );
+        assertTrue( checker.isValidSyntax( "(      2.5.4.3      NAME ('cn'   'commonName')     SYNTAX       1.3.6.1.4.1.1466.115.121.1.15   )" ) );
+        
+        // COLLECTIVE requires usage userApplications
+        assertTrue( checker.isValidSyntax( "( 2.5.4.3 NAME 'cn' SUP name COLLECTIVE )" ) );
+        assertTrue( checker.isValidSyntax( "( 2.5.4.3 NAME 'cn' SUP name COLLECTIVE USAGE userApplications )" ) );
+
+        // NO-USER-MODIFICATION requires an operational usage.
+        assertTrue( checker.isValidSyntax( "( 2.5.4.3 NAME 'cn' SUP name NO-USER-MODIFICATION USAGE dSAOperation )" ) );
+        assertTrue( checker.isValidSyntax( "( 2.5.4.3 NAME 'cn' SUP name NO-USER-MODIFICATION USAGE directoryOperation )" ) );
+        assertTrue( checker.isValidSyntax( "( 2.5.4.3 NAME 'cn' SUP name NO-USER-MODIFICATION USAGE distributedOperation )" ) );
+
+    }
+
+
+    public void testInvalid()
+    {
+        // 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.4.3 NAME 'cn' SUP name" ) );
+
+        // missing quotes
+        assertFalse( checker.isValidSyntax( "( 2.5.4.3 NAME cn SUP name )" ) );
+
+        // lowercase NAME, SUP
+        assertFalse( checker.isValidSyntax( "( 2.5.4.3 name ( 'cn' 'commonName' )  sup name " ) );
+
+        // SYNTAX or SUP must be contained
+        assertFalse( checker.isValidSyntax( "( 2.5.4.3 NAME ( 'cn' 'commonName' ) DESC 'RFC2256: common name(s) for which the entity is known by' )" ) );
+        
+        // COLLECTIVE requires usage userApplications
+        assertFalse( checker.isValidSyntax( "( 2.5.4.3 NAME 'cn' SUP name COLLECTIVE USAGE dSAOperation)" ) );
+        assertFalse( checker.isValidSyntax( "( 2.5.4.3 NAME 'cn' SUP name COLLECTIVE USAGE directoryOperation )" ) );
+        assertFalse( checker.isValidSyntax( "( 2.5.4.3 NAME 'cn' SUP name COLLECTIVE USAGE distributedOperation )" ) );
+        
+        // NO-USER-MODIFICATION requires an operational usage.
+        assertFalse( checker.isValidSyntax( "( 2.5.4.3 NAME 'cn' SUP name NO-USER-MODIFICATION )" ) );
+        assertFalse( checker.isValidSyntax( "( 2.5.4.3 NAME 'cn' SUP name NO-USER-MODIFICATION USAGE userApplications )" ) );
+
+        // invalid characters
+        assertFalse( checker.isValidSyntax( "( 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 )" ) );
+        assertFalse( checker.isValidSyntax( "( 2.5.4.3 NAME ( 'cn' 'common_name' ) 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 )" ) );
+        assertFalse( checker.isValidSyntax( "( 2.5.4.3 NAME ( 'cn' 'commonName' ) DESC 'RFC2256: common name(s) for which the entity is known by'  SUP na=me EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 USAGE userApplications )" ) );
+    }
+
+}
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 0)
+++ shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserAttributeTypeDescriptionTest.java	(Revision 0)
@@ -0,0 +1,1054 @@
+/*
+ *  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 java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.shared.ldap.schema.syntax.parser.AttributeTypeDescriptionSchemaParser;
+
+
+/**
+ * Tests the AttributeTypeDescriptionSchemaParser class.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class SchemaParserAttributeTypeDescriptionTest extends TestCase
+{
+    /** the parser instance */
+    private AttributeTypeDescriptionSchemaParser parser;
+
+    /** holds multithreaded success value */
+    boolean isSuccessMultithreaded = true;
+
+
+    protected void setUp() throws Exception
+    {
+        parser = new AttributeTypeDescriptionSchemaParser();
+    }
+
+
+    protected void tearDown() throws Exception
+    {
+        parser = null;
+    }
+
+
+    /**
+     * Test numericoid
+     * 
+     * @throws ParseException
+     */
+    public void testNumericOid() throws ParseException
+    {
+        String value = null;
+        AttributeTypeDescription atd = null;
+
+        // null test
+        value = null;
+        try
+        {
+            parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, null" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // no oid
+        value = "( )";
+        try
+        {
+            parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, no NUMERICOID" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // simple
+        value = "( 1.1 )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( "1.1", atd.getNumericOid() );
+
+        // simple with spaces
+        value = "(          1.1          )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( "1.1", atd.getNumericOid() );
+
+        // non-numeric not allowed
+        value = "( cn )";
+        try
+        {
+            parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid NUMERICOID top" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // to short
+        value = "( 1 )";
+        try
+        {
+            parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid NUMERICOID 1" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // dot only
+        value = "( . )";
+        try
+        {
+            parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid NUMERICOID ." );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // ends with dot
+        value = "( 1.1. )";
+        try
+        {
+            parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid NUMERICOID 1.1." );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // quotes not allowed
+        value = "( '1.1' )";
+        try
+        {
+            parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid NUMERICOID '1.1' (quoted)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+    }
+
+
+    /**
+     * Tests NAME and its values
+     * 
+     * @throws ParseException
+     */
+    public void testNames() throws ParseException
+    {
+        String value = null;
+        AttributeTypeDescription atd = null;
+
+        // alpha
+        value = "( 1.1 NAME 'test' )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( 1, atd.getNames().size() );
+        assertEquals( "test", atd.getNames().get( 0 ) );
+
+        // alpha-num-hypen
+        value = "( 1.1 NAME 'a-z-0-9' )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( 1, atd.getNames().size() );
+        assertEquals( "a-z-0-9", atd.getNames().get( 0 ) );
+
+        // with parentheses
+        value = "( 1.1 NAME ( 'a-z-0-9' ) )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( 1, atd.getNames().size() );
+        assertEquals( "a-z-0-9", atd.getNames().get( 0 ) );
+
+        // with parentheses, without space
+        value = "( 1.1 NAME ('a-z-0-9') )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( 1, atd.getNames().size() );
+        assertEquals( "a-z-0-9", atd.getNames().get( 0 ) );
+
+        // multi with space
+        value = "( 1.1 NAME ( 'test' 'a-z-0-9' ) )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( 2, atd.getNames().size() );
+        assertEquals( "test", atd.getNames().get( 0 ) );
+        assertEquals( "a-z-0-9", atd.getNames().get( 1 ) );
+
+        // multi without space
+        value = "( 1.1 NAME ('test' 'a-z-0-9' 'givenName') )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( 3, atd.getNames().size() );
+        assertEquals( "test", atd.getNames().get( 0 ) );
+        assertEquals( "a-z-0-9", atd.getNames().get( 1 ) );
+        assertEquals( "givenName", atd.getNames().get( 2 ) );
+
+        // multi with many spaces
+        value = "(          1.1          NAME          (          'test'          'a-z-0-9'          'givenName'          )          )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( 3, atd.getNames().size() );
+        assertEquals( "test", atd.getNames().get( 0 ) );
+        assertEquals( "a-z-0-9", atd.getNames().get( 1 ) );
+        assertEquals( "givenName", atd.getNames().get( 2 ) );
+
+        // lowercase
+        value = "( 1.1 name 'test' )";
+        try
+        {
+            parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, NAME is lowercase" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // unquoted
+        value = "( 1.1 NAME test )";
+        try
+        {
+            parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid NAME test (unquoted)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // start with number
+        value = "( 1.1 NAME '1test' )";
+        try
+        {
+            parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid NAME 1test (starts with number)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // start with hypen
+        value = "( 1.1 NAME '-test' )";
+        try
+        {
+            parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid NAME -test (starts with hypen)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // invalid character
+        value = "( 1.1 NAME 'te_st' )";
+        try
+        {
+            parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid NAME te_st (contains invalid character)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // NAM unknown
+        value = "( 1.1 NAM 'test' )";
+        try
+        {
+            parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid token NAM" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // one valid, one invalid
+        value = "( 1.1 NAME ( 'test' 'te_st' ) )";
+        try
+        {
+            parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid NAME te_st (contains invalid character)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // no space between values
+        value = "( 1.1 NAME ( 'test''test2' ) )";
+        try
+        {
+            atd = parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid NAME values (no space between values)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+    }
+
+
+    /**
+     * Tests DESC
+     * 
+     * @throws ParseException
+     */
+    public void testDescription() throws ParseException
+    {
+        String value = null;
+        AttributeTypeDescription atd = null;
+
+        // simple
+        value = "(1.1 NAME 'test' DESC 'Descripton')";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( "Descripton", atd.getDescription() );
+
+        // unicode
+        value = "( 1.1 NAME 'test' DESC 'Descripton äöüß 部長' )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( "Descripton äöüß 部長", atd.getDescription() );
+
+        // lowercase
+        value = "( 1.1 desc 'Descripton' )";
+        try
+        {
+            parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, DESC is lowercase" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+    }
+
+
+    /**
+     * Tests OBSOLETE
+     * 
+     * @throws ParseException
+     */
+    public void testObsolete() throws ParseException
+    {
+        String value = null;
+        AttributeTypeDescription atd = null;
+
+        // not obsolete
+        value = "( 1.1 NAME 'test' DESC 'Descripton' )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertFalse( atd.isObsolete() );
+
+        // obsolete
+        value = "(1.1 NAME 'test' DESC 'Descripton' OBSOLETE)";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertTrue( atd.isObsolete() );
+
+        // obsolete 
+        value = "(1.1 OBSOLETE)";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertTrue( atd.isObsolete() );
+
+        // ivalid
+        value = "(1.1 NAME 'test' DESC 'Descripton' OBSOLET )";
+        try
+        {
+            atd = parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid OBSOLETE value" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+    }
+
+
+    /**
+     * Test SUP and its values.
+     * 
+     * @throws ParseException
+     */
+    public void testSuperType() throws ParseException
+    {
+        String value = null;
+        AttributeTypeDescription atd = null;
+
+        // no SUP
+        value = "( 1.1 )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertNull( atd.getSuperType() );
+
+        // SUP numericoid
+        value = "( 1.1 SUP 1.2.3 )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( "1.2.3", atd.getSuperType() );
+
+        // SUP descr
+        value = "( 1.1 SUP name )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( "name", atd.getSuperType() );
+
+        // no quote allowed
+        value = "( 1.1 SUP 'name' )";
+        try
+        {
+            atd = parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid SUP 'name' (quoted)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // no quote allowed
+        value = "( 1.1 SUP '1.2.3.4' )";
+        try
+        {
+            atd = parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid SUP '1.2.3.4' (quoted)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // invalid character
+        value = "( 1.1 SUP 1.2.3.4.A )";
+        try
+        {
+            atd = parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid SUP '1.2.3.4.A' (invalid character)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // only single SUP allowed
+        value = "( 1.1 SUP ( name1 $ name2 ) )";
+        try
+        {
+            atd = parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, only single SUP allowed" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // empty sup
+        value = "( 1.1 SUP )";
+        try
+        {
+            atd = parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, no SUP value" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+    }
+
+
+    /**
+     * Tests EQUALITY and its values.
+     * Very similar to SUP, so here are less test cases. 
+     * 
+     * @throws ParseException
+     */
+    public void testEquality() throws ParseException
+    {
+        String value = null;
+        AttributeTypeDescription atd = null;
+
+        // no EQUALITY
+        value = "( 1.1 )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertNull( atd.getEqualityMatchingRule() );
+
+        // EQUALITY numericoid
+        value = "( 1.1 EQUALITY 1.2.3 )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( "1.2.3", atd.getEqualityMatchingRule() );
+
+        // EQUALITY descr
+        value = "( 1.1 EQUALITY caseExcactMatch )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( "caseExcactMatch", atd.getEqualityMatchingRule() );
+
+        // no quote allowed
+        value = "( 1.1 EQUALITY 'caseExcactMatch' )";
+        try
+        {
+            atd = parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid EQUALITY 'caseExcactMatch' (quoted)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+    }
+
+
+    /**
+     * Tests ORDERING and its values.
+     * Very similar to SUP, so here are less test cases. 
+     * 
+     * @throws ParseException
+     */
+    public void testOrdering() throws ParseException
+    {
+        String value = null;
+        AttributeTypeDescription atd = null;
+
+        // no EQUALITY
+        value = "( 1.1 )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertNull( atd.getOrderingMatchingRule() );
+
+        // EQUALITY numericoid
+        value = "( 1.1 ORDERING 1.2.3 )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( "1.2.3", atd.getOrderingMatchingRule() );
+
+        // EQUALITY descr
+        value = "( 1.1 ORDERING generalizedTimeOrderingMatch )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( "generalizedTimeOrderingMatch", atd.getOrderingMatchingRule() );
+
+        // no quote allowed
+        value = "( 1.1 ORDERING 'generalizedTimeOrderingMatch' )";
+        try
+        {
+            atd = parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid ORDERING 'caseExcactMatch' (quoted)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+    }
+
+
+    /**
+     * Tests SUBSTRING and its values.
+     * Very similar to SUP, so here are less test cases. 
+     * 
+     * @throws ParseException
+     */
+    public void testSubstring() throws ParseException
+    {
+        String value = null;
+        AttributeTypeDescription atd = null;
+
+        // no EQUALITY
+        value = "( 1.1 )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertNull( atd.getSubstringsMatchingRule() );
+
+        // EQUALITY numericoid
+        value = "( 1.1 SUBSTR 1.2.3 )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( "1.2.3", atd.getSubstringsMatchingRule() );
+
+        // EQUALITY descr
+        value = "( 1.1 SUBSTR caseIgnoreSubstringsMatch )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( "caseIgnoreSubstringsMatch", atd.getSubstringsMatchingRule() );
+
+        // no quote allowed
+        value = "( 1.1 SUBSTR 'generalizedTimeOrderingMatch' )";
+        try
+        {
+            atd = parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid SUBSTR 'generalizedTimeOrderingMatch' (quoted)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+    }
+
+
+    /**
+     * Tests SYNTAX
+     * 
+     * @throws ParseException
+     */
+    public void testSyntax() throws ParseException
+    {
+        String value = null;
+        AttributeTypeDescription atd = null;
+
+        // no SYNTAX
+        value = "( 1.1 )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertNull( atd.getSyntax() );
+        assertEquals( 0, atd.getSyntaxLength() );
+
+        // SYNTAX numericoid
+        value = "( 1.1 SYNTAX 1.2.3 )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( "1.2.3", atd.getSyntax() );
+        assertEquals( 0, atd.getSyntaxLength() );
+
+        // SYNTAX numericoid and length
+        value = "( 1.1 SYNTAX 1.2.3{32} )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( "1.2.3", atd.getSyntax() );
+        assertEquals( 32, atd.getSyntaxLength() );
+
+        // SYNTAX numericoid and zero length
+        value = "( 1.1 SYNTAX 1.2.3{0} )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( "1.2.3", atd.getSyntax() );
+        assertEquals( 0, atd.getSyntaxLength() );
+
+        // no quote allowed
+        value = "( 1.1 SYNTAX '1.2.3{32}' )";
+        try
+        {
+            atd = parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid SYNTAX '1.2.3{32}' (quoted)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // empty syntax
+        value = "( 1.1 SYNTAX 1.2.3.4{} )";
+        try
+        {
+            atd = parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid SYNTAX 1.2.3.4{} (empty length)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // zero syntax
+        value = "( 1.1 SYNTAX 1.2.3.4{01} )";
+        try
+        {
+            atd = parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid SYNTAX 1.2.3.4{01} (leading zero length)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // invalid syntax
+        value = "( 1.1 SYNTAX 1.2.3.4{X} )";
+        try
+        {
+            atd = parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid SYNTAX 1.2.3.4{X} (zero length)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // no syntax
+        value = "( 1.1 SYNTAX {32} )";
+        try
+        {
+            atd = parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid SYNTAX {32} (no syntax)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+    }
+
+
+    /**
+     * Tests SINGLE-VALUE
+     * 
+     * @throws ParseException
+     */
+    public void testSingleValue() throws ParseException
+    {
+        String value = null;
+        AttributeTypeDescription atd = null;
+
+        // not single-value
+        value = "( 1.1 NAME 'test' DESC 'Descripton' )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertFalse( atd.isSingleValued() );
+
+        // single-value
+        value = "(1.1 NAME 'test' DESC 'Descripton' SINGLE-VALUE)";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertTrue( atd.isSingleValued() );
+
+        // single-value 
+        value = "(1.1 SINGLE-VALUE)";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertTrue( atd.isSingleValued() );
+
+        // ivalid
+        value = "(1.1 NAME 'test' DESC 'Descripton' SINGLE-VALU )";
+        try
+        {
+            atd = parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid SINGLE-VALUE value" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+    }
+
+
+    /**
+     * Tests COLLECTIVE
+     * 
+     * @throws ParseException
+     */
+    public void testCollective() throws ParseException
+    {
+        String value = null;
+        AttributeTypeDescription atd = null;
+
+        // not collective
+        value = "( 1.1 NAME 'test' DESC 'Descripton' )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertFalse( atd.isCollective() );
+
+        // single-value
+        value = "(1.1 NAME 'test' DESC 'Descripton' COLLECTIVE )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertTrue( atd.isCollective() );
+
+        // single-value 
+        value = "(1.1 COLLECTIVE)";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertTrue( atd.isCollective() );
+
+        // ivalid
+        value = "(1.1 NAME 'test' DESC 'Descripton' COLLECTIV )";
+        try
+        {
+            atd = parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid COLLECTIVE value" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+    }
+
+
+    /**
+     * Tests NO-USER-MODIFICATION
+     * 
+     * @throws ParseException
+     */
+    public void testNoUserModification() throws ParseException
+    {
+        String value = null;
+        AttributeTypeDescription atd = null;
+
+        // not NO-USER-MODIFICATION
+        value = "( 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 )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertFalse( atd.isUserModifiable() );
+
+        // NO-USER-MODIFICATION 
+        value = "(1.1 NO-USER-MODIFICATION)";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertFalse( atd.isUserModifiable() );
+
+        // ivalid
+        value = "(1.1 NAME 'test' DESC 'Descripton' NO-USER-MODIFICATIO )";
+        try
+        {
+            atd = parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid COLLECTIVE value" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+    }
+
+
+    /**
+     * Tests usage 
+     * 
+     * @throws ParseException
+     */
+    public void testUsage() throws ParseException
+    {
+        String value = null;
+        AttributeTypeDescription atd = null;
+
+        // DEFAULT is userApplications
+        value = "( 1.1 )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( AttributeTypeDescription.Usage.USER_APPLICATIONS, atd.getUsage() );
+
+        // userApplications
+        value = "( 1.1 USAGE userApplications )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( AttributeTypeDescription.Usage.USER_APPLICATIONS, atd.getUsage() );
+
+        // directoryOperation
+        value = "( 1.1 USAGE directoryOperation )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( AttributeTypeDescription.Usage.DIRECTORY_OPERATION, atd.getUsage() );
+
+        // AUXILIARY
+        value = "( 1.1 USAGE distributedOperation )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( AttributeTypeDescription.Usage.DISTRIBUTED_OPERATION, atd.getUsage() );
+
+        // STRUCTURAL
+        value = "( 1.1 USAGE dSAOperation )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( AttributeTypeDescription.Usage.DSA_OPERATION, atd.getUsage() );
+
+        // TODO: case insensitive?
+
+        // ivalid
+        value = "( 1.1 USAGE abc )";
+        try
+        {
+            atd = parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid USAGE value" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+    }
+
+
+    /**
+     * Test extensions.
+     * 
+     * @throws ParseException
+     */
+    public void testExtensions() throws ParseException
+    {
+        String value = null;
+        AttributeTypeDescription atd = null;
+
+        // no extension
+        value = "( 1.1 )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( 0, atd.getExtensions().size() );
+
+        // single extension with one value
+        value = "( 1.1 X-TEST 'test' )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( 1, atd.getExtensions().size() );
+        assertNotNull( atd.getExtensions().get( "X-TEST" ) );
+        assertEquals( 1, atd.getExtensions().get( "X-TEST" ).size() );
+        assertEquals( "test", atd.getExtensions().get( "X-TEST" ).get( 0 ) );
+
+        // single extension with multiple values
+        value = "( 1.1 X-TEST-ABC ('test1' 'test äöüß'       'test 部長' ) )";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( 1, atd.getExtensions().size() );
+        assertNotNull( atd.getExtensions().get( "X-TEST-ABC" ) );
+        assertEquals( 3, atd.getExtensions().get( "X-TEST-ABC" ).size() );
+        assertEquals( "test1", atd.getExtensions().get( "X-TEST-ABC" ).get( 0 ) );
+        assertEquals( "test äöüß", atd.getExtensions().get( "X-TEST-ABC" ).get( 1 ) );
+        assertEquals( "test 部長", atd.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'))";
+        atd = parser.parseAttributeTypeDescription( value );
+        assertEquals( 2, atd.getExtensions().size() );
+        assertNotNull( atd.getExtensions().get( "X-TEST-a" ) );
+        assertEquals( 2, atd.getExtensions().get( "X-TEST-a" ).size() );
+        assertEquals( "test1-1", atd.getExtensions().get( "X-TEST-a" ).get( 0 ) );
+        assertEquals( "test1-2", atd.getExtensions().get( "X-TEST-a" ).get( 1 ) );
+        assertNotNull( atd.getExtensions().get( "X-TEST-b" ) );
+        assertEquals( 2, atd.getExtensions().get( "X-TEST-b" ).size() );
+        assertEquals( "test2-1", atd.getExtensions().get( "X-TEST-b" ).get( 0 ) );
+        assertEquals( "test2-2", atd.getExtensions().get( "X-TEST-b" ).get( 1 ) );
+
+        // invalid extension, no number allowed
+        value = "( 1.1 X-TEST1 'test' )";
+        try
+        {
+            atd = parser.parseAttributeTypeDescription( value );
+            fail( "Exception expected, invalid extension X-TEST1 (no number allowed)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+    }
+
+
+    public void testIgnoreElementOrder() throws ParseException
+    {
+        String value = "( 2.5.4.3 SUP name SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 USAGE userApplications DESC 'RFC2256: common name(s) for which the entity is known by'  EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch NAME ( 'cn' 'commonName' )  )";
+        AttributeTypeDescription atd = parser.parseAttributeTypeDescription( value );
+
+        assertEquals( "2.5.4.3", atd.getNumericOid() );
+        assertEquals( 2, atd.getNames().size() );
+        assertEquals( "cn", atd.getNames().get( 0 ) );
+        assertEquals( "commonName", atd.getNames().get( 1 ) );
+        assertEquals( "RFC2256: common name(s) for which the entity is known by", atd.getDescription() );
+        assertEquals( "name", atd.getSuperType() );
+        assertEquals( "caseIgnoreMatch", atd.getEqualityMatchingRule() );
+        assertEquals( "caseIgnoreSubstringsMatch", atd.getSubstringsMatchingRule() );
+        assertEquals( "1.3.6.1.4.1.1466.115.121.1.15", atd.getSyntax() );
+        assertEquals( AttributeTypeDescription.Usage.USER_APPLICATIONS, atd.getUsage() );
+        assertEquals( 0, atd.getExtensions().size() );
+    }
+
+
+    ////////////////////////////////////////////////////////////////
+    //         Some real-world attribute type definitions         //
+    ////////////////////////////////////////////////////////////////
+
+    public void testRfcUid() throws ParseException
+    {
+        String value = "( 0.9.2342.19200300.100.1.1 NAME ( 'uid' 'userid' ) DESC 'RFC1274: user identifier' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} USAGE userApplications )";
+        AttributeTypeDescription atd = parser.parseAttributeTypeDescription( value );
+
+        assertEquals( "0.9.2342.19200300.100.1.1", atd.getNumericOid() );
+        assertEquals( 2, atd.getNames().size() );
+        assertEquals( "uid", atd.getNames().get( 0 ) );
+        assertEquals( "userid", atd.getNames().get( 1 ) );
+        assertEquals( "RFC1274: user identifier", atd.getDescription() );
+        assertNull( atd.getSuperType() );
+
+        assertEquals( "caseIgnoreMatch", atd.getEqualityMatchingRule() );
+        assertEquals( "caseIgnoreSubstringsMatch", atd.getSubstringsMatchingRule() );
+        assertNull( atd.getOrderingMatchingRule() );
+        assertEquals( "1.3.6.1.4.1.1466.115.121.1.15", atd.getSyntax() );
+        assertEquals( 256, atd.getSyntaxLength() );
+        assertEquals( AttributeTypeDescription.Usage.USER_APPLICATIONS, atd.getUsage() );
+
+        assertFalse( atd.isObsolete() );
+        assertFalse( atd.isCollective() );
+        assertFalse( atd.isSingleValued() );
+        assertTrue( atd.isUserModifiable() );
+
+        assertEquals( 0, atd.getExtensions().size() );
+    }
+
+
+    /**
+     * Tests the multithreaded use of a single parser.
+     */
+    public void testMultiThreaded() throws Exception
+    {
+        // start up and track all threads (40 threads)
+        List<Thread> threads = new ArrayList<Thread>();
+        for ( int ii = 0; ii < 10; ii++ )
+        {
+            Thread t0 = new Thread( new ParseSpecification( "( 1.1 )" ) );
+            Thread t1 = new Thread(
+                new ParseSpecification(
+                    "( 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 )" ) );
+            Thread t2 = new Thread(
+                new ParseSpecification(
+                    "( 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 )" ) );
+            Thread t3 = new Thread(
+                new ParseSpecification(
+                    "( 2.5.18.3 NAME 'creatorsName' DESC 'RFC2252: name of creator'  EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )" ) );
+            threads.add( t0 );
+            threads.add( t1 );
+            threads.add( t2 );
+            threads.add( t3 );
+            t0.start();
+            t1.start();
+            t2.start();
+            t3.start();
+        }
+
+        // wait until all threads have died
+        boolean hasLiveThreads = false;
+        do
+        {
+            hasLiveThreads = false;
+
+            for ( int ii = 0; ii < threads.size(); ii++ )
+            {
+                Thread t = ( Thread ) threads.get( ii );
+                hasLiveThreads = hasLiveThreads || t.isAlive();
+            }
+        }
+        while ( hasLiveThreads );
+
+        // check that no one thread failed to parse and generate a SS object
+        assertTrue( isSuccessMultithreaded );
+    }
+
+    /**
+     * Used to test multithreaded use of a single parser.
+     */
+    class ParseSpecification implements Runnable
+    {
+        private final String atd;
+
+        AttributeTypeDescription result;
+
+
+        public ParseSpecification( String atd )
+        {
+            this.atd = atd;
+        }
+
+
+        public void run()
+        {
+            try
+            {
+                result = parser.parseAttributeTypeDescription( atd );
+            }
+            catch ( ParseException e )
+            {
+                e.printStackTrace();
+            }
+
+            isSuccessMultithreaded = isSuccessMultithreaded && ( result != null );
+        }
+    }
+
+}
Index: shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/LdapSyntaxDescriptionSyntaxCheckerTest.java
===================================================================
--- shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/LdapSyntaxDescriptionSyntaxCheckerTest.java	(Revision 0)
+++ shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/LdapSyntaxDescriptionSyntaxCheckerTest.java	(Revision 0)
@@ -0,0 +1,100 @@
+/*
+ *  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 LdapSyntaxDescriptionSyntaxChecker.
+ * 
+ * There are also many test cases in SchemaParserLdapSyntaxDescriptionTest.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class LdapSyntaxDescriptionSyntaxCheckerTest extends TestCase
+{
+    LdapSyntaxDescriptionSyntaxChecker checker = new LdapSyntaxDescriptionSyntaxChecker();
+
+
+    public void testNullString()
+    {
+        assertFalse( checker.isValidSyntax( null ) );
+    }
+
+
+    public void testEmptyString()
+    {
+        assertFalse( checker.isValidSyntax( "" ) );
+    }
+
+
+    public void testOneCharString()
+    {
+        assertFalse( checker.isValidSyntax( "A" ) );
+        assertFalse( checker.isValidSyntax( "1" ) );
+        assertFalse( checker.isValidSyntax( "-" ) );
+        assertFalse( checker.isValidSyntax( "(" ) );
+    }
+
+
+    public void testValid()
+    {
+        assertTrue( checker.isValidSyntax( ( "( 1.3.6.1.4.1.1466.115.121.1.15 )" ) ) );
+        assertTrue( checker.isValidSyntax( ( "( 1.3.6.1.4.1.1466.115.121.1.15 DESC 'Directory String' )" ) ) );
+        assertTrue( checker.isValidSyntax( ( "( 1.3.6.1.4.1.1466.115.121.1.15 DESC 'Directory String' X-ABC-DEF 'test' )" ) ) );
+
+        // spaces
+        assertTrue( checker.isValidSyntax( "(1.3.6.1.4.1.1466.115.121.1.15)" ) );
+        assertTrue( checker.isValidSyntax( "(      1.3.6.1.4.1.1466.115.121.1.15        DESC 'Directory String' X-ABC-DEF     'test'     )" ) );
+        
+    }
+
+
+    public void testInvalid()
+    {
+        // 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( "( 1.3.6.1.4.1.1466.115.121.1.15 " ) );
+
+        // missing quotes
+        assertFalse( checker.isValidSyntax( "( 1.3.6.1.4.1.1466.115.121.1.15 DESC Directory String )" ) );
+
+        // lowercase DESC
+        assertFalse( checker.isValidSyntax( "( 1.3.6.1.4.1.1466.115.121.1.15 desc 'Directory String' )" ) );
+
+        // invalid extension
+        assertFalse( checker.isValidSyntax( "( 1.3.6.1.4.1.1466.115.121.1.15 DESC 'Directory String' X-ABC-DEF )" ) );
+        assertFalse( checker.isValidSyntax( "( 1.3.6.1.4.1.1466.115.121.1.15 DESC 'Directory String' X-ABC-123 'test' )" ) );
+        
+    }
+
+}
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 0)
+++ shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserLdapSyntaxDescriptionTest.java	(Revision 0)
@@ -0,0 +1,363 @@
+/*
+ *  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 java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.shared.ldap.schema.syntax.parser.LdapSyntaxDescriptionSchemaParser;
+
+
+/**
+ * Tests the LdapSyntaxDescriptionSchemaParser class.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class SchemaParserLdapSyntaxDescriptionTest extends TestCase
+{
+    /** the parser instance */
+    private LdapSyntaxDescriptionSchemaParser parser;
+
+    /** holds multithreaded success value */
+    boolean isSuccessMultithreaded = true;
+
+
+    protected void setUp() throws Exception
+    {
+        parser = new LdapSyntaxDescriptionSchemaParser();
+    }
+
+
+    protected void tearDown() throws Exception
+    {
+        parser = null;
+    }
+
+
+    /**
+     * Test numericoid
+     * 
+     * @throws ParseException
+     */
+    public void testNumericOid() throws ParseException
+    {
+        String value = null;
+        LdapSyntaxDescription lsd = null;
+
+        // null test
+        value = null;
+        try
+        {
+            parser.parseLdapSyntaxDescription( value );
+            fail( "Exception expected, null" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // no oid
+        value = "( )";
+        try
+        {
+            parser.parseLdapSyntaxDescription( value );
+            fail( "Exception expected, no NUMERICOID" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // simple
+        value = "( 1.1 )";
+        lsd = parser.parseLdapSyntaxDescription( value );
+        assertEquals( "1.1", lsd.getNumericOid() );
+
+        // simple with spaces
+        value = "(          1.1          )";
+        lsd = parser.parseLdapSyntaxDescription( value );
+        assertEquals( "1.1", lsd.getNumericOid() );
+
+        // non-numeric not allowed
+        value = "( cn )";
+        try
+        {
+            parser.parseLdapSyntaxDescription( value );
+            fail( "Exception expected, invalid NUMERICOID top" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // to short
+        value = "( 1 )";
+        try
+        {
+            parser.parseLdapSyntaxDescription( value );
+            fail( "Exception expected, invalid NUMERICOID 1" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // dot only
+        value = "( . )";
+        try
+        {
+            parser.parseLdapSyntaxDescription( value );
+            fail( "Exception expected, invalid NUMERICOID ." );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // ends with dot
+        value = "( 1.1. )";
+        try
+        {
+            parser.parseLdapSyntaxDescription( value );
+            fail( "Exception expected, invalid NUMERICOID 1.1." );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // quotes not allowed
+        value = "( '1.1' )";
+        try
+        {
+            parser.parseLdapSyntaxDescription( value );
+            fail( "Exception expected, invalid NUMERICOID '1.1' (quoted)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+        // leading 0 not allowed
+        value = "( 01.1 )";
+        try
+        {
+            parser.parseLdapSyntaxDescription( value );
+            fail( "Exception expected, invalid NUMERICOID 01.1 (leading zero)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+    }
+
+
+    /**
+     * Tests DESC
+     * 
+     * @throws ParseException
+     */
+    public void testDescription() throws ParseException
+    {
+        String value = null;
+        LdapSyntaxDescription lsd = null;
+
+        // simple
+        value = "(1.1 DESC 'Descripton')";
+        lsd = parser.parseLdapSyntaxDescription( value );
+        assertEquals( "Descripton", lsd.getDescription() );
+
+        // unicode
+        value = "( 1.1 DESC 'Descripton äöüß 部長' )";
+        lsd = parser.parseLdapSyntaxDescription( value );
+        assertEquals( "Descripton äöüß 部長", lsd.getDescription() );
+
+        // lowercase
+        value = "( 1.1 desc 'Descripton' )";
+        try
+        {
+            parser.parseLdapSyntaxDescription( value );
+            fail( "Exception expected, DESC is lowercase" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+    }
+
+
+    /**
+     * Test extensions.
+     * 
+     * @throws ParseException
+     */
+    public void testExtensions() throws ParseException
+    {
+        String value = null;
+        LdapSyntaxDescription lsd = null;
+
+        // no extension
+        value = "( 1.1 )";
+        lsd = parser.parseLdapSyntaxDescription( value );
+        assertEquals( 0, lsd.getExtensions().size() );
+
+        // single extension with one value
+        value = "( 1.1 X-TEST 'test' )";
+        lsd = parser.parseLdapSyntaxDescription( value );
+        assertEquals( 1, lsd.getExtensions().size() );
+        assertNotNull( lsd.getExtensions().get( "X-TEST" ) );
+        assertEquals( 1, lsd.getExtensions().get( "X-TEST" ).size() );
+        assertEquals( "test", lsd.getExtensions().get( "X-TEST" ).get( 0 ) );
+
+        // single extension with multiple values
+        value = "( 1.1 X-TEST-ABC ('test1' 'test äöüß'       'test 部長' ) )";
+        lsd = parser.parseLdapSyntaxDescription( value );
+        assertEquals( 1, lsd.getExtensions().size() );
+        assertNotNull( lsd.getExtensions().get( "X-TEST-ABC" ) );
+        assertEquals( 3, lsd.getExtensions().get( "X-TEST-ABC" ).size() );
+        assertEquals( "test1", lsd.getExtensions().get( "X-TEST-ABC" ).get( 0 ) );
+        assertEquals( "test äöüß", lsd.getExtensions().get( "X-TEST-ABC" ).get( 1 ) );
+        assertEquals( "test 部長", lsd.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'))";
+        lsd = parser.parseLdapSyntaxDescription( value );
+        assertEquals( 2, lsd.getExtensions().size() );
+        assertNotNull( lsd.getExtensions().get( "X-TEST-a" ) );
+        assertEquals( 2, lsd.getExtensions().get( "X-TEST-a" ).size() );
+        assertEquals( "test1-1", lsd.getExtensions().get( "X-TEST-a" ).get( 0 ) );
+        assertEquals( "test1-2", lsd.getExtensions().get( "X-TEST-a" ).get( 1 ) );
+        assertNotNull( lsd.getExtensions().get( "X-TEST-b" ) );
+        assertEquals( 2, lsd.getExtensions().get( "X-TEST-b" ).size() );
+        assertEquals( "test2-1", lsd.getExtensions().get( "X-TEST-b" ).get( 0 ) );
+        assertEquals( "test2-2", lsd.getExtensions().get( "X-TEST-b" ).get( 1 ) );
+
+        // invalid extension, no number allowed
+        value = "( 1.1 X-TEST1 'test' )";
+        try
+        {
+            lsd = parser.parseLdapSyntaxDescription( value );
+            fail( "Exception expected, invalid extension X-TEST1 (no number allowed)" );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
+
+    }
+
+
+    ////////////////////////////////////////////////////////////////
+    //         Some real-world attribute type definitions         //
+    ////////////////////////////////////////////////////////////////
+
+    public void testRfcBinary() throws ParseException
+    {
+        String value = "( 1.3.6.1.4.1.1466.115.121.1.5 DESC 'Binary' X-NOT-HUMAN-READABLE 'TRUE' )";
+        LdapSyntaxDescription lsd = parser.parseLdapSyntaxDescription( value );
+
+        assertEquals( "1.3.6.1.4.1.1466.115.121.1.5", lsd.getNumericOid() );
+        assertEquals( "Binary", lsd.getDescription() );
+        assertEquals( 1, lsd.getExtensions().size() );
+        assertNotNull( lsd.getExtensions().get( "X-NOT-HUMAN-READABLE" ) );
+        assertEquals( 1, lsd.getExtensions().get( "X-NOT-HUMAN-READABLE" ).size() );
+        assertEquals( "TRUE", lsd.getExtensions().get( "X-NOT-HUMAN-READABLE" ).get( 0 ) );
+    }
+
+
+    /**
+     * Tests the multithreaded use of a single parser.
+     */
+    public void testMultiThreaded() throws Exception
+    {
+        // start up and track all threads (40 threads)
+        List<Thread> threads = new ArrayList<Thread>();
+        for ( int ii = 0; ii < 10; ii++ )
+        {
+            Thread t0 = new Thread( new ParseSpecification( "( 1.1 )" ) );
+            Thread t1 = new Thread( new ParseSpecification(
+                "( 1.3.6.1.4.1.1466.115.121.1.5 DESC 'Binary' X-NOT-HUMAN-READABLE 'TRUE' )" ) );
+            Thread t2 = new Thread( new ParseSpecification( "( 1.3.6.1.4.1.1466.115.121.1.7 DESC 'Boolean' )" ) );
+            Thread t3 = new Thread( new ParseSpecification( "( 1.3.6.1.4.1.1466.115.121.1.36 DESC 'Numeric String' )" ) );
+            threads.add( t0 );
+            threads.add( t1 );
+            threads.add( t2 );
+            threads.add( t3 );
+            t0.start();
+            t1.start();
+            t2.start();
+            t3.start();
+        }
+
+        // wait until all threads have died
+        boolean hasLiveThreads = false;
+        do
+        {
+            hasLiveThreads = false;
+
+            for ( int ii = 0; ii < threads.size(); ii++ )
+            {
+                Thread t = ( Thread ) threads.get( ii );
+                hasLiveThreads = hasLiveThreads || t.isAlive();
+            }
+        }
+        while ( hasLiveThreads );
+
+        // check that no one thread failed to parse and generate a SS object
+        assertTrue( isSuccessMultithreaded );
+    }
+
+    /**
+     * Used to test multithreaded use of a single parser.
+     */
+    class ParseSpecification implements Runnable
+    {
+        private final String lsd;
+
+        LdapSyntaxDescription result;
+
+
+        public ParseSpecification( String lsd )
+        {
+            this.lsd = lsd;
+        }
+
+
+        public void run()
+        {
+            try
+            {
+                result = parser.parseLdapSyntaxDescription( lsd );
+            }
+            catch ( ParseException e )
+            {
+                e.printStackTrace();
+            }
+
+            isSuccessMultithreaded = isSuccessMultithreaded && ( result != null );
+        }
+    }
+
+}
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 485856)
+++ shared/ldap/src/test/java/org/apache/directory/shared/ldap/schema/syntax/SchemaParserObjectClassDescriptionTest.java	(Arbeitskopie)
@@ -95,6 +95,11 @@
         ocd = parser.parseObjectClassDescription( value );
         assertEquals( "1.1", ocd.getNumericOid() );
 
+        // simple
+        value = "( 0.0 )";
+        ocd = parser.parseObjectClassDescription( value );
+        assertEquals( "0.0", ocd.getNumericOid() );
+        
         // simple with spaces
         value = "(          1.1          )";
         ocd = parser.parseObjectClassDescription( value );
@@ -159,6 +164,18 @@
         {
             // expected
         }
+        
+        // leading 0
+        value = "( 01.1 )";
+        try
+        {
+            parser.parseObjectClassDescription( value );
+            fail( "Exception expected, invalid NUMERICOID 01.1." );
+        }
+        catch ( ParseException pe )
+        {
+            // expected
+        }
     }
 
 
Index: shared/ldap/src/main/antlr/schema-extension.g
===================================================================
--- shared/ldap/src/main/antlr/schema-extension.g	(Revision 485296)
+++ shared/ldap/src/main/antlr/schema-extension.g	(Arbeitskopie)
@@ -81,8 +81,8 @@
      */
 extension returns [AntlrSchemaParser.Extension extension = new AntlrSchemaParser.Extension()]
     :
-    ( xkey:XKEY { extension.setKey(xkey.getText()); } )
-    ( xvalues:XVALUES { extension.setValues(qdstrings(xvalues.getText())); } )
+    ( xkey:XKEY { extension.key = xkey.getText(); } )
+    ( xvalues:XVALUES { extension.values = qdstrings(xvalues.getText()); } )
     ;
     
     
Index: shared/ldap/src/main/antlr/schema-value.g
===================================================================
--- shared/ldap/src/main/antlr/schema-value.g	(Revision 485296)
+++ shared/ldap/src/main/antlr/schema-value.g	(Arbeitskopie)
@@ -34,7 +34,7 @@
 class AntlrSchemaValueLexer extends Lexer;
 
 options    {
-    k = 2 ;
+    k = 3 ;
     exportVocab=AntlrSchemaValue ;
     charVocabulary = '\3'..'\377' ;
     caseSensitive = true ;
@@ -46,20 +46,19 @@
 
 LPAR : '(' ;
 RPAR : ')' ;
+protected LDIGIT : '1'..'9' ;
+protected DIGIT : '0'..'9' ; 
+protected NUMBER : DIGIT | ( LDIGIT (DIGIT)+ ) ;
 
 QUOTE : '\'' ;
 DOLLAR : '$' ;
-LBRACKET : '{' ;
-RBRACKET : '}' ;
-LEN : LBRACKET (DIGIT)+ RBRACKET ;
-DIGIT : ('0'..'9') ; 
-NUMERICOID : ('0'..'9')+ ( '.' ('0'..'9')+ )+ ;
+LCURLY : '{' ;
+RCURLY : '}' ;
+NUMERICOID : NUMBER ( '.' NUMBER )+ ;
 DESCR : ( 'a'..'z' | 'A'..'Z' ) ( 'a'..'z' | 'A'..'Z' | '0'..'9' | '-' )* ;
+LEN : LCURLY n:NUMBER RCURLY { setText(n.getText()); } ;
 
 
-
-
-
 /**
  * An antlr generated schema parser. This is a sub-parser used to parse
  * numericoid, oid, oids, qdescr, qdescrs according to RFC4512.
@@ -76,6 +75,21 @@
 
 
     /**
+     * noidlen = numericoid [ LCURLY len RCURLY ]
+     * len = number
+     */
+noidlen returns [AntlrSchemaParser.NoidLen noidlen = new AntlrSchemaParser.NoidLen()]
+    :
+    ( 
+      o:NUMERICOID { noidlen.noid = o.getText(); } 
+      (
+        l:LEN { noidlen.len = Integer.parseInt(l.getText()); } 
+      )?
+    )
+    ;
+
+
+    /**
      * numericoid = number 1*( DOT number )
      */
 numericoid returns [String numericoid=null]
Index: shared/ldap/src/main/antlr/schema.g
===================================================================
--- shared/ldap/src/main/antlr/schema.g	(Revision 485296)
+++ shared/ldap/src/main/antlr/schema.g	(Arbeitskopie)
@@ -52,11 +52,6 @@
 
 LEN : LBRACKET ('0'..'9')+ RBRACKET ;
 
-USERAPPLICATIONS : "userApplications" ;
-DIRECTORYOPERATION : "directoryOperation" ;
-DISTRIBUTEDOPERATION : "distributedOperation" ;
-DSAOPERATION : "dSAOperation" ;
-
 SINGLE_VALUE : ( "SINGLE-VALUE" (WHSP)? ) ;
 COLLECTIVE : ( "COLLECTIVE" (WHSP)? ) ;
 NO_USER_MODIFICATION : ( "NO-USER-MODIFICATION" (WHSP)? ) ;
@@ -76,7 +71,6 @@
 ORDERING : ( "ORDERING" WHSP ordering:VALUES ) { setText(ordering.getText().trim()); } ;
 SUBSTR : ( "SUBSTR" WHSP substr:VALUES ) { setText(substr.getText().trim()); } ;
 SYNTAX : ( "SYNTAX" WHSP syntax:VALUES (len:LEN)? ) { setText(syntax.getText().trim() + (len!=null?len.getText().trim():"")); } ;
-USAGE : ( "USAGE" WHSP op:VALUES ) { setText(op.getText().trim()); } ;
 APPLIES : ( "APPLIES" WHSP applies:VALUES ) { setText(applies.getText().trim()); } ;
 EXTENSION : x:( "X-" ( 'a'..'z' | 'A'..'Z' | '-' | '_' )+ WHSP VALUES ) ; 
 
@@ -85,6 +79,11 @@
 protected UNQUOTED_STRING : (options{greedy=true;}: 'a'..'z' | 'A'..'Z' | '0'..'9' | '-' | ';' | '.' )+ ;
 protected QUOTED_STRING : ( QUOTE (~'\'')* QUOTE ) ;
 
+USAGE : ( "USAGE" (WHSP)? ) ;
+USER_APPLICATIONS : ( "userApplications" (WHSP)? ) ;
+DIRECTORY_OPERATION : ( "directoryOperation" (WHSP)? ) ;
+DISTRIBUTED_OPERATION : ( "distributedOperation" (WHSP)? ) ;
+DSA_OPERATION : ( "dSAOperation" (WHSP)? ) ;
 
 /**
  * An antlr generated schema main parser.
@@ -102,44 +101,19 @@
 {
 	static class Extension
 	{
-	
-	    private String key;
+	    String key = "";
+	    List<String> values = new ArrayList<String>();
 	    
-	    private List<String> values;
-	    
-	    public Extension()
-	    {
-	        this.key = "";
-	        this.values = new ArrayList<String>();
-	    }
-	
-	    public String getKey()
-	    {
-	        return key;
-	    }
-	
-	    public void setKey( String key )
-	    {
-	        this.key = key;
-	    }
-	
-	    public List<String> getValues()
-	    {
-	        return values;
-	    }
-	
-	    public void setValues( List<String> values )
-	    {
-	        this.values = values;
-	    }
-	    
-	    
 	    public void addValue( String value )
 	    {
 	        this.values.add( value );
 	    }
-	    
 	}
+	static class NoidLen
+	{
+	    String noid = "";
+	    int len = 0;
+	}
 }
 
 
@@ -167,7 +141,7 @@
     */
 objectClassDescription returns [ObjectClassDescription ocd = new ObjectClassDescription()]
     :
-    ( oid:STARTNUMERICOID { ocd.setOid(numericoid(oid.getText())); } )
+    ( oid:STARTNUMERICOID { ocd.setNumericOid(numericoid(oid.getText())); } )
     (
 	    ( name:NAME { ocd.setNames(qdescrs(name.getText())); } )
 	    |
@@ -190,13 +164,128 @@
 	    |
 	    ( extension:EXTENSION { 
 	        Extension ex = extension(extension.getText());
-	        ocd.addExtension(ex.getKey(), ex.getValues()); 
+	        ocd.addExtension(ex.key, ex.values); 
 	     } )
 	)*    
     RPAR
     ;
 
 
+    /**
+     * Production for matching attribute type descriptions. It is fault-tolerant
+     * against element ordering.
+     *
+     * <pre>
+     * AttributeTypeDescription = LPAREN WSP
+     *     numericoid                    ; object identifier
+     *     [ SP "NAME" SP qdescrs ]      ; short names (descriptors)
+     *     [ SP "DESC" SP qdstring ]     ; description
+     *     [ SP "OBSOLETE" ]             ; not active
+     *     [ SP "SUP" SP oid ]           ; supertype
+     *     [ SP "EQUALITY" SP oid ]      ; equality matching rule
+     *     [ SP "ORDERING" SP oid ]      ; ordering matching rule
+     *     [ SP "SUBSTR" SP oid ]        ; substrings matching rule
+     *     [ SP "SYNTAX" SP noidlen ]    ; value syntax
+     *     [ SP "SINGLE-VALUE" ]         ; single-value
+     *     [ SP "COLLECTIVE" ]           ; collective
+     *     [ SP "NO-USER-MODIFICATION" ] ; not user modifiable
+     *     [ SP "USAGE" SP usage ]       ; usage
+     *     extensions WSP RPAREN         ; extensions
+     * 
+     * usage = "userApplications"     /  ; user
+     *         "directoryOperation"   /  ; directory operational
+     *         "distributedOperation" /  ; DSA-shared operational
+     *         "dSAOperation"            ; DSA-specific operational     
+     * 
+     * extensions = *( SP xstring SP qdstrings )
+     * xstring = "X" HYPHEN 1*( ALPHA / HYPHEN / USCORE ) 
+     * </pre>
+    */
+attributeTypeDescription returns [AttributeTypeDescription atd = new AttributeTypeDescription()]
+    :
+    ( oid:STARTNUMERICOID { atd.setNumericOid(numericoid(oid.getText())); } )
+    (
+	    ( name:NAME { atd.setNames(qdescrs(name.getText())); } )
+	    |
+	    ( desc:DESC { atd.setDescription(qdstring(desc.getText())); } )
+	    |
+	    ( OBSOLETE { atd.setObsolete( true ); } )
+	    |
+	    ( sup:SUP { atd.setSuperType(oid(sup.getText())); } )
+	    |
+        ( equality:EQUALITY { atd.setEqualityMatchingRule(oid(equality.getText())); } )
+        |
+        ( ordering:ORDERING { atd.setOrderingMatchingRule(oid(ordering.getText())); } )
+        |
+        ( substr:SUBSTR { atd.setSubstringsMatchingRule(oid(substr.getText())); } )
+        |
+        ( syntax:SYNTAX { 
+            NoidLen noidlen = noidlen(syntax.getText());
+            atd.setSyntax(noidlen.noid); 
+            atd.setSyntaxLength(noidlen.len);
+          } )
+        |
+        ( SINGLE_VALUE { atd.setSingleValued( true ); } )
+        |
+        ( COLLECTIVE { atd.setCollective( true ); } )
+        |
+        ( NO_USER_MODIFICATION { atd.setUserModifiable( false ); } )
+        |
+	    ( USAGE (WHSP)* USER_APPLICATIONS { atd.setUsage( AttributeTypeDescription.Usage.USER_APPLICATIONS ); }
+	      |
+	      USAGE DIRECTORY_OPERATION { atd.setUsage( AttributeTypeDescription.Usage.DIRECTORY_OPERATION ); }
+	      |
+	      USAGE DISTRIBUTED_OPERATION { atd.setUsage( AttributeTypeDescription.Usage.DISTRIBUTED_OPERATION ); } 
+	      |
+	      USAGE DSA_OPERATION { atd.setUsage( AttributeTypeDescription.Usage.DSA_OPERATION ); } 
+	    )
+	    |
+	    ( extension:EXTENSION { 
+	        Extension ex = extension(extension.getText());
+	        atd.addExtension(ex.key, ex.values); 
+	     } )
+	)*    
+    RPAR
+    ;
+
+
+    /**
+     * Production for matching ldap syntax descriptions. It is fault-tolerant
+     * against element ordering.
+     *
+     * <pre>
+     * SyntaxDescription = LPAREN WSP
+     *    numericoid                 ; object identifier
+     *    [ SP "DESC" SP qdstring ]  ; description
+     *    extensions WSP RPAREN      ; extensions
+     * </pre>
+    */
+ldapSyntaxDescription returns [LdapSyntaxDescription lsd = new LdapSyntaxDescription()]
+     :
+    ( oid:STARTNUMERICOID { lsd.setNumericOid(numericoid(oid.getText())); } )
+    (
+	    ( desc:DESC { lsd.setDescription(qdstring(desc.getText())); } )
+	    |
+	    ( extension:EXTENSION { 
+	        Extension ex = extension(extension.getText());
+	        lsd.addExtension(ex.key, ex.values); 
+	     } )
+    )*
+    RPAR
+    ;
+
+
+noidlen [String s] returns [NoidLen noidlen]
+    {
+        noidlen = new NoidLen();
+        AntlrSchemaValueLexer lexer = new AntlrSchemaValueLexer(new StringReader(s));
+        AntlrSchemaValueParser parser = new AntlrSchemaValueParser(lexer);
+        noidlen = parser.noidlen();
+    }
+    :
+    ;
+
+
 extension [String s] returns [Extension extension]
     {
         extension = new Extension();
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 0)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/AttributeTypeDescriptionSyntaxChecker.java	(Revision 0)
@@ -0,0 +1,167 @@
+/*
+ *  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.AttributeTypeDescriptionSchemaParser;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+
+/**
+ * A SyntaxChecker which verifies that a value follows the
+ * attribute type descripton syntax according to RFC 4512, par 4.2.2:
+ * 
+*  <pre>
+ * AttributeTypeDescription = LPAREN WSP
+ *     numericoid                    ; object identifier
+ *     [ SP "NAME" SP qdescrs ]      ; short names (descriptors)
+ *     [ SP "DESC" SP qdstring ]     ; description
+ *     [ SP "OBSOLETE" ]             ; not active
+ *     [ SP "SUP" SP oid ]           ; supertype
+ *     [ SP "EQUALITY" SP oid ]      ; equality matching rule
+ *     [ SP "ORDERING" SP oid ]      ; ordering matching rule
+ *     [ SP "SUBSTR" SP oid ]        ; substrings matching rule
+ *     [ SP "SYNTAX" SP noidlen ]    ; value syntax
+ *     [ SP "SINGLE-VALUE" ]         ; single-value
+ *     [ SP "COLLECTIVE" ]           ; collective
+ *     [ SP "NO-USER-MODIFICATION" ] ; not user modifiable
+ *     [ SP "USAGE" SP usage ]       ; usage
+ *     extensions WSP RPAREN         ; extensions
+ * 
+ * usage = "userApplications"     /  ; user
+ *         "directoryOperation"   /  ; directory operational
+ *         "distributedOperation" /  ; DSA-shared operational
+ *         "dSAOperation"            ; DSA-specific operational     
+ * 
+ * extensions = *( SP xstring SP qdstrings )
+ * xstring = "X" HYPHEN 1*( ALPHA / HYPHEN / USCORE ) 
+ * 
+ * Each attribute type description must contain at least one of the SUP
+ * or SYNTAX fields. 
+ * 
+ * COLLECTIVE requires usage userApplications.
+ * 
+ * NO-USER-MODIFICATION requires an operational usage.
+ * 
+ * 
+ * </pre>
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class AttributeTypeDescriptionSyntaxChecker implements SyntaxChecker
+{
+
+    /** The Syntax OID, according to RFC 4517, par. 3.3.1 */
+    public static final String OID = "1.3.6.1.4.1.1466.115.121.1.3";
+
+	/** The schema parser used to parse the AttributeTypeDescription Syntax */
+    private AttributeTypeDescriptionSchemaParser schemaParser = new AttributeTypeDescriptionSchemaParser();
+
+
+    /**
+     * 
+     * Creates a new instance of AttributeTypeDescriptionSchemaParser.
+     *
+     */
+    public AttributeTypeDescriptionSyntaxChecker()
+    {
+    }
+
+
+    /* (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
+        {
+            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() != AttributeTypeDescription.Usage.USER_APPLICATIONS) {
+                return false;
+            }
+            
+            // NO-USER-MODIFICATION requires an operational usage.
+            if(!atd.isUserModifiable() && atd.getUsage() == AttributeTypeDescription.Usage.USER_APPLICATIONS) {
+                return false;
+            }
+                
+            return true;
+        }
+        catch ( ParseException pe )
+        {
+            return false;
+        }
+    }
+}
Index: shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/LdapSyntaxDescriptionSyntaxChecker.java
===================================================================
--- shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/LdapSyntaxDescriptionSyntaxChecker.java	(Revision 0)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/LdapSyntaxDescriptionSyntaxChecker.java	(Revision 0)
@@ -0,0 +1,123 @@
+/*
+ *  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.LdapSyntaxDescriptionSchemaParser;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+
+/**
+ * A SyntaxChecker which verifies that a value follows the
+ * LDAP syntax descripton syntax according to RFC 4512, par 4.2.2:
+ * 
+ * <pre>
+ * SyntaxDescription = LPAREN WSP
+ *    numericoid                 ; object identifier
+ *    [ SP "DESC" SP qdstring ]  ; description
+ *    extensions WSP RPAREN      ; extensions
+ * </pre>
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class LdapSyntaxDescriptionSyntaxChecker implements SyntaxChecker
+{
+
+    /** The Syntax OID, according to RFC 4517, par. 3.3.18 */
+    public static final String OID = "1.3.6.1.4.1.1466.115.121.1.54";
+
+	/** The schema parser used to parse the LdapSyntaxDescription Syntax */
+    private LdapSyntaxDescriptionSchemaParser schemaParser = new LdapSyntaxDescriptionSchemaParser();
+
+
+    /**
+     * 
+     * Creates a new instance of AttributeTypeDescriptionSchemaParser.
+     *
+     */
+    public LdapSyntaxDescriptionSyntaxChecker()
+    {
+    }
+
+
+    /* (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.parseLdapSyntaxDescription( strValue );
+            return true;
+        }
+        catch ( ParseException pe )
+        {
+            return false;
+        }
+    }
+}
Index: shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/ObjectClassDescription.java
===================================================================
--- shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/ObjectClassDescription.java	(Revision 485856)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/ObjectClassDescription.java	(Arbeitskopie)
@@ -39,7 +39,7 @@
         ABSTRACT, STRUCTURAL, AUXILIARY
     }
 
-    private String oid;
+    private String numericOid;
 
     private List<String> names;
 
@@ -60,7 +60,7 @@
 
     public ObjectClassDescription()
     {
-        this.oid = "";
+        this.numericOid = "";
         names = new ArrayList<String>();
         description = "";
         isObsolete = false;
@@ -146,13 +146,13 @@
 
     public String getNumericOid()
     {
-        return oid;
+        return numericOid;
     }
 
 
-    public void setOid( String oid )
+    public void setNumericOid( String numericOid )
     {
-        this.oid = oid;
+        this.numericOid = numericOid;
     }
 
 
Index: shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/AttributeTypeDescription.java
===================================================================
--- shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/AttributeTypeDescription.java	(Revision 0)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/AttributeTypeDescription.java	(Revision 0)
@@ -0,0 +1,278 @@
+/*
+ *  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.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+ 
+ 
+/**
+ * RFC 4512 - 4.1.2. Attribute Types
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class AttributeTypeDescription
+{
+    public static enum Usage
+    {
+        USER_APPLICATIONS, DIRECTORY_OPERATION, DISTRIBUTED_OPERATION, DSA_OPERATION
+    }
+ 
+    private String numericOid;
+ 
+    private List<String> names;
+ 
+    private String description;
+ 
+    private boolean isObsolete;
+ 
+    private String superType;
+ 
+    private String equalityMatchingRule;
+ 
+    private String orderingMatchingRule;
+ 
+    private String substringsMatchingRule;
+ 
+    private String syntax;
+ 
+    private int syntaxLength;
+ 
+    private boolean isSingleValued;
+ 
+    private boolean isCollective;
+ 
+    private boolean isUserModifiable;
+ 
+    private Usage usage;
+ 
+    private Map<String, List<String>> extensions;
+ 
+ 
+    public AttributeTypeDescription( )
+    {
+        this.numericOid = "";
+        names = new ArrayList<String>();
+        description = "";
+        isObsolete = false;
+        superType = null;
+        equalityMatchingRule = null;
+        orderingMatchingRule = null;
+        substringsMatchingRule = null;
+        syntax = null;
+        syntaxLength = 0;
+        isSingleValued = false;
+        isCollective = false;
+        isUserModifiable = true;
+        usage = Usage.USER_APPLICATIONS;
+        extensions = new LinkedHashMap<String, List<String>>();;
+    }
+ 
+ 
+    public String getDescription()
+    {
+        return description;
+    }
+ 
+ 
+    public void setDescription( String description )
+    {
+        this.description = description;
+    }
+ 
+ 
+    public String getEqualityMatchingRule()
+    {
+        return equalityMatchingRule;
+    }
+ 
+ 
+    public void setEqualityMatchingRule( String equalityMatchingRule )
+    {
+        this.equalityMatchingRule = equalityMatchingRule;
+    }
+ 
+ 
+    public Map<String, List<String>> getExtensions()
+    {
+        return extensions;
+    }
+ 
+ 
+    public void setExtensions( Map<String, List<String>> extensions )
+    {
+        this.extensions = extensions;
+    }
+ 
+ 
+    public boolean isCollective()
+    {
+        return isCollective;
+    }
+ 
+ 
+    public void setCollective( boolean isCollective )
+    {
+        this.isCollective = isCollective;
+    }
+ 
+ 
+    public boolean isObsolete()
+    {
+        return isObsolete;
+    }
+ 
+ 
+    public void setObsolete( boolean isObsolete )
+    {
+        this.isObsolete = isObsolete;
+    }
+ 
+ 
+    public boolean isUserModifiable()
+    {
+        return isUserModifiable;
+    }
+ 
+ 
+    public void setUserModifiable( boolean isUserModifiable )
+    {
+        this.isUserModifiable = isUserModifiable;
+    }
+ 
+ 
+    public List<String> getNames()
+    {
+        return names;
+    }
+ 
+ 
+    public void setNames( List<String> names )
+    {
+        this.names = names;
+    }
+ 
+ 
+    public String getNumericOid()
+    {
+        return numericOid;
+    }
+ 
+ 
+    public void setNumericOid( String oid )
+    {
+        this.numericOid = oid;
+    }
+ 
+ 
+    public String getOrderingMatchingRule()
+    {
+        return orderingMatchingRule;
+    }
+ 
+ 
+    public void setOrderingMatchingRule( String orderingMatchingRule )
+    {
+        this.orderingMatchingRule = orderingMatchingRule;
+    }
+ 
+ 
+    public boolean isSingleValued()
+    {
+        return isSingleValued;
+    }
+ 
+ 
+    public void setSingleValued( boolean singleValued )
+    {
+        this.isSingleValued = singleValued;
+    }
+ 
+ 
+    public String getSubstringsMatchingRule()
+    {
+        return substringsMatchingRule;
+    }
+ 
+ 
+    public void setSubstringsMatchingRule( String substringsMatchingRule )
+    {
+        this.substringsMatchingRule = substringsMatchingRule;
+    }
+ 
+ 
+    public String getSuperType()
+    {
+        return superType;
+    }
+ 
+ 
+    public void setSuperType( String superType )
+    {
+        this.superType = superType;
+    }
+ 
+ 
+    public String getSyntax()
+    {
+        return syntax;
+    }
+ 
+ 
+    public void setSyntax( String syntax )
+    {
+        this.syntax = syntax;
+    }
+ 
+ 
+    public int getSyntaxLength()
+    {
+        return syntaxLength;
+    }
+ 
+ 
+    public void setSyntaxLength( int syntaxLenght )
+    {
+        this.syntaxLength = syntaxLenght;
+    }
+ 
+ 
+    public Usage getUsage()
+    {
+        return usage;
+    }
+ 
+ 
+    public void setUsage( Usage usage )
+    {
+        this.usage = usage;
+    }
+    
+    public void addExtension( String key, List<String> values )
+    {
+        this.extensions.put( key, values );
+    }
+ 
+}
\ Kein Zeilenvorschub am Ende der Datei
Index: shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/ReusableAntlrSchemaParser.java
===================================================================
--- shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/ReusableAntlrSchemaParser.java	(Revision 0)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/ReusableAntlrSchemaParser.java	(Revision 0)
@@ -0,0 +1,38 @@
+package org.apache.directory.shared.ldap.schema.syntax.parser;
+
+import org.apache.directory.shared.ldap.schema.syntax.AntlrSchemaParser;
+
+import antlr.TokenStream;
+
+/**
+ * A reusable parser class extended from antlr generated parser for an LDAP
+ * schema as defined in RFC 4512. This class
+ * enables the reuse of the antlr parser without having to recreate the it every
+ * time as stated in <a
+ * href="http://www.antlr.org:8080/pipermail/antlr-interest/2003-April/003631.html">
+ * a Antlr Interest Group mail</a> .
+ * 
+ * @see <a href="http://www.faqs.org/rfcs/rfc3672.html">RFC 3672</a>
+ */
+class ReusableAntlrSchemaParser extends AntlrSchemaParser
+{
+    /**
+     * Creates a ReusableAntlrSchemaParser instance.
+     */
+    public ReusableAntlrSchemaParser( TokenStream lexer )
+    {
+        super( lexer );
+    }
+
+
+    /**
+     * Resets the state of an antlr parser.
+     */
+    public void resetState()
+    {
+        // no set method for this protected field.
+        this.traceDepth = 0;
+
+        this.getInputState().reset();
+    }
+}
\ Kein Zeilenvorschub am Ende der Datei
Index: shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/ReusableAntlrSchemaLexer.java
===================================================================
--- shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/ReusableAntlrSchemaLexer.java	(Revision 0)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/ReusableAntlrSchemaLexer.java	(Revision 0)
@@ -0,0 +1,58 @@
+package org.apache.directory.shared.ldap.schema.syntax.parser;
+
+import java.io.Reader;
+
+import org.apache.directory.shared.ldap.schema.syntax.AntlrSchemaLexer;
+
+import antlr.CharBuffer;
+import antlr.LexerSharedInputState;
+
+/**
+ * A reusable lexer class extended from antlr generated lexer for an LDAP
+ * schema as defined in RFC 4512. This class
+ * enables the reuse of the antlr lexer without having to recreate the it every
+ * time as stated in <a
+ * href="http://www.antlr.org:8080/pipermail/antlr-interest/2003-April/003631.html">
+ * a Antlr Interest Group mail</a> .
+ * 
+ * @see <a href="http://www.faqs.org/rfcs/rfc3672.html">RFC 3672</a>
+ */
+class ReusableAntlrSchemaLexer extends AntlrSchemaLexer
+{
+    private boolean savedCaseSensitive;
+
+    private boolean savedCaseSensitiveLiterals;
+
+
+    /**
+     * Creates a ReusableAntlrSchemaLexer instance.
+     * 
+     * @param in
+     *            the input to the lexer
+     */
+    public ReusableAntlrSchemaLexer( Reader in )
+    {
+        super( in );
+        savedCaseSensitive = getCaseSensitive();
+        savedCaseSensitiveLiterals = getCaseSensitiveLiterals();
+    }
+
+
+    /**
+     * Resets the state of an antlr lexer and initializes it with new input.
+     * 
+     * @param in
+     *            the input to the lexer
+     */
+    public void prepareNextInput( Reader in )
+    {
+        CharBuffer buf = new CharBuffer( in );
+        LexerSharedInputState state = new LexerSharedInputState( buf );
+        this.setInputState( state );
+
+        this.setCaseSensitive( savedCaseSensitive );
+
+        // no set method for this protected field.
+        this.caseSensitiveLiterals = savedCaseSensitiveLiterals;
+    }
+}
\ Kein Zeilenvorschub am Ende der Datei
Index: shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/AttributeTypeDescriptionSchemaParser.java
===================================================================
--- shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/AttributeTypeDescriptionSchemaParser.java	(Revision 0)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/AttributeTypeDescriptionSchemaParser.java	(Revision 0)
@@ -0,0 +1,137 @@
+/*
+ *  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.io.StringReader;
+import java.text.ParseException;
+
+import org.apache.directory.shared.ldap.schema.syntax.AttributeTypeDescription;
+
+import antlr.RecognitionException;
+import antlr.TokenStreamException;
+
+
+/**
+ * A parser for RFC 4512 attribute type descriptions.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class AttributeTypeDescriptionSchemaParser
+{
+
+    /** the antlr generated parser being wrapped */
+    private ReusableAntlrSchemaParser parser;
+
+    /** the antlr generated lexer being wrapped */
+    private ReusableAntlrSchemaLexer lexer;
+    
+    static
+    {
+    }
+
+
+    /**
+     * Creates a schema parser instance.
+     */
+    public AttributeTypeDescriptionSchemaParser()
+    {
+        lexer = new ReusableAntlrSchemaLexer( new StringReader( "" ) );
+        parser = new ReusableAntlrSchemaParser( lexer );
+    }
+    
+    /**
+     * Initializes the plumbing by creating a pipe and coupling the parser/lexer
+     * pair with it. param spec the specification to be parsed
+     */
+    private void reset( String spec )
+    {
+        StringReader in = new StringReader( spec );
+        lexer.prepareNextInput( in );
+        parser.resetState();
+    }
+
+
+    /**
+     * Parses a attribute type description according to RFC 4512:
+     * 
+     * <pre>
+     * AttributeTypeDescription = LPAREN WSP
+     *     numericoid                    ; object identifier
+     *     [ SP "NAME" SP qdescrs ]      ; short names (descriptors)
+     *     [ SP "DESC" SP qdstring ]     ; description
+     *     [ SP "OBSOLETE" ]             ; not active
+     *     [ SP "SUP" SP oid ]           ; supertype
+     *     [ SP "EQUALITY" SP oid ]      ; equality matching rule
+     *     [ SP "ORDERING" SP oid ]      ; ordering matching rule
+     *     [ SP "SUBSTR" SP oid ]        ; substrings matching rule
+     *     [ SP "SYNTAX" SP noidlen ]    ; value syntax
+     *     [ SP "SINGLE-VALUE" ]         ; single-value
+     *     [ SP "COLLECTIVE" ]           ; collective
+     *     [ SP "NO-USER-MODIFICATION" ] ; not user modifiable
+     *     [ SP "USAGE" SP usage ]       ; usage
+     *     extensions WSP RPAREN         ; extensions
+     * 
+     * usage = "userApplications"     /  ; user
+     *         "directoryOperation"   /  ; directory operational
+     *         "distributedOperation" /  ; DSA-shared operational
+     *         "dSAOperation"            ; DSA-specific operational     
+     * 
+     * extensions = *( SP xstring SP qdstrings )
+     * xstring = "X" HYPHEN 1*( ALPHA / HYPHEN / USCORE ) 
+     * </pre>
+     * 
+     * @param attributeTypeDescription the attribute type description to be parsed
+     * @return the parsed AttributeTypeDescription bean
+     * @throws ParseException if there are any recognition errors (bad syntax)
+     */
+    public synchronized AttributeTypeDescription parseAttributeTypeDescription( String attributeTypeDescription )
+        throws ParseException
+    {
+
+        if ( attributeTypeDescription == null )
+        {
+            throw new ParseException( "Null", 0 );
+        }
+
+        reset( attributeTypeDescription ); // reset and initialize the parser / lexer pair
+
+        try
+        {
+            AttributeTypeDescription atd = parser.attributeTypeDescription();
+            return atd;
+        }
+        catch ( RecognitionException re )
+        {
+            String msg = "Parser failure on attribute type description:\n\t" + attributeTypeDescription;
+            msg += "\nAntlr message: " + re.getMessage();
+            msg += "\nAntlr column: " + re.getColumn();
+            throw new ParseException( msg, re.getColumn() );
+        }
+        catch ( TokenStreamException tse )
+        {
+            String msg = "Parser failure on attribute type description:\n\t" + attributeTypeDescription;
+            msg += "\nAntlr message: " + tse.getMessage();
+            throw new ParseException( msg, 0 );
+        }
+
+    }
+
+}
Index: shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/LdapSyntaxDescriptionSchemaParser.java
===================================================================
--- shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/LdapSyntaxDescriptionSchemaParser.java	(Revision 0)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/LdapSyntaxDescriptionSchemaParser.java	(Revision 0)
@@ -0,0 +1,118 @@
+/*
+ *  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.io.StringReader;
+import java.text.ParseException;
+
+import org.apache.directory.shared.ldap.schema.syntax.LdapSyntaxDescription;
+
+import antlr.RecognitionException;
+import antlr.TokenStreamException;
+
+
+/**
+ * A parser for RFC 4512 LDAP syntx descriptions.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class LdapSyntaxDescriptionSchemaParser
+{
+
+    /** the antlr generated parser being wrapped */
+    private ReusableAntlrSchemaParser parser;
+
+    /** the antlr generated lexer being wrapped */
+    private ReusableAntlrSchemaLexer lexer;
+    
+    static
+    {
+    }
+
+
+    /**
+     * Creates a schema parser instance.
+     */
+    public LdapSyntaxDescriptionSchemaParser()
+    {
+        lexer = new ReusableAntlrSchemaLexer( new StringReader( "" ) );
+        parser = new ReusableAntlrSchemaParser( lexer );
+    }
+    
+    /**
+     * Initializes the plumbing by creating a pipe and coupling the parser/lexer
+     * pair with it. param spec the specification to be parsed
+     */
+    private void reset( String spec )
+    {
+        StringReader in = new StringReader( spec );
+        lexer.prepareNextInput( in );
+        parser.resetState();
+    }
+
+
+    /**
+     * Parses a LDAP syntax description according to RFC 4512:
+     * 
+     * <pre>
+     * SyntaxDescription = LPAREN WSP
+     *    numericoid                 ; object identifier
+     *    [ SP "DESC" SP qdstring ]  ; description
+     *    extensions WSP RPAREN      ; extensions
+     * </pre>
+     * 
+     * @param ldapSyntaxDescription the LDAP syntay description to be parsed
+     * @return the parsed LdapSyntaxDescription bean
+     * @throws ParseException if there are any recognition errors (bad syntax)
+     */
+    public synchronized LdapSyntaxDescription parseLdapSyntaxDescription( String ldapSyntaxDescription )
+        throws ParseException
+    {
+
+        if ( ldapSyntaxDescription == null )
+        {
+            throw new ParseException( "Null", 0 );
+        }
+
+        reset( ldapSyntaxDescription ); // reset and initialize the parser / lexer pair
+
+        try
+        {
+            LdapSyntaxDescription lsd = parser.ldapSyntaxDescription();
+            return lsd;
+        }
+        catch ( RecognitionException re )
+        {
+            String msg = "Parser failure on LDAP syntay description:\n\t" + ldapSyntaxDescription;
+            msg += "\nAntlr message: " + re.getMessage();
+            msg += "\nAntlr column: " + re.getColumn();
+            throw new ParseException( msg, re.getColumn() );
+        }
+        catch ( TokenStreamException tse )
+        {
+            String msg = "Parser failure on LDAP syntay description:\n\t" + ldapSyntaxDescription;
+            msg += "\nAntlr message: " + tse.getMessage();
+            throw new ParseException( msg, 0 );
+        }
+
+    }
+
+}
Index: shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/ObjectClassDescriptionSchemaParser.java
===================================================================
--- shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/ObjectClassDescriptionSchemaParser.java	(Revision 485856)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/parser/ObjectClassDescriptionSchemaParser.java	(Arbeitskopie)
@@ -20,23 +20,17 @@
 package org.apache.directory.shared.ldap.schema.syntax.parser;
 
 
-import java.io.Reader;
 import java.io.StringReader;
 import java.text.ParseException;
 
-import org.apache.directory.shared.ldap.schema.syntax.AntlrSchemaLexer;
-import org.apache.directory.shared.ldap.schema.syntax.AntlrSchemaParser;
 import org.apache.directory.shared.ldap.schema.syntax.ObjectClassDescription;
 
-import antlr.CharBuffer;
-import antlr.LexerSharedInputState;
 import antlr.RecognitionException;
-import antlr.TokenStream;
 import antlr.TokenStreamException;
 
 
 /**
- * A parser for RFC 4512 schema objects
+ * A parser for RFC 4512 object class descriptons
  * 
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
@@ -131,87 +125,4 @@
         }
 
     }
-
-    /**
-     * A reusable lexer class extended from antlr generated lexer for an LDAP
-     * schema as defined in RFC 4512. This class
-     * enables the reuse of the antlr lexer without having to recreate the it every
-     * time as stated in <a
-     * href="http://www.antlr.org:8080/pipermail/antlr-interest/2003-April/003631.html">
-     * a Antlr Interest Group mail</a> .
-     * 
-     * @see <a href="http://www.faqs.org/rfcs/rfc3672.html">RFC 3672</a>
-     */
-    private static class ReusableAntlrSchemaLexer extends AntlrSchemaLexer
-    {
-        private boolean savedCaseSensitive;
-
-        private boolean savedCaseSensitiveLiterals;
-
-
-        /**
-         * Creates a ReusableAntlrSchemaLexer instance.
-         * 
-         * @param in
-         *            the input to the lexer
-         */
-        public ReusableAntlrSchemaLexer( Reader in )
-        {
-            super( in );
-            savedCaseSensitive = getCaseSensitive();
-            savedCaseSensitiveLiterals = getCaseSensitiveLiterals();
-        }
-
-
-        /**
-         * Resets the state of an antlr lexer and initializes it with new input.
-         * 
-         * @param in
-         *            the input to the lexer
-         */
-        public void prepareNextInput( Reader in )
-        {
-            CharBuffer buf = new CharBuffer( in );
-            LexerSharedInputState state = new LexerSharedInputState( buf );
-            this.setInputState( state );
-
-            this.setCaseSensitive( savedCaseSensitive );
-
-            // no set method for this protected field.
-            this.caseSensitiveLiterals = savedCaseSensitiveLiterals;
-        }
-    }
-
-    /**
-     * A reusable parser class extended from antlr generated parser for an LDAP
-     * schema as defined in RFC 4512. This class
-     * enables the reuse of the antlr parser without having to recreate the it every
-     * time as stated in <a
-     * href="http://www.antlr.org:8080/pipermail/antlr-interest/2003-April/003631.html">
-     * a Antlr Interest Group mail</a> .
-     * 
-     * @see <a href="http://www.faqs.org/rfcs/rfc3672.html">RFC 3672</a>
-     */
-    private static class ReusableAntlrSchemaParser extends AntlrSchemaParser
-    {
-        /**
-         * Creates a ReusableAntlrSchemaParser instance.
-         */
-        public ReusableAntlrSchemaParser( TokenStream lexer )
-        {
-            super( lexer );
-        }
-
-
-        /**
-         * Resets the state of an antlr parser.
-         */
-        public void resetState()
-        {
-            // no set method for this protected field.
-            this.traceDepth = 0;
-
-            this.getInputState().reset();
-        }
-    }
 }
Index: shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/LdapSyntaxDescription.java
===================================================================
--- shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/LdapSyntaxDescription.java	(Revision 0)
+++ shared/ldap/src/main/java/org/apache/directory/shared/ldap/schema/syntax/LdapSyntaxDescription.java	(Revision 0)
@@ -0,0 +1,94 @@
+/*
+ *  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.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+ 
+ 
+/**
+ * RFC 4512 - 4.1.5. LDAP Syntaxes
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class LdapSyntaxDescription
+{
+ 
+    private String numericOid;
+ 
+    private String description;
+ 
+    private Map<String, List<String>> extensions;
+ 
+ 
+    public LdapSyntaxDescription( )
+    {
+        this.numericOid = "";
+        description = "";
+        extensions = new LinkedHashMap<String, List<String>>();;
+    }
+ 
+ 
+    public String getDescription()
+    {
+        return description;
+    }
+ 
+ 
+    public void setDescription( String description )
+    {
+        this.description = description;
+    }
+ 
+ 
+    public Map<String, List<String>> getExtensions()
+    {
+        return extensions;
+    }
+ 
+ 
+    public void setExtensions( Map<String, List<String>> extensions )
+    {
+        this.extensions = extensions;
+    }
+ 
+ 
+    public String getNumericOid()
+    {
+        return numericOid;
+    }
+ 
+ 
+    public void setNumericOid( String oid )
+    {
+        this.numericOid = oid;
+    }
+ 
+    
+    public void addExtension( String key, List<String> values )
+    {
+        this.extensions.put( key, values );
+    }
+ 
+}
\ Kein Zeilenvorschub am Ende der Datei
