Package org.easybatch.flatfile.dsv

Source Code of org.easybatch.flatfile.dsv.DelimitedRecordMapper

/*
* The MIT License
*
*   Copyright (c) 2014, Mahmoud Ben Hassine (md.benhassine@gmail.com)
*
*   Permission is hereby granted, free of charge, to any person obtaining a copy
*   of this software and associated documentation files (the "Software"), to deal
*   in the Software without restriction, including without limitation the rights
*   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
*   copies of the Software, and to permit persons to whom the Software is
*   furnished to do so, subject to the following conditions:
*
*   The above copyright notice and this permission notice shall be included in
*   all copies or substantial portions of the Software.
*
*   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
*   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
*   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
*   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
*   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
*   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
*   THE SOFTWARE.
*/

package org.easybatch.flatfile.dsv;

import org.easybatch.core.api.Record;
import org.easybatch.core.api.RecordMapper;
import org.easybatch.core.mapper.ObjectMapper;
import org.easybatch.flatfile.FlatFileField;
import org.easybatch.flatfile.FlatFileRecord;
import org.easybatch.core.converter.TypeConverter;

import java.util.Arrays;
import java.util.List;

/**
* DSV to Object mapper implementation.
*
* @param <T> the target domain object type
*
* @author Mahmoud Ben Hassine (md.benhassine@gmail.com)
*/
public class DelimitedRecordMapper<T> implements RecordMapper<T> {

    /**
     * The default delimiter.
     */
    public static final String DEFAULT_DELIMITER = ",";

    /**
     * The default data qualifier.
     */
    public static final String DEFAULT_QUALIFIER = "";

    /**
     * Default whitespace trimming value.
     */
    public static final boolean DEFAULT_WHITESPACE_TRIMMING = true;

    /**
     * Fields delimiter.
     */
    private String delimiter;

    /**
     * Parameter to trim whitespaces.
     */
    private boolean trimWhitespaces;

    /**
     * Data qualifier.
     */
    private String qualifier;

    /**
     * The object mapper.
     */
    private ObjectMapper<T> objectMapper;

    /**
     * Total number of characters expected based on declared fields length.
     */
    private int recordExpectedLength;

    /**
     * Array of indexes of fields to retain.
     */
    private List<Integer> fieldsPositions;

    /**
     * private default constructor to initialize the mapper with default parameter values.
     */
    private DelimitedRecordMapper() {
        this.delimiter = DEFAULT_DELIMITER;
        this.qualifier = DEFAULT_QUALIFIER;
        this.trimWhitespaces = DEFAULT_WHITESPACE_TRIMMING;
    }

    /**
     * Constructs a default DelimitedRecordMapper instance. Column names and expected record size will be calculated from the header record.
     * and set to fields with the same name of the target object.
     * @param recordClass the target domain object class
     */
    public DelimitedRecordMapper(final Class<? extends T> recordClass) {
        this();
        objectMapper = new ObjectMapper<T>(recordClass);
    }

    /**
     * Constructs a DelimitedRecordMapper instance. Expected record size will be calculated from the header record.
     * @param recordClass the target domain object class
     * @param fieldsMapping a String array containing target type field names in the same order as in the delimited flat file.
     */
    public DelimitedRecordMapper(final Class<? extends T> recordClass, final String[] fieldsMapping) {
        this();
        recordExpectedLength = fieldsMapping.length;
        objectMapper = new ObjectMapper<T>(recordClass, fieldsMapping);
    }

    /**
     * Constructs a DelimitedRecordMapper instance. Column names and expected record size will be calculated from the header record.
     * @param recordClass the target domain object class
     * @param fieldsPositions array of indexes of fields to retain
     */
    public DelimitedRecordMapper(final Class<? extends T> recordClass, final Integer[] fieldsPositions) {
        this();
        this.fieldsPositions = Arrays.asList(fieldsPositions);
        objectMapper = new ObjectMapper<T>(recordClass);
    }

    /**
     * Constructs a DelimitedRecordMapper instance. Column names will be calculated from the header record.
     * @param recordClass the target domain object class
     * @param fieldsPositions array of indexes of fields to retain
     * @param recordExpectedLength record expected length
     */
    public DelimitedRecordMapper(final Class<? extends T> recordClass, final Integer[] fieldsPositions,
                                 final int recordExpectedLength) {
        this();
        this.fieldsPositions = Arrays.asList(fieldsPositions);
        this.recordExpectedLength = recordExpectedLength;
        objectMapper = new ObjectMapper<T>(recordClass);
    }

    /**
     * Constructs a DelimitedRecordMapper instance. Expected record size will be calculated from the header record.
     * @param recordClass the target domain object class
     * @param fieldsPositions array of indexes of fields to retain
     * @param fieldsMapping a String array representing fields name in the same order in the DSV flat file.
     */
    public DelimitedRecordMapper(final Class<? extends T> recordClass, final Integer[] fieldsPositions, final String[] fieldsMapping) {
        this();
        this.fieldsPositions = Arrays.asList(fieldsPositions);
        objectMapper = new ObjectMapper<T>(recordClass, fieldsMapping);
    }

    /**
     * Constructs a DelimitedRecordMapper instance.
     * @param recordClass the target domain object class
     * @param fieldsPositions array of indexes of fields to retain
     * @param fieldsMapping a String array representing fields name in the same order in the DSV flat file.
     * @param recordExpectedLength record expected length
     */
    public DelimitedRecordMapper(final Class<? extends T> recordClass, final Integer[] fieldsPositions,
                                 final String[] fieldsMapping, final int recordExpectedLength) {
        this();
        this.fieldsPositions = Arrays.asList(fieldsPositions);
        this.recordExpectedLength = recordExpectedLength;
        objectMapper = new ObjectMapper<T>(recordClass, fieldsMapping);
    }

    @Override
    public T mapRecord(final Record record) throws Exception {

        FlatFileRecord flatFileRecord = parseRecord(record);
        String[] fieldsContents = new String[flatFileRecord.getFlatFileFields().size()];
        for (FlatFileField flatFileField : flatFileRecord.getFlatFileFields()) {
            fieldsContents[flatFileField.getIndex()] = flatFileField.getRawContent();
        }
        return objectMapper.mapObject(fieldsContents);
    }

    public FlatFileRecord parseRecord(final Record record) throws Exception {

        String recordRawContent = (String) record.getRawContent();
        String[] tokens = recordRawContent.split(delimiter, -1);

        // convention over configuration : if expected record size is not specified, calculate it from the header record
        if (this.recordExpectedLength == 0) {
            this.recordExpectedLength = tokens.length;
        }

        // convention over configuration : if fields mapping is not specified, retrieve it from the header record (done only once)
        if (objectMapper.getHeadersMapping() == null) {
            String[] fieldsMapping = new String[tokens.length];
            for (int i = 0; i < tokens.length; i++) {
                String token = tokens[i];
                if (trimWhitespaces) {
                    token = token.trim();
                }
                final int qualifierLength = qualifier.length();
                if (qualifierLength > 0) {
                    token = token.substring(qualifierLength, token.length() - qualifierLength);
                }
                fieldsMapping[i] = token.toLowerCase();
            }

            String[] headersMapping;
            if (fieldsPositions != null) {
                headersMapping = new String[fieldsPositions.size()];
                int j = 0;
                for (int i = 0; i < fieldsMapping.length; i++) {
                    if (fieldsPositions.contains(i + 1)) {
                        headersMapping[j++] = fieldsMapping[i];
                    }
                }
            } else {
                headersMapping = fieldsMapping;
            }

            objectMapper.setHeadersMapping(headersMapping);
        }

        if (tokens.length != recordExpectedLength) {
            throw new Exception("record length (" + tokens.length + " fields) not equal to expected length of "
                    + recordExpectedLength + " fields");
        }

        if (qualifier.length() > 0) {
            for (String token : tokens) {
                if (!token.startsWith(qualifier) || !token.endsWith(qualifier)) {
                    throw new Exception("field [" + token + "] is not enclosed as expected with '" + qualifier + "'");
                }
            }
        }

        FlatFileRecord flatFileRecord = new FlatFileRecord(record.getNumber(), recordRawContent);
        int i = 0;
        int j = 0;
        for (String token : tokens) {
            if (trimWhitespaces) {
                token = token.trim();
            }
            final int qualifierLength = qualifier.length();
            if (qualifierLength > 0) {
                token = token.substring(qualifierLength, token.length() - qualifierLength);
            }
            if (fieldsPositions != null && !fieldsPositions.contains(++j)) {
                continue;
            }
            flatFileRecord.getFlatFileFields().add(new FlatFileField(i++, token));
        }

        return flatFileRecord;
    }

    /**
     * Set the delimiter to use.
     * @param delimiter the delimiter to use
     */
    public void setDelimiter(final String delimiter) {
        String prefix = "";
        //escape the "pipe" character used in regular expression of String.split method
        if ("|".equals(delimiter)) {
            prefix = "\\";
        }
        this.delimiter = prefix + delimiter;
    }

    /**
     * Trim white spaces when parsing the DSV record.
     * @param trimWhitespaces true if whitespaces should be trimmed
     */
    public void setTrimWhitespaces(final boolean trimWhitespaces) {
        this.trimWhitespaces = trimWhitespaces;
    }

    /**
     * Set the data qualifier to use.
     * @param qualifier the data qualifier to use.
     */
    public void setQualifier(final String qualifier) {
        this.qualifier = qualifier;
    }

    /**
     * Register a custom type converter.
     * @param type the target type
     * @param typeConverter the type converter to user
     */
    public void registerTypeConverter(final Class type, final TypeConverter typeConverter) {
        objectMapper.registerTypeConverter(type, typeConverter);
    }

}
TOP

Related Classes of org.easybatch.flatfile.dsv.DelimitedRecordMapper

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.