de.symeda.sormas.ui.utils.DownloadUtil.java Source code

Java tutorial

Introduction

Here is the source code for de.symeda.sormas.ui.utils.DownloadUtil.java

Source

/*******************************************************************************
 * SORMAS - Surveillance Outbreak Response Management & Analysis System
 * Copyright  2016-2018 Helmholtz-Zentrum fr Infektionsforschung GmbH (HZI)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *******************************************************************************/
package de.symeda.sormas.ui.utils;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.opencsv.CSVWriter;
import com.vaadin.server.Page;
import com.vaadin.server.StreamResource;
import com.vaadin.server.StreamResource.StreamSource;
import com.vaadin.ui.Notification;
import com.vaadin.ui.Notification.Type;
import com.vaadin.v7.data.Container.Indexed;
import com.vaadin.v7.ui.CheckBox;
import com.vaadin.v7.ui.Grid.Column;

import de.symeda.sormas.api.EntityDto;
import de.symeda.sormas.api.FacadeProvider;
import de.symeda.sormas.api.i18n.I18nProperties;
import de.symeda.sormas.api.i18n.Strings;
import de.symeda.sormas.api.importexport.DatabaseTable;
import de.symeda.sormas.api.utils.CSVUtils;
import de.symeda.sormas.api.utils.DataHelper;
import de.symeda.sormas.api.utils.DateHelper;
import de.symeda.sormas.api.utils.ExportErrorException;
import de.symeda.sormas.api.utils.Order;
import de.symeda.sormas.ui.statistics.DatabaseExportView;

public class DownloadUtil {

    public static final int DETAILED_EXPORT_STEP_SIZE = 200;

    private DownloadUtil() {

    }

    @SuppressWarnings("serial")
    public static StreamResource createDatabaseExportStreamResource(DatabaseExportView databaseExportView,
            String fileName, String mimeType) {
        StreamResource streamResource = new StreamResource(new StreamSource() {
            @Override
            public InputStream getStream() {
                try {
                    Map<CheckBox, DatabaseTable> databaseToggles = databaseExportView.getDatabaseTableToggles();
                    List<DatabaseTable> tablesToExport = new ArrayList<>();
                    for (CheckBox checkBox : databaseToggles.keySet()) {
                        if (checkBox.getValue() == true) {
                            tablesToExport.add(databaseToggles.get(checkBox));
                        }
                    }

                    String zipPath = FacadeProvider.getExportFacade().generateDatabaseExportArchive(tablesToExport);
                    return new BufferedInputStream(Files.newInputStream(new File(zipPath).toPath()));
                } catch (IOException | ExportErrorException e) {
                    // TODO This currently requires the user to click the "Export" button again or reload the page as the UI
                    // is not automatically updated; this should be changed once Vaadin push is enabled (see #516)
                    databaseExportView.showExportErrorNotification();
                    return null;
                }
            }
        }, fileName);
        streamResource.setMIMEType(mimeType);
        streamResource.setCacheTime(0);
        return streamResource;
    }

    public static StreamResource createGridExportStreamResource(Indexed container, List<Column> columns,
            String tempFilePrefix, String fileName, String... ignoredPropertyIds) {
        return new V7GridExportStreamResource(container, columns, tempFilePrefix, fileName, ignoredPropertyIds);
    }

    @SuppressWarnings("serial")
    public static StreamResource createFileStreamResource(String filePath, String fileName, String mimeType,
            String errorTitle, String errorText) {
        StreamResource streamResource = new StreamResource(new StreamSource() {
            @Override
            public InputStream getStream() {
                try {
                    return new BufferedInputStream(Files.newInputStream(new File(filePath).toPath()));
                } catch (IOException e) {
                    // TODO This currently requires the user to click the "Export" button again or reload the page as the UI
                    // is not automatically updated; this should be changed once Vaadin push is enabled (see #516)
                    new Notification(errorTitle, errorText, Type.ERROR_MESSAGE, false).show(Page.getCurrent());
                    return null;
                }
            }
        }, fileName);
        streamResource.setMIMEType(mimeType);
        streamResource.setCacheTime(0);
        return streamResource;
    }

    @SuppressWarnings("serial")
    public static StreamResource createStringStreamResource(String content, String fileName, String mimeType) {
        StreamResource streamResource = new StreamResource(new StreamSource() {
            @Override
            public InputStream getStream() {
                return new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8));
            }
        }, fileName);
        streamResource.setMIMEType(mimeType);
        return streamResource;
    }

    @SuppressWarnings("serial")
    public static <T> StreamResource createCsvExportStreamResource(Class<T> exportRowClass,
            BiFunction<Integer, Integer, List<T>> exportRowsSupplier,
            BiFunction<String, Class<?>, String> propertyIdCaptionFunction, String exportFileName) {
        StreamResource extendedStreamResource = new StreamResource(new StreamSource() {
            @Override
            public InputStream getStream() {
                try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream()) {
                    try (CSVWriter writer = CSVUtils.createCSVWriter(
                            new OutputStreamWriter(byteStream, StandardCharsets.UTF_8.name()),
                            FacadeProvider.getConfigFacade().getCsvSeparator())) {

                        // 1. fields in order of declaration - not using Introspector here, because it gives properties in alphabetical order
                        List<Method> readMethods = new ArrayList<Method>();
                        readMethods.addAll(Arrays.stream(exportRowClass.getDeclaredMethods())
                                .filter(m -> (m.getName().startsWith("get") || m.getName().startsWith("is"))
                                        && m.isAnnotationPresent(Order.class))
                                .sorted((a, b) -> Integer.compare(a.getAnnotationsByType(Order.class)[0].value(),
                                        b.getAnnotationsByType(Order.class)[0].value()))
                                .collect(Collectors.toList()));

                        // 2. replace entity fields with all the columns of the entity 
                        Map<Method, Function<T, ?>> subEntityProviders = new HashMap<Method, Function<T, ?>>();
                        for (int i = 0; i < readMethods.size(); i++) {
                            Method method = readMethods.get(i);
                            if (EntityDto.class.isAssignableFrom(method.getReturnType())) {

                                // allows us to access the sub entity
                                Function<T, ?> subEntityProvider = (o) -> {
                                    try {
                                        return method.invoke(o);
                                    } catch (IllegalAccessException | IllegalArgumentException
                                            | InvocationTargetException e) {
                                        throw new RuntimeException(e);
                                    }
                                };

                                // remove entity field
                                readMethods.remove(i);

                                // add columns of the entity
                                List<Method> subReadMethods = Arrays
                                        .stream(method.getReturnType().getDeclaredMethods())
                                        .filter(m -> (m.getName().startsWith("get") || m.getName().startsWith("is"))
                                                && m.isAnnotationPresent(Order.class))
                                        .sorted((a, b) -> Integer.compare(
                                                a.getAnnotationsByType(Order.class)[0].value(),
                                                b.getAnnotationsByType(Order.class)[0].value()))
                                        .collect(Collectors.toList());
                                readMethods.addAll(i, subReadMethods);
                                i--;

                                for (Method subReadMethod : subReadMethods) {
                                    subEntityProviders.put(subReadMethod, subEntityProvider);
                                }
                            }
                        }

                        String[] fieldValues = new String[readMethods.size()];
                        for (int i = 0; i < readMethods.size(); i++) {
                            Method method = readMethods.get(i);
                            // field caption
                            String propertyId = method.getName().startsWith("get") ? method.getName().substring(3)
                                    : method.getName().substring(2);
                            propertyId = Character.toLowerCase(propertyId.charAt(0)) + propertyId.substring(1);
                            fieldValues[i] = propertyIdCaptionFunction.apply(propertyId, method.getReturnType());
                        }
                        writer.writeNext(fieldValues);

                        int startIndex = 0;
                        List<T> exportRows = exportRowsSupplier.apply(startIndex, DETAILED_EXPORT_STEP_SIZE);
                        while (!exportRows.isEmpty()) {
                            try {
                                for (T exportRow : exportRows) {
                                    for (int i = 0; i < readMethods.size(); i++) {
                                        Method method = readMethods.get(i);
                                        Function<T, ?> subEntityProvider = subEntityProviders.getOrDefault(method,
                                                null);
                                        Object entity = subEntityProvider != null
                                                ? subEntityProvider.apply(exportRow)
                                                : exportRow;
                                        // Sub entity might be null
                                        Object value = entity != null ? method.invoke(entity) : null;
                                        if (value == null) {
                                            fieldValues[i] = "";
                                        } else if (value instanceof Date) {
                                            fieldValues[i] = DateHelper.formatLocalShortDate((Date) value);
                                        } else if (value.getClass().equals(boolean.class)
                                                || value.getClass().equals(Boolean.class)) {
                                            fieldValues[i] = DataHelper.parseBoolean((Boolean) value);
                                        } else if (value instanceof Set) {
                                            StringBuilder sb = new StringBuilder();
                                            for (Object o : (Set<?>) value) {
                                                if (sb.length() != 0) {
                                                    sb.append(", ");
                                                }
                                                sb.append(o);
                                            }
                                            fieldValues[i] = sb.toString();
                                        } else {
                                            fieldValues[i] = value.toString();
                                        }
                                    }
                                    writer.writeNext(fieldValues);
                                }
                                ;
                            } catch (InvocationTargetException | IllegalAccessException
                                    | IllegalArgumentException e) {
                                throw new RuntimeException(e);
                            }

                            writer.flush();
                            startIndex += DETAILED_EXPORT_STEP_SIZE;
                            exportRows = exportRowsSupplier.apply(startIndex, DETAILED_EXPORT_STEP_SIZE);
                        }
                    }
                    return new BufferedInputStream(new ByteArrayInputStream(byteStream.toByteArray()));
                } catch (IOException e) {
                    // TODO This currently requires the user to click the "Export" button again or reload the page as the UI
                    // is not automatically updated; this should be changed once Vaadin push is enabled (see #516)
                    new Notification(I18nProperties.getString(Strings.headingExportFailed),
                            I18nProperties.getString(Strings.messageExportFailed), Type.ERROR_MESSAGE, false)
                                    .show(Page.getCurrent());
                    return null;
                }
            }
        }, exportFileName);
        extendedStreamResource.setMIMEType("text/csv");
        extendedStreamResource.setCacheTime(0);
        return extendedStreamResource;
    }

}