release 0.9.6
[debian/openrocket] / src / net / sf / openrocket / gui / adaptors / IntegerModel.java
1 package net.sf.openrocket.gui.adaptors;
2
3 import java.lang.reflect.InvocationTargetException;
4 import java.lang.reflect.Method;
5 import java.util.ArrayList;
6
7 import javax.swing.SpinnerModel;
8 import javax.swing.SpinnerNumberModel;
9 import javax.swing.event.ChangeEvent;
10 import javax.swing.event.ChangeListener;
11
12 import net.sf.openrocket.util.BugException;
13 import net.sf.openrocket.util.ChangeSource;
14 import net.sf.openrocket.util.Reflection;
15
16
17 public class IntegerModel implements ChangeListener {
18
19
20         //////////// JSpinner Model ////////////
21         
22         private class IntegerSpinnerModel extends SpinnerNumberModel {
23                 @Override
24                 public Object getValue() {
25                         return IntegerModel.this.getValue();
26                 }
27
28                 @Override
29                 public void setValue(Object value) {
30                         if (firing > 0)   // Ignore, if called when model is sending events
31                                 return;
32                         Number num = (Number)value;
33                         int newValue = num.intValue();
34                         IntegerModel.this.setValue(newValue);
35                         
36 //                      try {
37 //                              int newValue = Integer.parseInt((String)value);
38 //                              IntegerModel.this.setValue(newValue);
39 //                      } catch (NumberFormatException e) { 
40 //                              IntegerModel.this.fireStateChanged();
41 //                      };
42                 }
43                         
44                 @Override
45                 public Object getNextValue() {
46                         int d = IntegerModel.this.getValue();
47                         if (d >= maxValue)
48                                 return null;
49                         return (d+1);
50                 }
51
52                 @Override
53                 public Object getPreviousValue() {
54                         int d = IntegerModel.this.getValue();
55                         if (d <= minValue)
56                                 return null;
57                         return (d-1);
58                 }
59                 
60                 @Override
61                 public void addChangeListener(ChangeListener l) {
62                         IntegerModel.this.addChangeListener(l);
63                 }
64
65                 @Override
66                 public void removeChangeListener(ChangeListener l) {
67                         IntegerModel.this.removeChangeListener(l);
68                 }
69         }
70         
71         /**
72          * Returns a new SpinnerModel with the same base as the DoubleModel.
73          * The values given to the JSpinner are in the currently selected units.
74          * 
75          * @return  A compatibility layer for a SpinnerModel.
76          */
77         public SpinnerModel getSpinnerModel() {
78                 return new IntegerSpinnerModel();
79         }
80         
81         
82
83
84         ////////////  Main model  /////////////
85
86         /*
87          * The main model handles all values in SI units, i.e. no conversion is made within the model.
88          */
89         
90         private final ChangeSource source;
91         private final String valueName;
92         
93         private final Method getMethod;
94         private final Method setMethod;
95         
96         private final ArrayList<ChangeListener> listeners = new ArrayList<ChangeListener>();
97
98         private final int minValue;
99         private final int maxValue;
100
101         
102         private int firing = 0;  //  >0 when model itself is sending events
103         
104         
105         // Used to differentiate changes in valueName and other changes in the source:
106         private int lastValue = 0;
107                 
108
109         
110         /**
111          * Generates a new DoubleModel that changes the values of the specified source.
112          * The double value is read and written using the methods "get"/"set" + valueName.
113          *  
114          * @param source Component whose parameter to use.
115          * @param valueName Name of metods used to get/set the parameter.
116          * @param multiplier Value shown by the model is the value from source.getXXX * multiplier
117          * @param min Minimum value allowed (in SI units)
118          * @param max Maximum value allowed (in SI units)
119          */
120         public IntegerModel(ChangeSource source, String valueName, int min, int max) {
121                 this.source = source;
122                 this.valueName = valueName;
123                 
124                 this.minValue = min;
125                 this.maxValue = max;
126                 
127                 try {
128                         getMethod = source.getClass().getMethod("get" + valueName);
129                         setMethod = source.getClass().getMethod("set" + valueName,int.class);
130                 } catch (NoSuchMethodException e) {
131                         throw new IllegalArgumentException("get/set methods for value '"+valueName+
132                                         "' not present in class "+source.getClass().getCanonicalName());
133                 }
134         }
135
136         public IntegerModel(ChangeSource source, String valueName, int min) {
137                 this(source,valueName,min,Integer.MAX_VALUE);
138         }
139         
140         public IntegerModel(ChangeSource source, String valueName) {
141                 this(source,valueName,Integer.MIN_VALUE,Integer.MAX_VALUE);
142         }
143         
144
145         
146         
147         /**
148          * Returns the value of the variable.
149          */
150         public int getValue() {
151                 try {
152                         return (Integer)getMethod.invoke(source);
153                 } catch (IllegalArgumentException e) {
154                         throw new BugException(e);
155                 } catch (IllegalAccessException e) {
156                         throw new BugException(e);
157                 } catch (InvocationTargetException e) {
158                         throw Reflection.handleWrappedException(e);
159                 }
160         }
161         
162         /**
163          * Sets the value of the variable.
164          */
165         public void setValue(int v) {
166                 try {
167                         setMethod.invoke(source, v);
168                 } catch (IllegalArgumentException e) {
169                         throw new BugException(e);
170                 } catch (IllegalAccessException e) {
171                         throw new BugException(e);
172                 } catch (InvocationTargetException e) {
173                         throw Reflection.handleWrappedException(e);
174                 }
175         }
176
177         
178         /**
179          * Add a listener to the model.  Adds the model as a listener to the Component if this
180          * is the first listener.
181          * @param l Listener to add.
182          */
183         public void addChangeListener(ChangeListener l) {
184                 if (listeners.isEmpty()) {
185                         source.addChangeListener(this);
186                         lastValue = getValue();
187                 }
188
189                 listeners.add(l);
190         }
191
192         /**
193          * Remove a listener from the model.  Removes the model from being a listener to the Component
194          * if this was the last listener of the model.
195          * @param l Listener to remove.
196          */
197         public void removeChangeListener(ChangeListener l) {
198                 listeners.remove(l);
199                 if (listeners.isEmpty()) {
200                         source.removeChangeListener(this);
201                 }
202         }
203         
204         public void fireStateChanged() {
205                 Object[] l = listeners.toArray();
206                 ChangeEvent event = new ChangeEvent(this);
207                 firing++;
208                 for (int i=0; i<l.length; i++)
209                         ((ChangeListener)l[i]).stateChanged(event);
210                 firing--;
211         }
212
213         /**
214          * Called when the source changes.  Checks whether the modeled value has changed, and if
215          * it has, updates lastValue and generates ChangeEvents for all listeners of the model.
216          */
217         public void stateChanged(ChangeEvent e) {
218                 int v = getValue();
219                 if (lastValue == v)
220                         return;
221                 lastValue = v;
222                 fireStateChanged();
223         }
224
225         /**
226          * Explain the DoubleModel as a String.
227          */
228         @Override
229         public String toString() {
230                 return "IntegerModel["+source.getClass().getCanonicalName()+":"+valueName+"]";
231         }
232         
233 }