View Javadoc

1   /*
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.components;
20  
21  /***
22  * NumericTextField is a "smart" text field that restricts the user's input.  The
23  * input is restructed to numeric only wich can be of two types: integer and real
24  * numbers.  A range can also be placed on the text field.  The default type is
25  * integer, with the being (Integer.MIN_VALUE, Integer.MAX_VALUE).
26  *
27  * @author rob@straylight.princeton.com
28  * @author $Author: cgruber $
29  * @version $Revision: 893 $
30  */
31  public class NumericTextField extends SmartTextField
32  {
33  
34  /********************************
35  * CONSTANTS
36  *******************************/
37  
38  /***
39  * Restrict the text input to integers (whole numbers) only.
40  */
41     public final static int INTEGER = 0;
42  
43  /***
44  * Restrict the text input to floating-point numbers only.
45  */
46     public final static int FLOAT = 1;
47  
48     private Number maximumValue = null;
49     private Number minimumValue = null;
50  
51     private boolean sign = false;
52     private int newCaretPosition = 0;
53     private int valueType = INTEGER;
54  
55  
56  /********************************
57  * PUBLIC METHODS
58  *******************************/
59  
60  /***
61  * Default constructor.
62  */
63     public NumericTextField()
64     {
65        this("", 0);
66     }
67  
68  /***
69  * Constructor.
70  * @param text The initial string the text field is set to.
71  */
72     public NumericTextField(String text)
73     {
74        this(text, 0);
75     }
76  
77  /***
78  * Constructor.
79  * @param columns Width of the text field (in characters).
80  */
81     public NumericTextField(int columns)
82     {
83        this("", columns);
84     }
85  
86  /***
87  * Constructor.
88  * @param text The initial string the text field is set to.
89  * @param columns Width of the text field (in characters).
90  */
91     public NumericTextField(String text, int columns)
92     {
93        super(text, columns);
94     }
95  
96  /***
97  * Sets the upper limit of the range of numbers to accept.
98  * @param newMaximumValue The maximum number accepted by the text field.
99  */
100    public void setMaximumValue(double newMaximumValue)
101    {
102       if (newMaximumValue >= 0)
103       {
104          maximumValue = new Double( newMaximumValue );
105       }
106       else
107       {
108          maximumValue = null;
109       }
110    }
111 
112 /***
113 * Returns the upper limit of the range of numbers to accept.
114 * @return The maximum number accepted by this text field.
115 */
116    public double getMaximumValue()
117    {
118       if ( valueType == INTEGER )
119       {
120         return (maximumValue == null) ? (double) Integer.MAX_VALUE : maximumValue.doubleValue();
121       }
122       else
123       {
124         return (maximumValue == null) ? Double.MAX_VALUE : maximumValue.doubleValue();
125       }
126    }
127 
128 /***
129 * Sets the lower limit of the range of numbers to accept.
130 * @param newMinimumValue The minimum number accepted by the text field.
131 */
132    public void setMinimumValue(double newMinimumValue)
133    {
134       if (newMinimumValue <= 0)
135       {
136          minimumValue  = new Double( newMinimumValue );
137       }
138       else
139       {
140          minimumValue = null;
141       }
142    }
143 
144 /***
145 * Returns the lower limit of the range of numbers to accept.
146 * @return The minimum number accepted by this text field.
147 */
148    public double getMinimumValue()
149    {
150       if ( valueType == INTEGER )
151       {
152         return (minimumValue == null) ? (double) Integer.MIN_VALUE : minimumValue.doubleValue();
153       }
154       else
155       {
156         return (minimumValue == null) ? -1.0*Double.MAX_VALUE : minimumValue.doubleValue();
157         // NOTE: Double.MIN_VALUE returns the smallest positive value - oooops.
158       }
159    }
160 
161 /***
162 * Sets which type of number this text field can accept.
163 * @see #INTEGER
164 * @see #FLOAT
165 * @param newValueType The type of number to accept.
166 */
167    public void setValueType(int newValueType)
168    {
169       if ((newValueType != INTEGER) && (newValueType != FLOAT))
170       {
171          valueType = INTEGER;
172       }
173       else
174       {
175          valueType = newValueType;
176       }
177    }
178 
179 /***
180 * Returns which type of number this text field accepts.  The default is
181 * integer.
182 * @see #INTEGER
183 * @see #FLOAT
184 * @return The type of number to accept.
185 */
186    public int getValueType()
187    {
188       return valueType;
189    }
190 
191 /***
192 * Returns the integer numeric value of the string in the text field.  The type
193 * can be either integer of float.
194 * @return The current value in the text field.
195 */
196    public int getIntValue()
197    {
198       int value = 0;
199 
200       try
201       {
202          value = Integer.parseInt(getText());
203       }
204       catch (NumberFormatException e)
205       {
206          try
207          {
208             Double dValue = Double.valueOf(getText());
209             value = dValue.intValue();
210          }
211          catch (NumberFormatException ignored) {}
212       }
213 
214       return value;
215    }
216 
217 /***
218 * Sets the text field to integer value specified.
219 * @param aValue An integer value to display in the text field.
220 */
221    public void setIntValue(int aValue)
222    {
223       setText(Integer.toString(aValue));
224    }
225 
226 /***
227 * Returns the real number numeric value of the string in the text field.  The type
228 * can be either integer of float.
229 * @return The current value in the text field.
230 */
231    public double getDoubleValue()
232    {
233       Double value = new Double(0);
234 
235       try
236       {
237          value = Double.valueOf(getText());
238       }
239       catch (NumberFormatException ignored) {}
240 
241       return value.doubleValue();
242    }
243 
244 /***
245 * Sets the text field to the double value specified.  If the text field type is
246 * FLOAT then the the number is display as a real number.  If the text field
247 * type is INTEGER then the number is converted to a whole number for displaying.
248 * @param aValue A double value to display in the text field.
249 */
250    public void setDoubleValue(double aValue)
251    {
252        Double temp = new Double(aValue);
253 
254        if (valueType == FLOAT)
255        {
256           setText(temp.toString());
257        }
258        else
259        {
260           setText(Integer.toString(temp.intValue()));
261        }
262    }
263 
264 /********************************
265 * PROTECTED METHODS
266 *******************************/
267 
268    protected boolean isValidCharacter(char aChar)
269    {
270       if (((aChar >= ' ') && (aChar <= '/')) || ((aChar >= ':') && (aChar <= '~')))
271       {
272          if (aChar == '.')
273          {
274              if ( valueType == FLOAT )
275              {
276                  return true;
277              }
278              else
279              {
280                  return false;
281              }
282          }
283          else if (aChar == '-')
284          {
285              if ( getMinimumValue() < 0 )
286              {
287                  return true;
288              }
289              else
290              {
291                  return false;
292              }
293          }
294          else if (aChar == '+')
295          {
296              if ( getMaximumValue() >= 0 )
297              {
298                  return true;
299              }
300              else
301              {
302                  return false;
303              }
304          }
305          return false;
306       }
307       return true;
308    }
309 
310    protected boolean isValidString(String aString)
311    {
312       int iValue = 0;
313       double dValue = 0.0;
314 
315       String tempString = new String(scanForSignChar(aString));
316 
317       if ( valueType == INTEGER )
318       {
319           try
320           {
321              iValue = Integer.parseInt(tempString);
322           }
323           catch (NumberFormatException e1)
324           {
325              if ((tempString.compareTo("-") == 0) && (getMinimumValue() < 0.0))
326              {
327                 iValue = 0;
328              }
329              else
330              {
331                 return false;
332              }
333           }
334           if ((((double)iValue) < getMinimumValue()) || (((double)iValue) > getMaximumValue()))
335           {
336              return false;
337           }
338       }
339       else
340       {
341           // Double.valueOf requires a zero before the decimal point
342           if ( tempString.startsWith( "." ) )
343           {
344               tempString = "0" + tempString;
345           }
346           try
347           {
348              dValue = Double.valueOf(tempString).doubleValue();
349           }
350           catch (NumberFormatException e2)
351           {
352              if ((tempString.compareTo("-") == 0) && (getMinimumValue() < 0.0))
353              {
354                 dValue = 0.0;
355              }
356              else
357              {
358                 return false;
359              }
360           }
361 
362           if ((dValue < getMinimumValue()) || (dValue > getMaximumValue()))
363           {
364              return false;
365           }
366       }
367 
368       return true;
369    }
370 
371    protected void postProcessing()
372    {
373       if (sign)
374       {
375          setText(scanForSignChar(getText()));
376          setCaretPosition(newCaretPosition);
377       }
378       sign = false;
379    }
380 
381 
382 /********************************
383 * PRIVATE METHODS
384 *******************************/
385 
386    private String scanForSignChar(String aString)
387    {
388       String newString = "";
389       boolean positive = false;
390       boolean negative = false;
391       int oldCaretPosition = getCaretPosition();
392       int charactersAdded = 0;
393 
394       newCaretPosition = 0;
395 
396       if (aString.length() <= 0)
397       {
398          return aString;
399       }
400 
401       for (int i = 0; i < aString.length(); ++i)
402       {
403          switch (aString.charAt(i))
404          {
405             case '+': positive = true;
406                       break;
407             case '-': negative = true;
408                       break;
409             default:  newString += aString.charAt(i);
410                       charactersAdded++;
411                       break;
412          }
413 
414          if ((i + 1) == oldCaretPosition)
415          {
416             newCaretPosition = charactersAdded;
417          }
418       }
419 
420       if ((!(positive)) && (negative))
421       {
422          newString = "-" + newString;
423          newCaretPosition++;
424       }
425 
426       if (positive || negative)
427       {
428          sign = true;
429       }
430 
431       return newString;
432    }
433 }
434