1

My current Java project requires me to work with dates and through my research and tinkering around I discovered the setLenient() method is defaulted to true. When I set it to false I begin to have problems with my code.

I have a JFormattedTextField intialized with a SimpleDateFormat. For my project, I need to have / placeholders always present so using the answer to this question, I was able to install /s as place holders in the form of MM/dd/yyyy

When the SimpleDateFormat is set to setLenient(false), and an incomplete date is entered (such as 12/34/5), and then have the focus shift away from the formatted textfield, the entire field including the /s is removed.

When SimpleDateFormat is set to setLenient(true), this doesn't happen. Instead, the incomplete entry of 12/34/5 is set to 01/03/0006 once focus is lost.

I need to be able to usesetLenient(false) on my simple date formatter while also always having / placeholders. I do not really know where the core issue lies in the unwanted behavior of the program and would appreciate any insight.

Simplified program concerning issue:

public class Hello implements ActionListener{
    private JFrame frame = new JFrame();
    private JPanel panel = new JPanel();
    private Date endingDate = new Date();
    private String endingString = null;
    private SimpleDateFormat dateFormatter = new SimpleDateFormat("MM/dd/yyyy");
    private JFormattedTextField formattedText = new JFormattedTextField(dateFormatter);
    private JLabel label1 = new JLabel();
    private JLabel label2 = new JLabel();
    private TextArea ta = new TextArea();
    private Button b = new Button("click");

    public Hello() {
        //setPreferredSize(new Dimension(500,500));
        b.addActionListener(this);
        label1 = new JLabel("test");
        label2 = new JLabel("test");
        formattedText.setColumns(10);
        dateFormatter.setLenient(false); // not efficient 
        try {
            MaskFormatter dateMask = new MaskFormatter("##/##/####");
            dateMask.install(formattedText);
        } catch (ParseException ex) {
            Logger.getLogger(Hello.class.getName()).log(Level.SEVERE, null, ex);
        }

        panel.add(formattedText);
        panel.add(label1);
        panel.add(label2);
        panel.add(ta);
        panel.add(b);
        frame.add(panel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);


    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable(){
            public void run(){
                new Hello();    
            }
        });

        System.out.println("Hello, World");
    }

    public void actionPerformed(ActionEvent e) {
        System.out.println("Action performed");
        System.out.println(formattedText);
        endingDate = (Date) formattedText.getValue();

        System.out.println(endingDate);
        endingString = dateFormatter.format(endingDate);
        System.out.println(endingString);

    }

}
Community
  • 1
  • 1
exit_1
  • 1,240
  • 4
  • 13
  • 32

1 Answers1

3

You can use an InputVerifier that will verify the contents. If the date is not valid, you can popup a JOptionPane.

See the example below. I try to parse the input in the method isValidDate. If it's not a valid date, it will return false, causing the InputVerifier to return false in it's verify method.

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.Box;
import javax.swing.InputVerifier;
import javax.swing.JComponent;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import javax.swing.text.MaskFormatter;

public class InputVerifyDate {

    private SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");

    public InputVerifyDate() {
        JFormattedTextField formattedField = createFormattedTextField();
        JTextField field = new JTextField(10);
        format.setLenient(false);

        Box box = Box.createVerticalBox();
        box.add(formattedField);
        box.add(Box.createVerticalStrut(10));
        box.add(field);
        box.setBorder(new EmptyBorder(10, 10, 10, 10));

        JFrame frame = new JFrame();
        frame.add(box);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private JFormattedTextField createFormattedTextField() {
        JFormattedTextField formattedField = null;
        try {
            MaskFormatter dateMask = new MaskFormatter("##/##/####");
            formattedField = new JFormattedTextField(dateMask);
        } catch (ParseException ex) {
            Logger.getLogger(InputVerifyDate.class.getName()).log(Level.SEVERE, null, ex);
        }
        formattedField.setColumns(10);
        formattedField.setInputVerifier(getInputVerifier());
        return formattedField;
    }

    private InputVerifier getInputVerifier() {
        InputVerifier verifier = new InputVerifier() {

            @Override
            public boolean verify(JComponent input) {
                JFormattedTextField field = (JFormattedTextField) input;
                String text = field.getText();
                return isValidDate(text);
            }

            @Override
            public boolean shouldYieldFocus(JComponent input) {
                boolean valid = verify(input);
                if (!valid) {
                    JOptionPane.showMessageDialog(null, "Please enter a valid date in format dd/mm/yyyy");
                }
                return valid;
            }

        };
        return verifier;
    }

    public boolean isValidDate(String dateString) {
        try {
            format.parse(dateString);
            return true;
        } catch (ParseException ex) {
            return false;

        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new InputVerifyDate();
            }
        });

    }
}
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • thanks for your answer @peeskillet, but after trying out your code, I noticed that formatted text does not accept dates such as 05/21/1995, but it does accept dates like 10/10/1995. Being able to accept the formerly mentioned date is an necessary functionality – exit_1 Apr 07 '14 at 16:16
  • Change my date format. I accidently put the days first. It should be months first – Paul Samsotha Apr 07 '14 at 16:17
  • your suggestion worked. the whole program does what I need it to do but instead of just copying and pasting your code and not growing in my understanding of java, can you please explain what exactly was causing my problem and how your solution specifically fixes it? – exit_1 Apr 07 '14 at 16:24
  • The reason my solution worked is because of the InputVerifier, which uses the focus system, a functionality the jforattedtext field doesn't have by default. I used my method `isValidDate` to verify in the InputVerifer. The other solution you have is to use a focuslistener on the text field. If it loses focus, and the date is not valid, you can set the date to a default date. Other than that, I would stick with the InputVerifier. – Paul Samsotha Apr 07 '14 at 16:28
  • Or you can just use a JSpinner with a SpinnerDateModel or just use any of the third part componenst like JDateChooser where the use can just select the date from a calendar. I prefer to use the third party component. Having to manually input dates isn't very user friendly IMO – Paul Samsotha Apr 07 '14 at 16:32
  • thanks, that helps me out and also brings up one more small question. I was in fact planning on using a focus listener in my next step of development to fire off other functions once the focus is lost. with this information, would you still recommend your method `isValidDate` to accomplish the current operation? – exit_1 Apr 07 '14 at 16:36
  • Yes, because when it loses focus, you still need to verify the date. If you don't you get your current result. When it's losing focus, validate the field, if it's not valid, you can just set a default date. – Paul Samsotha Apr 07 '14 at 16:38