mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Matthew Swift
29.49.2013 fdd9be22de1808af9159a95ae6dd64e6573f5310
Very minor code cleanup to LDIF package:

* add java.io.Reader and java.io.Writer based constructors to LDIF readers and writers
* remove very many duplicated or unnecessary Javadoc comments
7 files modified
703 ■■■■■ changed files
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/AbstractLDIFReader.java 242 ●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/AbstractLDIFStream.java 55 ●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/AbstractLDIFWriter.java 152 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIFChangeRecordReader.java 139 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIFChangeRecordWriter.java 50 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIFEntryReader.java 28 ●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIFEntryWriter.java 37 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/AbstractLDIFReader.java
@@ -22,18 +22,31 @@
 *
 *
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 *      Portions copyright 2011 ForgeRock AS
 *      Portions copyright 2011-2013 ForgeRock AS
 */
package org.forgerock.opendj.ldif;
import static com.forgerock.opendj.util.StaticUtils.toLowerCase;
import static org.forgerock.opendj.ldap.CoreMessages.*;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_COULD_NOT_BASE64_DECODE_ATTR;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_COULD_NOT_BASE64_DECODE_DN;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_INVALID_DN;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_INVALID_LEADING_SPACE;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_INVALID_URL;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_MALFORMED_ATTRIBUTE_NAME;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_MULTI_VALUED_SINGLE_VALUED_ATTRIBUTE;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_NO_ATTR_NAME;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_NO_DN;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_UNEXPECTED_BINARY_OPTION;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_UNKNOWN_ATTRIBUTE_TYPE;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_URL_IO_ERROR;
import static org.forgerock.opendj.ldap.CoreMessages.WARN_LDIF_DUPLICATE_ATTRIBUTE_VALUE;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.util.Iterator;
import java.util.LinkedList;
@@ -63,7 +76,6 @@
abstract class AbstractLDIFReader extends AbstractLDIFStream {
    static final class KeyValuePair {
        String key;
        String value;
    }
@@ -94,9 +106,7 @@
    static final class LDIFRecord {
        final Iterator<String> iterator;
        final LinkedList<String> ldifLines;
        final long lineNumber;
        private LDIFRecord(final long lineNumber, final LinkedList<String> ldifLines) {
@@ -110,22 +120,15 @@
     * LDIF output stream writer implementation.
     */
    private static final class LDIFReaderInputStreamImpl implements LDIFReaderImpl {
        private BufferedReader reader;
        /**
         * Creates a new LDIF input stream reader implementation.
         *
         * @param in
         *            The input stream to use.
         */
        LDIFReaderInputStreamImpl(final InputStream in) {
            this.reader = new BufferedReader(new InputStreamReader(in));
        LDIFReaderInputStreamImpl(final Reader reader) {
            this.reader =
                    reader instanceof BufferedReader ? (BufferedReader) reader
                            : new BufferedReader(reader);
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void close() throws IOException {
            if (reader != null) {
                reader.close();
@@ -133,9 +136,7 @@
            }
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public String readLine() throws IOException {
            String line = null;
            if (reader != null) {
@@ -153,29 +154,18 @@
     * LDIF output stream writer implementation.
     */
    private static final class LDIFReaderListImpl implements LDIFReaderImpl {
        private final Iterator<String> iterator;
        /**
         * Creates a new LDIF list reader.
         *
         * @param ldifLines
         *            The string list.
         */
        LDIFReaderListImpl(final List<String> ldifLines) {
            this.iterator = ldifLines.iterator();
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void close() throws IOException {
            // Nothing to do.
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public String readLine() throws IOException {
            if (iterator.hasNext()) {
                return iterator.next();
@@ -186,43 +176,50 @@
    }
    RejectedLDIFListener rejectedRecordListener = RejectedLDIFListener.FAIL_FAST;
    Schema schema = Schema.getDefaultSchema().asNonStrictSchema();
    SchemaValidationPolicy schemaValidationPolicy = SchemaValidationPolicy.ignoreAll();
    private final LDIFReaderImpl impl;
    private long lineNumber = 0;
    /**
     * Creates a new LDIF entry reader whose source is the provided input
     * stream.
     *
     * @param in
     *            The input stream to use.
     */
    AbstractLDIFReader(final InputStream in) {
        Validator.ensureNotNull(in);
        this.impl = new LDIFReaderInputStreamImpl(in);
        this(new InputStreamReader(in));
    }
    /**
     * Creates a new LDIF entry reader which will read lines of LDIF from the
     * provided list.
     *
     * @param ldifLines
     *            The list from which lines of LDIF should be read.
     */
    AbstractLDIFReader(final List<String> ldifLines) {
        Validator.ensureNotNull(ldifLines);
        this.impl = new LDIFReaderListImpl(ldifLines);
    }
    AbstractLDIFReader(final Reader reader) {
        this.impl = new LDIFReaderInputStreamImpl(reader);
    }
    final void close0() throws IOException {
        impl.close();
    }
    final void handleMalformedRecord(final LDIFRecord record, final LocalizableMessage message)
            throws DecodeException {
        rejectedRecordListener.handleMalformedRecord(record.lineNumber, record.ldifLines, message);
    }
    final void handleSchemaValidationFailure(final LDIFRecord record,
            final List<LocalizableMessage> messages) throws DecodeException {
        rejectedRecordListener.handleSchemaValidationFailure(record.lineNumber, record.ldifLines,
                messages);
    }
    final void handleSchemaValidationWarning(final LDIFRecord record,
            final List<LocalizableMessage> messages) throws DecodeException {
        rejectedRecordListener.handleSchemaValidationWarning(record.lineNumber, record.ldifLines,
                messages);
    }
    final void handleSkippedRecord(final LDIFRecord record, final LocalizableMessage message)
            throws DecodeException {
        rejectedRecordListener.handleSkippedRecord(record.lineNumber, record.ldifLines, message);
    }
    final int parseColonPosition(final LDIFRecord record, final String ldifLine)
            throws DecodeException {
        final int colonPos = ldifLine.indexOf(":");
@@ -237,11 +234,12 @@
    final ByteString parseSingleValue(final LDIFRecord record, final String ldifLine,
            final DN entryDN, final int colonPos, final String attrName) throws DecodeException {
        // Look at the character immediately after the colon. If there is
        // none, then assume an attribute with an empty value. If it is
        // another colon, then the value must be base64-encoded. If it is a
        // less-than sign, then assume that it is a URL. Otherwise, it is a
        // regular value.
        /*
         * Look at the character immediately after the colon. If there is none,
         * then assume an attribute with an empty value. If it is another colon,
         * then the value must be base64-encoded. If it is a less-than sign,
         * then assume that it is a URL. Otherwise, it is a regular value.
         */
        final int length = ldifLine.length();
        ByteString value;
        if (colonPos == length - 1) {
@@ -249,8 +247,10 @@
        } else {
            final char c = ldifLine.charAt(colonPos + 1);
            if (c == ':') {
                // The value is base64-encoded. Find the first non-blank
                // character, take the rest of the line, and base64-decode it.
                /*
                 * The value is base64-encoded. Find the first non-blank
                 * character, take the rest of the line, and base64-decode it.
                 */
                int pos = colonPos + 2;
                while (pos < length && ldifLine.charAt(pos) == ' ') {
                    pos++;
@@ -259,15 +259,19 @@
                try {
                    value = ByteString.valueOfBase64(ldifLine.substring(pos));
                } catch (final LocalizedIllegalArgumentException e) {
                    // The value did not have a valid base64-encoding.
                    /*
                     * The value did not have a valid base64-encoding.
                     */
                    final LocalizableMessage message =
                            ERR_LDIF_COULD_NOT_BASE64_DECODE_ATTR.get(entryDN.toString(),
                                    record.lineNumber, ldifLine, e.getMessageObject());
                    throw DecodeException.error(message);
                }
            } else if (c == '<') {
                // Find the first non-blank character, decode the rest of the
                // line as a URL, and read its contents.
                /*
                 * Find the first non-blank character, decode the rest of the
                 * line as a URL, and read its contents.
                 */
                int pos = colonPos + 2;
                while (pos < length && ldifLine.charAt(pos) == ' ') {
                    pos++;
@@ -298,8 +302,10 @@
                    value = builder.toByteString();
                } catch (final Exception e) {
                    // We were unable to read the contents of that URL for some
                    // reason.
                    /*
                     * We were unable to read the contents of that URL for some
                     * reason.
                     */
                    final LocalizableMessage message =
                            ERR_LDIF_URL_IO_ERROR.get(entryDN.toString(), record.lineNumber,
                                    attrName, String.valueOf(contentURL), String.valueOf(e));
@@ -314,8 +320,10 @@
                    }
                }
            } else {
                // The rest of the line should be the value. Skip over any
                // spaces and take the rest of the line as the value.
                /*
                 * The rest of the line should be the value. Skip over any
                 * spaces and take the rest of the line as the value.
                 */
                int pos = colonPos + 1;
                while (pos < length && ldifLine.charAt(pos) == ' ') {
                    pos++;
@@ -332,13 +340,11 @@
        final StringBuilder lastLineBuilder = new StringBuilder();
        final LinkedList<String> ldifLines = new LinkedList<String>();
        long recordLineNumber = 0;
        final int stateStart = 0;
        final int stateStartCommentLine = 1;
        final int stateGotLDIFLine = 2;
        final int stateGotCommentLine = 3;
        final int appendingLDIFLine = 4;
        int state = stateStart;
        while (true) {
@@ -355,8 +361,10 @@
                    // This is a comment at the start of the LDIF record.
                    state = stateStartCommentLine;
                } else if (isContinuationLine(line)) {
                    // Fatal: got a continuation line at the start of the
                    // record.
                    /*
                     * Fatal: got a continuation line at the start of the
                     * record.
                     */
                    final LocalizableMessage message =
                            ERR_LDIF_INVALID_LEADING_SPACE.get(lineNumber, line);
                    throw DecodeException.fatalError(message);
@@ -473,9 +481,10 @@
                schemaErrors.add(message);
                return true;
            default: // Ignore
                // This should not happen: we should be using a non-strict
                // schema for
                // this policy.
                /*
                 * This should not happen: we should be using a non-strict
                 * schema for this policy.
                 */
                throw new IllegalStateException("Schema is not consistent with policy", e);
            }
        } catch (final LocalizedIllegalArgumentException e) {
@@ -489,9 +498,11 @@
        final ByteString value =
                parseSingleValue(record, ldifLine, entry.getName(), colonPos, attrDescr);
        // Skip the attribute if requested before performing any schema
        // checking: the attribute may have been excluded because it is
        // known to violate the schema.
        /*
         * Skip the attribute if requested before performing any schema
         * checking: the attribute may have been excluded because it is known to
         * violate the schema.
         */
        if (isAttributeExcluded(attributeDescription)) {
            return true;
        }
@@ -515,7 +526,7 @@
        final boolean checkAttributeValues =
                schemaValidationPolicy.checkAttributeValues().needsChecking();
        if (checkAttributeValues) {
            LocalizableMessageBuilder builder = new LocalizableMessageBuilder();
            final LocalizableMessageBuilder builder = new LocalizableMessageBuilder();
            if (!syntax.valueIsAcceptable(value, builder)) {
                schemaErrors.add(builder.toMessage());
                if (schemaValidationPolicy.checkAttributeValues().isReject()) {
@@ -586,10 +597,11 @@
            throw DecodeException.error(message);
        }
        // Look at the character immediately after the colon. If there is
        // none, then assume the null DN. If it is another colon, then the
        // DN must be base64-encoded. Otherwise, it may be one or more
        // spaces.
        /*
         * Look at the character immediately after the colon. If there is none,
         * then assume the null DN. If it is another colon, then the DN must be
         * base64-encoded. Otherwise, it may be one or more spaces.
         */
        final int length = ldifLine.length();
        if (colonPos == length - 1) {
            return DN.rootDN();
@@ -598,8 +610,10 @@
        String dnString = null;
        if (ldifLine.charAt(colonPos + 1) == ':') {
            // The DN is base64-encoded. Find the first non-blank character
            // and take the rest of the line and base64-decode it.
            /*
             * The DN is base64-encoded. Find the first non-blank character and
             * take the rest of the line and base64-decode it.
             */
            int pos = colonPos + 2;
            while (pos < length && ldifLine.charAt(pos) == ' ') {
                pos++;
@@ -616,8 +630,10 @@
                throw DecodeException.error(message);
            }
        } else {
            // The rest of the value should be the DN. Skip over any spaces
            // and attempt to decode the rest of the line as the DN.
            /*
             * The rest of the value should be the DN. Skip over any spaces and
             * attempt to decode the rest of the line as the DN.
             */
            int pos = colonPos + 1;
            while (pos < length && ldifLine.charAt(pos) == ' ') {
                pos++;
@@ -645,8 +661,10 @@
        }
        pair.key = ldifLine.substring(0, colonPos);
        // Look at the character immediately after the colon. If there is
        // none, then no value was specified. Throw an exception
        /*
         * Look at the character immediately after the colon. If there is none,
         * then no value was specified. Throw an exception
         */
        final int length = ldifLine.length();
        if (colonPos == length - 1) {
            pair.key = null;
@@ -654,8 +672,10 @@
        }
        if (allowBase64 && ldifLine.charAt(colonPos + 1) == ':') {
            // The value is base64-encoded. Find the first non-blank
            // character, take the rest of the line, and base64-decode it.
            /*
             * The value is base64-encoded. Find the first non-blank character,
             * take the rest of the line, and base64-decode it.
             */
            int pos = colonPos + 2;
            while (pos < length && ldifLine.charAt(pos) == ' ') {
                pos++;
@@ -668,9 +688,11 @@
                return ldifLine;
            }
        } else {
            // The rest of the value should be the changetype. Skip over any
            // spaces and attempt to decode the rest of the line as the
            // changetype string.
            /*
             * The rest of the value should be the changetype. Skip over any
             * spaces and attempt to decode the rest of the line as the
             * changetype string.
             */
            int pos = colonPos + 1;
            while (pos < length && ldifLine.charAt(pos) == ' ') {
                pos++;
@@ -682,33 +704,12 @@
        return ldifLine;
    }
    final void handleMalformedRecord(final LDIFRecord record, final LocalizableMessage message)
            throws DecodeException {
        rejectedRecordListener.handleMalformedRecord(record.lineNumber, record.ldifLines, message);
    }
    final void handleSchemaValidationFailure(final LDIFRecord record,
            final List<LocalizableMessage> messages) throws DecodeException {
        rejectedRecordListener.handleSchemaValidationFailure(record.lineNumber, record.ldifLines,
                messages);
    }
    final void handleSchemaValidationWarning(final LDIFRecord record,
            final List<LocalizableMessage> messages) throws DecodeException {
        rejectedRecordListener.handleSchemaValidationWarning(record.lineNumber, record.ldifLines,
                messages);
    }
    final void handleSkippedRecord(final LDIFRecord record, final LocalizableMessage message)
            throws DecodeException {
        rejectedRecordListener.handleSkippedRecord(record.lineNumber, record.ldifLines, message);
    }
    // Determine whether the provided line is a continuation line. Note
    // that while RFC 2849 technically only allows a space in this
    // position, both OpenLDAP and the Sun Java System Directory Server
    // allow a tab as well, so we will too for compatibility reasons. See
    // issue #852 for details.
    /*
     * Determine whether the provided line is a continuation line. Note that
     * while RFC 2849 technically only allows a space in this position, both
     * OpenLDAP and the Sun Java System Directory Server allow a tab as well, so
     * we will too for compatibility reasons. See issue #852 for details.
     */
    private boolean isContinuationLine(final String line) {
        return line.charAt(0) == ' ' || line.charAt(0) == '\t';
    }
@@ -720,5 +721,4 @@
        }
        return line;
    }
}
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/AbstractLDIFStream.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2011 ForgeRock AS
 *      Portions copyright 2011-2013 ForgeRock AS
 */
package org.forgerock.opendj.ldif;
@@ -42,51 +42,30 @@
 * Common LDIF reader/writer functionality.
 */
abstract class AbstractLDIFStream {
    final Set<AttributeDescription> excludeAttributes = new HashSet<AttributeDescription>();
    boolean excludeOperationalAttributes = false;
    boolean excludeUserAttributes = false;
    final Set<AttributeDescription> includeAttributes = new HashSet<AttributeDescription>();
    final Set<DN> includeBranches = new HashSet<DN>();
    final Set<DN> excludeBranches = new HashSet<DN>();
    final List<Matcher> excludeFilters = new LinkedList<Matcher>();
    boolean excludeOperationalAttributes = false;
    boolean excludeUserAttributes = false;
    final Set<AttributeDescription> includeAttributes = new HashSet<AttributeDescription>();
    final Set<DN> includeBranches = new HashSet<DN>();
    final List<Matcher> includeFilters = new LinkedList<Matcher>();
    final List<Matcher> excludeFilters = new LinkedList<Matcher>();
    /**
     * Creates a new abstract LDIF stream.
     */
    AbstractLDIFStream() {
        // Nothing to do.
    }
    final boolean isAttributeExcluded(final AttributeDescription attributeDescription) {
        // Let explicit include override more general exclude.
        if (!excludeAttributes.isEmpty() && excludeAttributes.contains(attributeDescription)) {
            return true;
        }
        // Let explicit include override more general exclude.
        if (!includeAttributes.isEmpty()) {
        } else if (!includeAttributes.isEmpty()) {
            return !includeAttributes.contains(attributeDescription);
        } else {
            final AttributeType type = attributeDescription.getAttributeType();
            return (excludeOperationalAttributes && type.isOperational())
                    || (excludeUserAttributes && !type.isOperational());
        }
        final AttributeType type = attributeDescription.getAttributeType();
        if (excludeOperationalAttributes && type.isOperational()) {
            return true;
        }
        if (excludeUserAttributes && !type.isOperational()) {
            return true;
        }
        return false;
    }
    final boolean isBranchExcluded(final DN dn) {
@@ -97,7 +76,6 @@
                }
            }
        }
        if (!includeBranches.isEmpty()) {
            for (final DN includeBranch : includeBranches) {
                if (includeBranch.isSuperiorOrEqualTo(dn)) {
@@ -105,9 +83,9 @@
                }
            }
            return true;
        } else {
            return false;
        }
        return false;
    }
    final boolean isEntryExcluded(final Entry entry) {
@@ -118,7 +96,6 @@
                }
            }
        }
        if (!includeFilters.isEmpty()) {
            for (final Matcher includeFilter : includeFilters) {
                if (includeFilter.matches(entry).toBoolean()) {
@@ -126,9 +103,9 @@
                }
            }
            return true;
        } else {
            return false;
        }
        return false;
    }
}
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/AbstractLDIFWriter.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2011-2012 ForgeRock AS
 *      Portions copyright 2011-2013 ForgeRock AS
 */
package org.forgerock.opendj.ldif;
@@ -31,6 +31,7 @@
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.List;
import java.util.regex.Pattern;
@@ -100,45 +101,29 @@
     * LDIF string list writer implementation.
     */
    private static final class LDIFWriterListImpl implements LDIFWriterImpl {
        private final StringBuilder builder = new StringBuilder();
        private final List<String> ldifLines;
        /**
         * Creates a new LDIF list writer.
         *
         * @param ldifLines
         *            The string list.
         */
        LDIFWriterListImpl(final List<String> ldifLines) {
            this.ldifLines = ldifLines;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void close() throws IOException {
            // Nothing to do.
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void flush() throws IOException {
            // Nothing to do.
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void print(final CharSequence s) throws IOException {
            builder.append(s);
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void println() throws IOException {
            ldifLines.add(builder.toString());
            builder.setLength(0);
@@ -149,81 +134,54 @@
     * LDIF output stream writer implementation.
     */
    private static final class LDIFWriterOutputStreamImpl implements LDIFWriterImpl {
        private final BufferedWriter writer;
        /**
         * Creates a new LDIF output stream writer.
         *
         * @param out
         *            The output stream.
         */
        LDIFWriterOutputStreamImpl(final OutputStream out) {
            this.writer = new BufferedWriter(new OutputStreamWriter(out));
        LDIFWriterOutputStreamImpl(final Writer writer) {
            this.writer =
                    writer instanceof BufferedWriter ? (BufferedWriter) writer
                            : new BufferedWriter(writer);
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void close() throws IOException {
            writer.close();
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void flush() throws IOException {
            writer.flush();
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void print(final CharSequence s) throws IOException {
            writer.append(s);
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void println() throws IOException {
            writer.newLine();
        }
    }
    // Regular expression used for splitting comments on line-breaks.
    /*
     * Regular expression used for splitting comments on line-breaks.
     */
    private static final Pattern SPLIT_NEWLINE = Pattern.compile("\\r?\\n");
    boolean addUserFriendlyComments = false;
    final LDIFWriterImpl impl;
    int wrapColumn = 0;
    private final StringBuilder builder = new StringBuilder(80);
    /**
     * Creates a new LDIF entry writer which will append lines of LDIF to the
     * provided list.
     *
     * @param ldifLines
     *            The list to which lines of LDIF should be appended.
     */
    public AbstractLDIFWriter(final List<String> ldifLines) {
        Validator.ensureNotNull(ldifLines);
    AbstractLDIFWriter(final List<String> ldifLines) {
        this.impl = new LDIFWriterListImpl(ldifLines);
    }
    /**
     * Creates a new LDIF entry writer whose destination is the provided output
     * stream.
     *
     * @param out
     *            The output stream to use.
     */
    public AbstractLDIFWriter(final OutputStream out) {
        Validator.ensureNotNull(out);
        this.impl = new LDIFWriterOutputStreamImpl(out);
    AbstractLDIFWriter(final OutputStream out) {
        this(new OutputStreamWriter(out));
    }
    AbstractLDIFWriter(final Writer writer) {
        this.impl = new LDIFWriterOutputStreamImpl(writer);
    }
    final void close0() throws IOException {
@@ -238,12 +196,16 @@
    final void writeComment0(final CharSequence comment) throws IOException {
        Validator.ensureNotNull(comment);
        // First, break up the comment into multiple lines to preserve the
        // original spacing that it contained.
        /*
         * First, break up the comment into multiple lines to preserve the
         * original spacing that it contained.
         */
        final String[] lines = SPLIT_NEWLINE.split(comment);
        // Now iterate through the lines and write them out, prefixing and
        // wrapping them as necessary.
        /*
         * Now iterate through the lines and write them out, prefixing and
         * wrapping them as necessary.
         */
        for (final String line : lines) {
            if (!shouldWrap()) {
                impl.print("# ");
@@ -251,7 +213,6 @@
                impl.println();
            } else {
                final int breakColumn = wrapColumn - 2;
                if (line.length() <= breakColumn) {
                    impl.print("# ");
                    impl.print(line);
@@ -267,7 +228,6 @@
                            startPos = line.length();
                        } else {
                            final int endPos = startPos + breakColumn;
                            int i = endPos - 1;
                            while (i > startPos) {
                                if (line.charAt(i) == ' ') {
@@ -278,19 +238,17 @@
                                    startPos = i + 1;
                                    continue outerLoop;
                                }
                                i--;
                            }
                            // If we've gotten here, then there are no spaces on
                            // the
                            // entire line. If that happens, then we'll have to
                            // break
                            // in the middle of a word.
                            /*
                             * If we've gotten here, then there are no spaces on
                             * the entire line. If that happens, then we'll have
                             * to break in the middle of a word.
                             */
                            impl.print("# ");
                            impl.print(line.substring(startPos, endPos));
                            impl.println();
                            startPos = endPos;
                        }
                    }
@@ -304,7 +262,6 @@
            final StringBuilder key = new StringBuilder("control: ");
            key.append(control.getOID());
            key.append(control.isCritical() ? " true" : " false");
            if (control.hasValue()) {
                writeKeyAndValue(key, control.getValue());
            } else {
@@ -317,17 +274,20 @@
            throws IOException {
        builder.setLength(0);
        // If the value is empty, then just append a single colon and a
        // single space.
        /*
         * If the value is empty, then just append a single colon and a single
         * space.
         */
        if (value.length() == 0) {
            builder.append(key);
            builder.append(": ");
        } else if (needsBase64Encoding(value)) {
            if (addUserFriendlyComments) {
                // TODO: Only display comments for valid UTF-8 values, not
                // binary values.
                /*
                 * TODO: Only display comments for valid UTF-8 values, not
                 * binary values.
                 */
            }
            builder.setLength(0);
            builder.append(key);
            builder.append(":: ");
@@ -337,7 +297,6 @@
            builder.append(": ");
            builder.append(value.toString());
        }
        writeLine(builder);
    }
@@ -372,8 +331,10 @@
            return false;
        }
        // If the value starts with a space, colon, or less than, then it
        // needs to be base64 encoded.
        /*
         * If the value starts with a space, colon, or less than, then it needs
         * to be base64 encoded.
         */
        switch (bytes.byteAt(0)) {
        case 0x20: // Space
        case 0x3A: // Colon
@@ -381,14 +342,17 @@
            return true;
        }
        // If the value ends with a space, then it needs to be
        // base64 encoded.
        /*
         * If the value ends with a space, then it needs to be base64 encoded.
         */
        if (length > 1 && bytes.byteAt(length - 1) == 0x20) {
            return true;
        }
        // If the value contains a null, newline, or return character, then
        // it needs to be base64 encoded.
        /*
         * If the value contains a null, newline, or return character, then it
         * needs to be base64 encoded.
         */
        byte b;
        for (int i = 0; i < bytes.length(); i++) {
            b = bytes.byteAt(i);
@@ -404,7 +368,7 @@
            }
        }
        // If we've made it here, then there's no reason to base64 encode.
        /* If we've made it here, then there's no reason to base64 encode. */
        return false;
    }
@@ -415,11 +379,9 @@
    @SuppressWarnings("unused")
    private void writeKeyAndURL(final CharSequence key, final CharSequence url) throws IOException {
        builder.setLength(0);
        builder.append(key);
        builder.append(":: ");
        builder.append(url);
        writeLine(builder);
    }
}
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIFChangeRecordReader.java
@@ -28,10 +28,30 @@
package org.forgerock.opendj.ldif;
import static com.forgerock.opendj.util.StaticUtils.toLowerCase;
import static org.forgerock.opendj.ldap.CoreMessages.*;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_ATTRIBUTE_NAME_MISMATCH;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_BAD_CHANGE_TYPE;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_BAD_MODIFICATION_TYPE;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_CHANGE_EXCLUDED_BY_DN;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_MALFORMED_ATTRIBUTE_NAME;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_MALFORMED_CHANGE_TYPE;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_MALFORMED_CONTROL;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_MALFORMED_DELETE;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_MALFORMED_DELETE_OLD_RDN;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_MALFORMED_MODIFICATION_TYPE;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_MALFORMED_NEW_RDN;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_MALFORMED_NEW_SUPERIOR;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_NO_CHANGE_TYPE;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_NO_DELETE_OLD_RDN;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_NO_NEW_RDN;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_UNEXPECTED_BINARY_OPTION;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_UNKNOWN_ATTRIBUTE_TYPE;
import static org.forgerock.opendj.ldap.CoreMessages.WARN_READ_LDIF_RECORD_MULTIPLE_CHANGE_RECORDS_FOUND;
import static org.forgerock.opendj.ldap.CoreMessages.WARN_READ_LDIF_RECORD_NO_CHANGE_RECORD_FOUND;
import static org.forgerock.opendj.ldap.CoreMessages.WARN_READ_LDIF_RECORD_UNEXPECTED_IO_ERROR;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
@@ -91,6 +111,12 @@
 *      Interchange Format (LDIF) - Technical Specification </a>
 */
public final class LDIFChangeRecordReader extends AbstractLDIFReader implements ChangeRecordReader {
    private static final Pattern CONTROL_REGEX = Pattern
            .compile("^\\s*(\\d+(.\\d+)*)(\\s+((true)|(false)))?\\s*(:(:)?\\s*?\\S+)?\\s*$");
    // Poison used to indicate end of LDIF.
    private static final ChangeRecord EOF = Requests.newAddRequest(DN.rootDN());
    /**
     * Parses the provided array of LDIF lines as a single LDIF change record.
     *
@@ -140,12 +166,6 @@
    private ChangeRecord nextChangeRecord = null;
    // Poison used to indicate end of LDIF.
    private static final ChangeRecord EOF = Requests.newAddRequest(DN.rootDN());
    private static final Pattern CONTROL_REGEX =
            Pattern.compile("^\\s*(\\d+(.\\d+)*)(\\s+((true)|(false)))?\\s*(:(:)?\\s*?\\S+)?\\s*$");
    /**
     * Creates a new LDIF change record reader whose source is the provided
     * input stream.
@@ -173,6 +193,19 @@
    }
    /**
     * Creates a new LDIF change record reader whose source is the provided
     * character stream reader.
     *
     * @param reader
     *            The character stream reader to use.
     * @throws NullPointerException
     *             If {@code reader} was {@code null}.
     */
    public LDIFChangeRecordReader(final Reader reader) {
        super(reader);
    }
    /**
     * Creates a new LDIF change record reader which will read lines of LDIF
     * from the provided array of LDIF lines.
     *
@@ -379,8 +412,10 @@
            }
            try {
                // Read the DN of the entry and see if it is one that should be
                // included in the import.
                /*
                 * Read the DN of the entry and see if it is one that should be
                 * included in the import.
                 */
                final DN entryDN = readLDIFRecordDN(record);
                if (entryDN == null) {
                    // Skip version record.
@@ -450,7 +485,7 @@
                    // Add the controls to the record.
                    if (controls != null) {
                        for (Control control : controls) {
                        for (final Control control : controls) {
                            nextChangeRecord.addControl(control);
                        }
                    }
@@ -463,27 +498,6 @@
        return nextChangeRecord;
    }
    private Control parseControl(DN entryDN, LDIFRecord record, String ldifLine, String value)
            throws DecodeException {
        Matcher matcher = CONTROL_REGEX.matcher(value);
        if (!matcher.matches()) {
            final LocalizableMessage message =
                    ERR_LDIF_MALFORMED_CONTROL.get(record.lineNumber, entryDN.toString(), ldifLine);
            throw DecodeException.error(message);
        }
        String oid = matcher.group(1);
        boolean isCritical = matcher.group(5) != null;
        String controlValueString = matcher.group(7);
        ByteString controlValue = null;
        if (controlValueString != null) {
            controlValue = parseSingleValue(record, ldifLine, entryDN,  ldifLine.indexOf(':', 8), oid);
        }
        return GenericControl.newControl(oid, isCritical, controlValue);
    }
    private ChangeRecord parseAddChangeRecordEntry(final DN entryDN, final String lastLDIFLine,
            final LDIFRecord record) throws DecodeException {
        // Use an Entry for the AttributeSequence.
@@ -520,6 +534,25 @@
        return Requests.newAddRequest(entry);
    }
    private Control parseControl(final DN entryDN, final LDIFRecord record, final String ldifLine,
            final String value) throws DecodeException {
        final Matcher matcher = CONTROL_REGEX.matcher(value);
        if (!matcher.matches()) {
            final LocalizableMessage message =
                    ERR_LDIF_MALFORMED_CONTROL.get(record.lineNumber, entryDN.toString(), ldifLine);
            throw DecodeException.error(message);
        }
        final String oid = matcher.group(1);
        final boolean isCritical = matcher.group(5) != null;
        final String controlValueString = matcher.group(7);
        ByteString controlValue = null;
        if (controlValueString != null) {
            controlValue =
                    parseSingleValue(record, ldifLine, entryDN, ldifLine.indexOf(':', 8), oid);
        }
        return GenericControl.newControl(oid, isCritical, controlValue);
    }
    private ChangeRecord parseDeleteChangeRecordEntry(final DN entryDN, final LDIFRecord record)
            throws DecodeException {
        if (record.iterator.hasNext()) {
@@ -582,9 +615,10 @@
                    schemaErrors.add(message);
                    continue;
                default: // Ignore
                    // This should not happen: we should be using a non-strict
                    // schema for
                    // this policy.
                    /*
                     * This should not happen: we should be using a non-strict
                     * schema for this policy.
                     */
                    throw new IllegalStateException("Schema is not consistent with policy", e);
                }
            } catch (final LocalizedIllegalArgumentException e) {
@@ -594,9 +628,11 @@
                throw DecodeException.error(message);
            }
            // Skip the attribute if requested before performing any schema
            // checking: the attribute may have been excluded because it is
            // known to violate the schema.
            /*
             * Skip the attribute if requested before performing any schema
             * checking: the attribute may have been excluded because it is
             * known to violate the schema.
             */
            if (isAttributeExcluded(attributeDescription)) {
                continue;
            }
@@ -620,8 +656,10 @@
                attributeDescription = attributeDescription.withOption("binary");
            }
            // Now go through the rest of the attributes until the "-" line is
            // reached.
            /*
             * Now go through the rest of the attributes until the "-" line is
             * reached.
             */
            attributeValues.clear();
            while (record.iterator.hasNext()) {
                ldifLine = record.iterator.next();
@@ -637,9 +675,11 @@
                try {
                    attributeDescription2 = AttributeDescription.valueOf(attrDescr, schema);
                } catch (final LocalizedIllegalArgumentException e) {
                    // No need to catch schema exception here because it implies
                    // that the
                    // attribute name is wrong and the record is malformed.
                    /*
                     * No need to catch schema exception here because it implies
                     * that the attribute name is wrong and the record is
                     * malformed.
                     */
                    final LocalizableMessage message =
                            ERR_LDIF_MALFORMED_ATTRIBUTE_NAME.get(record.lineNumber, entryDN
                                    .toString(), attrDescr);
@@ -664,15 +704,14 @@
                final ByteString value =
                        parseSingleValue(record, ldifLine, entryDN, colonPos, attrDescr);
                if (schemaValidationPolicy.checkAttributeValues().needsChecking()) {
                    LocalizableMessageBuilder builder = new LocalizableMessageBuilder();
                    final LocalizableMessageBuilder builder = new LocalizableMessageBuilder();
                    if (!syntax.valueIsAcceptable(value, builder)) {
                        // Just log a message, but don't skip the value since
                        // this could
                        // change the semantics of the modification (e.g. if all
                        // values in a
                        // delete are skipped then this implies that the whole
                        // attribute
                        // should be removed).
                        /*
                         * Just log a message, but don't skip the value since
                         * this could change the semantics of the modification
                         * (e.g. if all values in a delete are skipped then this
                         * implies that the whole attribute should be removed).
                         */
                        if (schemaValidationPolicy.checkAttributeValues().isReject()) {
                            schemaValidationFailure = true;
                        }
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIFChangeRecordWriter.java
@@ -22,13 +22,15 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2012 ForgeRock AS.
 *      Portions copyright 2012-2013 ForgeRock AS.
 */
package org.forgerock.opendj.ldif;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.util.List;
import org.forgerock.opendj.ldap.Attribute;
@@ -72,6 +74,25 @@
public final class LDIFChangeRecordWriter extends AbstractLDIFWriter implements ChangeRecordWriter {
    /**
     * Returns the LDIF string representation of the provided change record.
     *
     * @param change
     *            The change record.
     * @return The LDIF string representation of the provided change record.
     */
    public static String toString(final ChangeRecord change) {
        final StringWriter writer = new StringWriter(128);
        try {
            new LDIFChangeRecordWriter(writer).setAddUserFriendlyComments(true).writeChangeRecord(
                    change).close();
        } catch (final IOException e) {
            // Should never happen.
            throw new IllegalStateException(e);
        }
        return writer.toString();
    }
    /**
     * Creates a new LDIF change record writer which will append lines of LDIF
     * to the provided list.
     *
@@ -94,8 +115,20 @@
    }
    /**
     * Creates a new LDIF change record writer whose destination is the provided
     * character stream writer.
     *
     * @param writer
     *            The character stream writer to use.
     */
    public LDIFChangeRecordWriter(final Writer writer) {
        super(writer);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void close() throws IOException {
        close0();
    }
@@ -103,6 +136,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public void flush() throws IOException {
        flush0();
    }
@@ -230,6 +264,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public LDIFChangeRecordWriter writeChangeRecord(final AddRequest change) throws IOException {
        Validator.ensureNotNull(change);
@@ -262,6 +297,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public LDIFChangeRecordWriter writeChangeRecord(final ChangeRecord change) throws IOException {
        Validator.ensureNotNull(change);
@@ -281,6 +317,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public LDIFChangeRecordWriter writeChangeRecord(final DeleteRequest change) throws IOException {
        Validator.ensureNotNull(change);
@@ -302,6 +339,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public LDIFChangeRecordWriter writeChangeRecord(final ModifyDNRequest change)
            throws IOException {
        Validator.ensureNotNull(change);
@@ -314,9 +352,11 @@
        writeKeyAndValue("dn", change.getName().toString());
        writeControls(change.getControls());
        // Write the changetype. Some older tools may not support the
        // "moddn" changetype, so only use it if a newSuperior element has
        // been provided, but use modrdn elsewhere.
        /*
         * Write the changetype. Some older tools may not support the "moddn"
         * changetype, so only use it if a newSuperior element has been
         * provided, but use modrdn elsewhere.
         */
        if (change.getNewSuperior() == null) {
            writeLine("changetype: modrdn");
        } else {
@@ -338,6 +378,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public LDIFChangeRecordWriter writeChangeRecord(final ModifyRequest change) throws IOException {
        Validator.ensureNotNull(change);
@@ -381,6 +422,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public LDIFChangeRecordWriter writeComment(final CharSequence comment) throws IOException {
        writeComment0(comment);
        return this;
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIFEntryReader.java
@@ -22,15 +22,20 @@
 *
 *
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 *      Portions copyright 2011-2012 ForgeRock AS
 *      Portions copyright 2011-2013 ForgeRock AS
 */
package org.forgerock.opendj.ldif;
import static org.forgerock.opendj.ldap.CoreMessages.*;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_ENTRY_EXCLUDED_BY_DN;
import static org.forgerock.opendj.ldap.CoreMessages.ERR_LDIF_ENTRY_EXCLUDED_BY_FILTER;
import static org.forgerock.opendj.ldap.CoreMessages.WARN_READ_LDIF_RECORD_MULTIPLE_CHANGE_RECORDS_FOUND;
import static org.forgerock.opendj.ldap.CoreMessages.WARN_READ_LDIF_RECORD_NO_CHANGE_RECORD_FOUND;
import static org.forgerock.opendj.ldap.CoreMessages.WARN_READ_LDIF_RECORD_UNEXPECTED_IO_ERROR;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
@@ -134,6 +139,19 @@
    }
    /**
     * Creates a new LDIF entry reader whose source is the provided character
     * stream reader.
     *
     * @param reader
     *            The character stream reader to use.
     * @throws NullPointerException
     *             If {@code reader} was {@code null}.
     */
    public LDIFEntryReader(final Reader reader) {
        super(reader);
    }
    /**
     * Creates a new LDIF entry reader which will read lines of LDIF from the
     * provided array of LDIF lines.
     *
@@ -366,8 +384,10 @@
            }
            try {
                // Read the DN of the entry and see if it is one that should be
                // included in the import.
                /*
                 * Read the DN of the entry and see if it is one that should be
                 * included in the import.
                 */
                final DN entryDN = readLDIFRecordDN(record);
                if (entryDN == null) {
                    // Skip version record.
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIFEntryWriter.java
@@ -22,13 +22,15 @@
 *
 *
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 *      Portions copyright 2012 ForgeRock AS.
 *      Portions copyright 2012-2013 ForgeRock AS.
 */
package org.forgerock.opendj.ldif;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.util.List;
import org.forgerock.opendj.ldap.Attribute;
@@ -50,6 +52,24 @@
public final class LDIFEntryWriter extends AbstractLDIFWriter implements EntryWriter {
    /**
     * Returns the LDIF string representation of the provided entry.
     *
     * @param entry
     *            The entry.
     * @return The LDIF string representation of the provided entry.
     */
    public static String toString(final Entry entry) {
        final StringWriter writer = new StringWriter(128);
        try {
            new LDIFEntryWriter(writer).setAddUserFriendlyComments(true).writeEntry(entry).close();
        } catch (final IOException e) {
            // Should never happen.
            throw new IllegalStateException(e);
        }
        return writer.toString();
    }
    /**
     * Creates a new LDIF entry writer which will append lines of LDIF to the
     * provided list.
     *
@@ -72,8 +92,20 @@
    }
    /**
     * Creates a new LDIF entry writer whose destination is the provided
     * character stream writer.
     *
     * @param writer
     *            The character stream writer to use.
     */
    public LDIFEntryWriter(final Writer writer) {
        super(writer);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void close() throws IOException {
        close0();
    }
@@ -81,6 +113,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public void flush() throws IOException {
        flush0();
    }
@@ -235,6 +268,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public LDIFEntryWriter writeComment(final CharSequence comment) throws IOException {
        writeComment0(comment);
        return this;
@@ -243,6 +277,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public LDIFEntryWriter writeEntry(final Entry entry) throws IOException {
        Validator.ensureNotNull(entry);