/* * Copyright [2014] [Christian Loehnert, krampenschiesser@gmail.com] * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.ks.idnadrev.expimp.xls.sheet; import com.google.common.primitives.Primitives; import de.ks.idnadrev.expimp.xls.ColumnProvider; import de.ks.idnadrev.expimp.xls.XlsxColumn; import de.ks.persistence.entity.IdentifyableEntity; import org.apache.poi.xssf.model.SharedStringsTable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.helpers.DefaultHandler; import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.*; import java.util.function.Consumer; public class ImportSheetHandler extends DefaultHandler { private static final Logger log = LoggerFactory.getLogger(ImportSheetHandler.class); public static final String CELL_TYPE_STRING = "s"; public static final String CELL_TYPE_INLINESTRING = "inlineStr"; public static final String CELL_TYPE_NUMBER = "n"; private static final String INLINESTRING_ELEMENT = "is"; private static final String VALUE_ELEMENT = "v"; private static final String CELL_ELEMENT = "c"; private static final String CELL_ATTRIBUTE_TYPE = "t"; private static final String CELL_ATTRIBUTE_ID = "r"; private static final String CELL_ATTRIBUTE_STYLE = "s"; public static final int COLUMN_DEF_ROW = 1; private final Class<?> clazz; private final SharedStringsTable sharedStringsTable; private final Consumer<List<ImportValue>> importCallback; private final List<XlsxColumn> columns; private ImportValueParser<?> nextValue; private CellId currentCell; private final Map<String, XlsxColumn> columnId2Converter = new HashMap<>(); private final List<ImportValue> currentValues; private XlsxColumn currentColumnDef; public ImportSheetHandler(Class<?> class2Import, SharedStringsTable sharedStringsTable, ColumnProvider columnProvider, Consumer<List<ImportValue>> importCallback) { this.clazz = class2Import; this.sharedStringsTable = sharedStringsTable; this.importCallback = importCallback; columns = columnProvider.getColumns(clazz); currentValues = new ArrayList<>(columns.size()); } public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { try { if (name.equals(CELL_ELEMENT)) { CellId lastCell = currentCell; currentCell = CellId.from(attributes.getValue(CELL_ATTRIBUTE_ID)); if (lastCell != null && lastCell.row < currentCell.row) { importCallback.accept(currentValues); currentValues.clear(); } String cellType = attributes.getValue(CELL_ATTRIBUTE_TYPE); log.trace("Cell {} has type {}", attributes.getValue(CELL_ATTRIBUTE_ID), cellType); if (currentCell.row == COLUMN_DEF_ROW) { nextValue = getNextValueForDefinition(cellType); } else if (cellType != null) { currentColumnDef = columnId2Converter.get(currentCell.col); if (currentColumnDef != null) { nextValue = getColumnParser(currentColumnDef, cellType); } else { nextValue = null; } } else { nextValue = null; } } else if (name.equals(VALUE_ELEMENT) || name.equals(INLINESTRING_ELEMENT)) { if (nextValue != null) { nextValue.beginRecording(); } } } catch (Exception e) { throw new SAXException(e); } } @Override public void endDocument() throws SAXException { try { importCallback.accept(currentValues); currentValues.clear(); } catch (Exception e) { throw new SAXException(e); } } public void endElement(String uri, String localName, String name) throws SAXException { try { if (name.equals(VALUE_ELEMENT) || name.equals(INLINESTRING_ELEMENT)) { if (nextValue != null) { nextValue.endRecording(); if (currentCell.row == COLUMN_DEF_ROW) { String columnName = (String) nextValue.getValue(); Optional<XlsxColumn> column = columns.stream()// .filter(c -> c.getIdentifier().toLowerCase(Locale.ROOT).equals(columnName.toLowerCase(Locale.ROOT)))// .findFirst(); if (column.isPresent()) { columnId2Converter.put(currentCell.col, column.get()); log.debug("Found column definition {} for column {}, pos={}", column.get(), columnName, currentCell); } else { log.warn("Could not find any column definition for {}, pos={}", columnName, currentCell); } } else { Object value = nextValue.getValue(); if (currentColumnDef == null) { throw new IllegalStateException("column def is null for cell" + currentCell); } currentValues.add(new ImportValue(currentColumnDef, value, currentCell)); log.trace("new value {} in cell {}", value, currentCell); } } } } catch (Exception e) { throw new SAXException(e); } } public void characters(char[] ch, int start, int length) throws SAXException { try { if (nextValue != null) { String val = String.valueOf(Arrays.copyOfRange(ch, start, start + length)); nextValue.append(val); } } catch (Exception e) { throw new SAXException(e); } } private ImportValueParser<?> getNextValueForDefinition(String cellType) { if (CELL_TYPE_INLINESTRING.equals(cellType)) { return new InlineStringValueParser(); } if (CELL_TYPE_STRING.equals(cellType)) { return new SharedStringValueParser(sharedStringsTable); } return null; } private ImportValueParser<?> getColumnParser(XlsxColumn columnDef, String cellType) { Class<?> fieldType = columnDef.getFieldType(); if (String.class.equals(fieldType)) { if (cellType.equals(CELL_TYPE_INLINESTRING)) { return new InlineStringValueParser(); } else if (cellType.equals(CELL_TYPE_STRING)) { return new SharedStringValueParser(sharedStringsTable); } } if (LocalDateTime.class.equals(fieldType)) { return new LocalDateTimeValueParser(); } if (LocalDate.class.equals(fieldType)) { return new LocalDateValueParser(); } if (Duration.class.equals(fieldType)) { return new DurationValueParser(); } Class<?> unwrap = Primitives.unwrap(fieldType); if (Double.class.isAssignableFrom(unwrap) || Float.class.isAssignableFrom(unwrap)) { return new DoubleValueParser(); } if (Long.class.isAssignableFrom(unwrap) || Integer.class.isAssignableFrom(unwrap)) { return new LongValueParser(); } if (IdentifyableEntity.class.isAssignableFrom(fieldType)) { if (cellType.equals(CELL_TYPE_INLINESTRING)) { return new InlineStringValueParser(); } else if (cellType.equals(CELL_TYPE_STRING)) { return new SharedStringValueParser(sharedStringsTable); } } log.warn("No value parser for celltype {} and coumndef {}", cellType, columnDef); return null; } //Print exceptions @Override public void warning(SAXParseException e) throws SAXException { log.error("Could not parse {}", currentCell, e); } @Override public void error(SAXParseException e) throws SAXException { log.error("Could not parse {}", currentCell, e); } @Override public void fatalError(SAXParseException e) throws SAXException { log.error("Could not parse {}", currentCell, e); } }