Package pivot.serialization

Source Code of pivot.serialization.CSVSerializer$StreamIterator

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except in
* compliance with the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package pivot.serialization;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.NoSuchElementException;

import pivot.beans.BeanDictionary;
import pivot.collections.ArrayList;
import pivot.collections.Dictionary;
import pivot.collections.HashMap;
import pivot.collections.List;
import pivot.collections.Sequence;

/**
* Implementation of the {@link Serializer} interface that reads data from
* and writes data to a comma-separated value (CSV) file.
* <p>
* TODO Add "firstLineContainsKeys" flag.
*
* @author gbrown
*/
public class CSVSerializer implements Serializer<List<?>> {
    /**
     * Class representing the serializers key sequence.
     */
    public class KeySequence implements Sequence<String> {
        public int add(String item) {
            return keys.add(item);
        }

        public void insert(String item, int index) {
            keys.insert(item, index);
        }

        public String update(int index, String item) {
            return keys.update(index, item);
        }

        public int remove(String item) {
            return keys.remove(item);
        }

        public Sequence<String> remove(int index, int count) {
            return keys.remove(index, count);
        }

        public String get(int index) {
            return keys.get(index);
        }

        public int indexOf(String item) {
            return keys.indexOf(item);
        }

        public int getLength() {
            return keys.getLength();
        }
    }

    /**
     * Allows a caller to retrieve the contents of a CSV stream iteratively.
     *
     * @author gbrown
     */
    public class StreamIterator {
        private Reader reader;

        private StreamIterator(Reader reader) throws IOException {
            this.reader = reader;

            // Move to the first character
            c = reader.read();
        }

        public boolean hasNext() {
            return (c != -1);
        }

        public Object next() throws IOException, SerializationException {
            if (c == -1) {
                throw new NoSuchElementException();
            }

            Object item = readItem(reader);
            if (item != null) {
                // Move to next line
                while (c != -1
                    && (c == '\r' || c == '\n')) {
                    c = reader.read();
                }
            }

            return item;
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }
    }

    private Charset charset;

    private ArrayList<String> keys = new ArrayList<String>();
    private KeySequence keySequence = new KeySequence();

    public static final String MIME_TYPE = "text/csv";
    public static final int BUFFER_SIZE = 2048;

    int c = -1;
    private Class<?> itemClass = HashMap.class;

    public CSVSerializer() {
        this(Charset.defaultCharset());
    }

    public CSVSerializer(String charsetName) {
        this(charsetName == null ? Charset.defaultCharset() : Charset.forName(charsetName));
    }

    public CSVSerializer(Charset charset) {
        if (charset == null) {
            throw new IllegalArgumentException("charset is null.");
        }

        this.charset = charset;
    }

    /**
     * Returns a sequence representing the fields that will be read or written
     * by this serializer.
     */
    public KeySequence getKeys() {
        return keySequence;
    }

    /**
     * Returns the item class that will be instantiated by the serializer during
     * a read operation.
     */
    public Class<?> getItemClass() {
        return itemClass;
    }

    /**
     * Sets the item class that will be instantiated by the serializer during
     * a read operation. The class must implement the {@link Dictionary}
     * interface.
     */
    public void setItemClass(Class<?> itemClass) {
        if (itemClass == null) {
            throw new IllegalArgumentException("itemClass is null.");
        }

        this.itemClass = itemClass;
    }

    /**
     * Reads values from a comma-separated value stream.
     *
     * @param inputStream
     * The input stream from which data will be read.
     *
     * @see #readObject(Reader)
     */
    public List<?> readObject(InputStream inputStream)
        throws IOException, SerializationException {
        if (inputStream == null) {
            throw new IllegalArgumentException("inputStream is null.");
        }

        Reader reader = new BufferedReader(new InputStreamReader(inputStream, charset),
            BUFFER_SIZE);
        return readObject(reader);
    }

    /**
     * Reads values from a comma-separated value stream.
     *
     * @param reader
     * The reader from which data will be read.
     *
     * @return
     * A list containing the data read from the CSV file. The list items are
     * instances of Dictionary<String, Object> populated by mapping columns in
     * the CSV file to keys in the key sequence.
     */
    public List<?> readObject(Reader reader)
        throws IOException, SerializationException {
        if (reader == null) {
            throw new IllegalArgumentException("reader is null.");
        }

        ArrayList<Object> items = new ArrayList<Object>();

        // Move to the first character
        c = reader.read();

        while (c != -1) {
            Object item = readItem(reader);
            while (item != null) {
                items.add(item);

                // Move to next line
                while (c != -1
                    && (c == '\r' || c == '\n')) {
                    c = reader.read();
                }

                // Read the next item
                item = readItem(reader);
            }
        }

        return items;
    }

    /**
     * Reads values from a comma-separated value stream.
     *
     * @param inputStream
     * The input stream from which data will be read.
     *
     * @see #getStreamIterator(Reader)
     */
    public StreamIterator getStreamIterator(InputStream inputStream) throws IOException {
        Reader reader = new BufferedReader(new InputStreamReader(inputStream, charset),
            BUFFER_SIZE);
        return getStreamIterator(reader);
    }

    /**
     * Reads values from a comma-separated value stream.
     *
     * @param reader
     * The reader from which data will be read.
     *
     * @return
     * A stream iterator on the data read from the CSV file. The list items are
     * instances of Dictionary<String, Object> populated by mapping columns in
     * the CSV file to keys in the key sequence.
     */
    public StreamIterator getStreamIterator(Reader reader) throws IOException {
        return new StreamIterator(reader);
    }

    @SuppressWarnings({"unchecked"})
    private Object readItem(Reader reader)
        throws IOException, SerializationException {
        Object item = null;

        if (c != -1) {
            // Instantiate the item
            Dictionary<String, Object> itemDictionary;

            try {
                item = itemClass.newInstance();

                if (item instanceof Dictionary) {
                    itemDictionary = (Dictionary<String, Object>)item;
                } else {
                    itemDictionary = new BeanDictionary(item);
                }
            } catch(IllegalAccessException exception) {
                throw new SerializationException(exception);
            } catch(InstantiationException exception) {
                throw new SerializationException(exception);
            }

            // Add values to the item
            for (int i = 0, n = keys.getLength(); i < n; i++) {
                String key = keys.get(i);
                String value = readValue(reader);
                if (value == null) {
                    throw new SerializationException("Error reading value for "
                        + key + " from input stream.");
                }

                itemDictionary.put(key, value);
            }
        }

        return item;
    }

    private String readValue(Reader reader)
        throws IOException, SerializationException {
        String value = null;

        // Read the next value from this line, returning null if there are
        // no more values on the line
        if (c != -1
            && (c != '\r' && c != '\n')) {
            // Read the value
            StringBuilder valueBuilder = new StringBuilder();

            // Values may be bounded in quotes; the double-quote character is
            // escaped by two successive occurrences
            boolean quoted = (c == '"');
            if (quoted) {
                c = reader.read();
            }

            while (c != -1
                && (quoted || c != ',')
                && (c != '\r' && c != '\n')) {
                if (c == '"') {
                    c = reader.read();
                    quoted &= (c == '"');
                }

                if (quoted || c != ',') {
                    valueBuilder.append((char)c);
                    c = reader.read();
                }
            }

            if (quoted) {
                throw new SerializationException("Unterminated string.");
            }

            value = valueBuilder.toString();

            // Move to the next character after ','
            c = reader.read();
        }

        return value;
    }

    /**
     * Writes values to a comma-separated value stream.
     *
     * @param items
     *
     * @param outputStream
     * The output stream to which data will be written.
     *
     * @see #writeObject(List, Writer)
     */
    public void writeObject(List<?> items, OutputStream outputStream)
        throws IOException, SerializationException {
        if (items == null) {
            throw new IllegalArgumentException("items is null.");
        }

        if (outputStream == null) {
            throw new IllegalArgumentException("outputStream is null.");
        }

        Writer writer = new BufferedWriter(new OutputStreamWriter(outputStream, charset),
            BUFFER_SIZE);
        writeObject(items, writer);
    }

    /**
     * Writes values to a comma-separated value stream.
     *
     * @param items
     * A list containing the data to write to the CSV
     * file. List items must be instances of Dictionary<String, Object>. The
     * dictionary values will be written out in the order specified by the
     * key sequence.
     *
     * @param writer
     * The writer to which data will be written.
     */
    @SuppressWarnings({"unchecked"})
    public void writeObject(List<?> items, Writer writer)
        throws IOException, SerializationException {
        if (items == null) {
            throw new IllegalArgumentException("items is null.");
        }

        if (writer == null) {
            throw new IllegalArgumentException("writer is null.");
        }

        for (Object item : items) {
            Dictionary<String, Object> itemDictionary;
            if (item instanceof Dictionary) {
                itemDictionary = (Dictionary<String, Object>)item;
            } else {
                itemDictionary = new BeanDictionary(item);
            }

            for (int i = 0, n = keys.getLength(); i < n; i++) {
                String key = keys.get(i);

                if (i > 0) {
                    writer.append(",");
                }

                Object value = itemDictionary.get(key);
                writer.append(value.toString());
            }

            writer.append("\r\n");
        }

        writer.flush();
    }

    public String getMIMEType(List<?> objects) {
        return MIME_TYPE + "; charset=" + charset.name();
    }
}
TOP

Related Classes of pivot.serialization.CSVSerializer$StreamIterator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.