import javax.swing.*;
import javax.swing.text.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;

/** DemoInputValidation3 - demonstrate data-model input validation.<p>
*
* This program is a follow-on to <code>DemoInputValidation2</code>.
* Validating input via a component's data model requires an
* understanding of Java's
* model-view-controller (MVC) architecture.  Like all <code>JComponent</code>
* objects, <code>JTextField</code> has a data model that holds and
* manages its data.  By designing a custom data model, we can control
* the text placed in the <code>JTextField</code>.<p>
*
* Our task, therefore, is to define a custom data model for a
* <code>JTextField</code> and include the desired input validation
* in the methods that affect the data contained in
* the <code>JTextField</code>.
* <p>
*
* The default data model for a <code>JTextField</code> is provided by
* the
* <code>PlainDocument</code> class in the <code>javax.swing.text</code>
* package.  The only method in <code>PlainDocument</code> of concern
* here is <code>insertString</code>.
* This method is called when text is added to a
* <code>JTextField</code>.  This occurs not only
* when single characters are added but, also, when
* a paste operation is performed.<p>
*
* Our custom data model is a subclass of <code>PlainDocument</code>.
* It overrides <code>insertString</code> and
* adds to its usual task the additional task
* of validating the content -- in other words, make sure the
* data model <i>would be</i> valid if
* the new text were added.
* This sounds complicated, but it's quite straight forward.
* <p>
*
* Two data model classes are defined: one for our numeric text field,
* called <code>IntegerDocument</code>, and one for our alpha text field,
* called <code>AlphaDocument</code>.  Both are subclasses
* of <code>PlainDocument</code>, and, for convenience, both are defined
* as inner classes.
* <p>
*
* The approach to validating input is slightly different in each model.
* For the <code>IntegerDocument</code> model, the new
* "tentative"
* content is generated and validated.  Validation involves passing
* the new text to the <code>Integer.parseInt</code> method to see if it
* throws an exception.
* If the validation passes (i.e., no exception), then
* the <code>insertString</code> method of the
* superclass is called to finalize the change.  If validation fails,
* a beep is sounded and
* the superclass method is <i>not called</i>.  In this case, the
* <code>JTextField</code> retains its previous input.
* A simpler approach is just to check the new characters to ensure
* they are all digits; however, this would not catch the situation where
* the new content is out of range for an integer.
* <p>
*
* For the <code>AlphaDocument</code> model, the simple approach just
* described works
* fine.  The string to insert is checked character-by-charcter using
* the <code>Character.isLetter</code> method.  If all characters are letters,
* then the validation has passed and <code>insertString</code> of
* the superclass is called.  Otherwise, a beep is sounded and the
* insert does not take place.
* <p>
*
* Our new data models are installed in their corresponding
* <code>JTextField</code> components using the <code>setDocument</code>
* method.
* <p>
*
* <b>Note:</b> The idea for this demo program is from the November 20, 2001
* issue of <i>JDC Tech Tips</i>, written by John Zukowski.
* <a href="http://developer.java.sun.com/developer/JDCTechTips/2001/tt1120.html">
* Click here</a> to view this and other JDC Tech Tips on line.
* <p>
*
* @see <a href="DemoInputValidation3.java">source code</a>
* @author Scott MacKenzie, 2001
*/
public class DemoInputValidation3
{
   public static void main(String[] args)
   {
      DemoInputValidation3Frame frame = new DemoInputValidation3Frame();
      frame.setTitle("DemoInputValidation3");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.pack();
      frame.show();
   }
}

class DemoInputValidation3Frame extends JFrame
{
   private JTextField numericField;
   private JTextField alphaField;

   // constructor

   public DemoInputValidation3Frame()
   {
      // -------------------------------
      // create and configure components
      // -------------------------------

      numericField = new JTextField(10);
      alphaField = new JTextField(10);

      // set a new data model for the numeric field

      numericField.setDocument(new IntegerDocument()); 
      alphaField.setDocument(new AlphaDocument());

      // ------------------
      // arrange components
      // ------------------

      // put components in a panel

      JPanel p1 = new JPanel();
      p1.add(numericField);
      p1.setBorder(new TitledBorder(new EtchedBorder(),
         "Numeric field"));
      
      JPanel p2 = new JPanel();
      p2.add(alphaField);
      p2.setBorder(new TitledBorder(new EtchedBorder(),
         "Alpha field"));
      
      JPanel panel = new JPanel();
      panel.add(p1);
      panel.add(p2);

      // make the panel this extended JFrame's content pane

      this.setContentPane(panel);
   }

   // -------------
   // inner classes
   // -------------

   class IntegerDocument extends PlainDocument
   {
      public void insertString(int offset, String s, AttributeSet as)
      throws BadLocationException
      {
         if (s == null)
            return;

         // get the 'tentative' new content of the text field

         String tentative;
         int length = this.getLength();
         if (length == 0)
         {
            tentative = s;
         } else
         {
            String current = this.getText(0, length);
            StringBuffer sb = new StringBuffer(current);
            sb.insert(offset, s);
            tentative = sb.toString();
         }

         // see if the 'tentative' new content is OK
         // ... if so, perform the insert
         // ... if not, beep and leave as is

         try
         {
            Integer.parseInt(tentative);
            super.insertString(offset, s, as);
         } catch (NumberFormatException nfe)
         {
            Toolkit.getDefaultToolkit().beep();
         }
      }
   } 

   class AlphaDocument extends PlainDocument
   {
      public void insertString(int offset, String s, AttributeSet as)
      throws BadLocationException
      {

         // make sure string contains only letters

         boolean ok = true;
         for (int i = 0; i < s.length(); ++i)
         {
            char c = s.charAt(i);
            if (!Character.isLetter(c))
            {
               ok = false;
               break;
            }
         }

         // if only letters, insert string, otherwise beep

         if (ok)
            super.insertString(offset, s, as);
         else
            Toolkit.getDefaultToolkit().beep();
      }
   } 
}


