blob: f49ee804c7b42b4dcdb2f1803baf3489baa3b8b8 [file] [log] [blame]
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
*
* https://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.api.ldap.model.name;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
import javax.naming.NameParser;
import org.apache.directory.api.ldap.model.entry.Value;
import org.apache.directory.api.ldap.model.schema.SchemaManager;
import org.apache.directory.api.ldap.model.schema.AttributeType;
import org.apache.directory.api.util.ExpansibleByteBuffer;
import org.apache.directory.api.util.Strings;
import org.apache.directory.api.util.Unicode;
}
/**
* An antlr generated Dn lexer.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
class AntlrDnLexer extends Lexer;
options {
k = 3 ;
exportVocab=AntlrDn ;
charVocabulary = '\u0000'..'\uFFFE';
caseSensitive = false ;
defaultErrorHandler = false ;
}
COMMA : ',' ;
EQUALS : '=' ;
PLUS : '+' ;
HYPHEN : '-' ;
UNDERSCORE : '_' ;
DQUOTE : '"' ;
SEMI : ';' ;
LANGLE : '<' ;
RANGLE : '>' ;
SPACE : ' ' ;
NUMERICOID_OR_ALPHA_OR_DIGIT
: ( NUMERICOID ) => NUMERICOID { $setType(NUMERICOID); }
| ( DIGIT ) => DIGIT { $setType(DIGIT); }
| ( ALPHA ) => ALPHA { $setType(ALPHA); }
;
protected NUMERICOID : ( "oid." )? NUMBER ( DOT NUMBER )+ ;
protected DOT: '.' ;
protected NUMBER: DIGIT | ( LDIGIT ( DIGIT )+ ) ;
protected LDIGIT : '1'..'9' ;
protected DIGIT : '0'..'9' ;
protected ALPHA : 'a'..'z' ;
HEXPAIR_OR_ESCESC_ESCSHARP_OR_ESC
: (ESC HEX HEX) => HEXPAIR { $setType(HEXPAIR); }
| ESCESC { $setType(ESCESC); }
| ESCSHARP { $setType(ESCSHARP); }
| ESC { $setType(ESC); }
;
protected HEXPAIR : ESC! HEX HEX ;
protected ESC : '\\';
protected ESCESC : ESC ESC;
protected ESCSHARP : ESC SHARP;
protected HEX: DIGIT | 'a'..'f' ;
HEXVALUE_OR_SHARP
: (SHARP ( HEX HEX )+) => HEXVALUE { $setType(HEXVALUE); }
| SHARP { $setType(SHARP); }
;
protected HEXVALUE : SHARP! ( HEX HEX )+ ;
protected SHARP: '#' ;
UTFMB : '\u0080'..'\uFFFE' ;
/**
* RFC 4514, Section 3:
* <pre>
* LUTF1 = %x01-1F / %x21 / %x24-2A / %x2D-3A /
* %x3D / %x3F-5B / %x5D-7F
*
* To avoid nondeterminism the following
* rules are excluded. These rules are
* explicitly added in the productions.
* EQUALS (0x3D)
* HYPHEN (0x2D)
* UNDERSCORE (0x5F)
* DIGIT (0x30-0x39)
* ALPHA (0x41-0x5A and 0x61-0x7A)
* </pre>
*
* @param _createToken If a Token is to be to created
* @throws RecognitionException If the token is invalid
* @throws TokenStreamException When we weren't able to fetch a token
* @throws CharStreamException If we can't process the stream
*/
CHAR_REST :
'\u0001'..'\u001F' |
'\u0021' |
'\u0024'..'\u002A' |
'\u002E'..'\u002F' |
'\u003A' |
'\u003F'..'\u0040' |
'\u005B' |
'\u005D'..'\u005E' |
'\u0060' |
'\u007B'..'\u007F'
;
/**
* An antlr generated Dn parser.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
class AntlrDnParser extends Parser;
options {
k = 3 ;
defaultErrorHandler = false ;
//buildAST=true ;
}
{
private void matchedProduction( String msg )
{
}
/**
* This class is used to store the decoded value
*/
private static class UpAndNormValue
{
// The value as a byte array
ExpansibleByteBuffer bytes = new ExpansibleByteBuffer();
// The user provided value
StringBuilder upValue = new StringBuilder();
// The normalized value
StringBuilder normValue = new StringBuilder();
// A flag set to false if we have a binary value
boolean isHR = true;
}
}
/**
* Parses a Dn string.
*
* RFC 4514, Section 3
* <pre>
* distinguishedName = [ relativeDistinguishedName
* *( COMMA relativeDistinguishedName ) ]
* </pre>
*
* RFC 2253, Section 3
* <pre>
* distinguishedName = [name]
* name = name-component *("," name-component)
* </pre>
*
* RFC 1779, Section 2.3
* <pre>
* &lt;name&gt; ::= &lt;name-component&gt; ( &lt;spaced-separator&gt; )
* | &lt;name-component&gt; &lt;spaced-separator&gt; &lt;name&gt;
* &lt;spaced-separator&gt; ::= &lt;optional-space&gt;
* &lt;separator&gt;
* &lt;optional-space&gt;
* &lt;separator&gt; ::= "," | ";"
* &lt;optional-space&gt; ::= ( &lt;CR&gt; ) *( " " )
* </pre>
*
* @param schemaManager The SchemaManager
* @param dn The Dn to update
* @throws RecognitionException If the token is invalid
* @throws TokenStreamException When we weren't able to fetch a token
*/
distinguishedName [SchemaManager schemaManager, Dn dn]
{
matchedProduction( "distinguishedName()" );
Rdn rdn = new Rdn( schemaManager );
}
:
(
relativeDistinguishedName[schemaManager, rdn]
{
try
{
dn.add( rdn );
}
catch ( LdapInvalidDnException lide )
{
// Do nothing, can't get an exception here
}
}
(
( COMMA | SEMI )
{
rdn = new Rdn( schemaManager );
}
relativeDistinguishedName[schemaManager, rdn]
{
try
{
dn.add( rdn );
}
catch ( LdapInvalidDnException lide )
{
// Do nothing, can't get an exception here
}
}
)*
EOF
)?
;
/**
* Parses a Dn string.
*
* RFC 4514, Section 3
* <pre>
* distinguishedName = [ relativeDistinguishedName
* *( COMMA relativeDistinguishedName ) ]
* </pre>
*
* RFC 2253, Section 3
* <pre>
* distinguishedName = [name]
* name = name-component *("," name-component)
* </pre>
*
* RFC 1779, Section 2.3
* <pre>
* &lt;name&gt; ::= &lt;name-component&gt; ( &lt;spaced-separator&gt; )
* | &lt;name-component&gt; &lt;spaced-separator&gt; &lt;name&gt;
* &lt;spaced-separator&gt; ::= &lt;optional-space&gt;
* &lt;separator&gt;
* &lt;optional-space&gt;
* &lt;separator&gt; ::= "," | ";"
* &lt;optional-space&gt; ::= ( &lt;CR&gt; ) *( " " )
* </pre>
*
* @param schemaManager The SchemaManager
* @param rdns The list of Rdns to update
* @return The normalized Dn
* @throws RecognitionException If the token is invalid
* @throws TokenStreamException When we weren't able to fetch a token
*/
relativeDistinguishedNames [SchemaManager schemaManager, List<Rdn> rdns] returns [String normNameStr]
{
matchedProduction( "relativeDistinguishedNames()" );
Rdn rdn = new Rdn( schemaManager );
StringBuilder dnNormSb = new StringBuilder();
}
:
(
relativeDistinguishedName[ schemaManager, rdn]
{
rdns.add( rdn );
dnNormSb.append( rdn.getNormName() );
rdn = new Rdn( schemaManager );
}
(
( COMMA | SEMI )
relativeDistinguishedName[schemaManager, rdn]
{
rdns.add( rdn );
dnNormSb.append( ',' );
dnNormSb.append( rdn.getNormName() );
rdn = new Rdn( schemaManager );
}
)*
EOF
)?
{
normNameStr = dnNormSb.toString();
}
;
/**
* Parses a Rdn string.
*
* RFC 4514, Section 3
* <pre>
* relativeDistinguishedName = attributeTypeAndValue
* *( PLUS attributeTypeAndValue )
* </pre>
*
* RFC 2253, Section 3
* <pre>
* name-component = attributeTypeAndValue *("+" attributeTypeAndValue)
* </pre>
*
* RFC 1779, Section 2.3
* <pre>
* &lt;name-component&gt; ::= &lt;attribute&gt;
* | &lt;attribute&gt; &lt;optional-space&gt; "+"
* &lt;optional-space&gt; &lt;name-component&gt;
* </pre>
*
* @param schemaManager The SchemaManager
* @param rdn The Rdn to update
* @throws RecognitionException If the token is invalid
* @throws TokenStreamException When we weren't able to fetch a token
*/
relativeDistinguishedName [SchemaManager schemaManager, Rdn rdn]
{
matchedProduction( "relativeDistinguishedName()" );
String tmp;
// The rdnStr variable is used to gather the full RDN string
// as provided
StringBuilder rdnStr = new StringBuilder();
StringBuilder rdnNormStr = new StringBuilder();
int avaPos = 0;
Ava currentAva;
AttributeType attributeType;
Value val;
}
:
(
tmp = attributeTypeAndValue[schemaManager, rdn]
{
rdnStr.append( tmp );
currentAva = rdn.getAva( avaPos );
attributeType = currentAva.getAttributeType();
if ( schemaManager == null )
{
rdnNormStr.append( currentAva.getNormType() );
rdnNormStr.append( '=' );
}
val = currentAva.getValue();
if ( ( schemaManager == null ) && ( val != null ) )
{
if ( val.getNormalized() != null )
{
rdnNormStr.append( val.getNormalized() );
}
else
{
// We can't tell if the value is HR or not.
// Use the Value User Provided value
rdnNormStr.append( val.getUpValue() );
}
}
}
(
PLUS
{
rdnStr.append( '+' );
if ( schemaManager == null )
{
rdnNormStr.append( '+' );
}
avaPos++;
}
tmp = attributeTypeAndValue[schemaManager, rdn]
{
rdnStr.append( tmp );
currentAva = rdn.getAva( avaPos );
attributeType = currentAva.getAttributeType();
if ( schemaManager == null )
{
rdnNormStr.append( tmp );
rdnNormStr.append( '=' );
}
val = currentAva.getValue();
if ( ( schemaManager == null ) && ( val != null ) )
{
if ( val.getNormalized() != null )
{
rdnNormStr.append( val.getNormalized() );
}
else
{
// We can't tell if the value is HR or not.
// Use the Value User Provided value
rdnNormStr.append( val.getUpValue() );
}
}
}
)*
)
{
rdn.hashCode();
rdn.setUpName( rdnStr.toString() );
if ( schemaManager != null )
{
// process the multi-value RDN, ordering them by attributes
boolean isFirst = true;
for ( Ava ava : rdn )
{
if ( isFirst )
{
isFirst = false;
}
else
{
rdnNormStr.append( '+' );
}
rdnNormStr.append( ava.getAttributeType().getOid() );
rdnNormStr.append( '=' );
val = ava.getValue();
if ( ( val != null ) && ( val.getNormalized() != null ) )
{
rdnNormStr.append( val.getNormalized() );
}
}
}
rdn.setNormName( rdnNormStr.toString() );
}
;
/**
* RFC 4514, Section 3
* <pre>
* attributeTypeAndValue = attributeType EQUALS attributeValue
* </pre>
*
* RFC 2253, Section 3
* <pre>
* attributeTypeAndValue = attributeType "=" attributeValue
* </pre>
*
* @param schemaManager The SchemaManager
* @param rdn The Rdn to update
* @return The user provided Ava
* @throws RecognitionException If the token is invalid
* @throws TokenStreamException When we weren't able to fetch a token
*/
attributeTypeAndValue [SchemaManager schemaManager, Rdn rdn] returns [String upNameStr]
{
matchedProduction( "attributeTypeAndValue()" );
String type = null;
UpAndNormValue value = new UpAndNormValue();
StringBuilder rdnUpName = new StringBuilder();
}
:
(
( SPACE { rdnUpName.append( ' ' ); } )*
type = attributeType { rdnUpName.append( type ); }
( SPACE { rdnUpName.append( ' ' ); } )*
EQUALS { rdnUpName.append( '=' ); }
( SPACE { rdnUpName.append( ' ' ); } )*
attributeValue[value]
{
try
{
// We have to remove the ending spaces that may have been added, as the tutf1 rule
// cannot be processed
rdnUpName.append( value.upValue );
AttributeType attributeType = null;
Ava ava = null;
if ( schemaManager != null )
{
if ( ( type.startsWith( "oid." ) ) || ( type.startsWith( "OID." ) ) )
{
type = type.substring( 4 );
}
attributeType = schemaManager.getAttributeType( type );
}
if ( ( ( attributeType != null ) && attributeType.isHR() ) || value.isHR )
{
int valueLength = value.upValue.length();
int pos = value.bytes.position();
for ( int i = valueLength - 1; i >= 0; i-- )
{
if ( value.upValue.charAt( i ) == ' ' )
{
if ( i == 0 )
{
// The value is empty
ava = new Ava( schemaManager, type, rdnUpName.toString(), ( String ) null );
break;
}
else if ( value.upValue.charAt( i - 1 ) != '\\' )
{
// This is a trailing space, get rid of it
value.upValue.deleteCharAt( i );
pos--;
value.bytes.position( pos );
}
else
{
// This is an escaped space, get out
break;
}
}
else
{
break;
}
}
if ( ava == null )
{
ava = new Ava( schemaManager, type, rdnUpName.toString(), Strings.utf8ToString( value.bytes.copyOfUsedBytes() ) );
}
}
else
{
ava = new Ava( schemaManager, type, rdnUpName.toString(), value.bytes.copyOfUsedBytes() );
}
rdn.addAVA( schemaManager, ava );
}
catch ( LdapInvalidDnException e )
{
throw new SemanticException( e.getMessage() );
}
}
( SPACE { rdnUpName.append( ' ' ); } )*
)
{
upNameStr = rdnUpName.toString();
}
;
/**
* RFC 4514 Section 3
*
* <pre>
* attributeType = descr / numericoid
* </pre>
*
* @return The AttributeType
* @throws RecognitionException If the token is invalid
* @throws TokenStreamException When we weren't able to fetch a token
*/
attributeType returns [String attributeType]
{
matchedProduction( "attributeType()" );
}
:
(
attributeType = descr
|
attributeType = numericoid
)
;
/**
* RFC 4512 Section 1.4
*
* <pre>
* descr = keystring
* keystring = leadkeychar *keychar
* leadkeychar = ALPHA
* keychar = ALPHA / DIGIT / HYPHEN
* </pre>
*
* We additionally add UNDERSCORE because some servers allow them.
*
* @return The description
* @throws RecognitionException If the token is invalid
* @throws TokenStreamException When we weren't able to fetch a token
*/
descr returns [String descr]
{
matchedProduction( "descr()" );
StringBuilder descrSb = new StringBuilder();
}
:
leadkeychar:ALPHA { descrSb.append( leadkeychar.getText() ); }
(
alpha:ALPHA { descrSb.append( alpha.getText() ); }
|
digit:DIGIT { descrSb.append( digit.getText() ); }
|
HYPHEN { descrSb.append( '-' ); }
|
UNDERSCORE { descrSb.append( '_' ); }
)*
{
descr = descrSb.toString();
}
;
/**
* RFC 4512 Section 1.4
*
* <pre>
* numericoid = number 1*( DOT number )
* number = DIGIT / ( LDIGIT 1*DIGIT )
* DIGIT = %x30 / LDIGIT ; "0"-"9"
* LDIGIT = %x31-39 ; "1"-"9"
* </pre>
*
* @return The numeric OID
* @throws RecognitionException If the token is invalid
* @throws TokenStreamException When we weren't able to fetch a token
*/
numericoid returns [String numericoid = ""]
{
matchedProduction( "numericoid()" );
}
:
noid:NUMERICOID { numericoid = noid.getText(); }
;
/**
* RFC 4514, Section 3
* <pre>
* attributeValue = string / hexstring
* </pre>
*
* RFC 2253, Section 3
* <pre>
* attributeValue = string
* string = *( stringchar / pair )
* / "#" hexstring
* / QUOTATION *( quotechar / pair ) QUOTATION ; only from v2
*
* We still accept both forms, which means we can have a value surrounded by '"'
* </pre>
*
* @param value The value to update
* @throws RecognitionException If the token is invalid
* @throws TokenStreamException When we weren't able to fetch a token
*/
attributeValue [UpAndNormValue value]
{
matchedProduction( "attributeValue()" );
}
:
(
// Special for RFC 2253
quotestring [value]
|
string [value]
|
hexstring [value]
)?
;
/**
* RFC 2253, Section 3
* <pre>
* / QUOTATION *( quotechar / pair ) QUOTATION ; only from v2
* quotechar = &lt;any character except "\" or QUOTATION &gt;
* </pre>
*
* @param value The value to update
* @throws RecognitionException If the token is invalid
* @throws TokenStreamException When we weren't able to fetch a token
*/
quotestring [UpAndNormValue value]
{
matchedProduction( "quotestring()" );
}
:
(
DQUOTE { value.upValue.append( '"' ); }
(
(
s:~(DQUOTE|ESC|ESCESC|ESCSHARP|HEXPAIR)
{
value.upValue.append( s.getText() );
value.bytes.append( Strings.getBytesUtf8( s.getText() ) );
}
)
|
pair [value]
)*
DQUOTE { value.upValue.append( '"' ); }
)
;
/**
* RFC 4514 Section 3
*
* <pre>
* hexstring = SHARP 1*hexpair
*
* If in &lt;hexstring&gt; form, a BER representation can be obtained from
* converting each &lt;hexpair&gt; of the &lt;hexstring&gt; to the octet indicated
* by the &lt;hexpair&gt;.
* </pre>
*
* @param value The value to update
* @throws RecognitionException If the token is invalid
* @throws TokenStreamException When we weren't able to fetch a token
*/
hexstring [UpAndNormValue value]
{
matchedProduction( "hexstring()" );
}
:
hexValue:HEXVALUE
{
String hexStr = hexValue.getText();
value.upValue.append( '#' ).append( hexStr );
value.bytes.append( Strings.toByteArray( hexStr ) );
value.isHR = false;
}
;
/**
* RFC 4514 Section 3
*
* <pre>
* ; The following characters are to be escaped when they appear
* ; in the value to be encoded: ESC, one of &lt;escaped&gt;, &lt;leading&gt;
* ; SHARP or SPACE, trailing SPACE, and NULL.
* string = [ ( leadchar / pair ) [ *( stringchar / pair ) ( trailchar / pair ) ] ]
* leadchar = LUTF1 | UTFMB
* stringchar = SUTF1 / UTFMB
* trailchar = TUTF1 / UTFMB
* </pre>
*
* @param value The value to update
* @throws RecognitionException If the token is invalid
* @throws TokenStreamException When we weren't able to fetch a token
*/
string [UpAndNormValue value]
{
matchedProduction( "string()" );
}
:
(
// Note that we don't distinguish between sutf1 and tutf1, as it would be ambiguous.
// The final spaces will be handled later.
( lutf1 [value] | utfmb [value] | pair [value] ) ( sutf1 [value] | utfmb [value] | pair [value] )*
)
;
/**
* RFC 4514, Section 3:
* <pre>
* LUTF1 = %x01-1F / %x21 / %x24-2A / %x2D-3A /
* %x3D / %x3F-5B / %x5D-7F
*
* The rule CHAR_REST doesn't contain the following charcters,
* so we must check them additionally
* EQUALS (0x3D)
* HYPHEN (0x2D)
* UNDERSCORE (0x5F)
* DIGIT (0x30-0x39)
* ALPHA (0x41-0x5A and 0x61-0x7A)
* </pre>
*
* @param value The value to update
* @throws RecognitionException If the token is invalid
* @throws TokenStreamException When we weren't able to fetch a token
*/
lutf1 [UpAndNormValue value]
{
matchedProduction( "lutf1()" );
}
:
rest:CHAR_REST
{
char c = rest.getText().charAt( 0 );
value.upValue.append( c );
value.bytes.append( ( byte ) c );
}
|
EQUALS
{
value.upValue.append( '=' );
value.bytes.append( '=' );
}
|
HYPHEN
{
value.upValue.append( '-' );
value.bytes.append( '-' );
}
|
UNDERSCORE
{
value.upValue.append( '_' );
value.bytes.append( '_' );
}
|
digit:DIGIT
{
char c = digit.getText().charAt( 0 );
value.upValue.append( c );
value.bytes.append( ( byte ) c );
}
|
alpha:ALPHA
{
char c = alpha.getText().charAt( 0 );
value.upValue.append( c );
value.bytes.append( ( byte ) c );
}
|
// Another hack : having a String like 127.0.0.1 in the value
// will not match a DIGIT, because it's swallowed by the NUMERICOID
// token
numericoid:NUMERICOID
{
String number = numericoid.getText();
value.upValue.append( number );
value.bytes.append( Strings.getBytesUtf8( number ) );
}
;
/**
* RFC 4514, Section 3:
* <pre>
* SUTF1 = %x01-21 / %x23-2A / %x2D-3A /
* %x3D / %x3F-5B / %x5D-7F
*
* The rule CHAR_REST doesn't contain the following charcters,
* so we must check them additionally
* EQUALS (0x3D)
* HYPHEN (0x2D)
* UNDERSCORE (0x5F)
* DIGIT (0x30-0x39)
* ALPHA (0x41-0x5A and 0x61-0x7A)
* SHARP (0x23)
* SPACE (0x20)
* </pre>
*
* @param value The value to update
* @throws RecognitionException If the token is invalid
* @throws TokenStreamException When we weren't able to fetch a token
*/
sutf1 [UpAndNormValue value]
{
matchedProduction( "sutf1()" );
}
:
rest:CHAR_REST
{
char c = rest.getText().charAt( 0 );
value.upValue.append( c );
value.bytes.append( ( byte ) c );
}
|
EQUALS
{
value.upValue.append( '=' );
value.bytes.append( '=' );
}
|
HYPHEN
{
value.upValue.append( '-' );
value.bytes.append( '-' );
}
|
UNDERSCORE
{
value.upValue.append( '_' );
value.bytes.append( '_' );
}
|
digit:DIGIT
{
char c = digit.getText().charAt( 0 );
value.upValue.append( c );
value.bytes.append( ( byte ) c );
}
|
alpha:ALPHA
{
char c = alpha.getText().charAt( 0 );
value.upValue.append( c );
value.bytes.append( ( byte ) c );
}
|
SHARP
{
value.upValue.append( '#' );
value.bytes.append( '#' );
}
|
SPACE
{
value.upValue.append( ' ' );
value.bytes.append( ' ' );
}
|
// This is a hack to deal with #NN included into the value, due to
// some collision with the HEXVALUE token. In this case, we should
// consider that a hex value is in fact a String
hex:HEXVALUE
{
String hexStr = hex.getText();
value.upValue.append( '#' ).append( hexStr );
value.bytes.append( '#' );
value.bytes.append( Strings.getBytesUtf8( hexStr ) );
}
|
// Another hack : having a String like 127.0.0.1 in the value
// will not match a DIGIT, because it's swallowed by the NUMERICOID
// token
numericoid:NUMERICOID
{
String number = numericoid.getText();
value.upValue.append( number );
value.bytes.append( Strings.getBytesUtf8( number ) );
}
;
/**
* Process a UTFMB char
*
* @param value The value to update
* @throws RecognitionException If the token is invalid
* @throws TokenStreamException When we weren't able to fetch a token
*/
utfmb [UpAndNormValue value]
{
matchedProduction( "utfmb()" );
}
:
s:UTFMB
{
char c = s.getText().charAt( 0 );
value.upValue.append( c );
value.bytes.append( Unicode.charToBytes( c ) );
}
;
/**
* RFC 4514, Section 3
* <pre>
* pair = ESC ( ESC / special / hexpair )
* special = escaped / SPACE / SHARP / EQUALS
* escaped = DQUOTE / PLUS / COMMA / SEMI / LANGLE / RANGLE
* hexpair = HEX HEX
*
* If in &lt;string&gt; form, a LDAP string representation asserted value can
* be obtained by replacing (left to right, non-recursively) each &lt;pair&gt;
* appearing in the &lt;string&gt; as follows:
* replace &lt;ESC&gt;&lt;ESC&gt; with &lt;ESC&gt;;
* replace &lt;ESC&gt;&lt;special&gt; with &lt;special&gt;;
* replace &lt;ESC&gt;&lt;hexpair&gt; with the octet indicated by the &lt;hexpair&gt;.
* </pre>
*
* RFC 2253, Section 3
* <pre>
* pair = "\" ( special / "\" / QUOTATION / hexpair )
* special = "," / "=" / "+" / "&lt;" / "&gt;" / "#" / ";"
* </pre>
*
* RFC 1779, Section 2.3
* <pre>
* &lt;pair&gt; ::= "\" ( &lt;special&gt; | "\" | '"')
* &lt;special&gt; ::= "," | "=" | &lt;CR&gt; | "+" | "&lt;" | "&gt;"
* | "#" | ";"
* </pre>
*
* @param value The value to update
* @throws RecognitionException If the token is invalid
* @throws TokenStreamException When we weren't able to fetch a token
*/
pair [UpAndNormValue value]
{
matchedProduction( "pair()" );
char specialChar;
}
:
(
ESCESC
{
value.upValue.append( "\\\\" );
value.bytes.append( '\\' );
}
)
|
(
ESCSHARP
{
value.upValue.append( "\\#" );
value.bytes.append( '#' );
}
)
|
(
ESC
specialChar = special
{
value.upValue.append( '\\' ).append( specialChar );
value.bytes.append( specialChar );
}
)
|
(
// A String like "\C4", corresponding to the hex value 0xC4.
hexpair:HEXPAIR
{
value.upValue.append( '\\' ).append( hexpair.getText() );
value.bytes.append( Strings.toByteArray( hexpair.getText() ) );
}
)
;
/**
* RFC 4514 Section 3
*
* <pre>
* special = escaped / SPACE / SHARP / EQUALS
* escaped = DQUOTE / PLUS / COMMA / SEMI / LANGLE / RANGLE
* </pre>
*
* @return The special char
* @throws RecognitionException If the token is invalid
* @throws TokenStreamException When we weren't able to fetch a token
*/
special returns [char special]
{
matchedProduction( "()" );
}
:
(
DQUOTE { special = '"'; }
|
PLUS { special = '+'; }
|
COMMA { special = ','; }
|
SEMI { special = ';'; }
|
LANGLE { special = '<'; }
|
RANGLE { special = '>'; }
|
SPACE { special = ' '; }
|
SHARP { special = '#'; }
|
EQUALS { special = '='; }
)
;