1

I look all over the net and all the things I tried didn't work....

I found that I can use some JTextFormatterField but it didn't work.

Then I found that I can use DocumentFilter with regex and that what I did:

JTextField jFormattedTextFieldMoneyToConvert = new JTextField();
    ((AbstractDocument) jFormattedTextFieldMoneyToConvert.getDocument()).setDocumentFilter(new DocumentFilter(){
        Pattern regEx = Pattern.compile("^\\d+\\.?\\d*$");

        @Override
        public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
            Matcher matcher = regEx.matcher(text);
            if (!matcher.matches()) {
                return;
            }
            super.replace(fb, offset, length, text, attrs);
        }
    });

but it doesn't work...It accepts only digits. I want it to accept also dots. And I also need it to not to start with a dot and not to end with a dot.

what I'm doing wrong?

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
Alex K
  • 5,092
  • 15
  • 50
  • 77
  • 1
    `but it doesn't work...` please define ... – Thomas May 11 '15 at 12:22
  • sorry, I edited my question – Alex K May 11 '15 at 12:47
  • `I also need it to not to start with a dot and not to end with a dot.` - the expression for that would be `^\d+(\.\d+)?$`, i.e. at least one digit optionally followed by a dot and at least one more digit. – Thomas May 11 '15 at 12:51

2 Answers2

3

This is probably the simplest code that works:

if (text.matches("\\d+(\\.\\d+)?"))

If there's a dot, it must be followed by some digits. If you want to allow a dot then no digits too, change the + to *:

if (text.matches("\\d+(\\.\\d*)?"))

Note that with String#matches(), the leading/trailing ^/$ are not necessary as they are implied, because the entire string must be matched for matches() to return true.

Allowing no leading digits too becomes trickier, because the regex (\d+)?(\.\d*) matches the blank string, so you need an alternation:

if (text.matches("\\d+(\\.\\d*)?|\\.\\d+"))

or a negative look ahead:

if (text.matches("(?!$)\\d*(\\.\\d*)?"))

The little look head asserts that the next position after the start is not the end (ie input is not blank).

Bohemian
  • 412,405
  • 93
  • 575
  • 722
2

I had a similar problem a while back. The problem wasn't with the regex, but my misunderstanding of how the filter works. My assumption, like yours was that the String text passed to the replace method, is the entire string, when it's really only the character being typed. Similar discussion here.

While all of @Bohemian's regex kung-fu might be what your looking for in terms of a matching regex (I don't know, it's not my forte), the problem (with the assumption) can be fixed by obtaining the entire document text, then try and match that, i.e.

String completeText = fb.getDocument().getText(0, fb.getDocument().getLength());

"And I also need it to not to start with a dot and not to end with a dot"

Think about this logically for a second. The filter is called for each character input. But you don't want to end with . but you want to allow . somewhere in the middle. Not allowing . at the end negate the possibility of a . at all as you would need to type a . at the end before the next number

You could probably just write a focus listener to add a 0 or remove the . when the field loses focus, if the user leaves a hanging .


UPDATE: full example

import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;

public class FilterDemo {

    public FilterDemo() {
        JFrame frame = new JFrame();
        frame.setLayout(new GridBagLayout());
        frame.setSize(300, 300);
        frame.add(createFilteredField());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationByPlatform(true);
        frame.setVisible(true);

    }

    private JTextField createFilteredField() {
        JTextField field = new JTextField(20);
        AbstractDocument document = (AbstractDocument) field.getDocument();
        final int maxCharacters = 10;
        document.setDocumentFilter(new DocumentFilter() {
            @Override
            public void replace(FilterBypass fb, int offs, int length,
                    String str, AttributeSet a) throws BadLocationException {

                String text = fb.getDocument().getText(0,
                        fb.getDocument().getLength());
                text += str;
                if (text.matches("^\\d+\\.?\\d*$")) {   //  "^[0-9]+[.]?[0-9]{0,}$"
                    super.replace(fb, offs, length, str, a);
                } else {
                    Toolkit.getDefaultToolkit().beep();
                }

            }

            @Override
            public void insertString(FilterBypass fb, int offs, String str,
                    AttributeSet a) throws BadLocationException {

                String text = fb.getDocument().getText(0,
                        fb.getDocument().getLength());
                text += str;
                if (text.matches("^\\d+\\.?\\d*$")) {
                    super.insertString(fb, offs, str, a);
                } else {
                    Toolkit.getDefaultToolkit().beep();
                }
            }
        });
        return field;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new FilterDemo();
            }
        });
    }
}
Community
  • 1
  • 1
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720