package gw.exportimport uses gw.api.locale.DisplayKey uses gw.api.util.LocaleUtil uses gw.exportimport.validation.ImportValidationResult uses gw.i18n.ILocale uses gw.lang.reflect.TypeSystem uses org.apache.poi.EmptyFileException uses org.apache.poi.POIXMLException uses org.apache.poi.ss.usermodel.Cell uses org.apache.poi.ss.usermodel.Row uses org.apache.poi.ss.usermodel.Sheet uses org.apache.poi.xssf.usermodel.XSSFWorkbook uses java.io.InputStream uses java.lang.Exception uses java.lang.Integer uses java.util.Set uses java.util.Map uses java.util.List uses gw.api.upgrade.Coercions /** * A {@link ImportData} for excel based data. */ @ReadOnly internal class ExcelDataImporter { var _registry : EntityInfoRegistry var _data : CellDataRow[] as readonly Data = {} var _metaData : ImportMetaData var _validationResult : ImportValidationResult = new ImportValidationResult() var _ignoredColumns : Set = {} construct() { this(XmlEntityInfoRegistry.Instance) } internal construct(registry : EntityInfoRegistry) { _registry = registry } /** * Imports the Excel data and converts it into a two-dimensional representation. * * @param inputStream The input stream of the Excel file * @return The converted ImportData */ function import(inputStream : InputStream) : ImportData { return import(inputStream, null) } /** * Imports the Excel data and converts it into a two-dimensional representation. * * @param inputStream The input stream of the Excel file * @param listener A RowProcessedListener listening to row actions * @return The converted ImportData */ function import(inputStream : InputStream, listener : RowProcessedListener) : ImportData { var workbook : XSSFWorkbook try { workbook = new XSSFWorkbook(inputStream) } catch (exception : POIXMLException) { return handleImportError() } catch (exception : EmptyFileException) { return handleImportError() } var entitySheet = workbook.getSheetAt(ExportImportConstants.ENTITY_SHEET) listener?.maxRowsCalculated(entitySheet.Count - 1) _metaData = readHeaders(workbook, entitySheet) if (_metaData.ValidationResult.hasErrors() ) { _metaData.ValidationResult.ErrorMessages.each(\ o -> print(o)) return new DefaultImportData(_metaData) } if (_metaData.Template) { checkForTemplateExcludedColumns() if (_metaData.ValidationResult.hasErrors() ) { return new DefaultImportData(_metaData) } } checkForMissingColumnInfos() if (_metaData.ValidationResult.hasErrors() ) { return new DefaultImportData(_metaData) } _data = readDataRows(entitySheet) _metaData.ValidationResult.SummaryLog.registerType(_metaData.EntityInfo.EntityType) return new DefaultImportData(_metaData) } function importOnlyHeaderInfo(inputStream : InputStream) : ImportData { var workbook : XSSFWorkbook try { workbook = new XSSFWorkbook(inputStream) } catch (formatException : POIXMLException) { _validationResult.addError(DisplayKey.get("Import.Validation.FatalErrors.IllegalFileFormat")) return new DefaultImportData( new DefaultMetaData() { :_validationResult2 = _validationResult }) } var entitySheet = workbook.getSheetAt(ExportImportConstants.ENTITY_SHEET) _metaData = readHeaders(workbook, entitySheet) return new DefaultImportData(_metaData) } private function readHeaders(workbook : XSSFWorkbook, entitySheet : Sheet) : ImportMetaData { // read metadata header information var languageType = getLanguage(workbook) var locale = LocaleUtil.toLanguage(languageType) var resolvedEntityType = getEntityType(workbook) var entityInfo = getEntityInfo(resolvedEntityType) var periodID = retrieveMetaDataVariableValue(ExportImportConstants.PERIOD_ID_EXCEL_VARIABLE, workbook) var jobID = retrieveMetaDataVariableValue(ExportImportConstants.JOB_ID_EXCEL_VARIABLE, workbook) var template = Coercions.makePBooleanFrom(retrieveMetaDataVariableValue(ExportImportConstants.TEMPLATE_EXCEL_VARIABLE, workbook)) var headerInfo = readHeader(locale, entityInfo, entitySheet) return new DefaultMetaData() { :_headerInfo = headerInfo, :_languageType = languageType, :_entityInfo = entityInfo, :_validationResult2 = _validationResult, :_periodID = periodID, :_jobID = jobID, :_template = template } } private function retrieveMetaDataVariableValue(variableName : String, workbook : XSSFWorkbook) : String { var variableValue = workbook.getName(variableName).RefersToFormula if (variableValue == null or variableValue.length < 2 or not (variableValue.charAt(0) == '\"') or not (variableValue.charAt(variableValue.length - 1) == '\"')) { _validationResult.addError(DisplayKey.get("Import.Validation.FatalErrors.MissingRequiredVariable", variableName)) return variableValue } return variableValue.substring(1, variableValue.length - 1) } private function getLanguage(workbook : XSSFWorkbook) : LanguageType { var language = retrieveMetaDataVariableValue(ExportImportConstants.LANGUAGE_EXCEL_VARIABLE, workbook) if (language == null) { return null } var languageType = LanguageType.get(language) if (languageType == null) { _validationResult.addError(DisplayKey.get("Import.Validation.FatalErrors.NoLanguageType", language)) } return languageType } private function getEntityType(workbook : XSSFWorkbook) : Type { var entityString = retrieveMetaDataVariableValue(ExportImportConstants.ENTITY_EXCEL_VARIABLE, workbook) if (entityString == null) { return null } try { var resolvedEntityType = TypeSystem.getByFullName(entityString) return resolvedEntityType } catch (e : Exception){ if (e.Cause.toString().startsWith("java.lang.ClassNotFoundException:")) { _validationResult.addError(DisplayKey.get("Import.Validation.FatalErrors.BadEntityValue", entityString)) return null } else { throw e } } } private function getEntityInfo(resolvedEntityType : Type) : EntityInfo { if (resolvedEntityType != null) { return _registry.findEntityInfo(resolvedEntityType) } return null } private function readHeader(locale : ILocale, entityInfo : EntityInfo, sheet : Sheet) : List { var returnedHeaders : List = {} if (locale == null or entityInfo == null or not sheet.HasElements) { return returnedHeaders } var firstRow = sheet.getRow(ExportImportConstants.HEADER_INDEX) for (columnNumber in 0 as short..|firstRow.LastCellNum) { var columnName = getCurrentCellValue(firstRow, columnNumber) var columnNameInExcel = ExportImportHelper.convertColumnToExcelFormat(columnNumber) if (not columnName.NotBlank) { _validationResult.addWarning(DisplayKey.get("Import.Validation.Warnings.IgnoreColumn", columnNameInExcel)) _ignoredColumns.add(columnNumber) continue } var cellInfo = findColumnInfoByName(locale, entityInfo, columnName) if ( cellInfo == null ) { _validationResult.addWarning(DisplayKey.get("Import.Validation.Warnings.UnknownColumn", columnName, columnNameInExcel)) _ignoredColumns.add(columnNumber) } else { if ( returnedHeaders.contains(cellInfo) ) { _validationResult.addError(DisplayKey.get("Import.Validation.FatalErrors.DuplicateColumn", columnName, columnNameInExcel)) } returnedHeaders.add(cellInfo) } } return returnedHeaders } private function checkForTemplateExcludedColumns() { if (_metaData.HeaderInfo.hasMatch(\ c -> _metaData.EntityInfo.ExcludedFromTemplateColumns.contains(c))) { _validationResult.addError(DisplayKey.get("Import.Validation.FatalErrors.ModifyNotAllowed", _metaData.EntityInfo.EntityType.DisplayName)) } } private function checkForMissingColumnInfos() { _metaData.MissingColumnInfos.map(\ column -> column.Header).each(\ missingHeader -> { _validationResult.addError(DisplayKey.get("Import.Validation.FatalErrors.RequiredColumn", gw.api.locale.DisplayKey.get(missingHeader))) }) } private function readDataRows(sheet : Sheet) : CellDataRow[] { var columnCount = sheet.getRow(ExportImportConstants.HEADER_INDEX).LastCellNum var resultData : List = {} for (row in sheet.rowIterator() index rowNumber) { if (rowNumber < ExportImportConstants.FIRST_DATA_ROW_INDEX) { continue } var thisRow : List = {} for (columnNumber in 0 as short..|columnCount) { if (_ignoredColumns.contains(columnNumber)) { continue } var cellValue = getCurrentCellValue(row, columnNumber) var cellData = new CellData(_metaData.HeaderInfo.get(thisRow.Count), cellValue, ExportImportHelper.convertColumnToExcelFormat(columnNumber)) thisRow.add(cellData) } var uiRowIndex = row.RowNum + 1 if (rowHasData(thisRow)) { resultData.add(new CellDataRow(thisRow.toTypedArray(), uiRowIndex, _metaData)) } } return resultData.toTypedArray() } private function getCurrentCellValue(row : Row, cellIndex : int) : String { var currentCell = row.getCell(cellIndex) var cellValue = "" if (currentCell != null) { currentCell.CellType = Cell.CELL_TYPE_STRING cellValue = currentCell.toString() } return cellValue } private function rowHasData(row : List) : Boolean { for (cell in row) { if (cell.Data.NotBlank) { return true } } return false } private function findColumnInfoByName(language : ILocale, entityInfo : EntityInfo, columnName : String) : ColumnInfo { var columnInfo : ColumnInfo LocaleUtil.runAsCurrentLanguage(language, \ -> { columnInfo = entityInfo.Columns.firstWhere(\ c -> { return columnName == gw.api.locale.DisplayKey.get(c.Header) }) }) return columnInfo } private function handleImportError() : ImportData { _validationResult.addError(DisplayKey.get("Import.Validation.FatalErrors.IllegalFileFormat")) return new DefaultImportData( new DefaultMetaData() { :_validationResult2 = _validationResult }) } internal class DefaultImportData implements ImportData { var metaData : ImportMetaData construct(metaDataArg : ImportMetaData) { metaData = metaDataArg } override property get DataRows() : CellDataRow[] { return outer._data.copy() } override property get MetaData() : ImportMetaData { return metaData } } internal class DefaultMetaData implements ImportMetaData { var _entityInfo : EntityInfo as EntityInfo var _languageType : LanguageType as LanguageType var _localeType : LocaleType as LocaleType var _headerInfo : List as HeaderInfo var _validationResult2 : ImportValidationResult as ValidationResult var _periodID : String as PeriodID var _jobID : String as JobID var _template : boolean as Template var _newParentEntityPublicIDs : Map = {} var _testParentEntityPublicIDS : Map = {} var _testState : boolean = false override function hasNewParentEntityWithPublicID(publicID : String) : boolean { return (_testState ? _testParentEntityPublicIDS : _newParentEntityPublicIDs).containsKey(publicID) } override function getNewParentEntityByPublicID(publicID : String) : KeyableBean { return (_testState ? _testParentEntityPublicIDS : _newParentEntityPublicIDs).get(publicID) } override function setNewParentEntityByPublicID(publicID : String, parentEntity : KeyableBean) { if ( _testState ) { _testParentEntityPublicIDS.put(publicID, parentEntity) } else { _newParentEntityPublicIDs.put(publicID, parentEntity) } } override function setTest() { _testState = true } override function setImport() { _testState = false } override property get MissingColumnInfos() : Set { if (_entityInfo == null) { return {} } var missingColumns = _entityInfo.Columns.subtract(_headerInfo) missingColumns.removeWhere(\ column -> not column.RequiredForImport and (not column.FlagsEntityId or (column.FlagsEntityId and Template))) return missingColumns } } }