All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.timo_reymann.csv_parser.io.CsvWriter Maven / Gradle / Ivy

package com.github.timo_reymann.csv_parser.io;

import com.github.timo_reymann.csv_parser.meta.CsvMetaDataReader;
import com.github.timo_reymann.csv_parser.util.Converter;
import com.github.timo_reymann.csv_parser.util.Platform;
import lombok.Data;

import java.io.*;
import java.lang.reflect.Field;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;

/**
 * Write csv files
 *
 * @author Timo Reymann
 * @since 20.12.17
 */
@Data
public class CsvWriter implements AutoCloseable, Closeable, Flushable {
    /**
     * Append to file instead of replacing it completely
     */
    private final boolean append;

    /**
     * File has csv headers
     */
    private final boolean hasHeadings;

    /**
     * Class for bean
     */
    private Class clazz;

    /**
     * Meta data provider for bean
     */
    private CsvMetaDataReader csvMetaDataReader;

    /**
     * File to save
     */
    private File file;

    /**
     * Seperator for lines
     */
    private String seperator = Seperator.SEMICOLON;

    /**
     * File writer instance
     */
    private FileWriter fileWriter;

    /**
     * Buffered Writer to write to file
     */
    private BufferedWriter bufferedWriter;

    /**
     * Converter api
     */
    private final Converter converter = new Converter();

    /**
     * Create new csv writer
     *
     * @param clazz       Class for bean
     * @param file        File to write
     * @param append      Append to output
     * @param hasHeadings Has the file headings, if this is a new file headers are automatically generated
     * @param seperator   Seperator for csv file
     * @throws IOException Error opening file streams
     */
    public CsvWriter(Class clazz, File file, boolean append, boolean hasHeadings, String seperator) throws IOException {
        this.clazz = clazz;
        this.file = file;
        this.csvMetaDataReader = new CsvMetaDataReader<>(clazz);
        this.append = append;
        this.hasHeadings = hasHeadings;
        this.seperator = seperator;
        init();
    }

    /**
     * Map beans to string array, this method automatically decides to map by heading or by index
     *
     * @param bean Bean to map
     * @return String array with bean data mapped
     * @throws IllegalAccessException Error getting values from bean
     * @throws IOException            Error writing header
     */
    private String[] map(T bean) throws IllegalAccessException, IOException {
        if (hasHeadings) {
            return mapByHeading(bean);
        }
        return mapByIndex(bean);
    }

    /**
     * Map bean by index
     *
     * @param bean Bean
     * @return String array with data mapped according to specification over annotations
     * @throws IllegalAccessException Error getting values from bean
     */
    private String[] mapByIndex(T bean) throws IllegalAccessException {
        HashMap effectiveMapping = csvMetaDataReader.getEffectiveValueForColumnMapping();
        String[] data = new String[effectiveMapping.size()];
        for (Map.Entry entry : effectiveMapping.entrySet()) {
            data[(int) entry.getKey()] = formatValue(entry.getValue(), bean);
        }
        return data;
    }

    private String formatValue(Field field, Object obj) throws IllegalAccessException {
        Object value = field.get(obj);
        Class type = field.getType();
        String format = csvMetaDataReader.getCsvColumnForField(field).format();

        if (type.isAssignableFrom(LocalDateTime.class)) {
            return converter.formatLocalDateTime(format, (LocalDateTime) value);
        } else if (type.isAssignableFrom(LocalDate.class)) {
            return converter.formatLocalDate(format, (LocalDate) value);
        } else {
            return value == null ? "" : value.toString();
        }
    }

    /**
     * Join list with strings to one single string separated by specified separator
     *
     * @param data List with strings to join
     * @return String
     */
    private String joinBySeperator(List data) {
        return String.join(seperator, data);
    }

    /**
     * Write file header then headings are used for mapping and no file exists or file is empty
     *
     * @param headings Headings
     * @throws IOException Error writing header to file
     */
    private void writeFileHeader(List headings) throws IOException {
        if (file.length() > 0 || !hasHeadings)
            return;

        writeRawData(headings);
        bufferedWriter.flush();
    }

    /**
     * Write raw string array to file
     *
     * @param data Raw data
     * @throws IOException Error writing to file
     */
    private void writeRawData(List data) throws IOException {
        bufferedWriter.write(joinBySeperator(data) + Platform.getLineSeperator());
    }

    private String[] mapByHeading(T bean) throws IllegalAccessException, IOException {
        HashMap effectiveMapping = csvMetaDataReader.getEffectiveValueForColumnMapping();

        List list = new ArrayList<>();
        for (Map.Entry objectFieldEntry : effectiveMapping.entrySet()) {
            Object key = objectFieldEntry.getKey();
            list.add(key.toString());
        }
        writeFileHeader(list);

        String[] data = new String[effectiveMapping.size()];
        for (Map.Entry entry : effectiveMapping.entrySet()) {
            int index = list.indexOf(entry.getKey().toString());
            data[index] = formatValue(entry.getValue(), bean);
        }
        return data;
    }

    /**
     * Write only file headings to file without any data
     *
     * @throws IllegalAccessException Error getting fields of entity
     * @throws InstantiationException Error creating new instance of entity
     * @throws IOException            Error writing to file
     */
    public void writeFileHeading() throws IllegalAccessException, InstantiationException, IOException {
        if (isHasHeadings()) {
            mapByHeading(clazz.newInstance());
        }
    }

    /**
     * Write bean to file
     *
     * @param bean Bean to write
     * @throws IllegalAccessException Error getting value from object
     * @throws IOException            Error writing to file
     */
    public void writeLine(T bean) throws IllegalAccessException, IOException {
        writeRawData(Arrays.asList(map(bean)));
    }

    /**
     * Write beans to file
     *
     * @param beans Beans to write
     * @throws IllegalAccessException Error getting value from object
     * @throws IOException            Error writing to file
     */
    public void writeLine(List beans) throws IOException, IllegalAccessException {
        for (T bean : beans) {
            writeLine(bean);
        }
    }

    /**
     * Init {@link BufferedWriter} and {@link FileWriter}
     *
     * @throws IOException Error open file
     */
    private void init() throws IOException {
        fileWriter = new FileWriter(file, append);
        bufferedWriter = new BufferedWriter(fileWriter);
    }

    /**
     * Flush underlying  {@link BufferedWriter} and {@link FileWriter}
     *
     * @throws IOException Error flushing
     */
    @Override
    public void flush() throws IOException {
        if (fileWriter != null)
            fileWriter.flush();

        if (bufferedWriter != null)
            bufferedWriter.flush();
    }

    /**
     * Close underyling {@link BufferedWriter} and {@link FileWriter}
     *
     * @throws IOException Error close writers
     */
    @Override
    public void close() throws IOException {
        if (bufferedWriter != null)
            bufferedWriter.close();

        if (fileWriter != null)
            fileWriter.close();
    }

    public static class Builder {
        /**
         * Append to file instead of replacing it completely
         */
        private boolean append = true;

        /**
         * File has csv headers
         */
        private boolean hasHeadings = false;

        /**
         * Class for bean
         */
        private Class clazz;

        /**
         * File to save
         */
        private File file;

        /**
         * Seperator for lines
         */
        private String seperator = Seperator.SEMICOLON;

        /**
         * Set class of bean
         *
         * @param clazz Class object for bean
         * @return Current builder
         */
        public Builder forClass(Class clazz) {
            this.clazz = clazz;
            return this;
        }

        /**
         * Set file to read from
         *
         * @param file CSV file
         * @return Current builder
         */
        public Builder file(File file) {
            this.file = file;
            return this;
        }

        /**
         * Set seperator for csv reader, default it is set to ';'
         *
         * @param seperator Seperator
         * @return Current builder
         */
        public Builder seperatedBy(String seperator) {
            this.seperator = seperator;
            return this;
        }

        /**
         * File to read has first row with heading
         *
         * @return Current builder
         */
        public Builder hasHeading() {
            this.hasHeadings = true;
            return this;
        }

        public Builder noAppend() {
            this.append = false;
            return this;
        }

        public CsvWriter build() throws IOException {
            return new CsvWriter<>(clazz, file, append, hasHeadings, seperator);
        }

    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy