/*
* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package javax.naming.ldap;
import java.util.
List;
import java.util.
ArrayList;
import javax.naming.
InvalidNameException;
/*
* RFC2253Parser implements a recursive descent parser for a single DN.
*/
final class
Rfc2253Parser {
private final
String name; // DN being parsed
private final char[]
chars; // characters in LDAP name being parsed
private final int
len; // length of "chars"
private int
cur = 0; // index of first unconsumed char in "chars"
/*
* Given an LDAP DN in string form, returns a parser for it.
*/
Rfc2253Parser(
String name) {
this.
name =
name;
len =
name.
length();
chars =
name.
toCharArray();
}
/*
* Parses the DN, returning a List of its RDNs.
*/
// public List<Rdn> getDN() throws InvalidNameException {
List<
Rdn>
parseDn() throws
InvalidNameException {
cur = 0;
// ArrayList<Rdn> rdns =
// new ArrayList<Rdn>(len / 3 + 10); // leave room for growth
ArrayList<
Rdn>
rdns =
new
ArrayList<>(
len / 3 + 10); // leave room for growth
if (
len == 0) {
return
rdns;
}
rdns.
add(
doParse(new
Rdn()));
while (
cur <
len) {
if (
chars[
cur] == ',' ||
chars[
cur] == ';') {
++
cur;
rdns.
add(0,
doParse(new
Rdn()));
} else {
throw new
InvalidNameException("Invalid name: " +
name);
}
}
return
rdns;
}
/*
* Parses the DN, if it is known to contain a single RDN.
*/
Rdn parseRdn() throws
InvalidNameException {
return
parseRdn(new
Rdn());
}
/*
* Parses the DN, if it is known to contain a single RDN.
*/
Rdn parseRdn(
Rdn rdn) throws
InvalidNameException {
rdn =
doParse(
rdn);
if (
cur <
len) {
throw new
InvalidNameException("Invalid RDN: " +
name);
}
return
rdn;
}
/*
* Parses the next RDN and returns it. Throws an exception if
* none is found. Leading and trailing whitespace is consumed.
*/
private
Rdn doParse(
Rdn rdn) throws
InvalidNameException {
while (
cur <
len) {
consumeWhitespace();
String attrType =
parseAttrType();
consumeWhitespace();
if (
cur >=
len ||
chars[
cur] != '=') {
throw new
InvalidNameException("Invalid name: " +
name);
}
++
cur; // consume '='
consumeWhitespace();
String value =
parseAttrValue();
consumeWhitespace();
rdn.
put(
attrType,
Rdn.
unescapeValue(
value));
if (
cur >=
len ||
chars[
cur] != '+') {
break;
}
++
cur; // consume '+'
}
rdn.
sort();
return
rdn;
}
/*
* Returns the attribute type that begins at the next unconsumed
* char. No leading whitespace is expected.
* This routine is more generous than RFC 2253. It accepts
* attribute types composed of any nonempty combination of Unicode
* letters, Unicode digits, '.', '-', and internal space characters.
*/
private
String parseAttrType() throws
InvalidNameException {
final int
beg =
cur;
while (
cur <
len) {
char
c =
chars[
cur];
if (
Character.
isLetterOrDigit(
c) ||
c == '.' ||
c == '-' ||
c == ' ') {
++
cur;
} else {
break;
}
}
// Back out any trailing spaces.
while ((
cur >
beg) && (
chars[
cur - 1] == ' ')) {
--
cur;
}
if (
beg ==
cur) {
throw new
InvalidNameException("Invalid name: " +
name);
}
return new
String(
chars,
beg,
cur -
beg);
}
/*
* Returns the attribute value that begins at the next unconsumed
* char. No leading whitespace is expected.
*/
private
String parseAttrValue() throws
InvalidNameException {
if (
cur <
len &&
chars[
cur] == '#') {
return
parseBinaryAttrValue();
} else if (
cur <
len &&
chars[
cur] == '"') {
return
parseQuotedAttrValue();
} else {
return
parseStringAttrValue();
}
}
private
String parseBinaryAttrValue() throws
InvalidNameException {
final int
beg =
cur;
++
cur; // consume '#'
while ((
cur <
len) &&
Character.
isLetterOrDigit(
chars[
cur])) {
++
cur;
}
return new
String(
chars,
beg,
cur -
beg);
}
private
String parseQuotedAttrValue() throws
InvalidNameException {
final int
beg =
cur;
++
cur; // consume '"'
while ((
cur <
len) &&
chars[
cur] != '"') {
if (
chars[
cur] == '\\') {
++
cur; // consume backslash, then what follows
}
++
cur;
}
if (
cur >=
len) { // no closing quote
throw new
InvalidNameException("Invalid name: " +
name);
}
++
cur; // consume closing quote
return new
String(
chars,
beg,
cur -
beg);
}
private
String parseStringAttrValue() throws
InvalidNameException {
final int
beg =
cur;
int
esc = -1; // index of the most recently escaped character
while ((
cur <
len) && !
atTerminator()) {
if (
chars[
cur] == '\\') {
++
cur; // consume backslash, then what follows
esc =
cur;
}
++
cur;
}
if (
cur >
len) { // 'twas backslash followed by nothing
throw new
InvalidNameException("Invalid name: " +
name);
}
// Trim off (unescaped) trailing whitespace.
int
end;
for (
end =
cur;
end >
beg;
end--) {
if (!
isWhitespace(
chars[
end - 1]) || (
esc ==
end - 1)) {
break;
}
}
return new
String(
chars,
beg,
end -
beg);
}
private void
consumeWhitespace() {
while ((
cur <
len) &&
isWhitespace(
chars[
cur])) {
++
cur;
}
}
/*
* Returns true if next unconsumed character is one that terminates
* a string attribute value.
*/
private boolean
atTerminator() {
return (
cur <
len &&
(
chars[
cur] == ',' ||
chars[
cur] == ';' ||
chars[
cur] == '+'));
}
/*
* Best guess as to what RFC 2253 means by "whitespace".
*/
private static boolean
isWhitespace(char
c) {
return (
c == ' ' ||
c == '\r');
}
}