class ExcelSheetParser implements SheetParser {

    private final Sheet sheet;
    private final boolean withFirstRecordAsHeader;
    private final Map<String, Integer> headerMap = new LinkedHashMap<>();
    private ExcelIterator iterator;

    private int rowsFirstIndex = Integer.MAX_VALUE;
    private int rowsLastIndex = Integer.MIN_VALUE;

    public ExcelSheetParser(Sheet sheet, boolean withFirstRecordAsHeader) {
        this.sheet = sheet;
        this.withFirstRecordAsHeader = withFirstRecordAsHeader;

        calculateRowStartAndEndIndexes();
        if (withFirstRecordAsHeader) {
            initHeaderInfo();
        }
    }

    private void calculateRowStartAndEndIndexes() {<FILL_FUNCTION_BODY>}

    private void initHeaderInfo() {
        Row firstRow = sheet.getRow(sheet.getFirstRowNum());
        int zeroBasedIndex = 0;
        for (int i = rowsFirstIndex; i <= rowsLastIndex; i++) {
            Cell cell = firstRow.getCell(i);
            String header = ExcelSheetRow.getRowCellAsString(cell, i);
            if (header != null && !header.trim().isEmpty()) {
                headerMap.put(header.trim(), zeroBasedIndex);
            }
            zeroBasedIndex++;
        }
    }

    @Override
    public Map<String, Integer> getHeaderMap() {
        return new LinkedHashMap<>(headerMap);
    }

    @Override
    public long getCurrentRecordNumber() {
        return iterator != null ? iterator.getRowNum() : 0;
    }

    @Override
    public Iterator<SheetRow> iterator() {
        return iterator = new ExcelIterator();
    }

    @Override
    public void close() throws IOException {
        iterator = null;
        sheet.getWorkbook().close();
    }

    private class ExcelIterator implements Iterator<SheetRow> {

        private final Iterator<Row> iterator;
        private Row currentRow = null;

        public ExcelIterator() {
            iterator = sheet.iterator();
            if (withFirstRecordAsHeader && iterator.hasNext()) {
                iterator.next();//Skip headers row
            }
        }

        @Override
        public boolean hasNext() {
            try {
                return iterator.hasNext();
            } catch (Exception e) {
                Logger.getLogger("").severe(e.getMessage());
                return false;//In case of malformed excel
            }
        }

        @Override
        public SheetRow next() {
            currentRow = iterator.next();
            return new ExcelSheetRow(currentRow, rowsFirstIndex, rowsLastIndex);
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }

        private long getRowNum() {
            if (currentRow != null) {
                return currentRow.getRowNum();
            } else {
                return 0;
            }
        }
    }
}

        int rowsToScan;

        if (withFirstRecordAsHeader) {
            rowsToScan = 1;
        } else {
            rowsToScan = 25;
        }

        for (int i = sheet.getFirstRowNum(), j = 0; i < sheet.getLastRowNum() && j < rowsToScan; i++, j++) {
            Row row = sheet.getRow(i);
            if (row != null) {
                rowsFirstIndex = Math.min(rowsFirstIndex, row.getFirstCellNum());
                rowsLastIndex = Math.max(rowsLastIndex, row.getLastCellNum() - 1);
            }
        }

        if (rowsFirstIndex == Integer.MAX_VALUE) {
            rowsFirstIndex = 0;
            rowsLastIndex = 0;
        }
    
<no_super_class>