/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero 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
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.studio.io.data.internal;
import java.util.NoSuchElementException;
import com.rapidminer.core.io.data.DataSet;
import com.rapidminer.core.io.data.DataSetException;
import com.rapidminer.core.io.data.DataSetRow;
import com.rapidminer.operator.OperatorException;
import com.rapidminer.operator.nio.model.DataResultSet;
import com.rapidminer.studio.io.data.StartRowNotFoundException;
/**
* Abstract class to transform a {@link DataResultSet} into a {@link DataSet}. Subclasses must
* implement {@link #getDataRow()}.
*
* @author Nils Woehler, Gisa Schaefer
* @since 7.0.0
*/
public abstract class ResultSetAdapter implements DataSet {
/**
* A flag that indicates that the whole {@link DataResultSet} should be read.
*/
public static final int NO_END_ROW = -1;
/**
* A flag that indicates that no header row is defined.
*/
public static final int NO_HEADER_ROW = -1;
/**
* A flag that indicates that no end column is defined.
*/
public static final int NO_COLUMN_END_INDEX = -1;
private final DataResultSet resultSet;
private final int startRow;
private int maxEndRow;
/**
* Creates a new {@link ResultSetAdapter} which wraps a {@link DataResultSet} into a
* {@link DataSet}.
*
* @param resultSet
* the {@link DataResultSet} which should be wrapped into a {@link DataSet}
* @param startRow
* the row index of the actual data content (it must <strong>not</strong> include the
* header row index)
* @param maxEndRow
* the maximum row index to read or {@link #NO_END_ROW} in case the whole
* {@link DataResultSet} should be read
* @throws DataSetException
* in case the underlying {@link DataResultSet} could not be reset (e.g. because of
* file reading issues)
*/
public ResultSetAdapter(DataResultSet resultSet, int startRow, int maxEndRow) throws DataSetException {
this.resultSet = resultSet;
this.startRow = startRow;
this.maxEndRow = maxEndRow;
reset();
}
/**
* @return the {@link DataResultSet} which is wrapped by this class
*/
public DataResultSet getResultSet() {
return resultSet;
}
@Override
public DataSetRow nextRow() throws DataSetException {
if (!hasNext()) {
throw new NoSuchElementException("No more data available");
}
try {
resultSet.next(null);
return getDataRow();
} catch (OperatorException e) {
throw new DataSetException(e.getMessage(), e.getCause());
}
}
@Override
public boolean hasNext() {
if (maxEndRow > NO_END_ROW && (resultSet.getCurrentRow() >= maxEndRow || startRow > maxEndRow)) {
return false;
}
return resultSet.hasNext();
}
@Override
public int getCurrentRowIndex() {
if (resultSet.getCurrentRow() < startRow) {
return -1;
}
return resultSet.getCurrentRow() - startRow;
}
@Override
public void reset() throws DataSetException {
try {
resultSet.reset(null);
skipToDataStartRow();
} catch (OperatorException e) {
throw new DataSetException(e.getMessage(), e.getCause());
}
}
@Override
public int getNumberOfColumns() {
return resultSet.getNumberOfColumns();
}
@Override
public int getNumberOfRows() {
// even if the max end row is defined we cannot calculate the number of rows here as the
// DataResultSet might have less rows than maxEndRow available
return NO_END_ROW;
}
@Override
public void close() throws DataSetException {
try {
resultSet.close();
} catch (OperatorException e) {
throw new DataSetException(e.getMessage(), e.getCause());
}
}
/**
* @return the index of the data start row
*/
protected final int getDataStartRow() {
return startRow;
}
/**
* Skips the first non-data rows until the index returned by
* {@link #resultSet#getCurrentRowIndex()} is equal to the index right before the
* {@link #startRow}.
*
* @throws StartRowNotFoundException
* in case the start row index is behind the actual data content size
* @throws OperatorException
* in case of other {@link DataResultSet} errors (e.g. file reading errors)
*/
protected final void skipToDataStartRow() throws StartRowNotFoundException, OperatorException {
while (resultSet.getCurrentRow() < startRow - 1) {
if (!hasNext()) {
throw new StartRowNotFoundException();
}
resultSet.next(null);
}
}
/**
* @return the current row of the {@link DataResultSet} wrapped in a {@link DataSetRow}
*/
protected abstract DataSetRow getDataRow();
/**
* Sets the maximum end row
*
* @param maxEndRow
*/
protected void setMaxEndRow(int maxEndRow) {
this.maxEndRow = maxEndRow;
}
}