/* XXL: The eXtensible and fleXible Library for data processing Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger Head of the Database Research Group Department of Mathematics and Computer Science University of Marburg Germany This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see <http://www.gnu.org/licenses/>. http://code.google.com/p/xxl/ */ package xxl.core.io.converters; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; /** * This class provides a converter that is able to read and write * <code>String</code> objects as streams of <tt>ASCII</tt> characters (the * <code>byte</code> values representing the <tt>ASCII</tt> characters). In * addition to the read and write methods that read or write * <code>String</code> objects (these methods get <code>Object</code>s and cast * them to <code>String</code> objects) this class contains * <code>readAsciiString</code> and <code>writeAsciiString</code> methods that * gets directly <code>String</code> objects. * * <p><b>Note:</b> Streams of <tt>ASCII</tt> characters (representing * <code>String</code> objects) are separated by special delimiters. Those * delimiters are sequences of one or more <tt>ASCII</tt> characters, that can * be specified by the user. When a stream (especially the last of a data * input) is not delimited by one of the specified delimiters, it cannot be * read by this converter.</p> * * <p>Example usage (1). * <code><pre> * // create a new AsciiStringConverter that uses "\r\n" (cariage return, line feed), * // "\t" (horizontal tab) and " " (white space) as delimiters * * AsciiStringConverter converter = new AsciiStringConverter( * new String[] { * "\r\n", * "\t", * " " * } * ); * * // create a byte array output stream * * ByteArrayOutputStream output = new ByteArrayOutputStream(); * * // write some strings (delimited by "\r\n" per default) to the output stream * * converter.write( * new DataOutputStream(output), * "Far out in the uncharted backwaters of the unfashionable end of the western spiral * arm of the Galaxy lies a small unregarded yellow sun." * ); * converter.write( * new DataOutputStream(output), * "Orbiting this at a distance of roughly ninety-two million miles is an utterly * insignificant little blue green planet whose ape-descended life forms are so * amazingly primitive that they still think digital watches are a pretty neat idea." * ); * * // create a byte array input stream on the output stream * * ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray()); * * // read strings from the input stream and print them as long as the stream contains data * * while (input.available() > 0) * System.out.println(converter.read(new DataInputStream(input))); * * // close the streams after use * * input.close(); * output.close(); * </pre></code></p> * * @see DataInput * @see DataOutput * @see IOException */ public class AsciiStringConverter extends Converter<String> { /** * The converter that is used for converting single <code>Character</code> * objects into <tt>ASCII</tt> characters and vice versa. */ protected CharacterConverter converter; /** * An array of delimiters that can be used to separate the converted * <code>String</code> objects. */ protected String[] delimiters; /** * Constructs a new ASCII string converter that uses the given converter * for converting single <code>Character</code> objects into <tt>ASCII</tt> * characters and vice versa. The specified array contains the delimiters * that can be used to separate the converted <code>String</code> objects. * * @param converter the converter that is used for converting single * <code>Character</code> objects into <tt>ASCII</tt> characters and * vice versa. * @param delimiters an array of delimiters that can be used to separate * the converted <code>String</code> objects. * @throws IllegalArgumentException if no delimiters are specified. */ public AsciiStringConverter(CharacterConverter converter, String... delimiters) { this.converter = converter; if (delimiters.length < 1) throw new IllegalArgumentException("at least one delimiter must be specified!"); this.delimiters = delimiters; } /** * Constructs a new ASCII string converter that uses a default converter * for converting single <code>Character</code> objects into <tt>ASCII</tt> * characters and vice versa. The specified array contains the delimiters * that can be used to separate the converted <code>String</code> objects. * * <p>This constructor is equivalent to the call of * <code>new AsciiStringConverter(AsciiConverter.DEFAULT_INSTANCE, delimiters)</code>.</p> * * @param delimiters an array of delimiters that can be used to separate * the converted <code>String</code> objects. * @throws IllegalArgumentException if no delimiters are specified. */ public AsciiStringConverter(String... delimiters) { this(AsciiConverter.DEFAULT_INSTANCE, delimiters); } /** * Constructs a new ASCII string converter that uses the given converter * for converting single <code>Character</code> objects into <tt>ASCII</tt> * characters and vice versa. <i>Cariage return</i> and <i>line feed</i> * are used as delimiters. * * <p>This constructor is equivalent to the call of * <code>new AsciiStringConverter(converter, new String[] {"\r", "\n"})</code>.</p> * * @param converter the converter that is used for converting single * <tt>Character</tt> objects into <tt>ASCII</tt> characters * and vice versa. */ public AsciiStringConverter(CharacterConverter converter) { this(converter, new String[] {"\r", "\n"}); } /** * Constructs a new ASCII string converter that uses a default converter * for converting single <code>Character</code> objects into <tt>ASCII</tt> * characters and vice versa. <i>Cariage return</i> and <i>line feed</i> * are used as delimiters. * * <p>This constructor is equivalent to the call of * <code>new AsciiStringConverter(AsciiConverter.DEFAULT_INSTANCE, new String [] {"\r", "\n"})</code>.</p> */ public AsciiStringConverter() { this(AsciiConverter.DEFAULT_INSTANCE); } /** * Reads in a string of <tt>ASCII</tt> characters and returns the restored * (<code>String</code>) object, when a delimiter is found. When a string * (especially the last) is not delimited by one of the specified * delimiters, this method will expect more data and therefore an * <code>EOFException</code> will be thrown. * * <p>This implementation ignores the specified object, so it does not * matter when the specified object is <code>null</code>.</p> * * @param dataInput the stream to read the <tt>ASCII</tt> characters * representing a string from in order to return a * <code>String</code> object. * @param object the (<code>String</code>) object to be restored. In this * implementation it is ignored. * @return the read <code>String</code> object. * @throws IOException if I/O errors occur. */ @Override public String read(DataInput dataInput, String object) throws IOException { StringBuffer buffer = new StringBuffer(); int index = -1; do { buffer.append(converter.readChar(dataInput)); } while ((index = endsWithDelimiter(buffer.toString())) == -1); buffer.setLength(buffer.length() - delimiters[index].length()); return buffer.toString(); } /** * Reads in a string of <tt>ASCII</tt> characters and returns the restored * (<code>String</code>) object, when a delimiter is found. * * <p>This implementation calls the read method and casts its result to * <code>String</code>.</p> * * @param dataInput the stream to read the <tt>ASCII</tt> characters * representing a string from in order to return a * <code>String</code> object. * @return the read <code>String</code> object. * @throws IOException if I/O errors occur. */ public String readAsciiString(DataInput dataInput) throws IOException { return read(dataInput); } /** * Writes the specified <code>String</code> object to the specified data * output by writing a string of <tt>ASCII</tt> characters to it. The * string is delimited by the delimiter the given index points to. * * @param dataOutput the stream to write a string of <tt>ASCII</tt> * characters representing the specified <code>String</code> object * to. * @param object the <code>String</code> object that should be written to * the data output. * @param index the index of the delimiter that should be used to * delimit the converted string. * @throws IOException includes any I/O exceptions that may occur. */ public void write(DataOutput dataOutput, String object, int index) throws IOException { for (int i = 0; i < object.length(); i++) converter.writeChar(dataOutput, object.charAt(i)); for (int i = 0; i < delimiters[index].length(); i++) converter.writeChar(dataOutput, delimiters[index].charAt(i)); } /** * Writes the specified <code>String</code> object to the specified data * output by writing a string of <tt>ASCII</tt> characters to it. * * <p>This implementation calls the write method with the given data * output, the specified object and index <code>0</code>. That means, the * first delimiter of the array will be used to delimit the string per * default.</p> * * @param dataOutput the stream to write a string of <tt>ASCII</tt> * characters representing the specified <code>String</code> object * to. * @param object the <code>String</code> object that should be written to * the data output. * @throws IOException includes any I/O exceptions that may occur. */ @Override public void write(DataOutput dataOutput, String object) throws IOException { write(dataOutput, object, 0); } /** * Writes the specified <code>String</code> object to the specified data * output by writing a string of <tt>ASCII</tt> characters to it. * * <p>This implementation calls the write method with the given data * output, the specified object and the given index.</p> * * @param dataOutput the stream to write a string of <tt>ASCII</tt> * characters representing the specified <code>String</code> object * to. * @param object the <code>String</code> object that should be written to * the data output. * @param index the index of the delimiter that should be used to * delimit the converted string. * @throws IOException includes any I/O exceptions that may occur. */ public void writeAsciiString(DataOutput dataOutput, String object, int index) throws IOException { write(dataOutput, object, index); } /** * Writes the specified <code>String</code> object to the specified data * output by writing a string of <tt>ASCII</tt> characters to it. * * <p>This implementation calls the write method with the given data * output, the specified object and index <code>0</code>. That means, the * first delimiter of the array will be used to delimit the string per * default.</p> * * @param dataOutput the stream to write a string of <tt>ASCII</tt> * characters representing the specified <code>String</code> object * to. * @param object the <code>String</code> object that should be written to * the data output. * @throws IOException includes any I/O exceptions that may occur. */ public void writeAsciiString(DataOutput dataOutput, String object) throws IOException { write(dataOutput, object, 0); } /** * Determines whether the specified <code>String</code> object ends with * one of the delimiters stored in the array. The index of the delimiter in * the array is returned, if the string is delimited by it, else * <code>-1</code> is returned. * * @param buffer the string that should be tested whether it ends with one * of the specified delimiters. * @return the index of the delimiter in the array, if the string is * delimited by it, else <code>-1</code>. */ protected int endsWithDelimiter(String buffer) { for (int i = 0; i < delimiters.length; i++) if (buffer.endsWith(delimiters[i])) return i; return -1; } }