hrytsenko.gscripts.io.CsvFiles.java Source code

Java tutorial

Introduction

Here is the source code for hrytsenko.gscripts.io.CsvFiles.java

Source

/*
 * #%L
 * gscripts
 * %%
 * Copyright (C) 2015 Anton Hrytsenko
 * %%
 * Licensed 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.
 * #L%
 */
package hrytsenko.gscripts.io;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.input.BOMInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.dataformat.csv.CsvGenerator;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;

import hrytsenko.gscripts.AppException;
import hrytsenko.gscripts.util.NamedArgs;
import hrytsenko.gscripts.util.Records;

/**
 * Extensions for load and save of the CSV files.
 * 
 * <p>
 * Named arguments:
 * <dl>
 * <dt>{@link NamedArgs#PATH}</dt>
 * <dd>The path to file.</dd>
 * <dt>{@link #CHARSET}</dt>
 * <dd>The character set for file, default: UTF-8 ({@link #CHARSET_DEFAULT}).</dd>
 * <dt>{@link #SEPARATOR}</dt>
 * <dd>The separator of columns, default: comma ({@link #SEPARATOR_DEFAULT}).</dd>
 * <dt>{@link #QUALIFIER}</dt>
 * <dd>The qualifier for text, default: double-quote ({@link #QUALIFIER_DEFAULT}).</dd>
 * </dl>
 * 
 * @author hrytsenko.anton
 */
public final class CsvFiles {

    private static final Logger LOGGER = LoggerFactory.getLogger(CsvFiles.class);

    public static final String CHARSET = "charset";
    public static final Charset CHARSET_DEFAULT = StandardCharsets.UTF_8;

    public static final String QUALIFIER = "qualifier";
    public static final String QUALIFIER_DEFAULT = "\"";

    public static final String SEPARATOR = "separator";
    public static final String SEPARATOR_DEFAULT = ",";

    private CsvFiles() {
    }

    /**
     * Load records from file.
     * 
     * @param args
     *            the named arguments.
     * 
     * @return the list of text records.
     * 
     * @throws AppException
     *             if file could not be loaded.
     */
    public static List<Map<String, String>> loadCsv(Map<String, ?> args) {
        Path path = NamedArgs.findPath(args);
        LOGGER.info("Load {}.", path.getFileName());

        CsvSchema schema = schemaFrom(args).setUseHeader(true).build();

        try (InputStream dataStream = Files.newInputStream(path);
                InputStream bomStream = new BOMInputStream(dataStream);
                Reader dataReader = new InputStreamReader(bomStream, charsetFrom(args))) {
            CsvMapper mapper = new CsvMapper();
            ObjectReader reader = mapper.readerFor(Map.class).with(schema);
            return Lists.newArrayList(reader.readValues(dataReader));
        } catch (Exception exception) {
            throw new AppException(String.format("Could not load file %s.", path.getFileName()), exception);
        }
    }

    /**
     * Save records into file.
     * 
     * <p>
     * If file already exists, then it will be overridden.
     * 
     * @param records
     *            the list of records to save.
     * @param args
     *            the named arguments.
     * 
     * @throws IOException
     *             if file could not be saved.
     */
    public static void saveCsv(List<Map<String, ?>> records, Map<String, ?> args) {
        if (records.isEmpty()) {
            LOGGER.info("No records to save.");
            return;
        }

        Path path = NamedArgs.findPath(args);
        LOGGER.info("Save {}.", path.getFileName());

        CsvSchema.Builder csvSchema = schemaFrom(args).setUseHeader(true);
        Records.columns(records).forEach(csvSchema::addColumn);

        try (Writer writer = Files.newBufferedWriter(path, charsetFrom(args))) {
            CsvMapper csvMapper = new CsvMapper();
            csvMapper.configure(CsvGenerator.Feature.ALWAYS_QUOTE_STRINGS, true);

            csvMapper.writer().with(csvSchema.build()).writeValue(writer, Records.normalize(records));
        } catch (IOException exception) {
            throw new AppException(String.format("Could not save file %s.", path.getFileName()), exception);
        }
    }

    private static Charset charsetFrom(Map<String, ?> args) {
        return NamedArgs.tryFind(args, CHARSET).map(Charset::forName).orElse(CHARSET_DEFAULT);
    }

    private static CsvSchema.Builder schemaFrom(Map<String, ?> args) {
        String separator = NamedArgs.tryFind(args, SEPARATOR).orElse(SEPARATOR_DEFAULT);
        Preconditions.checkArgument(separator.length() == 1, "Invalid separator.");

        String qualifier = NamedArgs.tryFind(args, QUALIFIER).orElse(QUALIFIER_DEFAULT);
        Preconditions.checkArgument(qualifier.length() == 1, "Invalid qualifier.");

        return CsvSchema.builder().setColumnSeparator(separator.charAt(0)).setQuoteChar(qualifier.charAt(0));
    }

}