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