org.ldaptive.DnParser.java Source code

Java tutorial

Introduction

Here is the source code for org.ldaptive.DnParser.java

Source

/*
  $Id$
    
  Copyright (C) 2003-2012 Virginia Tech.
  All rights reserved.
    
  SEE LICENSE FOR MORE INFORMATION
    
  Author:  Middleware Services
  Email:   middleware@vt.edu
  Version: $Revision$
  Updated: $Date$
*/
package org.ldaptive;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.ldaptive.asn1.DERParser;
import org.ldaptive.asn1.OctetStringType;
import org.ldaptive.asn1.ParseHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Parses DNs following the rules in <a
 * href="http://www.ietf.org/rfc/rfc4514.txt">RFC 4514</a>. Attempts to be as
 * generous as possible in the format of allowed DNs.
 *
 * @author  Middleware Services
 * @version  $Revision$ $Date$
 */
public final class DnParser {

    /** Logger for this class. */
    private static final Logger LOGGER = LoggerFactory.getLogger(DnParser.class);

    /** Hexadecimal radix. */
    private static final int HEX_RADIX = 16;

    /** Default constructor. */
    private DnParser() {
    }

    /**
     * Returns the RDN values for the attribute type with the supplied name.
     *
     * @param  dn  to parse
     * @param  name  of the attribute type to return values for
     *
     * @return  DN attribute values
     */
    public static Collection<String> getValues(final String dn, final String name) {
        final Collection<String> values = new ArrayList<String>();
        for (LdapAttribute la : convertDnToAttributes(dn)) {
            if (la.getName().equalsIgnoreCase(name)) {
                values.addAll(la.getStringValues());
            }
        }
        return values;
    }

    /**
     * Returns the RDN value for the attribute type with the supplied name. If the
     * component has multiple values, the first one is returned.
     *
     * @param  dn  to parse
     * @param  name  of the attribute to return value for
     *
     * @return  DN attribute value
     */
    public static String getValue(final String dn, final String name) {
        final Collection<String> values = getValues(dn, name);
        if (values.isEmpty()) {
            return "";
        }
        return values.iterator().next();
    }

    /**
     * Returns a string representation of the supplied DN beginning at the
     * supplied index. The leftmost component begins at index 0.
     *
     * @param  dn  to parse
     * @param  index  components included in the result
     *
     * @return  DN from the supplied index
     *
     * @throws  IndexOutOfBoundsException  if index is less than 0 or greater than
     * the number of RDNs
     */
    public static String substring(final String dn, final int index) {
        if (index < 0) {
            throw new IndexOutOfBoundsException("index cannot be negative");
        }

        final List<LdapAttribute> attrs = convertDnToAttributes(dn);
        if (index >= attrs.size()) {
            throw new IndexOutOfBoundsException("index cannot be larger than the number of RDNs");
        }

        final StringBuilder sb = new StringBuilder();
        for (int i = 0; i < attrs.size(); i++) {
            if (i >= index) {
                final LdapAttribute la = attrs.get(i);
                sb.append(la.getName()).append("=").append(la.getStringValue()).append(",");
            }
        }
        if (sb.length() > 0 && sb.charAt(sb.length() - 1) == ',') {
            sb.deleteCharAt(sb.length() - 1);
        }
        return sb.toString();
    }

    /**
     * Parses the supplied DN and converts each RDN into a {@link LdapAttribute}.
     *
     * @param  dn  to parse
     *
     * @return  list of ldap attributes for each RDN
     */
    protected static List<LdapAttribute> convertDnToAttributes(final String dn) {
        LOGGER.debug("parsing DN: {}", dn);

        final List<LdapAttribute> attributes = new ArrayList<LdapAttribute>();
        if (dn.isEmpty()) {
            return attributes;
        }

        int pos = 0;
        while (pos < dn.length()) {
            final int endAttrNamePos = readToChar(dn, new char[] { '=' }, pos);
            final String attrName = dn.substring(pos, endAttrNamePos);
            LOGGER.trace("read attribute name: [{}]", attrName);
            pos = endAttrNamePos;
            // error if char isn't an '='
            if (pos >= dn.length() || dn.charAt(pos++) != '=') {
                throw new IllegalArgumentException("Invalid DN: " + dn);
            }

            final int endAttrValuePos = readToChar(dn, new char[] { '+', ',' }, pos);
            String attrValue = dn.substring(pos, endAttrValuePos);
            LOGGER.trace("read attribute value: [{}]", attrValue);
            attrValue = attrValue.trim();
            // error if attribute value is empty
            if (attrValue.isEmpty()) {
                throw new IllegalArgumentException("Invalid DN: " + dn);
            }
            if (attrValue.startsWith("#")) {
                final DERParser parser = new DERParser();
                final OctetStringHandler handler = new OctetStringHandler();
                parser.registerHandler("/OCTSTR", handler);

                final String hexData = attrValue.substring(1, attrValue.length());
                parser.parse(ByteBuffer.wrap(decodeHexValue(hexData.toCharArray())));
                attributes.add(new LdapAttribute(attrName.trim(), handler.getDecodedValue()));
            } else {
                attributes.add(new LdapAttribute(attrName.trim(), decodeStringValue(attrValue)));
            }
            pos = endAttrValuePos + 1;
        }
        LOGGER.debug("parsed DN into: {}", attributes);
        return attributes;
    }

    /**
     * Decodes the supplied hexadecimal value.
     *
     * @param  value  hex to decode
     *
     * @return  decoded bytes
     */
    protected static byte[] decodeHexValue(final char[] value) {
        if (value == null || value.length == 0) {
            throw new IllegalArgumentException("Invalid HEX value: value cannot be null or empty");
        }
        try {
            return Hex.decodeHex(value);
        } catch (DecoderException e) {
            throw new IllegalArgumentException("Invalid HEX value: " + String.valueOf(value), e);
        }
    }

    /**
     * Decodes the supplied string attribute value. Unescapes escaped characters.
     * If escaped character is a hex value, it is decoded.
     *
     * @param  value  to decode
     *
     * @return  decoded string
     */
    protected static String decodeStringValue(final String value) {
        final StringBuilder sb = new StringBuilder();
        int pos = 0;
        final StringBuilder hexValue = new StringBuilder();
        while (pos < value.length()) {
            char c = value.charAt(pos);
            boolean appendHex = false;
            boolean appendValue = false;
            switch (c) {

            case '\\':
                if (pos + 1 < value.length()) {
                    c = value.charAt(++pos);
                    // if hexadecimal character add to buffer to decode later
                    if (Character.digit(c, HEX_RADIX) != -1) {
                        if (pos + 1 < value.length()) {
                            hexValue.append(c).append(value.charAt(++pos));
                            if (pos + 1 == value.length()) {
                                appendHex = true;
                            }
                        } else {
                            throw new IllegalArgumentException("Invalid HEX value: " + c);
                        }
                    } else {
                        appendHex = hexValue.length() > 0;
                        appendValue = true;
                    }
                }
                break;

            default:
                appendHex = hexValue.length() > 0;
                appendValue = true;
                break;
            }
            if (appendHex) {
                sb.append(LdapUtils.utf8Encode(decodeHexValue(hexValue.toString().toCharArray())));
                hexValue.setLength(0);
            }
            if (appendValue) {
                sb.append(c);
            }

            pos++;
        }
        return sb.toString();
    }

    /**
     * Reads the supplied string starting at the supplied position until one of
     * the supplied characters is found. Characters escaped with '\' are ignored.
     *
     * @param  s  to read
     * @param  chars  to match
     * @param  pos  to start reading at
     *
     * @return  string index that matched a character or the last index in the
     * string
     */
    private static int readToChar(final String s, final char[] chars, final int pos) {
        int i = pos;
        while (i < s.length()) {
            boolean match = false;
            final char sChar = s.charAt(i);
            // ignore escaped characters
            if (sChar == '\\' && i + 1 < s.length()) {
                i++;
            } else {
                for (char c : chars) {
                    if (c == s.charAt(i)) {
                        match = true;
                    }
                }
                if (match) {
                    break;
                }
            }
            i++;
        }
        return i;
    }

    /** Parse handler for decoding octet strings. */
    private static class OctetStringHandler implements ParseHandler {

        /** Decoded octet string. */
        private String decoded;

        /** {@inheritDoc} */
        @Override
        public void handle(final DERParser parser, final ByteBuffer encoded) {
            decoded = OctetStringType.decode(encoded);
        }

        /**
         * Returns the decoded octet string value.
         *
         * @return  decoded octet string
         */
        public String getDecodedValue() {
            return decoded;
        }
    }
}