org.noroomattheinn.timeseries.InMemoryTS.java Source code

Java tutorial

Introduction

Here is the source code for org.noroomattheinn.timeseries.InMemoryTS.java

Source

/*
 * InMemoryTS.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.util.ArrayList;
import java.util.List;
import java.util.NavigableMap;
import java.util.TreeMap;

/**
 * InMemoryTS: In-Memory Time Series
 * 
 * @author Joe Pasqua <joe at NoRoomAtTheInn dot org>
 */
public class InMemoryTS extends TSBase implements IndexedTimeSeries {
    /*------------------------------------------------------------------------------
     *
     * Internal State
     * 
     *----------------------------------------------------------------------------*/

    private final NavigableMap<Long, Row> index;
    private final List<Row> rows;
    private final boolean forceOrdering;

    /*==============================================================================
     * -------                                                               -------
     * -------              Public Interface To This Class                   ------- 
     * -------                                                               -------
     *============================================================================*/

    /**
     * Create an In-Memory Time Series store
     * 
     * @param descriptor    Describes the schema of the rows in the store
     * @param forceOrdering If true, then all data added to the time series
     *                      will be forced to have monotonically increasing
     *                      timestamps. If a row or value is added whose time-
     *                      stamp is less than a value that has already been
     *                      added, the newer timestamp will be used.
     *                      If false, an old timestamp will result in an
     *                      IllegalArgumentException
     */
    public InMemoryTS(RowDescriptor descriptor, boolean forceOrdering) {
        super(descriptor);
        this.index = new TreeMap<>();
        this.rows = new ArrayList<>();
        this.forceOrdering = forceOrdering;

        // The code assumes there is always a "previous" row, so create a
        // zero-th row that serves as a backstop. It's never presented as
        // part of the actual data
        rows.add(new Row(0, 0, descriptor.nColumns));
    }

    /*------------------------------------------------------------------------------
     *
     * Methods overriden from TimeSeries
     * 
     *----------------------------------------------------------------------------*/

    @Override
    public Row storeRow(Row rowToStore) throws IllegalArgumentException {
        Row existingRow = rows.get(rows.size() - 1);
        int nColumns = rowToStore.values.length;
        long newTime = adjustTimeIfNeeded(rowToStore.timestamp, existingRow.timestamp);

        if (newTime == existingRow.timestamp) {
            // Merge this row into existingRow
            logger.info("Merging rows at time: " + newTime);
            long bit = 1;
            for (int i = 0; i < nColumns; i++) {
                if (rowToStore.includes(bit)) {
                    existingRow.values[i] = rowToStore.values[i];
                    existingRow.bitVector |= bit;
                }
                bit = bit << 1;
            }
            return existingRow;
        } else {
            // Create new row based on the existing values
            Row newRow = new Row(newTime, rowToStore.bitVector, existingRow.values);

            // Now set the values given by rowToStore
            long bit = 1;
            for (int i = 0; i < nColumns; i++) {
                if (rowToStore.includes(bit)) {
                    newRow.values[i] = rowToStore.values[i];
                }
                bit = bit << 1;
            }
            rows.add(newRow);
            index.put(rowToStore.timestamp, newRow);
            return newRow;
        }
    }

    @Override
    public void streamRows(Range<Long> period, RowCollector collector) {
        NavigableMap<Long, Row> subMap = getIndex(period);
        for (Row row : subMap.values()) {
            if (!collector.collect(row))
                return;
        }
    }

    @Override
    public long firstTime() {
        return (rows.size() == 1) ? Long.MAX_VALUE : rows.get(1).timestamp;
    }

    @Override
    public void close() {
    }

    @Override
    public void flush() {
    }

    /*------------------------------------------------------------------------------
     *
     * Methods overriden from IndexedTimeSeries
     * 
     *----------------------------------------------------------------------------*/

    @Override
    public NavigableMap<Long, Row> getIndex() {
        return index;
    }

    @Override
    public NavigableMap<Long, Row> getIndex(Range<Long> period) {
        long from = period.hasLowerBound() ? period.lowerEndpoint() : 0;
        long to = period.hasUpperBound() ? period.upperEndpoint() : Long.MAX_VALUE;
        return index.subMap(from, true, to, true);
    }

    /*------------------------------------------------------------------------------
     *
     * Private Utility Methods
     * 
     *----------------------------------------------------------------------------*/

    private long adjustTimeIfNeeded(long newTime, long oldTime) {
        if (newTime >= oldTime)
            return newTime;
        if (forceOrdering) {
            logger.fine("Forcing timestamp: " + oldTime + ", " + newTime);
            return oldTime;
        } else {
            throw new IllegalArgumentException("Timestamps out of sequence: " + oldTime + ", " + newTime);
        }
    }

}