2

I have a problem when I am trying to get the size of a JFormattedTextField. Actually I need the user to enter a simple pinCode, and then get the size of what he enters to loop on it right after it. If he entered 4 digits it's ok, or else he has to do it again. But when I run my project, I have an infinite loop with "Pin must be 4 digits"...

I already found this link, but it did not fix my problem.

Here's my code :

package codePin;

import java.io.*;
import java.text.NumberFormat;
import java.util.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class Main extends JFrame {

    private static final long serialVersionUID = 1L;

    private JPanel container = new JPanel();
    private JFormattedTextField jtf = new JFormattedTextField(NumberFormat.getIntegerInstance());
    private JLabel label = new JLabel("Enter Pin: ");
    private JButton b = new JButton("OK");

    public Main() {
        this.setTitle("APP");
        this.setSize(300, 500);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLocationRelativeTo(null);

        container.setBackground(Color.white);
        container.setLayout(new BorderLayout());
        JPanel top = new JPanel();
        Font police = new Font("Arial", Font.BOLD, 14);
        jtf.setFont(police);
        jtf.setPreferredSize(new Dimension(150, 30));
        jtf.setForeground(Color.BLUE);

        b.addActionListener(new BoutonListener());

        top.add(label);
        top.add(jtf);
        top.add(b); 

        this.setContentPane(top);
        this.setVisible(true);
    }

    class BoutonListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            int nbTry = 0;
            boolean authenticated = false;

            do {
                do {

                    if (jtf.getText().length() != 4) { 
                        System.out.println("Pin must be 4 digits");
                    } else {
                        System.out.println("Checking...");
                    }

                    ArrayList<Integer> pins = new ArrayList<Integer>(); 
                    readPinsData(new File("bdd.txt"), pins);

                    String[] thePins = new String[pins.size()];
                    for (int i = 0; i < thePins.length; i++) {
                        thePins[i] = pins.get(i).toString();
                    }

                    String passEntered = String.valueOf(jtf);

                    for (int i = 0; i < thePins.length; i++) {
                        if (passEntered.equals(thePins[i]) && jtf.getText().length() == 4) {
                            System.out.println(":)");
                            authenticated = true;
                            break;
                        }
                    }
                } while (jtf.getText().length() != 4);
                if (!authenticated && jtf.getText().length() == 4) {
                    System.out.println(":(");
                    nbTry++;
                }
            } while (nbTry < 3 && !authenticated);
            //System.out.println("TEXT : jtf " + jtf.getText());

        }
    }

    // Function to read/access my pins database (file bdd.txt)
    static public boolean readPinsData(File dataFile, ArrayList<Integer> data) {
        boolean err = false;
        try {
            Scanner scanner = new Scanner(dataFile);
            String line;
            while (scanner.hasNext()) {
                line = scanner.nextLine();
                try {
                    data.add(Integer.parseInt(line));
                } catch (NumberFormatException e) {
                    e.printStackTrace();
                    err = true;
                }
            }
            scanner.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            err = true;
        }

        return err;
    }

    public static void main(String[] args) {

        Main fen = new Main();
    }
}

bdd.txt :

1111
1234
2222
3333
4444
5555
6666
7777
8888
9999

How can I do that ? Any ideas ?

Thanks, Florent.

Community
  • 1
  • 1
hacks4life
  • 691
  • 5
  • 16
  • 38

6 Answers6

1

You seem to have misunderstood the concept of an actionListener: The Listener listens to your answer, and does nothing else. You have loops in the listener that wait for an correct amount of digits - thats wrong, you need to only handle one input in the listener (and after another click, the listener will be called again). And so, for sure, because you got your loop with only one answer the user entered, you got a non-ending loop. Just handle one input in the action listener, and you'll be fine. Here is an description how to write it: http://docs.oracle.com/javase/tutorial/uiswing/events/actionlistener.html.

David Georg Reichelt
  • 963
  • 1
  • 15
  • 36
1

None of the answers actually work. Short answer, you have to do the following check:

if (jtf.getText().replaceAll("\u00A0","").length() != 4) {
    System.out.println("Pin must be 4 digits");
    JOptionPane.showMessageDialog(null,"Pin must be 4 digits");
    return;
}

Explanation: the unicode character \u0160 which is used in the NumberFormat is not a non-breaking space. After the \u there must be the hexadecimal representation of the character, that is \u00A0.

Complete code:

package codePin;
import java.io.*;
import java.text.NumberFormat;
import java.util.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.concurrent.atomic.AtomicInteger;


public class Main extends JFrame {

    private static final long serialVersionUID = 1L;

    private JPanel container = new JPanel();
    private JFormattedTextField jtf = new JFormattedTextField(NumberFormat.getIntegerInstance());
    private JLabel label = new JLabel("Enter Pin: ");
    private JButton b = new JButton("OK");

    public Main() {
        this.setTitle("APP");
        this.setSize(300, 500);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLocationRelativeTo(null);

        container.setBackground(Color.white);
        container.setLayout(new BorderLayout());
        JPanel top = new JPanel();
        Font police = new Font("Arial", Font.BOLD, 14);
        jtf.setFont(police);
        jtf.setPreferredSize(new Dimension(150, 30));
        jtf.setForeground(Color.BLUE);

        b.addActionListener(new BoutonListener());

        top.add(label);
        top.add(jtf);
        top.add(b);

        this.setContentPane(top);
        this.setVisible(true);
    }

    class BoutonListener implements ActionListener {
        private final AtomicInteger nbTry = new AtomicInteger(0);
        ArrayList<Integer> pins = readPinsData("bdd.txt");
        public void actionPerformed(ActionEvent e) {
            if (nbTry.get() > 2) {
                JOptionPane.showMessageDialog(null, "Number of tries exceeded");
                return;
            }
            final String passEntered=jtf.getText().replaceAll("\u00A0", "");
            if (passEntered.length() != 4) {
                System.out.println("Pin must be 4 digits");
                JOptionPane.showMessageDialog(null, "Ping must be 4 digits");
                return;
            }
            System.out.println("Checking...");
            SwingWorker worker = new SwingWorker<Void, Void>() {
                @Override
                protected Void doInBackground() throws Exception {
                    boolean authenticated = false;
                    if (pins.contains(Integer.parseInt(passEntered))) {
                        System.out.println(":)");
                        authenticated = true;
                    }

                    if (!authenticated) {
                        System.out.println(":(");
                        nbTry.incrementAndGet();
                    }
                    return null;
                }
            };
            worker.execute();
        }


    }

    // Function to read/access my pins database (file bdd.txt)
    static public ArrayList<Integer> readPinsData(String dataFile) {
        final ArrayList<Integer> data=new ArrayList<Integer>();
        try {
            BufferedReader reader = new BufferedReader(new FileReader(new File(dataFile)));
            String line;
            try {
                while ((line = reader.readLine()) != null) {
                    try {
                        data.add(Integer.parseInt(line));
                    } catch (NumberFormatException e) {
                        e.printStackTrace();
                        System.err.printf("error parsing line '%s'\n", line);
                    }
                }
            } finally {
                reader.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("error:"+e.getMessage());
        }

        return data;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                Main fen = new Main();
            }
        });

    }
}
  • Thank you, it does work ! That is exactly what I was looking for. I just have one question, if I do not use do wile loop anywhore, how is this possible this way to use nbTry variable to let the user have 3 tries ? – hacks4life Mar 25 '14 at 11:07
  • By the way, apparently the value of nbTry is never used.. that is weird. – hacks4life Mar 25 '14 at 11:21
  • @FlorentP I did not know that you give the user only three attempts. In that case, you need to hold on the nbTry, yes. –  Mar 25 '14 at 11:31
  • What do you mean by hold on? – hacks4life Mar 25 '14 at 11:33
  • @FlorentP I have updated the code with the nbTry. It only allows three attempts. –  Mar 25 '14 at 11:40
  • Thank you naimdjon, this is perfect for me. – hacks4life Mar 25 '14 at 12:24
  • @naimdjon Actually I just have one more thing to ask you, with this final BoutonListener class, how can I access my bdd.txt file to check if the pin entered by the user is good or not ? Before I transform the code to use a JFrame, it was working in console. I was able to access the file and parse all the pins to check if good or not (as you can see in "protected Void doInBackground()". But now how can I reach my file this way? – hacks4life Mar 25 '14 at 13:31
  • @FlorentP what do you mean? The code is reading the file content of the file, doesn't it? `readPinsData(new File("bdd.txt"), pins);` –  Mar 25 '14 at 14:36
  • Are you sure ? Because it does not seem to read the file. I have edited my post so you can see what is in bdd.txt file. And actually if I enter one of those pin, I always have "Checking... :(" in the output, whereas I had ":)" before. – hacks4life Mar 25 '14 at 14:38
  • 1
    @FlorentP updated the code and it should fix your problem. The problem was because there was a check on `jtf.getText().length()` –  Mar 25 '14 at 15:40
  • I wanted to ask you also : in the future, if I need to interact with my button directly in my JFrame (for example instead of system.out.printing(":)") in the console, I want to display a message in the JFrame to let the user know his pin was correct such as in our cellphones), where do you suggest me to read doc to be able to do that? Thanks – hacks4life Mar 25 '14 at 16:01
  • @FlorentP you are welcome. It depends on the kind of application, but if you want the easiest solution, it would be: ` JOptionPane.showMessageDialog(null, "The PIN is correct!");` or something similar. –  Mar 25 '14 at 16:15
  • Ok just like you already did in the program. Perfect thank you. – hacks4life Mar 25 '14 at 16:19
0

You can get the length of the text of a JFormattedTextField, say jtf here this way:

jtf.getText().length(); 

Note: according to what you want use this :

String s = jtf.getText();
s = s.replaceAll(",", "");
if (s.length() != 4) 

But the problem you've got is due to the way you've used the loops.

I think you want to show a reaction when the user enters exactly for digits, and if so, you don't need a loop at all.


According to the comments:

  • Remove the loops (nTry < 3 ... )
  • Define int nbTry = 0; as a class member and keep track of it in your action listener.
  • Inside the BoutonListener check if everything is right do whatever otherwise nTry++, and if nTry >=3 do whatever you want.

import java.io.*;
import java.text.NumberFormat;
import java.util.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class Main extends JFrame {
    //-------------------------------here
    int nbTry = 0;
    //-------------------------------here

    private static final long serialVersionUID = 1L;

    private JPanel container = new JPanel();
    private JFormattedTextField jtf = new JFormattedTextField(NumberFormat.getIntegerInstance());
    private JLabel label = new JLabel("Enter Pin: ");
    private JButton b = new JButton("OK");

    public Main() {


        jtf.getText().length();
        this.setTitle("APP");
        this.setSize(300, 500);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLocationRelativeTo(null);

        container.setBackground(Color.white);
        container.setLayout(new BorderLayout());
        JPanel top = new JPanel();
        Font police = new Font("Arial", Font.BOLD, 14);
        jtf.setFont(police);
        jtf.setPreferredSize(new Dimension(150, 30));
        jtf.setForeground(Color.BLUE);

        b.addActionListener(new BoutonListener());

        top.add(label);
        top.add(jtf);
        top.add(b); 

        this.setContentPane(top);
        this.setVisible(true);
    }

    class BoutonListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            String s = jtf.getText();
            s = s.replaceAll(",", "");
            System.out.println(s);
            System.out.println(s.length());
            boolean authenticated = false;
            if (nbTry > 3) {
                //Oops, enough!
            }
            if (s.length() != 4) { 
                System.out.println("Pin must be 4 digits");
            } else {
                System.out.println("Checking...");
                //and check here!
                ArrayList<Integer> pins = new ArrayList<Integer>(); 
                readPinsData(new File("bdd.txt"), pins);

                String[] thePins = new String[pins.size()];
                for (int i = 0; i < thePins.length; i++) {
                    thePins[i] = pins.get(i).toString();
                }

                String passEntered = String.valueOf(jtf);

                for (int i = 0; i < thePins.length; i++) {
                    if (passEntered.equals(thePins[i]) && jtf.getText().length() == 4) {
                        System.out.println(":)");
                        authenticated = true;
                        break;
                    }
                }
                if (authenticated) {
                    //Congratulation! 
                }
                else {
                    nbTry++;
                }
            }
        }
    }

    // Function to read/access my pins database (file bdd.txt)
    static public boolean readPinsData(File dataFile, ArrayList<Integer> data) {
        boolean err = false;
        try {
            Scanner scanner = new Scanner(dataFile);
            String line;
            while (scanner.hasNext()) {
                line = scanner.nextLine();
                try {
                    data.add(Integer.parseInt(line));
                } catch (NumberFormatException e) {
                    e.printStackTrace();
                    err = true;
                }
            }
            scanner.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            err = true;
        }

        return err;
    }

    public static void main(String[] args) {

        Main fen = new Main();
    }
}
Mohsen Kamrani
  • 7,177
  • 5
  • 42
  • 66
  • Yes, that is what I did. But I do not understand why it does not work when I run the project. It seems the length is not taken into account. – hacks4life Mar 25 '14 at 10:48
  • You're using the loops badly. Please tell me what exactly do you want to do? – Mohsen Kamrani Mar 25 '14 at 10:50
  • What I need is asking a user to enter a pin code. If he enters 4 digits, then we check into a .txt file if the pin is there (if yes :) or else he has 2 more tries left). If the user enters a pin Code of a wrong size (that means 1 digit, 2 digits, 3 digits, 5 digits, etc...) we are just asking him to do it again with a simple "Pin must be 4 digits", but nbTry won't be affected (we can assimilate this to a credit card, while the PinEntered is not 4 digits, we still have our 3 tries, because by definition a Pin is 4 digits). – hacks4life Mar 25 '14 at 10:54
  • Where in the class do I need to define nbTry ? – hacks4life Mar 25 '14 at 11:09
  • I have used naimdjon solution, and so for I do not have this infinite loop problem anymore. But now I am looking to offer 3 chances to a user when he enters pin. In case the pin is not in the bdd.txt file, it displays ":(" and he has 2 more tries. But I can not figure it out if I do not use do while loop anymore. Any idea? – hacks4life Mar 25 '14 at 11:13
  • I tried your last edit solution, but no matter what I enter, I get "Pin must be 4 digits" in console. (I have entered : "4444", ",1", "421", "zfzee", "eefe", etc...) – hacks4life Mar 25 '14 at 11:19
  • @FlorentP- You're doing something wrong, I've added my result in my post and you can look at it. – Mohsen Kamrani Mar 25 '14 at 11:23
  • 1234 is `s` and 4 is s.length – Mohsen Kamrani Mar 25 '14 at 11:24
  • So it should work. It's so simple. Review it more carefully and you'll find where is mistake. – Mohsen Kamrani Mar 25 '14 at 11:26
  • Could you add the whole code you have for this output ? Because I copied/pasted exactly your post I do not get it. – hacks4life Mar 25 '14 at 11:27
  • Ok I tried your last edit (4mins ago), if I enter "1234", my output is 1 234 5 Pin must be 4 digits – hacks4life Mar 25 '14 at 11:34
  • That's completely a new problem! and your's seems to be answered already. – Mohsen Kamrani Mar 25 '14 at 11:36
0

You have an infinite loop in your code, so when the BoutonListener.actionPerformed is called for the first time it just spins there without exiting.

You will have to significantly modify your code. Start with extracting the variables used in the do-while conditions into the fields, and update these fields each time the actionPerformed is called.

Oleg Estekhin
  • 8,063
  • 5
  • 49
  • 52
0

You're running into an endless loop, because the user has no chance to reenter his PIN, if it doesn't match the length.

Looping and Listeners don't work quite well in this way. Why don't you just keep the relevant variables (e.g. nbTry) as members and perform your checks without looping?

Ray
  • 3,084
  • 2
  • 19
  • 27
0

Your mistake is in this if() condition :

for (int i = 0; i < thePins.length; i++) {
    if (passEntered.equals(thePins[i]) && jtf.getText().length() == 4) {
        System.out.println(":)");
        authenticated = true;
        break;
    }
}

As the legnth of your pinCode is not equal to 4, you never validate the condition, and never break the do-while() loop.

By the way, using break is not a very good idea since you can always find a loop type or condition that allows you to avoid it !

Siger
  • 9
  • 2
  • But I tried to enter a 4 digits pin, so the condition should be verified since the length of the pinCode is 4 this way. No? – hacks4life Mar 25 '14 at 10:57