package com.venky.swf.db.model.io.xls; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.Date; import java.sql.Timestamp; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.logging.Logger; import org.apache.poi.hssf.usermodel.HSSFDateUtil; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import com.venky.cache.Cache; import com.venky.core.collections.SequenceMap; import com.venky.core.collections.SequenceSet; import com.venky.core.string.StringUtil; import com.venky.core.util.ObjectUtil; import com.venky.swf.db.Database; import com.venky.swf.db.model.Model; import com.venky.swf.db.model.io.ModelReader; import com.venky.swf.db.model.reflection.ModelReflector; import com.venky.swf.exceptions.AccessDeniedException; import com.venky.swf.exceptions.IncompleteDataException; import com.venky.swf.sql.Conjunction; import com.venky.swf.sql.Expression; import com.venky.swf.sql.Operator; import com.venky.swf.sql.Select; public class XLSModelReader<M extends Model> extends XLSModelIO<M> implements ModelReader<M,Row>{ public XLSModelReader(Class<M> modelClass) { super(modelClass); } @Override public List<M> read(InputStream source) throws IOException{ return read(source,getBeanClass().getSimpleName()); } public List<M> read(Sheet sheet){ List<M> records = new ArrayList<M>(); if (sheet == null){ return records; } Iterator<Row> rowIterator = sheet.iterator(); Row header = rowIterator.hasNext() ? rowIterator.next() : null; if (header == null){ return records; } Map<String,Integer> headingIndexMap = headingIndexMap(sheet); while (rowIterator.hasNext()){ Row row = rowIterator.next(); M m = read(row,headingIndexMap); records.add(m); } return records; } public CellStyle getHeaderStyle(Sheet sheet){ return sheet.getRow(0).getCell(0).getCellStyle(); } public static Object getCellValue(Cell cell, Class<?> hint){ Object value = null; switch (cell.getCellType()) { case Cell.CELL_TYPE_NUMERIC: if (HSSFDateUtil.isCellDateFormatted(cell)) { value = cell.getDateCellValue(); } else { value = cell.getNumericCellValue(); } break; case Cell.CELL_TYPE_BOOLEAN: value = cell.getBooleanCellValue(); break; case Cell.CELL_TYPE_FORMULA: try { if (isDate(hint)) { value = cell.getDateCellValue(); } else if (isBoolean(hint)) { value = cell.getBooleanCellValue(); } else if (isNumeric(hint)) { value = cell.getNumericCellValue(); } else { value = cell.getStringCellValue(); } }catch (IllegalStateException ex){ try { value = cell.getStringCellValue(); }catch (IllegalStateException e){ throw new RuntimeException(cell.toString(), e); } } break; default: value = cell.getStringCellValue(); break; } if (value != null) { if (value instanceof String){ value = ((String)value).trim(); }else if (value instanceof java.util.Date){ if (Timestamp.class.isAssignableFrom(hint)){ value = new Timestamp(((java.util.Date)value).getTime()); }else { value = new Date(((java.util.Date)value).getTime()); } } } return value; } private Map<String,Integer> headingIndexMap(Sheet sheet){ Map<String,Integer> headingIndexMap = new SequenceMap<String, Integer>(); if (sheet == null || sheet.getLastRowNum() < 0){ return headingIndexMap; } Row header = sheet.getRow(0); for (int i = 0 ; i < header.getLastCellNum() ; i ++ ){ headingIndexMap.put(header.getCell(i).getStringCellValue(), i); } return headingIndexMap; } @Override public M read(Row source) { Map<String,Integer> headingIndexMap = headingIndexMap(source.getSheet()); return read(source , headingIndexMap); } private M read(Row source,Map<String, Integer> headingIndexMap){ M m = createInstance(); copyRowValuesToBean(m, source, headingIndexMap); return Database.getTable(getBeanClass()).getRefreshed(m); } protected void copyRowValuesToBean(M m, Row row, Map<String, Integer> headingIndexMap) { String[] heading = headingIndexMap.keySet().toArray(new String[]{}); ModelReflector<M> ref = getReflector(); SequenceSet<String> handledReferenceFields = new SequenceSet<String>(); for (int i = 0; i < heading.length; i++) { Method getter = getGetter(row,heading[i],headingIndexMap); if (getter == null) { continue; } GetterType type = GetterType.UNKNOWN_GETTER; String fieldName = null; if (ref.getFieldGetterMatcher().matches(getter)) { type = GetterType.FIELD_GETTER; fieldName = ref.getFieldName(getter); } else if (ref.getReferredModelGetterMatcher().matches(getter)) { type = GetterType.REFERENCE_MODEL_GETTER; fieldName = ref.getReferenceField(getter); } if (fieldName == null){ continue; } Method setter = null; if (ref.isFieldSettable(fieldName)){ setter = ref.getFieldSetter(fieldName); } if (setter == null) { continue; } Cell cell = row.getCell(i); Object value = null; if (cell == null) { continue; } if (type == GetterType.REFERENCE_MODEL_GETTER ) { if (handledReferenceFields.contains(fieldName)){ continue; } handledReferenceFields.add(fieldName); String baseFieldHeading = getter.getName().substring("get".length()); Class<? extends Model> referredModelClass = ref.getReferredModelClass(getter); ModelReflector<? extends Model> referredModelReflector = ModelReflector.instance(referredModelClass); SequenceSet<String> refModelFields = new SequenceSet<String>(); loadFieldsToExport(refModelFields, baseFieldHeading, referredModelReflector); Map<String,Cell> fieldValues = new HashMap<String, Cell>(); boolean referenceFieldsPassed = false; for (String field:refModelFields){ Integer headingIndex = headingIndexMap.get(field); if (headingIndex == null){ continue; } Cell cell1 = row.getCell(headingIndex); if (cell1 == null){ continue; } if (cell1.getCellType() == Cell.CELL_TYPE_STRING && ObjectUtil.isVoid(cell1.getStringCellValue())){ continue; } if (cell1.getCellType() == Cell.CELL_TYPE_BLANK){ continue; } referenceFieldsPassed = true; fieldValues.put(field.substring(field.indexOf('.')+1), cell1); } Model referredModel = null; if (referenceFieldsPassed){ referredModel = getModel(referredModelReflector,fieldValues); } if (referredModel == null && referenceFieldsPassed){ handleInvalidReference(m,row,fieldName, referredModelClass, fieldValues); }else if (referredModel != null){ value = referredModel.getId(); }else { value = null; } } else if (type == GetterType.FIELD_GETTER) { value = getCellValue(cell,getter.getReturnType()); } try { set(m,fieldName,value); } catch (Exception e) { throw new RuntimeException("Cannot set " + heading[i] + " as " + value + " of Class " + value.getClass().getName(), e); } } } protected void handleInvalidReference(M model, Row xlsRow, String fieldName, Class<? extends Model> referredModelClass, Map<String,Cell> fieldValues){ throw new RuntimeException(referredModelClass.getSimpleName() + " not found for passed information " + fieldValues.toString()); } protected void set(M m , String fieldName, Object value) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException{ getReflector().set(m, fieldName,value); } private Model getModel(ModelReflector<? extends Model> reflector, Map<String, Cell> headingValues) { Expression where = new Expression(reflector.getPool(),Conjunction.AND); Cache<String,Map<String,Cell>> newHeadingValues = new Cache<String, Map<String,Cell>>(){ /** * */ private static final long serialVersionUID = -1497598693844155855L; @Override protected Map<String, Cell> getValue(String k) { return new HashMap<String, Cell>(); } }; Iterator<String> headingIterator = headingValues.keySet().iterator(); while (headingIterator.hasNext()){ String heading = headingIterator.next(); int indexOfDot = heading.indexOf('.'); if (indexOfDot >= 0){ String referredFieldName = StringUtil.underscorize(heading.substring(0, indexOfDot) + "Id"); newHeadingValues.get(referredFieldName).put(heading.substring(indexOfDot+1), headingValues.get(heading)); }else { String fieldName = StringUtil.underscorize(heading); Object value = getCellValue(headingValues.get(heading),reflector.getFieldGetter(fieldName).getReturnType()); String columnName = reflector.getColumnDescriptor(fieldName).getName(); where.add(new Expression(reflector.getPool(),columnName,Operator.EQ,value)); } } for (String referenceFieldName: newHeadingValues.keySet()){ Class<? extends Model> referredModelClass = reflector.getReferredModelClass(reflector.getReferredModelGetterFor(reflector.getFieldGetter(referenceFieldName))); Model referred = getModel(ModelReflector.instance(referredModelClass), newHeadingValues.get(referenceFieldName)); if (referred != null){ where.add(new Expression(reflector.getPool(),reflector.getColumnDescriptor(referenceFieldName).getName(),Operator.EQ,referred.getId())); } } List<? extends Model> m = new Select().from(reflector.getModelClass()).where(where).execute(reflector.getModelClass()); if (m.size() == 1){ Model model = m.get(0); if (model.isAccessibleBy(Database.getInstance().getCurrentUser())){ return model; }else { throw new AccessDeniedException("A reference to " + reflector.getModelClass().getSimpleName() + " identified by " + reflector.get(model, reflector.getDescriptionField()) + " cannot be made. "); } }else if (m.size() == 0){ return null; }else { Logger.getLogger(XLSModelReader.class.getName()).warning("Import Failed: " + reflector.getTableName() + " cannot be identified for " + where.getRealSQL() + " with heading Values " + headingValues.toString()); throw new IncompleteDataException("Import Failed: " + reflector.getModelClass().getSimpleName() + " data was incomplete while importing " + getBeanClass().getSimpleName() ); } } @Override public List<M> read(InputStream in, String rootElementName) throws IOException { Workbook book = null; try { book = new HSSFWorkbook(in); Sheet sheet = book.getSheet(StringUtil.pluralize(rootElementName)); return read(sheet); }finally { if (book != null){ book.close(); } } } }