blob: b400ec043928faec155ea072e715671f2995b4e2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2020 Original authors and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Original authors and others - initial API and implementation
******************************************************************************/
package org.eclipse.nebula.widgets.nattable.blink;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.nebula.widgets.nattable.data.IRowIdAccessor;
import org.eclipse.nebula.widgets.nattable.layer.event.PropertyUpdateEvent;
/**
* Cache for the update events coming in.
*
* This cache is used by the {@link BlinkLayer} to check if updates are
* available for a cell (hence, does it need to blink).
*
* @param <T>
* Type of the Bean in the backing list.
*/
public class UpdateEventsCache<T> {
/** Initial startup delay for the expired event removal task */
public static final long INITIAL_DELAY = 100;
/** TTL for an event in the cache. The event is deleted when this expires */
public static final long TIME_TO_LIVE = 500;
private final IRowIdAccessor<T> rowIdAccessor;
private final KeyStrategy keyStrategy;
private final ScheduledExecutorService cleanupScheduler;
private Map<String, TimeStampedEvent> updateEvents;
private ScheduledFuture<?> scheduledFutureCleanup;
public UpdateEventsCache(IRowIdAccessor<T> rowIdAccessor,
KeyStrategy keyStrategy, ScheduledExecutorService cleanupScheduler) {
this.rowIdAccessor = rowIdAccessor;
this.keyStrategy = keyStrategy;
this.cleanupScheduler = cleanupScheduler;
this.updateEvents = new HashMap<String, TimeStampedEvent>();
}
/**
* We are not interested in update events which are too old and need not be
* blinked. This task cleans them up, by looking at the received time stamp.
*/
private Runnable getStaleUpdatesCleanupTask() {
return () -> {
Map<String, TimeStampedEvent> recentEvents = new HashMap<String, TimeStampedEvent>();
Date recent = new Date(System.currentTimeMillis()
- TIME_TO_LIVE);
for (Map.Entry<String, TimeStampedEvent> entry : UpdateEventsCache.this.updateEvents
.entrySet()) {
if (entry.getValue().timeRecieved.after(recent)) {
recentEvents.put(entry.getKey(), entry.getValue());
}
}
synchronized (UpdateEventsCache.this.updateEvents) {
UpdateEventsCache.this.updateEvents = recentEvents;
checkUpdateEvents();
}
};
}
private void checkUpdateEvents() {
if (this.updateEvents.isEmpty()) {
if (this.scheduledFutureCleanup != null) {
this.scheduledFutureCleanup.cancel(true);
this.scheduledFutureCleanup = null;
}
} else {
if (this.scheduledFutureCleanup == null) {
this.scheduledFutureCleanup = this.cleanupScheduler.scheduleAtFixedRate(
getStaleUpdatesCleanupTask(), INITIAL_DELAY,
TIME_TO_LIVE, TimeUnit.MILLISECONDS);
}
}
}
public void put(PropertyUpdateEvent<T> event) {
String key = getKey(event);
this.updateEvents.put(key, new TimeStampedEvent(event));
checkUpdateEvents();
}
protected String getKey(PropertyUpdateEvent<T> event) {
String rowId = this.rowIdAccessor.getRowId(event.getSourceBean()).toString();
return getKey(event.getPropertyName(), rowId);
}
public String getKey(String columnProperty, String rowId) {
return this.keyStrategy.getKey(columnProperty, rowId);
}
public PropertyUpdateEvent<T> getEvent(String key) {
return this.updateEvents.get(key).event;
}
public int getCount() {
return this.updateEvents.size();
}
public boolean contains(String columnProperty, String rowId) {
return this.updateEvents.containsKey(getKey(columnProperty, rowId));
}
public boolean isUpdated(String key) {
return this.updateEvents.containsKey(key);
}
public void clear() {
this.updateEvents.clear();
checkUpdateEvents();
}
public void remove(String key) {
this.updateEvents.remove(key);
checkUpdateEvents();
}
/**
* Class to keep track of the time when an event was received
*/
private class TimeStampedEvent {
Date timeRecieved;
PropertyUpdateEvent<T> event;
public TimeStampedEvent(PropertyUpdateEvent<T> event) {
this.event = event;
this.timeRecieved = new Date();
}
}
}