Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
TextInputRangeChecker |
|
| 2.2;2.2 |
1 | 0 | /* |
2 | Wotonomy: OpenStep design patterns for pure Java applications. |
|
3 | Copyright (C) 2000 Blacksmith, Inc. |
|
4 | ||
5 | This library is free software; you can redistribute it and/or |
|
6 | modify it under the terms of the GNU Lesser General Public |
|
7 | License as published by the Free Software Foundation; either |
|
8 | version 2.1 of the License, or (at your option) any later version. |
|
9 | ||
10 | This library is distributed in the hope that it will be useful, |
|
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
13 | Lesser General Public License for more details. |
|
14 | ||
15 | You should have received a copy of the GNU Lesser General Public |
|
16 | License along with this library; if not, see http://www.gnu.org |
|
17 | */ |
|
18 | ||
19 | package net.wotonomy.ui.swing.util; |
|
20 | ||
21 | import java.awt.Component; |
|
22 | import java.awt.event.FocusEvent; |
|
23 | import java.awt.event.FocusListener; |
|
24 | import java.util.ArrayList; |
|
25 | import java.util.Collection; |
|
26 | import java.util.Iterator; |
|
27 | ||
28 | import javax.swing.JOptionPane; |
|
29 | import javax.swing.SwingUtilities; |
|
30 | import javax.swing.text.JTextComponent; |
|
31 | ||
32 | /** |
|
33 | * This class will actively check the inputs of 2 numbers in seperate text |
|
34 | * components. The number in the text components represent an upper and lower |
|
35 | * bound to some range. This class checks to make sure the user inputs values |
|
36 | * in the lower bound text field that are less than the value of the upper |
|
37 | * bound and vice versa for the upper bound text field. This class will also |
|
38 | * check to make sure the bounds fall within a given range if specified. |
|
39 | * |
|
40 | * The checks are automatically performed when the focus is lost on either |
|
41 | * component. If the inputs are correct then no event occurs. If the inputs |
|
42 | * are not correct, then a dialog message is displayed stating the reason why |
|
43 | * the bounds are invalid, and the original correct value is restored into the |
|
44 | * text components. |
|
45 | * |
|
46 | * @author rglista |
|
47 | * @author $Author: cgruber $ |
|
48 | * @version $Revision: 904 $ |
|
49 | */ |
|
50 | public class TextInputRangeChecker implements FocusListener |
|
51 | { |
|
52 | protected static final int NONE = 0; |
|
53 | protected static final int LOWER = 1; |
|
54 | protected static final int UPPER = 2; |
|
55 | ||
56 | private JTextComponent lowerComponent; |
|
57 | private JTextComponent upperComponent; |
|
58 | private double maxRange; |
|
59 | private double lowerNumber; |
|
60 | private double upperNumber; |
|
61 | private Collection focusListeners; |
|
62 | ||
63 | private String invalidLowerMessage; |
|
64 | private String invalidUpperMessage; |
|
65 | private String invalidEitherMessage; |
|
66 | private String invalidRangeMessage; |
|
67 | ||
68 | ||
69 | /** |
|
70 | * Constructor with some of the settable parameters. No range checking is used. |
|
71 | * @param aLowerTextComponent A text component for the lower bound. |
|
72 | * @param anUpperTextComponent A text component for the upper bound. |
|
73 | */ |
|
74 | public TextInputRangeChecker( JTextComponent aLowerTextComponent, |
|
75 | JTextComponent anUpperTextComponent ) |
|
76 | { |
|
77 | 0 | this( aLowerTextComponent, anUpperTextComponent, null, null, 0.0 ); |
78 | 0 | } |
79 | ||
80 | /** |
|
81 | * Constructor with some of the settable parameters. No range checking is |
|
82 | * used. |
|
83 | * @param aLowerTextComponent A text component for the lower bound. |
|
84 | * @param anUpperTextComponent A text component for the upper bound. |
|
85 | * @param lowerTextName The name of the lower bound, eg - start year. |
|
86 | * @param upperTextName The name of the upper bound, eg - end year. |
|
87 | * is used. |
|
88 | */ |
|
89 | public TextInputRangeChecker( JTextComponent aLowerTextComponent, |
|
90 | JTextComponent anUpperTextComponent, |
|
91 | String lowerTextName, String upperTextName ) |
|
92 | { |
|
93 | 0 | this( aLowerTextComponent, anUpperTextComponent, lowerTextName, upperTextName, 0.0 ); |
94 | 0 | } |
95 | ||
96 | /** |
|
97 | * Constructor with some of the settable parameters. |
|
98 | * @param aLowerTextComponent A text component for the lower bound. |
|
99 | * @param anUpperTextComponent A text component for the upper bound. |
|
100 | * @param aMaxRange The range the bounds muist fall between, if 0 then no range |
|
101 | * is used. |
|
102 | */ |
|
103 | public TextInputRangeChecker( JTextComponent aLowerTextComponent, |
|
104 | JTextComponent anUpperTextComponent, |
|
105 | double aMaxRange ) |
|
106 | { |
|
107 | 0 | this( aLowerTextComponent, anUpperTextComponent, null, null, aMaxRange ); |
108 | 0 | } |
109 | ||
110 | /** |
|
111 | * Constructor with all the settable parameters. |
|
112 | * @param aLowerTextComponent A text component for the lower bound. |
|
113 | * @param anUpperTextComponent A text component for the upper bound. |
|
114 | * @param lowerTextName The name of the lower bound, eg - start year. |
|
115 | * @param upperTextName The name of the upper bound, eg - end year. |
|
116 | * @param aMaxRange The range the bounds muist fall between, if 0 then no range |
|
117 | * is used. |
|
118 | */ |
|
119 | 0 | public TextInputRangeChecker( JTextComponent aLowerTextComponent, |
120 | JTextComponent anUpperTextComponent, |
|
121 | String lowerTextName, String upperTextName, |
|
122 | double aMaxRange ) |
|
123 | 0 | { |
124 | 0 | lowerComponent = aLowerTextComponent; |
125 | 0 | upperComponent = anUpperTextComponent; |
126 | 0 | maxRange = aMaxRange; |
127 | ||
128 | 0 | focusListeners = new ArrayList( 1 ); // For most cases, there will be only 1 listener. |
129 | ||
130 | 0 | lowerComponent.addFocusListener( this ); |
131 | 0 | upperComponent.addFocusListener( this ); |
132 | ||
133 | 0 | lowerNumber = getNumber( lowerComponent ); |
134 | 0 | upperNumber = getNumber( upperComponent ); |
135 | ||
136 | 0 | if ( ( lowerTextName != null ) && ( upperTextName != null ) ) |
137 | { |
|
138 | 0 | invalidLowerMessage = "The " + lowerTextName + " must be less than or equal to the " + upperTextName + "."; |
139 | 0 | invalidUpperMessage = "The " + upperTextName + " must be greater than or equal to the " + lowerTextName + "."; |
140 | 0 | invalidEitherMessage = "The " + lowerTextName + " and/or the " + upperTextName + " are not correct."; |
141 | 0 | invalidRangeMessage = "The maximum range for the " + lowerTextName + " and " + upperTextName + " is " + maxRange + "."; |
142 | 0 | } |
143 | else |
|
144 | { |
|
145 | 0 | invalidLowerMessage = "The lower bound must be less than or equal to the upper bound."; |
146 | 0 | invalidUpperMessage = "The upper bound must be greater than or equal to the lower bound."; |
147 | 0 | invalidEitherMessage = "The upper and/or lower bounds are not correct."; |
148 | 0 | invalidRangeMessage = "The maximum range is " + maxRange + "."; |
149 | } |
|
150 | 0 | } |
151 | ||
152 | /** |
|
153 | * Allows the caller to perform the validation of the bounds programatically. |
|
154 | * The lower bound is compared to the upper bound and range checking is performed. |
|
155 | * If the lower bound is greater than the upper bound, or the range between the |
|
156 | * bounds is greater than the max range, then validation fails. |
|
157 | * @return TRUE is validation is successfull, FALSE if it fails. |
|
158 | */ |
|
159 | public boolean performCheck() |
|
160 | { |
|
161 | 0 | return validate( null ); |
162 | } |
|
163 | ||
164 | /** |
|
165 | * Adds the listener to the lists of focus listener maintened by this object. |
|
166 | * When one of the 2 text components receives a focus event, this object will |
|
167 | * fire that focus event to any of its listeners. This is useful when the |
|
168 | * calling object wants to be notified of the components focus events, but wants |
|
169 | * to ensure that the validation has occured first. |
|
170 | * <br><br> |
|
171 | * NOTE: The focus is only fired if the validation was successful. This might |
|
172 | * have to be changed. |
|
173 | * @param aListener A Focus Listener to receive Focus Events. |
|
174 | */ |
|
175 | public void addFocusListener( FocusListener aListener ) |
|
176 | { |
|
177 | 0 | focusListeners.add( aListener ); |
178 | 0 | } |
179 | ||
180 | /** |
|
181 | * Returns the last valid value of the lower bound. If this is called while |
|
182 | * the user is updating the text component but before the focus is lost, the |
|
183 | * value returned will be the original value before the user started updating |
|
184 | * the bound. |
|
185 | * @return The last valid value of the lower bound. |
|
186 | */ |
|
187 | public double getLastValidatedLowerNumber() |
|
188 | { |
|
189 | 0 | return lowerNumber; |
190 | } |
|
191 | ||
192 | /** |
|
193 | * Returns the last valid value of the upper bound. If this is called while |
|
194 | * the user is updating the text component but before the focus is lost, the |
|
195 | * value returned will be the original value before the user started updating |
|
196 | * the bound. |
|
197 | * @return The last valid value of the upper bound. |
|
198 | */ |
|
199 | public double getLastValidatedUpperNumber() |
|
200 | { |
|
201 | 0 | return upperNumber; |
202 | } |
|
203 | ||
204 | /** |
|
205 | * Method used to be notified when one of the text components has gained its |
|
206 | * focus. |
|
207 | */ |
|
208 | public void focusGained( FocusEvent e ) |
|
209 | { |
|
210 | 0 | lowerNumber = getNumber( lowerComponent ); |
211 | 0 | upperNumber = getNumber( upperComponent ); |
212 | 0 | } |
213 | ||
214 | /** |
|
215 | * Method used to be notified when one of the text components has lost its |
|
216 | * focus. Automatic validation occurs here. |
|
217 | */ |
|
218 | public void focusLost( FocusEvent e ) |
|
219 | { |
|
220 | 0 | if ( e.isTemporary() ) |
221 | { |
|
222 | 0 | return; |
223 | } |
|
224 | ||
225 | 0 | if ( validate( e.getSource() ) ) |
226 | { |
|
227 | 0 | fireFocusEvent( e ); |
228 | } |
|
229 | 0 | } |
230 | ||
231 | /** |
|
232 | * Fires a focus lost event if the validation was successfull. |
|
233 | */ |
|
234 | protected void fireFocusEvent( FocusEvent e ) |
|
235 | { |
|
236 | 0 | for ( Iterator it = focusListeners.iterator(); it.hasNext(); ) |
237 | { |
|
238 | 0 | ( ( FocusListener )it.next() ).focusLost( e ); |
239 | 0 | } |
240 | 0 | } |
241 | ||
242 | /** |
|
243 | * Validates the bounds inputed by the user. |
|
244 | * @param aComponent The component to use to display a dialog window, if neccessray. |
|
245 | * If null, then the parent window of the text componets will be used. |
|
246 | * @return TRUE if validation was successful, FALSE otherwise. |
|
247 | */ |
|
248 | protected boolean validate( Object aComponent ) |
|
249 | { |
|
250 | 0 | int componentUsed = NONE; |
251 | 0 | if ( aComponent == lowerComponent ) |
252 | { |
|
253 | 0 | componentUsed = LOWER; |
254 | 0 | } |
255 | 0 | else if ( aComponent == upperComponent ) |
256 | { |
|
257 | 0 | componentUsed = UPPER; |
258 | } |
|
259 | ||
260 | 0 | double lower = getNumber( lowerComponent ); |
261 | 0 | double upper = getNumber( upperComponent ); |
262 | ||
263 | 0 | if ( lower > upper ) |
264 | { |
|
265 | 0 | if ( componentUsed == LOWER ) |
266 | { |
|
267 | 0 | lowerComponent.setText( Double.toString( lowerNumber ) ); |
268 | 0 | displayMessage( invalidLowerMessage, lowerComponent ); |
269 | 0 | } |
270 | 0 | else if ( componentUsed == UPPER ) |
271 | { |
|
272 | 0 | upperComponent.setText( Double.toString( upperNumber ) ); |
273 | 0 | displayMessage( invalidUpperMessage, upperComponent ); |
274 | 0 | } |
275 | else |
|
276 | { |
|
277 | 0 | upperComponent.setText( Double.toString( upperNumber ) ); |
278 | 0 | lowerComponent.setText( Double.toString( lowerNumber ) ); |
279 | 0 | displayMessage( invalidEitherMessage, lowerComponent.getTopLevelAncestor() ); |
280 | } |
|
281 | ||
282 | 0 | return false; |
283 | } |
|
284 | ||
285 | 0 | if ( maxRange != 0.0 ) |
286 | { |
|
287 | 0 | if ( ( upper - lower ) > maxRange ) |
288 | { |
|
289 | 0 | if ( componentUsed == LOWER ) |
290 | { |
|
291 | 0 | lowerComponent.setText( Double.toString( lowerNumber ) ); |
292 | 0 | displayMessage( invalidRangeMessage, lowerComponent ); |
293 | 0 | } |
294 | 0 | else if ( componentUsed == UPPER ) |
295 | { |
|
296 | 0 | upperComponent.setText( Double.toString( upperNumber ) ); |
297 | 0 | displayMessage( invalidRangeMessage, upperComponent ); |
298 | 0 | } |
299 | else |
|
300 | { |
|
301 | 0 | upperComponent.setText( Double.toString( upperNumber ) ); |
302 | 0 | lowerComponent.setText( Double.toString( lowerNumber ) ); |
303 | 0 | displayMessage( invalidRangeMessage, lowerComponent.getTopLevelAncestor() ); |
304 | } |
|
305 | ||
306 | 0 | return false; |
307 | } |
|
308 | } |
|
309 | ||
310 | 0 | lowerNumber = lower; |
311 | 0 | upperNumber = upper; |
312 | 0 | return true; |
313 | } |
|
314 | ||
315 | /** |
|
316 | * Creates a JOptionPane to display the reason why the bounds failed validation. |
|
317 | */ |
|
318 | protected void displayMessage( final String message, final Component parent ) |
|
319 | { |
|
320 | 0 | SwingUtilities.invokeLater( new Runnable() |
321 | { |
|
322 | 0 | public void run() |
323 | { |
|
324 | 0 | JOptionPane.showMessageDialog( parent, message, "Data Entry Error", |
325 | 0 | JOptionPane.ERROR_MESSAGE ); |
326 | 0 | } |
327 | } ); |
|
328 | 0 | } |
329 | ||
330 | /** |
|
331 | * Gets the number represented in the text component. If the text does not |
|
332 | * represent a number, then zero is returned. |
|
333 | */ |
|
334 | protected double getNumber( JTextComponent aComponent ) |
|
335 | { |
|
336 | try |
|
337 | { |
|
338 | 0 | return Double.valueOf( aComponent.getText() ).doubleValue(); |
339 | //1.2 return Double.parseDouble( aComponent.getText() ); |
|
340 | } |
|
341 | 0 | catch ( NumberFormatException e ) |
342 | { |
|
343 | 0 | System.out.println("[GUI] TextInputRangeChecker.getNumber(): The text is NOT a number: " + aComponent.getText() ); |
344 | 0 | return 0.0; |
345 | } |
|
346 | } |
|
347 | } |
|
348 | ||
349 | /* |
|
350 | * $Log$ |
|
351 | * Revision 1.2 2006/02/18 23:19:05 cgruber |
|
352 | * Update imports and maven dependencies. |
|
353 | * |
|
354 | * Revision 1.1 2006/02/16 13:22:22 cgruber |
|
355 | * Check in all sources in eclipse-friendly maven-enabled packages. |
|
356 | * |
|
357 | * Revision 1.2 2003/08/06 23:07:53 chochos |
|
358 | * general code cleanup (mostly, removing unused imports) |
|
359 | * |
|
360 | * Revision 1.1.1.1 2000/12/21 15:51:49 mpowers |
|
361 | * Contributing wotonomy. |
|
362 | * |
|
363 | * Revision 1.2 2000/12/20 16:25:46 michael |
|
364 | * Added log to all files. |
|
365 | * |
|
366 | * |
|
367 | */ |
|
368 |