- 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 parseDn() throws InvalidNameException {
- cur = 0;
-
- // ArrayList<Rdn> rdns =
- // new ArrayList<Rdn>(len / 3 + 10); // leave room for growth
-
- ArrayList 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');
- }
- }