package org.openslx.dozmod.gui.changemonitor; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Map; import org.openslx.dozmod.gui.changemonitor.DialogChangeMonitor.ValidationConstraint; /** * Control/element wrapper */ public abstract class AbstractControlWrapper { private final DialogChangeMonitor dcm; protected boolean wasEverChanged; protected boolean isCurrentlyChanged; private T originalContent; private List> constraints; private final Comparator cmp; protected String currentError = null; /** * If muted, this wrapper will not react to changes of the underlying * control. */ private boolean muted = false; protected AbstractControlWrapper(DialogChangeMonitor dcm, Comparator comp) { this.dcm = dcm; this.cmp = comp; } public boolean hasChangedSinceInit() { return wasEverChanged; } public boolean isCurrentlyChanged() { return isCurrentlyChanged; } /** * Returns the current error message for the first failed constraint. If no * constraint currently yields invalid, null is returned. * * @return */ public String currentConstraintError() { return currentError; } public AbstractControlWrapper addConstraint(ValidationConstraint constraint) { if (constraints == null) { constraints = new ArrayList<>(); } constraints.add(constraint); return this; } /** * Resets the change state of this control * and removes the "muted" flag, if set. */ public void reset() { boolean wasChanged = wasEverChanged; resetChangeState(); if (wasChanged) { // It's possible that the global change state changed dcm.contentChanged(this); } } protected final void resetChangeState() { muted = false; isCurrentlyChanged = wasEverChanged = false; originalContent = getCurrentValue(); checkValid(originalContent); } /** * To be called by the implementation whenever the content of the control * changed and according checks should be triggered. */ protected final void contentChanged() { T text = getCurrentValue(); checkChanged(text); checkValid(text); } /** * Ignore any changes, don't trigger callbacks regarding * this component. This flag can be removed by calling * reset() on this ControlWrapper or the enclosing * DialogChangeMonitor. */ public void mute() { muted = true; } /** * Method MUST return the current value of the monitored component, so * that the current state can be considered as the original unmodified * state. */ abstract T getCurrentValue(); protected void checkChanged(T newContent) { if (muted) return; final boolean changed; if (newContent == originalContent) { changed = false; // This also covers null == null } else if (newContent == null || originalContent == null) { changed = true; // So here we know either/or is null, not both } else if (newContent instanceof Collection && ((Collection) newContent).size() != ((Collection) originalContent).size()) { changed = true; } else if (newContent instanceof Map && ((Map) newContent).size() != ((Map) originalContent).size()) { changed = true; } else if (cmp == null) { changed = !originalContent.equals(newContent); } else { // User supplied comparator changed = cmp.compare(originalContent, newContent) != 0; } if (isCurrentlyChanged != changed) { isCurrentlyChanged = changed; dcm.contentChanged(this); } } protected void checkValid(T text) { if (muted || constraints == null || constraints.isEmpty()) return; String error = null; for (ValidationConstraint i : constraints) { error = i.checkStateValid(text); if (error != null) break; } if ((error == null && currentError != null) || (error != null && currentError == null) || (error != null && !error.equals(currentError))) { currentError = error; dcm.validityChanged(this); } } }