DocumentManager.java
/**
* VStar: a statistical analysis tool for variable star data.
* Copyright (C) 2010 AAVSO (http://www.aavso.org/)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.aavso.tools.vstar.ui.mediator;
import java.awt.Window;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import org.aavso.tools.vstar.data.SeriesType;
import org.aavso.tools.vstar.ui.IMainUI;
import org.aavso.tools.vstar.ui.UIType;
import org.aavso.tools.vstar.ui.mediator.message.NewStarMessage;
import org.aavso.tools.vstar.ui.mediator.message.PhaseChangeMessage;
import org.aavso.tools.vstar.ui.model.list.AbstractModelObservationTableModel;
import org.aavso.tools.vstar.ui.model.list.PhasePlotModelObservationTableModel;
import org.aavso.tools.vstar.ui.model.list.RawDataModelObservationTableModel;
import org.aavso.tools.vstar.ui.pane.list.SyntheticObservationListPane;
import org.aavso.tools.vstar.util.model.IModel;
import org.aavso.tools.vstar.util.notification.Listener;
import org.aavso.tools.vstar.util.stats.BinningResult;
import org.aavso.tools.vstar.util.stats.PhaseCalcs;
/**
* This class manages the creation of VStar "documents", i.e. models and GUI
* components and important state. It will also expand to manage document
* printing, saving (and the need to) etc. Another thing this class will allow
* us to do is to cache GUI components (and by association, their models)
* permitting reuse of these and updates to models. TODO: call it
* ComponentManager instead?
*/
@SuppressWarnings("serial")
public class DocumentManager {
private Mediator mediator;
// Model and residuals maps.
private Map<String, SyntheticObservationListPane<AbstractModelObservationTableModel>> rawDataModelComponents;
private Map<String, SyntheticObservationListPane<AbstractModelObservationTableModel>> phasedModelComponents;
private Map<String, SyntheticObservationListPane<AbstractModelObservationTableModel>> rawDataResidualComponents;
private Map<String, SyntheticObservationListPane<AbstractModelObservationTableModel>> phasedResidualComponents;
private boolean phasePlotExists;
private double epoch;
private double period;
// state of user-controllable plot pane characteristics
private Map<AnalysisType, Boolean> showErrorBars;
private Map<AnalysisType, Boolean> showCrossHairs;
private Map<AnalysisType, Boolean> invertRange;
private Map<AnalysisType, Boolean> invertSeriesOrder;
private Map<AnalysisType, Boolean> joinMeans;
private Map<String, String> statsInfo;
private static int filterNum = 0;
public DocumentManager() {
mediator = Mediator.getInstance();
init();
}
// ** phase, epoch, period methods **
public boolean phasePlotExists() {
return phasePlotExists;
}
public double getEpoch() {
return epoch;
}
public double getPeriod() {
return period;
}
// ** user-controllable plot pane methods **
public void toggleErrorBarState() {
togglePlotControlState(showErrorBars);
}
public void toggleCrossHairState() {
togglePlotControlState(showCrossHairs);
}
public void toggleRangeAxisInversionState() {
togglePlotControlState(invertRange);
}
public void toggleSeriesOrderInversionState() {
togglePlotControlState(invertSeriesOrder);
}
public void toggleJoinMeansState() {
togglePlotControlState(joinMeans);
}
public boolean shouldShowErrorBars(AnalysisType analysisType) {
return showErrorBars.get(analysisType);
}
public boolean shouldShowErrorBars() {
return shouldShowErrorBars(mediator.getAnalysisType());
}
public boolean shouldShowCrossHairs(AnalysisType analysisType) {
return showCrossHairs.get(analysisType);
}
public boolean shouldShowCrossHairs() {
return shouldShowCrossHairs(mediator.getAnalysisType());
}
public boolean shouldInvertRange(AnalysisType analysisType) {
return invertRange.get(analysisType);
}
public boolean shouldInvertRange() {
return shouldInvertRange(mediator.getAnalysisType());
}
public boolean shouldInvertSeriesOrder(AnalysisType analysisType) {
return invertSeriesOrder.get(analysisType);
}
public boolean shouldInvertSeriesOrder() {
return shouldInvertSeriesOrder(mediator.getAnalysisType());
}
public boolean shouldJoinMeans(AnalysisType analysisType) {
return joinMeans.get(analysisType);
}
public boolean shouldJoinMeans() {
return shouldJoinMeans(mediator.getAnalysisType());
}
private void togglePlotControlState(Map<AnalysisType, Boolean> map) {
AnalysisType analysisType = mediator.getAnalysisType();
map.put(analysisType, !map.get(analysisType));
}
// ** List pane methods **
public SyntheticObservationListPane<AbstractModelObservationTableModel> getModelListPane(AnalysisType type,
IModel model) {
SyntheticObservationListPane<AbstractModelObservationTableModel> pane = null;
String key = model.getDescription();
switch (type) {
case RAW_DATA:
if (!rawDataModelComponents.containsKey(key)) {
// Create model table model and GUI component since they have
// not been.
RawDataModelObservationTableModel modelTableModel = new RawDataModelObservationTableModel(
model.getFit(), SeriesType.Model);
String summary = model.toString();
SyntheticObservationListPane<AbstractModelObservationTableModel> modelPane = new SyntheticObservationListPane<AbstractModelObservationTableModel>(
modelTableModel, summary);
rawDataModelComponents.put(key, modelPane);
}
pane = rawDataModelComponents.get(key);
break;
case PHASE_PLOT:
key = getPhasedModelKey(model);
if (!phasedModelComponents.containsKey(key)) {
// Set the fit list's phases according to the last phase change.
// It's okay to modify the original data.
PhaseCalcs.setPhases(model.getFit(), epoch, period);
// Create model table model and GUI component since they have
// not been.
PhasePlotModelObservationTableModel modelTableModel = new PhasePlotModelObservationTableModel(
model.getFit(), SeriesType.Model);
String summary = model.toString();
SyntheticObservationListPane<AbstractModelObservationTableModel> modelPane = new SyntheticObservationListPane<AbstractModelObservationTableModel>(
modelTableModel, summary);
phasedModelComponents.put(key, modelPane);
}
pane = phasedModelComponents.get(key);
break;
}
return pane;
}
public SyntheticObservationListPane<AbstractModelObservationTableModel> getResidualsListPane(AnalysisType type,
IModel model) {
SyntheticObservationListPane<AbstractModelObservationTableModel> pane = null;
String key = model.getDescription();
switch (type) {
case RAW_DATA:
if (!rawDataResidualComponents.containsKey(key)) {
RawDataModelObservationTableModel residualsTableModel = new RawDataModelObservationTableModel(
model.getResiduals(), SeriesType.Residuals);
String summary = model.toString();
SyntheticObservationListPane<AbstractModelObservationTableModel> residualsPane = new SyntheticObservationListPane<AbstractModelObservationTableModel>(
residualsTableModel, summary);
rawDataResidualComponents.put(key, residualsPane);
}
pane = rawDataResidualComponents.get(key);
break;
case PHASE_PLOT:
key = getPhasedModelKey(model);
if (!phasedResidualComponents.containsKey(key)) {
// Set the residual list's phases according to the last phase
// change.
// It's okay to modify the original data.
PhaseCalcs.setPhases(model.getResiduals(), epoch, period);
// Create model table model and GUI component since they have
// not been.
PhasePlotModelObservationTableModel residualsTableModel = new PhasePlotModelObservationTableModel(
model.getResiduals(), SeriesType.Residuals);
String summary = model.toString();
SyntheticObservationListPane<AbstractModelObservationTableModel> residualsPane = new SyntheticObservationListPane<AbstractModelObservationTableModel>(
residualsTableModel, summary);
phasedResidualComponents.put(key, residualsPane);
}
pane = phasedResidualComponents.get(key);
break;
}
return pane;
}
// ** Stats info methods **
/**
* Add a statistics information string.
*
* @param key The key against which this information is to be stored.
* @param info The information string to be added.
*/
public void addStatsInfo(String key, String info) {
statsInfo.put(key, info);
}
/**
* @return the statsInfo
*/
public Map<String, String> getStatsInfo() {
return statsInfo;
}
// Returns a mean observation change (binning result) listener.
protected Listener<BinningResult> createBinChangeListener() {
return new Listener<BinningResult>() {
public void update(BinningResult info) {
updateAnovaInfo(info);
}
public boolean canBeRemoved() {
return false;
}
};
}
/**
* Update the anova information from a binning result.
*
* @param binningResult The binning result to use.
*/
public void updateAnovaInfo(BinningResult binningResult) {
addStatsInfo("Mean Source Series", binningResult.getSeries().getDescription());
addStatsInfo("anova", createAnovaText(binningResult));
}
// Returns ANOVA result text suitable for display.
public String createAnovaText(BinningResult binningResult) {
return binningResult.createAnovaText();
}
// ** Filter-related methods **
/**
* Return the name of the next untitled filter.
*
* @return the next untitled filter name
*/
public String getNextUntitledFilterName() {
filterNum++;
return "Untitled Filter " + filterNum;
}
// Helpers
/**
* Returns a unique phase model key for a model given the current epoch and
* period associated with a phase change.
*
* @param model The model whose description we will use as part of the key.
* @return The unique key from the tuple: <description, epoch, period>.
*/
private String getPhasedModelKey(IModel model) {
return String.format("%s:e=%f,p=%f", model.getDescription(), epoch, period);
}
/**
* Initialise (or reset) data members
*/
public void init() {
phasePlotExists = false;
epoch = 0;
period = 0;
// model maps
if (rawDataModelComponents == null) {
rawDataModelComponents = new HashMap<String, SyntheticObservationListPane<AbstractModelObservationTableModel>>();
}
rawDataModelComponents.clear();
if (phasedModelComponents == null) {
phasedModelComponents = new HashMap<String, SyntheticObservationListPane<AbstractModelObservationTableModel>>();
}
phasedModelComponents.clear();
if (rawDataResidualComponents == null) {
rawDataResidualComponents = new HashMap<String, SyntheticObservationListPane<AbstractModelObservationTableModel>>();
}
rawDataResidualComponents.clear();
if (phasedResidualComponents == null) {
phasedResidualComponents = new HashMap<String, SyntheticObservationListPane<AbstractModelObservationTableModel>>();
}
phasedResidualComponents.clear();
// Boolean maps
if (showErrorBars == null) {
showErrorBars = new HashMap<AnalysisType, Boolean>();
}
showErrorBars.put(AnalysisType.RAW_DATA, true);
showErrorBars.put(AnalysisType.PHASE_PLOT, true);
if (showCrossHairs == null) {
showCrossHairs = new HashMap<AnalysisType, Boolean>();
}
showCrossHairs.put(AnalysisType.RAW_DATA, true);
showCrossHairs.put(AnalysisType.PHASE_PLOT, true);
if (invertRange == null) {
invertRange = new HashMap<AnalysisType, Boolean>();
}
invertRange.put(AnalysisType.RAW_DATA, true);
invertRange.put(AnalysisType.PHASE_PLOT, true);
if (invertSeriesOrder == null) {
invertSeriesOrder = new HashMap<AnalysisType, Boolean>();
}
invertSeriesOrder.put(AnalysisType.RAW_DATA, true);
invertSeriesOrder.put(AnalysisType.PHASE_PLOT, true);
if (joinMeans == null) {
joinMeans = new HashMap<AnalysisType, Boolean>();
}
joinMeans.put(AnalysisType.RAW_DATA, true);
joinMeans.put(AnalysisType.PHASE_PLOT, true);
// stats info map
if (statsInfo == null) {
statsInfo = new TreeMap<String, String>();
}
statsInfo.clear();
}
/**
* Return a phase change listener that updates epoch and period information in
* preparation for creating or retrieving phase plot components.<br/>
* TODO: when we have finally unified observations as a single list across all
* models, a listener for this message can call setPhases() on that list.
*/
public Listener<PhaseChangeMessage> createPhaseChangeListener() {
return new Listener<PhaseChangeMessage>() {
@Override
public void update(PhaseChangeMessage info) {
phasePlotExists = true;
epoch = info.getEpoch();
period = info.getPeriod();
}
@Override
public boolean canBeRemoved() {
return false;
}
};
}
/**
* Find and return the active window or null if one does not exist, e.g. the
* case where the UI is that of an applet.
*/
public static Window findActiveWindow() {
Window wdw = null;
IMainUI ui = Mediator.getUI();
if (ui != null) {
if (ui.getUiType() == UIType.DESKTOP) {
if (Window.getWindows().length > 0) {
for (Window window : Window.getWindows()) {
// At least find the main window...
if (window instanceof org.aavso.tools.vstar.ui.MainFrame) {
wdw = window;
}
// ...even better if it's the focus owner. If nothing
// else, by the end of the loop, we should have found
// the main window, whether or not it has the focus.
if (window.isFocusOwner()) {
wdw = window;
break;
}
}
}
}
}
return wdw;
}
}