class ODFSpreadsheetReader implements SpreadsheetReader {

	public void read(InputStream inputStream, Range rowRange, Range columnRange, boolean ignoreBlankRows, SpreadsheetRowProcessor rowProcessor)
			throws SpreadsheetReadException {
		NodeList rowList = null;
		try {
			// Load the ODF document
			OdfDocument odfDoc = OdfDocument.loadDocument(inputStream);
			logger.debug("Reading document of type : " + odfDoc.getMediaType());
			// Get the content as DOM tree
			OdfFileDom odfContent = odfDoc.getContentDom();
			// Initialize XPath
			XPath xpath = odfDoc.getXPath();
			// Get the rows of the first table
			String rowsPath = ("//table:table[1]/table:table-row");
			rowList = (NodeList) xpath.evaluate(rowsPath, odfContent, XPathConstants.NODESET);
		} catch (Exception e) {
			if (e instanceof RuntimeException) {
				throw (RuntimeException) e;
			}
			throw new SpreadsheetReadException("The spreadsheet file could not be read", e);
		}

		if (rowRange.getEnd() < 0) {
			rowRange.setEnd(calculateRowCount(rowList) - 1);
		}

		SortedMap<Integer, String> currentDataRow = new TreeMap<Integer, String>();
		int rowRep = 0;
		for (int rowIndex = rowRange.getStart(); rowIndex <= rowRange.getEnd(); rowIndex++) {
			boolean blankRow = true;
			OdfTableRow row = (OdfTableRow) rowList.item(rowIndex);
			int columnRep = 0;
			for (int columnIndex = columnRange.getStart(); columnIndex <= columnRange.getEnd(); columnIndex++) {
				String value = null;
				OdfTableCell cell = null;
				if (row != null) {
					cell = (OdfTableCell) row.getCellAt(columnIndex);
					if (cell != null) {
						String type = cell.getOfficeValueTypeAttribute();
						if ("float".equals(type)) {
							value = cell.getOfficeValueAttribute().toString();
						} else if ("percentage".equals(type)) {
							value = cell.getOfficeValueAttribute().toString();
						} else if ("currency".equals(type)) {
							value = cell.getOfficeValueAttribute().toString();
						} else if ("date".equals(type)) {
							value = cell.getOfficeDateValueAttribute();
						} else if ("time".equals(type)) {
							value = cell.getOfficeTimeValueAttribute();
						} else if ("boolean".equals(type)) {
							value = cell.getOfficeBooleanValueAttribute().toString();
						} else if ("string".equals(type)) {
							value = cell.getOfficeStringValueAttribute();
							if (value == null) {
								value = cell.getTextContent();
							}
						} else {
							value = cell.getTextContent();
						}
					}
				}
				value = "".equals(value) ? null : value;
				if (value != null) {
					blankRow = false;
				}
				// if the cell is within the column range add it to the row values
				if (columnRange.contains(columnIndex + columnRep)) {
					currentDataRow.put(columnIndex + columnRep, value);
				}
				// check if this cell is repeated
				int repeatedCells = cell == null ? 0 : cell
						.getTableNumberColumnsRepeatedAttribute() - 1;
				while (repeatedCells > 0 && columnIndex + columnRep < columnRange.getEnd()) {
					columnRep++;
					if (columnRange.contains(columnIndex + columnRep)) {
						currentDataRow
								.put(columnIndex + columnRep, value);
					}
					repeatedCells--;
				}
				// if it's the last cell in the range process the row
				if (columnIndex == columnRange.getEnd()) {
					if (rowRange.contains(rowIndex + rowRep)) {
						if (!ignoreBlankRows || !blankRow) {
							rowProcessor.processRow(rowIndex + rowRep, currentDataRow);
						}
					}
					// check if this row is repeated
					int repeatedRows = row == null ? 0
							: row.getTableNumberRowsRepeatedAttribute() - 1;
					while (repeatedRows > 0 && rowIndex + rowRep < rowRange.getEnd()) {
						rowRep++;
						if (rowRange.contains(rowIndex + rowRep)) {
							if (!ignoreBlankRows || !blankRow) {
								rowProcessor.processRow(rowIndex + rowRep, currentDataRow);
							}
						}
						repeatedRows--;
					}
					currentDataRow = new TreeMap<Integer, String>();
				}

			}
		}

	}

}