de.speexx.csv.table.app.Application.java Source code

Java tutorial

Introduction

Here is the source code for de.speexx.csv.table.app.Application.java

Source

/* CSV query table to work with CSV files and SQL like statements.
 *
 * Copyright (C) 2016  Sascha Kohlmann
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package de.speexx.csv.table.app;

import com.beust.jcommander.JCommander;
import de.speexx.csv.table.CsvReader;
import de.speexx.csv.table.Entry;
import de.speexx.csv.table.EntryDescriptor;
import de.speexx.csv.table.Row;
import de.speexx.csv.table.RowReader;
import de.speexx.csv.table.Table;
import de.speexx.csv.table.TableBuilder;
import de.speexx.csv.table.TableException;
import de.speexx.csv.table.app.sql.FromInfo;
import de.speexx.csv.table.app.sql.SelectData;
import de.speexx.csv.table.app.sql.SelectQueryData;
import de.speexx.csv.table.metric.SimpleRowDataMetric;
import de.speexx.csv.table.metric.TypeIndentifyRowReaderDelegate;
import de.speexx.csv.table.transformer.TypeTransformer;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Application {

    private static final Logger LOG = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
    private static final String APPLICATION_NAME = "scq";

    public static void main(final String... args) {
        try {
            new Application().run(args);
        } catch (final Throwable t) {
            LOG.error("Unexpected end of application: {}", t.getMessage());
            final StackTraceElement[] st = t.getStackTrace();
            LOG.error("Location - class: {} - method: {} - line: {}", st[0].getClassName(), st[0].getMethodName(),
                    st[0].getLineNumber());
            System.exit(1);
        }
        System.exit(0);
    }

    void run(final String... args) throws Exception {
        final Configuration conf = new Configuration();
        final JCommander jc = new JCommander(conf);
        jc.setProgramName(APPLICATION_NAME);
        jc.parse(args);

        if (conf.isHelp()) {
            jc.usage();
            return;
        }

        final Optional<List<Table>> tables = loadTable(conf);
        final Optional<RowReader> rows = executeQuery(conf,
                tables.orElseThrow(() -> new TableException("No table available")));
        if (rows.isPresent()) {
            exportResult(conf, rows.get());
        }
    }

    Optional<List<Table>> loadTable(final Configuration conf) throws Exception {
        if (conf.isVerbose()) {
            LOG.info("Load table");
        }
        final long loadStart = System.currentTimeMillis();

        final SelectData selectData = conf.getQueryData();
        final SelectQueryData queryData = selectData.getQueryData();

        final List<Table> tables = new ArrayList<>();
        for (final FromInfo fromInfo : queryData.getFromInfo()) {

            final RowReader reader = createSourceReader(fromInfo);
            if (conf.isWithoutTypeDetections()) {
                final Table table = loadTableFromSource(fromInfo, reader);
                doVerboseLog(conf, "Load table tock {}ms", System.currentTimeMillis() - loadStart);
                tables.add(table);
            } else {
                final SimpleRowDataMetric metric = new SimpleRowDataMetric();
                final TypeIndentifyRowReaderDelegate delegationReader = new TypeIndentifyRowReaderDelegate(reader,
                        metric);

                final Table table = loadTableFromSource(fromInfo, delegationReader);
                doVerboseLog(conf, "Load table tock {}ms", System.currentTimeMillis() - loadStart);
                adjustTableColumns(conf, table, metric);
                tables.add(table);
            }
        }

        return Optional.of(tables);
    }

    RowReader createSourceReader(final FromInfo fromInfo) throws IOException {
        final String source = fromInfo.getOriginalFrom();
        return new CsvReader(source);
    }

    Table loadTableFromSource(final FromInfo fromInfo, final RowReader delegationReader) {
        final String adjusted = fromInfo.getAdjustedFrom();
        final TableBuilder tableBuilder = TableBuilder.of();
        return tableBuilder.addName(adjusted).addRowReader(delegationReader).build();
    }

    void adjustTableColumns(final Configuration conf, final Table table, final SimpleRowDataMetric metric) {
        assert Objects.nonNull(table) : "Table is null";
        assert Objects.nonNull(metric) : "Metric is null";

        if (conf.isVerbose()) {
            LOG.info("Adjust column types");
        }

        final List<EntryDescriptor> changeDescriptors = new ArrayList<>();
        table.getEntryDescriptors().forEach(desc -> {
            final String columnName = desc.getName();
            final Optional<EntryDescriptor.Type> mostSignificantType = metric
                    .getMostSignificantTypeForName(columnName);

            if (mostSignificantType.isPresent()) {
                final EntryDescriptor.Type type = mostSignificantType.get();
                if (type != EntryDescriptor.Type.STRING) { // From CSV there are only strings, so ignore that
                    doVerboseLog(conf, "    Change column  '{}' to type {}", columnName, type);
                    changeDescriptors.add(new EntryDescriptor() {
                        @Override
                        public EntryDescriptor.Type getType() {
                            return type;
                        }

                        @Override
                        public String getName() {
                            return columnName;
                        }
                    });
                }
            }
        });
        doVerboseLog(conf, "Adjusting column types starting");
        final long changeStart = System.currentTimeMillis();
        table.changeColumnTypes(changeDescriptors.toArray(new EntryDescriptor[changeDescriptors.size()]));

        doVerboseLog(conf, "Adjust columns tock {}ms", System.currentTimeMillis() - changeStart);
    }

    Optional<RowReader> executeQuery(final Configuration conf, final List<Table> tables) throws Exception {
        assert Objects.nonNull(conf) : "Configuration is null";
        assert Objects.nonNull(tables) : "No tables. Is null";

        // At this time only one table is supported. Might be improved later.
        if (!tables.isEmpty()) {
            final SelectQueryData queryData = conf.getQueryData().getQueryData();
            final String select = queryData.getAdjustedQuery().getQuery();
            final Table table = tables.get(0);
            final RowReader result = table.executeSql(select);
            return Optional.of(result);
        }
        return Optional.empty();
    }

    void exportResult(final Configuration conf, final RowReader rows) throws Exception {
        assert Objects.nonNull(rows) : "Rows are null";
        assert Objects.nonNull(conf) : "configuration is null";

        final CSVPrinter printer = createCsvPrinter(rows, conf);

        for (final Row row : rows) {
            final List<String> recordEntries = new ArrayList<>();
            for (final Entry entry : row) {
                final EntryDescriptor.Type type = entry.getDescriptor().getType();
                final TypeTransformer transformer = TypeTransformer.of(type, EntryDescriptor.Type.STRING);
                final Optional<String> opt = transformer.transform(entry.getValue());
                recordEntries.add(opt.orElseGet(() -> ""));
            }
            printer.printRecord(recordEntries);
        }
    }

    CSVPrinter createCsvPrinter(final RowReader rows, final Configuration conf) throws IOException {
        final List<EntryDescriptor> descriptors = rows.getEntryDescriptors();
        if (conf.isWithoutHeader()) {
            return CSVFormat.RFC4180.print(System.out);
        }
        final List<String> headers = descriptors.stream().map(desc -> desc.getName()).collect(Collectors.toList());
        return CSVFormat.RFC4180.withHeader(headers.toArray(new String[headers.size()])).print(System.out);
    }

    void doVerboseLog(final Configuration conf, final String message, final Object... values) {
        assert Objects.nonNull(conf);
        if (conf.isVerbose()) {
            LOG.info(message, values);
        }
    }
}