org.eclipse.nebula.widgets.nattable.blink.BlinkLayer.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.nebula.widgets.nattable.blink.BlinkLayer.java

Source

/*******************************************************************************
 * Copyright (c) 2012, 2013 Original authors and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Original authors and others - initial API and implementation
 ******************************************************************************/
package org.eclipse.nebula.widgets.nattable.blink;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang.ArrayUtils;
import org.eclipse.nebula.widgets.nattable.blink.command.BlinkTimerEnableCommandHandler;
import org.eclipse.nebula.widgets.nattable.blink.event.BlinkEvent;
import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry;
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.data.IColumnPropertyResolver;
import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
import org.eclipse.nebula.widgets.nattable.data.IRowDataProvider;
import org.eclipse.nebula.widgets.nattable.data.IRowIdAccessor;
import org.eclipse.nebula.widgets.nattable.layer.AbstractLayerTransform;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;
import org.eclipse.nebula.widgets.nattable.layer.LabelStack;
import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent;
import org.eclipse.nebula.widgets.nattable.layer.event.PropertyUpdateEvent;
import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
import org.eclipse.swt.widgets.Display;

/**
 * Blinks cells when they are updated.
 * Returns blinking cell styles for the cells which have been updated.
 *
 * Every time its asked for config labels:
 *     Checks the UpdateEventsCache for changes to the cell
 *     If a cell is updated
 *       The cell is tracked as 'blinking' and blinking config labels are returned
 *      A TimerTask is started which will stop the blinking after the blink period is over
 *
 * @param <T> Type of the Bean in the backing {@linkplain IDataProvider}
 */
public class BlinkLayer<T> extends AbstractLayerTransform implements IUniqueIndexLayer {

    private static class DefaultSchedulerLazyInitializer {
        private final static ScheduledExecutorService SCHEDULER = Executors.newSingleThreadScheduledExecutor();
    }

    private final IUniqueIndexLayer dataLayer;
    private final IRowDataProvider<T> rowDataProvider;
    private final IConfigRegistry configRegistry;
    private final IRowIdAccessor<T> rowIdAccessor;
    private final IColumnPropertyResolver columnPropertyResolver;
    private final ScheduledExecutorService scheduler;

    protected boolean blinkingEnabled = true;

    /** Cache all the update events allowing the layer to track what got updated */
    private final UpdateEventsCache<T> updateEventsCache;

    /** Duration of a single blink */
    private int blinkDurationInMilis = 1000;

    /** Track the updates which are currently blinking */
    Map<String, PropertyUpdateEvent<T>> blinkingUpdates = new HashMap<String, PropertyUpdateEvent<T>>();

    /** Track the blinking tasks which are currently running*/
    Map<String, ScheduledFuture<?>> blinkingTasks = new HashMap<String, ScheduledFuture<?>>();

    public BlinkLayer(IUniqueIndexLayer dataLayer, IRowDataProvider<T> listDataProvider,
            IRowIdAccessor<T> rowIdAccessor, IColumnPropertyResolver columnPropertyResolver,
            IConfigRegistry configRegistry) {
        this(dataLayer, listDataProvider, rowIdAccessor, columnPropertyResolver, configRegistry, false);
    }

    public BlinkLayer(IUniqueIndexLayer dataLayer, IRowDataProvider<T> listDataProvider,
            IRowIdAccessor<T> rowIdAccessor, IColumnPropertyResolver columnPropertyResolver,
            IConfigRegistry configRegistry, boolean triggerBlinkOnRowUpdate) {
        this(dataLayer, listDataProvider, rowIdAccessor, columnPropertyResolver, configRegistry,
                triggerBlinkOnRowUpdate, DefaultSchedulerLazyInitializer.SCHEDULER);
    }

    public BlinkLayer(IUniqueIndexLayer dataLayer, IRowDataProvider<T> listDataProvider,
            IRowIdAccessor<T> rowIdAccessor, IColumnPropertyResolver columnPropertyResolver,
            IConfigRegistry configRegistry, boolean triggerBlinkOnRowUpdate, ScheduledExecutorService scheduler) {
        super(dataLayer);
        this.dataLayer = dataLayer;
        this.rowDataProvider = listDataProvider;
        this.rowIdAccessor = rowIdAccessor;
        this.columnPropertyResolver = columnPropertyResolver;
        this.configRegistry = configRegistry;
        this.scheduler = scheduler;
        this.updateEventsCache = new UpdateEventsCache<T>(rowIdAccessor,
                triggerBlinkOnRowUpdate ? new RowKeyStrategyImpl() : new CellKeyStrategyImpl(), scheduler);

        registerCommandHandler(new BlinkTimerEnableCommandHandler(this));
    }

    @Override
    public void dispose() {
        super.dispose();

        scheduler.shutdown();
    }

    @Override
    public LabelStack getConfigLabelsByPosition(int columnPosition, int rowPosition) {
        if (!blinkingEnabled) {
            return getUnderlyingLayer().getConfigLabelsByPosition(columnPosition, rowPosition);
        }

        ILayerCell cell = underlyingLayer.getCellByPosition(columnPosition, rowPosition);

        int columnIndex = getUnderlyingLayer().getColumnIndexByPosition(columnPosition);
        String columnProperty = columnPropertyResolver.getColumnProperty(columnIndex);

        int rowIndex = getUnderlyingLayer().getRowIndexByPosition(rowPosition);
        String rowId = rowIdAccessor.getRowId(rowDataProvider.getRowObject(rowIndex)).toString();

        String key = updateEventsCache.getKey(columnProperty, rowId);

        LabelStack underlyingLabelStack = getUnderlyingLayer().getConfigLabelsByPosition(columnPosition,
                rowPosition);

        // Cell has been updated
        if (updateEventsCache.isUpdated(key)) {
            PropertyUpdateEvent<T> event = updateEventsCache.getEvent(key);

            // Old update in middle of a blink - cancel it
            ScheduledFuture<?> scheduledFuture = blinkingTasks.remove(key);
            blinkingUpdates.remove(key);
            if (scheduledFuture != null) {
                scheduledFuture.cancel(true);
            }

            LabelStack blinkingConfigTypes = resolveConfigTypes(cell, event.getOldValue(), event.getNewValue());

            // start blinking cell
            if (blinkingConfigTypes != null) {
                Runnable stopBlinkTask = getStopBlinkTask(key, this);
                blinkingUpdates.put(key, event);
                updateEventsCache.remove(key);
                blinkingTasks.put(key,
                        scheduler.schedule(stopBlinkTask, blinkDurationInMilis, TimeUnit.MILLISECONDS));
                return blinkingConfigTypes;
            } else {
                return new LabelStack();
            }
        }
        // Previous blink timer is still running
        if (blinkingUpdates.containsKey(key)) {
            PropertyUpdateEvent<T> event = blinkingUpdates.get(key);
            return resolveConfigTypes(cell, event.getOldValue(), event.getNewValue());

        }
        return underlyingLabelStack;
    }

    private IBlinkingCellResolver getBlinkingCellResolver(List<String> configTypes) {
        return configRegistry.getConfigAttribute(BlinkConfigAttributes.BLINK_RESOLVER, DisplayMode.NORMAL,
                configTypes);
    }

    /**
     * Find the {@link IBlinkingCellResolver} from the {@link ConfigRegistry}.
     * Use this to find the config types associated with a blinking cell.
     * @param cell the cell
     * @param oldValue the old value
     * @param newValue the new value
     * @return a LabelStack containing resolved config types associated with the cell
     */
    public LabelStack resolveConfigTypes(ILayerCell cell, Object oldValue, Object newValue) {
        // Acquire default config types for the coordinate. Use these to search for the associated resolver.
        LabelStack underlyingLabelStack = underlyingLayer.getConfigLabelsByPosition(cell.getColumnIndex(),
                cell.getRowIndex());

        String[] blinkConfigTypes = null;
        IBlinkingCellResolver resolver = getBlinkingCellResolver(underlyingLabelStack.getLabels());
        if (resolver != null) {
            blinkConfigTypes = resolver.resolve(cell, configRegistry, oldValue, newValue);
        }
        if (!ArrayUtils.isEmpty(blinkConfigTypes)) { //blinkConfigTypes != null && blinkConfigTypes.length > 0
            return new LabelStack(blinkConfigTypes);
        }
        return underlyingLabelStack;
    }

    /**
     * Stops the cell from blinking at the end of the blinking period.
     */
    private Runnable getStopBlinkTask(final String key, final ILayer layer) {

        return new Runnable() {
            public void run() {

                Display.getDefault().asyncExec(new Runnable() {
                    public void run() {

                        blinkingUpdates.remove(key);
                        blinkingTasks.remove(key);
                        fireLayerEvent(new BlinkEvent(layer));
                    }
                });
            }
        };

    }

    @SuppressWarnings("unchecked")
    @Override
    public void handleLayerEvent(ILayerEvent event) {
        if (blinkingEnabled) {
            if (event instanceof PropertyUpdateEvent) {
                updateEventsCache.put((PropertyUpdateEvent<T>) event);
            }
        }
        super.handleLayerEvent(event);
    }

    public void setBlinkingEnabled(boolean enabled) {
        this.blinkingEnabled = enabled;
    }

    public int getColumnPositionByIndex(int columnIndex) {
        return dataLayer.getColumnPositionByIndex(columnIndex);
    }

    public int getRowPositionByIndex(int rowIndex) {
        return dataLayer.getRowPositionByIndex(rowIndex);
    }

    public void setBlinkDurationInMilis(int blinkDurationInMilis) {
        this.blinkDurationInMilis = blinkDurationInMilis;
    }

}