|
|
package org.openslx.dozmod.gui.changemonitor;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import javax.swing.ButtonGroup;
import javax.swing.ButtonModel;
import javax.swing.JCheckBox;
import javax.swing.JSpinner;
import javax.swing.JTable;
import javax.swing.text.JTextComponent;
import javax.swing.tree.TreePath;
import org.apache.log4j.Logger;
import org.jdatepicker.JDatePicker;
import org.openslx.dozmod.gui.control.ComboBox;
import org.openslx.dozmod.gui.control.JCheckBoxTree;
import org.openslx.util.Util;
public class DialogChangeMonitor {
private static final Logger LOGGER = Logger.getLogger(DialogChangeMonitor.class);
private final Callback callback;
private final List<AbstractControlWrapper<?>> controls = new ArrayList<>();
private boolean isCurrentlyModified;
private boolean wasEverModified;
private boolean isValid = true;
public DialogChangeMonitor(Callback cb) {
this.callback = cb;
}
/*
* Methods for adding various controls
*/
private <T> AbstractControlWrapper<T> add(AbstractControlWrapper<T> elem) {
controls.add(elem);
elem.resetChangeState();
return elem;
}
public AbstractControlWrapper<String> add(JTextComponent component) {
return add(new TextControlWrapper(this, component));
}
public <T> AbstractControlWrapper<T> addFixedCombo(ComboBox<T> component, Comparator<T> comparator) {
return add(new FixedComboBoxWrapper<T>(this, component, comparator));
}
public AbstractControlWrapper<Object> addEditableCombo(ComboBox<?> component,
Comparator<Object> comparator) {
return add(new EditableComboBoxWrapper(this, component, comparator));
}
public AbstractControlWrapper<Boolean> add(JCheckBox component) {
return add(new CheckBoxWrapper(this, component));
}
public AbstractControlWrapper<TreePath[]> add(JCheckBoxTree component) {
return add(new CheckBoxTreeWrapper(this, component));
}
public AbstractControlWrapper<ButtonModel> add(ButtonGroup group) {
return add(new ButtonGroupWrapper(this, group));
}
public AbstractControlWrapper<Object> add(JDatePicker picker) {
return add(new DatePickerWrapper(this, picker));
}
public AbstractControlWrapper<Object> add(JSpinner spinner) {
return add(new TimeSpinnerWrapper(this, spinner));
}
public AbstractControlWrapper<ClonedTableModel> add(JTable table) {
return add(new TableWrapper(this, table));
}
public <T> AbstractControlWrapper<T> add(GenericControlWindow<T> editor) {
return add(new GenericControlWrapper<T>(this, editor));
}
/*
* public methods for controlling the monitor
*/
public boolean wasEverModified() {
return wasEverModified;
}
public boolean isCurrentlyModified() {
return isCurrentlyModified;
}
public boolean isValid() {
return isValid;
}
public void reset() {
boolean oldState = wasEverModified;
this.wasEverModified = this.isCurrentlyModified = false;
for (AbstractControlWrapper<?> cw : controls) {
cw.resetChangeState();
}
if (callback != null && oldState) {
callback.modificationChanged();
}
}
/*
*
*/
void validityChanged(AbstractControlWrapper<?> cw) {
String error = null;
//LOGGER.info(cw.getClass().getSimpleName() + " changed validity to " + cw.isValid);
boolean oldValid = this.isValid;
if (cw.currentError != null) {
this.isValid = false;
error = cw.currentError;
} else {
this.isValid = true;
}
if (error == null) {
for (AbstractControlWrapper<?> c : controls) {
if (c.currentError != null) {
this.isValid = false;
error = c.currentError;
break;
}
}
}
/*
* Trigger callback, either when
* a) global state changing from invalid to valid, so the error message can get cleared
* b) some control changed from valid to invalid, and we have an error message
*/
if (callback != null && ((isValid && !oldValid) || error != null)) {
callback.validityChanged(error);
}
}
/**
* Called from one of the inner "ControlWrapper" classes to trigger
* the callback for the according window/control.
*
* @param cw The ControlWrapper where the changed state changed.
*/
void contentChanged(AbstractControlWrapper<?> cw) {
LOGGER.info(cw.getClass().getSimpleName() + " is changed: " + cw.isCurrentlyChanged);
final boolean oldEver = this.wasEverModified;
final boolean oldCurrent = this.isCurrentlyModified;
this.wasEverModified = false;
this.isCurrentlyModified = false;
for (AbstractControlWrapper<?> c : controls) {
if (c.isCurrentlyChanged) {
this.isCurrentlyModified = true;
c.wasEverChanged = true;
}
if (c.wasEverChanged) {
this.wasEverModified = true;
}
}
if (callback != null && (oldCurrent != isCurrentlyModified || oldEver != wasEverModified)) {
callback.modificationChanged();
}
}
/*
* Validators
*/
public static interface ValidationConstraint<T> {
/**
* Method should return <code>null</code> if the input is valid, an
* error message otherwise.
*
* @param userInput Current content/state of the control being monitored
* @return error message if invalid, <code>null</code> otherwise
*/
String checkStateValid(T userInput);
}
/**
* Simple validator that checks for text not being empty
*/
public static class TextNotEmptyConstraint implements ValidationConstraint<String> {
private final String errorMsg;
public TextNotEmptyConstraint(String errorMessage) {
this.errorMsg = errorMessage;
}
@Override
public String checkStateValid(String userInput) {
if (Util.isEmptyString(userInput))
return errorMsg;
return null;
}
}
/**
* Simple validator that checks if object is not <code>null</code>
*/
public static class NotNullConstraint<T> implements ValidationConstraint<T> {
private final String errorMsg;
public NotNullConstraint(String errorMessage) {
this.errorMsg = errorMessage;
}
@Override
public String checkStateValid(T userInput) {
if (userInput == null)
return errorMsg;
return null;
}
}
/*
* Default validators
*/
/**
* Callback for class using the monitor
*/
public static interface Callback {
public void validityChanged(String errorMessage);
public void modificationChanged();
}
}
|