Source Code Cross Referenced for LdapName.java in  » 6.0-JDK-Modules-com.sun » jndi » com » sun » jndi » ldap » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » 6.0 JDK Modules com.sun » jndi » com.sun.jndi.ldap 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Copyright 1999-2004 Sun Microsystems, Inc.  All Rights Reserved.
0003:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004:         *
0005:         * This code is free software; you can redistribute it and/or modify it
0006:         * under the terms of the GNU General Public License version 2 only, as
0007:         * published by the Free Software Foundation.  Sun designates this
0008:         * particular file as subject to the "Classpath" exception as provided
0009:         * by Sun in the LICENSE file that accompanied this code.
0010:         *
0011:         * This code is distributed in the hope that it will be useful, but WITHOUT
0012:         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013:         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
0014:         * version 2 for more details (a copy is included in the LICENSE file that
0015:         * accompanied this code).
0016:         *
0017:         * You should have received a copy of the GNU General Public License version
0018:         * 2 along with this work; if not, write to the Free Software Foundation,
0019:         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020:         *
0021:         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022:         * CA 95054 USA or visit www.sun.com if you need additional information or
0023:         * have any questions.
0024:         */
0025:
0026:        package com.sun.jndi.ldap;
0027:
0028:        import java.util.Enumeration;
0029:        import java.util.Vector;
0030:
0031:        import javax.naming.*;
0032:        import javax.naming.directory.Attributes;
0033:        import javax.naming.directory.Attribute;
0034:        import javax.naming.directory.BasicAttributes;
0035:
0036:        /**
0037:         * <code>LdapName</code> implements compound names for LDAP v3 as
0038:         * specified by RFC 2253.
0039:         *<p>
0040:         * RFC 2253 has a few ambiguities and outright inconsistencies.  These
0041:         * are resolved as follows:
0042:         * <ul>
0043:         * <li>	RFC 2253 leaves the term "whitespace" undefined.  The
0044:         *	definition of "optional-space" given in RFC 1779 is used in
0045:         *	its place:  either a space character or a carriage return ("\r").
0046:         * <li>	Whitespace is allowed on either side of ',', ';', '=', and '+'.
0047:         *	Such whitespace is accepted but not generated by this code,
0048:         *	and is ignored when comparing names.
0049:         * <li>	AttributeValue strings containing '=' or non-leading '#'
0050:         *	characters (unescaped) are accepted.
0051:         * </ul>
0052:         *<p>
0053:         * String names passed to <code>LdapName</code> or returned by it
0054:         * use the full 16-bit Unicode character set.  They may also contain
0055:         * characters encoded into UTF-8 with each octet represented by a
0056:         * three-character substring such as "\\B4".
0057:         * They may not, however, contain characters encoded into UTF-8 with
0058:         * each octet represented by a single character in the string:  the
0059:         * meaning would be ambiguous.
0060:         *<p>
0061:         * <code>LdapName</code> will properly parse all valid names, but
0062:         * does not attempt to detect all possible violations when parsing
0063:         * invalid names.  It's "generous".
0064:         *<p>
0065:         * When names are tested for equality, attribute types and binary
0066:         * values are case-insensitive, and string values are by default
0067:         * case-insensitive.
0068:         * String values with different but equivalent usage of quoting,
0069:         * escaping, or UTF8-hex-encoding are considered equal.  The order of
0070:         * components in multi-valued RDNs (such as "ou=Sales+cn=Bob") is not
0071:         * significant.
0072:         *
0073:         * @author Scott Seligman
0074:         */
0075:
0076:        public final class LdapName implements  Name {
0077:
0078:            private transient String unparsed; // if non-null, the DN in unparsed form
0079:            private transient Vector rdns; // parsed name components
0080:            private transient boolean valuesCaseSensitive = false;
0081:
0082:            /**
0083:             * Constructs an LDAP name from the given DN.
0084:             *
0085:             * @param name	An LDAP DN.  To JNDI, a compound name.
0086:             *
0087:             * @throws InvalidNameException if a syntax violation is detected.
0088:             */
0089:            public LdapName(String name) throws InvalidNameException {
0090:                unparsed = name;
0091:                parse();
0092:            }
0093:
0094:            /*
0095:             * Constructs an LDAP name given its parsed components and, optionally
0096:             * (if "name" is not null), the unparsed DN.
0097:             */
0098:            private LdapName(String name, Vector rdns) {
0099:                unparsed = name;
0100:                this .rdns = (Vector) rdns.clone();
0101:            }
0102:
0103:            /*
0104:             * Constructs an LDAP name given its parsed components (the elements
0105:             * of "rdns" in the range [beg,end)) and, optionally
0106:             * (if "name" is not null), the unparsed DN.
0107:             */
0108:            private LdapName(String name, Vector rdns, int beg, int end) {
0109:                unparsed = name;
0110:                this .rdns = new Vector();
0111:                for (int i = beg; i < end; i++) {
0112:                    this .rdns.addElement(rdns.elementAt(i));
0113:                }
0114:            }
0115:
0116:            public Object clone() {
0117:                return new LdapName(unparsed, rdns);
0118:            }
0119:
0120:            public String toString() {
0121:                if (unparsed != null) {
0122:                    return unparsed;
0123:                }
0124:
0125:                StringBuffer buf = new StringBuffer();
0126:                for (int i = rdns.size() - 1; i >= 0; i--) {
0127:                    if (i < rdns.size() - 1) {
0128:                        buf.append(',');
0129:                    }
0130:                    Rdn rdn = (Rdn) rdns.elementAt(i);
0131:                    buf.append(rdn);
0132:                }
0133:
0134:                unparsed = new String(buf);
0135:                return unparsed;
0136:            }
0137:
0138:            public boolean equals(Object obj) {
0139:                return ((obj instanceof  LdapName) && (compareTo(obj) == 0));
0140:            }
0141:
0142:            public int compareTo(Object obj) {
0143:                LdapName that = (LdapName) obj;
0144:
0145:                if ((obj == this ) || // check possible shortcuts
0146:                        (unparsed != null && unparsed.equals(that.unparsed))) {
0147:                    return 0;
0148:                }
0149:
0150:                // Compare RDNs one by one, lexicographically.
0151:                int minSize = Math.min(rdns.size(), that.rdns.size());
0152:                for (int i = 0; i < minSize; i++) {
0153:                    // Compare a single pair of RDNs.
0154:                    Rdn rdn1 = (Rdn) rdns.elementAt(i);
0155:                    Rdn rdn2 = (Rdn) that.rdns.elementAt(i);
0156:
0157:                    int diff = rdn1.compareTo(rdn2);
0158:                    if (diff != 0) {
0159:                        return diff;
0160:                    }
0161:                }
0162:                return (rdns.size() - that.rdns.size()); // longer DN wins
0163:            }
0164:
0165:            public int hashCode() {
0166:                // Sum up the hash codes of the components.
0167:                int hash = 0;
0168:
0169:                // For each RDN...
0170:                for (int i = 0; i < rdns.size(); i++) {
0171:                    Rdn rdn = (Rdn) rdns.elementAt(i);
0172:                    hash += rdn.hashCode();
0173:                }
0174:                return hash;
0175:            }
0176:
0177:            public int size() {
0178:                return rdns.size();
0179:            }
0180:
0181:            public boolean isEmpty() {
0182:                return rdns.isEmpty();
0183:            }
0184:
0185:            public Enumeration getAll() {
0186:                final Enumeration enum_ = rdns.elements();
0187:
0188:                return new Enumeration() {
0189:                    public boolean hasMoreElements() {
0190:                        return enum_.hasMoreElements();
0191:                    }
0192:
0193:                    public Object nextElement() {
0194:                        return enum_.nextElement().toString();
0195:                    }
0196:                };
0197:            }
0198:
0199:            public String get(int pos) {
0200:                return rdns.elementAt(pos).toString();
0201:            }
0202:
0203:            public Name getPrefix(int pos) {
0204:                return new LdapName(null, rdns, 0, pos);
0205:            }
0206:
0207:            public Name getSuffix(int pos) {
0208:                return new LdapName(null, rdns, pos, rdns.size());
0209:            }
0210:
0211:            public boolean startsWith(Name n) {
0212:                int len1 = rdns.size();
0213:                int len2 = n.size();
0214:                return (len1 >= len2 && matches(0, len2, n));
0215:            }
0216:
0217:            public boolean endsWith(Name n) {
0218:                int len1 = rdns.size();
0219:                int len2 = n.size();
0220:                return (len1 >= len2 && matches(len1 - len2, len1, n));
0221:            }
0222:
0223:            /**
0224:             * Controls whether string-values are treated as case-sensitive
0225:             * when the string values within names are compared.  The default
0226:             * behavior is case-insensitive comparison.
0227:             */
0228:            public void setValuesCaseSensitive(boolean caseSensitive) {
0229:                toString();
0230:                rdns = null; // clear any cached information
0231:                try {
0232:                    parse();
0233:                } catch (InvalidNameException e) {
0234:                    // shouldn't happen
0235:                    throw new IllegalStateException("Cannot parse name: "
0236:                            + unparsed);
0237:                }
0238:                valuesCaseSensitive = caseSensitive;
0239:            }
0240:
0241:            /*
0242:             * Helper method for startsWith() and endsWith().
0243:             * Returns true if components [beg,end) match the components of "n".
0244:             * If "n" is not an LdapName, each of its components is parsed as
0245:             * the string form of an RDN.
0246:             * The following must hold:  end - beg == n.size().
0247:             */
0248:            private boolean matches(int beg, int end, Name n) {
0249:                for (int i = beg; i < end; i++) {
0250:                    Rdn rdn;
0251:                    if (n instanceof  LdapName) {
0252:                        LdapName ln = (LdapName) n;
0253:                        rdn = (Rdn) ln.rdns.elementAt(i - beg);
0254:                    } else {
0255:                        String rdnString = n.get(i - beg);
0256:                        try {
0257:                            rdn = (new DnParser(rdnString, valuesCaseSensitive))
0258:                                    .getRdn();
0259:                        } catch (InvalidNameException e) {
0260:                            return false;
0261:                        }
0262:                    }
0263:
0264:                    if (!rdn.equals(rdns.elementAt(i))) {
0265:                        return false;
0266:                    }
0267:                }
0268:                return true;
0269:            }
0270:
0271:            public Name addAll(Name suffix) throws InvalidNameException {
0272:                return addAll(size(), suffix);
0273:            }
0274:
0275:            /*
0276:             * If "suffix" is not an LdapName, each of its components is parsed as
0277:             * the string form of an RDN.
0278:             */
0279:            public Name addAll(int pos, Name suffix)
0280:                    throws InvalidNameException {
0281:                if (suffix instanceof  LdapName) {
0282:                    LdapName s = (LdapName) suffix;
0283:                    for (int i = 0; i < s.rdns.size(); i++) {
0284:                        rdns.insertElementAt(s.rdns.elementAt(i), pos++);
0285:                    }
0286:                } else {
0287:                    Enumeration comps = suffix.getAll();
0288:                    while (comps.hasMoreElements()) {
0289:                        DnParser p = new DnParser((String) comps.nextElement(),
0290:                                valuesCaseSensitive);
0291:                        rdns.insertElementAt(p.getRdn(), pos++);
0292:                    }
0293:                }
0294:                unparsed = null; // no longer valid
0295:                return this ;
0296:            }
0297:
0298:            public Name add(String comp) throws InvalidNameException {
0299:                return add(size(), comp);
0300:            }
0301:
0302:            public Name add(int pos, String comp) throws InvalidNameException {
0303:                Rdn rdn = (new DnParser(comp, valuesCaseSensitive)).getRdn();
0304:                rdns.insertElementAt(rdn, pos);
0305:                unparsed = null; // no longer valid
0306:                return this ;
0307:            }
0308:
0309:            public Object remove(int pos) throws InvalidNameException {
0310:                String comp = get(pos);
0311:                rdns.removeElementAt(pos);
0312:                unparsed = null; // no longer valid
0313:                return comp;
0314:            }
0315:
0316:            private void parse() throws InvalidNameException {
0317:                rdns = (new DnParser(unparsed, valuesCaseSensitive)).getDn();
0318:            }
0319:
0320:            /*
0321:             * Best guess as to what RFC 2253 means by "whitespace".
0322:             */
0323:            private static boolean isWhitespace(char c) {
0324:                return (c == ' ' || c == '\r');
0325:            }
0326:
0327:            /**
0328:             * Given the value of an attribute, returns a string suitable
0329:             * for inclusion in a DN.  If the value is a string, this is
0330:             * accomplished by using backslash (\) to escape the following
0331:             * characters:
0332:             *<ul>
0333:             *<li>leading and trailing whitespace
0334:             *<li><pre>, = + < > # ; " \</pre>
0335:             *</ul>
0336:             * If the value is a byte array, it is converted to hex
0337:             * notation (such as "#CEB1DF80").
0338:             */
0339:            public static String escapeAttributeValue(Object val) {
0340:                return TypeAndValue.escapeValue(val);
0341:            }
0342:
0343:            /**
0344:             * Given an attribute value formated according to RFC 2253,
0345:             * returns the unformated value.  Returns a string value as
0346:             * a string, and a binary value as a byte array.
0347:             */
0348:            public static Object unescapeAttributeValue(String val) {
0349:                return TypeAndValue.unescapeValue(val);
0350:            }
0351:
0352:            /**
0353:             * Serializes only the unparsed DN, for compactness and to avoid
0354:             * any implementation dependency.
0355:             *
0356:             * @serialdata	The DN string and a boolean indicating whether
0357:             * the values are case sensitive.
0358:             */
0359:            private void writeObject(java.io.ObjectOutputStream s)
0360:                    throws java.io.IOException {
0361:                s.writeObject(toString());
0362:                s.writeBoolean(valuesCaseSensitive);
0363:            }
0364:
0365:            private void readObject(java.io.ObjectInputStream s)
0366:                    throws java.io.IOException, ClassNotFoundException {
0367:                unparsed = (String) s.readObject();
0368:                valuesCaseSensitive = s.readBoolean();
0369:                try {
0370:                    parse();
0371:                } catch (InvalidNameException e) {
0372:                    // shouldn't happen
0373:                    throw new java.io.StreamCorruptedException("Invalid name: "
0374:                            + unparsed);
0375:                }
0376:            }
0377:
0378:            static final long serialVersionUID = -1595520034788997356L;
0379:
0380:            /*
0381:             * DnParser implements a recursive descent parser for a single DN.
0382:             */
0383:            static class DnParser {
0384:
0385:                private final String name; // DN being parsed
0386:                private final char[] chars; // characters in LDAP name being parsed
0387:                private final int len; // length of "chars"
0388:                private int cur = 0; // index of first unconsumed char in "chars"
0389:                private boolean valuesCaseSensitive;
0390:
0391:                /*
0392:                 * Given an LDAP DN in string form, returns a parser for it.
0393:                 */
0394:                DnParser(String name, boolean valuesCaseSensitive)
0395:                        throws InvalidNameException {
0396:                    this .name = name;
0397:                    len = name.length();
0398:                    chars = name.toCharArray();
0399:                    this .valuesCaseSensitive = valuesCaseSensitive;
0400:                }
0401:
0402:                /*
0403:                 * Parses the DN, returning a Vector of its RDNs.
0404:                 */
0405:                Vector getDn() throws InvalidNameException {
0406:                    cur = 0;
0407:                    Vector rdns = new Vector(len / 3 + 10); // leave room for growth
0408:
0409:                    if (len == 0) {
0410:                        return rdns;
0411:                    }
0412:
0413:                    rdns.addElement(parseRdn());
0414:                    while (cur < len) {
0415:                        if (chars[cur] == ',' || chars[cur] == ';') {
0416:                            ++cur;
0417:                            rdns.insertElementAt(parseRdn(), 0);
0418:                        } else {
0419:                            throw new InvalidNameException("Invalid name: "
0420:                                    + name);
0421:                        }
0422:                    }
0423:                    return rdns;
0424:                }
0425:
0426:                /*
0427:                 * Parses the DN, if it is known to contain a single RDN.
0428:                 */
0429:                Rdn getRdn() throws InvalidNameException {
0430:                    Rdn rdn = parseRdn();
0431:                    if (cur < len) {
0432:                        throw new InvalidNameException("Invalid RDN: " + name);
0433:                    }
0434:                    return rdn;
0435:                }
0436:
0437:                /*
0438:                 * Parses the next RDN and returns it.  Throws an exception if
0439:                 * none is found.  Leading and trailing whitespace is consumed.
0440:                 */
0441:                private Rdn parseRdn() throws InvalidNameException {
0442:
0443:                    Rdn rdn = new Rdn();
0444:                    while (cur < len) {
0445:                        consumeWhitespace();
0446:                        String attrType = parseAttrType();
0447:                        consumeWhitespace();
0448:                        if (cur >= len || chars[cur] != '=') {
0449:                            throw new InvalidNameException("Invalid name: "
0450:                                    + name);
0451:                        }
0452:                        ++cur; // consume '='
0453:                        consumeWhitespace();
0454:                        String value = parseAttrValue();
0455:                        consumeWhitespace();
0456:
0457:                        rdn.add(new TypeAndValue(attrType, value,
0458:                                valuesCaseSensitive));
0459:                        if (cur >= len || chars[cur] != '+') {
0460:                            break;
0461:                        }
0462:                        ++cur; // consume '+'
0463:                    }
0464:                    return rdn;
0465:                }
0466:
0467:                /*
0468:                 * Returns the attribute type that begins at the next unconsumed
0469:                 * char.  No leading whitespace is expected.
0470:                 * This routine is more generous than RFC 2253.  It accepts
0471:                 * attribute types composed of any nonempty combination of Unicode
0472:                 * letters, Unicode digits, '.', '-', and internal space characters.
0473:                 */
0474:                private String parseAttrType() throws InvalidNameException {
0475:
0476:                    final int beg = cur;
0477:                    while (cur < len) {
0478:                        char c = chars[cur];
0479:                        if (Character.isLetterOrDigit(c) || c == '.'
0480:                                || c == '-' || c == ' ') {
0481:                            ++cur;
0482:                        } else {
0483:                            break;
0484:                        }
0485:                    }
0486:                    // Back out any trailing spaces.
0487:                    while ((cur > beg) && (chars[cur - 1] == ' ')) {
0488:                        --cur;
0489:                    }
0490:
0491:                    if (beg == cur) {
0492:                        throw new InvalidNameException("Invalid name: " + name);
0493:                    }
0494:                    return new String(chars, beg, cur - beg);
0495:                }
0496:
0497:                /*
0498:                 * Returns the attribute value that begins at the next unconsumed
0499:                 * char.  No leading whitespace is expected.
0500:                 */
0501:                private String parseAttrValue() throws InvalidNameException {
0502:
0503:                    if (cur < len && chars[cur] == '#') {
0504:                        return parseBinaryAttrValue();
0505:                    } else if (cur < len && chars[cur] == '"') {
0506:                        return parseQuotedAttrValue();
0507:                    } else {
0508:                        return parseStringAttrValue();
0509:                    }
0510:                }
0511:
0512:                private String parseBinaryAttrValue()
0513:                        throws InvalidNameException {
0514:                    final int beg = cur;
0515:                    ++cur; // consume '#'
0516:                    while (cur < len && Character.isLetterOrDigit(chars[cur])) {
0517:                        ++cur;
0518:                    }
0519:                    return new String(chars, beg, cur - beg);
0520:                }
0521:
0522:                private String parseQuotedAttrValue()
0523:                        throws InvalidNameException {
0524:
0525:                    final int beg = cur;
0526:                    ++cur; // consume '"'
0527:
0528:                    while ((cur < len) && chars[cur] != '"') {
0529:                        if (chars[cur] == '\\') {
0530:                            ++cur; // consume backslash, then what follows
0531:                        }
0532:                        ++cur;
0533:                    }
0534:                    if (cur >= len) { // no closing quote
0535:                        throw new InvalidNameException("Invalid name: " + name);
0536:                    }
0537:                    ++cur; // consume closing quote
0538:
0539:                    return new String(chars, beg, cur - beg);
0540:                }
0541:
0542:                private String parseStringAttrValue()
0543:                        throws InvalidNameException {
0544:
0545:                    final int beg = cur;
0546:                    int esc = -1; // index of the most recently escaped character
0547:
0548:                    while ((cur < len) && !atTerminator()) {
0549:                        if (chars[cur] == '\\') {
0550:                            ++cur; // consume backslash, then what follows
0551:                            esc = cur;
0552:                        }
0553:                        ++cur;
0554:                    }
0555:                    if (cur > len) { // 'twas backslash followed by nothing
0556:                        throw new InvalidNameException("Invalid name: " + name);
0557:                    }
0558:
0559:                    // Trim off (unescaped) trailing whitespace.
0560:                    int end;
0561:                    for (end = cur; end > beg; end--) {
0562:                        if (!isWhitespace(chars[end - 1]) || (esc == end - 1)) {
0563:                            break;
0564:                        }
0565:                    }
0566:                    return new String(chars, beg, end - beg);
0567:                }
0568:
0569:                private void consumeWhitespace() {
0570:                    while ((cur < len) && isWhitespace(chars[cur])) {
0571:                        ++cur;
0572:                    }
0573:                }
0574:
0575:                /*
0576:                 * Returns true if next unconsumed character is one that terminates
0577:                 * a string attribute value.
0578:                 */
0579:                private boolean atTerminator() {
0580:                    return (cur < len && (chars[cur] == ','
0581:                            || chars[cur] == ';' || chars[cur] == '+'));
0582:                }
0583:            }
0584:
0585:            /*
0586:             * Class Rdn represents a set of TypeAndValue.
0587:             */
0588:            static class Rdn {
0589:
0590:                /*
0591:                 * A vector of the TypeAndValue elements of this Rdn.
0592:                 * It is sorted to facilitate set operations.
0593:                 */
0594:                private final Vector tvs = new Vector();
0595:
0596:                void add(TypeAndValue tv) {
0597:
0598:                    // Set i to index of first element greater than tv, or to
0599:                    // tvs.size() if there is none.
0600:                    int i;
0601:                    for (i = 0; i < tvs.size(); i++) {
0602:                        int diff = tv.compareTo(tvs.elementAt(i));
0603:                        if (diff == 0) {
0604:                            return; // tv is a duplicate:  ignore it
0605:                        } else if (diff < 0) {
0606:                            break;
0607:                        }
0608:                    }
0609:
0610:                    tvs.insertElementAt(tv, i);
0611:                }
0612:
0613:                public String toString() {
0614:                    StringBuffer buf = new StringBuffer();
0615:                    for (int i = 0; i < tvs.size(); i++) {
0616:                        if (i > 0) {
0617:                            buf.append('+');
0618:                        }
0619:                        buf.append(tvs.elementAt(i));
0620:                    }
0621:                    return new String(buf);
0622:                }
0623:
0624:                public boolean equals(Object obj) {
0625:                    return ((obj instanceof  Rdn) && (compareTo(obj) == 0));
0626:                }
0627:
0628:                // Compare TypeAndValue components one by one, lexicographically.
0629:                public int compareTo(Object obj) {
0630:                    Rdn that = (Rdn) obj;
0631:                    int minSize = Math.min(tvs.size(), that.tvs.size());
0632:                    for (int i = 0; i < minSize; i++) {
0633:                        // Compare a single pair of type/value pairs.
0634:                        TypeAndValue tv = (TypeAndValue) tvs.elementAt(i);
0635:                        int diff = tv.compareTo(that.tvs.elementAt(i));
0636:                        if (diff != 0) {
0637:                            return diff;
0638:                        }
0639:                    }
0640:                    return (tvs.size() - that.tvs.size()); // longer RDN wins
0641:                }
0642:
0643:                public int hashCode() {
0644:                    // Sum up the hash codes of the components.
0645:                    int hash = 0;
0646:
0647:                    // For each type/value pair...
0648:                    for (int i = 0; i < tvs.size(); i++) {
0649:                        hash += tvs.elementAt(i).hashCode();
0650:                    }
0651:                    return hash;
0652:                }
0653:
0654:                Attributes toAttributes() {
0655:                    Attributes attrs = new BasicAttributes(true);
0656:                    TypeAndValue tv;
0657:                    Attribute attr;
0658:
0659:                    for (int i = 0; i < tvs.size(); i++) {
0660:                        tv = (TypeAndValue) tvs.elementAt(i);
0661:                        if ((attr = attrs.get(tv.getType())) == null) {
0662:                            attrs.put(tv.getType(), tv.getUnescapedValue());
0663:                        } else {
0664:                            attr.add(tv.getUnescapedValue());
0665:                        }
0666:                    }
0667:                    return attrs;
0668:                }
0669:            }
0670:
0671:            /*
0672:             * Class TypeAndValue represents an attribute type and its
0673:             * corresponding value.
0674:             */
0675:            static class TypeAndValue {
0676:
0677:                private final String type;
0678:                private final String value; // value, escaped or quoted
0679:                private final boolean binary;
0680:                private final boolean valueCaseSensitive;
0681:
0682:                // If non-null, a canonical represention of the value suitable
0683:                // for comparison using String.compareTo().
0684:                private String comparable = null;
0685:
0686:                TypeAndValue(String type, String value,
0687:                        boolean valueCaseSensitive) {
0688:                    this .type = type;
0689:                    this .value = value;
0690:                    binary = value.startsWith("#");
0691:                    this .valueCaseSensitive = valueCaseSensitive;
0692:                }
0693:
0694:                public String toString() {
0695:                    return (type + "=" + value);
0696:                }
0697:
0698:                public int compareTo(Object obj) {
0699:                    // NB: Any change here affecting equality must be
0700:                    //     reflected in hashCode().
0701:
0702:                    TypeAndValue that = (TypeAndValue) obj;
0703:
0704:                    int diff = type.toUpperCase().compareTo(
0705:                            that.type.toUpperCase());
0706:                    if (diff != 0) {
0707:                        return diff;
0708:                    }
0709:                    if (value.equals(that.value)) { // try shortcut
0710:                        return 0;
0711:                    }
0712:                    return getValueComparable().compareTo(
0713:                            that.getValueComparable());
0714:                }
0715:
0716:                public boolean equals(Object obj) {
0717:                    // NB:  Any change here must be reflected in hashCode().
0718:                    if (!(obj instanceof  TypeAndValue)) {
0719:                        return false;
0720:                    }
0721:                    TypeAndValue that = (TypeAndValue) obj;
0722:                    return (type.equalsIgnoreCase(that.type) && (value
0723:                            .equals(that.value) || getValueComparable().equals(
0724:                            that.getValueComparable())));
0725:                }
0726:
0727:                public int hashCode() {
0728:                    // If two objects are equal, their hash codes must match.
0729:                    return (type.toUpperCase().hashCode() + getValueComparable()
0730:                            .hashCode());
0731:                }
0732:
0733:                /*
0734:                 * Returns the type.
0735:                 */
0736:                String getType() {
0737:                    return type;
0738:                }
0739:
0740:                /*
0741:                 * Returns the unescaped value.
0742:                 */
0743:                Object getUnescapedValue() {
0744:                    return unescapeValue(value);
0745:                }
0746:
0747:                /*
0748:                 * Returns a canonical representation of "value" suitable for
0749:                 * comparison using String.compareTo().  If "value" is a string,
0750:                 * it is returned with escapes and quotes stripped away, and
0751:                 * hex-encoded UTF-8 converted to 16-bit Unicode chars.
0752:                 * If value's case is to be ignored, it is returned in uppercase.
0753:                 * If "value" is binary, it is returned in uppercase but
0754:                 * otherwise unmodified.
0755:                 */
0756:                private String getValueComparable() {
0757:                    if (comparable != null) {
0758:                        return comparable; // return cached result
0759:                    }
0760:
0761:                    // cache result
0762:                    if (binary) {
0763:                        comparable = value.toUpperCase();
0764:                    } else {
0765:                        comparable = (String) unescapeValue(value);
0766:                        if (!valueCaseSensitive) {
0767:                            comparable = comparable.toUpperCase(); // ignore case
0768:                        }
0769:                    }
0770:                    return comparable;
0771:                }
0772:
0773:                /*
0774:                 * Given the value of an attribute, returns a string suitable
0775:                 * for inclusion in a DN.
0776:                 */
0777:                static String escapeValue(Object val) {
0778:                    return (val instanceof  byte[]) ? escapeBinaryValue((byte[]) val)
0779:                            : escapeStringValue((String) val);
0780:                }
0781:
0782:                /*
0783:                 * Given the value of a string-valued attribute, returns a
0784:                 * string suitable for inclusion in a DN.  This is accomplished by
0785:                 * using backslash (\) to escape the following characters:
0786:                 *	leading and trailing whitespace
0787:                 *	, = + < > # ; " \
0788:                 */
0789:                private static String escapeStringValue(String val) {
0790:
0791:                    final String escapees = ",=+<>#;\"\\";
0792:                    char[] chars = val.toCharArray();
0793:                    StringBuffer buf = new StringBuffer(2 * val.length());
0794:
0795:                    // Find leading and trailing whitespace.
0796:                    int lead; // index of first char that is not leading whitespace
0797:                    for (lead = 0; lead < chars.length; lead++) {
0798:                        if (!isWhitespace(chars[lead])) {
0799:                            break;
0800:                        }
0801:                    }
0802:                    int trail; // index of last char that is not trailing whitespace
0803:                    for (trail = chars.length - 1; trail >= 0; trail--) {
0804:                        if (!isWhitespace(chars[trail])) {
0805:                            break;
0806:                        }
0807:                    }
0808:
0809:                    for (int i = 0; i < chars.length; i++) {
0810:                        char c = chars[i];
0811:                        if ((i < lead) || (i > trail)
0812:                                || (escapees.indexOf(c) >= 0)) {
0813:                            buf.append('\\');
0814:                        }
0815:                        buf.append(c);
0816:                    }
0817:                    return new String(buf);
0818:                }
0819:
0820:                /*
0821:                 * Given the value of a binary attribute, returns a string
0822:                 * suitable for inclusion in a DN (such as "#CEB1DF80").
0823:                 */
0824:                private static String escapeBinaryValue(byte[] val) {
0825:
0826:                    StringBuffer buf = new StringBuffer(1 + 2 * val.length);
0827:                    buf.append("#");
0828:
0829:                    for (int i = 0; i < val.length; i++) {
0830:                        byte b = val[i];
0831:                        buf.append(Character.forDigit(0xF & (b >>> 4), 16));
0832:                        buf.append(Character.forDigit(0xF & b, 16));
0833:                    }
0834:
0835:                    return (new String(buf)).toUpperCase();
0836:                }
0837:
0838:                /*
0839:                 * Given an attribute value formated according to RFC 2253,
0840:                 * returns the unformated value.  Escapes and quotes are
0841:                 * stripped away, and hex-encoded UTF-8 is converted to 16-bit
0842:                 * Unicode chars.  Returns a string value as a String, and a
0843:                 * binary value as a byte array.
0844:                 */
0845:                static Object unescapeValue(String val) {
0846:
0847:                    char[] chars = val.toCharArray();
0848:                    int beg = 0;
0849:                    int end = chars.length;
0850:
0851:                    // Trim off leading and trailing whitespace.
0852:                    while ((beg < end) && isWhitespace(chars[beg])) {
0853:                        ++beg;
0854:                    }
0855:                    while ((beg < end) && isWhitespace(chars[end - 1])) {
0856:                        --end;
0857:                    }
0858:
0859:                    // Add back the trailing whitespace with a preceeding '\'
0860:                    // (escaped or unescaped) that was taken off in the above
0861:                    // loop. Whether or not to retain this whitespace is
0862:                    // decided below.
0863:                    if (end != chars.length && (beg < end)
0864:                            && chars[end - 1] == '\\') {
0865:                        end++;
0866:                    }
0867:                    if (beg >= end) {
0868:                        return "";
0869:                    }
0870:
0871:                    if (chars[beg] == '#') {
0872:                        // Value is binary (eg: "#CEB1DF80").
0873:                        return decodeHexPairs(chars, ++beg, end);
0874:                    }
0875:
0876:                    // Trim off quotes.
0877:                    if ((chars[beg] == '\"') && (chars[end - 1] == '\"')) {
0878:                        ++beg;
0879:                        --end;
0880:                    }
0881:
0882:                    StringBuffer buf = new StringBuffer(end - beg);
0883:                    int esc = -1; // index of the last escaped character
0884:
0885:                    for (int i = beg; i < end; i++) {
0886:                        if ((chars[i] == '\\') && (i + 1 < end)) {
0887:                            if (!Character.isLetterOrDigit(chars[i + 1])) {
0888:                                ++i; // skip backslash
0889:                                buf.append(chars[i]); // snarf escaped char
0890:                                esc = i;
0891:                            } else {
0892:
0893:                                // Convert hex-encoded UTF-8 to 16-bit chars.
0894:                                byte[] utf8 = getUtf8Octets(chars, i, end);
0895:                                if (utf8.length > 0) {
0896:                                    try {
0897:                                        buf.append(new String(utf8, "UTF8"));
0898:                                    } catch (java.io.UnsupportedEncodingException e) {
0899:                                        // shouldn't happen
0900:                                    }
0901:                                    i += utf8.length * 3 - 1;
0902:                                } else {
0903:                                    throw new IllegalArgumentException(
0904:                                            "Not a valid attribute string value:"
0905:                                                    + val
0906:                                                    + ", improper usage of backslash");
0907:                                }
0908:                            }
0909:                        } else {
0910:                            buf.append(chars[i]); // snarf unescaped char
0911:                        }
0912:                    }
0913:
0914:                    // Get rid of the unescaped trailing whitespace with the
0915:                    // preceeding '\' character that was previously added back.
0916:                    int len = buf.length();
0917:                    if (isWhitespace(buf.charAt(len - 1)) && esc != (end - 1)) {
0918:                        buf.setLength(len - 1);
0919:                    }
0920:
0921:                    return new String(buf);
0922:                }
0923:
0924:                /*
0925:                 * Given an array of chars (with starting and ending indexes into it)
0926:                 * representing bytes encoded as hex-pairs (such as "CEB1DF80"),
0927:                 * returns a byte array containing the decoded bytes.
0928:                 */
0929:                private static byte[] decodeHexPairs(char[] chars, int beg,
0930:                        int end) {
0931:                    byte[] bytes = new byte[(end - beg) / 2];
0932:                    for (int i = 0; beg + 1 < end; i++) {
0933:                        int hi = Character.digit(chars[beg], 16);
0934:                        int lo = Character.digit(chars[beg + 1], 16);
0935:                        if (hi < 0 || lo < 0) {
0936:                            break;
0937:                        }
0938:                        bytes[i] = (byte) ((hi << 4) + lo);
0939:                        beg += 2;
0940:                    }
0941:                    if (beg != end) {
0942:                        throw new IllegalArgumentException(
0943:                                "Illegal attribute value: #"
0944:                                        + new String(chars));
0945:                    }
0946:                    return bytes;
0947:                }
0948:
0949:                /*
0950:                 * Given an array of chars (with starting and ending indexes into it),
0951:                 * finds the largest prefix consisting of hex-encoded UTF-8 octets,
0952:                 * and returns a byte array containing the corresponding UTF-8 octets.
0953:                 *
0954:                 * Hex-encoded UTF-8 octets look like this:
0955:                 *	\03\B1\DF\80
0956:                 */
0957:                private static byte[] getUtf8Octets(char[] chars, int beg,
0958:                        int end) {
0959:                    byte[] utf8 = new byte[(end - beg) / 3]; // allow enough room
0960:                    int len = 0; // index of first unused byte in utf8
0961:
0962:                    while ((beg + 2 < end) && (chars[beg++] == '\\')) {
0963:                        int hi = Character.digit(chars[beg++], 16);
0964:                        int lo = Character.digit(chars[beg++], 16);
0965:                        if (hi < 0 || lo < 0) {
0966:                            break;
0967:                        }
0968:                        utf8[len++] = (byte) ((hi << 4) + lo);
0969:                    }
0970:
0971:                    if (len == utf8.length) {
0972:                        return utf8;
0973:                    } else {
0974:                        byte[] res = new byte[len];
0975:                        System.arraycopy(utf8, 0, res, 0, len);
0976:                        return res;
0977:                    }
0978:                }
0979:            }
0980:
0981:            /*
0982:             * For testing.
0983:             */
0984:            /*
0985:             public static void main(String[] args) {
0986:
0987:             try {
0988:             if (args.length == 1) {		// parse and print components
0989:             LdapName n = new LdapName(args[0]);
0990:
0991:             Enumeration rdns = n.rdns.elements();
0992:             while (rdns.hasMoreElements()) {
0993:             Rdn rdn = (Rdn)rdns.nextElement();
0994:             for (int i = 0; i < rdn.tvs.size(); i++) {
0995:             System.out.print("[" + rdn.tvs.elementAt(i) + "]");
0996:             }
0997:             System.out.println();
0998:             }
0999:
1000:             } else {				// compare two names
1001:             LdapName n1 = new LdapName(args[0]);
1002:             LdapName n2 = new LdapName(args[1]);
1003:             n1.unparsed = null;
1004:             n2.unparsed = null;
1005:             boolean eq = n1.equals(n2);
1006:             System.out.println("[" + n1 + (eq ? "] == [" : "] != [")
1007:             + n2 + "]");
1008:             }
1009:             } catch (Exception e) {
1010:             e.printStackTrace();
1011:             }
1012:             }
1013:             */
1014:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.