1 package net.sf.openrocket.gui.adaptors;
3 import java.lang.reflect.InvocationTargetException;
4 import java.lang.reflect.Method;
5 import java.util.ArrayList;
7 import javax.swing.SpinnerModel;
8 import javax.swing.SpinnerNumberModel;
9 import javax.swing.event.ChangeEvent;
10 import javax.swing.event.ChangeListener;
12 import net.sf.openrocket.logging.LogHelper;
13 import net.sf.openrocket.startup.Application;
14 import net.sf.openrocket.util.BugException;
15 import net.sf.openrocket.util.ChangeSource;
16 import net.sf.openrocket.util.Reflection;
19 public class IntegerModel implements ChangeListener {
20 private static final LogHelper log = Application.getLogger();
23 //////////// JSpinner Model ////////////
25 private class IntegerSpinnerModel extends SpinnerNumberModel {
27 public Object getValue() {
28 return IntegerModel.this.getValue();
32 public void setValue(Object value) {
34 // Ignore, if called when model is sending events
35 log.verbose("Ignoring call to SpinnerModel setValue for " + IntegerModel.this.toString() +
36 " value=" + value + ", currently firing events");
40 Number num = (Number) value;
41 int newValue = num.intValue();
42 log.user("SpinnerModel setValue called for " + IntegerModel.this.toString() + " newValue=" + newValue);
43 IntegerModel.this.setValue(newValue);
47 public Object getNextValue() {
48 int d = IntegerModel.this.getValue();
55 public Object getPreviousValue() {
56 int d = IntegerModel.this.getValue();
63 public void addChangeListener(ChangeListener l) {
64 IntegerModel.this.addChangeListener(l);
68 public void removeChangeListener(ChangeListener l) {
69 IntegerModel.this.removeChangeListener(l);
74 * Returns a new SpinnerModel with the same base as the DoubleModel.
75 * The values given to the JSpinner are in the currently selected units.
77 * @return A compatibility layer for a SpinnerModel.
79 public SpinnerModel getSpinnerModel() {
80 return new IntegerSpinnerModel();
86 //////////// Main model /////////////
89 * The main model handles all values in SI units, i.e. no conversion is made within the model.
92 private final ChangeSource source;
93 private final String valueName;
95 private final Method getMethod;
96 private final Method setMethod;
98 private final ArrayList<ChangeListener> listeners = new ArrayList<ChangeListener>();
100 private final int minValue;
101 private final int maxValue;
103 private String toString = null;
106 private int firing = 0; // >0 when model itself is sending events
109 // Used to differentiate changes in valueName and other changes in the source:
110 private int lastValue = 0;
115 * Generates a new DoubleModel that changes the values of the specified source.
116 * The double value is read and written using the methods "get"/"set" + valueName.
118 * @param source Component whose parameter to use.
119 * @param valueName Name of methods used to get/set the parameter.
120 * @param min Minimum value allowed (in SI units)
121 * @param max Maximum value allowed (in SI units)
123 public IntegerModel(ChangeSource source, String valueName, int min, int max) {
124 this.source = source;
125 this.valueName = valueName;
131 getMethod = source.getClass().getMethod("get" + valueName);
132 setMethod = source.getClass().getMethod("set" + valueName, int.class);
133 } catch (NoSuchMethodException e) {
134 throw new IllegalArgumentException("get/set methods for value '" + valueName +
135 "' not present in class " + source.getClass().getCanonicalName());
139 public IntegerModel(ChangeSource source, String valueName, int min) {
140 this(source, valueName, min, Integer.MAX_VALUE);
143 public IntegerModel(ChangeSource source, String valueName) {
144 this(source, valueName, Integer.MIN_VALUE, Integer.MAX_VALUE);
151 * Returns the value of the variable.
153 public int getValue() {
155 return (Integer) getMethod.invoke(source);
156 } catch (IllegalArgumentException e) {
157 throw new BugException(e);
158 } catch (IllegalAccessException e) {
159 throw new BugException(e);
160 } catch (InvocationTargetException e) {
161 throw Reflection.handleWrappedException(e);
166 * Sets the value of the variable.
168 public void setValue(int v) {
169 log.debug("Setting value " + v + " for " + this);
171 setMethod.invoke(source, v);
172 } catch (IllegalArgumentException e) {
173 throw new BugException(e);
174 } catch (IllegalAccessException e) {
175 throw new BugException(e);
176 } catch (InvocationTargetException e) {
177 throw Reflection.handleWrappedException(e);
183 * Add a listener to the model. Adds the model as a listener to the Component if this
184 * is the first listener.
185 * @param l Listener to add.
187 public void addChangeListener(ChangeListener l) {
188 if (listeners.isEmpty()) {
189 source.addChangeListener(this);
190 lastValue = getValue();
194 log.verbose(this + " adding listener (total " + listeners.size() + "): " + l);
198 * Remove a listener from the model. Removes the model from being a listener to the Component
199 * if this was the last listener of the model.
200 * @param l Listener to remove.
202 public void removeChangeListener(ChangeListener l) {
204 if (listeners.isEmpty()) {
205 source.removeChangeListener(this);
207 log.verbose(this + " removing listener (total " + listeners.size() + "): " + l);
212 protected void finalize() throws Throwable {
214 if (!listeners.isEmpty()) {
215 log.warn(this + " being garbage-collected while having listeners " + listeners);
220 public void fireStateChanged() {
221 Object[] l = listeners.toArray();
222 ChangeEvent event = new ChangeEvent(this);
224 for (int i = 0; i < l.length; i++)
225 ((ChangeListener) l[i]).stateChanged(event);
230 * Called when the source changes. Checks whether the modeled value has changed, and if
231 * it has, updates lastValue and generates ChangeEvents for all listeners of the model.
233 public void stateChanged(ChangeEvent e) {
242 * Explain the DoubleModel as a String.
245 public String toString() {
246 if (toString == null) {
247 toString = "IntegerModel[" + source.getClass().getSimpleName() + ":" + valueName + "]";