253 lines · 8.5 KB · Java
RawDownload
/* * 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');        }    }