Home » openjdk-7 » javax » naming » ldap » [javadoc | source]

    1   /*
    2    * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package javax.naming.ldap;
   27   
   28   import java.util.Iterator;
   29   import java.util.NoSuchElementException;
   30   import java.util.ArrayList;
   31   import java.util.Collections;
   32   
   33   import javax.naming.InvalidNameException;
   34   import javax.naming.directory.BasicAttributes;
   35   import javax.naming.directory.Attributes;
   36   import javax.naming.directory.Attribute;
   37   import javax.naming.NamingEnumeration;
   38   import javax.naming.NamingException;
   39   
   40   import java.io.Serializable;
   41   import java.io.ObjectOutputStream;
   42   import java.io.ObjectInputStream;
   43   import java.io.IOException;
   44   
   45   /**
   46    * This class represents a relative distinguished name, or RDN, which is a
   47    * component of a distinguished name as specified by
   48    * <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>.
   49    * An example of an RDN is "OU=Sales+CN=J.Smith". In this example,
   50    * the RDN consist of multiple attribute type/value pairs. The
   51    * RDN is parsed as described in the class description for
   52    * {@link javax.naming.ldap.LdapName <tt>LdapName</tt>}.
   53    * <p>
   54    * The Rdn class represents an RDN as attribute type/value mappings,
   55    * which can be viewed using
   56    * {@link javax.naming.directory.Attributes Attributes}.
   57    * In addition, it contains convenience methods that allow easy retrieval
   58    * of type and value when the Rdn consist of a single type/value pair,
   59    * which is how it appears in a typical usage.
   60    * It also contains helper methods that allow escaping of the unformatted
   61    * attribute value and unescaping of the value formatted according to the
   62    * escaping syntax defined in RFC2253. For methods that take or return
   63    * attribute value as an Object, the value is either a String
   64    * (in unescaped form) or a byte array.
   65    * <p>
   66    * <code>Rdn</code> will properly parse all valid RDNs, but
   67    * does not attempt to detect all possible violations when parsing
   68    * invalid RDNs. It is "generous" in accepting invalid RDNs.
   69    * The "validity" of a name is determined ultimately when it
   70    * is supplied to an LDAP server, which may accept or
   71    * reject the name based on factors such as its schema information
   72    * and interoperability considerations.
   73    *
   74    * <p>
   75    * The following code example shows how to construct an Rdn using the
   76    * constructor that takes type and value as arguments:
   77    * <pre>
   78    *      Rdn rdn = new Rdn("cn", "Juicy, Fruit");
   79    *      System.out.println(rdn.toString());
   80    * </pre>
   81    * The last line will print <tt>cn=Juicy\, Fruit</tt>. The
   82    * {@link #unescapeValue(String) <tt>unescapeValue()</tt>} method can be
   83    * used to unescape the escaped comma resulting in the original
   84    * value <tt>"Juicy, Fruit"</tt>. The {@link #escapeValue(Object)
   85    * <tt>escapeValue()</tt>} method adds the escape back preceding the comma.
   86    * <p>
   87    * This class can be instantiated by a string representation
   88    * of the RDN defined in RFC 2253 as shown in the following code example:
   89    * <pre>
   90    *      Rdn rdn = new Rdn("cn=Juicy\\, Fruit");
   91    *      System.out.println(rdn.toString());
   92    * </pre>
   93    * The last line will print <tt>cn=Juicy\, Fruit</tt>.
   94    * <p>
   95    * Concurrent multithreaded read-only access of an instance of
   96    * <tt>Rdn</tt> need not be synchronized.
   97    * <p>
   98    * Unless otherwise noted, the behavior of passing a null argument
   99    * to a constructor or method in this class will cause NullPointerException
  100    * to be thrown.
  101    *
  102    * @since 1.5
  103    */
  104   
  105   public class Rdn implements Serializable, Comparable<Object> {
  106   
  107       // private transient ArrayList<RdnEntry> entries;
  108       private transient ArrayList entries;
  109   
  110       // The common case.
  111       private static final int DEFAULT_SIZE = 1;
  112   
  113       private static final long serialVersionUID = -5994465067210009656L;
  114   
  115       /**
  116        * Constructs an Rdn from the given attribute set. See
  117        * {@link javax.naming.directory.Attributes Attributes}.
  118        * <p>
  119        * The string attribute values are not interpretted as
  120        * <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>
  121        * formatted RDN strings. That is, the values are used
  122        * literally (not parsed) and assumed to be unescaped.
  123        *
  124        * @param attrSet The non-null and non-empty attributes containing
  125        * type/value mappings.
  126        * @throws InvalidNameException If contents of <tt>attrSet</tt> cannot
  127        *          be used to construct a valid RDN.
  128        */
  129       public Rdn(Attributes attrSet) throws InvalidNameException {
  130           if (attrSet.size() == 0) {
  131               throw new InvalidNameException("Attributes cannot be empty");
  132           }
  133           entries = new ArrayList(attrSet.size());
  134           NamingEnumeration attrs = attrSet.getAll();
  135           try {
  136               for (int nEntries = 0; attrs.hasMore(); nEntries++) {
  137                   RdnEntry entry = new RdnEntry();
  138                   Attribute attr = (Attribute) attrs.next();
  139                   entry.type = attr.getID();
  140                   entry.value = attr.get();
  141                   entries.add(nEntries, entry);
  142               }
  143           } catch (NamingException e) {
  144               InvalidNameException e2 = new InvalidNameException(
  145                                           e.getMessage());
  146               e2.initCause(e);
  147               throw e2;
  148           }
  149           sort(); // arrange entries for comparison
  150       }
  151   
  152       /**
  153        * Constructs an Rdn from the given string.
  154        * This constructor takes a string formatted according to the rules
  155        * defined in <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>
  156        * and described in the class description for
  157        * {@link javax.naming.ldap.LdapName}.
  158        *
  159        * @param rdnString The non-null and non-empty RFC2253 formatted string.
  160        * @throws InvalidNameException If a syntax error occurs during
  161        *                  parsing of the rdnString.
  162        */
  163       public Rdn(String rdnString) throws InvalidNameException {
  164           entries = new ArrayList(DEFAULT_SIZE);
  165           (new Rfc2253Parser(rdnString)).parseRdn(this);
  166       }
  167   
  168       /**
  169        * Constructs an Rdn from the given <tt>rdn</tt>.
  170        * The contents of the <tt>rdn</tt> are simply copied into the newly
  171        * created Rdn.
  172        * @param rdn The non-null Rdn to be copied.
  173        */
  174       public Rdn(Rdn rdn) {
  175           entries = new ArrayList(rdn.entries.size());
  176           entries.addAll(rdn.entries);
  177       }
  178   
  179       /**
  180        * Constructs an Rdn from the given attribute type and
  181        * value.
  182        * The string attribute values are not interpretted as
  183        * <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>
  184        * formatted RDN strings. That is, the values are used
  185        * literally (not parsed) and assumed to be unescaped.
  186        *
  187        * @param type The non-null and non-empty string attribute type.
  188        * @param value The non-null and non-empty attribute value.
  189        * @throws InvalidNameException If type/value cannot be used to
  190        *                  construct a valid RDN.
  191        * @see #toString()
  192        */
  193       public Rdn(String type, Object value) throws InvalidNameException {
  194           if (value == null) {
  195               throw new NullPointerException("Cannot set value to null");
  196           }
  197           if (type.equals("") || isEmptyValue(value)) {
  198               throw new InvalidNameException(
  199                   "type or value cannot be empty, type:" + type +
  200                   " value:" + value);
  201           }
  202           entries = new ArrayList(DEFAULT_SIZE);
  203           put(type, value);
  204       }
  205   
  206       private boolean isEmptyValue(Object val) {
  207           return ((val instanceof String) && val.equals("")) ||
  208           ((val instanceof byte[]) && (((byte[]) val).length == 0));
  209       }
  210   
  211       // An empty constructor used by the parser
  212       Rdn() {
  213           entries = new ArrayList(DEFAULT_SIZE);
  214       }
  215   
  216       /*
  217        * Adds the given attribute type and value to this Rdn.
  218        * The string attribute values are not interpretted as
  219        * <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>
  220        * formatted RDN strings. That is the values are used
  221        * literally (not parsed) and assumed to be unescaped.
  222        *
  223        * @param type The non-null and non-empty string attribute type.
  224        * @param value The non-null and non-empty attribute value.
  225        * @return The updated Rdn, not a new one. Cannot be null.
  226        * @see #toString()
  227        */
  228       Rdn put(String type, Object value) {
  229   
  230           // create new Entry
  231           RdnEntry newEntry = new RdnEntry();
  232           newEntry.type =  type;
  233           if (value instanceof byte[]) {  // clone the byte array
  234               newEntry.value = ((byte[]) value).clone();
  235           } else {
  236               newEntry.value = value;
  237           }
  238           entries.add(newEntry);
  239           return this;
  240       }
  241   
  242       void sort() {
  243           if (entries.size() > 1) {
  244               Collections.sort(entries);
  245           }
  246       }
  247   
  248       /**
  249        * Retrieves one of this Rdn's value.
  250        * This is a convenience method for obtaining the value,
  251        * when the RDN contains a single type and value mapping,
  252        * which is the common RDN usage.
  253        * <p>
  254        * For a multi-valued RDN, this method returns value corresponding
  255        * to the type returned by {@link #getType() getType()} method.
  256        *
  257        * @return The non-null attribute value.
  258        */
  259       public Object getValue() {
  260           return ((RdnEntry) entries.get(0)).getValue();
  261       }
  262   
  263       /**
  264        * Retrieves one of this Rdn's type.
  265        * This is a convenience method for obtaining the type,
  266        * when the RDN contains a single type and value mapping,
  267        * which is the common RDN usage.
  268        * <p>
  269        * For a multi-valued RDN, the type/value pairs have
  270        * no specific order defined on them. In that case, this method
  271        * returns type of one of the type/value pairs.
  272        * The {@link #getValue() getValue()} method returns the
  273        * value corresponding to the type returned by this method.
  274        *
  275        * @return The non-null attribute type.
  276        */
  277       public String getType() {
  278           return ((RdnEntry) entries.get(0)).getType();
  279       }
  280   
  281       /**
  282        * Returns this Rdn as a string represented in a format defined by
  283        * <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a> and described
  284        * in the class description for {@link javax.naming.ldap.LdapName LdapName}.
  285        *
  286        * @return The string representation of the Rdn.
  287        */
  288       public String toString() {
  289           StringBuilder builder = new StringBuilder();
  290           int size = entries.size();
  291           if (size > 0) {
  292               builder.append(entries.get(0));
  293           }
  294           for (int next = 1; next < size; next++) {
  295               builder.append('+');
  296               builder.append(entries.get(next));
  297           }
  298           return builder.toString();
  299       }
  300   
  301       /**
  302        * Compares this Rdn with the specified Object for order.
  303        * Returns a negative integer, zero, or a positive integer as this
  304        * Rdn is less than, equal to, or greater than the given Object.
  305        * <p>
  306        * If obj is null or not an instance of Rdn, ClassCastException
  307        * is thrown.
  308        * <p>
  309        * The attribute type and value pairs of the RDNs are lined up
  310        * against each other and compared lexicographically. The order of
  311        * components in multi-valued Rdns (such as "ou=Sales+cn=Bob") is not
  312        * significant.
  313        *
  314        * @param obj The non-null object to compare against.
  315        * @return  A negative integer, zero, or a positive integer as this Rdn
  316        *          is less than, equal to, or greater than the given Object.
  317        * @exception ClassCastException if obj is null or not a Rdn.
  318        * <p>
  319        */
  320       public int compareTo(Object obj) {
  321           if (!(obj instanceof Rdn)) {
  322               throw new ClassCastException("The obj is not a Rdn");
  323           }
  324           if (obj == this) {
  325               return 0;
  326           }
  327           Rdn that = (Rdn) obj;
  328           int minSize = Math.min(entries.size(), that.entries.size());
  329           for (int i = 0; i < minSize; i++) {
  330   
  331               // Compare a single pair of type/value pairs.
  332               int diff = ((RdnEntry) entries.get(i)).compareTo(
  333                                           that.entries.get(i));
  334               if (diff != 0) {
  335                   return diff;
  336               }
  337           }
  338           return (entries.size() - that.entries.size());  // longer RDN wins
  339       }
  340   
  341       /**
  342        * Compares the specified Object with this Rdn for equality.
  343        * Returns true if the given object is also a Rdn and the two Rdns
  344        * represent the same attribute type and value mappings. The order of
  345        * components in multi-valued Rdns (such as "ou=Sales+cn=Bob") is not
  346        * significant.
  347        * <p>
  348        * Type and value equalilty matching is done as below:
  349        * <ul>
  350        * <li> The types are compared for equality with their case ignored.
  351        * <li> String values with different but equivalent usage of quoting,
  352        * escaping, or UTF8-hex-encoding are considered equal.
  353        * The case of the values is ignored during the comparison.
  354        * </ul>
  355        * <p>
  356        * If obj is null or not an instance of Rdn, false is returned.
  357        * <p>
  358        * @param obj object to be compared for equality with this Rdn.
  359        * @return true if the specified object is equal to this Rdn.
  360        * @see #hashCode()
  361        */
  362       public boolean equals(Object obj) {
  363           if (obj == this) {
  364               return true;
  365           }
  366           if (!(obj instanceof Rdn)) {
  367               return false;
  368           }
  369           Rdn that = (Rdn) obj;
  370           if (entries.size() != that.size()) {
  371               return false;
  372           }
  373           for (int i = 0; i < entries.size(); i++) {
  374               if (!entries.get(i).equals(that.entries.get(i))) {
  375                   return false;
  376               }
  377           }
  378           return true;
  379       }
  380   
  381       /**
  382        * Returns the hash code of this RDN. Two RDNs that are
  383        * equal (according to the equals method) will have the same
  384        * hash code.
  385        *
  386        * @return An int representing the hash code of this Rdn.
  387        * @see #equals
  388        */
  389       public int hashCode() {
  390   
  391           // Sum up the hash codes of the components.
  392           int hash = 0;
  393   
  394           // For each type/value pair...
  395           for (int i = 0; i < entries.size(); i++) {
  396               hash += entries.get(i).hashCode();
  397           }
  398           return hash;
  399       }
  400   
  401       /**
  402        * Retrieves the {@link javax.naming.directory.Attributes Attributes}
  403        * view of the type/value mappings contained in this Rdn.
  404        *
  405        * @return  The non-null attributes containing the type/value
  406        *          mappings of this Rdn.
  407        */
  408       public Attributes toAttributes() {
  409           Attributes attrs = new BasicAttributes(true);
  410           for (int i = 0; i < entries.size(); i++) {
  411               RdnEntry entry = (RdnEntry) entries.get(i);
  412               Attribute attr = attrs.put(entry.getType(), entry.getValue());
  413               if (attr != null) {
  414                   attr.add(entry.getValue());
  415                   attrs.put(attr);
  416               }
  417           }
  418           return attrs;
  419       }
  420   
  421   
  422       private static class RdnEntry implements Comparable {
  423           private String type;
  424           private Object value;
  425   
  426           // If non-null, a cannonical representation of the value suitable
  427           // for comparison using String.compareTo()
  428           private String comparable = null;
  429   
  430           String getType() {
  431               return type;
  432           }
  433   
  434           Object getValue() {
  435               return value;
  436           }
  437   
  438           public int compareTo(Object obj) {
  439   
  440               // Any change here affecting equality must be
  441               // reflected in hashCode().
  442               RdnEntry that = (RdnEntry) obj;
  443   
  444               int diff = type.toUpperCase().compareTo(
  445                           that.type.toUpperCase());
  446               if (diff != 0) {
  447                   return diff;
  448               }
  449               if (value.equals(that.value)) {     // try shortcut
  450                   return 0;
  451               }
  452               return getValueComparable().compareTo(
  453                           that.getValueComparable());
  454           }
  455   
  456           public boolean equals(Object obj) {
  457               if (obj == this) {
  458                   return true;
  459               }
  460               if (!(obj instanceof RdnEntry)) {
  461                   return false;
  462               }
  463   
  464               // Any change here must be reflected in hashCode()
  465               RdnEntry that = (RdnEntry) obj;
  466               return (type.equalsIgnoreCase(that.type)) &&
  467                           (getValueComparable().equals(
  468                           that.getValueComparable()));
  469           }
  470   
  471           public int hashCode() {
  472               return (type.toUpperCase().hashCode() +
  473                   getValueComparable().hashCode());
  474           }
  475   
  476           public String toString() {
  477               return type + "=" + escapeValue(value);
  478           }
  479   
  480           private String getValueComparable() {
  481               if (comparable != null) {
  482                   return comparable;              // return cached result
  483               }
  484   
  485               // cache result
  486               if (value instanceof byte[]) {
  487                   comparable = escapeBinaryValue((byte[]) value);
  488               } else {
  489                   comparable = ((String) value).toUpperCase();
  490               }
  491               return comparable;
  492           }
  493       }
  494   
  495       /**
  496        * Retrieves the number of attribute type/value pairs in this Rdn.
  497        * @return The non-negative number of type/value pairs in this Rdn.
  498        */
  499       public int size() {
  500           return entries.size();
  501       }
  502   
  503       /**
  504        * Given the value of an attribute, returns a string escaped according
  505        * to the rules specified in
  506        * <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>.
  507        * <p>
  508        * For example, if the val is "Sue, Grabbit and Runn", the escaped
  509        * value returned by this method is "Sue\, Grabbit and Runn".
  510        * <p>
  511        * A string value is represented as a String and binary value
  512        * as a byte array.
  513        *
  514        * @param val The non-null object to be escaped.
  515        * @return Escaped string value.
  516        * @throws ClassCastException if val is is not a String or byte array.
  517        */
  518       public static String escapeValue(Object val) {
  519           return (val instanceof byte[])
  520                   ? escapeBinaryValue((byte[])val)
  521                   : escapeStringValue((String)val);
  522       }
  523   
  524       /*
  525        * Given the value of a string-valued attribute, returns a
  526        * string suitable for inclusion in a DN.  This is accomplished by
  527        * using backslash (\) to escape the following characters:
  528        *  leading and trailing whitespace
  529        *  , = + < > # ; " \
  530        */
  531       private static final String escapees = ",=+<>#;\"\\";
  532   
  533       private static String escapeStringValue(String val) {
  534   
  535               char[] chars = val.toCharArray();
  536               StringBuilder builder = new StringBuilder(2 * val.length());
  537   
  538               // Find leading and trailing whitespace.
  539               int lead;   // index of first char that is not leading whitespace
  540               for (lead = 0; lead < chars.length; lead++) {
  541                   if (!isWhitespace(chars[lead])) {
  542                       break;
  543                   }
  544               }
  545               int trail;  // index of last char that is not trailing whitespace
  546               for (trail = chars.length - 1; trail >= 0; trail--) {
  547                   if (!isWhitespace(chars[trail])) {
  548                       break;
  549                   }
  550               }
  551   
  552               for (int i = 0; i < chars.length; i++) {
  553                   char c = chars[i];
  554                   if ((i < lead) || (i > trail) || (escapees.indexOf(c) >= 0)) {
  555                       builder.append('\\');
  556                   }
  557                   builder.append(c);
  558               }
  559               return builder.toString();
  560       }
  561   
  562       /*
  563        * Given the value of a binary attribute, returns a string
  564        * suitable for inclusion in a DN (such as "#CEB1DF80").
  565        * TBD: This method should actually generate the ber encoding
  566        * of the binary value
  567        */
  568       private static String escapeBinaryValue(byte[] val) {
  569   
  570           StringBuilder builder = new StringBuilder(1 + 2 * val.length);
  571           builder.append("#");
  572   
  573           for (int i = 0; i < val.length; i++) {
  574               byte b = val[i];
  575               builder.append(Character.forDigit(0xF & (b >>> 4), 16));
  576               builder.append(Character.forDigit(0xF & b, 16));
  577           }
  578           return builder.toString();
  579           // return builder.toString().toUpperCase();
  580       }
  581   
  582       /**
  583        * Given an attribute value string formated according to the rules
  584        * specified in
  585        * <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>,
  586        * returns the unformated value.  Escapes and quotes are
  587        * stripped away, and hex-encoded UTF-8 is converted to equivalent
  588        * UTF-16 characters. Returns a string value as a String, and a
  589        * binary value as a byte array.
  590        * <p>
  591        * Legal and illegal values are defined in RFC 2253.
  592        * This method is generous in accepting the values and does not
  593        * catch all illegal values.
  594        * Therefore, passing in an illegal value might not necessarily
  595        * trigger an <tt>IllegalArgumentException</tt>.
  596        *
  597        * @param   val     The non-null string to be unescaped.
  598        * @return          Unescaped value.
  599        * @throws          IllegalArgumentException When an Illegal value
  600        *                  is provided.
  601        */
  602       public static Object unescapeValue(String val) {
  603   
  604               char[] chars = val.toCharArray();
  605               int beg = 0;
  606               int end = chars.length;
  607   
  608               // Trim off leading and trailing whitespace.
  609               while ((beg < end) && isWhitespace(chars[beg])) {
  610                   ++beg;
  611               }
  612   
  613               while ((beg < end) && isWhitespace(chars[end - 1])) {
  614                   --end;
  615               }
  616   
  617               // Add back the trailing whitespace with a preceeding '\'
  618               // (escaped or unescaped) that was taken off in the above
  619               // loop. Whether or not to retain this whitespace is decided below.
  620               if (end != chars.length &&
  621                       (beg < end) &&
  622                       chars[end - 1] == '\\') {
  623                   end++;
  624               }
  625               if (beg >= end) {
  626                   return "";
  627               }
  628   
  629               if (chars[beg] == '#') {
  630                   // Value is binary (eg: "#CEB1DF80").
  631                   return decodeHexPairs(chars, ++beg, end);
  632               }
  633   
  634               // Trim off quotes.
  635               if ((chars[beg] == '\"') && (chars[end - 1] == '\"')) {
  636                   ++beg;
  637                   --end;
  638               }
  639   
  640               StringBuilder builder = new StringBuilder(end - beg);
  641               int esc = -1; // index of the last escaped character
  642   
  643               for (int i = beg; i < end; i++) {
  644                   if ((chars[i] == '\\') && (i + 1 < end)) {
  645                       if (!Character.isLetterOrDigit(chars[i + 1])) {
  646                           ++i;                            // skip backslash
  647                           builder.append(chars[i]);       // snarf escaped char
  648                           esc = i;
  649                       } else {
  650   
  651                           // Convert hex-encoded UTF-8 to 16-bit chars.
  652                           byte[] utf8 = getUtf8Octets(chars, i, end);
  653                           if (utf8.length > 0) {
  654                               try {
  655                                   builder.append(new String(utf8, "UTF8"));
  656                               } catch (java.io.UnsupportedEncodingException e) {
  657                                   // shouldn't happen
  658                               }
  659                               i += utf8.length * 3 - 1;
  660                           } else { // no utf8 bytes available, invalid DN
  661   
  662                               // '/' has no meaning, throw exception
  663                               throw new IllegalArgumentException(
  664                                   "Not a valid attribute string value:" +
  665                                   val + ",improper usage of backslash");
  666                           }
  667                       }
  668                   } else {
  669                       builder.append(chars[i]);   // snarf unescaped char
  670                   }
  671               }
  672   
  673               // Get rid of the unescaped trailing whitespace with the
  674               // preceeding '\' character that was previously added back.
  675               int len = builder.length();
  676               if (isWhitespace(builder.charAt(len - 1)) && esc != (end - 1)) {
  677                   builder.setLength(len - 1);
  678               }
  679               return builder.toString();
  680           }
  681   
  682   
  683           /*
  684            * Given an array of chars (with starting and ending indexes into it)
  685            * representing bytes encoded as hex-pairs (such as "CEB1DF80"),
  686            * returns a byte array containing the decoded bytes.
  687            */
  688           private static byte[] decodeHexPairs(char[] chars, int beg, int end) {
  689               byte[] bytes = new byte[(end - beg) / 2];
  690               for (int i = 0; beg + 1 < end; i++) {
  691                   int hi = Character.digit(chars[beg], 16);
  692                   int lo = Character.digit(chars[beg + 1], 16);
  693                   if (hi < 0 || lo < 0) {
  694                       break;
  695                   }
  696                   bytes[i] = (byte)((hi<<4) + lo);
  697                   beg += 2;
  698               }
  699               if (beg != end) {
  700                   throw new IllegalArgumentException(
  701                           "Illegal attribute value: " + new String(chars));
  702               }
  703               return bytes;
  704           }
  705   
  706           /*
  707            * Given an array of chars (with starting and ending indexes into it),
  708            * finds the largest prefix consisting of hex-encoded UTF-8 octets,
  709            * and returns a byte array containing the corresponding UTF-8 octets.
  710            *
  711            * Hex-encoded UTF-8 octets look like this:
  712            *      \03\B1\DF\80
  713            */
  714           private static byte[] getUtf8Octets(char[] chars, int beg, int end) {
  715               byte[] utf8 = new byte[(end - beg) / 3];    // allow enough room
  716               int len = 0;        // index of first unused byte in utf8
  717   
  718               while ((beg + 2 < end) &&
  719                      (chars[beg++] == '\\')) {
  720                   int hi = Character.digit(chars[beg++], 16);
  721                   int lo = Character.digit(chars[beg++], 16);
  722                   if (hi < 0 || lo < 0) {
  723                      break;
  724                   }
  725                   utf8[len++] = (byte)((hi<<4) + lo);
  726               }
  727               if (len == utf8.length) {
  728                   return utf8;
  729               } else {
  730                   byte[] res = new byte[len];
  731                   System.arraycopy(utf8, 0, res, 0, len);
  732                   return res;
  733               }
  734           }
  735   
  736       /*
  737        * Best guess as to what RFC 2253 means by "whitespace".
  738        */
  739       private static boolean isWhitespace(char c) {
  740           return (c == ' ' || c == '\r');
  741       }
  742   
  743       /**
  744        * Serializes only the unparsed RDN, for compactness and to avoid
  745        * any implementation dependency.
  746        *
  747        * @serialData      The RDN string
  748        */
  749       private void writeObject(ObjectOutputStream s)
  750               throws java.io.IOException {
  751           s.defaultWriteObject();
  752           s.writeObject(toString());
  753       }
  754   
  755       private void readObject(ObjectInputStream s)
  756               throws IOException, ClassNotFoundException {
  757           s.defaultReadObject();
  758           entries = new ArrayList(DEFAULT_SIZE);
  759           String unparsed = (String) s.readObject();
  760           try {
  761               (new Rfc2253Parser(unparsed)).parseRdn(this);
  762           } catch (InvalidNameException e) {
  763               // shouldn't happen
  764               throw new java.io.StreamCorruptedException(
  765                       "Invalid name: " + unparsed);
  766           }
  767       }
  768   }

Home » openjdk-7 » javax » naming » ldap » [javadoc | source]