ObservationFilterDialog.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.dialog.filter;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JPanel;
import javax.swing.JTextField;
import org.aavso.tools.vstar.data.ValidObservation;
import org.aavso.tools.vstar.data.filter.IFilterDescription;
import org.aavso.tools.vstar.data.filter.IObservationFieldMatcher;
import org.aavso.tools.vstar.data.filter.ObservationFilter;
import org.aavso.tools.vstar.ui.dialog.AbstractOkCancelDialog;
import org.aavso.tools.vstar.ui.dialog.MessageBox;
import org.aavso.tools.vstar.ui.mediator.Mediator;
import org.aavso.tools.vstar.ui.mediator.message.FilteredObservationMessage;
import org.aavso.tools.vstar.ui.mediator.message.NewStarMessage;
import org.aavso.tools.vstar.ui.mediator.message.ObservationSelectionMessage;
import org.aavso.tools.vstar.util.locale.LocaleProps;
import org.aavso.tools.vstar.util.notification.Listener;
/**
* This dialog permits the user to specify a conjunctive filter, i.e. a set of
* sub-filters each of which must be true (i.e. provide a match) for the
* overall filter to be true (i.e. match). The result of applying a filter is
* that some subset of the current observations will be captured in a collection
* and a message sent.
*/
@SuppressWarnings("serial")
public class ObservationFilterDialog extends AbstractOkCancelDialog {
private ObservationSelectionMessage observationSelectionMessage;
private ObservationFilter filter;
private JTextField nameField;
private List<ObservationFilterPane> filterPanes;
private JCheckBox useSelectedObservationCheckbox;
private JCheckBox includeFainterThanObservationCheckbox;
private JCheckBox includeDiscrepantObservationCheckbox;
private JCheckBox includeExcludedObservationCheckbox;
/**
* Constructor.
*/
public ObservationFilterDialog() {
super("Filter Observations");
observationSelectionMessage = null;
filter = new ObservationFilter();
filterPanes = new LinkedList<ObservationFilterPane>();
Container contentPane = this.getContentPane();
JPanel topPane = new JPanel();
topPane.setLayout(new BoxLayout(topPane, BoxLayout.PAGE_AXIS));
topPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
topPane.add(createNamePane());
topPane.add(createFilterPane());
topPane.add(createButtonPane());
contentPane.add(topPane);
this.pack();
}
@Override
public void showDialog() {
String defaultName = Mediator.getInstance().getDocumentManager()
.getNextUntitledFilterName();
nameField.setText(defaultName);
super.showDialog();
}
private JPanel createNamePane() {
JPanel panel = new JPanel();
// panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
nameField = new JTextField();
nameField.setBorder(BorderFactory.createTitledBorder("Filter Name"));
panel.add(nameField);
return panel;
}
private JPanel createFilterPane() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
// Create filter panes for each unique filter.
for (int i = 0; i < ObservationFilter.MATCHERS.size(); i++) {
ObservationFilterPane filterPane = new ObservationFilterPane();
filterPanes.add(filterPane);
panel.add(filterPane);
panel.add(Box.createRigidArea(new Dimension(75, 10)));
}
return panel;
}
@Override
protected JPanel createButtonPane() {
JPanel extraButtonPanel = new JPanel(new FlowLayout());
useSelectedObservationCheckbox = new JCheckBox(
"Use selected observation");
useSelectedObservationCheckbox
.addActionListener(createUseSelectedObservationCheckBoxHandler());
useSelectedObservationCheckbox.setEnabled(false);
extraButtonPanel.add(useSelectedObservationCheckbox);
JPanel includePanel = new JPanel(new FlowLayout());
includePanel.setBorder(BorderFactory.createTitledBorder("Include"));
includeFainterThanObservationCheckbox = new JCheckBox("Fainter Than?");
includePanel.add(includeFainterThanObservationCheckbox);
includeDiscrepantObservationCheckbox = new JCheckBox("Discrepant?");
includePanel.add(includeDiscrepantObservationCheckbox);
includeExcludedObservationCheckbox = new JCheckBox("Excluded?");
includePanel.add(includeExcludedObservationCheckbox);
extraButtonPanel.add(includePanel);
JButton resetButton = new JButton("Reset");
resetButton.addActionListener(createResetButtonListener());
extraButtonPanel.add(resetButton, BorderLayout.LINE_END);
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
panel.add(extraButtonPanel);
panel.add(super.createButtonPane());
return panel;
}
// Return a listener for the reset button.
private ActionListener createResetButtonListener() {
return new ActionListener() {
public void actionPerformed(ActionEvent e) {
resetFilters();
}
};
}
/**
* @return A new star listener for the filter dialog.
*/
public Listener<NewStarMessage> createNewStarListener() {
return new Listener<NewStarMessage>() {
@Override
public void update(NewStarMessage info) {
resetFilters();
useSelectedObservationCheckbox.setEnabled(false);
}
@Override
public boolean canBeRemoved() {
return false;
}
};
}
/**
* @return An observation selection listener for the filter dialog.
*/
public Listener<ObservationSelectionMessage> createObservationSelectionListener() {
return new Listener<ObservationSelectionMessage>() {
@Override
public void update(ObservationSelectionMessage msg) {
// Check whether we already have this message's observation from
// another source before proceeding.
if (observationSelectionMessage == null
|| msg.getObservation() != observationSelectionMessage
.getObservation()) {
// Record the observation selection and enable the
// use-selected-observation-checkbox so that it can be
// selected.
observationSelectionMessage = msg;
useSelectedObservationCheckbox.setEnabled(true);
// Pass the selected observation to each filter
// pane if the checkbox was already selected.
if (useSelectedObservationCheckbox.isSelected()) {
for (ObservationFilterPane pane : filterPanes) {
pane.useObservation(msg);
}
}
}
}
@Override
public boolean canBeRemoved() {
return false;
}
};
}
// Returns a use-selected-observation-checkbox listener.
private ActionListener createUseSelectedObservationCheckBoxHandler() {
return new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ObservationSelectionMessage selMsg = null;
// If the checkbox is selected, retrieve the last selected
// observation from the current view. This of course could be
// null, except that this checkbox is only enabled when an
// observation *has* been selected, so it really *can't* be null
// at this point. :)
if (useSelectedObservationCheckbox.isSelected()) {
selMsg = observationSelectionMessage;
}
// Pass the last selected observation or null to each filter
// pane.
for (ObservationFilterPane pane : filterPanes) {
pane.useObservation(selMsg);
pack();
}
}
};
}
/**
* @see org.aavso.tools.vstar.ui.dialog.AbstractOkCancelDialog#cancelAction()
*/
@Override
protected void cancelAction() {
// Nothing to be done.
}
/**
* @see org.aavso.tools.vstar.ui.dialog.AbstractOkCancelDialog#okAction()
*/
@Override
protected void okAction() {
// Add all non-null (valid) matchers to the filter.
boolean filterError = false;
for (ObservationFilterPane filterPane : filterPanes) {
try {
IObservationFieldMatcher matcher = filterPane.getFieldMatcher();
if (matcher != null) {
filter.addMatcher(matcher);
}
} catch (IllegalArgumentException e) {
filterError = true;
MessageBox.showErrorDialog(Mediator.getUI().getComponent(),
LocaleProps.get("OBSERVATION_FILTER_DLG_TITLE"),
e.getMessage());
}
}
if (!filterError) {
if (filter.getMatchers().size() != 0) {
setVisible(false);
// Apply the filter (and all its matchers) to the full set
// of observations.
List<ValidObservation> obs = Mediator.getInstance().getValidObsList();
Set<ValidObservation> filteredObs = filter
.getFilteredObservations(obs,
includeFainterThanObservationCheckbox
.isSelected(),
includeDiscrepantObservationCheckbox
.isSelected(),
includeExcludedObservationCheckbox.isSelected());
if (filteredObs.size() != 0) {
// Send a message containing the observation subset.
IFilterDescription desc = new IFilterDescription() {
private final String filterName = nameField.getText();
private final String filterRepresentation = createFilterRepresentation();
@Override
public boolean isParsable() {
return false;
}
@Override
public String getFilterName() {
return filterName;
}
@Override
public String getFilterDescription() {
return filterRepresentation;
}
};
FilteredObservationMessage msg = new FilteredObservationMessage(
this, desc, filteredObs);
Mediator.getInstance().getFilteredObservationNotifier()
.notifyListeners(msg);
} else {
String msg = LocaleProps
.get("NO_OBSERVATIONS_MATCHED_ERR_MSG");
MessageBox.showWarningDialog(Mediator.getUI()
.getComponent(), LocaleProps
.get("OBSERVATION_FILTER_DLG_TITLE"), msg);
}
// MainFrame.getInstance().setCursor(null);
} else {
String msg = LocaleProps.get("NO_FILTER_SELECTED_ERR_MSG");
MessageBox.showWarningDialog(Mediator.getUI().getComponent(),
LocaleProps.get("OBSERVATION_FILTER_DLG_TITLE"), msg);
}
// Clear state for next use of this dialog.
filter.reset();
}
}
// Return a machine-readable (able to be parsed) representation.
private String createFilterRepresentation() {
StringBuffer buf = new StringBuffer();
int activeFilterCount = 0;
for (ObservationFilterPane filterPane : filterPanes) {
IObservationFieldMatcher matcher = filterPane.getFieldMatcher();
if (matcher != null) {
activeFilterCount++;
}
}
for (ObservationFilterPane filterPane : filterPanes) {
IObservationFieldMatcher matcher = filterPane.getFieldMatcher();
if (matcher != null) {
String desc = matcher.getParsableDescription();
buf.append(desc);
activeFilterCount--;
if (activeFilterCount > 0) {
buf.append(" AND\n");
}
}
}
return buf.toString();
}
private void resetFilters() {
for (ObservationFilterPane filterPane : filterPanes) {
filterPane.resetFilter();
}
}
}