Package org.eclipse.nebula.widgets.nattable.extension.glazedlists

Source Code of org.eclipse.nebula.widgets.nattable.extension.glazedlists.DetailGlazedListsEventLayer

/*******************************************************************************
* Copyright (c) 2013 Dirk Fauth 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:
*    Dirk Fauth <dirk.fauth@gmail.com> - initial API and implementation
*******************************************************************************/
package org.eclipse.nebula.widgets.nattable.extension.glazedlists;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.nebula.widgets.nattable.coordinate.Range;
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.event.PropertyUpdateEvent;
import org.eclipse.nebula.widgets.nattable.layer.event.RowDeleteEvent;
import org.eclipse.nebula.widgets.nattable.layer.event.RowInsertEvent;
import org.eclipse.swt.widgets.Display;

import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.event.ListEventListener;

/**
* This layer acts as the event listener for:
* <ol>
* <li>Glazed list events - {@link ListEvent}
* <li>Bean updates - PropertyChangeEvent(s)
* </ol>
*
* Compared to the GlazedListsEventLayer, this layer does not conflate events
* and only fire a single RowStructuralRefreshEvent for all events within 100ms.
* Instead it will fire a corresponding NatTable event with the detail
* information for every {@link ListEvent} fired by the GlazedLists immediately.
*
* @param <T>
*            Type of the bean in the backing list.
*
* @author Dirk Fauth
*
*/
public class DetailGlazedListsEventLayer<T> extends AbstractLayerTransform
        implements IUniqueIndexLayer, ListEventListener<T>,
        PropertyChangeListener {

    /**
     * The underlying layer of type {@link IUniqueIndexLayer} This is necessary
     * because {@link AbstractLayerTransform} only specifies {@link ILayer} as
     * the type of the underlying layer. But as this event layer implements
     * {@link IUniqueIndexLayer} the underlying layer needs to be of type
     * {@link IUniqueIndexLayer} too so the necessary methods can delegate to
     * it. Storing the underlying layer reference as {@link IUniqueIndexLayer}
     * in here avoids casting operations at every access.
     */
    private final IUniqueIndexLayer underlyingLayer;

    /**
     * The {@link EventList} whose events this layer is processing. Needed here
     * so it is possible to exchange the list at runtime.
     */
    private EventList<T> eventList;

    /**
     * Create a new {@link DetailGlazedListsEventLayer} which is in fact a
     * {@link ListEventListener} that listens to GlazedLists events and
     * translate them into events that are understandable by the NatTable.
     *
     * @param underlyingLayer
     *            The underlying layer of type {@link IUniqueIndexLayer}
     * @param eventList
     *            The {@link EventList} this layer should be added as listener.
     */
    public DetailGlazedListsEventLayer(IUniqueIndexLayer underlyingLayer,
            EventList<T> eventList) {
        super(underlyingLayer);
        this.underlyingLayer = underlyingLayer;

        // add ourself as listener to the EventList
        this.eventList = eventList;
        this.eventList.addListEventListener(this);
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * ca.odell.glazedlists.event.ListEventListener#listChanged(ca.odell.glazedlists
     * .event.ListEvent)
     */
    /**
     * GlazedLists event handling. Will transform received GlazedLists
     * ListEvents into corresponding NatTable RowStructuralChangeEvents. Ensures
     * that no other changes can be made to the GlazedLists instance until the
     * events are processed in NatTable itself. This is necessary to avoid
     * concurrent modifications which will lead to asynchronous states of
     * NatTable and GlazedLists.
     */
    @Override
    public void listChanged(final ListEvent<T> event) {
        try {
            this.eventList.getReadWriteLock().readLock().lock();

            int currentEventType = -1;

            // as the delete events in GlazedLists are containing indexes that
            // are related
            // to prior deletes we need to ensure index consistency within
            // NatTable,
            // e.g. filtering so the complete list would be empty would result
            // in getting
            // events that all tell that index 0 is deleted
            int deleteCount = 0;

            final List<Range> deleteRanges = new ArrayList<Range>();
            final List<Range> insertRanges = new ArrayList<Range>();
            while (event.next()) {
                int eventType = event.getType();

                // first event, go ahead
                if (currentEventType == -1) {
                    currentEventType = eventType;
                } else if (currentEventType != eventType) {
                    // there is a new event type, fire the collected events
                    internalFireEvents(deleteRanges, insertRanges);

                    // and clear for clean further processing
                    deleteRanges.clear();
                    deleteCount = 0;
                    insertRanges.clear();
                }

                if (eventType == ListEvent.DELETE) {
                    int index = event.getIndex() + deleteCount;
                    deleteRanges.add(new Range(index, index + 1));
                    deleteCount++;
                } else if (eventType == ListEvent.INSERT) {
                    insertRanges.add(new Range(event.getIndex(), event
                            .getIndex() + 1));
                }
            }

            internalFireEvents(deleteRanges, insertRanges);
        } finally {
            this.eventList.getReadWriteLock().readLock().unlock();
        }
    }

    /**
     * Fire events with detail informations to update the NatTable accordingly.
     * <p>
     * The RowStructuralChangeEvents will cause a repaint of the NatTable. We
     * need to fire the event from the SWT Display thread, otherwise there will
     * be an exception because painting can only be triggered from the SWT
     * Display thread.
     * </p>
     * <p>
     * As there is a structural change, there need to be some processing for
     * indexes and positions in layers above this one. Therefore we need to
     * ensure that the processing is handled synchronous, otherwise we would get
     * into an asynchronous state were we try to process events based on a
     * ListEvent, while the list itself has already changed again. e.g.
     * filtering: clear + apply
     * </p>
     *
     * @param deleteRanges
     *            The ranges that were deleted and should be fired in an event.
     * @param insertRanges
     *            The ranges that were inserted and should be fired in an event.
     */
    private void internalFireEvents(final List<Range> deleteRanges,
            final List<Range> insertRanges) {
        if (!deleteRanges.isEmpty()) {
            Display.getDefault().syncExec(new Runnable() {
                @Override
                public void run() {
                    fireLayerEvent(new RowDeleteEvent(getUnderlyingLayer(),
                            deleteRanges));
                }
            });
        }

        if (!insertRanges.isEmpty()) {
            Display.getDefault().syncExec(new Runnable() {
                @Override
                public void run() {
                    fireLayerEvent(new RowInsertEvent(getUnderlyingLayer(),
                            insertRanges));
                }
            });
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see java.beans.PropertyChangeListener#propertyChange(java.beans.
     * PropertyChangeEvent)
     */
    /**
     * Object property updated event
     */
    @SuppressWarnings("unchecked")
    @Override
    public void propertyChange(PropertyChangeEvent event) {
        // We can cast since we know that the EventList is of type T
        final PropertyUpdateEvent<T> updateEvent = new PropertyUpdateEvent<T>(
                this, (T) event.getSource(), event.getPropertyName(),
                event.getOldValue(), event.getNewValue());

        // The PropertyUpdateEvent will cause a repaint of the NatTable.
        // We need to fire the event from the SWT Display thread, otherwise
        // there will be an exception because painting can only be triggered
        // from the SWT Display thread.
        // As a property change doesn't indicate a structural change, the
        // event can be fired asynchronously.
        Display.getDefault().asyncExec(new Runnable() {
            @Override
            public void run() {
                fireLayerEvent(updateEvent);
            }
        });
    }

    /**
     * Change the underlying {@link EventList} this layer is listening to.
     *
     * @param newEventList
     *            the {@link EventList} to listen on.
     */
    public void setEventList(EventList<T> newEventList) {
        this.eventList.removeListEventListener(this);
        this.eventList = newEventList;
        this.eventList.addListEventListener(this);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer#
     * getColumnPositionByIndex(int)
     */
    @Override
    public int getColumnPositionByIndex(int columnIndex) {
        return underlyingLayer.getColumnPositionByIndex(columnIndex);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer#
     * getRowPositionByIndex(int)
     */
    @Override
    public int getRowPositionByIndex(int rowIndex) {
        return underlyingLayer.getRowPositionByIndex(rowIndex);
    }

}
TOP

Related Classes of org.eclipse.nebula.widgets.nattable.extension.glazedlists.DetailGlazedListsEventLayer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.