001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2009-2010 Sun Microsystems, Inc.
025 *      Portions copyright 2012-2013 ForgeRock AS.
026 */
027
028package org.forgerock.opendj.ldif;
029
030import java.io.IOException;
031import java.io.OutputStream;
032import java.io.StringWriter;
033import java.io.Writer;
034import java.util.List;
035
036import org.forgerock.opendj.ldap.Attribute;
037import org.forgerock.opendj.ldap.AttributeDescription;
038import org.forgerock.opendj.ldap.ByteString;
039import org.forgerock.opendj.ldap.DN;
040import org.forgerock.opendj.ldap.Entry;
041import org.forgerock.opendj.ldap.Matcher;
042
043import org.forgerock.util.Reject;
044
045/**
046 * An LDIF entry writer writes attribute value records (entries) using the LDAP
047 * Data Interchange Format (LDIF) to a user defined destination.
048 *
049 * @see <a href="http://tools.ietf.org/html/rfc2849">RFC 2849 - The LDAP Data
050 *      Interchange Format (LDIF) - Technical Specification </a>
051 */
052public final class LDIFEntryWriter extends AbstractLDIFWriter implements EntryWriter {
053
054    /**
055     * Returns the LDIF string representation of the provided entry.
056     *
057     * @param entry
058     *            The entry.
059     * @return The LDIF string representation of the provided entry.
060     */
061    public static String toString(final Entry entry) {
062        final StringWriter writer = new StringWriter(128);
063        try {
064            new LDIFEntryWriter(writer).setAddUserFriendlyComments(true).writeEntry(entry).close();
065        } catch (final IOException e) {
066            // Should never happen.
067            throw new IllegalStateException(e);
068        }
069        return writer.toString();
070    }
071
072    /**
073     * Creates a new LDIF entry writer which will append lines of LDIF to the
074     * provided list.
075     *
076     * @param ldifLines
077     *            The list to which lines of LDIF should be appended.
078     */
079    public LDIFEntryWriter(final List<String> ldifLines) {
080        super(ldifLines);
081    }
082
083    /**
084     * Creates a new LDIF entry writer whose destination is the provided output
085     * stream.
086     *
087     * @param out
088     *            The output stream to use.
089     */
090    public LDIFEntryWriter(final OutputStream out) {
091        super(out);
092    }
093
094    /**
095     * Creates a new LDIF entry writer whose destination is the provided
096     * character stream writer.
097     *
098     * @param writer
099     *            The character stream writer to use.
100     */
101    public LDIFEntryWriter(final Writer writer) {
102        super(writer);
103    }
104
105    /** {@inheritDoc} */
106    @Override
107    public void close() throws IOException {
108        close0();
109    }
110
111    /** {@inheritDoc} */
112    @Override
113    public void flush() throws IOException {
114        flush0();
115    }
116
117    /**
118     * Specifies whether or not user-friendly comments should be added whenever
119     * distinguished names or UTF-8 attribute values are encountered which
120     * contained non-ASCII characters. The default is {@code false}.
121     *
122     * @param addUserFriendlyComments
123     *            {@code true} if user-friendly comments should be added, or
124     *            {@code false} otherwise.
125     * @return A reference to this {@code LDIFEntryWriter}.
126     */
127    public LDIFEntryWriter setAddUserFriendlyComments(final boolean addUserFriendlyComments) {
128        this.addUserFriendlyComments = addUserFriendlyComments;
129        return this;
130    }
131
132    /**
133     * Specifies whether or not all operational attributes should be excluded
134     * from any entries that are written to LDIF. The default is {@code false}.
135     *
136     * @param excludeOperationalAttributes
137     *            {@code true} if all operational attributes should be excluded,
138     *            or {@code false} otherwise.
139     * @return A reference to this {@code LDIFEntryWriter}.
140     */
141    public LDIFEntryWriter setExcludeAllOperationalAttributes(
142            final boolean excludeOperationalAttributes) {
143        this.excludeOperationalAttributes = excludeOperationalAttributes;
144        return this;
145    }
146
147    /**
148     * Specifies whether or not all user attributes should be excluded from any
149     * entries that are written to LDIF. The default is {@code false}.
150     *
151     * @param excludeUserAttributes
152     *            {@code true} if all user attributes should be excluded, or
153     *            {@code false} otherwise.
154     * @return A reference to this {@code LDIFEntryWriter}.
155     */
156    public LDIFEntryWriter setExcludeAllUserAttributes(final boolean excludeUserAttributes) {
157        this.excludeUserAttributes = excludeUserAttributes;
158        return this;
159    }
160
161    /**
162     * Excludes the named attribute from any entries that are written to LDIF.
163     * By default all attributes are included unless explicitly excluded.
164     *
165     * @param attributeDescription
166     *            The name of the attribute to be excluded.
167     * @return A reference to this {@code LDIFEntryWriter}.
168     */
169    public LDIFEntryWriter setExcludeAttribute(final AttributeDescription attributeDescription) {
170        Reject.ifNull(attributeDescription);
171        excludeAttributes.add(attributeDescription);
172        return this;
173    }
174
175    /**
176     * Excludes all entries beneath the named entry (inclusive) from being
177     * written to LDIF. By default all entries are written unless explicitly
178     * excluded or included by branches or filters.
179     *
180     * @param excludeBranch
181     *            The distinguished name of the branch to be excluded.
182     * @return A reference to this {@code LDIFEntryWriter}.
183     */
184    public LDIFEntryWriter setExcludeBranch(final DN excludeBranch) {
185        Reject.ifNull(excludeBranch);
186        excludeBranches.add(excludeBranch);
187        return this;
188    }
189
190    /**
191     * Excludes all entries which match the provided filter matcher from being
192     * written to LDIF. By default all entries are written unless explicitly
193     * excluded or included by branches or filters.
194     *
195     * @param excludeFilter
196     *            The filter matcher.
197     * @return A reference to this {@code LDIFEntryWriter}.
198     */
199    public LDIFEntryWriter setExcludeFilter(final Matcher excludeFilter) {
200        Reject.ifNull(excludeFilter);
201        excludeFilters.add(excludeFilter);
202        return this;
203    }
204
205    /**
206     * Ensures that the named attribute is not excluded from any entries that
207     * are written to LDIF. By default all attributes are included unless
208     * explicitly excluded.
209     *
210     * @param attributeDescription
211     *            The name of the attribute to be included.
212     * @return A reference to this {@code LDIFEntryWriter}.
213     */
214    public LDIFEntryWriter setIncludeAttribute(final AttributeDescription attributeDescription) {
215        Reject.ifNull(attributeDescription);
216        includeAttributes.add(attributeDescription);
217        return this;
218    }
219
220    /**
221     * Ensures that all entries beneath the named entry (inclusive) are written
222     * to LDIF. By default all entries are written unless explicitly excluded or
223     * included by branches or filters.
224     *
225     * @param includeBranch
226     *            The distinguished name of the branch to be included.
227     * @return A reference to this {@code LDIFEntryWriter}.
228     */
229    public LDIFEntryWriter setIncludeBranch(final DN includeBranch) {
230        Reject.ifNull(includeBranch);
231        includeBranches.add(includeBranch);
232        return this;
233    }
234
235    /**
236     * Ensures that all entries which match the provided filter matcher are
237     * written to LDIF. By default all entries are written unless explicitly
238     * excluded or included by branches or filters.
239     *
240     * @param includeFilter
241     *            The filter matcher.
242     * @return A reference to this {@code LDIFEntryWriter}.
243     */
244    public LDIFEntryWriter setIncludeFilter(final Matcher includeFilter) {
245        Reject.ifNull(includeFilter);
246        includeFilters.add(includeFilter);
247        return this;
248    }
249
250    /**
251     * Specifies the column at which long lines should be wrapped. A value less
252     * than or equal to zero (the default) indicates that no wrapping should be
253     * performed.
254     *
255     * @param wrapColumn
256     *            The column at which long lines should be wrapped.
257     * @return A reference to this {@code LDIFEntryWriter}.
258     */
259    public LDIFEntryWriter setWrapColumn(final int wrapColumn) {
260        this.wrapColumn = wrapColumn;
261        return this;
262    }
263
264    /** {@inheritDoc} */
265    @Override
266    public LDIFEntryWriter writeComment(final CharSequence comment) throws IOException {
267        writeComment0(comment);
268        return this;
269    }
270
271    /** {@inheritDoc} */
272    @Override
273    public LDIFEntryWriter writeEntry(final Entry entry) throws IOException {
274        Reject.ifNull(entry);
275
276        // Skip if branch containing the entry is excluded.
277        if (isBranchExcluded(entry.getName())) {
278            return this;
279        }
280
281        // Skip if the entry is excluded by any filters.
282        if (isEntryExcluded(entry)) {
283            return this;
284        }
285
286        writeKeyAndValue("dn", entry.getName().toString());
287        for (final Attribute attribute : entry.getAllAttributes()) {
288            // Filter the attribute if required.
289            if (isAttributeExcluded(attribute.getAttributeDescription())) {
290                continue;
291            }
292
293            final String attributeDescription = attribute.getAttributeDescriptionAsString();
294            if (attribute.isEmpty()) {
295                writeKeyAndValue(attributeDescription, ByteString.empty());
296            } else {
297                for (final ByteString value : attribute) {
298                    writeKeyAndValue(attributeDescription, value);
299                }
300            }
301        }
302
303        // Make sure there is a blank line after the entry.
304        impl.println();
305
306        return this;
307    }
308}