BeanReaderImpl.java

/*
 * CSVeed (https://github.com/42BV/CSVeed)
 *
 * Copyright 2013-2023 CSVeed.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of The Apache Software License,
 * Version 2.0 which accompanies this distribution, and is available at
 * https://www.apache.org/licenses/LICENSE-2.0.txt
 */
package org.csveed.bean;

import java.io.Reader;
import java.util.ArrayList;
import java.util.List;

import org.csveed.api.Header;
import org.csveed.api.Row;
import org.csveed.report.CsvException;
import org.csveed.report.GeneralError;
import org.csveed.row.RowReader;
import org.csveed.row.RowReaderImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The Class BeanReaderImpl.
 *
 * @param <T>
 *            the generic type
 */
public class BeanReaderImpl<T> implements BeanReader<T> {

    /** The Constant logger. */
    private static final Logger logger = LoggerFactory.getLogger(BeanReaderImpl.class);

    /** The row reader. */
    private final RowReader rowReader;

    /** The bean instructions. */
    private final BeanInstructions beanInstructions;

    /** The mapper. */
    private AbstractMapper<T> mapper;

    /** The current dynamic column. */
    private final DynamicColumn currentDynamicColumn;

    /** The unmapped row. */
    private Row unmappedRow;

    /**
     * Instantiates a new bean reader impl.
     *
     * @param reader
     *            the reader
     * @param beanClass
     *            the bean class
     */
    public BeanReaderImpl(Reader reader, Class<T> beanClass) {
        this(reader, new BeanParser().getBeanInstructions(beanClass));
    }

    /**
     * Instantiates a new bean reader impl.
     *
     * @param reader
     *            the reader
     * @param beanInstructions
     *            the bean instructions
     */
    public BeanReaderImpl(Reader reader, BeanInstructions beanInstructions) {
        this.beanInstructions = beanInstructions;
        this.rowReader = new RowReaderImpl(reader, this.beanInstructions.getRowInstructions());
        this.currentDynamicColumn = new DynamicColumn(this.beanInstructions.getStartIndexDynamicColumns());
    }

    /**
     * Gets the mapper.
     *
     * @return the mapper
     */
    public AbstractMapper<T> getMapper() {
        if (this.mapper == null) {
            this.mapper = this.createMappingStrategy();
            mapper.setBeanInstructions(this.beanInstructions);
        }
        return mapper;
    }

    @Override
    public List<T> readBeans() {
        List<T> beans = new ArrayList<>();
        while (!isFinished()) {
            T bean = readBean();
            if (bean != null) {
                beans.add(bean);
            }
        }
        return beans;
    }

    @Override
    public T readBean() {
        logSettings();

        if (this.beanInstructions.useHeader()) {
            getMapper().verifyHeader(getHeader());
        }

        currentDynamicColumn.checkForReset(((RowReaderImpl) rowReader).getMaxNumberOfColumns());
        if (currentDynamicColumn.atFirstDynamicColumn()) {
            unmappedRow = rowReader.readRow();
        }
        if (unmappedRow == null) {
            return null;
        }

        T bean = getMapper().convert(instantiateBean(), unmappedRow, getCurrentLine(), currentDynamicColumn);
        currentDynamicColumn.advanceDynamicColumn();
        return bean;
    }

    /**
     * Log settings.
     */
    protected void logSettings() {
        this.beanInstructions.logSettings();
    }

    /**
     * Gets the header.
     *
     * @return the header
     */
    protected Header getHeader() {
        if (this.beanInstructions.useHeader()) {
            return rowReader.getHeader();
        }
        return null;
    }

    @Override
    public Header readHeader() {
        return rowReader.readHeader();
    }

    @Override
    public int getCurrentLine() {
        return this.rowReader.getCurrentLine();
    }

    @Override
    public boolean isFinished() {
        return rowReader.isFinished();
    }

    @Override
    public RowReader getRowReader() {
        return this.rowReader;
    }

    /**
     * Instantiate bean.
     *
     * @return the t
     */
    private T instantiateBean() {
        try {
            return this.getBeanClass().getDeclaredConstructor().newInstance();
        } catch (Exception err) {
            throw new CsvException(new GeneralError("Unable to instantiate the bean class "
                    + this.getBeanClass().getName() + ". Does it have a no-arg public constructor?"));
        }
    }

    /**
     * Gets the bean class.
     *
     * @return the bean class
     */
    @SuppressWarnings("unchecked")
    public Class<T> getBeanClass() {
        return this.beanInstructions.getBeanClass();
    }

    /**
     * Creates the mapping strategy.
     *
     * @return the abstract mapper
     */
    @SuppressWarnings("unchecked")
    public AbstractMapper<T> createMappingStrategy() {
        try {
            return this.beanInstructions.getMappingStrategy().getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            logger.trace("", e);
            throw new CsvException(new GeneralError("Unable to instantiate the mapping strategy"));
        }
    }

    @Override
    public BeanInstructions getBeanInstructions() {
        return this.beanInstructions;
    }

}