/* * Copyright (C) 2012, Katy Hilgenberg. * Special acknowledgments to: Knowledge & Data Engineering Group, University of Kassel (http://www.kde.cs.uni-kassel.de). * Contact: sdcf@cs.uni-kassel.de * * This file is part of the SDCFramework project. * * The SDCFramework is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The SDCFramework 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the SDCFramework. If not, see <http://www.gnu.org/licenses/>. */ package de.unikassel.android.sdcframework.demo.related.util; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import de.unikassel.android.sdcframework.data.independent.BasicSample; import de.unikassel.android.sdcframework.data.independent.SampleData; import de.unikassel.android.sdcframework.util.ObservableEventSourceImpl; /** * Base class for observable sample sources. * * @author Katy Hilgenberg * @param <T> * the observable event type * @param <U> * the comparable type */ public abstract class ObservableSampleSource< T extends SortedSampleCollection< U >, U extends Comparable< ? super U > > extends ObservableEventSourceImpl< T > { /** * The maximum time difference to store samples */ public final long maxTimeDiffToKeepSamples; /** * The queue to store samples grouped by its time stamp */ private final List< T > listSampleCollections; /** * The current frequency in milliseconds */ private final AtomicLong frequency = new AtomicLong(); /** * Time stamp of the last update */ private final AtomicLong lastUpdateTime = new AtomicLong(); /** * The count of most recent sample time stamps for merged results */ protected static final int MAX_MERGE_COUNT = 3; /** * Constructor * * @param maxTimeDiffToKeepSamples * the maximum time difference in milliseconds to keep samples */ public ObservableSampleSource( long maxTimeDiffToKeepSamples ) { super(); this.maxTimeDiffToKeepSamples = maxTimeDiffToKeepSamples; this.listSampleCollections = Collections.synchronizedList( new LinkedList< T >() ); } /** * Getter for the current frequency in milliseconds * * @return the current frequency in milliseconds */ public long getFrequency() { return frequency.get(); } /** * Setter for the current frequency in milliseconds * * @param frequency * the current frequency in milliseconds */ protected void setFrequency( long frequency ) { this.frequency.set( frequency ); } /** * Getter for the last update time * * @return the last update time */ public long getLastUpdateTime() { return lastUpdateTime.get(); } /** * Setter for the last update time * * @param lastUpdateTime * the last update time to set */ public void setLastUpdateTime( long lastUpdateTime ) { this.lastUpdateTime.set( lastUpdateTime ); } /** * Getter for the maximum time difference in milliseconds to keep samples * @return the maximum time difference to keep samples */ public long getMaxTimeDiffToKeepSamples() { return maxTimeDiffToKeepSamples; } /** * Method to prune the list with collected sample sets * * @param actualTS * the actual time stamp */ protected final synchronized void pruneList( long actualTS ) { int size = listSampleCollections.size() - 1; int pos = -1; // prune the sample history while ( pos < size ) { T currentSet = listSampleCollections.get( pos + 1 ); if ( ( actualTS - currentSet.getTs() ) <= maxTimeDiffToKeepSamples ) break; pos++; } if ( pos >= 0 ) { listSampleCollections.subList( 0, pos ).clear(); } } /** * Preparation for a new sample * * @param sample * the sample to prepare * @return the collection to add the sample too */ protected T prepareSample( BasicSample sample ) { long ts = sample.getTimeStamp(); // prepare int size = listSampleCollections.size(); T currentSamples = size > 0 ? listSampleCollections.get( size - 1 ) : null; if ( currentSamples == null || ts > currentSamples.getTs() ) { // create new sample collection for the new time stamp if ( currentSamples != null ) setFrequency( ts - currentSamples.getTs() ); currentSamples = newCollection( ts ); listSampleCollections.add( currentSamples ); pruneList( ts ); notify( currentSamples ); } return currentSamples; } /** * Method to create a new collection * * @param ts * the time stamp for the new collection * @return a new collection for the type T */ protected abstract T newCollection( long ts ); /** * Method to create a new collection * * @param collection * the collection to clone from * @return a new collection for the type T */ protected abstract T newCollection( T collection ); /** * Method to add samples * * @param sample * the new sample */ public final void addSample( BasicSample sample ) { if ( !testForSampleDataType( sample.getData() ) ) return; T currentSamples = prepareSample( sample ); addSample( currentSamples, sample ); setLastUpdateTime( System.currentTimeMillis() ); } /** * Method to add a new sample to the collection * * @param currentSamples * the current sample list * @param sample * the sample data to wrap and add to the list */ protected abstract void addSample( T currentSamples, BasicSample sample ); /** * Method to test for sample data type * * @param data * the sample data * @return true if type is correct */ protected abstract boolean testForSampleDataType( SampleData data ); /** * Method to get the most recent sample * * @param refreshInterval * the time difference in milliseconds for which the samples have to * be regarded as recent * @return the most recent samples */ public final synchronized T getMostRecentSamples( long refreshInterval ) { if ( ( System.currentTimeMillis() - getLastUpdateTime() ) < refreshInterval ) { // merge collected sample set backwards to keep most recent samples int pos = listSampleCollections.size() - 1; int lastPos = Math.max( 0, pos - MAX_MERGE_COUNT ); if ( pos >= lastPos ) { T currentSet = listSampleCollections.get( pos ); // clone the most recent set T result = newCollection( currentSet ); pos--; // merge with older sets up to specified position while ( pos >= 0 ) { currentSet = listSampleCollections.get( pos ); result.addAll( currentSet ); pos--; } // update sample rate return result; } } return newCollection( 0L ); } /** * Method to get the stored sample time line * * @return the most recent sample */ public final List< T > getSampleTimeLine() { return listSampleCollections; } }