org.nuxeo.ecm.directory.DirectoryCSVLoader.java Source code

Java tutorial

Introduction

Here is the source code for org.nuxeo.ecm.directory.DirectoryCSVLoader.java

Source

/*
 * (C) Copyright 2016 Nuxeo SA (http://nuxeo.com/) and others.
 *
 * 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.
 *
 * Contributors:
 *     Florent Guillaume
 */
package org.nuxeo.ecm.directory;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.core.schema.types.Field;
import org.nuxeo.ecm.core.schema.types.Schema;
import org.nuxeo.ecm.core.schema.types.Type;
import org.nuxeo.ecm.core.schema.types.primitives.DateType;
import org.nuxeo.runtime.api.Framework;

/**
 * Helper to load data from a CSV file.
 * <p>
 * The actual consumer of rows is a parameter passed by the caller.
 *
 * @since 8.4
 */
public class DirectoryCSVLoader {

    private static final Log log = LogFactory.getLog(DirectoryCSVLoader.class);

    /**
     * The special CSV value ({@value}) used to denote that a {@code null} should be used for a value.
     */
    public static final String CSV_NULL_MARKER = "__NULL__";

    private DirectoryCSVLoader() {
    }

    /**
     * Loads the CSV data file based on the provided schema, and creates the corresponding entries using the provided
     * loader.
     *
     * @param dataFileName the file name containing CSV data
     * @param delimiter the CSV column separator
     * @param schema the data schema
     * @param loader the actual consumer of loaded rows
     * @since 8.4
     */
    public static void loadData(String dataFileName, char delimiter, Schema schema,
            Consumer<Map<String, Object>> loader) throws DirectoryException {
        try (InputStream in = getResource(dataFileName); //
                CSVParser csvParser = new CSVParser(new InputStreamReader(in, "UTF-8"),
                        CSVFormat.DEFAULT.withDelimiter(delimiter).withHeader())) {
            Map<String, Integer> header = csvParser.getHeaderMap();

            List<Field> fields = new ArrayList<>();
            for (String columnName : header.keySet()) {
                Field field = schema.getField(columnName.trim());
                if (field == null) {
                    throw new DirectoryException(
                            "Column not found: " + columnName + " in schema: " + schema.getName());
                }
                fields.add(field);
            }

            int lineno = 1; // header was first line
            for (CSVRecord record : csvParser) {
                lineno++;
                if (record.size() == 0 || record.size() == 1 && StringUtils.isBlank(record.get(0))) {
                    // NXP-2538: allow columns with only one value but skip empty lines
                    continue;
                }
                if (!record.isConsistent()) {
                    log.error("Invalid column count while reading CSV file: " + dataFileName + ", line: " + lineno
                            + ", values: " + record);
                    continue;
                }

                Map<String, Object> map = new HashMap<String, Object>();
                for (int i = 0; i < header.size(); i++) {
                    Field field = fields.get(i);
                    String value = record.get(i);
                    Object v = CSV_NULL_MARKER.equals(value) ? null : decode(field, value);
                    map.put(field.getName().getPrefixedName(), v);
                }
                loader.accept(map);
            }
        } catch (IOException e) {
            throw new DirectoryException("Read error while reading data file: " + dataFileName, e);
        }
    }

    protected static Object decode(Field field, String value) {
        Type type = field.getType();
        if (type instanceof DateType) {
            // compat with earlier code, interpret in the local timezone and not UTC
            Calendar cal = new GregorianCalendar();
            cal.setTime(Timestamp.valueOf(value));
            return cal;
        } else {
            return type.decode(value);
        }
    }

    @SuppressWarnings("resource")
    protected static InputStream getResource(String name) {
        InputStream in = DirectoryCSVLoader.class.getClassLoader().getResourceAsStream(name);
        if (in == null) {
            in = Framework.getResourceLoader().getResourceAsStream(name);
            if (in == null) {
                throw new DirectoryException("Data file not found: " + name);
            }
        }
        return in;
    }

}