Package gov.nasa.arc.mct.fastplot.bridge

Source Code of gov.nasa.arc.mct.fastplot.bridge.PlotView$Builder

/*******************************************************************************
* 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.fastplot.bridge;


import gov.nasa.arc.mct.components.FeedProvider;
import gov.nasa.arc.mct.fastplot.bridge.PlotConstants.AxisOrientationSetting;
import gov.nasa.arc.mct.fastplot.bridge.PlotConstants.LimitAlarmState;
import gov.nasa.arc.mct.fastplot.bridge.PlotConstants.TimeAxisSubsequentBoundsSetting;
import gov.nasa.arc.mct.fastplot.bridge.PlotConstants.XAxisMaximumLocationSetting;
import gov.nasa.arc.mct.fastplot.bridge.PlotConstants.YAxisMaximumLocationSetting;
import gov.nasa.arc.mct.fastplot.bridge.controls.BoundaryArrow;
import gov.nasa.arc.mct.fastplot.bridge.controls.ControllableAxis;
import gov.nasa.arc.mct.fastplot.bridge.controls.CornerResetButton;
import gov.nasa.arc.mct.fastplot.bridge.controls.LocalControlKeyEventDispatcher;
import gov.nasa.arc.mct.fastplot.bridge.controls.PanControls;
import gov.nasa.arc.mct.fastplot.bridge.controls.ZoomControls;
import gov.nasa.arc.mct.fastplot.scatter.NonTimeFixedBoundManager;
import gov.nasa.arc.mct.fastplot.settings.LineSettings;
import gov.nasa.arc.mct.fastplot.settings.PlotConfiguration;
import gov.nasa.arc.mct.fastplot.settings.PlotConfigurationDelegator;
import gov.nasa.arc.mct.fastplot.settings.PlotSettings;
import gov.nasa.arc.mct.fastplot.utils.AbbreviatingPlotLabelingAlgorithm;
import gov.nasa.arc.mct.fastplot.view.Axis;
import gov.nasa.arc.mct.fastplot.view.LegendEntryPopupMenuFactory;
import gov.nasa.arc.mct.fastplot.view.PinSupport;
import gov.nasa.arc.mct.fastplot.view.Pinnable;
import gov.nasa.arc.mct.fastplot.view.PlotViewManifestation;
import gov.nasa.arc.mct.fastplot.view.legend.AbstractLegendEntry;
import gov.nasa.arc.mct.gui.FeedView.SynchronizationControl;

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.ArrayList;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeMap;

import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;

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


/**
* Implementation of the general plot interface.
*
* Construct using the builder pattern.
*
* Example:
* <code>Plot.Builder(new <GeneralPlottingPackageInterface).plotName("Your Plot Name").build();</code>
*
* RefinedAbstraction in bridge pattern.
*/

public class PlotView extends PlotConfigurationDelegator implements PlotAbstraction {
 
  private final static Logger logger = LoggerFactory.getLogger(PlotView.class)

  private static final Timer timer = new Timer();

    // Manifestation holding this plot
  private PlotViewManifestation plotUser; 
 
  // Used in time synchronization line mode.
  private SynchronizationControl synControl;
 
  private Class <? extends AbstractPlottingPackage> plotPackage;
  private String plotName;
   
  /* Appearance Constants */
  // - Fonts
  private Font timeAxisFont;

  // Thickness of the plotted lines.
  private int plotLineThickness;

  private Color plotBackgroundFrameColor;
  private Color plotAreaBackgroundColor;

  // - Axis
  // -- x-axis
  // Point where x-axis intercepts y axis
  private int timeAxisIntercept;
  //color for drawing x-axis
  private Color timeAxisColor;
  // x-axis labels
  private Color timeAxisLabelColor;
  private Color timeAxisLabelTextColor;

  // format of the date when shown on the x-axis
  private String timeAxisDataFormat;

  // -- y-axis
  private Color nonTimeAxisColor;

  // - Gridlines
  private Color gridLineColor;

  /* Scrolling and scaling behaviors */
  // Number of sample to accumulate before autoscaling the y-axis. This
  // prevents rapid changing of the y axis.
  private int minSamplesForAutoScale;
    
    private boolean compressionEnabled;
    private boolean localControlsEnabled;
    private int numberOfSubPlots;
   
   
    /** The list of sub plots. */
    public List<AbstractPlottingPackage> subPlots;
   
    /** The plot panel. */
    JPanel plotPanel;
   
    /** Map for containing the data set name to sub-group. */
    public Map<String, Set<AbstractPlottingPackage>> dataSetNameToSubGroupMap = new HashMap<String, Set<AbstractPlottingPackage>>();
   
    /** Map for containing the data set name to display map. */
    public Map<String, String> dataSetNameToDisplayMap = new HashMap<String, String>();
   
    private AbbreviatingPlotLabelingAlgorithm plotLabelingAlgorithm = new AbbreviatingPlotLabelingAlgorithm();;
   
    /** List of plot subjects. */
    List<PlotSubject> subPlotsToIgnoreNextUpdateFrom = new ArrayList<PlotSubject>();
   
    /** Time axis at start of update cycle. */
    GregorianCalendar timeAxisMaxAtStartOfDataUpdateCycle = new GregorianCalendar();
   
    /** Lock updates flag. */
    boolean lockUpdates = false;

  private PinSupport pinSupport = new PinSupport() {
    protected void informPinned(boolean pinned) {
      if(pinned) {
        pause();
      } else {
        unpause();
      }
    }
  };

  private Pinnable timeSyncLinePin = createPin();

  private Axis timeAxis = new Axis();


  private Pinnable timeAxisUserPin = timeAxis.createPin();

  private TimerTask updateTimeBoundsTask;

  private AbstractAxis plotTimeAxis;

  /**
   * Sets the plot view manifestation.
   * @param theManifestation - the plot view manifestation.
   */
    public void setManifestation(PlotViewManifestation theManifestation) {
        if (theManifestation == null ) {
         throw new IllegalArgumentException("Plot must not have a null user");
        }
     
      plotUser = theManifestation;

      for (AbstractPlottingPackage p: subPlots) {
          p.setPlotAbstraction(this);
      }
    }
   
   
    @Override
  public JPanel getPlotPanel() {
    return plotPanel;
  }
 
  @Override
  public void addDataSet(String dataSetName) {
    addDataSet(dataSetName.toLowerCase(), getNextColor(subPlots.size()-1));
  }
 
  @Override
  public void addDataSet(String dataSetName, Color plottingColor) {
    throwIllegalArgumentExcpetionIfWeHaveNoPlots();
   
    getLastPlot().addDataSet(dataSetName.toLowerCase(), plottingColor);
    String name = dataSetName.toLowerCase();
    Set<AbstractPlottingPackage> set = dataSetNameToSubGroupMap.get(name);
    if(set == null) {
      set = new HashSet<AbstractPlottingPackage>();
      dataSetNameToSubGroupMap.put(name, set);
    }
    set.add(getLastPlot());
    dataSetNameToDisplayMap.put(dataSetName.toLowerCase(), dataSetName);
  }

  @Override
  public void addDataSet(String dataSetName, String displayName) {
    throwIllegalArgumentExcpetionIfWeHaveNoPlots();
   
    getLastPlot().addDataSet(dataSetName.toLowerCase(), getNextColor(subPlots.size()-1), displayName);
    String name = dataSetName.toLowerCase();
    Set<AbstractPlottingPackage> set = dataSetNameToSubGroupMap.get(name);
    if(set == null) {
      set = new HashSet<AbstractPlottingPackage>();
      dataSetNameToSubGroupMap.put(name, set);
    }
    set.add(getLastPlot());
    dataSetNameToDisplayMap.put(dataSetName.toLowerCase(), displayName);
  }

  /**
   * Adds the data set per subgroup index, data set name and display name.
   * @param subGroupIndex - the subgroup index.
   * @param dataSetName - data set name.
   * @param displayName - base display name.
   */
  public void addDataSet(int subGroupIndex, String dataSetName, String displayName) {
    throwIllegalArgumentExcpetionIfIndexIsNotInSubPlots(subGroupIndex);

    String lowerCaseDataSetName = dataSetName.toLowerCase();
    int actualIndex = subGroupIndex;
    subPlots.get(actualIndex).addDataSet(lowerCaseDataSetName,
        getNextColor(subGroupIndex), displayName);
    Set<AbstractPlottingPackage> set = dataSetNameToSubGroupMap
        .get(lowerCaseDataSetName);
    if (set == null) {
      set = new HashSet<AbstractPlottingPackage>();
      dataSetNameToSubGroupMap.put(lowerCaseDataSetName, set);
    }
    set.add(subPlots.get(actualIndex));
    dataSetNameToDisplayMap.put(lowerCaseDataSetName, displayName);
  }
 
  /**
   * Adds the data set per subgroup index, data set name and display name.
   * @param subGroupIndex - the subgroup index.
   * @param dataSetName - data set name.
   * @param displayName - base display name.
   */
  public void addDataSet(int subGroupIndex, String dataSetName, AbstractLegendEntry legendEntry) {
    throwIllegalArgumentExcpetionIfIndexIsNotInSubPlots(subGroupIndex);

    String lowerCaseDataSetName = dataSetName.toLowerCase();
    int actualIndex = subGroupIndex;
    subPlots.get(actualIndex).getLegendManager().addLegendEntry(legendEntry);
    subPlots.get(actualIndex).addDataSet(lowerCaseDataSetName,
        getNextColor(subGroupIndex), legendEntry);
    Set<AbstractPlottingPackage> set = dataSetNameToSubGroupMap
        .get(lowerCaseDataSetName);
    if (set == null) {
      set = new HashSet<AbstractPlottingPackage>();
      dataSetNameToSubGroupMap.put(lowerCaseDataSetName, set);
    }
    set.add(subPlots.get(actualIndex));
    dataSetNameToDisplayMap.put(lowerCaseDataSetName, legendEntry.getDisplayedName());
  }
 
 
  /**
   * Adds the popup menus to plot legend entry.
   */
  public void addPopupMenus() {
    LegendEntryPopupMenuFactory popupManager = new LegendEntryPopupMenuFactory(plotUser);
    for (int index = 0; index < subPlots.size(); index++) {
      AbstractPlottingPackage plot = subPlots.get(index);
      for (LegendEntry entry : plot.getLegendManager().getLegendEntryList()) {
        entry.setPopup(popupManager);
      }
    }
  }

 
  /**
   * Get per-line settings currently in use for this stack of plots.
   * Each element of the returned list corresponds,
   * in order, to the sub-plots displayed, and maps subscription ID to a
   * LineSettings object describing how its plot line should be drawn.
   * @return a list of subscription->setting mappings for this plot
   */
  public List<Map<String, LineSettings>> getLineSettings() {
    List<Map<String,LineSettings>> settingsAssignments = new ArrayList<Map<String,LineSettings>>();
    for (int subPlotIndex = 0; subPlotIndex < subPlots.size(); subPlotIndex++) {
      Map<String, LineSettings> settingsMap = new HashMap<String, LineSettings>();
      settingsAssignments.add(settingsMap);
      AbstractPlottingPackage plot = subPlots.get(subPlotIndex);
      for (LegendEntry entry : plot.getLegendManager().getLegendEntryList()) {
        settingsMap.put(entry.getDataSetName(), entry.getLineSettings());
      }
    } 
    return settingsAssignments;
  }
 

  /**
   * Set line settings for use in this stack of plots.
   * Each element corresponds, in order, to the sub-plots displayed, and maps
   * subscription ID to the line settings described by a LineSettings object
   * @param lineSettings a list of subscription->line setting mappings for this plot
   */
  public void setLineSettings(
      List<Map<String, LineSettings>> lineSettings) {
    if (lineSettings != null) {
      for (int subPlotIndex = 0; subPlotIndex < lineSettings.size() && subPlotIndex < subPlots.size(); subPlotIndex++) {
        AbstractPlottingPackage plot = subPlots.get(subPlotIndex);     
        for (Entry<String, LineSettings> entry : lineSettings.get(subPlotIndex).entrySet()) {
          AbstractPlotDataSeries series = plot.getPlotDataManager().getNamedDataSeries(entry.getKey());
          if (series != null) {
            AbstractLegendEntry legendEntry = series.getLegendEntry();
            if (legendEntry instanceof LegendEntry) {
              ((LegendEntry) legendEntry).setLineSettings(entry.getValue());
            }
          }
        }
      }
    }   
  }

 
  @Override
  public boolean isKnownDataSet(String setName) {
    assert setName != null : "data set is null";
    for (AbstractPlottingPackage p : subPlots) {
      if (p.isKnownDataSet(setName.toLowerCase())) return true;
    }
    return false;
    //return dataSetNameToSubGroupMap.containsKey(setName.toLowerCase());
  }

  @Override
  public void refreshDisplay() {
    for (AbstractPlottingPackage p: subPlots) {
      p.refreshDisplay();
    }
  }
 
  public void initialDataRequest() {
    plotUser.requestDataRefresh(getLastPlot().getCurrentTimeAxisMin(), getLastPlot().getCurrentTimeAxisMax());
  }


  @Override
  public void updateLegend(String dataSetName, FeedProvider.RenderingInfo info) {
    String dataSetNameLower = dataSetName.toLowerCase();
    if (dataSetNameToSubGroupMap.containsKey(dataSetNameLower)) {
      for(AbstractPlottingPackage plot : dataSetNameToSubGroupMap.get(dataSetNameLower)) {     
        plot.updateLegend(dataSetNameLower, info);
      }
    }
  }
 
  @Override
  public LimitAlarmState getNonTimeMaxAlarmState(int subGroupIndex) {
    throwIllegalArgumentExcpetionIfIndexIsNotInSubPlots(subGroupIndex);
   
    return subPlots.get(subGroupIndex).getDependentMaxAlarmState();
  }
 
  @Override
  public LimitAlarmState getNonTimeMinAlarmState(int subGroupIndex) {
    throwIllegalArgumentExcpetionIfIndexIsNotInSubPlots(subGroupIndex);
   
      return subPlots.get(subGroupIndex).getDependentMinAlarmState();
  }
 
 
 
  @Override
  public void showTimeSyncLine(GregorianCalendar time) {
    assert time != null;
    timeSyncLinePin.setPinned(true);
    for (AbstractPlottingPackage p: subPlots) {
     p.showTimeSyncLine(time);
    }
  }
 
  @Override
  public void removeTimeSyncLine() {
    timeSyncLinePin.setPinned(false);
    for (AbstractPlottingPackage p: subPlots) {
       p.removeTimeSyncLine();
    }
  }
 
  @Override
  public boolean isTimeSyncLineVisible() {
    return getLastPlot().isTimeSyncLineVisible();
  }
 
  @Override
  public void initiateGlobalTimeSync(GregorianCalendar time) {
    synControl = plotUser.synchronizeTime(time.getTimeInMillis());
  }
 
  @Override
  public void updateGlobalTimeSync(GregorianCalendar time) {
    if(synControl == null) {
      synControl = plotUser.synchronizeTime(time.getTimeInMillis());
    } else {
      synControl.update(time.getTimeInMillis());
    }
  }

  @Override
  public void notifyGlobalTimeSyncFinished() {
    if (synControl!=null) {
      synControl.synchronizationDone();
      }
    removeTimeSyncLine();
  }
 
  @Override
  public boolean inTimeSyncMode() {
    return getLastPlot().inTimeSyncMode();
  }
 
  private Color getNextColor(int subGroupIndex) {
    throwIllegalArgumentExcpetionIfIndexIsNotInSubPlots(subGroupIndex);
   
    if (subPlots.get(subGroupIndex).getDataSetSize() < PlotLineColorPalette.getColorCount()) {
      return PlotLineColorPalette.getColor(subPlots.get(subGroupIndex).getDataSetSize());
    } else {
      // Exceeded the number of colors in the pallet.
      return PlotConstants.ROLL_OVER_PLOT_LINE_COLOR;
    }
  }
 
  @Override
  public double getNonTimeMaxCurrentlyDisplayed() {
    return getLastPlot().getNonTimeMaxDataValueCurrentlyDisplayed();
  }
     
  @Override
  public double getNonTimeMinCurrentlyDisplayed() {
    return getLastPlot().getNonTimeMinDataValueCurrentlyDisplayed();
  }

  @Override
  public String toString() {
    assert plotPackage != null : "Plot package not initalized";

    return "Plot: + " + plotName + "\n" + plotPackage.toString();
  }

  /**
   * Construct plots using the builder pattern.
   *
   */
  public static class Builder {
    // Required parameters
    private Class<? extends AbstractPlottingPackage> plotPackage;

    //Optional parameters
    // default values give a "traditional" chart with time on the x-axis etc.
    private String plotName = "Plot Name Undefined";
    private PlotConfiguration settings = new PlotSettings();
   
    // initial settings
    private Font timeAxisFont = PlotConstants.DEFAULT_TIME_AXIS_FONT;
    private int plotLineThickness = PlotConstants.DEFAULT_PLOTLINE_THICKNESS ;
    private Color plotBackgroundFrameColor = PlotConstants.DEFAULT_PLOT_FRAME_BACKGROUND_COLOR;
    private Color plotAreaBackgroundColor = PlotConstants.DEFAULT_PLOT_AREA_BACKGROUND_COLOR;
    private int timeAxisIntercept = PlotConstants.DEFAULT_TIME_AXIS_INTERCEPT;
    private Color timeAxisColor = PlotConstants.DEFAULT_TIME_AXIS_COLOR;
    private Color timeAxisLabelColor = PlotConstants.DEFAULT_TIME_AXIS_LABEL_COLOR;
    private String timeAxisDateFormat = PlotConstants.DEFAULT_TIME_AXIS_DATA_FORMAT;
    private Color nonTimeAxisColor = PlotConstants.DEFAULT_NON_TIME_AXIS_COLOR;
    private Color gridLineColor = PlotConstants.DEFAULT_GRID_LINE_COLOR;
    private int minSamplesForAutoScale = PlotConstants.DEFAULT_MIN_SAMPLES_FOR_AUTO_SCALE;
        private boolean compressionEnabled = PlotConstants.COMPRESSION_ENABLED_BY_DEFAULT;
        private int numberOfSubPlots = PlotConstants.DEFAULT_NUMBER_OF_SUBPLOTS;
        private boolean localControlsEnabled = PlotConstants.LOCAL_CONTROLS_ENABLED_BY_DEFAULT;
        private AbbreviatingPlotLabelingAlgorithm plotLabelingAlgorithm = new AbbreviatingPlotLabelingAlgorithm();

       
    /**
     * Specifies the required parameters for constructing a plot.
     * @param selectedPlotPackage plotting package to render the plot
     */
    public Builder(Class<? extends AbstractPlottingPackage>selectedPlotPackage) {
      this.plotPackage = selectedPlotPackage;
    }

    /**
     * Specify the plot's user readable name.
     * @param initPlotName the initial plot name.
     * @return builder the plot view.
     */
    public Builder plotName(String initPlotName) {
      plotName = initPlotName;
      return this;
    }
   
    /**
     * Specify the grouped plot settings for this chart
     * @param plotSettings
     * @return
     */
    public Builder plotSettings(PlotConfiguration plotSettings) {
      settings = plotSettings;
      return this;
    }


    /**
     * Specify the size of the font of the labels on the time axis.
     * @param theTimeAxisFontSize font size.
     * @return the builder the plot view.
     */
    public Builder timeAxisFontSize(int theTimeAxisFontSize) {
      timeAxisFont = new Font(timeAxisFont.getFontName(), Font.PLAIN, theTimeAxisFontSize);
      return this;
    }

    /**
     * Specify the font that will be used to draw the labels on the axis axis.
     * This parameter overrides the time axis font size parameter when specified.
     * @param theTimeAxisFont the font size.
     * @return the builder the plot view.
     */
    public Builder timeAxisFont(Font theTimeAxisFont) {
      timeAxisFont = theTimeAxisFont;
      return this;
    }

    /**
     * Specify the thickness of the line used to plot data on the plot.
     * @param theThickness the thickness.
     * @return the builder the plot view.
     */
    public Builder plotLineThickness(int theThickness) {
      plotLineThickness = theThickness;
      return this;
    }

    /**
     * Specify the color of the frame surrounding the plot area.
     * @param theBackgroundColor the color.
     * @return the builder the plot view.
     */
    public Builder plotBackgroundFrameColor(Color theBackgroundColor) {
      plotBackgroundFrameColor = theBackgroundColor;
      return this;
    }

    /**
     * Specify the background color of the plot area.
     * @param thePlotAreaColor the color.
     * @return the builder the plot view.
     */
    public Builder plotAreaBackgroundColor (Color thePlotAreaColor) {
      plotAreaBackgroundColor = thePlotAreaColor;
      return this;
    }

    /**
     * Specify the point at which the time axis intercepts the non time axis.
     * @param theIntercept the intercept point.
     * @return the builder the plot view.
     */
    public Builder timeAxisIntercept(int theIntercept) {
      timeAxisIntercept = theIntercept;
      return this;
    }

    /**
     * Specify the color of the time axis.
     * @param theTimeAxisColor the color.
     * @return the builder the plot view.
     */
    public Builder timeAxisColor(Color theTimeAxisColor) {
      timeAxisColor = theTimeAxisColor;
      return this;
    }

    /**
     * Specify color of text on the time axis.
     * @param theTimeAxisTextColor the color.
     * @return the builder the plot view.
     */
    public Builder timeAxisTextColor(Color theTimeAxisTextColor) {
      timeAxisLabelColor = theTimeAxisTextColor;
      return this;
    }


   
    /**
     * Set the format of how time information is printed on time axis labels.
     * @param theTimeAxisDateFormat the format.
     * @return the builder the plot view.
     */
    public Builder timeAxisDateFormat(String theTimeAxisDateFormat) {
      timeAxisDateFormat = theTimeAxisDateFormat;
      return this;
    }

    /**
     * Set the color of the non time axis.
     * @param theNonTimeAxisColor the color.
     * @return the builder the plot view.
     */
    public Builder nonTimeAxisColor(Color theNonTimeAxisColor) {
      nonTimeAxisColor = theNonTimeAxisColor;
      return this;
    }

    /**
     * Set the color of the plot gridlines.
     * @param theGridLineColor the color.
     * @return the builder the plot view.
     */
    public Builder gridLineColor(Color theGridLineColor) {
      gridLineColor = theGridLineColor;
      return this;
    }

    /**
     * The minimum number of samples to accumulate out of range before an autoscale occurs. This
     * prevents rapid autoscaling on every plot action.
     * @param theMinSamplesForAutoScale the number of samples.
     * @return the plot view.
     */
    public Builder minSamplesForAutoScale(int theMinSamplesForAutoScale) {
      minSamplesForAutoScale = theMinSamplesForAutoScale;
      return this;
    }
       
 
        /**
         * Specify if the plot is to compress its data to match the screen resolution.
         * @param state true to compress, false otherwise.
         * @return the builder the plot view.
         */
        public Builder isCompressionEnabled(boolean state) {
          compressionEnabled = state;
          return this;
        }
       
        /**
         * Specify the number of subplots in this plotview.
         * @param theNumberOfSubPlots the number of sub-plots.
         * @return the builder the plot view.
         */
        public Builder numberOfSubPlots(int theNumberOfSubPlots) {
          numberOfSubPlots = theNumberOfSubPlots;
          return this;
        }
       
        /**
         * Turn the plot local controls on and off.
         * @param theIsEnabled true enabled; otherwise false.
         * @return builder the plot view.
         */
        public Builder localControlsEnabled(boolean theIsEnabled) {
          localControlsEnabled = theIsEnabled;
          return this;
        }
      
        /**
         * Specify the plot abbreviation labeling algorithm.
         * @param thePlotLabelingAlgorithm the plot labeling algorithm.
         * @return builder the plot view.
         */
        public Builder plotLabelingAlgorithm(AbbreviatingPlotLabelingAlgorithm thePlotLabelingAlgorithm) {
          plotLabelingAlgorithm = thePlotLabelingAlgorithm;
          assert plotLabelingAlgorithm != null : "Plot labeling algorithm should NOT be NULL at this point.";
          return this;
        }
       
       
    /**
     * Build a new plot instance and return it.
     * @return the new plot instance.
     */
    public PlotView build() {
      return new PlotView(this);
    }  
  }

  // Private constructor. Construct using builder pattern.
  private PlotView(Builder builder) { 
    super(builder.settings);

    plotPackage = builder.plotPackage;
    plotName = builder.plotName;
   

    timeAxisFont = builder.timeAxisFont;
    plotLineThickness = builder.plotLineThickness;
    plotBackgroundFrameColor = builder.plotBackgroundFrameColor;
    plotAreaBackgroundColor = builder.plotAreaBackgroundColor;
    timeAxisIntercept = builder.timeAxisIntercept;
    timeAxisColor = builder.timeAxisColor;
    timeAxisLabelTextColor = builder.timeAxisLabelColor;
    timeAxisDataFormat = builder.timeAxisDateFormat;
    nonTimeAxisColor = builder.nonTimeAxisColor;
    gridLineColor = builder.gridLineColor;
    minSamplesForAutoScale = builder.minSamplesForAutoScale;
    compressionEnabled = builder.compressionEnabled;
    numberOfSubPlots = builder.numberOfSubPlots;
    localControlsEnabled = builder.localControlsEnabled;
    plotLabelingAlgorithm = builder.plotLabelingAlgorithm;
   

   
   
    plotPanel = new JPanel();
    plotPanel.addAncestorListener(new AncestorListener() {
      @Override
      public synchronized void ancestorRemoved(AncestorEvent event) {
        if(updateTimeBoundsTask != null) {
          updateTimeBoundsTask.cancel();
          updateTimeBoundsTask = null;
        }
      }


      @Override
      public void ancestorMoved(AncestorEvent event) {
      }


      @Override
      public synchronized void ancestorAdded(AncestorEvent event) {
        for(AbstractPlottingPackage p : subPlots) {
          p.updateCompressionRatio();
        }
        updateTimeBoundsTask = new TimerTask() {
          @Override
          public void run() {
            try {
              timeReachedEnd();
            } catch(Exception e) {
              // We need to catch exceptions because they can kill the timer.
              logger.error(e.toString(), e);
            }
          }
        };
        timer.schedule(updateTimeBoundsTask, 0, 1000);
      }
    });
    /* Make sure key events get dispatched to local controls (which enable for Alt/Ctrl/etc...) */
    plotPanel.addAncestorListener(new LocalControlKeyEventDispatcher(this));
    GridBagLayout layout = new StackPlotLayout(this);
    plotPanel.setLayout(layout);
   
    subPlots = new ArrayList<AbstractPlottingPackage>(numberOfSubPlots);
   
    // create the specified number of subplots
    for (int i=0; i< numberOfSubPlots; i++) {
      AbstractPlottingPackage newPlot;
      try {
        newPlot = plotPackage.newInstance();
        boolean isTimeLabelEnabled = i == (numberOfSubPlots -1);

        newPlot.createChart(timeAxisFont,
            plotLineThickness,
            plotBackgroundFrameColor,
            plotAreaBackgroundColor,
            timeAxisIntercept,
            timeAxisColor,
            timeAxisLabelColor,
            timeAxisLabelTextColor,
            timeAxisDataFormat,
            nonTimeAxisColor,
            gridLineColor,
            minSamplesForAutoScale,
            compressionEnabled,
            isTimeLabelEnabled,
            localControlsEnabled,
            this, plotLabelingAlgorithm);
       
        // TODO: Move control attachment to its own class?
        attachLocalControls(newPlot);
       
        newPlot.setPlotLabelingAlgorithm(plotLabelingAlgorithm);
        subPlots.add(newPlot);
        newPlot.registerObservor(this);
       
        logger.debug("plotLabelingAlgorithm.getPanelContextTitleList().size()="
            + plotLabelingAlgorithm.getPanelContextTitleList().size()
            + ", plotLabelingAlgorithm.getCanvasContextTitleList().size()="
            + plotLabelingAlgorithm.getCanvasContextTitleList().size());
       
      } catch (InstantiationException e) {
        e.printStackTrace();
      } catch (IllegalAccessException e) {
        e.printStackTrace();
      }
    }
   
   
    if (getAxisOrientationSetting() == AxisOrientationSetting.Y_AXIS_AS_TIME) {
      Collections.reverse(subPlots);
    }
   
    for (AbstractPlottingPackage subPlot: subPlots) {
      JComponent subPanel = subPlot.getPlotComponent();
      plotPanel.add(subPanel);
      GridBagConstraints c = new GridBagConstraints();
      c.fill = GridBagConstraints.BOTH;
      c.weightx = 1;
      c.weighty = 1;
      if(getAxisOrientationSetting() == AxisOrientationSetting.X_AXIS_AS_TIME) {
        c.gridwidth = GridBagConstraints.REMAINDER;
      }
      layout.setConstraints(subPanel, c);
      }

   
    if (builder.settings.getPinTimeAxis()) {
      timeAxisUserPin.setPinned(true);
      // update the corner reset buttons after the plot is visible
      SwingUtilities.invokeLater(new Runnable() {
        public void run() {
          for(AbstractPlottingPackage subPlot : subPlots) {
            subPlot.updateResetButtons();
          }
        }
      });
    }
   
  }
 
  private void attachLocalControls(AbstractPlottingPackage newPlot) {
    List<ControllableAxis> observableAxes = new ArrayList<ControllableAxis>();
    for (AbstractAxis axis : newPlot.getAxes()) {
      if (axis != null && axis.getVisibleOrientation() != null) {
        ControllableAxis a = new ControllableAxis(newPlot, axis);
        observableAxes.add(a);
        newPlot.attachLocalControl(new PanControls(a));
        newPlot.attachLocalControl(new ZoomControls(a));
        newPlot.attachLocalControl(new CornerResetButton(a));
        for (AbstractAxisBoundManager mgr : newPlot.getBoundManagers(a.getVisibleOrientation())) {
          newPlot.attachLocalControl(new BoundaryArrow(mgr));
        }
      }
    }
    newPlot.attachLocalControl(new CornerResetButton(observableAxes.toArray(new ControllableAxis[observableAxes.size()])));
  }


  /**
   * Gets the pinnable time axis by user.
   * @return pinnable time axis.
   */
  public Pinnable getTimeAxisUserPin() {
    return timeAxisUserPin;
  }

  /**
   * Gets the plot labeling algorithm.
   * @return abbreviating plot labeling algorithm.
   */
  public AbbreviatingPlotLabelingAlgorithm getPlotLabelingAlgorithm() {
    return plotLabelingAlgorithm;
  }

  /**
   * Sets the plot labeling algorithm.
   * @param thePlotLabelingAlgorithm the plot labeling algorithm.
   */
  public void setPlotLabelingAlgorithm(AbbreviatingPlotLabelingAlgorithm thePlotLabelingAlgorithm) {
    plotLabelingAlgorithm = thePlotLabelingAlgorithm;
  }
 
  @Override
  public AbstractPlottingPackage returnPlottingPackage() {
    return getLastPlot();
  }
 
  @Override
  public void setCompressionEnabled(boolean compression) {
    for (AbstractPlottingPackage p: subPlots) {
      p.setCompressionEnabled(compression);
    }
  }
 
  @Override
  public boolean isCompressionEnabled() {
    return getLastPlot().isCompressionEnabled();
  }
 
  @Override
  public void requestPlotData(GregorianCalendar startTime, GregorianCalendar endTime) {
    plotUser.requestDataRefresh(startTime, endTime);
  }
 
  private void requestPredictivePlotData(GregorianCalendar startTime, GregorianCalendar endTime) {
    plotUser.requestPredictiveData(startTime, endTime);
  }


  @Override
  public void informUpdateDataEventStarted() {
    timeAxisMaxAtStartOfDataUpdateCycle.setTimeInMillis(this.getLastPlot().getCurrentTimeAxisMax().getTimeInMillis());
    for (AbstractPlottingPackage p: subPlots) {
      p.informUpdateCachedDataStreamStarted();
    }
  }
 
  @Override
  public void informUpdateFromFeedEventStarted() {
    timeAxisMaxAtStartOfDataUpdateCycle.setTimeInMillis(this.getLastPlot().getCurrentTimeAxisMax().getTimeInMillis());
    for (AbstractPlottingPackage p: subPlots) {
      p.informUpdateFromLiveDataStreamStarted();
    }   
  }
 
  @Override
  public void informUpdateDataEventCompleted() {
    for (AbstractPlottingPackage p: subPlots) {
       p.informUpdateCacheDataStreamCompleted();
    }
    syncTimeAxisAcrossPlots();
  }
 
  @Override
    public void informUpdateFromFeedEventCompleted() {
    for (AbstractPlottingPackage p: subPlots) {
      p.informUpdateFromLiveDataStreamCompleted();
    }
    syncTimeAxisAcrossPlots();
  }
 
  /**
   * Synchronizes the time axis across all plots.
   */
  void syncTimeAxisAcrossPlots() {
    long maxAtStart = timeAxisMaxAtStartOfDataUpdateCycle.getTimeInMillis();
    long currentMaxTime = maxAtStart;
    long currentMinTime = maxAtStart;
    for (AbstractPlottingPackage p: subPlots) {
        long max = p.getMaxTime();
        if (max > currentMaxTime) {
          currentMaxTime = max;
          currentMinTime = p.getMinTime();
        }
    }

    if (currentMaxTime > maxAtStart) {
      boolean inverted;
      if(getAxisOrientationSetting() == AxisOrientationSetting.X_AXIS_AS_TIME) {
        inverted = getXAxisMaximumLocation() == XAxisMaximumLocationSetting.MAXIMUM_AT_LEFT;
      } else {
        inverted = getYAxisMaximumLocation() == YAxisMaximumLocationSetting.MAXIMUM_AT_BOTTOM;
      }
      long start;
      long end;
      if(inverted) {
        start = currentMaxTime;
        end = currentMinTime;
      } else {
        start = currentMinTime;
        end = currentMaxTime;
      }
      for (AbstractPlottingPackage p: subPlots) {   
        p.setTimeAxisStartAndStop(start, end);
      }
    }
  }
 
  @Override
  public long getCurrentMCTTime() { 
      return plotUser.getCurrentMCTTime();
    }

  /**
   * Gets the plot user view manifestation.
   * @return plotUser the plot user view manifestation.
   */
  public PlotViewManifestation getPlotUser() {
    return plotUser;
  }

  /**
   * Gets the last plot.
   * @return abstract plotting package.
   */
  AbstractPlottingPackage getLastPlot() {
    throwIllegalArgumentExcpetionIfWeHaveNoPlots();
   
    return subPlots.get(subPlots.size() - 1);
  }
 
  private void throwIllegalArgumentExcpetionIfWeHaveNoPlots() {
     if (subPlots.size() < 1) {
        throw new IllegalArgumentException("Plot contains no sub plots");
      }
  }
 
  private void throwIllegalArgumentExcpetionIfIndexIsNotInSubPlots(int subGroupIndex) {
    if ((subPlots.size() -1) < subGroupIndex) {
      throw new IllegalArgumentException("subgroup is out of range" + subGroupIndex + " > " + (subPlots.size() -1));
    }
  }

  @Override
  public boolean plotMatchesSetting(PlotConfiguration settings) {
      if (settings.getOrdinalPositionForStackedPlots() != this.getOrdinalPositionForStackedPlots())
        return false;
      if (settings.getPinTimeAxis() != getPinTimeAxis())
        return false;
      if (settings.getAxisOrientationSetting() != this.getAxisOrientationSetting())
        return false;
      if (settings.getMaxDependent() != getMaxDependent())
        return false;
      if (settings.getMaxNonTime() != getMaxNonTime())
        return false;
      if (settings.getMaxTime() != getMaxTime())
        return false;
      if (settings.getMinDependent() != getMinDependent())
        return false;
      if (settings.getMinNonTime() != getMinNonTime())
        return false;
      if (settings.getMinTime() != getMinTime())
        return false;
      if (settings.getNonTimeAxisSubsequentMaxSetting() != getNonTimeAxisSubsequentMaxSetting())
        return false;
      if (settings.getNonTimeAxisSubsequentMinSetting() != getNonTimeAxisSubsequentMinSetting())
        return false;
      if (settings.getNonTimeMaxPadding() != getNonTimeMaxPadding())
        return false;
      if (settings.getNonTimeMinPadding() != getNonTimeMinPadding())
        return false;
      if (!settings.getPlotLineConnectionType().equals(getPlotLineConnectionType()))
        return false;
      if (!settings.getTimeAxisSubsequentSetting().equals(getTimeAxisSubsequentSetting()))
        return false;
      if (settings.getTimePadding() != getTimePadding())
        return false;
      if (settings.getXAxisMaximumLocation() != getXAxisMaximumLocation())
        return false;
      if (settings.getYAxisMaximumLocation() != getYAxisMaximumLocation())
        return false;
      if (settings.getPlotLineDraw().drawLine() != getPlotLineDraw().drawLine())
        return false;
      if (settings.getPlotLineDraw().drawMarkers() != getPlotLineDraw().drawMarkers())
        return false;
     
      return true;
    }

  @Override
  public void updateTimeAxis(PlotSubject subject, long startTime, long endTime) {       
        for (AbstractPlottingPackage plot: subPlots) {
          if (plot!= subject) {
          plot.setTimeAxisStartAndStop(startTime, endTime);
          }
      }
  } 


  @Override
  public void updateResetButtons() {
    for(AbstractPlottingPackage p : subPlots) {
      p.updateResetButtons();
    }
  }


    @Override
  public void clearAllDataFromPlot() {
      for (AbstractPlottingPackage plot: subPlots) {
        plot.clearAllDataFromPlot();
    }
   
  }


  @Override
  public Pinnable createPin() {
    return pinSupport.createPin();
  }


  private void pause() {
    for(AbstractPlottingPackage plot : subPlots) {
      plot.pause(true);
    }
  }

  private void unpause() {
    // Request data from buffer to fill in what was missed while paused.
    plotUser.updateFromFeed(null);
    for(AbstractPlottingPackage plot : subPlots) {
      plot.pause(false);
    }
  }

  @Override
  public boolean isPinned() {
    return pinSupport.isPinned();
  }

  @Override
  public List<AbstractPlottingPackage> getSubPlots() {
    return Collections.unmodifiableList(subPlots);
  }

  @Override
  public Axis getTimeAxis() {
    return timeAxis;
  }

  /**
   * Adds data set per map.
   * @param dataForPlot data map.
   */
  public void addData(Map<String, SortedMap<Long, Double>> dataForPlot) {
    for(Entry<String, SortedMap<Long, Double>> feedData : dataForPlot.entrySet()) {
      String feedID = feedData.getKey();
      String dataSetNameLower = feedID.toLowerCase();
      if (!isKnownDataSet(dataSetNameLower)) {
        throw new IllegalArgumentException("Attempting to set value for an unknown data set " + feedID);
      }
      if (getAxisOrientationSetting() != AxisOrientationSetting.Z_AXIS_AS_TIME) {
        Set<AbstractPlottingPackage> feedPlots = dataSetNameToSubGroupMap.get(dataSetNameLower);
 
        SortedMap<Long, Double> points = feedData.getValue();
        for(AbstractPlottingPackage plot : feedPlots) {
          plot.addData(dataSetNameLower, points);
        }
      } else {
        for(AbstractPlottingPackage plot : subPlots) {
          if (plot.isKnownDataSet(feedID)) {
            plot.addData(dataSetNameLower, feedData.getValue());
          }
        }
      }
    }
  }

  /**
   * Adds data set per feed Id, timestamp, and telemetry value.
   * @param feedID the feed Id.
   * @param time timestamp in millisecs.
   * @param value telemetry value in double.
   */
  public void addData(String feedID, long time, double value) {
    SortedMap<Long, Double> points = new TreeMap<Long, Double>();
    points.put(time, value);
    addData(Collections.singletonMap(feedID, points));
  }

  /**
   * Sets the plot X-Y time axis.
   * @param axis the X-Y time axis.
   */
  public void setPlotTimeAxis(AbstractAxis axis) {
    this.plotTimeAxis = axis;
  }

  /**
   * Gets the plot X-Y time axis.
   * @return X-Y time axis.
   */
  public AbstractAxis getPlotTimeAxis() {
    return plotTimeAxis;
  }


  private void timeReachedEnd() {
    long maxTime = getCurrentMCTTime();
    double plotMax = Math.max(plotTimeAxis.getStart(), plotTimeAxis.getEnd());
    double lag = maxTime - plotMax;
    double scrollRescaleTimeMargin = this.getTimePadding();
    if (scrollRescaleTimeMargin == 0) {
      scrollRescaleTimeMargin = (maxTime - plotMax) / Math.abs(plotTimeAxis.getEnd() - plotTimeAxis.getStart());
    }
    if(lag > 0 && !timeAxis.isPinned()) {
      if(getTimeAxisSubsequentSetting() == TimeAxisSubsequentBoundsSetting.JUMP) {
        double increment = Math.abs(scrollRescaleTimeMargin * (plotTimeAxis.getEnd() - plotTimeAxis.getStart()));
        plotTimeAxis.shift(Math.ceil(lag / increment) * increment);
        for(AbstractPlottingPackage subPlot : subPlots) {
          subPlot.setTimeAxisStartAndStop(plotTimeAxis.getStartAsLong(), plotTimeAxis.getEndAsLong());
        }
      } else if(getTimeAxisSubsequentSetting() == TimeAxisSubsequentBoundsSetting.SCRUNCH) {
        double max = plotTimeAxis.getEnd();
        double min = plotTimeAxis.getStart();
        double diff = max - min;
        assert diff != 0 : "min = max = " + min;
        double scrunchFactor = 1 + scrollRescaleTimeMargin;
        if((max < min)) {
          min = max + (maxTime - max)*(scrunchFactor);
        } else {
          max = min + (maxTime - min)*(scrunchFactor);
        }
        plotTimeAxis.setStart(min);
        plotTimeAxis.setEnd(max);
        for(AbstractPlottingPackage subPlot : subPlots) {
          subPlot.setTimeAxisStartAndStop(plotTimeAxis.getStartAsLong(), plotTimeAxis.getEndAsLong());
          subPlot.updateCompressionRatio();
        }
      } else {
        assert false : "Unrecognized timeAxisSubsequentSetting: " + getTimeAxisSubsequentSetting().name();
      }
      double newPlotMax = Math.max(plotTimeAxis.getStart(), plotTimeAxis.getEnd());
      if(newPlotMax != plotMax) {
        GregorianCalendar start = new GregorianCalendar();
        GregorianCalendar end = new GregorianCalendar();
        start.setTimeInMillis((long) plotMax);
        end.setTimeInMillis((long) newPlotMax);
        requestPredictivePlotData(start, end);
      }
    }
  }


  @Override
  public void plotAxisChanged(PlotSubject subject, AbstractAxis axis) {
  }


  @Override
  public void dataPlotted() {
  }

}
TOP

Related Classes of gov.nasa.arc.mct.fastplot.bridge.PlotView$Builder

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.