/* * TSBase.java - Copyright(c) 2014 Joe Pasqua * Provided under the MIT License. See the LICENSE file for details. * Created: Nov 25, 2014 */ package org.noroomattheinn.timeseries; import com.google.common.collect.Range; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.logging.Logger; import jxl.Workbook; import jxl.format.Colour; import jxl.write.DateFormat; import jxl.write.Label; import jxl.write.NumberFormats; import jxl.write.WritableCellFormat; import jxl.write.WritableSheet; import jxl.write.WritableWorkbook; import jxl.write.WriteException; /** * TSBase: Simple base class for all TimeSeries implementations. * * @author Joe Pasqua <joe at NoRoomAtTheInn dot org> */ public abstract class TSBase implements TimeSeries { /*------------------------------------------------------------------------------ * * Constants and Enums * *----------------------------------------------------------------------------*/ protected static final Logger logger = Logger.getLogger("org.noroomattheinn.timeseries"); /*------------------------------------------------------------------------------ * * Internal State * *----------------------------------------------------------------------------*/ protected final RowDescriptor schema; /*============================================================================== * ------- ------- * ------- Public Interface To This Class ------- * ------- ------- *============================================================================*/ TSBase(RowDescriptor rd) { this.schema = rd; } /*------------------------------------------------------------------------------ * * Methods overriden from TimeSeries * *----------------------------------------------------------------------------*/ @Override public synchronized void loadInto(final TimeSeries ts, Range<Long> period) { streamRows(period, new RowCollector() { @Override public boolean collect(Row r) { ts.storeRow(r); return true; } }); } @Override public synchronized void streamValues( Range<Long> period, final ValueCollector collector) { streamRows(period, new RowCollector() { @Override public boolean collect(Row r) { long timestamp = r.timestamp; for (String c:schema.columnNames) { long bit = schema.bitForColumn(c); if (r.includes(bit)) { collector.collect(timestamp, c, r.values[Row.indexForBit(bit)]); } } return true; } }); } @Override public boolean export( File toFile, Range<Long> exportPeriod, List<String> columns, boolean includeDerived) { if (columns == null) columns = new ArrayList<>(Arrays.asList(schema.columnNames)); try { final WritableWorkbook workbook = Workbook.createWorkbook(toFile); final WritableSheet sheet = workbook.createSheet("Sheet1", 0); addTableHeader(sheet, columns); // Run through the table and add each row... streamRows(exportPeriod, new RowHandler(sheet, 1, columns, includeDerived)); workbook.write(); workbook.close(); return true; } catch (IOException | WriteException ex) { logger.warning("Failure exporting repo: " + ex); return false; } } @Override public RowDescriptor getSchema() { return schema; } /*------------------------------------------------------------------------------ * * PRIVATE - Support code for exporting to Excel format * *----------------------------------------------------------------------------*/ private class RowHandler implements RowCollector { // Don't make these static! final WritableCellFormat dateFormat = new WritableCellFormat(new DateFormat("m/d/yy hh:mm:ss")); final WritableCellFormat integerFormat = new WritableCellFormat(NumberFormats.INTEGER); final WritableCellFormat lightGrayCell = new WritableCellFormat(); private final WritableSheet sheet; private final boolean includeDerived; private final List<String> columns; private final int nColumnsToInclude; private final long columnsIncluded; private int rNum; RowHandler( WritableSheet sheet, int initialRow, List<String>columns, boolean includeDerived) { this.sheet = sheet; this.rNum = initialRow; this.columns = columns; this.nColumnsToInclude = columns.size(); this.includeDerived = includeDerived; this.columnsIncluded = bitVectorForColumns(); try { lightGrayCell.setBackground(Colour.GREY_25_PERCENT); } catch (WriteException ex) { logger.warning("Can't Happen: " + ex); } } @Override public boolean collect(Row row) { // Don't bother with rows that have only derived values if ((row.bitVector & columnsIncluded) == 0L) return true; try { jxl.write.Number timeCell = new jxl.write.Number( 0, rNum, row.timestamp, integerFormat); sheet.addCell(timeCell); int cNum = 1; for (String c:schema.columnNames) { if (columns.contains(c)) { long bit = schema.bitForColumn(c); int valueIndex = Row.indexForBit(bit); boolean derived = row.excludes(bit); double val = (!derived || includeDerived) ? row.values[valueIndex] : 0; jxl.write.Number cell = new jxl.write.Number(cNum, rNum, val); sheet.addCell(cell); if (derived) { cell.setCellFormat(lightGrayCell); } cNum++; } } sheet.addCell(new jxl.write.DateTime( nColumnsToInclude+1, rNum, new Date(row.timestamp), dateFormat)); rNum++; return true; } catch (WriteException e) { logger.warning("Export failed with: " + e); return false; } } private long bitVectorForColumns() { long bitVector = 0; for (String c:columns) { bitVector |= schema.bitForColumn(c); } return bitVector; } } private void addTableHeader(WritableSheet sheet, List<String> columns) throws WriteException { // Start with the timestamp column sheet.addCell(new Label(0, 0, "Timestamp")); sheet.setColumnView(0, 14); // Big enough for a timestamp; // Now handle the data columns int columnNumber = 1; for (int i = 0; i < schema.nColumns; i++) { String c = schema.columnNames[i]; if (columns.contains(c)) { sheet.addCell(new Label(columnNumber++, 0, c)); } } // Now add the Date column int dateColumn = columns.size() + 1; sheet.addCell(new Label(dateColumn, 0, "Date")); sheet.setColumnView(dateColumn, 16); // Big enough for a Date string; // Make the header row stationary sheet.getSettings().setVerticalFreeze(1); } }