/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.components.excel.execution; import java.io.File; import java.io.IOException; import org.apache.commons.io.FileUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import de.rcenvironment.components.excel.common.ChannelValue; import de.rcenvironment.components.excel.common.ExcelAddress; import de.rcenvironment.components.excel.common.ExcelComponentConstants; import de.rcenvironment.components.excel.common.ExcelComponentHistoryDataItem; import de.rcenvironment.components.excel.common.ExcelUtils; import de.rcenvironment.core.component.api.ComponentConstants; import de.rcenvironment.core.component.api.ComponentException; import de.rcenvironment.core.component.execution.api.Component; import de.rcenvironment.core.component.model.api.LocalExecutionOnly; import de.rcenvironment.core.datamodel.api.DataType; import de.rcenvironment.core.datamodel.api.DataTypeException; import de.rcenvironment.core.datamodel.api.TypedDatum; import de.rcenvironment.core.datamodel.types.api.BooleanTD; import de.rcenvironment.core.datamodel.types.api.FloatTD; import de.rcenvironment.core.datamodel.types.api.IntegerTD; import de.rcenvironment.core.datamodel.types.api.ShortTextTD; import de.rcenvironment.core.datamodel.types.api.SmallTableTD; import de.rcenvironment.core.utils.common.StringUtils; import de.rcenvironment.core.utils.common.TempFileService; import de.rcenvironment.core.utils.common.TempFileServiceAccess; /** * Excel implementation of {@link Component}. * * @author Markus Kunde * @author Patrick Schaefer */ @LocalExecutionOnly public class ExcelComponent extends ExcelRCEComponent { private static final String EXCEPTION_MSG_CANNOT_DETERMINE_VALUE = "No value at Excel cell(s), cannot determine value(s)"; private static final String EXCEPTION_MSG_WRONGTYPE_2 = " while value is of type "; private static final String EXCEPTION_MSG_WRONGTYPE_1 = "Output type is "; /** OLE cannot handle filename longer than 20 characters. */ private static final int MAXIMUM_FILENAME_OLE_ACCEPTS = 20; private static final Log LOG = LogFactory.getLog(ExcelComponent.class); private long iteration = 0; private ExcelComponentHistoryDataItem historyDataItem; private TempFileService tempFileUtils = TempFileServiceAccess.getInstance(); @Override public void start() throws ComponentException { super.start(); initializeNewHistoryDataItem(); } @Override protected void executingOneStep() throws ComponentException { initializeNewHistoryDataItem(); File excelWorkingFile = null; try { iteration++; /* Copy origin Excel file to temp Excel file. */ File originExcelFile = null; try { originExcelFile = ExcelUtils.getAbsoluteFile(componentContext.getConfigurationValue(ExcelComponentConstants.XL_FILENAME)); if (originExcelFile == null) { throw new ComponentException("No Excel file given"); } excelWorkingFile = tempFileUtils.createTempFileWithFixedFilename(originExcelFile.getName()); FileUtils.copyFile(originExcelFile, excelWorkingFile, true); /* Cutting filename to maximum size. */ String fileName = excelWorkingFile.getName(); if (fileName.length() > MAXIMUM_FILENAME_OLE_ACCEPTS) { String newFileName = fileName.substring(fileName.length() - MAXIMUM_FILENAME_OLE_ACCEPTS, fileName.length()); File dest = new File(excelWorkingFile.getParent() + File.separator + newFileName); boolean renamed = excelWorkingFile.renameTo(dest); if (renamed) { excelWorkingFile = dest; } else { componentLog.componentError("Failed to shorten file name. It's possible that VBA Codes did not execute." + " Component will continue to try."); } } } catch (IOException e) { throw new ComponentException("Failed to copy origin Excel file to temporary directory" + " (required for the Excel component to work)", e); } /* Run macro start */ excelService.runMacro(excelWorkingFile, componentContext.getConfigurationValue(ExcelComponentConstants.PRE_MACRO)); /* Processing inputs */ if (!componentContext.getInputs().isEmpty()) { processingInputChannels(excelWorkingFile); } /* Run macro run */ excelService.runMacro(excelWorkingFile, componentContext.getConfigurationValue( ExcelComponentConstants.RUN_MACRO)); /* Processing outputs */ processingOutputChannels(excelWorkingFile); /* Run macro close */ excelService.runMacro(excelWorkingFile, componentContext.getConfigurationValue( ExcelComponentConstants.POST_MACRO)); } finally { storeHistoryDataItem(); if (excelWorkingFile != null) { try { tempFileUtils.disposeManagedTempDirOrFile(excelWorkingFile); } catch (IOException e) { LOG.error("Failed to delete temporary Excel file", e); } } } } /** * Processes all output channels in one processing step. * * @param excelFile the Excel file where input values should be written to * @throws ComponentException thrown if processing output channel has gone wrong */ private void processingOutputChannels(final File excelFile) throws ComponentException { for (String outputName: componentContext.getOutputs()) { final boolean expand = Boolean.valueOf(componentContext.getOutputMetaDataValue(outputName, ExcelComponentConstants.METADATA_EXPANDING)); final String address = componentContext.getOutputMetaDataValue(outputName, ExcelComponentConstants.METADATA_ADDRESS); final ExcelAddress addr = new ExcelAddress(excelFile, address); SmallTableTD value = excelService.getValueOfCells(excelFile, addr); final TypedDatum td; switch(componentContext.getOutputDataType(outputName)) { case ShortText: td = value.getTypedDatumOfCell(0, 0); if (td != null && td instanceof ShortTextTD) { ShortTextTD text = (ShortTextTD) td; componentContext.writeOutput(outputName, text); } else if (td != null) { componentLog.componentInfo(StringUtils.format("Trying to convert '%s' (value of cell in Excel) to" + " %s (data type of output '%s')...", td, DataType.ShortText.getDisplayName(), outputName)); ShortTextTD text; try { text = typedDatumConverter.castOrConvertUnsafe(td, ShortTextTD.class); componentLog.componentInfo(StringUtils.format("Sucessfully converted '%s' to ShortText", td)); componentContext.writeOutput(outputName, text); } catch (DataTypeException e) { throw new ComponentException(EXCEPTION_MSG_WRONGTYPE_1 + DataType.ShortText.getDisplayName() + EXCEPTION_MSG_WRONGTYPE_2 + td.getDataType().getDisplayName(), e); } } else { throw new ComponentException(EXCEPTION_MSG_CANNOT_DETERMINE_VALUE); } break; case Float: td = value.getTypedDatumOfCell(0, 0); if (td != null && td instanceof FloatTD) { FloatTD number = (FloatTD) td; componentContext.writeOutput(outputName, number); } else if (td != null) { componentLog.componentInfo(StringUtils.format("Trying to convert '%s' (value of cell in Excel) to" + " %s (data type of output '%s')...", td, DataType.Float.getDisplayName(), outputName)); FloatTD number; try { number = typedDatumConverter.castOrConvert(td, FloatTD.class); componentLog.componentInfo(StringUtils.format("Sucessfully converted '%s' to Float", td)); componentContext.writeOutput(outputName, number); } catch (DataTypeException e) { throw new ComponentException(EXCEPTION_MSG_WRONGTYPE_1 + DataType.Float.getDisplayName() + EXCEPTION_MSG_WRONGTYPE_2 + td.getDataType().getDisplayName(), e); } } else { throw new ComponentException(EXCEPTION_MSG_CANNOT_DETERMINE_VALUE); } break; case Integer: td = value.getTypedDatumOfCell(0, 0); if (td != null && td instanceof IntegerTD) { IntegerTD number = (IntegerTD) td; componentContext.writeOutput(outputName, number); } else if (td != null) { componentLog.componentInfo(StringUtils.format("Trying to convert '%s' (value of cell in Excel) to %s " + "(data type of output '%s')...", td, DataType.Integer.getDisplayName(), outputName)); IntegerTD number; try { number = typedDatumConverter.castOrConvert(td, IntegerTD.class); componentLog.componentInfo(StringUtils.format("Sucessfully converted '%s' to Integer", td)); componentContext.writeOutput(outputName, number); } catch (DataTypeException e) { throw new ComponentException(EXCEPTION_MSG_WRONGTYPE_1 + DataType.Integer.getDisplayName() + EXCEPTION_MSG_WRONGTYPE_2 + td.getDataType().getDisplayName(), e); } } else { throw new ComponentException(EXCEPTION_MSG_CANNOT_DETERMINE_VALUE); } break; case Boolean: td = value.getTypedDatumOfCell(0, 0); if (td != null && td instanceof BooleanTD) { BooleanTD b = (BooleanTD) td; componentContext.writeOutput(outputName, b); } else if (td != null) { componentLog.componentInfo(StringUtils.format("Trying to convert '%s' (value of cell in Excel) to %s " + "(data type of output '%s')...", td, DataType.Boolean.getDisplayName(), outputName)); BooleanTD b; try { b = typedDatumConverter.castOrConvert(td, BooleanTD.class); componentLog.componentInfo(StringUtils.format("Sucessfully converted '%s' to Integer", td)); componentContext.writeOutput(outputName, b); } catch (DataTypeException e) { throw new ComponentException(EXCEPTION_MSG_WRONGTYPE_1 + DataType.Boolean.getDisplayName() + EXCEPTION_MSG_WRONGTYPE_2 + td.getDataType().getDisplayName(), e); } } else { throw new ComponentException(EXCEPTION_MSG_CANNOT_DETERMINE_VALUE); } break; case SmallTable: boolean pruning = Boolean.valueOf(componentContext.getOutputMetaDataValue(outputName, ExcelComponentConstants.METADATA_PRUNING)); if (pruning) { int latestRowIndex = ExcelUtils.getRowIndexLastCellFilled(value.toArray()); int latestColumnIndex = value.getColumnCount(); value = value.getSubTable(0, 0, latestRowIndex + 1, latestColumnIndex); } if (value != null) { componentContext.writeOutput(outputName, value); } else { throw new ComponentException(EXCEPTION_MSG_CANNOT_DETERMINE_VALUE); } break; default: throw new ComponentException("Output type not supported: " + componentContext .getOutputDataType(outputName).getDisplayName()); } //Fill runtime GUI data File originExcelFile = ExcelUtils.getAbsoluteFile(componentContext.getConfigurationValue(ExcelComponentConstants.XL_FILENAME)); ChannelValue dataval = new ChannelValue(originExcelFile, addr, outputName, false, expand, iteration); dataval.setValues(value); dataval.setPreMacro(componentContext.getConfigurationValue(ExcelComponentConstants.PRE_MACRO)); dataval.setRunMacro(componentContext.getConfigurationValue(ExcelComponentConstants.RUN_MACRO)); dataval.setPostMacro(componentContext.getConfigurationValue(ExcelComponentConstants.POST_MACRO)); notificationService.send(componentContext.getExecutionIdentifier() + ExcelComponentConstants.NOTIFICATION_SUFFIX, dataval); } } /** * Processes all input channels in one processing step. * * @param excelFile the Excel file where input values should be written to * @param newInput {@link #runStep(Input, Map) newInput} * @param inputValues {@link #runStep(Input, Map) inputValues} * @throws ComponentException thrown if processing output channel has gone wrong */ private void processingInputChannels(final File excelFile) throws ComponentException { for (final String inputName : componentContext.getInputsWithDatum()) { TypedDatum input = componentContext.readInput(inputName); String address = componentContext.getInputMetaDataValue(inputName, ExcelComponentConstants.METADATA_ADDRESS); ExcelAddress addr = new ExcelAddress(excelFile, address); final boolean expand = Boolean.valueOf(componentContext.getInputMetaDataValue(inputName, ExcelComponentConstants.METADATA_EXPANDING)); SmallTableTD value = null; switch(componentContext.getInputDataType(inputName)) { case ShortText: value = typedDatumFactory.createSmallTable(1, 1); value.setTypedDatumForCell(input, 0, 0); excelService.setValues(excelFile, addr, value); break; case Float: value = typedDatumFactory.createSmallTable(1, 1); value.setTypedDatumForCell(input, 0, 0); excelService.setValues(excelFile, addr, value); break; case Integer: value = typedDatumFactory.createSmallTable(1, 1); value.setTypedDatumForCell(input, 0, 0); excelService.setValues(excelFile, addr, value); break; case Boolean: value = typedDatumFactory.createSmallTable(1, 1); value.setTypedDatumForCell(input, 0, 0); excelService.setValues(excelFile, addr, value); break; case SmallTable: value = (SmallTableTD) input; if (expand) { addr = ExcelAddress.getExcelAddressForTableRange(excelFile, addr, value.getRowCount(), value.getColumnCount()); } excelService.setValues(excelFile, addr, value); break; default: break; } //Fill data for runtime GUI and send it final File originExcelFile = ExcelUtils.getAbsoluteFile(componentContext .getConfigurationValue(ExcelComponentConstants.XL_FILENAME)); final ChannelValue dataval = new ChannelValue(originExcelFile, addr, inputName, true, expand, iteration); dataval.setValues(value); dataval.setPreMacro(componentContext.getConfigurationValue(ExcelComponentConstants.PRE_MACRO)); dataval.setRunMacro(componentContext.getConfigurationValue(ExcelComponentConstants.RUN_MACRO)); dataval.setPostMacro(componentContext.getConfigurationValue(ExcelComponentConstants.POST_MACRO)); notificationService.send(componentContext.getExecutionIdentifier() + ExcelComponentConstants.NOTIFICATION_SUFFIX, dataval); } } private void initializeNewHistoryDataItem() { if (Boolean.valueOf(componentContext.getConfigurationValue(ComponentConstants.CONFIG_KEY_STORE_DATA_ITEM))) { historyDataItem = new ExcelComponentHistoryDataItem(); historyDataItem.setExcelFilePath(ExcelUtils.getAbsoluteFile(componentContext.getConfigurationValue( ExcelComponentConstants.XL_FILENAME)).getAbsolutePath()); } } private void storeHistoryDataItem() { if (Boolean.valueOf(componentContext.getConfigurationValue(ComponentConstants.CONFIG_KEY_STORE_DATA_ITEM))) { componentContext.writeFinalHistoryDataItem(historyDataItem); } } }