Coverage Report - net.wotonomy.ui.swing.util.TextInputRangeChecker
 
Classes in this File Line Coverage Branch Coverage Complexity
TextInputRangeChecker
0% 
0% 
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