/* See LICENSE for licensing and NOTICE for copyright. */
package org.ldaptive.io;
import java.io.IOException;
import java.io.Writer;
import org.ldaptive.LdapAttribute;
import org.ldaptive.LdapEntry;
import org.ldaptive.LdapUtils;
import org.ldaptive.SearchReference;
import org.ldaptive.SearchResult;
/**
* Writes a {@link SearchResult} as LDIF to a {@link Writer}.
*
* @author Middleware Services
*/
public class LdifWriter implements SearchResultWriter
{
/** ASCII decimal value of nul. */
private static final int NUL_CHAR = 0;
/** ASCII decimal value of line feed. */
private static final int LF_CHAR = 10;
/** ASCII decimal value of carriage return. */
private static final int CR_CHAR = 13;
/** ASCII decimal value of space. */
private static final int SP_CHAR = 32;
/** ASCII decimal value of colon. */
private static final int COLON_CHAR = 58;
/** ASCII decimal value of left arrow. */
private static final int LA_CHAR = 60;
/** ASCII decimal value of highest character. */
private static final int MAX_ASCII_CHAR = 127;
/** Line separator. */
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
/** Writer to write to. */
private final Writer ldifWriter;
/**
* Creates a new ldif writer.
*
* @param writer to write LDIF to
*/
public LdifWriter(final Writer writer)
{
ldifWriter = writer;
}
/**
* Writes the supplied search result to the writer.
*
* @param result search result to write
*
* @throws IOException if an error occurs using the writer
*/
@Override
public void write(final SearchResult result)
throws IOException
{
ldifWriter.write(createLdif(result));
ldifWriter.flush();
}
/**
* Creates an LDIF using the supplied search result.
*
* @param result search result
*
* @return LDIF
*/
protected String createLdif(final SearchResult result)
{
// build string from results
final StringBuilder ldif = new StringBuilder();
if (result != null) {
for (LdapEntry le : result.getEntries()) {
ldif.append(createLdifEntry(le));
}
for (SearchReference sr : result.getReferences()) {
ldif.append(createSearchReference(sr));
}
}
return ldif.toString();
}
/**
* Creates an LDIF using the supplied ldap entry.
*
* @param entry ldap entry
*
* @return LDIF
*/
protected String createLdifEntry(final LdapEntry entry)
{
if (entry == null) {
return "";
}
final StringBuilder entryLdif = new StringBuilder();
final String dn = entry.getDn();
if (dn != null) {
if (encodeData(dn)) {
entryLdif.append("dn:: ").append(LdapUtils.base64Encode(dn)).append(LINE_SEPARATOR);
} else {
entryLdif.append("dn: ").append(dn).append(LINE_SEPARATOR);
}
}
for (LdapAttribute attr : entry.getAttributes()) {
final String attrName = attr.getName();
for (String attrValue : attr.getStringValues()) {
if (attr.isBinary()) {
entryLdif.append(attrName).append(":: ").append(attrValue).append(LINE_SEPARATOR);
} else if (encodeData(attrValue)) {
entryLdif.append(attrName).append(":: ").append(LdapUtils.base64Encode(attrValue)).append(LINE_SEPARATOR);
} else {
entryLdif.append(attrName).append(": ").append(attrValue).append(LINE_SEPARATOR);
}
}
}
if (entryLdif.length() > 0) {
entryLdif.append(LINE_SEPARATOR);
}
return entryLdif.toString();
}
/**
* Creates an LDIF using the supplied search reference.
*
* @param ref search reference
*
* @return LDIF
*/
protected String createSearchReference(final SearchReference ref)
{
if (ref == null) {
return "";
}
final StringBuilder refLdif = new StringBuilder();
for (String url : ref.getReferralUrls()) {
if (encodeData(url)) {
refLdif.append("ref:: ").append(LdapUtils.base64Encode(url)).append(LINE_SEPARATOR);
} else {
refLdif.append("ref: ").append(url).append(LINE_SEPARATOR);
}
}
if (refLdif.length() > 0) {
refLdif.append(LINE_SEPARATOR);
}
return refLdif.toString();
}
/**
* Determines whether the supplied data should be base64 encoded. See http://www.faqs.org/rfcs/rfc2849.html for more
* details.
*
* @param data to inspect
*
* @return whether the data should be base64 encoded
*/
private boolean encodeData(final String data)
{
boolean encode = false;
final char[] dataCharArray = data.toCharArray();
for (int i = 0; i < dataCharArray.length; i++) {
final int charInt = (int) dataCharArray[i];
// check for NUL
if (charInt == NUL_CHAR) {
encode = true;
// check for LF
} else if (charInt == LF_CHAR) {
encode = true;
// check for CR
} else if (charInt == CR_CHAR) {
encode = true;
// check for SP at beginning or end of string
} else if (charInt == SP_CHAR && (i == 0 || i == dataCharArray.length - 1)) {
encode = true;
// check for colon(:) at beginning of string
} else if (charInt == COLON_CHAR && i == 0) {
encode = true;
// check for left arrow(<) at beginning of string
} else if (charInt == LA_CHAR && i == 0) {
encode = true;
// check for any character above 127
} else if (charInt > MAX_ASCII_CHAR) {
encode = true;
}
}
return encode;
}
}