You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by er...@apache.org on 2005/11/08 19:22:34 UTC

svn commit: r331857 - in /directory/shared/ldap/trunk/common/src: antlr/subtree-specification.g test/org/apache/ldap/common/subtree/SubtreeSpecificationParserTest.java

Author: ersiner
Date: Tue Nov  8 10:22:19 2005
New Revision: 331857

URL: http://svn.apache.org/viewcvs?rev=331857&view=rev
Log:
Fix for DIRLDAP-66.
Provided a more flexible grammar for subtreeSpecification where order of components does not matter.

Modified:
    directory/shared/ldap/trunk/common/src/antlr/subtree-specification.g
    directory/shared/ldap/trunk/common/src/test/org/apache/ldap/common/subtree/SubtreeSpecificationParserTest.java

Modified: directory/shared/ldap/trunk/common/src/antlr/subtree-specification.g
URL: http://svn.apache.org/viewcvs/directory/shared/ldap/trunk/common/src/antlr/subtree-specification.g?rev=331857&r1=331856&r2=331857&view=diff
==============================================================================
--- directory/shared/ldap/trunk/common/src/antlr/subtree-specification.g (original)
+++ directory/shared/ldap/trunk/common/src/antlr/subtree-specification.g Tue Nov  8 10:22:19 2005
@@ -36,6 +36,8 @@
 import org.apache.ldap.common.filter.AbstractExprNode;
 import org.apache.ldap.common.subtree.SubtreeSpecification;
 import org.apache.ldap.common.subtree.SubtreeSpecificationModifier;
+import org.apache.ldap.common.util.ComponentsMonitor;
+import org.apache.ldap.common.util.OptionalComponentsMonitor;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -62,8 +64,7 @@
 
 options
 {
-    k = 2;
-
+    k = 1;
     defaultErrorHandler = false;
 }
 
@@ -77,12 +78,14 @@
     private DnParser dnParser;
     
     private boolean isNormalizing = false;
-    NameComponentNormalizer normalizer;
+    private NameComponentNormalizer normalizer;
     
     private Set chopBeforeExclusions = new HashSet();
     private Set chopAfterExclusions = new HashSet();
 
-    SubtreeSpecificationModifier ssModifier = null;
+    private SubtreeSpecificationModifier ssModifier = null;
+    
+    private ComponentsMonitor subtreeSpecificationComponentsMonitor = null;
 
     /**
      * Creates a (normalizing) subordinate DnParser for parsing LocalNames.
@@ -123,6 +126,24 @@
         this.normalizer = normalizer;
         this.isNormalizing = true;
     }
+    
+    private int token2Integer( Token token ) throws RecognitionException
+    {
+        int i = 0;
+        
+        try
+        {
+            i = Integer.parseInt( token.getText());
+        }
+        catch ( NumberFormatException e )
+        {
+            throw new RecognitionException( "Value of INTEGER token " +
+                                            token.getText() +
+                                            " cannot be converted to an Integer" );
+        }
+        
+        return i;
+    }
 }
 
 
@@ -142,311 +163,596 @@
     }
     ;
 
-
 subtreeSpecification returns [SubtreeSpecification ss]
 {
     log.debug( "entered subtreeSpecification()" );
-    // clear out ss and ssModifier in case something is left from the last parse
+    // clear out ss, ssModifier and subtreeSpecificationComponentsMonitor
+    // in case something is left from the last parse
     ss = null;
     ssModifier = new SubtreeSpecificationModifier();
-} :
-    LBRACKET ( SP )*
-        (
-            (
-              ss_base ss_base_follower
-            | ss_specificExclusions ss_specificExclusions_follower
-            | ss_minimum ss_minimum_follower
-            | ss_maximum ss_maximum_follower
-            | ss_specificationFilter
-            )
-            ( SP )*
-        )?
-    RBRACKET
+    subtreeSpecificationComponentsMonitor = new OptionalComponentsMonitor( 
+            new String [] { "base", "specificExclusions", "minimum", "maximum", "specificationFilter" } );
+}
+    :
+    OPEN_CURLY ( SP )*
+        ( subtreeSpecificationComponent ( SP )*
+            ( SEP ( SP )* subtreeSpecificationComponent ( SP )* )* )?
+    CLOSE_CURLY
     {
         ss = ssModifier.getSubtreeSpecification();
     }
     ;
 
-    
-ss_base_follower
-    :
-    ( SEP ( SP )*
-        (
-            ss_specificExclusions ss_specificExclusions_follower
-            | ss_minimum ss_minimum_follower
-            | ss_maximum ss_maximum_follower
-            | ss_specificationFilter
-        )
-    )?
-    ;
-
-    
-ss_specificExclusions_follower
-    :
-    ( SEP ( SP )*
-        (
-            ss_minimum ss_minimum_follower
-            | ss_maximum ss_maximum_follower
-            | ss_specificationFilter
-        )
-    )?
-    ;
-
-    
-ss_minimum_follower
-    :
-    ( SEP ( SP )*
-        (
-            ss_maximum ss_maximum_follower
-            | ss_specificationFilter
-        )
-    )?
-    ;
-
-    
-ss_maximum_follower
+subtreeSpecificationComponent
+{
+    log.debug( "entered subtreeSpecification()" );
+}
     :
-    ( SEP ( SP )*
-        (
-            ss_specificationFilter
-        )
-    )?
+    ss_base
+    {
+        subtreeSpecificationComponentsMonitor.useComponent( "base" );
+    }
+    | ss_specificExclusions
+    {
+        subtreeSpecificationComponentsMonitor.useComponent( "specificExclusions" );
+    }
+    | ss_minimum
+    {
+        subtreeSpecificationComponentsMonitor.useComponent( "minimum" );
+    }
+    | ss_maximum
+    {
+        subtreeSpecificationComponentsMonitor.useComponent( "maximum" );
+    }
+    | ss_specificationFilter
+    {
+        subtreeSpecificationComponentsMonitor.useComponent( "specificationFilter" );
+    }
     ;
-
+    exception
+    catch [IllegalArgumentException e]
+    {
+        throw new RecognitionException( e.getMessage() );
+    }
 
 ss_base
 {
     log.debug( "entered ss_base()" );
     Name base = null;
-} :
-    "base" ( SP )+ base=localName
+}
+    :
+    ID_base ( SP )+ base=distinguishedName
     {
         ssModifier.setBase( base );
     }
     ;
 
-
 ss_specificExclusions
 {
     log.debug( "entered ss_specificExclusions()" );
-} :
-    "specificExclusions" ( SP )+ specificExclusions
+}
+    :
+    ID_specificExclusions ( SP )+ specificExclusions
     {
         ssModifier.setChopBeforeExclusions( chopBeforeExclusions );
         ssModifier.setChopAfterExclusions( chopAfterExclusions );
     }
     ;
 
-
 specificExclusions
 {
     log.debug( "entered specificExclusions()" );
-} :
-    LBRACKET
-        ( ( SP )* specificExclusion
-            ( SEP ( SP )* specificExclusion )*
+}
+    :
+    OPEN_CURLY ( SP )*
+        ( specificExclusion ( SP )*
+            ( SEP ( SP )* specificExclusion ( SP )* )*
         )?
-    SP RBRACKET
+    CLOSE_CURLY
     ;
 
-
 specificExclusion
 {
     log.debug( "entered specificExclusion()" );
-} :
+}
+    :
     chopBefore | chopAfter
     ;
 
-
 chopBefore
 {
     log.debug( "entered chopBefore()" );
     Name chopBeforeExclusion = null;
-} :
-    "chopBefore" COLON chopBeforeExclusion=localName
+}
+    :
+    ID_chopBefore ( SP )* COLON ( SP )* chopBeforeExclusion=distinguishedName
     {
         chopBeforeExclusions.add( chopBeforeExclusion );
     }
     ;
 
-
 chopAfter
 {
     log.debug( "entered chopAfter()" );
     Name chopAfterExclusion = null;
-} :
-    "chopAfter" COLON chopAfterExclusion=localName
+}
+    :
+    ID_chopAfter ( SP )* COLON ( SP )* chopAfterExclusion=distinguishedName
     {
         chopAfterExclusions.add( chopAfterExclusion );
     }
     ;
 
-
 ss_minimum
 {
     log.debug( "entered ss_minimum()" );
     int minimum = 0;
-} :
-    "minimum" ( SP )+ minimum=baseDistance
+}
+    :
+    ID_minimum ( SP )+ minimum=baseDistance
     {
         ssModifier.setMinBaseDistance( minimum );
     }
     ;
 
-
 ss_maximum
 {
     log.debug( "entered ss_maximum()" );
     int maximum = 0;
-} :
-    "maximum" ( SP )+ maximum=baseDistance
+}
+    :
+    ID_maximum ( SP )+ maximum=baseDistance
     {
         ssModifier.setMaxBaseDistance( maximum );
     }
     ;
 
-
 ss_specificationFilter
 {
     log.debug( "entered ss_specificationFilter()" );
     ExprNode theRefinement = null;
-}:
-    "specificationFilter" ( SP )+ theRefinement=refinement
+}
+    :
+    ID_specificationFilter ( SP )+ theRefinement=refinement
     {
         ssModifier.setRefinement( theRefinement );
     }
     ;
-
-
-localName returns [Name name] 
+    
+distinguishedName returns [ Name name ] 
 {
-    log.debug( "entered localName()" );
+    log.debug( "entered distinguishedName()" );
     name = null;
-} :
-    token:DQUOTEDSTRING
+}
+    :
+    token:SAFEUTF8STRING
     {
         name = dnParser.parse( token.getText() );
+        log.debug( "recognized a DistinguishedName: " + token.getText() );
     }
     ;
     exception
     catch [Exception e]
     {
-        throw new RecognitionException( "dnParser failed." + e.getMessage() );
+        throw new RecognitionException( "dnParser failed for " + token.getText() + " " + e.getMessage() );
     }
 
-
-baseDistance returns [int distance]
+baseDistance returns [ int distance ]
 {
     log.debug( "entered baseDistance()" );
     distance = 0;
-} :
-    token:NUMBER
+}
+    :
+    token:INTEGER
     {
-        distance = Integer.parseInt( token.getText() );
+        distance = token2Integer( token );
     }
     ;
 
+oid returns [ String result ]
+{
+    log.debug( "entered oid()" );
+    result = null;
+    Token token = null;
+}
+    :
+    { token = LT( 1 ); } // an interesting trick goes here ;-)
+    ( DESCR | NUMERICOID )
+    {
+        result = token.getText();
+        log.debug( "recognized an oid: " + result );
+    }
+    ;
 
-refinement returns [ExprNode node]
+refinement returns [ ExprNode node ]
 {
     log.debug( "entered refinement()" );
     node = null;
-} :
+}
+    :
     node=item | node=and | node=or | node=not
     ;
 
-
-item returns [LeafNode node]
+item returns [ LeafNode node ]
 {
     log.debug( "entered item()" );
     node = null;
-    String oid = null;
-} :
-    "item" COLON oid=objectIdentifier
-    {
-        node = new SimpleNode( "objectClass" , oid , AbstractExprNode.EQUALITY );
-    }
-    ;
-
-
-objectIdentifier returns [String oid]
-{
-    oid = null;
-} :
-    token1:DESCR
-    {
-        oid = token1.getText();
-    }
-    |
-    token2:NUMERICOID
+    String l_oid = null;
+}
+    :
+    ID_item ( SP )* COLON ( SP )* l_oid=oid
     {
-        oid = token2.getText();
+        node = new SimpleNode( "objectClass" , l_oid , AbstractExprNode.EQUALITY );
     }
     ;
 
-
-and returns [BranchNode node]
+and returns [ BranchNode node ]
 {
     log.debug( "entered and()" );
     node = null;
     ArrayList children = null; 
-} :
-    "and" COLON children=refinements
+}
+    :
+    ID_and ( SP )* COLON ( SP )* children=refinements
     {
         node = new BranchNode( AbstractExprNode.AND , children );
     }
     ;
 
-
-or returns [BranchNode node]
+or returns [ BranchNode node ]
 {
     log.debug( "entered or()" );
     node = null;
     ArrayList children = null; 
-} :
-    "or" COLON children=refinements
+}
+    :
+    ID_or ( SP )* COLON ( SP )* children=refinements
     {
         node = new BranchNode( AbstractExprNode.OR , children );
     }
     ;
 
-
-not returns [BranchNode node]
+not returns [ BranchNode node ]
 {
     log.debug( "entered not()" );
     node = null;
     ArrayList children = null;
-} :
-    "not" COLON children=refinements
+}
+    :
+    ID_not ( SP )* COLON ( SP )* children=refinements
     {
         node = new BranchNode( AbstractExprNode.NOT , children );
     }
     ;
 
-
-refinements returns [ArrayList children]
+refinements returns [ ArrayList children ]
 {
     log.debug( "entered refinements()" );
     children = null;
     ExprNode child = null;
     ArrayList tempChildren = new ArrayList();
-} :
-    LBRACKET
-    ( SP
-        child=refinement
+}
+    :
+    OPEN_CURLY ( SP )*
+    (
+        child=refinement ( SP )*
         {
             tempChildren.add( child );
         }
-        ( SEP ( SP )* child=refinement
+        ( SEP ( SP )* child=refinement ( SP )*
         {
             tempChildren.add( child );
         } )*
-    )? ( SP )* RBRACKET
+    )? CLOSE_CURLY
     {
         children = tempChildren;
     }
     ;
 
+//subtreeSpecification returns [SubtreeSpecification ss]
+//{
+//    log.debug( "entered subtreeSpecification()" );
+//    // clear out ss and ssModifier in case something is left from the last parse
+//    ss = null;
+//    ssModifier = new SubtreeSpecificationModifier();
+//} :
+//    LBRACKET ( SP )*
+//        (
+//            (
+//              ss_base ss_base_follower
+//            | ss_specificExclusions ss_specificExclusions_follower
+//            | ss_minimum ss_minimum_follower
+//            | ss_maximum ss_maximum_follower
+//            | ss_specificationFilter
+//            )
+//            ( SP )*
+//        )?
+//    RBRACKET
+//    {
+//        ss = ssModifier.getSubtreeSpecification();
+//    }
+//    ;
+//
+//    
+//ss_base_follower
+//    :
+//    ( SEP ( SP )*
+//        (
+//            ss_specificExclusions ss_specificExclusions_follower
+//            | ss_minimum ss_minimum_follower
+//            | ss_maximum ss_maximum_follower
+//            | ss_specificationFilter
+//        )
+//    )?
+//    ;
+//
+//    
+//ss_specificExclusions_follower
+//    :
+//    ( SEP ( SP )*
+//        (
+//            ss_minimum ss_minimum_follower
+//            | ss_maximum ss_maximum_follower
+//            | ss_specificationFilter
+//        )
+//    )?
+//    ;
+//
+//    
+//ss_minimum_follower
+//    :
+//    ( SEP ( SP )*
+//        (
+//            ss_maximum ss_maximum_follower
+//            | ss_specificationFilter
+//        )
+//    )?
+//    ;
+//
+//    
+//ss_maximum_follower
+//    :
+//    ( SEP ( SP )*
+//        (
+//            ss_specificationFilter
+//        )
+//    )?
+//    ;
+//
+//
+//ss_base
+//{
+//    log.debug( "entered ss_base()" );
+//    Name base = null;
+//} :
+//    "base" ( SP )+ base=localName
+//    {
+//        ssModifier.setBase( base );
+//    }
+//    ;
+//
+//
+//ss_specificExclusions
+//{
+//    log.debug( "entered ss_specificExclusions()" );
+//} :
+//    "specificExclusions" ( SP )+ specificExclusions
+//    {
+//        ssModifier.setChopBeforeExclusions( chopBeforeExclusions );
+//        ssModifier.setChopAfterExclusions( chopAfterExclusions );
+//    }
+//    ;
+//
+//
+//specificExclusions
+//{
+//    log.debug( "entered specificExclusions()" );
+//} :
+//    LBRACKET
+//        ( ( SP )* specificExclusion
+//            ( SEP ( SP )* specificExclusion )*
+//        )?
+//    SP RBRACKET
+//    ;
+//
+//
+//specificExclusion
+//{
+//    log.debug( "entered specificExclusion()" );
+//} :
+//    chopBefore | chopAfter
+//    ;
+//
+//
+//chopBefore
+//{
+//    log.debug( "entered chopBefore()" );
+//    Name chopBeforeExclusion = null;
+//} :
+//    "chopBefore" COLON chopBeforeExclusion=localName
+//    {
+//        chopBeforeExclusions.add( chopBeforeExclusion );
+//    }
+//    ;
+//
+//
+//chopAfter
+//{
+//    log.debug( "entered chopAfter()" );
+//    Name chopAfterExclusion = null;
+//} :
+//    "chopAfter" COLON chopAfterExclusion=localName
+//    {
+//        chopAfterExclusions.add( chopAfterExclusion );
+//    }
+//    ;
+//
+//
+//ss_minimum
+//{
+//    log.debug( "entered ss_minimum()" );
+//    int minimum = 0;
+//} :
+//    "minimum" ( SP )+ minimum=baseDistance
+//    {
+//        ssModifier.setMinBaseDistance( minimum );
+//    }
+//    ;
+//
+//
+//ss_maximum
+//{
+//    log.debug( "entered ss_maximum()" );
+//    int maximum = 0;
+//} :
+//    "maximum" ( SP )+ maximum=baseDistance
+//    {
+//        ssModifier.setMaxBaseDistance( maximum );
+//    }
+//    ;
+//
+//
+//ss_specificationFilter
+//{
+//    log.debug( "entered ss_specificationFilter()" );
+//    ExprNode theRefinement = null;
+//}:
+//    "specificationFilter" ( SP )+ theRefinement=refinement
+//    {
+//        ssModifier.setRefinement( theRefinement );
+//    }
+//    ;
+//
+//
+//localName returns [Name name] 
+//{
+//    log.debug( "entered localName()" );
+//    name = null;
+//} :
+//    token:DQUOTEDSTRING
+//    {
+//        name = dnParser.parse( token.getText() );
+//    }
+//    ;
+//    exception
+//    catch [Exception e]
+//    {
+//        throw new RecognitionException( "dnParser failed." + e.getMessage() );
+//    }
+//
+//
+//baseDistance returns [int distance]
+//{
+//    log.debug( "entered baseDistance()" );
+//    distance = 0;
+//} :
+//    token:NUMBER
+//    {
+//        distance = Integer.parseInt( token.getText() );
+//    }
+//    ;
+//
+//
+//refinement returns [ExprNode node]
+//{
+//    log.debug( "entered refinement()" );
+//    node = null;
+//} :
+//    node=item | node=and | node=or | node=not
+//    ;
+//
+//
+//item returns [LeafNode node]
+//{
+//    log.debug( "entered item()" );
+//    node = null;
+//    String oid = null;
+//} :
+//    "item" COLON oid=objectIdentifier
+//    {
+//        node = new SimpleNode( "objectClass" , oid , AbstractExprNode.EQUALITY );
+//    }
+//    ;
+//
+//
+//objectIdentifier returns [String oid]
+//{
+//    oid = null;
+//} :
+//    token1:DESCR
+//    {
+//        oid = token1.getText();
+//    }
+//    |
+//    token2:NUMERICOID
+//    {
+//        oid = token2.getText();
+//    }
+//    ;
+//
+//
+//and returns [BranchNode node]
+//{
+//    log.debug( "entered and()" );
+//    node = null;
+//    ArrayList children = null; 
+//} :
+//    "and" COLON children=refinements
+//    {
+//        node = new BranchNode( AbstractExprNode.AND , children );
+//    }
+//    ;
+//
+//
+//or returns [BranchNode node]
+//{
+//    log.debug( "entered or()" );
+//    node = null;
+//    ArrayList children = null; 
+//} :
+//    "or" COLON children=refinements
+//    {
+//        node = new BranchNode( AbstractExprNode.OR , children );
+//    }
+//    ;
+//
+//
+//not returns [BranchNode node]
+//{
+//    log.debug( "entered not()" );
+//    node = null;
+//    ArrayList children = null;
+//} :
+//    "not" COLON children=refinements
+//    {
+//        node = new BranchNode( AbstractExprNode.NOT , children );
+//    }
+//    ;
+//
+//
+//refinements returns [ArrayList children]
+//{
+//    log.debug( "entered refinements()" );
+//    children = null;
+//    ExprNode child = null;
+//    ArrayList tempChildren = new ArrayList();
+//} :
+//    LBRACKET
+//    ( SP
+//        child=refinement
+//        {
+//            tempChildren.add( child );
+//        }
+//        ( SEP ( SP )* child=refinement
+//        {
+//            tempChildren.add( child );
+//        } )*
+//    )? ( SP )* RBRACKET
+//    {
+//        children = tempChildren;
+//    }
+//    ;
+
 
 // ----------------------------------------------------------------------------
 // lexer class definition
@@ -471,8 +777,21 @@
     k = 2;
 
     charVocabulary = '\u0001'..'\u0127';
+}
 
-    testLiterals = false;
+tokens
+{
+    ID_base = "base";
+    ID_specificExclusions = "specificExclusions";
+    ID_chopBefore = "chopBefore";
+    ID_chopAfter = "chopAfter";
+    ID_minimum = "minimum";
+    ID_maximum = "maximum";
+    ID_specificationFilter = "specificationFilter";
+    ID_item = "item";
+    ID_and = "and";
+    ID_or = "or";
+    ID_not = "not";
 }
 
 
@@ -493,36 +812,32 @@
 
 COLON : ':' { log.debug( "matched COLON(':')" ); } ;
 
-LBRACKET : '{' { log.debug( "matched LBRACKET('{')" ); } ;
+OPEN_CURLY : '{' { log.debug( "matched LBRACKET('{')" ); } ;
 
-RBRACKET : '}' { log.debug( "matched RBRACKET('}')" ); } ;
-
-DQUOTE : '"' { log.debug( "matched DQUOTE('\"')" ); } ;
+CLOSE_CURLY : '}' { log.debug( "matched RBRACKET('}')" ); } ;
 
 SEP : ',' { log.debug( "matched SEP(',')" ); } ;
 
-DQUOTEDSTRING : DQUOTE! ( SAFEUTF8CHAR )* DQUOTE! { log.debug( "matched DQUOTEDSTRING: \"" + getText() + "\"" ); } ;
-
-DESCR options { testLiterals = true; } : ALPHA ( ALPHA | DIGIT | '-' )* { log.debug( "matched DESCR" ); } ;
+SAFEUTF8STRING : '"'! ( SAFEUTF8CHAR )* '"'! { log.debug( "matched SAFEUTF8CHAR: \"" + getText() + "\"" ); } ;
 
-// This rule is required to prevent nondeterminism problem caused by NUMBER and NUMERICOID rules.
+DESCR : ALPHA ( ALPHA | DIGIT | '-' )* { log.debug( "matched DESCR" ); } ;
 
-NUMBER_OR_NUMERICOID
+INTEGER_OR_NUMERICOID
     :
-    ( NUMBER DOT ) => NUMERICOID
+    ( INTEGER DOT ) => NUMERICOID
     {
-        $setType(NUMERICOID);
+        $setType( NUMERICOID );
     }
     |
-    NUMBER
+    INTEGER
     {
-        $setType(NUMBER);
+        $setType( INTEGER );
     }
     ;
 
-protected NUMBER: DIGIT | ( LDIGIT ( DIGIT )+ ) { log.debug( "matched NUMBER: " + getText() ); } ;
+protected INTEGER: DIGIT | ( LDIGIT ( DIGIT )+ ) { log.debug( "matched INTEGER: " + getText() ); } ;
 
-protected NUMERICOID: NUMBER ( DOT NUMBER )+ { log.debug( "matched NUMERICOID: " + getText() ); } ;
+protected NUMERICOID: INTEGER ( DOT INTEGER )+ { log.debug( "matched NUMERICOID: " + getText() ); } ;
 
 protected DOT: '.' ;
 

Modified: directory/shared/ldap/trunk/common/src/test/org/apache/ldap/common/subtree/SubtreeSpecificationParserTest.java
URL: http://svn.apache.org/viewcvs/directory/shared/ldap/trunk/common/src/test/org/apache/ldap/common/subtree/SubtreeSpecificationParserTest.java?rev=331857&r1=331856&r2=331857&view=diff
==============================================================================
--- directory/shared/ldap/trunk/common/src/test/org/apache/ldap/common/subtree/SubtreeSpecificationParserTest.java (original)
+++ directory/shared/ldap/trunk/common/src/test/org/apache/ldap/common/subtree/SubtreeSpecificationParserTest.java Tue Nov  8 10:22:19 2005
@@ -90,8 +90,8 @@
         ", minimum 7, maximum   77" + 
         ", specificationFilter     and:{ and:{ item:1.2.3, or:{ item:4.5.6, item:7.8.9 } }, not:{ item:10.11.12 } } }";
     
-    /** An invalid specification with wrong component order */
-    private static final String INVALID_SPEC_WITH_WRONG_COMPONENT_ORDER =
+    /** An valid specification with unordinary component order */
+    private static final String SPEC_ORDER_OF_COMPONENTS_DOES_NOT_MATTER =
         "{ base \"ou=system\", minimum 3, specificExclusions { chopBefore:\"x=y\" } }";
 
     /** An invalid specification with completely unrelated content */
@@ -316,19 +316,12 @@
 
 
     /**
-     * Tests the parser with an invalid specification with wrong component order.
+     * Tests the parser with a valid specification with unordinary component order.
      */
-    public void testInvalidSpecWithWrongComponentOrder() throws Exception
-    {    
-        try
-        {
-            parser.parse( INVALID_SPEC_WITH_WRONG_COMPONENT_ORDER );
-            fail( "testInvalidSpecWithWrongComponentOrder() should never come here..." );
-        }
-        catch ( ParseException e )
-        {
-            assertNotNull( e );
-        }
+    public void testSpecOrderOfComponentsDoesNotMatter() throws Exception
+    {
+        SubtreeSpecification ss = parser.parse( SPEC_ORDER_OF_COMPONENTS_DOES_NOT_MATTER );
+        assertNotNull( ss );
     }