Index: project.xml =================================================================== --- project.xml (revision 240333) +++ project.xml (working copy) @@ -62,6 +62,15 @@ + + + org.slf4j + nlog4j + 1.2.14 + http://slf4j.org/nlog4j + + + dev@directory.apache.org ${basedir}/src/java @@ -71,6 +80,16 @@ **/*Test.java + + + + ${basedir}/src/test/resources + + **/* + + + + Index: common/src/test/org/apache/ldap/common/subtree/SubtreeParserTest.java =================================================================== --- common/src/test/org/apache/ldap/common/subtree/SubtreeParserTest.java (revision 240333) +++ common/src/test/org/apache/ldap/common/subtree/SubtreeParserTest.java (working copy) @@ -1,141 +0,0 @@ -/* - * Copyright 2004 The Apache Software Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package org.apache.ldap.common.subtree; - - -import junit.framework.TestCase; - -import java.io.StringReader; - -import antlr.TokenStreamException; -import antlr.RecognitionException; - -import org.apache.ldap.common.name.LdapName; - - -/** - * Tests the SubtreeSpecification parser. - * - * @author Apache Directory Project - * @version $Rev$ - */ -public class SubtreeParserTest extends TestCase -{ - /** A valid empty specification with white space */ - private static final String EMPTY_SPEC_SP = "{ } "; - - /** A valid empty specification */ - private static final String EMPTY_SPEC = "{}"; - - /** A valid specification only with a base set */ - private static final String SPEC_BASE = "{ base \"ou=system\" }"; - - - /** - * Parses a subtreeSpecification attribute value using the antlr generated - * lexers and parsers. - * - * @param value the subtree specification value - * @return a SubtreeSpecification instance object representing the spec string - * @throws TokenStreamException if we encounter invalid tokens - * @throws RecognitionException if we cannot recognize a token - */ - private SubtreeSpecification parse( String value ) throws TokenStreamException, RecognitionException - { - StringReader in = new StringReader( value ); - - AntlrSubtreeSpecificationLexer lexer = new AntlrSubtreeSpecificationLexer( in ); - - AntlrSubtreeSpecificationParser parser = new AntlrSubtreeSpecificationParser( lexer ); - - SubtreeSpecification spec = parser.subtreeSpecification(); - - assertNotNull( spec ); - - return spec; - } - - - /** - * Tests to see if an empty yet legal spec can be parsed without any - * whitespace. - * - * @throws Exception if the parser fails - */ - public void testEmptySpecTest() throws Exception - { - SubtreeSpecification spec = parse( EMPTY_SPEC ); - - assertTrue( spec.getBase().isEmpty() ); - - assertTrue( spec.getChopAfterExclusions().isEmpty() ); - - assertTrue( spec.getChopBeforeExclusions().isEmpty() ); - - assertEquals( SubtreeSpecification.UNBOUNDED_MAX, spec.getMaxBaseDistance() ); - - assertEquals( 0, spec.getMinBaseDistance() ); - - assertNull( spec.getRefinement() ); - } - - - /** - * Tests to see if an empty yet legal spec can be parsed with whitespace. - * - * @throws Exception if the parser fails - */ - public void testEmptyWhitespaceSpecTest() throws Exception - { - SubtreeSpecification spec = parse( EMPTY_SPEC_SP ); - - assertEquals( new LdapName(), spec.getBase() ); - - assertTrue( spec.getChopAfterExclusions().isEmpty() ); - - assertTrue( spec.getChopBeforeExclusions().isEmpty() ); - - assertEquals( SubtreeSpecification.UNBOUNDED_MAX, spec.getMaxBaseDistance() ); - - assertEquals( 0, spec.getMinBaseDistance() ); - - assertNull( spec.getRefinement() ); - } - - - /** - * Tests to see if the base is set with a legal spec containing only the subtree base. - * - * @throws Exception if the parser fails - */ - public void testBaseOnlySpecTest() throws Exception - { - SubtreeSpecification spec = parse( SPEC_BASE ); - - assertEquals( new LdapName( "ou=system" ), spec.getBase() ); - - assertTrue( spec.getChopAfterExclusions().isEmpty() ); - - assertTrue( spec.getChopBeforeExclusions().isEmpty() ); - - assertEquals( SubtreeSpecification.UNBOUNDED_MAX, spec.getMaxBaseDistance() ); - - assertEquals( 0, spec.getMinBaseDistance() ); - - assertNull( spec.getRefinement() ); - } -} Index: common/src/test/org/apache/ldap/common/subtree/SubtreeSpecificationParserTest.java =================================================================== --- common/src/test/org/apache/ldap/common/subtree/SubtreeSpecificationParserTest.java (revision 0) +++ common/src/test/org/apache/ldap/common/subtree/SubtreeSpecificationParserTest.java (revision 0) @@ -0,0 +1,484 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +package org.apache.ldap.common.subtree; + + +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.apache.ldap.common.filter.AbstractExprNode; +import org.apache.ldap.common.filter.BranchNode; +import org.apache.ldap.common.filter.SimpleNode; +import org.apache.ldap.common.name.LdapName; +import org.apache.ldap.common.subtree.SubtreeSpecification; + + +/** + * Unit tests class for Subtree Specification parser (wrapper). + * + * @author Apache Directory Project + * @version $Rev$ + */ +public class SubtreeSpecificationParserTest extends TestCase +{ + /** A valid empty specification with single white space between brackets */ + private static final String EMPTY_SPEC = + "{ }"; + + /** An invalid empty specification with two white spaces between brackets */ + private static final String INVALID_EMPTY_SPEC_WITH_EXTRA_WS = + "{ }"; + + /** A valid specification only with base set */ + private static final String SPEC_WITH_BASE = + "{ base \"ou=system\" }"; + + /** An invalid specification with missing white space and base set */ + private static final String INVALID_SPEC_WITH_BASE_AND_MISSING_WS = + "{ base\"ou=system\"}"; + + /** An invalid specification with extra whitespace and base set */ + private static final String INVALID_SPEC_WITH_BASE_AND_EXTRA_WS = + "{ base ou=system\" } "; + + /** A valid specification with some specific exclusions set */ + private static final String SPEC_WITH_SPECIFICEXCLUSIONS = + "{, specificExclusions { chopAfter:\"ef=gh\", chopBefore:\"ab=cd\" } }"; + + /** A valid specification with empty specific exclusions set */ + private static final String SPEC_WITH_EMPTY_SPECIFICEXCLUSIONS = + "{, specificExclusions { } }"; + + /** A valid specification with minimum and maximum set */ + private static final String SPEC_WITH_MINIMUM_AND_MAXIMUM = + "{, minimum 1, maximum 2 }"; + + /** A valid specification with base and minimum and maximum set */ + private static final String SPEC_WITH_BASE_AND_MINIMUM_AND_MAXIMUM = + "{ base \"ou=ORGANIZATION UNIT\", minimum 1, maximum 2 }"; + + /** A valid specification with base and specific exclusions and minimum and maximum set */ + private static final String SPEC_WITH_BASE_AND_SPECIFICEXCLUSIONS_AND_MINIMUM_AND_MAXIMUM = + "{ base \"ou=people\", specificExclusions { chopBefore:\"x=y\"" + + ", chopAfter:\"k=l\", chopBefore:\"y=z\", chopAfter:\"l=m\" }, minimum 7, maximum 77 }"; + + /** A valid specification with refinement set */ + private static final String SPEC_WITH_REFINEMENT = + "{ base \"ou=system\", specificationFilter and:{ and:{ item:1.2.3" + + ", or:{ item:4.5.6, item:person-7 } }, not:{ item:10.11.12 } } }"; + + /** A valid specification with base and an empty refinement set */ + private static final String SPEC_WITH_BASE_AND_EMPTY_REFINEMENT = + "{ base \"ou=system\", specificationFilter and:{ } }"; + + /** A valid specification with ALL IN ONE */ + private static final String SPEC_WITH_ALL_IN_ONE = + "{ base \"ou=departments\"" + + ", specificExclusions { chopBefore:\"x=y\", chopAfter:\"k=l\", chopBefore:\"y=z\", chopAfter:\"l=m\" }" + + ", 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 } } }"; + + private static final String INVALID_SPEC_WITH_WRONG_COMPONENT_ORDER = + "{ base \"ou=system\", minimum 3, specificExclusions { chopBefore:\"x=y\" } }"; + + private static final String INVALID_SILLY_THING = + "How much wood would a wood chuck chuck if a wood chuck would chuck wood?"; + + + /** the ss parser wrapper */ + SubtreeSpecificationParser parser; + + /** holds multithreaded success value */ + boolean isSuccessMultithreaded = true; + + + /** + * Creates a SubtreeSpecificationParserTest instance. + */ + public SubtreeSpecificationParserTest() + { + super(); + parser = new SubtreeSpecificationParser(); + } + + + /** + * Creates a SubtreeSpecificationParserTest instance. + */ + public SubtreeSpecificationParserTest( String s ) + { + super( s ); + parser = new SubtreeSpecificationParser(); + } + + + /** + * Tests the parser with a valid empty specification. + */ + public void testEmptySpec() throws Exception + { + SubtreeSpecification ss = parser.parse( EMPTY_SPEC ); + assertNotNull( ss ); + + // try a second time + ss = parser.parse( EMPTY_SPEC ); + assertNotNull( ss ); + + // try a third time + ss = parser.parse( EMPTY_SPEC ); + assertNotNull( ss ); + } + + + /** + * Tests the parser with an invalid empty specification with extra white spaces. + */ + public void testInvalidEmptySpecWithExtraWS() throws Exception + { + try + { + SubtreeSpecification ss = parser.parse( INVALID_EMPTY_SPEC_WITH_EXTRA_WS ); + fail( "testInvalidEmptySpecWithExtraWS() should never come here..." ); + } + catch ( ParseException e ) + { + assertNotNull( e ); + } + catch ( IOException e ) + { + assertNotNull( e ); + } + } + + + /** + * Tests the parser with a valid specification with base set. + */ + public void testSpecWithBase() throws Exception + { + SubtreeSpecification ss = parser.parse( SPEC_WITH_BASE ); + assertNotNull( ss ); + + assertEquals( "ou=system" , ss.getBase().toString() ); + } + + + /** + * Tests the parser with an invalid specification with missing white spaces and base set. + */ + public void testInvalidSpecWithBaseAndMissingWS() throws Exception + { + try + { + SubtreeSpecification ss = parser.parse( INVALID_SPEC_WITH_BASE_AND_MISSING_WS ); + fail( "testInvalidSpecWithBaseAndMissingWS() should never come here..." ); + } + catch ( ParseException e ) + { + assertNotNull( e ); + } + catch ( IOException e ) + { + assertNotNull( e ); + } + } + + + /** + * Tests the parser with an invalid specification with extra white spaces and base set. + */ + public void testInvalidSpecWithBaseAndExtraWS() throws Exception + { + try + { + SubtreeSpecification ss = parser.parse( INVALID_SPEC_WITH_BASE_AND_EXTRA_WS ); + fail( "testInvalidSpecWithBaseAndExtraWS() should never come here..." ); + } + catch ( ParseException e ) + { + assertNotNull( e ); + } + catch ( IOException e ) + { + assertNotNull( e ); + } + } + + + /** + * Tests the parser with a valid specification with some specific exclusions set. + */ + public void testSpecWithSpecificExclusions() throws Exception + { + SubtreeSpecification ss = parser.parse( SPEC_WITH_SPECIFICEXCLUSIONS ); + assertFalse( ss.getChopBeforeExclusions().isEmpty() ); + assertFalse( ss.getChopAfterExclusions().isEmpty() ); + assertTrue( ss.getChopBeforeExclusions().contains( new LdapName( "ab=cd" ) ) ); + assertTrue( ss.getChopAfterExclusions().contains( new LdapName( "ef=gh" ) ) ); + + // try a second time + ss = parser.parse( SPEC_WITH_SPECIFICEXCLUSIONS ); + assertFalse( ss.getChopBeforeExclusions().isEmpty() ); + assertFalse( ss.getChopAfterExclusions().isEmpty() ); + assertTrue( ss.getChopBeforeExclusions().contains( new LdapName( "ab=cd" ) ) ); + assertTrue( ss.getChopAfterExclusions().contains( new LdapName( "ef=gh" ) ) ); + + // try a third time + ss = parser.parse( SPEC_WITH_SPECIFICEXCLUSIONS ); + assertFalse( ss.getChopBeforeExclusions().isEmpty() ); + assertFalse( ss.getChopAfterExclusions().isEmpty() ); + assertTrue( ss.getChopBeforeExclusions().contains( new LdapName( "ab=cd" ) ) ); + assertTrue( ss.getChopAfterExclusions().contains( new LdapName( "ef=gh" ) ) ); + } + + + /** + * Tests the parser with a valid specification with an empty specific exclusions set. + */ + public void testSpecWithEmptySpecificExclusions() throws Exception + { + SubtreeSpecification ss = parser.parse( SPEC_WITH_EMPTY_SPECIFICEXCLUSIONS ); + assertNotNull( ss ); + + assertTrue( ss.getChopBeforeExclusions().isEmpty() ); + } + + + /** + * Tests the parser with a valid specification with minimum and maximum set. + */ + public void testSpecWithMinimumAndMaximum() throws Exception + { + SubtreeSpecification ss = parser.parse( SPEC_WITH_MINIMUM_AND_MAXIMUM ); + assertEquals( 1 , ss.getMinBaseDistance() ); + assertEquals( 2 , ss.getMaxBaseDistance() ); + + // try a second time + ss = parser.parse( SPEC_WITH_MINIMUM_AND_MAXIMUM ); + assertEquals( 1 , ss.getMinBaseDistance() ); + assertEquals( 2 , ss.getMaxBaseDistance() ); + + // try a third time + ss = parser.parse( SPEC_WITH_MINIMUM_AND_MAXIMUM ); + assertEquals( 1 , ss.getMinBaseDistance() ); + assertEquals( 2 , ss.getMaxBaseDistance() ); + } + + + /** + * Tests the parser with a valid specification with base and minimum and maximum set. + */ + public void testWithBaseAndMinimumAndMaximum() throws Exception + { + SubtreeSpecification ss = parser.parse( SPEC_WITH_BASE_AND_MINIMUM_AND_MAXIMUM ); + + assertEquals( new LdapName( "ou=ORGANIZATION UNIT" ) , ss.getBase() ); + assertEquals( 1 , ss.getMinBaseDistance()); + assertEquals( 2 , ss.getMaxBaseDistance()); + } + + + /** + * Tests the parser with a valid specification with base and specific exclusions and minimum and maximum set. + */ + public void testSpecWithBaseAndSpecificExclusionsAndMinimumAndMaximum() throws Exception + { + SubtreeSpecification ss = parser.parse( SPEC_WITH_BASE_AND_SPECIFICEXCLUSIONS_AND_MINIMUM_AND_MAXIMUM ); + assertNotNull ( ss ); + + assertEquals ( "ou=people", ss.getBase().toString() ); + assertTrue ( ss.getChopBeforeExclusions().contains( new LdapName( "x=y" ) ) ); + assertTrue ( ss.getChopBeforeExclusions().contains( new LdapName( "y=z" ) ) ); + assertTrue ( ss.getChopAfterExclusions().contains( new LdapName( "k=l" ) ) ); + assertTrue ( ss.getChopAfterExclusions().contains( new LdapName( "l=m" ) ) ); + assertEquals ( 7 , ss.getMinBaseDistance() ); + assertEquals ( 77 , ss.getMaxBaseDistance() ); + } + + + /** + * Tests the parser with a valid specification with refinement set. + */ + public void testSpecWithRefinement() throws Exception + { + SubtreeSpecification ss = parser.parse( SPEC_WITH_REFINEMENT ); + + SimpleNode n1 = new SimpleNode( "objectClass" , "1.2.3" , 0 ); + SimpleNode n2 = new SimpleNode( "objectClass" , "4.5.6" , 0 ); + SimpleNode n3 = new SimpleNode( "objectClass" , "person-7" , 0 ); + BranchNode n4 = new BranchNode( AbstractExprNode.OR ); + n4.addNode( n2 ); + n4.addNode( n3 ); + BranchNode n5 = new BranchNode( AbstractExprNode.AND ); + n5.addNode( n1 ); + n5.addNode( n4 ); + SimpleNode n6 = new SimpleNode( "objectClass" , "10.11.12" , 0 ); + BranchNode n7 = new BranchNode( AbstractExprNode.NOT ); + n7.addNode( n6 ); + BranchNode n8 = new BranchNode( AbstractExprNode.AND ); + n8.addNode( n5 ); + n8.addNode( n7 ); + + assertEquals( n8 , ss.getRefinement() ); + } + + + /** + * Tests the parser with a valid specification with base and empty refinement set. + */ + public void testSpecWithBaseAndEmptyRefinement() throws Exception + { + SubtreeSpecification ss = parser.parse( SPEC_WITH_BASE_AND_EMPTY_REFINEMENT ); + + assertEquals( "ou=system" , ss.getBase().toString() ); + } + + + /** + * Tests the parser with a valid specification with all components set. + */ + public void testSpecWithAllInOne() throws Exception + { + SubtreeSpecification ss = parser.parse( SPEC_WITH_ALL_IN_ONE ); + assertNotNull( ss ); + } + + + /** + * Tests the parser with an invalid specification with wrong component order. + */ + public void testInvalidSpecWithWrongComponentOrder() throws Exception + { + try + { + SubtreeSpecification ss = parser.parse( INVALID_SPEC_WITH_WRONG_COMPONENT_ORDER ); + fail( "testInvalidSpecWithWrongComponentOrder() should never come here..." ); + } + catch ( ParseException e ) + { + assertNotNull( e ); + } + catch ( IOException e ) + { + assertNotNull( e ); + } + } + + + /** + * Tests the parser with an invalid specification with silly things in. + */ + public void testInvalidSillyThing() throws Exception + { + try + { + SubtreeSpecification ss = parser.parse( INVALID_SILLY_THING ); + fail( "testInvalidSillyThing() should never come here..." ); + } + catch ( ParseException e ) + { + assertNotNull( e ); + } + catch ( IOException e ) + { + assertNotNull( e ); + } + } + + + /** + * Tests the multithreaded use of a single parser. + */ + public void testMultiThreaded() throws Exception + { + // start up and track all threads (40 threads) + List threads = new ArrayList(); + for ( int ii = 0; ii < 10; ii++ ) + { + Thread t0 = new Thread( new ParseSpecification( EMPTY_SPEC ) ); + Thread t1 = new Thread( new ParseSpecification( SPEC_WITH_SPECIFICEXCLUSIONS ) ); + Thread t2 = new Thread( new ParseSpecification( SPEC_WITH_MINIMUM_AND_MAXIMUM ) ); + Thread t3 = new Thread( new ParseSpecification( SPEC_WITH_ALL_IN_ONE ) ); + 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 specStr; + SubtreeSpecification result; + + + public ParseSpecification( String specStr ) + { + this.specStr = specStr; + } + + + public void run() + { + try + { + result = parser.parse( specStr ); + } + catch ( ParseException e ) + { + e.printStackTrace(); + } + catch ( IOException e ) + { + e.printStackTrace(); + } + + isSuccessMultithreaded = isSuccessMultithreaded && ( result != null ); + } + } +} Index: common/src/test/resources/log4j.properties =================================================================== --- common/src/test/resources/log4j.properties (revision 0) +++ common/src/test/resources/log4j.properties (revision 0) @@ -0,0 +1,5 @@ +log4j.rootCategory=INFO, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=[%d{HH:mm:ss}] %p [%c] - %m%n Index: common/src/antlr/subtree-specification.g =================================================================== --- common/src/antlr/subtree-specification.g (revision 240333) +++ common/src/antlr/subtree-specification.g (working copy) @@ -1,32 +1,43 @@ header { /* - * Copyright 2002-2004 The Apache Software Foundation. + * Copyright 2004 The Apache Software Foundation * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. + * 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. + * */ -/* - * Keep the semicolon right next to org.apache.ldap.common.filter or else there - * will be a bug that comes into the foreground in the new antlr release. - */ + package org.apache.ldap.common.subtree; +import java.util.Set; +import java.util.HashSet; +import java.util.ArrayList; import javax.naming.Name; import javax.naming.NamingException; -import org.apache.ldap.common.name.LdapName; +import org.apache.ldap.common.name.DnParser; +import org.apache.ldap.common.filter.ExprNode; +import org.apache.ldap.common.filter.LeafNode; +import org.apache.ldap.common.filter.SimpleNode; +import org.apache.ldap.common.filter.BranchNode; +import org.apache.ldap.common.filter.AbstractExprNode; +import org.apache.ldap.common.subtree.SubtreeSpecification; +import org.apache.ldap.common.subtree.SubtreeSpecificationModifier; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; } @@ -38,41 +49,330 @@ * The antlr generated subtree specification parser. * * @see RFC 3672 - * @author Alex Karasulu + * @author Apache Directory Project + * @version $Rev$ */ class AntlrSubtreeSpecificationParser extends Parser; // ---------------------------------------------------------------------------- -// parser productions +// parser options // ---------------------------------------------------------------------------- +options +{ + k = 3; -subtreeSpecification returns [SubtreeSpecification spec] + defaultErrorHandler = false; +} + + +// ---------------------------------------------------------------------------- +// parser initialization +// ---------------------------------------------------------------------------- + { - spec = null; + private static final Logger log = LoggerFactory.getLogger( AntlrSubtreeSpecificationParser.class ); + private final DnParser dnParser = createDnParser(); + + private Set chopBeforeExclusions = new HashSet(); + private Set chopAfterExclusions = new HashSet(); - SubtreeSpecificationModifier modifier = new SubtreeSpecificationModifier(); + SubtreeSpecificationModifier ssModifier = null; + + /** + * Creates a subordinate DnParser for parsing LocalNames. + * + * @return the DnParser to be used for parsing LocalNames + */ + private DnParser createDnParser() + { + try + { + return new DnParser(); + } + catch ( NamingException e ) + { + String msg = "Failed to initialize the subordinate DnParser for this AntlrSubtreeSpecificationParser"; + + // We throw a NPE since this variable cannot be null for proper operation + // so we can catch the null pointer before the dnParser is even used. + + throw new NullPointerException( "dnParser is null: " + msg ); + } + } } - : + + +// ---------------------------------------------------------------------------- +// parser productions +// ---------------------------------------------------------------------------- + +wrapperEntryPoint returns [SubtreeSpecification ss] +{ + log.debug( "entered wrapperEntryPoint()" ); + ss = null; + SubtreeSpecification tempSs = null; +} : + tempSs=subtreeSpecification "end" + { + ss = tempSs; + } + ; + + +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 - ( - BASE rdn:LOCALNAME + ( SP ss_base )? + ( SEP SP ss_specificExclusions )? + ( SEP SP ss_minimum )? + ( SEP SP ss_maximum )? + ( SEP SP ss_specificationFilter )? + SP RBRACKET + { + ss = ssModifier.getSubtreeSpecification(); + } + ; + + +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 { - String quoted = rdn.getText(); - try - { - modifier.setBase( new LdapName( quoted.substring( 1, quoted.length() - 1 ) ) ); - } - catch ( NamingException e ) - { - throw new RecognitionException( "LdapName parse failed on base." ); - } + tempChildren.add( child ); } - )? - ( SP )* - RBRACKET - { spec = modifier.getSubtreeSpecification(); }; + ( SEP SP child=refinement + { + tempChildren.add( child ); + } )* + )? SP RBRACKET + { + children = tempChildren; + } + ; // ---------------------------------------------------------------------------- @@ -83,7 +383,8 @@ * The parser's primary lexer. * * @see RFC 3672 - * @author Alex Karasulu + * @author Apache Directory Project + * @version $Rev$ */ class AntlrSubtreeSpecificationLexer extends Lexer; @@ -94,19 +395,20 @@ options { - k=5; + k = 3; - charVocabulary='\u0001'..'\u0127'; + charVocabulary = '\u0001'..'\u0127'; + + testLiterals = false; } -// ---------------------------------------------------------------------------- -// lexer class members -// ---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- +// lexer initialization +//---------------------------------------------------------------------------- { - /** the selector key used by this class of lexer */ - public static final String SELECTOR_KEY = "refinementLexer"; + private static final Logger log = LoggerFactory.getLogger( AntlrSubtreeSpecificationLexer.class ); } @@ -114,45 +416,55 @@ // attribute description lexer rules from models // ---------------------------------------------------------------------------- +SP : ' ' ; -SP: ' '; +COLON : ':' { log.debug( "matched COLON(':')" ); } ; -LBRACKET: '{' ( SP )*; +LBRACKET : '{' { log.debug( "matched LBRACKET('{')" ); } ; -RBRACKET: '}'; +RBRACKET : '}' { log.debug( "matched RBRACKET('}')" ); } ; -DQUOTE: '"'; +DQUOTE : '"' { log.debug( "matched DQUOTE('\"')" ); } ; -COMMA: ','; +SEP : ',' { log.debug( "matched SEP(',')" ); } ; -OR: "or:"; +DQUOTEDSTRING : DQUOTE! ( SAFEUTF8CHAR )* DQUOTE! { log.debug( "matched DQUOTEDSTRING: \"" + getText() + "\"" ); } ; -AND: "and:"; +DESCR options { testLiterals = true; } : ALPHA ( ALPHA | DIGIT | '-' )* { log.debug( "matched DESCR" ); } ; -NOT: "not:"; +// This rule is required to prevent nondeterminism problem caused by NUMBER and NUMERICOID rules. -ITEM: "item:"; +NUMBER_OR_NUMERICOID + : + ( NUMBER DOT ) => NUMERICOID + { + $setType(NUMERICOID); + } + | + NUMBER + { + $setType(NUMBER); + } + ; -BASE: "base" ( SP )+ ; +protected NUMBER: DIGIT | ( LDIGIT ( DIGIT )+ ) { log.debug( "matched NUMBER: " + getText() ); } ; -protected DIGIT: '0' | LDIGIT; +protected NUMERICOID: NUMBER ( DOT NUMBER )+ { log.debug( "matched NUMERICOID: " + getText() ); } ; -protected LDIGIT: '1'..'9'; +protected DOT: '.' ; -protected ALPHA: 'A'..'Z' | 'a'..'z'; +protected DIGIT: '0' | LDIGIT ; -protected NUMBER: DIGIT | ( LDIGIT ( DIGIT )+ ); +protected LDIGIT: '1'..'9' ; -protected NUMERICOID: NUMBER ( '.' NUMBER )+; +protected ALPHA: 'A'..'Z' | 'a'..'z' ; -protected DESCR: ALPHA ( ALPHA | DIGIT | '-' )*; - -// this is all messed up - could not figure out how to get antlr to represent +// This is all messed up - could not figure out how to get antlr to represent // the safe UTF-8 character set from RFC 3642 for production SafeUTF8Character protected SAFEUTF8CHAR: - '\u0001' .. '\u0021' | - '\u0023' .. '\u007F' | + '\u0001'..'\u0021' | + '\u0023'..'\u007F' | '\u00c0'..'\u00d6' | '\u00d8'..'\u00f6' | '\u00f8'..'\u00ff' | @@ -162,8 +474,3 @@ '\u3400'..'\u3d2d' | '\u4e00'..'\u9fff' | '\uf900'..'\ufaff' ; - -OID: DESCR | NUMERICOID; - -LOCALNAME: DQUOTE ( SAFEUTF8CHAR )* DQUOTE ; - Index: common/src/java/org/apache/ldap/common/subtree/SubtreeSpecificationParser.java =================================================================== --- common/src/java/org/apache/ldap/common/subtree/SubtreeSpecificationParser.java (revision 0) +++ common/src/java/org/apache/ldap/common/subtree/SubtreeSpecificationParser.java (revision 0) @@ -0,0 +1,109 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.ldap.common.subtree; + +import java.io.IOException; +import java.io.StringReader; +import java.text.ParseException; + +import antlr.RecognitionException; +import antlr.TokenStreamException; + + +/** + * A reusable wrapper around the antlr generated parser for an LDAP subtree + * specification as defined by + * RFC 3672. This class enables the reuse of the antlr parser/lexer pair + * without having to recreate the pair every time. + * + * @see RFC 3672 + * @author Apache Directory Project + * @version $Rev$ + */ +public class SubtreeSpecificationParser +{ + /** the antlr generated parser being wrapped */ + private ReusableAntlrSubtreeSpecificationParser parser; + /** the antlr generated lexer being wrapped */ + private ReusableAntlrSubtreeSpecificationLexer lexer; + + + /** + * Creates a subtree specification parser. + */ + public SubtreeSpecificationParser() + { + StringReader in = new StringReader(""); // place holder for the first input + this.lexer = new ReusableAntlrSubtreeSpecificationLexer( in ); + this.parser = new ReusableAntlrSubtreeSpecificationParser( lexer ); + } + + + /** + * Initializes the plumbing by creating a pipe and coupling the parser/lexer + * pair with it. + * + * param spec the specification to be parsed + */ + private synchronized void reset(String spec) + { + StringReader in = new StringReader( spec + "end" ); // append end of input token + this.lexer.prepareNextInput(in); + this.parser.resetState(); + } + + + /** + * Parses a subtree specification without exhausting the parser. + * + * @param spec the specification to be parsed + * @return the specification bean + * @throws ParseException if there are any recognition errors (bad syntax) + * @throws IOException if there is a problem with underlying streams + */ + public synchronized SubtreeSpecification parse( String spec ) throws ParseException, IOException + { + SubtreeSpecification ss = null; + + if ( spec == null || spec.trim().equals( "" ) ) + { + return null; + } + + reset(spec); // reset and initialize the parser / lexer pair + + try + { + ss = this.parser.wrapperEntryPoint(); + } + catch ( TokenStreamException e ) + { + String msg = "Parser failure on subtree specification:\n\t" + spec ; + msg += "\nAntlr exception trace:\n" + e.getMessage(); + throw new ParseException( msg, 0 ); + } + catch ( RecognitionException e ) + { + String msg = "Parser failure on subtree specification:\n\t" + spec ; + msg += "\nAntlr exception trace:\n" + e.getMessage(); + throw new ParseException( msg, e.getColumn() ); + } + + return ss; + } +} Index: common/src/java/org/apache/ldap/common/subtree/ReusableAntlrSubtreeSpecificationParser.java =================================================================== --- common/src/java/org/apache/ldap/common/subtree/ReusableAntlrSubtreeSpecificationParser.java (revision 0) +++ common/src/java/org/apache/ldap/common/subtree/ReusableAntlrSubtreeSpecificationParser.java (revision 0) @@ -0,0 +1,58 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +package org.apache.ldap.common.subtree; + + +import antlr.TokenStream; + + +/** + * A reusable parser class extended from antlr generated parser for an LDAP + * subtree specification as defined by + * RFC 3672. This class enables the reuse of the antlr parser without having to + * recreate the it every time as stated in + * + * a Antlr Interest Group mail . + * + * @see RFC 3672 + * @author Apache Directory Project + * @version $Rev$ + */ +class ReusableAntlrSubtreeSpecificationParser extends AntlrSubtreeSpecificationParser +{ + /** + * Creates a ReusableAntlrSubtreeSpecificationParser instance. + */ + public ReusableAntlrSubtreeSpecificationParser( 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: common/src/java/org/apache/ldap/common/subtree/ReusableAntlrSubtreeSpecificationLexer.java =================================================================== --- common/src/java/org/apache/ldap/common/subtree/ReusableAntlrSubtreeSpecificationLexer.java (revision 0) +++ common/src/java/org/apache/ldap/common/subtree/ReusableAntlrSubtreeSpecificationLexer.java (revision 0) @@ -0,0 +1,74 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +package org.apache.ldap.common.subtree; + + +import java.io.Reader; + +import antlr.CharBuffer; +import antlr.LexerSharedInputState; + + +/** + * A reusable lexer class extended from antlr generated lexer for an LDAP + * subtree specification as defined by + * RFC 3672. This class enables the reuse of the antlr lexer without having to + * recreate the it every time as stated in + * + * a Antlr Interest Group mail . + * + * @see RFC 3672 + * @author Apache Directory Project + * @version $Rev$ + */ +public class ReusableAntlrSubtreeSpecificationLexer extends AntlrSubtreeSpecificationLexer +{ + private boolean savedCaseSensitive; + private boolean savedCaseSensitiveLiterals; + + /** + * Creates a ReusableAntlrSubtreeSpecificationLexer instance. + * + * @param in the input to the lexer + */ + public ReusableAntlrSubtreeSpecificationLexer( 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; + } +}