1 package net.sf.openrocket.gui.adaptors;
3 import java.awt.Component;
4 import java.awt.event.ActionEvent;
5 import java.beans.PropertyChangeListener;
6 import java.lang.reflect.InvocationTargetException;
7 import java.lang.reflect.Method;
8 import java.util.ArrayList;
11 import javax.swing.AbstractAction;
12 import javax.swing.event.ChangeEvent;
13 import javax.swing.event.ChangeListener;
15 import net.sf.openrocket.logging.LogHelper;
16 import net.sf.openrocket.startup.Application;
17 import net.sf.openrocket.util.BugException;
18 import net.sf.openrocket.util.ChangeSource;
19 import net.sf.openrocket.util.Invalidatable;
20 import net.sf.openrocket.util.Invalidator;
21 import net.sf.openrocket.util.MemoryManagement;
22 import net.sf.openrocket.util.Reflection;
26 * A class that adapts an isXXX/setXXX boolean variable. It functions as an Action suitable
27 * for usage in JCheckBox or JToggleButton. You can create a suitable button with
29 * check = new JCheckBox(new BooleanModel(component,"Value"))
30 * check.setText("Label");
32 * This will produce a button that uses isValue() and setValue(boolean) of the corresponding
35 * Additionally a number of component enabled states may be controlled by this class using
36 * the method {@link #addEnableComponent(Component, boolean)}.
38 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
41 public class BooleanModel extends AbstractAction implements ChangeListener, Invalidatable {
42 private static final LogHelper log = Application.getLogger();
44 private final ChangeSource source;
45 private final String valueName;
47 private final Method getMethod;
48 private final Method setMethod;
49 private final Method getEnabled;
51 private final List<Component> components = new ArrayList<Component>();
52 private final List<Boolean> componentEnableState = new ArrayList<Boolean>();
54 private String toString = null;
56 private int firing = 0;
58 private boolean oldValue;
59 private boolean oldEnabled;
61 private Invalidator invalidator = new Invalidator(this);
64 public BooleanModel(ChangeSource source, String valueName) {
66 this.valueName = valueName;
68 Method getter = null, setter = null;
73 getter = source.getClass().getMethod("is" + valueName);
74 } catch (NoSuchMethodException ignore) {
78 getter = source.getClass().getMethod("get" + valueName);
79 } catch (NoSuchMethodException ignore) {
83 setter = source.getClass().getMethod("set" + valueName, boolean.class);
84 } catch (NoSuchMethodException ignore) {
87 if (getter == null || setter == null) {
88 throw new IllegalArgumentException("get/is methods for boolean '" + valueName +
89 "' not present in class " + source.getClass().getCanonicalName());
97 e = source.getClass().getMethod("is" + valueName + "Enabled");
98 } catch (NoSuchMethodException ignore) {
102 oldValue = getValue();
103 oldEnabled = getIsEnabled();
105 this.setEnabled(oldEnabled);
106 this.putValue(SELECTED_KEY, oldValue);
108 source.addChangeListener(this);
111 public boolean getValue() {
113 return (Boolean) getMethod.invoke(source);
114 } catch (IllegalAccessException e) {
115 throw new BugException("getMethod execution error for source " + source, e);
116 } catch (InvocationTargetException e) {
117 throw Reflection.handleWrappedException(e);
121 public void setValue(boolean b) {
123 log.debug("Setting value of " + this + " to " + b);
125 setMethod.invoke(source, new Object[] { b });
126 } catch (IllegalAccessException e) {
127 throw new BugException("setMethod execution error for source " + source, e);
128 } catch (InvocationTargetException e) {
129 throw Reflection.handleWrappedException(e);
135 * Add a component the enabled status of which will be controlled by the value
136 * of this boolean. The <code>component</code> will be enabled exactly when
137 * the state of this model is equal to that of <code>enableState</code>.
139 * @param component the component to control.
140 * @param enableState the state in which the component should be enabled.
142 public void addEnableComponent(Component component, boolean enableState) {
144 components.add(component);
145 componentEnableState.add(enableState);
146 updateEnableStatus();
150 * Add a component which will be enabled when this boolean is <code>true</code>.
151 * This is equivalent to <code>booleanModel.addEnableComponent(component, true)</code>.
153 * @param component the component to control.
154 * @see #addEnableComponent(Component, boolean)
156 public void addEnableComponent(Component component) {
158 addEnableComponent(component, true);
161 private void updateEnableStatus() {
162 boolean state = getValue();
164 for (int i = 0; i < components.size(); i++) {
165 Component c = components.get(i);
166 boolean b = componentEnableState.get(i);
167 c.setEnabled(state == b);
173 private boolean getIsEnabled() {
174 if (getEnabled == null)
177 return (Boolean) getEnabled.invoke(source);
178 } catch (IllegalAccessException e) {
179 throw new BugException("getEnabled execution error for source " + source, e);
180 } catch (InvocationTargetException e) {
181 throw Reflection.handleWrappedException(e);
186 public void stateChanged(ChangeEvent event) {
190 log.debug("Ignoring stateChanged of " + this + ", currently firing events");
194 boolean v = getValue();
195 boolean e = getIsEnabled();
197 log.debug("Value of " + this + " has changed to " + v + " oldValue=" + oldValue);
200 this.putValue(SELECTED_KEY, getValue());
201 // this.firePropertyChange(SELECTED_KEY, !v, v);
202 updateEnableStatus();
205 if (oldEnabled != e) {
206 log.debug("Enabled status of " + this + " has changed to " + e + " oldEnabled=" + oldEnabled);
214 public void actionPerformed(ActionEvent e) {
216 log.debug("Ignoring actionPerformed of " + this + ", currently firing events");
220 boolean v = (Boolean) this.getValue(SELECTED_KEY);
221 log.user("Value of " + this + " changed to " + v + " oldValue=" + oldValue);
225 oldValue = getValue();
227 this.putValue(SELECTED_KEY, oldValue);
228 this.setEnabled(getIsEnabled());
229 updateEnableStatus();
236 public void addPropertyChangeListener(PropertyChangeListener listener) {
238 super.addPropertyChangeListener(listener);
243 * Invalidates this model by removing all listeners and removing this from
244 * listening to the source. After invalidation no listeners can be added to this
245 * model and the value cannot be set.
248 public void invalidate() {
249 invalidator.invalidate();
251 PropertyChangeListener[] listeners = this.getPropertyChangeListeners();
252 if (listeners.length > 0) {
253 log.warn("Invalidating " + this + " while still having listeners " + listeners);
254 for (PropertyChangeListener l : listeners) {
255 this.removePropertyChangeListener(l);
258 if (source != null) {
259 source.removeChangeListener(this);
261 MemoryManagement.collectable(this);
265 private void checkState(boolean error) {
266 invalidator.check(error);
272 public String toString() {
273 if (toString == null) {
274 toString = "BooleanModel[" + source.getClass().getSimpleName() + ":" + valueName + "]";