package au.com.vaadinutils.crud;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jsoup.Jsoup;
import org.jsoup.examples.HtmlToPlainText;
import com.vaadin.data.Item;
import com.vaadin.data.Property;
import com.vaadin.server.FileDownloader;
import com.vaadin.server.StreamResource;
import com.vaadin.server.StreamResource.StreamSource;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Grid;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Notification;
import com.vaadin.ui.UI;
import com.vaadin.ui.Window;
import au.com.bytecode.opencsv.CSVWriter;
import au.com.vaadinutils.jasper.AttachmentType;
import au.com.vaadinutils.util.PipedOutputStreamWrapper;
public class GridContainerCSVExport<E>
{
PipedOutputStreamWrapper stream = new PipedOutputStreamWrapper();
Logger logger = LogManager.getLogger();
private GridHeadingPropertySet headingsSet;
private Grid grid;
private LinkedHashMap<String, Object> extraColumnHeadersAndPropertyIds;
public GridContainerCSVExport(final String fileName, final Grid grid, final GridHeadingPropertySet headingsSet)
{
this.grid = grid;
this.headingsSet = headingsSet;
final Window window = new Window();
window.setCaption("Download " + fileName + " CSV data");
window.center();
window.setHeight("100");
window.setWidth("300");
window.setResizable(false);
window.setModal(true);
final HorizontalLayout layout = new HorizontalLayout();
layout.setSizeFull();
layout.setMargin(true);
window.setContent(layout);
UI.getCurrent().addWindow(window);
window.setVisible(true);
final Button downloadButton = createDownloadButton(fileName, window);
layout.addComponent(downloadButton);
layout.setComponentAlignment(downloadButton, Alignment.MIDDLE_CENTER);
}
private Button createDownloadButton(final String fileName, final Window window)
{
final Button downloadButton = new Button("Download CSV Data");
downloadButton.setDisableOnClick(true);
@SuppressWarnings("serial")
StreamSource source = new StreamSource()
{
@Override
public InputStream getStream()
{
try
{
ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(arrayOutputStream));
export(grid, bufferedWriter, headingsSet);
return new ByteArrayInputStream(arrayOutputStream.toByteArray());
}
catch (Throwable e)
{
logger.error(e, e);
Notification.show(e.getMessage());
}
finally
{
Runnable runner = new Runnable()
{
@Override
public void run()
{
try
{
Thread.sleep(500);
UI.getCurrent().access(new Runnable()
{
@Override
public void run()
{
window.close();
}
});
}
catch (InterruptedException e)
{
logger.error(e, e);
}
}
};
new Thread(runner, "Dialog closer").start();
}
return null;
}
};
StreamResource resource = new StreamResource(source, fileName + AttachmentType.CSV.getFileExtension());
resource.setMIMEType(AttachmentType.CSV.getMIMETypeString());
FileDownloader fileDownloader = new FileDownloader(resource);
fileDownloader.setOverrideContentType(false);
fileDownloader.extend(downloadButton);
return downloadButton;
}
public void export(Grid grid, Writer stream, GridHeadingPropertySet headingsSet) throws IOException
{
CSVWriter writer = new CSVWriter(stream);
Map<String, Object> headerPropertyMap = new LinkedHashMap<>();
List<GridHeadingToPropertyId> cols = headingsSet.getColumns();
for (GridHeadingToPropertyId col : cols)
{
headerPropertyMap.put(col.getHeader(), col.getPropertyId());
}
List<String> headerList = new LinkedList<>();
headerList.addAll(headerPropertyMap.keySet());
extraColumnHeadersAndPropertyIds = getExtraColumnHeadersAndPropertyIds();
headerList.addAll(extraColumnHeadersAndPropertyIds.keySet());
writeHeaders(writer, headerList);
Set<Object> properties = new LinkedHashSet<>();
properties.addAll(headerPropertyMap.values());
for (Object id : grid.getContainerDataSource().getItemIds())
{
writeRow(writer, grid, id, properties);
}
writer.flush();
}
private void writeRow(CSVWriter writer, Grid grid, Object id, Set<Object> properties)
{
Item item = grid.getContainerDataSource().getItem(id);
String[] values = new String[properties.size() + extraColumnHeadersAndPropertyIds.size()];
int i = 0;
for (Object propertyId : properties)
{
@SuppressWarnings("rawtypes")
final Property itemProperty = item.getItemProperty(propertyId);
if (itemProperty != null)
{
Object value = itemProperty.getValue();
if (value != null)
{
final Object convertedValue = convert(value);
if (convertedValue != null)
{
values[i++] = sanitiseValue(convertedValue);
}
else
{
values[i++] = "";
}
}
else
{
values[i++] = "";
}
}
}
for (Object columnId : extraColumnHeadersAndPropertyIds.values())
{
String value = getValueForExtraColumn(item, columnId);
if (value == null)
{
value = "";
}
values[i++] = value;
}
writer.writeNext(values);
}
public Object convert(Object value)
{
return value;
}
public String sanitiseValue(final Object value)
{
String sanitisedValue;
if (value instanceof String)
{
sanitisedValue = new HtmlToPlainText().getPlainText(Jsoup.parse(value.toString()));
}
else
{
sanitisedValue = value.toString();
}
if (sanitisedValue == null)
{
sanitisedValue = "";
}
return sanitisedValue;
}
private void writeHeaders(CSVWriter writer, List<String> headers)
{
writer.writeNext(headers.toArray(new String[]
{}));
}
/**
* propertyId's will later be passed to getValueForExtraColumn so it can
* generate the data for a column
*
* @return - an ordered map where key=heading string and value = a unique
* propertyId
*/
protected LinkedHashMap<String, Object> getExtraColumnHeadersAndPropertyIds()
{
return new LinkedHashMap<>();
}
/**
*
* @param item
* @param columnId
* - as specified in the map returned from
* getExtraColumnHeadersAndPropertyIds()
* @return
*/
protected String getValueForExtraColumn(Item item, Object columnId)
{
return null;
}
}