Package gov.nasa.arc.mct.table.view

Source Code of gov.nasa.arc.mct.table.view.TableViewManifestation

/*******************************************************************************
* Mission Control Technologies, Copyright (c) 2009-2012, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* The MCT platform is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* MCT includes source code licensed under additional open source licenses. See
* the MCT Open Source Licenses file included with this distribution or the About
* MCT Licenses dialog available at runtime from the MCT Help menu for additional
* information.
*******************************************************************************/
package gov.nasa.arc.mct.table.view;

import gov.nasa.arc.mct.components.AbstractComponent;
import gov.nasa.arc.mct.components.ExtendedProperties;
import gov.nasa.arc.mct.components.FeedProvider;
import gov.nasa.arc.mct.components.FeedProvider.FeedType;
import gov.nasa.arc.mct.components.FeedProvider.RenderingInfo;
import gov.nasa.arc.mct.components.TimeConversion;
import gov.nasa.arc.mct.evaluator.api.Evaluator;
import gov.nasa.arc.mct.gui.FeedView;
import gov.nasa.arc.mct.gui.FeedView.RenderingCallback;
import gov.nasa.arc.mct.gui.NamingContext;
import gov.nasa.arc.mct.gui.OptionBox;
import gov.nasa.arc.mct.gui.SelectionProvider;
import gov.nasa.arc.mct.gui.View;
import gov.nasa.arc.mct.policy.ExecutionResult;
import gov.nasa.arc.mct.policy.PolicyContext;
import gov.nasa.arc.mct.policy.PolicyInfo;
import gov.nasa.arc.mct.roles.events.AddChildEvent;
import gov.nasa.arc.mct.roles.events.PropertyChangeEvent;
import gov.nasa.arc.mct.roles.events.RemoveChildEvent;
import gov.nasa.arc.mct.services.component.PolicyManager;
import gov.nasa.arc.mct.services.component.ViewInfo;
import gov.nasa.arc.mct.services.component.ViewType;
import gov.nasa.arc.mct.table.access.ServiceAccess;
import gov.nasa.arc.mct.table.dnd.TableTransferHandler;
import gov.nasa.arc.mct.table.gui.LabeledTable;
import gov.nasa.arc.mct.table.gui.TableLayoutListener;
import gov.nasa.arc.mct.table.model.AbbreviatingTableLabelingAlgorithm;
import gov.nasa.arc.mct.table.model.ComponentTableModel;
import gov.nasa.arc.mct.table.model.TableOrientation;
import gov.nasa.arc.mct.table.model.TableStructure;
import gov.nasa.arc.mct.table.model.TableType;
import gov.nasa.arc.mct.table.policy.TableViewPolicy;
import gov.nasa.arc.mct.table.view.TableFormattingConstants.JVMFontFamily;
import gov.nasa.arc.mct.table.view.TimeFormat.DateFormatItem;

import java.awt.Color;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeListener;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IllegalFormatException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

import javax.swing.BorderFactory;
import javax.swing.DropMode;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Implements a visible manifestation of the the view role.
*/
@SuppressWarnings("serial")
public class TableViewManifestation extends FeedView
    implements RenderingCallback, SelectionProvider {
  private static final Logger logger = LoggerFactory.getLogger(TableViewManifestation.class);
 
  private LabeledTable table;
  private ComponentTableModel model;
  private AbbreviatingTableLabelingAlgorithm labelingAlgorithm;

  private Map<String, TimeConversion> timeConversionMap = new HashMap<String, TimeConversion>();
  private final AtomicReference<Collection<FeedProvider>> feedProvidersRef = new AtomicReference<Collection<FeedProvider>>(Collections.<FeedProvider>emptyList());
  private List<AbstractComponent> multipleEvaluators = new ArrayList<AbstractComponent>();
  private static final DecimalFormat[] formats;
  private boolean receivedData = false;
  private boolean updating = false;
  private TableSelectionListener selectionListener;
  private TableSettingsControlPanel controlPanel;

  static {
    formats = new DecimalFormat[11];
    formats[0] = new DecimalFormat("#");
    String formatString = "#.";
    for (int i = 1; i < formats.length; i++) {
      formatString += "0";
      DecimalFormat format = new DecimalFormat(formatString);
      formats[i] = format;
    }
  }
 
  /**
   * Creates a view manifestation for a component.
   *
   * If this is the first manifestation for a component, a new
   * manifestation will be created with default settings. Subsequent
   * manifestations will use the persisted properties.
   *
   * @param component
   *            the component for this manifestation
   * @param vi the view information for this view
   */
  public TableViewManifestation(AbstractComponent component, ViewInfo vi) {
    super(component,vi);

    labelingAlgorithm = new AbbreviatingTableLabelingAlgorithm();
    setLabelingContext(labelingAlgorithm, getNamingContext());
    TableStructure structure = TableViewPolicy
        .getTableStructure(getManifestedComponent());
    model = new ComponentTableModel(structure, labelingAlgorithm, this);
    model.updateLabels();

    TableViewCellRenderer renderer = new TableViewCellRenderer();
   
    table = new LabeledTable(model);
    table.getTable().setShowGrid(false);
    table.getTable().getColumnModel().setColumnMargin(0);
    table.getTable().setRowMargin(0);
   
    table.getTable().setDefaultRenderer(Object.class,
        renderer);
    table.getTable().setTransferHandler(
        new TableTransferHandler(this, table));
    table.getTable().setDragEnabled(true);
    table.getTable().setFillsViewportHeight(true);
    if (structure.getType() == TableType.TWO_DIMENSIONAL) {
      table.getTable().setDropMode(DropMode.ON_OR_INSERT);
    } else {
      table.getTable().setDropMode(DropMode.ON_OR_INSERT_ROWS);
    }
   
    Color bg = getColor("background");
    setBackground(bg);
    table.setBackground(bg);
    table.getTable().setBackground(bg);
    bg = getColor("header.background");
    if (bg != null) {
      table.getTable().getTableHeader().setBackground(bg);
      table.getRowHeaders().setBackground(bg);
    }
   
    Color grid = getColor("grid");
    if (grid != null) table.setHeaderBorder(BorderFactory.createLineBorder(grid, 0));

    Color defaultValueColor = getColor("defaultValueColor");
    if (defaultValueColor != null) {
      renderer.setForeground(defaultValueColor);
      table.getRowHeaders().setForeground(defaultValueColor);
      table.getTable().getTableHeader().setForeground(defaultValueColor);
    }
   
    Color bgSelectionColor = getColor("selection.background");
    Color fgSelectionColor = getColor("selection.foreground");
   
    if (bgSelectionColor != null) {
      table.getTable().setSelectionBackground(bgSelectionColor);
    }
    if (fgSelectionColor != null) {
      table.getTable().setSelectionForeground(fgSelectionColor);
    }     
   
    TableSettings tableSettings = loadSettingsFromPersistence();
    table.setRestoringSettings(true);
    table.updateColumnsFromModel(tableSettings!=null ? tableSettings.getColumnWidths() : null);
    if (tableSettings != null) {
      setTableSettings(tableSettings, table);
    }
   
    updateFeedProviders();
    add(table);
    table.setRestoringSettings(false);
    table.updateColumnsHeaderValuesOnly();
    table.addTableLayoutListener(new TableLayoutListener() {
      @Override
      public void tableChanged(Object source) {
        model.notifyTableStructureChanged();
      }
    });
   
    selectionListener = new TableSelectionListener();
   
    // JTable has different listeners for row and column selections so doing one or the other will miss some change events.
    table.getTable().getTableHeader().getColumnModel().getSelectionModel().addListSelectionListener(selectionListener);
    table.getTable().getSelectionModel().addListSelectionListener(selectionListener);
   
    table.getTable().addMouseListener(new MouseAdapter() {
      @Override
      public void mouseClicked(MouseEvent e) {
        if (e.getComponent().isEnabled()
            && e.getButton() == MouseEvent.BUTTON1
            && e.getClickCount() == 2) {
          Point p = e.getPoint();
          int row = table.getTable().rowAtPoint(p);
          int column = table.getTable().columnAtPoint(p);
          AbstractComponent ac = (AbstractComponent) model
              .getStoredValueAt(row, column);
          if (ac != null) {
            ac.open();
          }
        }
      }
    });

    addAncestorListener(new AncestorListener() {
      @Override
      public void ancestorAdded(AncestorEvent event) {
        final TableViewManifestation table = TableViewManifestation.this;
        Timer t = new Timer(1000, new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent e) {
            long startTime = Long.MIN_VALUE;
            for (FeedProvider fp : getVisibleFeedProviders()) {
              startTime = Math.max(startTime, fp
                  .getTimeService().getCurrentTime());
            }
            requestData(null, startTime, startTime, null, table, true);
          }
        });
        t.setRepeats(false);
        t.start();
        removeAncestorListener(this);
      }

      @Override
      public void ancestorMoved(AncestorEvent event) {
      }

      @Override
      public void ancestorRemoved(AncestorEvent event) {
      }
    });
   
    table.getTable().getColumnModel().addColumnModelListener(new TableColumnModelListener() {

      @Override
      public void columnAdded(TableColumnModelEvent e) {
      }

      @Override
      public void columnMarginChanged(ChangeEvent e) {
        boolean marginChanged = false;
        for (int i = 0; i < table.getTable().getColumnCount() ; i++) {
          if (getViewProperties().hasProperty() && (table.getTable().getColumnModel().getColumn(i).getWidth() != table.getColumnWidths()[i])) {
            marginChanged = true;
          }
        }
        if (marginChanged) {
          saveCellSettings();
          if (controlPanel != null) {
            controlPanel.loadSettings();
          }
        }
      }

      @Override
      public void columnMoved(TableColumnModelEvent e) {
      }

      @Override
      public void columnRemoved(TableColumnModelEvent e) {
      }

      @Override
      public void columnSelectionChanged(ListSelectionEvent e) {
      }

    });
  }

  private Color getColor(String name) {
        return UIManager.getColor(name);       
    }
 
  @Override
  protected void handleNamingContextChange() {
    updateMonitoredGUI();
  }
 
  /**
   * Set the surrounding context labels that should be removed when
   * calculating labels for the rows, columns, and cells of the table. The
   * surrounding context consists of the panel name in which the table is
   * embedded. This may be set in the manifest information or may be the
   * collection object name, if the panel title is not customized.
   * Specifically, if the panel title is visible and non-empty, that is
   * used as the labeling context (even if just blanks). If the panel
   * title is invisible, the labeling context is empty. Otherwise, the
   * labeling context is the component's display name.
   *
   * @param algorithm
   *            the table labeling algorithm
   * @param context
   *            to derive labels from
   */
  private void setLabelingContext(
      AbbreviatingTableLabelingAlgorithm algorithm,
      NamingContext context) {
    String surroundingName = "";
    if (context != null) {
      /* Is some name being shown by the labeling context? */
      if (context.getContextualName() != null) {
        surroundingName = context.getContextualName(); /* Get that name. */     
        if (surroundingName.isEmpty()) {
          /* A title bar or similar is displayed, but it's not overriding our *
           * base displayed name */
          surroundingName = getManifestedComponent().getDisplayName();
        }
      }
    } else {
      /* Labeling context is null, so we are in our own window or inspector */
      surroundingName = getManifestedComponent().getDisplayName();
    }
    algorithm.setContextLabels(surroundingName);
  }

  @Override
  protected JComponent initializeControlManifestation() {
    TableControlPanelController controller = new TableControlPanelController(
        this, table, model);
    controlPanel = new TableSettingsControlPanel(controller);
    return controlPanel;
  }

  @Override
  public SelectionProvider getSelectionProvider() {
    return this;
  }

  @Override
  public void removeSelectionChangeListener(
      PropertyChangeListener listener) {
    removePropertyChangeListener(
        SelectionProvider.SELECTION_CHANGED_PROP, listener);
  }

  @Override
  public void addSelectionChangeListener(PropertyChangeListener listener) {
    addPropertyChangeListener(SelectionProvider.SELECTION_CHANGED_PROP,
        listener);
  }

  @Override
  public Collection<View> getSelectedManifestations() {
    List<View> selectedManifestations = new ArrayList<View>();
    for (int row : table.getTable().getSelectedRows()) {
      for (int col : table.getTable().getSelectedColumns()) {
        AbstractComponent component = (AbstractComponent) model
            .getStoredValueAt(row, col);
        if (component != null
            && component != AbstractComponent.NULL_COMPONENT) {
          selectedManifestations.add(component.getViewInfos(ViewType.NODE).iterator().next().createView(component));
        }
      }
    }
    return selectedManifestations;
  }

  private boolean isClearingSelection = false;
  private boolean orientationChanged = false;
 
  public static final String VIEW_ROLE_NAME = "Alpha";
  @Override
  public void clearCurrentSelections() {
    isClearingSelection = true;
    try {
      table.getTable().clearSelection();
      selectionListener.clearLastSelectedComponents();
    } finally {
      isClearingSelection = false;
    }
  }

  @Override
  public void updateMonitoredGUI(AddChildEvent event) {
    recreateTable();
  }

  /**
   * Recreates the columns and rows of the table so the on-screen
   * representation matches the current state of the model. First
   * notifies the model that the underlying components may have
   * changed (so that the model will interrogate the components for
   * the current number of rows and columns). Then updates the set
   * of feed providers to match the model and updates the labels by
   * running the labeling algorithm. Afterwards, the columns in the
   * table are recreated, in case the number of columns has changed.
   * Finally, a table-structure-changed event is fired to force
   * complete redisplay of the table on-screen.
   */
  private void recreateTable() {
    //TODO: Recreate without clobbering user selections (except where appropriate)
    model.notifyTableStructureChanged();
    updateFeedProviders();
    model.updateLabels();
    table.updateColumnsFromModel(null);
    table.updateColumnsHeaderValuesOnly();
  }

  @Override
  public void updateMonitoredGUI(RemoveChildEvent event) {
    recreateTable();
  }

  @Override
  public void updateMonitoredGUI(PropertyChangeEvent event) {
    // Display name or panel title has changed. Must recalculate labels.
    setLabelingContext(labelingAlgorithm, getNamingContext());
    recreateTable();
  }

  @Override
  public Collection<FeedProvider> getVisibleFeedProviders() {
    return feedProvidersRef.get();
  }

  @Override
  public void synchronizeTime(
      Map<String, List<Map<String, String>>> data, long syncTime) {
    updateFromFeed(data);
  }

  @Override
  public void render(Map<String, List<Map<String, String>>> data) {
    if (!receivedData) {
      updateFromFeed(data);
    }
  }

  /**
   * Extract data from the feed and push it to the table.
   */
  @Override
  public void updateFromFeed(Map<String, List<Map<String, String>>> data) {
    receivedData = true;
    if (data != null) {
      Collection<FeedProvider> feeds = getVisibleFeedProviders();

      for (FeedProvider provider : feeds) {
        String feedId = provider.getSubscriptionId();
        List<Map<String, String>> dataForThisFeed = data
            .get(feedId);
        if (dataForThisFeed != null && !dataForThisFeed.isEmpty()) {
          // Process the first value for this feed.
          Map<String, String> entry = dataForThisFeed
              .get(dataForThisFeed.size() - 1);

          try {
            Object value = entry
                .get(FeedProvider.NORMALIZED_VALUE_KEY);
            RenderingInfo ri = provider.getRenderingInfo(entry);
            TableCellSettings settings = model
                .getCellSettings(provider
                    .getSubscriptionId());
           
            if (settings.getEvaluator() != null) {
              ri = settings
                  .getEvaluator()
                  .getCapability(Evaluator.class)
                  .evaluate(
                      data,
                      Collections
                          .singletonList(provider));
              value = ri.getValueText();             
            } else {
              if (provider.getFeedType() != FeedType.STRING) {
                if (settings.getDateFormat() != null && settings.getDateFormat() !=  DateFormatItem.None) {
                  TimeConversion tc = timeConversionMap.get(provider.getSubscriptionId());               
                  value = TimeFormat.applySimpleDateFormat(settings.getDateFormatter(),
                      tc, value.toString());
                else {
                  value = executeDecimalFormatter(provider,
                      value.toString(), data, settings);
                }
              }
            }
           
            DisplayedValue displayedValue = new DisplayedValue();
            displayedValue.setStatusText(ri.getStatusText());
            displayedValue.setValueColor(ri.getValueColor());
            if (ri.getStatusText().isEmpty() || ri.getStatusText().equals(" ")) {
              if (settings.getFontColor() != null) {
                displayedValue.setValueColor(settings.getFontColor());
              }
            }
//            Set color according to font color settings, as long as value is valid
            displayedValue.setValue(ri.isValid() ? value
                .toString() : "");
            displayedValue.setNumberOfDecimals(settings
                .getNumberOfDecimals());
            displayedValue
                .setAlignment(settings.getAlignment());

            model.setValue(provider.getSubscriptionId(),
                displayedValue);
           
          } catch (ClassCastException ex) {
            logger.error("Feed data entry of unexpected type",
                ex);
          } catch (NumberFormatException ex) {
            logger.error(
                "Feed data entry does not contain parsable value",
                ex);
          }
        }
      }

      // execute multiple evaluators to ensure their values are
      // updated also
      for (AbstractComponent multi : multipleEvaluators) {
        Evaluator evaluator = multi.getCapability(Evaluator.class);
        FeedProvider.RenderingInfo info = evaluator.evaluate(data,
            getFeedProviders(multi));
        if (info != null && info.getValueText() != null) {
          TableCellSettings settings = model.getCellSettings(model
              .getKey(multi));
          DisplayedValue displayedValue = new DisplayedValue();
          displayedValue.setValue(info.getValueText());
          displayedValue.setStatusText(info.getStatusText());
          displayedValue.setValueColor(info.getValueColor());
          displayedValue.setNumberOfDecimals(settings
              .getNumberOfDecimals());
          displayedValue.setAlignment(settings.getAlignment());
 
          assert !info.getValueColor().equals(Color.white) : "attempting to rendering white text on white foreground";
          if (info.getStatusText().isEmpty() || info.getStatusText().equals(" ")) {
            if (settings.getFontColor() != null) {
              displayedValue.setValueColor(settings.getFontColor());
            }
          }
          model.setValue(model.getKey(multi), displayedValue);
        }
      }
    } else {
      logger.debug("Data was null");
    }
  }

  private List<FeedProvider> getFeedProviders(AbstractComponent component) {

    List<FeedProvider> feedProviders = new ArrayList<FeedProvider>(
        component.getComponents().size());
    for (AbstractComponent referencedComponent : component.getComponents()) {
      FeedProvider fp = referencedComponent.getCapability(
          FeedProvider.class);
      if (fp != null) {
        feedProviders.add(fp);
      }
    }
    return feedProviders;
  }

  /**
   * Formats decimal places for the given value.
   *
   * @param value
   *            current value for the cell
   * @return evaluated value
   */
  private String executeDecimalFormatter(final FeedProvider provider,
      final String feedValue,
      final Map<String, List<Map<String, String>>> data,
      TableCellSettings cellSettings) {
    String rv = feedValue;


    // apply decimal places formatting if appropriate
    FeedType feedType = provider.getFeedType();
    int decimalPlaces = cellSettings.getNumberOfDecimals();
    if (feedType == FeedType.FLOATING_POINT
        || feedType == FeedType.INTEGER) {
      if (decimalPlaces == -1) {
        decimalPlaces = (feedType == FeedType.FLOATING_POINT) ? TableCellSettings.DEFAULT_DECIMALS
            : 0;
      }
      try {
        rv = formats[decimalPlaces]
                     .format(FeedType.FLOATING_POINT
                         .convert(feedValue));
      } catch (IllegalFormatException ife) {
        logger.error("unable to format", ife);
      } catch (NumberFormatException nfe) {
        logger.error(
            "unable to convert value to expected feed value",
            nfe);
      }

     
    }
    return rv;
  }

  private void updateFeedProviders() {
    TableStructure structure = TableViewPolicy
        .getTableStructure(getManifestedComponent());
    updateFeedProviders(structure);
    updateEvalutors();
    loadHeaderSettings();
    loadCellSettings();
  }

  private void loadHeaderSettings() {
    for (int row = 0; row < model.getRowCount(); ++row) {
      String labelAbbreviations = getViewProperties()
          .getProperty("ROW_LABEL_ABBREVIATIONS_" + row, String.class);
      if (labelAbbreviations != null) {
        LabelAbbreviations abbrevs = new LabelAbbreviations();
        abbrevs.addAbbreviationsFromString(labelAbbreviations);
        model.setRowLabelAbbreviations(row, abbrevs);
      }
    }

    for (int col = 0; col < model.getColumnCount(); ++col) {
      String labelAbbreviations = getViewProperties()
          .getProperty("COLUMN_LABEL_ABBREVIATIONS_" + col, String.class);
      if (labelAbbreviations != null) {
        LabelAbbreviations abbrevs = new LabelAbbreviations();
        abbrevs.addAbbreviationsFromString(labelAbbreviations);
        model.setColumnLabelAbbreviations(col, abbrevs);
      }
    }
  }

  private boolean saveHeaderSettings(boolean orientationChanged) {
    boolean settingsChanged = orientationChanged;
    ExtendedProperties viewProperties = getViewProperties();
    for (int row = 0; row < model.getRowCount(); ++row) {
      String oldLabelAbbreviations = getViewProperties()
          .getProperty("ROW_LABEL_ABBREVIATIONS_" + row, String.class);
      String newLabelAbbreviations = model.getRowLabelAbbreviations(
          row).toString();
      if (orientationChanged || !newLabelAbbreviations.equals(oldLabelAbbreviations)) {
        viewProperties.setProperty(
            "ROW_LABEL_ABBREVIATIONS_" + row,
            newLabelAbbreviations);
        settingsChanged = true;
      }
    }

    for (int col = 0; col < model.getColumnCount(); ++col) {
      String oldLabelAbbreviations = getViewProperties()
          .getProperty("COLUMN_LABEL_ABBREVIATIONS_" + col, String.class);
      String newLabelAbbreviations = model
          .getColumnLabelAbbreviations(col).toString();
      if (orientationChanged || !newLabelAbbreviations.equals(oldLabelAbbreviations)) {
        viewProperties.setProperty(
            "COLUMN_LABEL_ABBREVIATIONS_" + col,
            newLabelAbbreviations);
        settingsChanged = true;
      }
    }
    orientationChanged = false;
    return settingsChanged;
  }

  private void loadCellSettings() {
    for (int row = 0; row < model.getRowCount(); ++row) {
      for (int col = 0; col < model.getColumnCount(); ++col) {
        AbstractComponent component = (AbstractComponent) model
            .getStoredValueAt(row, col);
        if (component != null) {
          loadCellSettings(component);
        }
      }
    }
  }

  /**
   * Extracts the evaluators from the set of components. An evaluator is
   * associated with a component if the evaluator references only that
   * component.
   */
  private void updateEvalutors() {
    multipleEvaluators.clear();
    for (AbstractComponent component : getManifestedComponent().getComponents()) {
      addMultisOneLevel(component);
      addMultis(multipleEvaluators, component);
    }
    addMultis(multipleEvaluators, getManifestedComponent());
  }

  private void addMultis(List<AbstractComponent> multis,
      AbstractComponent component) {
    if (isEvaluator(component)
        && component.getCapability(Evaluator.class)
            .requiresMultipleInputs()) {
      multis.add(component);
    }
  }

  private void addMultisOneLevel(AbstractComponent root) {
    for (AbstractComponent component : root.getComponents()) {
      if (isEvaluator(component) && component.getCapability(Evaluator.class).requiresMultipleInputs()) {
        addMultis(multipleEvaluators, component);
      }
    }
  }

  private boolean isEvaluator(AbstractComponent comp) {
    return comp.getCapability(Evaluator.class) != null;
  }

  private void updateFeedProviders(TableStructure structure) {
    ArrayList<FeedProvider> feedProviders = new ArrayList<FeedProvider>();
    timeConversionMap.clear();
    for (int rowIndex = 0; rowIndex < structure.getRowCount(); ++rowIndex) {
      for (int columnIndex = 0; columnIndex < structure
          .getColumnCount(); ++columnIndex) {
        AbstractComponent component = structure.getValue(rowIndex,
            columnIndex);
        if (component != null) {
          FeedProvider fp = getFeedProvider(component);
          if (fp != null) {
            feedProviders.add(fp);
            TimeConversion tc = component.getCapability(TimeConversion.class);
            if (tc != null) {
              timeConversionMap.put(fp.getSubscriptionId(), tc);
            }             
          } else {
            if (component.getCapability(Evaluator.class) != null) {
              for (AbstractComponent referencedComponent : component.getComponents()) {
                fp = getFeedProvider(referencedComponent);
                if (fp != null) {
                  feedProviders.add(fp);
                }
              }
            }
          }
        }
      }
    }
    feedProviders.trimToSize();
    feedProvidersRef.set(feedProviders);
  }

  // Update the view manifestation when the manifestation info has
  // changed in the database.
  @Override
  public void updateMonitoredGUI() {
      if (!updating) {
          setTableSettings(loadSettingsFromPersistence(), table);
        recreateTable();
      }
  }

  @Override
  public void clear(Collection<FeedProvider> feedProviders) {
    for (FeedProvider provider : feedProviders) {
      model.setValue(provider.getSubscriptionId(), "");
    }
  }

  /**
   * Load the settings for the manifestation from persistence.
   *
   * @return
   */
  TableSettings loadSettingsFromPersistence() {
    TableSettings settings = new TableSettings();

    for (TableSettings.AvailableSettings setting : TableSettings.AvailableSettings
        .values()) {
      try {
        String name = setting.name();
        String value = getViewProperties().getProperty(name, String.class);
        settings.setValue(setting, value);
      } catch (Exception ex) {
        logger.error("exception when loading persistent settings", ex);
        // ignore - no persisted setting
      }
    }

    return settings;
  }

  private void loadCellSettings(AbstractComponent component) {
    String id = model.getKey(component);
    TableCellSettings cellSettings = model.getCellSettings(id);
    String value = getViewProperties().getProperty(
        "EVALUATOR_" + id, String.class);
    if (value == null || value.isEmpty()) {
      cellSettings.setEvaluator(null);
    } else {
      cellSettings.setEvaluator(findEvaluator(component, value));
    }

    LabelAbbreviations abbreviations = new LabelAbbreviations();
    value = getViewProperties().getProperty(
        "LABEL_ABBREVIATIONS_" + id, String.class);
    if (value != null && !value.isEmpty()) {
      abbreviations.addAbbreviationsFromString(value);
    }
    model.setCellLabelAbbreviations(id, abbreviations);

    value = getViewProperties().getProperty("DECIMAL_PLACES_" + id, String.class);
    if (value != null && !value.isEmpty()) {
      cellSettings.setNumberOfDecimals(Integer.valueOf(value));
    }
   
    value = getViewProperties().getProperty("FONT_" + id, String.class);
    if (value != null && !value.isEmpty()) {
      cellSettings.setCellFont(Enum.valueOf(TableFormattingConstants.JVMFontFamily.class, value));
    }
   
    value = getViewProperties().getProperty("FONT_COLOR_" + id, String.class);
    if (value != null && !value.isEmpty()) {
      cellSettings.setFontColor(new Color(Integer.valueOf(value).intValue()));
    }
   
    value = getViewProperties().getProperty("BG_COLOR_" + id, String.class);
    if (value != null && !value.isEmpty()) {
      cellSettings.setBackgroundColor(new Color(Integer.valueOf(value).intValue()));
    }
   
    value = getViewProperties().getProperty("FONT_SIZE_" + id, String.class);
    if (value != null && !value.isEmpty()) {
      cellSettings.setFontSize(Integer.valueOf(value).intValue());
    }
   
    value = getViewProperties().getProperty("FONT_STYLE_" + id, String.class);
    if (value != null && !value.isEmpty()) {
      cellSettings.setFontStyle(Integer.valueOf(value).intValue());
    }

    value = getViewProperties().getProperty("AS_DATE_" + id, String.class);
    if (value != null && !value.isEmpty()) {
      cellSettings.setDateFormat(DateFormatItem.valueOf(value));
    }
   
    value = getViewProperties().getProperty("ALIGNMENT_" + id, String.class);
    if (value != null && !value.isEmpty()) {
      cellSettings.setAlignment(ContentAlignment.valueOf(value));
    }
   
    value = getViewProperties().getProperty("BORDER_" + id, String.class);
    if (value != null && !value.isEmpty()) {
      cellSettings.setCellBorderState(new BorderState(value));
    }
   
    value = getViewProperties().getProperty("FONT_UNDERLINE_" + id, String.class);
    if (value != null && !value.isEmpty()) {
      cellSettings.setTextAttributeUnderline(Integer.valueOf(value).intValue());
    }
  }

  private AbstractComponent findEvaluator(AbstractComponent component,
      String id) {
    // The component may be its own evaluator.
    if (id.equals(component.getId())) {
      return component;
    }

    AbstractComponent parent = AbstractComponent.getComponentById(id);
    Evaluator e = parent == null ? null : parent.getCapability(Evaluator.class);
    if (e != null && !e.requiresMultipleInputs()
        && parent.getId().equals(id)) {

      return parent;
    }

    return null;
  }

  private void saveSettingsToPersistence() {
    boolean settingsChanged = false;
    TableSettings settings = getCurrentTableSettings(table);
    ExtendedProperties viewProperties = getViewProperties();
    for (TableSettings.AvailableSettings setting : TableSettings.AvailableSettings
        .values()) {
      String currentValue = getViewProperties().getProperty(
          setting.name(), String.class);
      String newValue = settings.getValue(setting);
      assert (newValue != null) : "Table setting for "
          + setting.toString() + " has null value";
      if (!newValue.equals(currentValue)) {
        viewProperties.setProperty(setting.name(),
            newValue);
        settingsChanged = true;
      }
    }

    // Save label settings.
    settingsChanged = (saveHeaderSettings(orientationChanged) || settingsChanged);

    // Also save all cell settings.
    for (int row = 0; row < model.getRowCount(); ++row) {
      for (int col = 0; col < model.getColumnCount(); ++col) {
        AbstractComponent component = (AbstractComponent) model
            .getStoredValueAt(row, col);
        if (component != null) {
          settingsChanged = (saveCellSettings(component) || settingsChanged);
        }
      }
    }

    if (settingsChanged) {
        try {
            updating = true;
            table.updateColumnsHeaderValuesOnly();
          getManifestedComponent().save();
        } finally {
            updating = false;
        }
    }
  }

  private boolean saveCellSettings(AbstractComponent component) {
    boolean settingsChanged = false;

    String id = model.getKey(component);
    TableCellSettings cellSettings = model.getCellSettings(id);
    String propertyName = "EVALUATOR_" + id;
    String currentValue = getViewProperties().getProperty(
        propertyName, String.class);
    ExtendedProperties viewProperties = getViewProperties();
    if (currentValue == null) {
      currentValue = "";
    }
    String newValue = (cellSettings.getEvaluator() == null) ? ""
        : cellSettings.getEvaluator().getId();
    if (!currentValue.equals(newValue)) {
      viewProperties.setProperty(propertyName, newValue);
      settingsChanged = true;
    }

    LabelAbbreviations abbrevs = model.getCellLabelAbbreviations(id);
    propertyName = "LABEL_ABBREVIATIONS_" + id;
    currentValue = getViewProperties().getProperty(propertyName, String.class);
    if (currentValue == null) {
      currentValue = "";
    }
    newValue = abbrevs.toString();
    if (!currentValue.equals(newValue)) {
      viewProperties.setProperty(propertyName, newValue);
      settingsChanged = true;
    }

    propertyName = "DECIMAL_PLACES_" + id;
    currentValue = getViewProperties().getProperty(propertyName, String.class);
    if (currentValue == null) {
      currentValue = "";
    }
    newValue = Integer.toString(cellSettings.getNumberOfDecimals());
    if (!currentValue.equals(newValue)) {
      viewProperties.setProperty(propertyName, newValue);
      settingsChanged = true;
    }

    propertyName = "ALIGNMENT_" + id;
    currentValue = getViewProperties().getProperty(propertyName, String.class);
    if (currentValue == null) {
      currentValue = "";
    }
    newValue = cellSettings.getAlignment().toString();
    if (!currentValue.equals(newValue)) {
      viewProperties.setProperty(propertyName, newValue);
      settingsChanged = true;
    }

    propertyName = "BORDER_" + id;
    currentValue = getViewProperties().getProperty(propertyName, String.class);
    if (currentValue == null) {
      currentValue = "";
    }
    newValue = cellSettings.getCellBorderState().toString();
    if (!currentValue.equals(newValue)) {
      viewProperties.setProperty(propertyName, newValue);
      settingsChanged = true;
    }
   
    propertyName = "AS_DATE_" + id;
    currentValue = getViewProperties().getProperty(propertyName, String.class);
    if (currentValue == null) {
      currentValue = "";
    }
   
    newValue = cellSettings.getDateFormat().toString();
    if (!currentValue.equals(newValue)) {
      viewProperties.setProperty(propertyName, newValue);
      settingsChanged = true;
    }
   
    propertyName = "FONT_" + id;
    currentValue = getViewProperties().getProperty(propertyName, String.class);
    if (currentValue == null) {
      currentValue = "";
    }
    newValue = cellSettings.getCellFont().name();
    newValue = (newValue == null ? "" : newValue);
    if (!currentValue.equals(newValue)) {
      viewProperties.setProperty(propertyName, newValue);
      settingsChanged = true;
    }
   
    propertyName = "FONT_SIZE_" + id;
    currentValue = getViewProperties().getProperty(propertyName, String.class);
    if (currentValue == null) {
      currentValue = "";
    }
    newValue = String.valueOf(cellSettings.getFontSize());
    if (!currentValue.equals(newValue)) {
      viewProperties.setProperty(propertyName, newValue);
      settingsChanged = true;
    }
   
    propertyName = "FONT_COLOR_" + id;
    currentValue = getViewProperties().getProperty(propertyName, String.class);
    if (currentValue == null) {
      currentValue = "";
    }
    newValue = (cellSettings.getForegroundColor() != null ?
        String.valueOf(cellSettings.getForegroundColor().getRGB()) : "");
    if (!currentValue.equals(newValue)) {
      viewProperties.setProperty(propertyName, newValue);
      settingsChanged = true;
    }
   
    propertyName = "BG_COLOR_" + id;
    currentValue = getViewProperties().getProperty(propertyName, String.class);
    if (currentValue == null) {
      currentValue = "";
    }
    newValue = (cellSettings.getBackgroundColor() != null ?
        String.valueOf(cellSettings.getBackgroundColor().getRGB()) : "");
    if (!currentValue.equals(newValue)) {
      viewProperties.setProperty(propertyName, newValue);
      settingsChanged = true;
    }
   
    propertyName = "FONT_STYLE_" + id;
    currentValue = getViewProperties().getProperty(propertyName, String.class);
    if (currentValue == null) {
      currentValue = "";
    }
    newValue = String.valueOf(cellSettings.getFontStyle());
    if (!currentValue.equals(newValue)) {
      viewProperties.setProperty(propertyName, newValue);
      settingsChanged = true;
    }
   
    propertyName = "FONT_UNDERLINE_" + id;
    currentValue = getViewProperties().getProperty(propertyName, String.class);
    if (currentValue == null) {
      currentValue = "";
    }
    newValue = String.valueOf(cellSettings.getTextAttributeUnderline());
    if (!currentValue.equals(newValue)) {
      viewProperties.setProperty(propertyName, newValue);
      settingsChanged = true;
    }
   
    return settingsChanged;

  }

  private void setTableSettings(TableSettings settings, LabeledTable table) {
    TableOrientation orientation = settings.getOrientation();
    int[] columnWidths = settings.getColumnWidths();
    int[] columnOrder = settings.getColumnOrder();
    int[] rowHeights = settings.getRowHeights();
    ContentAlignment[] rowHeaderAlignments = settings
        .getRowHeaderAlignments();
    ContentAlignment[] columnHeaderAlignments = settings
        .getColumnHeaderAlignments();
    JVMFontFamily[] rowFontNames = settings.getRowFontNames();
    int[] rowFontColors = settings.getRowFontColors();
    int[] rowBackgroundColors = settings.getRowBackgroundColors();
    int[] rowHeaderBorderColors = settings.getRowHeaderBorderColors();
    int[] rowFontSizes = settings.getRowFontSizes();
    int[] rowFontStyles = settings.getRowFontStyles();
    int[] rowTextAttributes = settings.getRowHeaderTextAttributes();
    BorderState[] rowHeaderBorderStates = settings.getRowHeaderBorderStates();
    BorderState[] columnHeaderBorderStates = settings.getColumnHeaderBorderStates();
    JVMFontFamily[] colFontNames = settings.getColumnFontNames();
    int[] columnFontColors = settings.getColumnFontColors();
    int[] columnBackgroundColors = settings.getColumnBackgroundColors();
    int[] columnHeaderBorderColors = settings.getColumnHeaderBorderColors();
    int[] colFontSizes = settings.getColumnFontSizes();
    int[] colFontStyles = settings.getColumnFontStyles();
    int[] columnTextAttributes = settings.getColumnHeaderTextAttributes();
   

    if (orientation != null) {
      labelingAlgorithm.setOrientation(orientation);
      model.setOrientation(orientation);
      table.updateDropMode();
    }
    // Set the column order first, so that the right values get the
    // right
    // width once we set the widths.
    if (columnOrder != null) {
      table.setColumnOrder(columnOrder);

    }
    if (columnWidths != null) {
      table.setColumnWidths(columnWidths);
    }
    if (rowHeights != null) {
      table.setRowHeights(rowHeights);
    }
    if (rowHeaderAlignments != null) {
      table.setRowHeaderAlignments(rowHeaderAlignments);
    }
    if (columnHeaderAlignments != null) {
      table.setColumnHeaderAlignments(columnHeaderAlignments);
    }
    if (rowFontNames != null) {
      table.setRowHeaderFontNames(rowFontNames);
    }
    if (rowFontColors != null) {
      table.setRowHeaderFontColors(intToColorArray(rowFontColors));
    }
    if (rowBackgroundColors != null) {
      table.setRowHeaderBackgroundColors(intToColorArray(rowBackgroundColors));
    }
    if (rowHeaderBorderColors != null) {
      table.setRowHeaderBorderColors(intToColorArray(rowHeaderBorderColors));
    }
    if (rowFontSizes != null) {
      table.setRowHeaderFontSizes(intToIntegerArray(rowFontSizes));
    }
    if (rowFontStyles != null) {
      table.setRowHeaderFontStyles(intToIntegerArray(rowFontStyles));
    }
    if (rowTextAttributes != null) {
      table.setRowHeaderTextAttributes(intToIntegerArray(rowTextAttributes));
    }
    if (rowHeaderBorderStates != null) {
      table.setRowHeaderBorderStates(rowHeaderBorderStates);
    }
    if (colFontNames != null) {
      table.setColumnHeaderFontNames(colFontNames);
    }
    if (columnFontColors != null) {
      table.setColumnHeaderFontColors(intToColorArray(columnFontColors));
    }
    if (columnBackgroundColors != null) {
      table.setColumnHeaderBackgroundColors(intToColorArray(columnBackgroundColors));
    }
    if (columnHeaderBorderColors != null) {
      table.setColumnHeaderBorderColors(intToColorArray(columnHeaderBorderColors));
    }
    if (colFontSizes != null) {
      table.setColumnHeaderFontSizes(intToIntegerArray(colFontSizes));
    }
    if (colFontStyles != null) {
      table.setColumnHeaderFontStyles(intToIntegerArray(colFontStyles));
    }
   
    if (columnTextAttributes != null) {
      table.setColumnHeaderTextAttributes(intToIntegerArray(columnTextAttributes));
    }
    if (columnHeaderBorderStates != null) {
      table.setColumnHeaderBorderStates(columnHeaderBorderStates);
    }
    table.getTable().setShowGrid(settings.isShowGrid());
  }

  private TableSettings getCurrentTableSettings(LabeledTable table) {
    TableSettings settings = new TableSettings();
    settings.setOrientation(model.getOrientation());
    settings.setColumnWidths(table.getColumnWidths());
    settings.setColumnOrder(table.getColumnOrder());
    settings.setShowGrid(table.getShowGrid());
    settings.setRowHeights(integerToIntArray(table.getRowHeights()));
    settings.setRowHeaderAlignments(table.getRowHeaderAlignments());
    settings.setColumnHeaderAlignments(table
        .getColummnHeaderAlignments());
    settings.setRowFontNames(table.getRowHeaderFontNames());
    settings.setRowFontColors(colorToIntArray(table.getRowHeaderFontColors()));
    settings.setRowHeaderBorderColors(colorToIntArray(table.getRowHeaderBorderColors()));
    settings.setRowBackgroundColors(colorToIntArray(table.getRowHeaderBackgroundColors()));
    settings.setRowFontSizes(integerToIntArray(table.getRowHeaderFontSizes()));
    settings.setRowFontStyles(integerToIntArray(table.getRowHeaderFontStyles()));
    settings.setRowTextAttributes(integerToIntArray(table.getRowHeaderTextAttributes()));
    settings.setColumnFontNames(table.getColumnHeaderFontNames());
    settings.setColumnFontColors(colorToIntArray(table.getColumnHeaderFontColors()));
    settings.setColumnBackgroundColors(colorToIntArray(table.getColumnHeaderBackgroundColors()));
    settings.setColumnHeaderBorderColors(colorToIntArray(table.getColumnHeaderBorderColors()));
    settings.setColumnFontSizes(integerToIntArray(table.getColumnHeaderFontSizes()));
    settings.setColumnFontStyles(integerToIntArray(table.getColumnHeaderFontStyles()));
    settings.setColumnTextAttributes(integerToIntArray(table.getColumnHeaderTextAttributes()));
    settings.setRowHeaderBorderStates(table.getRowHeaderBorderStates());
    settings.setColumnHeaderBorderStates(table.getColumnHeaderBorderStates());
   

    return settings;
  }
 
  private Color[] intToColorArray(int[] a) {
    Color[] newArray = new Color[a.length];
    for (int i = 0; i < a.length ; i++) {
      newArray[i] = new Color(a[i]);
    }
    return newArray;
  }
 
  private int[] colorToIntArray(Color[] a) {
    int[] newArray = new int[a.length];
    for (int i = 0; i < a.length ; i++) {
      newArray[i] = a[i].getRGB();
    }
    return newArray;
  }
 
  private int[] integerToIntArray(Integer[] a) {
    int[] newArray = new int[a.length];
    for (int i = 0; i < a.length ; i++) {
      newArray[i] = a[i].intValue();
    }
    return newArray;
  }
 
  private Integer[] intToIntegerArray(int[] a) {
    Integer[] newArray = new Integer[a.length];
    for (int i = 0; i < a.length ; i++) {
      newArray[i] = Integer.valueOf(a[i]);
    }
    return newArray;
  }

  /**
   * Save the cell settings when they are changed in the formatting panel.
   */
  public void saveCellSettings() {
    saveSettingsToPersistence();
  }

  /**
   * Save the cell settings when table orientation is changed in the formatting panel.
   */
  public void saveCellSettingsUponTableOrientationChange() {
    orientationChanged = true;
    saveSettingsToPersistence();
  }
 
  /**
   * Performs the object interactions required to drop an object into the
   * table.
   *
   * @param sourceViews
   *            an array of view roles for the components dropped onto the
   *            table
   * @param row
   *            the row at which the drop occurred
   * @param column
   *            the column at which the drop occurred
   * @param isInsertRow
   *            true, if we should insert a row at the drop position
   * @param isInsertColumn
   *            true, if we should insert a column at the drop position
   * @return true, if the drop was successful
   */
  public boolean handleDrop(View[] sourceViews, int row,
      int column, boolean isInsertRow, boolean isInsertColumn) {

    if (sourceViews.length > 0) {
      AbstractComponent component = sourceViews[0].getManifestedComponent();
      Collection<AbstractComponent> sourceComponents = Collections
          .singleton(component);
      AbstractComponent targetComponent = model
          .getModifiedComponentAt(row, column, isInsertRow,
              isInsertColumn);

      final ExecutionResult result = checkDropPolicy(targetComponent,
          sourceComponents, this);

      if (result.getStatus()) {
        model.setValueAt(component, row, column, isInsertRow,
            isInsertColumn);
        recreateTable();
      } else {
        // Action is _not_ permitted under policy constraint
        SwingUtilities.invokeLater(new Runnable() {
          @Override
          public void run() {
            // Inform the user that policy prohibited the
            // operation.
            OptionBox.showMessageDialog(
                TableViewManifestation.this,
                result.getMessage(),
                "Composition Error - ",
                OptionBox.ERROR_MESSAGE);
          }
        });
      }
    }

    return true;
  }

  private ExecutionResult checkDropPolicy(
      AbstractComponent targetComponent,
      Collection<AbstractComponent> sourceComponents,
      View targetViewManifesation) {
    // Establish policy context.
    PolicyContext context = new PolicyContext();
    context.setProperty(
        PolicyContext.PropertyName.TARGET_COMPONENT.getName(),
        targetComponent);
    context.setProperty(
        PolicyContext.PropertyName.SOURCE_COMPONENTS.getName(),
        sourceComponents);
    context.setProperty(PolicyContext.PropertyName.ACTION.getName(),
        Character.valueOf('w'));
    context.setProperty(
        PolicyContext.PropertyName.VIEW_MANIFESTATION_PROVIDER
            .getName(), targetViewManifesation);
    String compositionKey = PolicyInfo.CategoryType.COMPOSITION_POLICY_CATEGORY
        .getKey();
    // Execute policy
    return ServiceAccess.getService(PolicyManager.class).execute(
        compositionKey, context);
  }
 
  /**
     * Gets abbreviation view properties from persistence. Does a swap of row/col upon a table orientation
     * and persists.  Also updates the the new properties in the model. Uses the inclusive
     * square so that persisted settings for non-square tables are removed.
     * @param orientation the new table orientation
     */
  public void swapAbbreviationsUponTableOrientationChange(TableOrientation orientation) {

    int inclusiveSquare = Math.max(model.getRowCount(), model.getColumnCount());

    String[] rowAbbrevProperties = new String[inclusiveSquare];
    String[] colAbbrevProperties = new String[inclusiveSquare];

    // get all the row and col headers
    for (int i = 0; i < inclusiveSquare; i++) {
      rowAbbrevProperties[i] = getViewProperties().getProperty("ROW_LABEL_ABBREVIATIONS_" + i, String.class);
    }
    for (int j = 0; j < inclusiveSquare; j++) {
      colAbbrevProperties[j] = getViewProperties().getProperty("COLUMN_LABEL_ABBREVIATIONS_" + j, String.class);
    }

    //swap for persistence.  Also update the model.
    String labelAbbreviations = null;

    for (int i = 0; i < inclusiveSquare; i++) {
      labelAbbreviations = rowAbbrevProperties[i];
      if (labelAbbreviations == null) labelAbbreviations = "";
      // rather  than setting view properties here, we let saveHeaderSettings() set view properties.
      LabelAbbreviations abbrevs = new LabelAbbreviations();
            abbrevs.addAbbreviationsFromString(labelAbbreviations);
      model.setColumnLabelAbbreviations(i, abbrevs);
    }
    for (int j = 0; j < inclusiveSquare; j++) {
      labelAbbreviations = colAbbrevProperties[j];
      if (labelAbbreviations == null) labelAbbreviations = "";
      LabelAbbreviations abbrevs = new LabelAbbreviations();
      abbrevs.addAbbreviationsFromString(labelAbbreviations);
      model.setRowLabelAbbreviations(j, abbrevs);
    }
  }

  /**
   * @author dcberrio
   * A ListSelectionListener that caches the components selected in a table, in
   * order to conflate row and column change events
   */
  public class TableSelectionListener implements ListSelectionListener {

    /**
     * keep track of the last selected set of components this is done to eliminate duplicate events when
     * both the row and column have changed, since this would result in two events.
     */
    private Set<AbstractComponent> lastSelectedComponents = new HashSet<AbstractComponent>();
   
    private boolean lastSelectedComponentsAreSame(Collection<View> selectedManifestations) {
      Set<AbstractComponent> currentSelectedComponents = new HashSet<AbstractComponent>();
      for (View view:selectedManifestations) {
        currentSelectedComponents.add(view.getManifestedComponent());
      }
      boolean sameList = currentSelectedComponents.size() == lastSelectedComponents.size() &&
                 lastSelectedComponents.containsAll(currentSelectedComponents);
      lastSelectedComponents = currentSelectedComponents;
      return sameList;
    }
   
    @Override
    public void valueChanged(ListSelectionEvent e) {
      if (e.getValueIsAdjusting()) {
        return;
      }
     
      Collection<View> selectedManifestations = getSelectedManifestations();
      if (selectedManifestations.isEmpty())
        return;
     
      if (!lastSelectedComponentsAreSame(selectedManifestations) && !isClearingSelection) {
        TableViewManifestation.this.firePropertyChange(
            SelectionProvider.SELECTION_CHANGED_PROP,
            null, selectedManifestations);
      }
    }
   
    public void clearLastSelectedComponents() {
      lastSelectedComponents.clear();
    }

  }
}
TOP

Related Classes of gov.nasa.arc.mct.table.view.TableViewManifestation

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.