content: /*
   * 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;
      }
  
  }
  