Path: blob/master/src/java.naming/share/classes/javax/naming/ldap/Rfc2253Parser.java
41159 views
/*1* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package javax.naming.ldap;2627import java.util.List;28import java.util.ArrayList;2930import javax.naming.InvalidNameException;3132/*33* RFC2253Parser implements a recursive descent parser for a single DN.34*/35final class Rfc2253Parser {3637private final String name; // DN being parsed38private final char[] chars; // characters in LDAP name being parsed39private final int len; // length of "chars"40private int cur = 0; // index of first unconsumed char in "chars"4142/*43* Given an LDAP DN in string form, returns a parser for it.44*/45Rfc2253Parser(String name) {46this.name = name;47len = name.length();48chars = name.toCharArray();49}5051/*52* Parses the DN, returning a List of its RDNs.53*/54// public List<Rdn> getDN() throws InvalidNameException {5556List<Rdn> parseDn() throws InvalidNameException {57cur = 0;5859// ArrayList<Rdn> rdns =60// new ArrayList<Rdn>(len / 3 + 10); // leave room for growth6162ArrayList<Rdn> rdns =63new ArrayList<>(len / 3 + 10); // leave room for growth6465if (len == 0) {66return rdns;67}6869rdns.add(doParse(new Rdn()));70while (cur < len) {71if (chars[cur] == ',' || chars[cur] == ';') {72++cur;73rdns.add(0, doParse(new Rdn()));74} else {75throw new InvalidNameException("Invalid name: " + name);76}77}78return rdns;79}8081/*82* Parses the DN, if it is known to contain a single RDN.83*/84Rdn parseRdn() throws InvalidNameException {85return parseRdn(new Rdn());86}8788/*89* Parses the DN, if it is known to contain a single RDN.90*/91Rdn parseRdn(Rdn rdn) throws InvalidNameException {92rdn = doParse(rdn);93if (cur < len) {94throw new InvalidNameException("Invalid RDN: " + name);95}96return rdn;97}9899/*100* Parses the next RDN and returns it. Throws an exception if101* none is found. Leading and trailing whitespace is consumed.102*/103private Rdn doParse(Rdn rdn) throws InvalidNameException {104105while (cur < len) {106consumeWhitespace();107String attrType = parseAttrType();108consumeWhitespace();109if (cur >= len || chars[cur] != '=') {110throw new InvalidNameException("Invalid name: " + name);111}112++cur; // consume '='113consumeWhitespace();114String value = parseAttrValue();115consumeWhitespace();116117rdn.put(attrType, Rdn.unescapeValue(value));118if (cur >= len || chars[cur] != '+') {119break;120}121++cur; // consume '+'122}123rdn.sort();124return rdn;125}126127/*128* Returns the attribute type that begins at the next unconsumed129* char. No leading whitespace is expected.130* This routine is more generous than RFC 2253. It accepts131* attribute types composed of any nonempty combination of Unicode132* letters, Unicode digits, '.', '-', and internal space characters.133*/134private String parseAttrType() throws InvalidNameException {135136final int beg = cur;137while (cur < len) {138char c = chars[cur];139if (Character.isLetterOrDigit(c) ||140c == '.' ||141c == '-' ||142c == ' ') {143++cur;144} else {145break;146}147}148// Back out any trailing spaces.149while ((cur > beg) && (chars[cur - 1] == ' ')) {150--cur;151}152153if (beg == cur) {154throw new InvalidNameException("Invalid name: " + name);155}156return new String(chars, beg, cur - beg);157}158159/*160* Returns the attribute value that begins at the next unconsumed161* char. No leading whitespace is expected.162*/163private String parseAttrValue() throws InvalidNameException {164165if (cur < len && chars[cur] == '#') {166return parseBinaryAttrValue();167} else if (cur < len && chars[cur] == '"') {168return parseQuotedAttrValue();169} else {170return parseStringAttrValue();171}172}173174private String parseBinaryAttrValue() throws InvalidNameException {175final int beg = cur;176++cur; // consume '#'177while ((cur < len) &&178Character.isLetterOrDigit(chars[cur])) {179++cur;180}181return new String(chars, beg, cur - beg);182}183184private String parseQuotedAttrValue() throws InvalidNameException {185186final int beg = cur;187++cur; // consume '"'188189while ((cur < len) && chars[cur] != '"') {190if (chars[cur] == '\\') {191++cur; // consume backslash, then what follows192}193++cur;194}195if (cur >= len) { // no closing quote196throw new InvalidNameException("Invalid name: " + name);197}198++cur; // consume closing quote199200return new String(chars, beg, cur - beg);201}202203private String parseStringAttrValue() throws InvalidNameException {204205final int beg = cur;206int esc = -1; // index of the most recently escaped character207208while ((cur < len) && !atTerminator()) {209if (chars[cur] == '\\') {210++cur; // consume backslash, then what follows211esc = cur;212}213++cur;214}215if (cur > len) { // 'twas backslash followed by nothing216throw new InvalidNameException("Invalid name: " + name);217}218219// Trim off (unescaped) trailing whitespace.220int end;221for (end = cur; end > beg; end--) {222if (!isWhitespace(chars[end - 1]) || (esc == end - 1)) {223break;224}225}226return new String(chars, beg, end - beg);227}228229private void consumeWhitespace() {230while ((cur < len) && isWhitespace(chars[cur])) {231++cur;232}233}234235/*236* Returns true if next unconsumed character is one that terminates237* a string attribute value.238*/239private boolean atTerminator() {240return (cur < len &&241(chars[cur] == ',' ||242chars[cur] == ';' ||243chars[cur] == '+'));244}245246/*247* Best guess as to what RFC 2253 means by "whitespace".248*/249private static boolean isWhitespace(char c) {250return (c == ' ' || c == '\r');251}252}253254255