/* * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */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'); } }
/*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
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');
}
}