package name.abuchen.portfolio.ui.util.chart; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.StandardCharsets; import java.text.DateFormat; import java.text.DecimalFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashSet; import java.util.Set; import name.abuchen.portfolio.ui.Messages; import name.abuchen.portfolio.ui.util.AbstractCSVExporter; import org.apache.commons.csv.CSVPrinter; import org.eclipse.swt.widgets.Control; import org.swtchart.ISeries; public class TimelineChartCSVExporter extends AbstractCSVExporter { private final TimelineChart chart; private Set<String> discontinousSeries = new HashSet<String>(); private DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); //$NON-NLS-1$ private NumberFormat valueFormat = new DecimalFormat("#,##0.00"); //$NON-NLS-1$ public TimelineChartCSVExporter(TimelineChart viewer) { this.chart = viewer; } public void setDateFormat(DateFormat dateFormat) { this.dateFormat = dateFormat; } public void setValueFormat(NumberFormat valueFormat) { this.valueFormat = valueFormat; } public void addDiscontinousSeries(String seriesId) { discontinousSeries.add(seriesId); } @Override protected Control getControl() { return chart; } @Override protected void writeToFile(File file) throws IOException { try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) { CSVPrinter printer = new CSVPrinter(writer); printer.setStrategy(STRATEGY); ISeries[] series = chart.getSeriesSet().getSeries(); // write header printer.print(Messages.ColumnDate); for (ISeries s : series) printer.print(s.getId()); printer.println(); // write body Date[] dateSeries = series[0].getXDateSeries(); SeriesAdapter[] adapters = new SeriesAdapter[series.length]; for (int ii = 0; ii < series.length; ii++) { if (discontinousSeries.contains(series[ii].getId())) adapters[ii] = new DiscontinousAdapter(series[ii]); else adapters[ii] = new DefaultAdapter(series[ii]); } for (int line = 0; line < dateSeries.length; line++) { printer.print(dateFormat.format(dateSeries[line])); for (int col = 0; col < adapters.length; col++) printer.print(adapters[col].format(dateSeries[line], line)); printer.println(); } } } private interface SeriesAdapter { String format(Date date, int line); } private class DefaultAdapter implements SeriesAdapter { private double[] values; public DefaultAdapter(ISeries series) { this.values = series.getYSeries(); } @Override public String format(Date date, int line) { // benchmark data series might not have all values if (line >= values.length) return ""; //$NON-NLS-1$ return valueFormat.format(values[line]); } } private class DiscontinousAdapter implements SeriesAdapter { private Date[] dates; private double[] values; private int next = 0; public DiscontinousAdapter(ISeries series) { this.dates = series.getXDateSeries(); this.values = series.getYSeries(); } @Override public String format(Date date, int line) { if (next >= dates.length) return ""; //$NON-NLS-1$ if (date.getTime() != dates[next].getTime()) return ""; //$NON-NLS-1$ return valueFormat.format(values[next++]); } } }