Package com.cedarsoft.spring.rcp.table

Source Code of com.cedarsoft.spring.rcp.table.ObjectTable$ContextPopupMenuListener

package com.cedarsoft.spring.rcp.table;

import org.springframework.richclient.factory.AbstractControlFactory;
import org.springframework.richclient.table.support.GlazedTableModel;
import org.springframework.richclient.table.TableUtils;
import org.springframework.richclient.application.event.LifecycleApplicationEvent;
import org.springframework.richclient.application.statusbar.StatusBar;
import org.springframework.richclient.command.ActionCommandExecutor;
import org.springframework.richclient.command.CommandGroup;
import org.springframework.richclient.core.Guarded;
import org.springframework.richclient.util.PopupMenuMouseListener;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ApplicationEvent;
import org.springframework.beans.support.PropertyComparator;
import org.springframework.util.Assert;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.NonNls;
import org.joda.time.LocalDate;
import org.joda.time.LocalTime;
import org.joda.time.DateTime;
import org.joda.time.Interval;
import org.joda.time.Period;

import javax.swing.JComponent;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.JPopupMenu;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.table.TableColumnModel;

import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.SortedList;
import ca.odell.glazedlists.GlazedLists;
import ca.odell.glazedlists.util.concurrent.Lock;
import ca.odell.glazedlists.gui.TableFormat;
import ca.odell.glazedlists.event.ListEventListener;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.swing.EventSelectionModel;
import ca.odell.glazedlists.swing.TableComparatorChooser;

import java.util.List;
import java.util.Collections;
import java.util.Collection;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Arrays;
import java.math.BigDecimal;
import java.awt.event.MouseEvent;
import java.awt.event.MouseAdapter;

import com.cedarsoft.spring.rcp.PropertyPath;
import com.cedarsoft.spring.rcp.table.renderer.EnumTableCellRenderer;
import com.cedarsoft.spring.rcp.table.renderer.PercentageTableCellRenderer;
import com.cedarsoft.spring.rcp.table.renderer.LocalDateTableCellRenderer;
import com.cedarsoft.spring.rcp.table.renderer.LocalTimeTableCellRenderer;
import com.cedarsoft.spring.rcp.table.renderer.DateTimeTableCellRenderer;
import com.cedarsoft.spring.rcp.table.renderer.IntervalTableCellRenderer;
import com.cedarsoft.spring.rcp.table.renderer.PeriodCellRenderer;
import com.cedarsoft.spring.rcp.table.renderer.TaggedCellRenderer;
import com.cedarsoft.spring.rcp.events.SelectionEvent;
import com.cedarsoft.spring.rcp.selection.NoElementSelectedException;
import com.cedarsoft.utils.tags.Tagged;
import com.cedarsoft.utils.tags.TagSet;
import com.cedarsoft.ObjectAccess;

/**
* Improved Object table.
*
* @param <T> the type
*/
public abstract class ObjectTable<T> extends AbstractControlFactory implements ApplicationListener {
  @NotNull
  private final StatusBarSupport statusBarSupport;

  @NotNull
  @NonNls
  private final String modelId;
  /**
   * Whether this object table should listen to events from spring
   */
  protected final boolean handleApplicationEvents;
  @NotNull
  protected final Class<? super T> type;

  @NotNull
  private final List<ColumnDefinition> columnDefinitions = new ArrayList<ColumnDefinition>();

  /**
   * Creates a new object table
   *
   * @param modelId                 the model id
   * @param type                    the type
   * @param handleApplicationEvents whether this table should handle application events
   * @param columnDefinitions       the column definitions
   */
  protected ObjectTable( @NotNull @NonNls String modelId, @NotNull Class<? super T> type, boolean handleApplicationEvents, @NotNull ColumnDefinition... columnDefinitions ) {
    this.modelId = modelId;
    this.type = type;
    this.statusBarSupport = new StatusBarSupport( modelId );
    this.handleApplicationEvents = handleApplicationEvents;
    this.columnDefinitions.addAll( Arrays.asList( columnDefinitions ) );
  }

  @Override
  protected JComponent createControl() {
    // Contstruct the table model and table to display the data
    EventList<T> finalList = getFinalEventList();
    model = createTableModel( finalList );

    JTable table = getComponentFactory().createTable( model );
    table.setSelectionModel( new EventSelectionModel<T>( finalList ) );
    table.setSelectionMode( ListSelectionModel.MULTIPLE_INTERVAL_SELECTION );

    // Install the sorter
    SortedList<T> baseList = getBaseEventList();
    tableSorter = installTableSorter( table, baseList );

    // Allow the derived type to configure the table
    configureTable( table );

    int initialSortColumn = getInitialSortColumn();
    if ( initialSortColumn >= 0 ) {
      tableSorter.clearComparator();
      tableSorter.appendComparator( initialSortColumn, 0, false );
    }

    // Add the context menu listener
    table.addMouseListener( new ContextPopupMenuListener() );

    // Add our mouse handlers to setup our desired selection mechanics
    table.addMouseListener( new DoubleClickListener() );

    // Keep our status line up to date with the selections and filtering
    StatusBarUpdateListener statusBarUpdateListener = new StatusBarUpdateListener();
    table.getSelectionModel().addListSelectionListener( statusBarUpdateListener );
    getFinalEventList().addListEventListener( statusBarUpdateListener );

    return table;
  }

  /**
   * Returns the created JTable.
   *
   * @return the table
   */
  @NotNull
  protected JTable getTable() {
    return ( JTable ) getControl();
  }

  /**
   * Returns the column definitions
   *
   * @return the column definitions
   */
  @NotNull
  public List<? extends ColumnDefinition> getColumnDefinitions() {
    return Collections.unmodifiableList( columnDefinitions );
  }

  private TableComparatorChooser<T> tableSorter;

  /**
   * Returns the sorter which is used to sort the content of the table
   *
   * @return the sorter, null if {@link #getTable()} or {@link #createControl()} is not called before
   */
  @Nullable
  protected TableComparatorChooser<T> getTableSorter() {
    return tableSorter;
  }

  /**
   * Creates the table sorter
   *
   * @param table      the table
   * @param sortedList the sorted list
   * @return the table sorter
   */
  @NotNull
  protected TableComparatorChooser<T> installTableSorter( @NotNull JTable table, @NotNull SortedList<T> sortedList ) {
    return TableComparatorChooser.install( table, sortedList, isMultipleColumnSort() ? TableComparatorChooser.MULTIPLE_COLUMN_KEYBOARD : TableComparatorChooser.SINGLE_COLUMN );
  }

  protected boolean isMultipleColumnSort() {
    return true;
  }

  private EventList<T> finalEventList;

  /**
   * Get the event list to be use for constructing the table model.
   *
   * @return final event list
   */
  @NotNull
  public EventList<T> getFinalEventList() {
    if ( finalEventList == null ) {
      finalEventList = createFinalEventList();
    }
    //noinspection ReturnOfCollectionOrArrayField
    return finalEventList;
  }

  @NotNull
  protected EventList<T> createFinalEventList() {
    FinalEventListFactory<T> factory = finalEventListFactory;

    if ( factory == null ) {
      return getBaseEventList();
    }
    return factory.create( getBaseEventList() );
  }

  @Nullable
  private FinalEventListFactory<T> finalEventListFactory;

  @Nullable
  public FinalEventListFactory<T> getFinalEventListFactory() {
    return finalEventListFactory;
  }

  public void setFinalEventListFactory( @Nullable FinalEventListFactory<T> finalEventListFactory ) {
    this.finalEventListFactory = finalEventListFactory;
  }

  private SortedList<T> baseEventList;

  /**
   * Get the base event list for the table model. This can be used to build layered event models for filtering.
   *
   * @return base event list
   */
  @NotNull
  public SortedList<T> getBaseEventList() {
    if ( baseEventList == null ) {
      // Construct on demand
      List<? extends T> data = getInitialData();
      if ( logger.isDebugEnabled() ) {
        logger.debug( "Table data: got " + data.size() + " entries" );
      }
      // Construct the event list of all our data and layer on the sorting
      EventList<T> rawList = GlazedLists.eventList( data );
      int initialSortColumn = getInitialSortColumn();
      if ( initialSortColumn >= 0 ) {
        PropertyPath sortProperty = getColumnDefinition( initialSortColumn ).getPropertyPath();
        //noinspection unchecked
        baseEventList = new SortedList<T>( rawList, new PropertyComparator( sortProperty.getProperty(), false, true ) );
      } else {
        baseEventList = new SortedList<T>( rawList );
      }
    }
    //noinspection ReturnOfCollectionOrArrayField
    return baseEventList;
  }

  /**
   * Returns the column definition for the given column
   *
   * @param column the column
   * @return the column definition
   */
  @NotNull
  public ColumnDefinition getColumnDefinition( int column ) {
    return columnDefinitions.get( column );
  }

  /**
   * Get the default sort column. Defaults to 0.
   *
   * @return column to sort on
   */
  protected int getInitialSortColumn() {
    return 0;
  }


  protected void updateStatusBar() {
    int all = getBaseEventList().size();
    int showing = getFinalEventList().size();
    int selectedRow = getTable().getSelectedRowCount();

    statusBarSupport.updateStatusBar( all, showing, selectedRow );
  }

  /**
   * Returns the initial data
   *
   * @return the initial data
   */
  @NotNull
  protected abstract List<? extends T> getInitialData();

  /**
   * Selets the given object
   *
   * @param object the object that is selected
   */
  public void selectObject( @NotNull T object ) {
    try {
      getSelectionModel().setValueIsAdjusting( true );
      GlazedTableModel tableModel = getTableModel();
      int rowCount = tableModel.getRowCount();

      for ( int i = 0; i < rowCount; i++ ) {
        Object rowObject = tableModel.getElementAt( i );
        //noinspection ObjectEquality
        if ( rowObject == object ) {
          getSelectionModel().setSelectionInterval( i, i );
          TableUtils.scrollToRow( getTable(), i );
          return;
        }
      }
    } finally {
      getSelectionModel().setValueIsAdjusting( false );
    }
  }

  @NotNull
  protected GlazedTableModel createTableModel( @NotNull EventList<T> eventList ) {
    return new ExtendedTableModel( eventList );
  }

  /**
   * @param event the event
   * @return wether the application events should be handled
   */
  protected boolean shouldHandleEvent( @NotNull LifecycleApplicationEvent event ) {
    return handleApplicationEvents;
  }

  /**
   * Handle the addition of new object.
   *
   * @param objects the new objects to handle
   */
  protected void handleNewObjects( final Collection<? extends T> objects ) {
    runWithWriteLock( new Runnable() {
      @Override
      public void run() {
        getFinalEventList().addAll( objects );
      }
    } );
    getControl().repaint();
  }

  protected void handleUpdatedObjects( final Collection<? extends T> objects ) {
    runWithWriteLock( new Runnable() {
      @Override
      public void run() {
        for ( T object : objects ) {
          int index = getFinalEventList().indexOf( object );
          if ( index >= 0 ) {
            getFinalEventList().set( index, object );
          }
        }
      }
    } );
    getControl().repaint();
  }

  protected void handleDeletedObjects( final Collection<? extends T> objects ) {
    runWithWriteLock( new Runnable() {
      @Override
      public void run() {
        getFinalEventList().removeAll( objects );
      }
    } );
    getControl().repaint();
  }

  /**
   * Returns whether this object is selected
   *
   * @param object the object
   * @return true if this object is selected, false otherwise
   */
  public boolean isSelected( @NotNull T object ) {
    return getSelectedObjects().contains( object );
  }

  /**
   * Returns the (first) selected object.
   *
   * @return the selected object.
   *
   * @throws IllegalStateException      if no element is selected
   * @throws NoElementSelectedException
   */
  @NotNull
  public T getSelectedObject() throws NoElementSelectedException {
    List<? extends T> selectedElements = getSelectedObjects();
    if ( selectedElements.isEmpty() ) {
      throw new NoElementSelectedException();
    }
    return selectedElements.get( 0 );
  }

  /**
   * Returns the selected objects
   *
   * @return the selected objects
   */
  @NotNull
  public List<? extends T> getSelectedObjects() {
    List<T> selected = new ArrayList<T>();
    for ( int selectedIndicy : getTable().getSelectedRows() ) {
      //noinspection unchecked
      selected.add( ( T ) getTableModel().getElementAt( selectedIndicy ) );
    }
    return selected;
  }

  protected void configureTable( @NotNull final JTable table ) {
    TableColumnModel columnModel = table.getColumnModel();

    //Register the renderers
    registerDefaultRenderers( table );

    //Apply the column definitions
    for ( int i = 0; i < columnDefinitions.size(); i++ ) {
      ColumnDefinition columnDefinition = columnDefinitions.get( i );
      columnDefinition.apply( columnModel.getColumn( i ) );
    }

    //call the configurers
    for ( TableConfigurer tableConfigurer : tableConfigurers ) {
      tableConfigurer.configure( table );
    }

    //Publish changes to the selection model
    final EventSelectionModel<T> selectionModel = ( EventSelectionModel<T> ) table.getSelectionModel();
    selectionModel.getSelected().addListEventListener( new ListEventListener<T>() {
      @Override
      public void listChanged( @NotNull ListEvent<T> listChanges ) {
        //Iterate two times - first unselect, then select

        //First unselect
        while ( listChanges.next() ) {
          if ( listChanges.getType() == ListEvent.DELETE ) {
            getApplicationContext().publishEvent( new SelectionEvent( SelectionEvent.Type.UNSELECT, listChanges.getOldValue() ) );
          }
        }

        listChanges.reset();

        //Now select
        while ( listChanges.next() ) {
          if ( listChanges.getType() == ListEvent.INSERT ) {
            getApplicationContext().publishEvent( new SelectionEvent( SelectionEvent.Type.SELECT, selectionModel.getSelected().get( listChanges.getIndex() ) ) );
          }
        }
      }
    } );
  }

  @NotNull
  private final List<TableConfigurer> tableConfigurers = new ArrayList<TableConfigurer>();

  @NotNull
  public List<? extends TableConfigurer> getTableConfigurers() {
    return Collections.unmodifiableList( tableConfigurers );
  }

  public void addTableConfigurer( @NotNull TableConfigurer tableConfigurer ) {
    this.tableConfigurers.add( tableConfigurer );
  }

  /**
   * Get the selection model.
   *
   * @return selection model
   */
  public EventSelectionModel<T> getSelectionModel() {
    //noinspection unchecked
    return ( EventSelectionModel<T> ) getTable().getSelectionModel();
  }

  /**
   * Registers the renderes
   *
   * @param table the table the renderers should be registered at
   */
  protected void registerDefaultRenderers( @NotNull JTable table ) {
    table.setDefaultRenderer( Enum.class, new EnumTableCellRenderer() );
    table.setDefaultRenderer( BigDecimal.class, new PercentageTableCellRenderer() );

    table.setDefaultRenderer( LocalDate.class, new LocalDateTableCellRenderer() );
    table.setDefaultRenderer( LocalTime.class, new LocalTimeTableCellRenderer() );
    table.setDefaultRenderer( DateTime.class, new DateTimeTableCellRenderer( true ) );
    table.setDefaultRenderer( Interval.class, new IntervalTableCellRenderer() );
    table.setDefaultRenderer( Period.class, new PeriodCellRenderer() );

    table.setDefaultRenderer( Tagged.class, new TaggedCellRenderer() );
    table.setDefaultRenderer( TagSet.class, new TaggedCellRenderer() );
  }

  /**
   * Extended table model
   */
  private class ExtendedTableModel extends GlazedTableModel {
    private ExtendedTableModel( @NotNull EventList<T> eventList ) {
      super( eventList, ColumnDefinition.extractColumnProperties( getColumnDefinitions() ), ObjectTable.this.getModelId() );
    }

    /**
     * @noinspection RefusedBequest
     */
    @Override
    protected TableFormat<?> createTableFormat() {
      DefaultAdvancedTableFormat format = new DefaultAdvancedTableFormat();
      applyComparators( format );
      return format;
    }

    private void applyComparators( @NotNull DefaultAdvancedTableFormat format ) {
      //Set the comparators
      for ( int i = 0; i < columnDefinitions.size(); i++ ) {
        ColumnDefinition definition = columnDefinitions.get( i );

        Comparator<?> comparator = definition.getComparator();
        if ( comparator != null ) {
          format.setComparator( i, comparator );
        }
      }
    }

    @Override
    protected String[] createColumnNames( @NotNull String[] propertyColumnNames ) {
      String[] copy = propertyColumnNames.clone();

      for ( int i = 0; i < columnDefinitions.size(); i++ ) {
        ColumnDefinition definition = columnDefinitions.get( i );

        String override = definition.getColumnNameOverride();
        if ( override != null ) {
          copy[i] = override;
        }
      }

      return super.createColumnNames( copy );
    }
  }

  /**
   * @return the modelId
   */
  @NotNull
  @NonNls
  public String getModelId() {
    return modelId;
  }

  @Nullable
  private GlazedTableModel model;

  /**
   * Get the data model for the table.
   * <p/>
   * <em>Note:</em> This method returns null unless {@link #getTable()} or {@link #createTable()} is called
   *
   * @return model the table model which is used for the table
   */
  @NotNull
  public GlazedTableModel getTableModel() {
    if ( model == null ) {
      throw new IllegalArgumentException( "No model available yet" );
    }
    return model;
  }

  /**
   * Set the status bar associated with this table. If non-null, then any time the final event list on this table
   * changes, then the status bar will be updated with the current object counts.
   *
   * @param statusBar to update
   */
  public void setStatusBar( StatusBar statusBar ) {
    this.statusBarSupport.setCurrentStatusBar( statusBar );
    updateStatusBar();
  }

  @Nullable
  private ActionCommandExecutor doubleClickHandler;

  /**
   * @return the doubleClickHandler
   */
  @Nullable
  public ActionCommandExecutor getDoubleClickHandler() {
    return doubleClickHandler;
  }

  /**
   * Set the handler (action executor) that should be invoked when a row in the table is double-clicked.
   *
   * @param doubleClickHandler the doubleClickHandler to set
   */
  public void setDoubleClickHandler( @Nullable ActionCommandExecutor doubleClickHandler ) {
    this.doubleClickHandler = doubleClickHandler;
  }

  /**
   * Handle a double click on a row of the table. The row will already be selected.
   */
  protected void onDoubleClick() {
    // Dispatch this to the doubleClickHandler, if any
    ActionCommandExecutor handler = doubleClickHandler;
    if ( handler == null ) {
      return;
    }

    //May execute an guarded command executor
    if ( handler instanceof Guarded ) {
      if ( !( ( Guarded ) handler ).isEnabled() ) {
        return;
      }
    }

    handler.execute();
  }

  @Nullable
  private CommandGroup popupCommandGroup;

  public void setPopupCommandGroup( @Nullable CommandGroup popupCommandGroup ) {
    this.popupCommandGroup = popupCommandGroup;
  }

  /**
   * @return the popupCommandGroup
   */
  @Nullable
  public CommandGroup getPopupCommandGroup() {
    return popupCommandGroup;
  }


  /**
   * Create the context popup menu, if any, for this table. The default operation is to create the popup from the
   * command group if one has been specified. If not, then null is returned.
   *
   * @return popup menu to show, or null if none
   */
  @Nullable
  protected JPopupMenu createPopupContextMenu() {
    CommandGroup commandGroup = getPopupCommandGroup();
    if ( commandGroup == null ) {
      return null;
    }
    return commandGroup.createPopupMenu();
  }

  /**
   * Create the context popup menu, if any, for this table. The default operation is to create the popup from the
   * command group if one has been specified. If not, then null is returned.
   *
   * @param e the event which contains information about the current context.
   * @return popup menu to show, or null if none
   */
  @Nullable
  protected JPopupMenu createPopupContextMenu( MouseEvent e ) {
    return createPopupContextMenu();
  }

  /**
   * Handle the creation of a new object.
   *
   * @param object New object to handle
   */
  protected void handleNewObject( @NotNull final T object ) {
    runWithWriteLock( new Runnable() {
      @Override
      public void run() {
        getFinalEventList().add( object );
      }
    } );
    getControl().repaint();
  }

  /**
   * Handle an updated object in this table. Locate the existing entry (by equals) and replace it in the underlying
   * list.
   *
   * @param object Updated object to handle
   */
  protected void handleUpdatedObject( @NotNull final T object ) {
    runWithWriteLock( new Runnable() {
      @Override
      public void run() {
        int index = getFinalEventList().indexOf( object );
        if ( index >= 0 ) {
          getFinalEventList().set( index, object );
        }
      }
    } );
    getControl().repaint();
  }

  /**
   * Handle the deletion of an object in this table. Locate this entry (by equals) and delete it.
   *
   * @param object Updated object being deleted
   */
  protected void handleDeletedObject( @NotNull final T object ) {
    runWithWriteLock( new Runnable() {
      @Override
      public void run() {
        int index = getFinalEventList().indexOf( object );
        if ( index >= 0 ) {
          getFinalEventList().remove( index );
        }
      }
    } );
    getControl().repaint();
  }

  /**
   * Handle an application event. This will notify us of object adds, deletes, and modifications. Update our table
   * model accordingly.
   *
   * @param event event to process
   */
  @Override
  public void onApplicationEvent( ApplicationEvent event ) {
    if ( !( event instanceof LifecycleApplicationEvent ) ) {
      return;
    }

    LifecycleApplicationEvent lifecycleApplicationEvent = ( LifecycleApplicationEvent ) event;
    if ( !shouldHandleEvent( lifecycleApplicationEvent ) ) {
      return;
    }

    if ( !type.isAssignableFrom( lifecycleApplicationEvent.getObject().getClass() ) ) {
      return;
    }

    T eventObject = ( T ) lifecycleApplicationEvent.getObject();


    if ( LifecycleApplicationEvent.CREATED.equals( lifecycleApplicationEvent.getEventType() ) ) {
      handleNewObject( eventObject );
    } else if ( LifecycleApplicationEvent.MODIFIED.equals( lifecycleApplicationEvent.getEventType() ) ) {
      handleUpdatedObject( eventObject );
    } else if ( LifecycleApplicationEvent.DELETED.equals( lifecycleApplicationEvent.getEventType() ) ) {
      handleDeletedObject( eventObject );
    }
  }

  /**
   * Executes the runnable with a write lock on the event list.
   *
   * @param runnable its run method is executed while holding a write lock for
   *                 the event list.
   * @see #getFinalEventList()
   */
  protected void runWithWriteLock( @NotNull Runnable runnable ) {
    runWithLock( runnable, getFinalEventList().getReadWriteLock().writeLock() );
  }

  /**
   * Executes the runnable with a read lock on the event list.
   *
   * @param runnable its run method is executed while holding a read lock for
   *                 the event list.
   * @see #getFinalEventList()
   */
  protected void runWithReadLock( @NotNull Runnable runnable ) {
    runWithLock( runnable, getFinalEventList().getReadWriteLock().readLock() );
  }


  final class StatusBarUpdateListener implements ListSelectionListener, ListEventListener<T> {
    @Override
    public void valueChanged( ListSelectionEvent e ) {
      updateStatusBar();
    }

    @Override
    public void listChanged( ListEvent<T> listChanges ) {
      updateStatusBar();
    }
  }

  final class ContextPopupMenuListener extends PopupMenuMouseListener {
    /**
     * @noinspection RefusedBequest
     */
    @Nullable
    @Override
    protected JPopupMenu getPopupMenu( MouseEvent e ) {
      return createPopupContextMenu( e );
    }
  }

  final class DoubleClickListener extends MouseAdapter {
    @Override
    public void mousePressed( MouseEvent e ) {
      // If the user right clicks on a row other than the selection,
      // then move the selection to the current row
      if ( e.getButton() == MouseEvent.BUTTON3 ) {
        int rowUnderMouse = getTable().rowAtPoint( e.getPoint() );
        if ( rowUnderMouse != -1 && !getTable().isRowSelected( rowUnderMouse ) ) {
          // Select the row under the mouse
          getSelectionModel().setSelectionInterval( rowUnderMouse, rowUnderMouse );
        }
      }
    }

    /**
     * Handle double click.
     */
    @Override
    public void mouseClicked( MouseEvent e ) {
      // If the user double clicked on a row, then call onDoubleClick
      if ( e.getClickCount() == 2 ) {
        onDoubleClick();
      }
    }
  }

  protected static void runWithLock( @NotNull Runnable runnable, @NotNull Lock lock ) {
    Assert.notNull( runnable );
    Assert.notNull( lock );
    lock.lock();
    try {
      runnable.run();
    }
    finally {
      lock.unlock();
    }
  }
}
TOP

Related Classes of com.cedarsoft.spring.rcp.table.ObjectTable$ContextPopupMenuListener

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.