From 0745856d0f68343cbf0527f132899d0a7396ce94 Mon Sep 17 00:00:00 2001 From: Emanuela Epure <67077116+emanuelaepure10@users.noreply.github.com> Date: Wed, 12 Jul 2023 15:00:02 +0200 Subject: [PATCH] feat: export multiple feature type to Excel HALE studio supports selecting multiple feature types for export to Excel. Each selected feature type is added as a separate sheet in a single Excel workbook. https://github.com/halestudio/hale/issues/1032 --- .../hale/io/csv/InstanceTableIOConstants.java | 5 + .../test/writer/XLSInstanceWriterTest.java | 46 +++- .../META-INF/MANIFEST.MF | 6 +- .../eu.esdihumboldt.hale.io.xls.ui/plugin.xml | 2 +- .../XLSInstanceExportConfigurationPage.java | 200 ++++++++++++++++++ .../hale/io/xls/writer/XLSInstanceWriter.java | 139 +++++++----- 6 files changed, 341 insertions(+), 57 deletions(-) create mode 100644 io/plugins/eu.esdihumboldt.hale.io.xls.ui/src/eu/esdihumboldt/hale/io/xls/ui/XLSInstanceExportConfigurationPage.java diff --git a/io/plugins/eu.esdihumboldt.hale.io.csv/src/eu/esdihumboldt/hale/io/csv/InstanceTableIOConstants.java b/io/plugins/eu.esdihumboldt.hale.io.csv/src/eu/esdihumboldt/hale/io/csv/InstanceTableIOConstants.java index 0b547d15b7..4b9f968c32 100644 --- a/io/plugins/eu.esdihumboldt.hale.io.csv/src/eu/esdihumboldt/hale/io/csv/InstanceTableIOConstants.java +++ b/io/plugins/eu.esdihumboldt.hale.io.csv/src/eu/esdihumboldt/hale/io/csv/InstanceTableIOConstants.java @@ -45,4 +45,9 @@ public class InstanceTableIOConstants { */ public static final String EXPORT_TYPE = "selectedExportType"; + /** + * Parameter for exporting empty feature types to XLS Export + */ + public static final String EXPORT_EMPTY_FEATURETYPES = "emptyFeatureTypes"; + } diff --git a/io/plugins/eu.esdihumboldt.hale.io.xls.test/src/eu/esdihumboldt/hale/io/xls/test/writer/XLSInstanceWriterTest.java b/io/plugins/eu.esdihumboldt.hale.io.xls.test/src/eu/esdihumboldt/hale/io/xls/test/writer/XLSInstanceWriterTest.java index 88437eb27b..d35cef1a90 100644 --- a/io/plugins/eu.esdihumboldt.hale.io.xls.test/src/eu/esdihumboldt/hale/io/xls/test/writer/XLSInstanceWriterTest.java +++ b/io/plugins/eu.esdihumboldt.hale.io.xls.test/src/eu/esdihumboldt/hale/io/xls/test/writer/XLSInstanceWriterTest.java @@ -19,6 +19,7 @@ import static org.junit.Assert.assertTrue; import java.io.File; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -157,9 +158,7 @@ public void testWriteComplexSchema() throws Exception { Sheet sheet = wb.getSheetAt(0); checkHeader(sheet, header); - checkSheetName(sheet, "person"); - checkFirstDataRow(sheet, firstDataRow); } @@ -209,6 +208,49 @@ public void testWriteNotNestedProperties() throws Exception { checkFirstDataRow(sheet, firstDataRow); } + /** + * Test - write data of complex schema and analyze result + * + * @throws Exception , if an error occurs + */ + @Test + public void testExportMultiFeatureToExcel() throws Exception { + ArrayList examples = new ArrayList<>(); + TransformationExample example = TransformationExamples + .getExample(TransformationExamples.SIMPLE_COMPLEX); + TransformationExample example2 = TransformationExamples + .getExample(TransformationExamples.CARDINALITY_MOVE); + examples.add(example.getTargetInstances()); + examples.add(example2.getTargetInstances()); + + // set instances to xls instance writer + XLSInstanceWriter writer = new XLSInstanceWriter(); + IContentType contentType = HalePlatform.getContentTypeManager() + .getContentType("eu.esdihumboldt.hale.io.xls.xls"); + writer.setParameter(InstanceTableIOConstants.SOLVE_NESTED_PROPERTIES, Value.of(false)); + writer.setParameter(InstanceTableIOConstants.USE_SCHEMA, Value.of(false)); + writer.setParameter(InstanceTableIOConstants.EXPORT_EMPTY_FEATURETYPES, Value.of(true)); + + File tmpFile = tmpFolder.newFile("excelWith2Sheets.xls"); + // write instances to a temporary XLS file + writer.setTarget(new FileIOSupplier(tmpFile)); + writer.setContentType(contentType); + + for (InstanceCollection instanceCollection : examples) { + writer.setInstances(instanceCollection); + } + System.out.println("XLSInstanceWriterTest.testExportMultiFeatureToExcel()"); + + IOReport report = writer.execute(null); + Workbook wb; +// https: // poi.apache.org/components/spreadsheet/quick-guide.html#FileInputStream + try (POIFSFileSystem fs = new POIFSFileSystem(tmpFile)) { + wb = new HSSFWorkbook(fs.getRoot(), true); + } + int sheetNumber = wb.getNumberOfSheets(); + assertEquals(examples.size(), sheetNumber); + } + /** * * @param sheet the excel file sheet diff --git a/io/plugins/eu.esdihumboldt.hale.io.xls.ui/META-INF/MANIFEST.MF b/io/plugins/eu.esdihumboldt.hale.io.xls.ui/META-INF/MANIFEST.MF index 40e572fcee..0dae5efbab 100644 --- a/io/plugins/eu.esdihumboldt.hale.io.xls.ui/META-INF/MANIFEST.MF +++ b/io/plugins/eu.esdihumboldt.hale.io.xls.ui/META-INF/MANIFEST.MF @@ -5,7 +5,8 @@ Bundle-SymbolicName: eu.esdihumboldt.hale.io.xls.ui;singleton:=true Bundle-Version: 5.1.0.qualifier Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime, - eu.esdihumboldt.hale.ui + eu.esdihumboldt.hale.ui, + eu.esdihumboldt.hale.common.instance Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: de.fhg.igd.eclipse.util.extension, de.fhg.igd.slf4jplus, @@ -13,7 +14,10 @@ Import-Package: de.fhg.igd.eclipse.util.extension, eu.esdihumboldt.hale.common.core.io.project.model, eu.esdihumboldt.hale.common.core.io.supplier, eu.esdihumboldt.hale.common.instance.io, + eu.esdihumboldt.hale.common.schema, eu.esdihumboldt.hale.common.schema.io, + eu.esdihumboldt.hale.common.schema.model, + eu.esdihumboldt.hale.common.schema.model.constraint.type, eu.esdihumboldt.hale.io.csv, eu.esdihumboldt.hale.io.csv.ui, eu.esdihumboldt.hale.io.xls, diff --git a/io/plugins/eu.esdihumboldt.hale.io.xls.ui/plugin.xml b/io/plugins/eu.esdihumboldt.hale.io.xls.ui/plugin.xml index 6db1bee218..eac0b88b13 100644 --- a/io/plugins/eu.esdihumboldt.hale.io.xls.ui/plugin.xml +++ b/io/plugins/eu.esdihumboldt.hale.io.xls.ui/plugin.xml @@ -18,7 +18,7 @@ diff --git a/io/plugins/eu.esdihumboldt.hale.io.xls.ui/src/eu/esdihumboldt/hale/io/xls/ui/XLSInstanceExportConfigurationPage.java b/io/plugins/eu.esdihumboldt.hale.io.xls.ui/src/eu/esdihumboldt/hale/io/xls/ui/XLSInstanceExportConfigurationPage.java new file mode 100644 index 0000000000..aa59ff05fe --- /dev/null +++ b/io/plugins/eu.esdihumboldt.hale.io.xls.ui/src/eu/esdihumboldt/hale/io/xls/ui/XLSInstanceExportConfigurationPage.java @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2023 wetransform GmbH + * + * All rights reserved. This program and the accompanying materials are made + * available under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution. If not, see . + * + * Contributors: + * wetransform GmbH + */ + +package eu.esdihumboldt.hale.io.xls.ui; + +import java.util.ArrayList; +import java.util.Collection; + +import javax.xml.namespace.QName; + +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.viewers.ArrayContentProvider; +import org.eclipse.jface.viewers.CheckStateChangedEvent; +import org.eclipse.jface.viewers.CheckboxTableViewer; +import org.eclipse.jface.viewers.ICheckStateListener; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Table; +import org.eclipse.ui.PlatformUI; + +import eu.esdihumboldt.hale.common.core.io.Value; +import eu.esdihumboldt.hale.common.instance.io.InstanceWriter; +import eu.esdihumboldt.hale.common.schema.model.TypeDefinition; +import eu.esdihumboldt.hale.io.csv.InstanceTableIOConstants; +import eu.esdihumboldt.hale.io.csv.ui.InstanceExportConfigurationPage; + +/** + * Configuration page for exporting Excel + * + * @author Emanuela Epure + */ +public class XLSInstanceExportConfigurationPage extends InstanceExportConfigurationPage { + + private CheckboxTableViewer featureTypeTable; + private Button selectAll = null; + private Button exportEmptyData = null; + private Group chooseFeatureTypes; + private Table table; + + /** + * + */ + public XLSInstanceExportConfigurationPage() { + super(); + } + + /** + * @see eu.esdihumboldt.hale.io.csv.ui.InstanceExportConfigurationPage#createContent(org.eclipse.swt.widgets.Composite) + */ + @Override + protected void createContent(Composite page) { + this.page = page; + + page.setLayout(new GridLayout(1, false)); + + solveNestedProperties = new Button(page, SWT.CHECK); + solveNestedProperties.setText("Solve nested properties"); + solveNestedProperties.setSelection(true); + + useSchema = new Button(page, SWT.CHECK); + useSchema.setText("Use the source schema for the order of the exported columns"); + useSchema.setSelection(true); + + GridDataFactory groupData = GridDataFactory.fillDefaults().grab(true, false); + chooseFeatureTypes = new Group(page, SWT.NONE); + chooseFeatureTypes.setLayout(new GridLayout(1, false)); + chooseFeatureTypes.setText("Choose your Type you want to export"); + groupData.applyTo(chooseFeatureTypes); + + page.pack(); + + // wait for selected type + setPageComplete(false); + } + + /** + * @see eu.esdihumboldt.hale.io.csv.ui.InstanceExportConfigurationPage#onShowPage(boolean) + */ + @Override + protected void onShowPage(boolean firstShow) { + if (firstShow) { + + exportEmptyData = new Button(chooseFeatureTypes, SWT.CHECK); + exportEmptyData.setText("Ignore feature types without data"); + exportEmptyData.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1)); + + exportEmptyData.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + } + }); + exportEmptyData.setSelection(false); + + selectAll = new Button(chooseFeatureTypes, SWT.CHECK); + selectAll.setText("Select all"); + selectAll.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1)); + + selectAll.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + featureTypeTable.setAllChecked(((Button) e.getSource()).getSelection()); + page.layout(); + page.pack(); + setPageComplete(validate()); + } + }); + + table = new Table(chooseFeatureTypes, SWT.CHECK | SWT.MULTI | SWT.SCROLL_PAGE); + table.setHeaderVisible(false); + table.setLinesVisible(false); + table.setBackground( + PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_GRAY)); + GridDataFactory groupData = GridDataFactory.fillDefaults().grab(true, false); + groupData.applyTo(table); + + featureTypeTable = new CheckboxTableViewer(table); + featureTypeTable.setLabelProvider(new LabelProvider() { + + @Override + public String getText(Object element) { + return ((TypeDefinition) element).getDisplayName(); + } + + }); + featureTypeTable.setContentProvider(ArrayContentProvider.getInstance()); + + Collection relevantTypes = getWizard().getProvider() + .getTargetSchema().getMappingRelevantTypes(); + + ArrayList tableContent = new ArrayList<>(); + for (TypeDefinition typeDefinition : relevantTypes) { + tableContent.add(typeDefinition.getName()); + } + + featureTypeTable.setInput(relevantTypes); + + featureTypeTable.addCheckStateListener(new ICheckStateListener() { + + @Override + public void checkStateChanged(CheckStateChangedEvent event) { + page.layout(); + page.pack(); + setPageComplete(validate()); + } + }); + + page.layout(); + page.pack(); + } + } + + private boolean validate() { + return (featureTypeTable.getCheckedElements().length > 0); + } + + /** + * @see eu.esdihumboldt.hale.io.csv.ui.InstanceExportConfigurationPage#updateConfiguration(eu.esdihumboldt.hale.common.instance.io.InstanceWriter) + */ + @Override + public boolean updateConfiguration(InstanceWriter provider) { + provider.setParameter(InstanceTableIOConstants.SOLVE_NESTED_PROPERTIES, + Value.of(solveNestedProperties.getSelection())); + provider.setParameter(InstanceTableIOConstants.USE_SCHEMA, + Value.of(useSchema.getSelection())); + + provider.setParameter(InstanceTableIOConstants.EXPORT_EMPTY_FEATURETYPES, + Value.of(exportEmptyData.getSelection())); + + Object[] elements = featureTypeTable.getCheckedElements(); + String param = ""; + for (Object el : elements) { + param = param + ((TypeDefinition) el).getName().toString() + ","; + } + provider.setParameter(InstanceTableIOConstants.EXPORT_TYPE, Value.of(param)); + + return true; + } + +} diff --git a/io/plugins/eu.esdihumboldt.hale.io.xls/src/eu/esdihumboldt/hale/io/xls/writer/XLSInstanceWriter.java b/io/plugins/eu.esdihumboldt.hale.io.xls/src/eu/esdihumboldt/hale/io/xls/writer/XLSInstanceWriter.java index 6a940fbdb0..ae33e534c5 100644 --- a/io/plugins/eu.esdihumboldt.hale.io.xls/src/eu/esdihumboldt/hale/io/xls/writer/XLSInstanceWriter.java +++ b/io/plugins/eu.esdihumboldt.hale.io.xls/src/eu/esdihumboldt/hale/io/xls/writer/XLSInstanceWriter.java @@ -15,6 +15,7 @@ package eu.esdihumboldt.hale.io.xls.writer; +import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; @@ -55,7 +56,7 @@ */ public class XLSInstanceWriter extends AbstractTableInstanceWriter { - private Workbook wb; + private Workbook workbook; private CellStyle headerStyle; private CellStyle cellStyle; @@ -73,86 +74,118 @@ protected IOReport execute(ProgressIndicator progress, IOReporter reporter) boolean solveNestedProperties = getParameter( InstanceTableIOConstants.SOLVE_NESTED_PROPERTIES).as(Boolean.class, false); + boolean emptyFeatureTypes = getParameter(InstanceTableIOConstants.EXPORT_EMPTY_FEATURETYPES) + .as(Boolean.class, false); + // get the parameter to get the type definition String exportType = getParameter(InstanceTableIOConstants.EXPORT_TYPE).as(String.class); - QName selectedTypeName = null; + ArrayList selectedFeatureTypes = new ArrayList<>(); + + String[] array = exportType.split(","); + for (String input : array) { + + selectedFeatureTypes.add(QName.valueOf(input)); - if (exportType != null && !exportType.equals("") && !exportType.equals(" ")) { - selectedTypeName = QName.valueOf(exportType); } // write xls file if (getContentType().getId().equals("eu.esdihumboldt.hale.io.xls.xls")) { - wb = new HSSFWorkbook(); + workbook = new HSSFWorkbook(); } // write xlsx file else if (getContentType().getId().equals("eu.esdihumboldt.hale.io.xls.xlsx")) { - wb = new XSSFWorkbook(); + workbook = new XSSFWorkbook(); } else { reporter.error(new IOMessageImpl("Content type is invalid!", null)); return reporter; } - cellStyle = XLSCellStyles.getNormalStyle(wb, false); - headerStyle = XLSCellStyles.getHeaderStyle(wb); - - // get all instances of the selected Type - InstanceCollection instances = getInstanceCollection(selectedTypeName); - // use ResourceIterator in a try block because is closable - - // avoid infinite - // cleaning project after exporting data - try (ResourceIterator instanceIterator = instances.iterator();) { - Instance instance = null; - try { - instance = instanceIterator.next(); - } catch (NoSuchElementException e) { - reporter.error( - new IOMessageImpl("There are no instances for the selected type.", e)); - return reporter; + // write file and be sure to close resource with try-block + if (new File(getTarget().getLocation().getPath()).exists()) { + // Remove all existing sheets + while (workbook.getNumberOfSheets() > 0) { + workbook.removeSheetAt(0); } + } - List remainingInstances = new ArrayList(); + cellStyle = XLSCellStyles.getNormalStyle(workbook, false); + headerStyle = XLSCellStyles.getHeaderStyle(workbook); + + List remainingInstances = new ArrayList(); + for (QName selectedTypeName : selectedFeatureTypes) { + + // get all instances of the selected Type + InstanceCollection instances = getInstanceCollection(selectedTypeName); + // use ResourceIterator in a try block because is closable + // - + // avoid infinite + // cleaning project after exporting data + try (ResourceIterator instanceIterator = instances.iterator();) { + Instance instance = null; + try { + instance = instanceIterator.next(); + } catch (NoSuchElementException e) { +// reporter.error( +// new IOMessageImpl("There are no instances for the selected type.", e)); +// return reporter; + + if (!emptyFeatureTypes) { + Sheet sheet = workbook.createSheet(selectedTypeName.getLocalPart()); + sheet.createRow(0); + resizeSheet(sheet); + } + + continue; + } - headerRowStrings = new ArrayList(); + headerRowStrings = new ArrayList(); - boolean useSchema = getParameter(InstanceTableIOConstants.USE_SCHEMA).as(Boolean.class, - true); + boolean useSchema = getParameter(InstanceTableIOConstants.USE_SCHEMA) + .as(Boolean.class, true); - // all instances with equal type definitions are stored in an extra - // sheet - TypeDefinition definition = instance.getDefinition(); + // all instances with equal type definitions are stored in an + // extra + // sheet + TypeDefinition definition = instance.getDefinition(); - Sheet sheet = wb.createSheet(definition.getDisplayName()); - Row headerRow = sheet.createRow(0); - int rowNum = 1; - Row row = sheet.createRow(rowNum++); - writeRow(row, super.getPropertyMap(instance, headerRowStrings, useSchema, - solveNestedProperties)); + Sheet sheet; + try { + sheet = workbook.createSheet(definition.getDisplayName()); - while (instanceIterator.hasNext()) { - Instance nextInst = instanceIterator.next(); - if (nextInst.getDefinition().equals(definition)) { - row = sheet.createRow(rowNum++); - writeRow(row, super.getPropertyMap(nextInst, headerRowStrings, useSchema, + Row headerRow = sheet.createRow(0); + int rowNum = 1; + Row row = sheet.createRow(rowNum++); + writeRow(row, super.getPropertyMap(instance, headerRowStrings, useSchema, solveNestedProperties)); + + while (instanceIterator.hasNext()) { + Instance nextInst = instanceIterator.next(); + if (nextInst.getDefinition().equals(definition)) { + row = sheet.createRow(rowNum++); + writeRow(row, super.getPropertyMap(nextInst, headerRowStrings, + useSchema, solveNestedProperties)); + } + else + remainingInstances.add(nextInst); + } + + writeHeaderRow(headerRow, headerRowStrings); + setCellStyle(sheet, headerRowStrings.size()); + resizeSheet(sheet); + } catch (Exception e) { + continue; } - else - remainingInstances.add(nextInst); - } - writeHeaderRow(headerRow, headerRowStrings); - setCellStyle(sheet, headerRowStrings.size()); - resizeSheet(sheet); - - // write file and be sure to close resource with try-block - try (FileOutputStream out = new FileOutputStream( - getTarget().getLocation().getPath());) { - wb.write(out); + } + } - reporter.setSuccess(true); - return reporter; + try (FileOutputStream out = new FileOutputStream(getTarget().getLocation().getPath());) { + workbook.write(out); } + + reporter.setSuccess(true); + return reporter; } // close try-iterator @Override