/* * Sun Public License * * The contents of this file are subject to the Sun Public License Version * 1.0 (the "License"). You may not use this file except in compliance with * the License. A copy of the License is available at http://www.sun.com/ * * The Original Code is the SLAMD Distributed Load Generation Engine. * The Initial Developer of the Original Code is Neil A. Wilson. * Portions created by Neil A. Wilson are Copyright (C) 2004-2010. * Some preexisting portions Copyright (C) 2002-2006 Sun Microsystems, Inc. * All Rights Reserved. * * Contributor(s): Neil A. Wilson */ package com.slamd.tools.ldifstructure; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.text.ParseException; import java.util.ArrayList; import com.unboundid.util.Base64; /** * This class defines a utility for reading entries from an LDIF file. * * * @author Neil A. Wilson */ public class LDIFReader { // The buffered reader that will read lines from the file. private BufferedReader reader; // The line number of the last line read from the LDIF. private int lineNumber; /** * Creates a new LDIF reader that will read data from the specified file. * * @param fileName The name of the LDIF file from which to read the data. * * @throws IOException If a problem occurs while opening the LDIF file for * reading. */ public LDIFReader(String fileName) throws IOException { this(new BufferedReader(new FileReader(fileName))); } /** * Creates a new LDIF reader that will read data from provided input stream. * * @param inputStream The input stream from which to read the data. */ public LDIFReader(InputStream inputStream) { this(new BufferedReader(new InputStreamReader(inputStream))); } /** * Creates a new LDIF reader that will read data from the provided buffered * reader. * * @param reader The buffered reader from which to read the data. */ public LDIFReader(BufferedReader reader) { this.reader = reader; lineNumber = 0; } /** * Reads the next entry from the LDIF file. * * @return The next entry from the LDIF file, or <CODE>null</CODE> if there * is no more data to read. * * @throws IOException If a problem occurs while reading data from the file. * * @throws ParseException If a problem occurs while trying to parse an entry * from the file. */ public LDIFEntry nextEntry() throws IOException, ParseException { ArrayList<StringBuilder> entryLines = new ArrayList<StringBuilder>(); String line; // Read until we find the beginning of the next entry. while (true) { line = reader.readLine(); if (line == null) { return null; } lineNumber++; int length = line.length(); if (length == 0) { continue; } if (line.charAt(0) == '#') { // It's a comment. Ignore it. continue; } if (line.startsWith("version:")) { // It's the LDIF version. Ignore it. continue; } if (! line.startsWith("dn:")) { throw new ParseException("Invalid entry starting at line " + lineNumber + ": expected a DN line but got " + line, lineNumber); } entryLines.add(new StringBuilder(line)); break; } // Read the rest of the entry. char firstChar; while (true) { line = reader.readLine(); if (line == null) { break; } lineNumber++; if (line.length() == 0) { break; } firstChar = line.charAt(0); if (firstChar == '#') { continue; } else if (firstChar == ' ') { entryLines.get(entryLines.size()-1).append(line.substring(1)); } else { entryLines.add(new StringBuilder(line)); } } // Convert the data to an LDIF entry. StringBuilder dnLine = entryLines.get(0); dnLine.delete(0, 3); boolean isBase64 = false; while (((firstChar = dnLine.charAt(0)) == ' ') || (firstChar == ':')) { if (firstChar == ':') { isBase64 = true; } dnLine.deleteCharAt(0); } LDIFEntry entry; if (isBase64) { String dnStr = new String(Base64.decode(dnLine.toString()), "UTF-8").trim(); entry = new LDIFEntry(dnStr); } else { entry = new LDIFEntry(dnLine.toString().trim()); } attrLoop: for (int i=1; i < entryLines.size(); i++) { StringBuilder attrLine = entryLines.get(i); int colonPos = attrLine.indexOf(":"); if (colonPos <= 0) { throw new ParseException("Invalid attribute definition " + attrLine + " in entry ending at line " + lineNumber, lineNumber); } String attrName = attrLine.substring(0, colonPos); attrLine.delete(0, colonPos+1); if (attrLine.length() > 0) { isBase64 = false; while (((firstChar = attrLine.charAt(0)) == ' ') || (firstChar == ':')) { attrLine.deleteCharAt(0); if (attrLine.length() == 0) { continue attrLoop; } if (firstChar == ':') { isBase64 = true; } } if (isBase64) { String valueStr = new String(Base64.decode(attrLine.toString()), "UTF-8").trim(); entry.addAttribute(attrName, toLowerCase(attrName), valueStr); } else { entry.addAttribute(attrName, toLowerCase(attrName), attrLine.toString().trim()); } } } return entry; } /** * Closes this LDIF reader and the handle to the underlying file or input * stream. * * @throws IOException If a problem occurs while attempting to close this * reader. */ public void close() throws IOException { reader.close(); } /** * Retrieves an all-lowercase version of the provided string. This is much * faster than <CODE>String.toLowerCase()</CODE> for strings that contain only * ASCII characters. * * @param s The string to convert to lowercase. * * @return The lowercase representation of the provided string. */ public static String toLowerCase(String s) { int length = s.length(); StringBuilder buffer = new StringBuilder(length); for (int i=0; i < length; i++) { char c = s.charAt(i); if ((c & 0x7F) != c) { buffer.append(s.substring(i).toLowerCase()); return buffer.toString(); } switch (c) { case 'A': buffer.append('a'); break; case 'B': buffer.append('b'); break; case 'C': buffer.append('c'); break; case 'D': buffer.append('d'); break; case 'E': buffer.append('e'); break; case 'F': buffer.append('f'); break; case 'G': buffer.append('g'); break; case 'H': buffer.append('h'); break; case 'I': buffer.append('i'); break; case 'J': buffer.append('j'); break; case 'K': buffer.append('k'); break; case 'L': buffer.append('l'); break; case 'M': buffer.append('m'); break; case 'N': buffer.append('n'); break; case 'O': buffer.append('o'); break; case 'P': buffer.append('p'); break; case 'Q': buffer.append('q'); break; case 'R': buffer.append('r'); break; case 'S': buffer.append('s'); break; case 'T': buffer.append('t'); break; case 'U': buffer.append('u'); break; case 'V': buffer.append('v'); break; case 'W': buffer.append('w'); break; case 'X': buffer.append('x'); break; case 'Y': buffer.append('y'); break; case 'Z': buffer.append('z'); break; default: buffer.append(c); break; } } return buffer.toString(); } }