1 package net.sf.openrocket.gui.adaptors;
3 import java.lang.reflect.InvocationTargetException;
4 import java.lang.reflect.Method;
5 import java.util.ArrayList;
6 import java.util.EventListener;
7 import java.util.EventObject;
9 import javax.swing.SpinnerModel;
10 import javax.swing.SpinnerNumberModel;
11 import javax.swing.event.ChangeEvent;
12 import javax.swing.event.ChangeListener;
14 import net.sf.openrocket.logging.LogHelper;
15 import net.sf.openrocket.startup.Application;
16 import net.sf.openrocket.util.BugException;
17 import net.sf.openrocket.util.ChangeSource;
18 import net.sf.openrocket.util.Reflection;
19 import net.sf.openrocket.util.StateChangeListener;
22 public class IntegerModel implements StateChangeListener {
23 private static final LogHelper log = Application.getLogger();
26 //////////// JSpinner Model ////////////
28 private class IntegerSpinnerModel extends SpinnerNumberModel {
30 public Object getValue() {
31 return IntegerModel.this.getValue();
35 public void setValue(Object value) {
37 // Ignore, if called when model is sending events
38 log.verbose("Ignoring call to SpinnerModel setValue for " + IntegerModel.this.toString() +
39 " value=" + value + ", currently firing events");
43 Number num = (Number) value;
44 int newValue = num.intValue();
45 log.user("SpinnerModel setValue called for " + IntegerModel.this.toString() + " newValue=" + newValue);
46 IntegerModel.this.setValue(newValue);
50 public Object getNextValue() {
51 int d = IntegerModel.this.getValue();
58 public Object getPreviousValue() {
59 int d = IntegerModel.this.getValue();
66 public void addChangeListener(ChangeListener l) {
67 IntegerModel.this.addChangeListener(l);
71 public void removeChangeListener(ChangeListener l) {
72 IntegerModel.this.removeChangeListener(l);
77 * Returns a new SpinnerModel with the same base as the DoubleModel.
78 * The values given to the JSpinner are in the currently selected units.
80 * @return A compatibility layer for a SpinnerModel.
82 public SpinnerModel getSpinnerModel() {
83 return new IntegerSpinnerModel();
89 //////////// Main model /////////////
92 * The main model handles all values in SI units, i.e. no conversion is made within the model.
95 private final ChangeSource source;
96 private final String valueName;
98 private final Method getMethod;
99 private final Method setMethod;
101 private final ArrayList<EventListener> listeners = new ArrayList<EventListener>();
103 private final int minValue;
104 private final int maxValue;
106 private String toString = null;
109 private int firing = 0; // >0 when model itself is sending events
112 // Used to differentiate changes in valueName and other changes in the source:
113 private int lastValue = 0;
118 * Generates a new DoubleModel that changes the values of the specified source.
119 * The double value is read and written using the methods "get"/"set" + valueName.
121 * @param source Component whose parameter to use.
122 * @param valueName Name of methods used to get/set the parameter.
123 * @param min Minimum value allowed (in SI units)
124 * @param max Maximum value allowed (in SI units)
126 public IntegerModel(ChangeSource source, String valueName, int min, int max) {
127 this.source = source;
128 this.valueName = valueName;
134 getMethod = source.getClass().getMethod("get" + valueName);
135 setMethod = source.getClass().getMethod("set" + valueName, int.class);
136 } catch (NoSuchMethodException e) {
137 throw new IllegalArgumentException("get/set methods for value '" + valueName +
138 "' not present in class " + source.getClass().getCanonicalName());
142 public IntegerModel(ChangeSource source, String valueName, int min) {
143 this(source, valueName, min, Integer.MAX_VALUE);
146 public IntegerModel(ChangeSource source, String valueName) {
147 this(source, valueName, Integer.MIN_VALUE, Integer.MAX_VALUE);
154 * Returns the value of the variable.
156 public int getValue() {
158 return (Integer) getMethod.invoke(source);
159 } catch (IllegalArgumentException e) {
160 throw new BugException(e);
161 } catch (IllegalAccessException e) {
162 throw new BugException(e);
163 } catch (InvocationTargetException e) {
164 throw Reflection.handleWrappedException(e);
169 * Sets the value of the variable.
171 public void setValue(int v) {
172 log.debug("Setting value " + v + " for " + this);
174 setMethod.invoke(source, v);
175 } catch (IllegalArgumentException e) {
176 throw new BugException(e);
177 } catch (IllegalAccessException e) {
178 throw new BugException(e);
179 } catch (InvocationTargetException e) {
180 throw Reflection.handleWrappedException(e);
186 * Add a listener to the model. Adds the model as a listener to the Component if this
187 * is the first listener.
188 * @param l Listener to add.
190 public void addChangeListener(EventListener l) {
191 if (listeners.isEmpty()) {
192 source.addChangeListener(this);
193 lastValue = getValue();
197 log.verbose(this + " adding listener (total " + listeners.size() + "): " + l);
201 * Remove a listener from the model. Removes the model from being a listener to the Component
202 * if this was the last listener of the model.
203 * @param l Listener to remove.
205 public void removeChangeListener(ChangeListener l) {
207 if (listeners.isEmpty()) {
208 source.removeChangeListener(this);
210 log.verbose(this + " removing listener (total " + listeners.size() + "): " + l);
215 protected void finalize() throws Throwable {
217 if (!listeners.isEmpty()) {
218 log.warn(this + " being garbage-collected while having listeners " + listeners);
223 public void fireStateChanged() {
224 EventListener[] list = listeners.toArray(new EventListener[0] );
225 EventObject event = new EventObject(this);
226 ChangeEvent cevent = new ChangeEvent(this);
228 for( EventListener l : list ) {
229 if ( l instanceof ChangeListener) {
230 ((ChangeListener)l).stateChanged(cevent);
231 } else if ( l instanceof StateChangeListener ) {
232 ((StateChangeListener)l).stateChanged(event);
239 * Called when the source changes. Checks whether the modeled value has changed, and if
240 * it has, updates lastValue and generates ChangeEvents for all listeners of the model.
243 public void stateChanged(EventObject e) {
252 * Explain the DoubleModel as a String.
255 public String toString() {
256 if (toString == null) {
257 toString = "IntegerModel[" + source.getClass().getSimpleName() + ":" + valueName + "]";