| /******************************************************************************* |
| * 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<>(); |
| } |
| |
| /** |
| * 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<>(); |
| 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(); |
| } |
| } |
| |
| } |