Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6256410

JFormattedTextField looses its mask if using setValue on an invalid value

      FULL PRODUCT VERSION :
      Java HotSpot(TM) Client VM (build 1.4.2_03-b02, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Linux grovsl18 2.4.21-4.EL #1 Fri Oct 3 18:13:58 EDT 2003 i686 i686 i386 GNU/Linux
      SunOS grovss36 5.8 Generic_108528-15 sun4u sparc SUNW,Sun-Blade-1000


      A DESCRIPTION OF THE PROBLEM :
      If a JFormattedTextField is constructed with a javax.swing.text.MaskFormatter and a setValue() is performed on that TextField with an invalid value, then the mask is no longer displayed and it is impossible to type anything in the TextField anymore until the mask is restored by a setValue with a correct argument

      Ths is due to the install() method in JFormattedTextField.AbstractFormatter which performs a ftf.setText(""); if it catches a ParseException. In the case of a MaskFormatter, the original mask should be restored instead. As it is not the case, all subsequent typing is considered trivially false by MaskFormatter.getInvalidOffset

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :

      Select one value in the combobox then remove one or two characters from it and type 'Enter' so as to send an invalid value to the JFormattedTextField.

      This should erase its mask and make it beep each time you try to type any value into it.

      Initialize a JFormattedTextField with a MaskFormatter then set an invalid value.

      MaskFormatter formatter_ = new MaskFormatter("*****Y#");
      formatter_.setPlaceholderCharacter('_');

      JFormattedTextField reorderField_ = new JFormattedTextField(formatter_);

      reorderField_.setValue("aaaaaY_");

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The mask should have been restored in the JFormattedTextField : _____Y_
      ACTUAL -
      The JFormattedTextField is blank and beeps when trying to type into it.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      /**
       * if characters are removed from source in the combo, then the JFormattedTextField becomes blank
       * and it is no longer possible to enter any value with the keyboard
       */
      import java.io.StringWriter;
      import java.io.PrintWriter;

      import java.text.ParseException;

      import javax.swing.JLabel;
      import javax.swing.Box;
      import javax.swing.JFrame;
      import javax.swing.JComboBox;
      import javax.swing.DefaultComboBoxModel;
      import javax.swing.JComponent;
      import javax.swing.JPanel;
      import javax.swing.JFormattedTextField;
      import javax.swing.text.MaskFormatter;

      import java.awt.GridBagLayout;
      import java.awt.GridBagConstraints;
      import java.awt.Insets;
      import java.awt.BorderLayout;
      import java.awt.event.ItemListener;
      import java.awt.event.ItemEvent;

      public class PbMaskFormatter extends JFrame
      {
        private final GridBagLayout gridbag_ = new GridBagLayout();

        private final JLabel labReorder_ = new JLabel("Reorder :");
        private final JLabel labSource_ = new JLabel("Source :");

        private final JLabel[] labels_ = { labReorder_, labSource_ };

        private MaskFormatter formatter_;
        private final JFormattedTextField reorderField_;

        private final JComboBox comboSource_ = new JComboBox();

        public PbMaskFormatter() throws ParseException
        {
          super("Test case MaskFormatter");

          formatter_ = new MaskFormatter("*****Y#");
          formatter_.setPlaceholderCharacter('_');

          reorderField_ = new JFormattedTextField(formatter_);
          reorderField_.setFocusLostBehavior(JFormattedTextField.COMMIT);

          final JComponent[] fields = { reorderField_, comboSource_ };

          comboSource_.setEditable(true);

          // Valid values
          comboSource_.setModel(new DefaultComboBoxModel(new String[]{"TRICAP2", "BARNAP7"}));
          comboSource_.addItemListener(new ItemListener()
            {
              public void itemStateChanged(ItemEvent e)
              {
                if (e.getStateChange() != ItemEvent.SELECTED)
                  return;

                comboModified();
              }
            });

          GridBagConstraints contrainte = new GridBagConstraints();
          int nb = labels_.length;
          JPanel panelParams = new JPanel(gridbag_);

          for (int i = 0; i < nb; i++) {

            // label
            contrainte.anchor = GridBagConstraints.NORTHEAST;
            contrainte.gridx = 0;
            contrainte.gridy = i;
            contrainte.weightx = 0;
            contrainte.weighty = 0;
            contrainte.insets = new Insets(5, 10, 5, 5);
            contrainte.fill = GridBagConstraints.NONE;
            gridbag_.setConstraints(labels_[i], contrainte);

            // champ
            contrainte.anchor = GridBagConstraints.NORTHWEST;
            contrainte.gridx = 1;
            contrainte.gridy = i;
            contrainte.weightx = 0.98;
            contrainte.weighty = 0;
            contrainte.insets = new Insets(5, 5, 5, 5);
            contrainte.fill = GridBagConstraints.HORIZONTAL;
            gridbag_.setConstraints(fields[i], contrainte);

            panelParams.add(labels_[i]);
            panelParams.add(fields[i]);
          }

          // main panel
          Box panelPrincip_ = Box.createVerticalBox();
          panelPrincip_.add(Box.createVerticalStrut(8));
          panelPrincip_.add(panelParams);

          getContentPane().add(panelPrincip_, BorderLayout.CENTER);
          pack();
        }

        /**
         * source combo modified => update reordered name
         * if the value in the combo doesn't fit the right pattern (5 characters + anything + a digit)
         * the reorderField_ will be blank and will not accept keyboard typing anymore
         */
        private void comboModified()
        {
          String name = (String)comboSource_.getSelectedItem();
          if (name == null || name.length() == 0)
            return;

          reorderField_.setValue(concatName());
        }

        /**
         * name to display in the mask, based on comboSource_
         */
        private String concatName()
        {
          String name = (String) comboSource_.getSelectedItem();
          String source = name != null ? name.trim().toUpperCase() : "";

          // 5 first characters
          int taille = Math.min(5, source.length());
          StringBuffer result = new StringBuffer(source.substring(0, taille));

          // complete with _ if necessary
          int nb = result.length();
          if (nb < 5)
            for (int i = nb; i < 5; i++)
              result.append(formatter_.getPlaceholderCharacter());

          result.append('Y');

          // source number, character number 6
          if (source.length() > 6 && Character.isDigit(source.charAt(6)))
            result.append(source.charAt(6));
          else
            result.append(formatter_.getPlaceholderCharacter());

          return result.toString();
        }

        public static String getStackTrace(Throwable e)
        {
          StringWriter sw = new StringWriter();
          e.printStackTrace(new PrintWriter(sw));
          return sw.toString();
        }

        public static void main(String[] args)
        {
          try {
            PbMaskFormatter frame = new PbMaskFormatter();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setVisible(true);
          }
          catch (Exception e) {
            System.err.println(e + "\n" + getStackTrace(e));
            System.exit(-3);
          }
        }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      It is possible to extend the MaskFormatter class to override the install method :
      public void install(JFormattedTextField ftf)
      {
        super.install(ftf);

        if (ftf != null && !ftf.isEditValid()) // put the mask back
          ftf.setValue(null);
      }
      ###@###.### 2005-04-18 05:05:20 GMT

          Loading...
          Uploaded image for project: 'JDK'
          1. JDK
          2. JDK-6256410

          JFormattedTextField looses its mask if using setValue on an invalid value

              FULL PRODUCT VERSION :
              Java HotSpot(TM) Client VM (build 1.4.2_03-b02, mixed mode)

              ADDITIONAL OS VERSION INFORMATION :
              Linux grovsl18 2.4.21-4.EL #1 Fri Oct 3 18:13:58 EDT 2003 i686 i686 i386 GNU/Linux
              SunOS grovss36 5.8 Generic_108528-15 sun4u sparc SUNW,Sun-Blade-1000


              A DESCRIPTION OF THE PROBLEM :
              If a JFormattedTextField is constructed with a javax.swing.text.MaskFormatter and a setValue() is performed on that TextField with an invalid value, then the mask is no longer displayed and it is impossible to type anything in the TextField anymore until the mask is restored by a setValue with a correct argument

              Ths is due to the install() method in JFormattedTextField.AbstractFormatter which performs a ftf.setText(""); if it catches a ParseException. In the case of a MaskFormatter, the original mask should be restored instead. As it is not the case, all subsequent typing is considered trivially false by MaskFormatter.getInvalidOffset

              STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :

              Select one value in the combobox then remove one or two characters from it and type 'Enter' so as to send an invalid value to the JFormattedTextField.

              This should erase its mask and make it beep each time you try to type any value into it.

              Initialize a JFormattedTextField with a MaskFormatter then set an invalid value.

              MaskFormatter formatter_ = new MaskFormatter("*****Y#");
              formatter_.setPlaceholderCharacter('_');

              JFormattedTextField reorderField_ = new JFormattedTextField(formatter_);

              reorderField_.setValue("aaaaaY_");

              EXPECTED VERSUS ACTUAL BEHAVIOR :
              EXPECTED -
              The mask should have been restored in the JFormattedTextField : _____Y_
              ACTUAL -
              The JFormattedTextField is blank and beeps when trying to type into it.

              REPRODUCIBILITY :
              This bug can be reproduced always.

              ---------- BEGIN SOURCE ----------
              /**
               * if characters are removed from source in the combo, then the JFormattedTextField becomes blank
               * and it is no longer possible to enter any value with the keyboard
               */
              import java.io.StringWriter;
              import java.io.PrintWriter;

              import java.text.ParseException;

              import javax.swing.JLabel;
              import javax.swing.Box;
              import javax.swing.JFrame;
              import javax.swing.JComboBox;
              import javax.swing.DefaultComboBoxModel;
              import javax.swing.JComponent;
              import javax.swing.JPanel;
              import javax.swing.JFormattedTextField;
              import javax.swing.text.MaskFormatter;

              import java.awt.GridBagLayout;
              import java.awt.GridBagConstraints;
              import java.awt.Insets;
              import java.awt.BorderLayout;
              import java.awt.event.ItemListener;
              import java.awt.event.ItemEvent;

              public class PbMaskFormatter extends JFrame
              {
                private final GridBagLayout gridbag_ = new GridBagLayout();

                private final JLabel labReorder_ = new JLabel("Reorder :");
                private final JLabel labSource_ = new JLabel("Source :");

                private final JLabel[] labels_ = { labReorder_, labSource_ };

                private MaskFormatter formatter_;
                private final JFormattedTextField reorderField_;

                private final JComboBox comboSource_ = new JComboBox();

                public PbMaskFormatter() throws ParseException
                {
                  super("Test case MaskFormatter");

                  formatter_ = new MaskFormatter("*****Y#");
                  formatter_.setPlaceholderCharacter('_');

                  reorderField_ = new JFormattedTextField(formatter_);
                  reorderField_.setFocusLostBehavior(JFormattedTextField.COMMIT);

                  final JComponent[] fields = { reorderField_, comboSource_ };

                  comboSource_.setEditable(true);

                  // Valid values
                  comboSource_.setModel(new DefaultComboBoxModel(new String[]{"TRICAP2", "BARNAP7"}));
                  comboSource_.addItemListener(new ItemListener()
                    {
                      public void itemStateChanged(ItemEvent e)
                      {
                        if (e.getStateChange() != ItemEvent.SELECTED)
                          return;

                        comboModified();
                      }
                    });

                  GridBagConstraints contrainte = new GridBagConstraints();
                  int nb = labels_.length;
                  JPanel panelParams = new JPanel(gridbag_);

                  for (int i = 0; i < nb; i++) {

                    // label
                    contrainte.anchor = GridBagConstraints.NORTHEAST;
                    contrainte.gridx = 0;
                    contrainte.gridy = i;
                    contrainte.weightx = 0;
                    contrainte.weighty = 0;
                    contrainte.insets = new Insets(5, 10, 5, 5);
                    contrainte.fill = GridBagConstraints.NONE;
                    gridbag_.setConstraints(labels_[i], contrainte);

                    // champ
                    contrainte.anchor = GridBagConstraints.NORTHWEST;
                    contrainte.gridx = 1;
                    contrainte.gridy = i;
                    contrainte.weightx = 0.98;
                    contrainte.weighty = 0;
                    contrainte.insets = new Insets(5, 5, 5, 5);
                    contrainte.fill = GridBagConstraints.HORIZONTAL;
                    gridbag_.setConstraints(fields[i], contrainte);

                    panelParams.add(labels_[i]);
                    panelParams.add(fields[i]);
                  }

                  // main panel
                  Box panelPrincip_ = Box.createVerticalBox();
                  panelPrincip_.add(Box.createVerticalStrut(8));
                  panelPrincip_.add(panelParams);

                  getContentPane().add(panelPrincip_, BorderLayout.CENTER);
                  pack();
                }

                /**
                 * source combo modified => update reordered name
                 * if the value in the combo doesn't fit the right pattern (5 characters + anything + a digit)
                 * the reorderField_ will be blank and will not accept keyboard typing anymore
                 */
                private void comboModified()
                {
                  String name = (String)comboSource_.getSelectedItem();
                  if (name == null || name.length() == 0)
                    return;

                  reorderField_.setValue(concatName());
                }

                /**
                 * name to display in the mask, based on comboSource_
                 */
                private String concatName()
                {
                  String name = (String) comboSource_.getSelectedItem();
                  String source = name != null ? name.trim().toUpperCase() : "";

                  // 5 first characters
                  int taille = Math.min(5, source.length());
                  StringBuffer result = new StringBuffer(source.substring(0, taille));

                  // complete with _ if necessary
                  int nb = result.length();
                  if (nb < 5)
                    for (int i = nb; i < 5; i++)
                      result.append(formatter_.getPlaceholderCharacter());

                  result.append('Y');

                  // source number, character number 6
                  if (source.length() > 6 && Character.isDigit(source.charAt(6)))
                    result.append(source.charAt(6));
                  else
                    result.append(formatter_.getPlaceholderCharacter());

                  return result.toString();
                }

                public static String getStackTrace(Throwable e)
                {
                  StringWriter sw = new StringWriter();
                  e.printStackTrace(new PrintWriter(sw));
                  return sw.toString();
                }

                public static void main(String[] args)
                {
                  try {
                    PbMaskFormatter frame = new PbMaskFormatter();
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setVisible(true);
                  }
                  catch (Exception e) {
                    System.err.println(e + "\n" + getStackTrace(e));
                    System.exit(-3);
                  }
                }
              }
              ---------- END SOURCE ----------

              CUSTOMER SUBMITTED WORKAROUND :
              It is possible to extend the MaskFormatter class to override the install method :
              public void install(JFormattedTextField ftf)
              {
                super.install(ftf);

                if (ftf != null && !ftf.isEditValid()) // put the mask back
                  ftf.setValue(null);
              }
              ###@###.### 2005-04-18 05:05:20 GMT

                    vkarnauk Vladislav Karnaukhov
                    ndcosta Nelson Dcosta (Inactive)
                    Votes:
                    0 Vote for this issue
                    Watchers:
                    0 Start watching this issue

                      Created:
                      Updated:
                      Resolved:
                      Imported:
                      Indexed:

                        vkarnauk Vladislav Karnaukhov
                        ndcosta Nelson Dcosta (Inactive)
                        Votes:
                        0 Vote for this issue
                        Watchers:
                        0 Start watching this issue

                          Created:
                          Updated:
                          Resolved:
                          Imported:
                          Indexed: