package com.openMap1.mapper.util;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;
import org.w3c.dom.Element;
import com.openMap1.mapper.core.MapperException;
/**
* Class to read an Excel workbook from its XML form
*
* @author RPW
*
*/
public class ExcelReader {
// XML root of the workbook
private Element root;
// key = sheet name; value = root <table> element of sheet
private Hashtable<String,Element> sheetRoots;
// key = sheet name; value = Vector of column names, taken from the first row
private Hashtable<String,Vector<String>> columnNames;
// key = sheet name; value = Integer number of rows, including the header row if there is one
private Hashtable<String,Integer> sheetRowCount;
//-----------------------------------------------------------------------------------------------
// constructor and initial checks
//-----------------------------------------------------------------------------------------------
public ExcelReader(Element root) throws MapperException
{
this.root = root;
initialise();
findSheets();
}
private void initialise()
{
sheetRoots = new Hashtable<String,Element>() ;
columnNames = new Hashtable<String,Vector<String>>() ;
sheetRowCount = new Hashtable<String,Integer>() ;
}
/**
* find information about the worksheets in the workbook (sheet names and column headers)
* and make some checks
* @throws MapperException
*/
private void findSheets() throws MapperException
{
if (!root.getLocalName().equals("Workbook")) throw new MapperException("Root element is not 'Workbook'");
Vector<Element> sheets = XMLUtil.namedChildElements(root, "Worksheet");
for (int s = 0; s < sheets.size(); s++)
{
Element sheet = sheets.get(s);
String sheetName = sheet.getAttribute("ss:Name");
Element table = XMLUtil.firstNamedChild(sheet, "Table");
if (table == null) throw new MapperException("Sheet '" + sheetName + "' has no Table element");
sheetRoots.put(sheetName, table);
Vector<Element> rows = XMLUtil.namedChildElements(table, "Row");
sheetRowCount.put(sheetName, new Integer(rows.size()));
Vector<String> headers = new Vector<String>();
if (rows.size() > 0)
{
Element headerRow = rows.get(0);
Vector<Element> cells = XMLUtil.namedChildElements(headerRow, "Cell");
for (int c = 0; c < cells.size();c++)
{
String header = "";
Element data = XMLUtil.firstNamedChild(cells.get(c), "Data");
if (data != null) header = XMLUtil.getText(data);
headers.add(header);
}
}
columnNames.put(sheetName, headers);
}
}
//-----------------------------------------------------------------------------------------------
// read operations
//-----------------------------------------------------------------------------------------------
/**
*
* @param sheetName
* @return true if the named worksheet exists
*/
public boolean hasWorkSheet(String sheetName) {return (sheetRoots.get(sheetName) != null);}
public List<String> getSheetNames()
{
Vector<String> names = new Vector<String>();
for (Enumeration<String> en = sheetRoots.keys(); en.hasMoreElements();) names.add(en.nextElement());
return names;
}
/**
*
* @param sheetName
* @param columnName
* @return true if the worksheet has the named column
* @throws MapperException
*/
public boolean hasColumn(String sheetName, String columnName) throws MapperException
{
if (!hasWorkSheet(sheetName)) throw new MapperException("No worksheet '" + sheetName + "'");
return (GenUtil.inVector(columnName, columnNames.get(sheetName)));
}
/**
*
* @param sheetName
* @return number of rows in the worksheet, including the header row
* @throws MapperException
*/
public int rowCount(String sheetName) throws MapperException
{
Integer count = sheetRowCount.get(sheetName);
if (count == null) throw new MapperException("No worksheet '" + sheetName + "'");
return count.intValue();
}
/**
*
* @param sheetName
* @param rowNumber
* @param colName
* @param mustHaveColumn
* @return the value of the cell, at the row of the sheet , with the column name
* @throws MapperException
*/
public String getValue(String sheetName, int rowNumber, String colName, boolean mustHaveColumn) throws MapperException
{
Element table = sheetRoots.get(sheetName);
if (table == null) throw new MapperException("No worksheet '" + sheetName + "'");
if ((rowNumber < 0) || (rowNumber > rowCount(sheetName) - 1))
throw new MapperException("Row number " + rowNumber + " is outside range 0.." + (rowCount(sheetName) - 1) );
if (!hasColumn(sheetName,colName))
{
if (mustHaveColumn) throw new MapperException("Worksheet '" + sheetName + "' has no column '" + colName);
// if it is allowed not to find the column, return an empty value
else return "";
}
Element row = XMLUtil.namedChildElements(table, "Row").get(rowNumber);
// find the column index c for this column name
Vector<String> colNames = columnNames.get(sheetName);
int col = -1;
for (int c = 0; c < colNames.size();c++) if (colNames.get(c).equals(colName)) col = c;
// loop over all columns for which there is data
String value = ""; // default
int colNumber = -1; // to get zero after one increment
Vector<Element> cells = XMLUtil.namedChildElements(row, "Cell");
for (int i = 0; i < cells.size(); i++)
{
Element cell = cells.get(i);
String cellContent = "";
Element dataEl = XMLUtil.firstNamedChild(cell, "Data");
if (dataEl != null) cellContent = XMLUtil.getText(dataEl); // some cells have no data
// find the column index 0..N that this cell contains
String index = cell.getAttribute("ss:Index");
if (index.length() > 0) colNumber = new Integer(index).intValue() - 1;
// if no index is given, column is next after previous column
else colNumber = colNumber + 1;
// take the cell content when column index matches
if (colNumber == col) value = cellContent;
}
return value;
}
}