// ----------------------------------------------------------------------------
// file header
// ----------------------------------------------------------------------------
header {
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* 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.felix.bundledb.filter;
import antlr.*;
import java.util.*;
}
// ----------------------------------------------------------------------------
// class definition
// ----------------------------------------------------------------------------
/**
* A filter assertion value encoding parser. This parser is used by the top
* filter parser to handle equality, extensible, presence and substring
* assertion values. It also participates in multiplexing the underlying
* token stream by driving filter assertion value lexer.
*
* @see String Representation of Search Filters
* @author Apache Directory Project
*/
class AntlrFilterValueParser extends Parser;
// ----------------------------------------------------------------------------
// parser options
// ----------------------------------------------------------------------------
options
{
k = 3;
importVocab = FilterValueLexer;
defaultErrorHandler=false;
}
// ----------------------------------------------------------------------------
// parser class members
// ----------------------------------------------------------------------------
{
/** the monitor used to track the activities of this parser */
FilterParserMonitor monitor;
/** the primary lexer used by the filter parser */
AntlrFilterLexer lexer;
/** the token stream selector for the filter parser */
TokenStreamSelector selector;
/**
* Sets the token stream select so we can use it to switch to the filter
* parsers primary lexer.
*
* @param selector the token stream selector for the filter parser
*/
public void setSelector( TokenStreamSelector selector )
{
this.selector = selector;
}
/**
* Sets the filter's main lexer so we can switch back to it.
*
* @param lexer the primary lexer used by the filter parser
*/
public void setLexer( AntlrFilterLexer lexer )
{
this.lexer = lexer;
}
/**
* Sets the monitor used to track the activities of this parser.
*
* @param monitor used to track the activities of this parser
*/
public void setFilterParserMonitor( FilterParserMonitor monitor )
{
this.monitor = monitor;
}
/**
* Monitors FilterParser events where it matches a production.
*
* @param production the name of the production matched
*/
private void matchedProduction( String production )
{
if ( this.monitor != null )
{
this.monitor.matchedProduction( production );
}
}
}
// ----------------------------------------------------------------------------
// parser rules
// ----------------------------------------------------------------------------
/**
* Parser production that calls either equal, presence or substring productions.
*/
value [String attribute] returns [Object node]
{
node = null;
}
: ( node=equal | node=presence[attribute] | node=substring[attribute] );
/**
* Parser production that parses the equality expression value immediately
* after the '=' in a filter equality assertion.
*/
equal returns [String retval]
{
retval = null;
}
: val:VALUEENCODING
{
selector.select( lexer );
retval = val.getText();
}
RPAREN;
/**
* Parser production that parses the presence expressions immediately after the
* '=' in a presence assertion.
*/
presence [String attribute] returns [PresenceNode node]
{
node = null;
}
: ASTERISK
{
selector.select( lexer );
node = new PresenceNode( attribute.trim() );
}
RPAREN;
/**
* Parser production that parses the substring expression value immediately
* after the '=' in a substring filter assertion.
*/
substring [String attribute] returns [LeafNode node]
{
node = null;
String initial = null;
String fin = null;
ArrayList any = new ArrayList();
}
:
(
// A L T E R N A T I V E 1: initial*
alt1:VALUEENCODING
{
initial = alt1.getText().trim();
}
ASTERISK
|
// A L T E R N A T I V E 2: (*any) *final
( ASTERISK alt2:VALUEENCODING
{
if ( fin != null && fin.length() > 0 )
{
any.add( fin );
}
fin = alt2.getText().trim();
}
)+ ( ASTERISK
{
if ( fin != null && fin.length() > 0 )
{
any.add( fin );
}
fin = null;
})?
|
// A L T E R N A T I V E 3: initial (*any) *final
alt3t0:VALUEENCODING
{
initial = alt3t0.getText().trim();
}
( ASTERISK alt3t1:VALUEENCODING
{
if ( fin != null && fin.length() > 0 )
{
any.add( fin );
}
fin = alt3t1.getText().trim();
}
)+
)
RPAREN
{
selector.select( lexer );
/*
* Under special circumstances a presence string can appear to be a
* SubstringNode. Namely the following too filters are not equal:
*
* (ou=*) != (ou=* )
*
* The first on the left hand side has no space between the '*' and the
* closing parenthesis. The 2nd on the right hand side has a space. So
* the question arrises how do we interpret this. Intuitively both
* would be considered the same and that's what we shall do until we ask
* on the LDAPBIS mailing list. However note that the first filter will
* be processed by the presence rule while the second will be processed
* by this rule the substring rule. That is because according to the
* parser white space is significant as it should be in the value
* encoding just not on the periphery. So as a substring the 2nd filter
* would be interpretted as a search for all ou values ending in a
* whitespace. Again intuitively this does not make sense. I would
* imagine it best if this is what was intended that the escape sequence
* '\20' be used for white space rather than using actual whitespace on
* the periphery.
*
* So what do we do to solve this problem. Well we need to characterize
* first the range of errors that are possible. First any one of these
* filters are valid presence filters (note whitespace differences):
* #1 #2 #3 #4
* (ou= * ) (ou= *) ( ou = * ) (ou=* )
*
* #1: initial = " ", any is empty, final = " "
* #2: initial = " ", any is empty, final = null
* #3: initial = " ", any is empty, final = " "
* #4: initial = null, any is empty, final = " "
*
* To handle this and generate the appropriate node type we check for
* null, empty strings, and empty any arrays in the condition below:
*/
if ( any.isEmpty()
&& ( initial == null || initial.trim().length() == 0 )
&& ( fin == null || fin.trim().length() == 0 ) )
{
node = new PresenceNode( attribute );
}
else
{
if ( initial != null && initial.length() == 0 )
{
initial = null;
}
if ( fin != null && fin.length() == 0 )
{
fin = null;
}
node = new SubstringNode( any, attribute, initial, fin );
}
}
;