I want to perform data validation while JOptionPane.I found the following approach but i am not really satisfied with it
import javax.swing.*;
import java.util.regex.*;
public class ValidateJOptionPane {
public static void main(String[] args) {
String input = JOptionPane.showInputDialog("Enter number: ");
Pattern p = Pattern.compile("[A-Z,a-z,&%$##!()*^]");
Matcher m = p.matcher(input);
if (m.find()) {
JOptionPane.showMessageDialog(null, "Please enter only numbers");
}
}
}
It would have been better and more sensible to use the regex to detect the characters that can be entered rather than testing for characters that can't be entered.
Is there a better and simpler way to do data validation with JOptionPane ? . I feel regex is overkill here.Correct me if i am wrong:P
P.S i am a beginner with Java
The long answer is, use DocumentFilter, see Implementing a Document Filter and DocumentFilter Examples for more details.
The problem with this is, you need to take control, you can't rely on the "simple", "helper" functionality provided by JOptionPane.showInputDialog, as you need access to the text field been used to prompt the user...
For example...
The following examples uses a (slightly) modified version of the PatternFilter from DocumentFilter Examples
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import javax.swing.text.DocumentFilter.FilterBypass;
public class ValidateTest {
public static void main(String[] args) {
JTextField field = new JTextField(20);
Pattern p = Pattern.compile("[0-9]+");
((AbstractDocument) field.getDocument()).setDocumentFilter(new PatternFilter(p));
int option = ask("Enter number:", field);
if (option == JOptionPane.OK_OPTION) {
System.out.println("You have entered " + field.getText());
}
}
public static int ask(String label, JComponent comp) {
JPanel panel = new JPanel();
panel.add(new JLabel(label));
panel.add(comp);
return JOptionPane.showOptionDialog(null, panel, label,
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE, null, null, null);
}
public static class PatternFilter extends DocumentFilter {
// Useful for every kind of input validation !
// this is the insert pattern
// The pattern must contain all subpatterns so we can enter characters into a text component !
private Pattern pattern;
public PatternFilter(Pattern p) {
pattern = p;
}
public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr)
throws BadLocationException {
String newStr = fb.getDocument().getText(0, fb.getDocument().getLength()) + string;
Matcher m = pattern.matcher(newStr);
if (m.matches()) {
super.insertString(fb, offset, string, attr);
} else {
}
}
public void replace(FilterBypass fb, int offset,
int length, String string, AttributeSet attr) throws
BadLocationException {
if (length > 0) {
fb.remove(offset, length);
}
insertString(fb, offset, string, attr);
}
}
}
Now, with a little clever design, you could write a simple helper class which built all this internally and provided a nice askFor(String label, Pattern p) style method that could return a String (or null if the user canceled the operation)
As you said "It would have been better and more sensible to use the regex to detect the characters that can be entered rather than testing for characters that can't be entered."
So you can just do the reverse checking then :
import javax.swing.*;
import java.util.regex.*;
public class ValidateJOptionPane {
public static void main(String[] args) {
String input = JOptionPane.showInputDialog("Enter number: ");
Pattern p = Pattern.compile("^[0-9]*$");
Matcher m = p.matcher(input);
if (!m.find()) { // if pattern doesn't match (not found)
JOptionPane.showMessageDialog(null, "Please enter only numbers");
}
}
}
[0-9] means digit 0-9 while * means some spaces
Related
while (matcher.find()) {
if (matcher.group(matcherGroup) != null) occurrence++;
}
return occurrence;
This is the code that i am using to search for the number of matches of a certain group. However there is no function like matcher.group(matcherGroup1, matcherGroup2) nor there is matcher.group(null) to search for multiple groups or all groups.
I have read Link groups (its stupid to group the arrays into one) or multiple groups?
Thanks
Can you not write your own matcherGroup implementation? The link you have provided is c#.
I have written a simple Java implementation which takes a single string, and then matches it against multiple RegExp patterns. The number of matches are then returned.
Alternatively please explain in more detail what you are trying to do.
package com.chocksaway;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
class SingleMatcher {
private Pattern pattern;
private java.util.regex.Matcher matcher;
private boolean found;
SingleMatcher(String input, String inputPattern) {
pattern = Pattern.compile(inputPattern);
matcher = pattern.matcher(input);
if (matcher.find()) {
this.found = true;
}
}
boolean getFound() {
return this.found;
}
}
class MatcherGroup {
private int matchedOccurances;
MatcherGroup(String input, List<String> multipleInputPattern) {
for (String inputPattern : multipleInputPattern) {
SingleMatcher eachSingleMatcher = new SingleMatcher(input, inputPattern);
if (eachSingleMatcher.getFound()) {
matchedOccurances++;
}
}
}
int getMatchedOccurances() {
return this.matchedOccurances;
}
}
/**
* Author milesd on 28/05/2017.
*/
public class RegExpMatcher {
public static void main(String[] args) {
SingleMatcher singleMatcher = new SingleMatcher("123", "\\d\\d\\d");
System.out.println(singleMatcher.getFound());
List<String> inputPatternList = new ArrayList<String>();
inputPatternList.add("\\d\\d\\d");
inputPatternList.add("John");
MatcherGroup matcherGroup = new MatcherGroup("John", inputPatternList);
System.out.println(matcherGroup.getMatchedOccurances());
}
}
I am using pholser's port. I have to generate strings matching a given pattern like \[a-zA-Z0-9\\.\\-\\\\;\\:\\_\\#\\[\\]\\^/\\|\\}\\{]* Length 40.
I extend the Generator class as:
public class InputGenerator extends Generator<TestData> {...}
It overloads a function:
publicTestData generate(SourceOfRandomness random, GenerationStatus status) {...}
Now, random has functions like nextDouble(), nextInt() but there is nothing for strings! How can I generate random strings matching the above pattern?
Find below snippet for a custom generator which implement the generate(..) method to return a random string matching your posted pattern.
public class MyCharacterGenerator extends Generator<String> {
private static final String LOWERCASE_CHARS = "abcdefghijklmnopqrstuvwxyz";
private static final String UPPERCASE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String NUMBERS = "0123456789";
private static final String SPECIAL_CHARS = ".-\\;:_#[]^/|}{";
private static final String ALL_MY_CHARS = LOWERCASE_CHARS
+ UPPERCASE_CHARS + NUMBERS + SPECIAL_CHARS;
public static final int CAPACITY = 40;
public MyCharacterGenerator () {
super(String.class);
}
#Override
public String generate(SourceOfRandomness random, GenerationStatus status) {
StringBuilder sb = new StringBuilder(CAPACITY);
for (int i = 0; i < CAPACITY; i++) {
int randomIndex = random.nextInt(ALL_MY_CHARS.length());
sb.append(ALL_MY_CHARS.charAt(randomIndex));
}
return sb.toString();
}
}
edit A simple unit test to demonstrate the usage of the MyCharacterGenerator class.
import com.pholser.junit.quickcheck.ForAll;
import com.pholser.junit.quickcheck.From;
import static org.junit.Assert.assertTrue;
import org.junit.contrib.theories.Theories;
import org.junit.contrib.theories.Theory;
import org.junit.runner.RunWith;
#RunWith(Theories.class)
public class MyCharacterGeneratorTest {
#Theory
public void shouldHold(#ForAll #From(MyCharacterGenerator.class) String s) {
// here you should add your unit test which uses the generated output
//
// assertTrue(doMyUnitTest(s) == expectedResult);
// the below lines only for demonstration and currently
// check that the generated random has the expected
// length and matches the expected pattern
System.out.println("shouldHold(): " + s);
assertTrue(s.length() == MyCharacterGenerator.CAPACITY);
assertTrue(s.matches("[a-zA-Z0-9.\\-\\\\;:_#\\[\\]^/|}{]*"));
}
}
sample output generated by shouldHold
shouldHold(): MD}o/LAkW/hbJVWPGdI;:RHpwo_T.lGs^DOFwu2.
shouldHold(): IT_O{8Umhkz{#PY:pmK6}Cb[Wc19GqGZjWVa#4li
shouldHold(): KQwpEz.CW28vy_/WJR3Lx2.tRC6uLIjOTQtYP/VR
shouldHold(): pc2_T4hLdZpK78UfcVmU\RTe9WaJBSGJ}5v#z[Z\
...
There is no random.nextString(), but there is a way to generate random strings within junit-quickcheck-generators library. You can access it when creating new generators using gen().type(String.class). However, it seems we don't have much control over it.
Here is a silly example of a StringBuilder generator to demonstrate how to use the String generator:
import com.pholser.junit.quickcheck.generator.GenerationStatus;
import com.pholser.junit.quickcheck.generator.Generator;
import com.pholser.junit.quickcheck.random.SourceOfRandomness;
public class StringBuilderGenerator extends Generator<StringBuilder> {
public StringBuilderGenerator() {
super(StringBuilder.class);
}
#Override
public StringBuilder generate(SourceOfRandomness random, GenerationStatus status) {
String s = gen().type(String.class).generate(random, status);
return new StringBuilder(s);
}
}
I just made a library that suppose to do what you want in a generic way: https://github.com/SimY4/coregex
Simple usage example:
import com.pholser.junit.quickcheck.Property;
import com.pholser.junit.quickcheck.runner.JUnitQuickcheck;
import org.junit.runner.RunWith;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
#RunWith(JUnitQuickcheck.class)
public class CoregexGeneratorTest {
#Property
public void shouldGenerateMatchingUUIDString(
#Regex("[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}")
String uuid) {
assertEquals(uuid, UUID.fromString(uuid).toString());
}
}
I would like to compare the input from a JTextField to all the elements in a string arraylist. If the input is equal to an element in the list I would like the program to acknowledge by saying "This is in my vocabulary.", and if it is not, I would like the program to say "This is NOT in my vocabulary." In my code, I have tried getting this to work buy I always get the message "This is NOT in my vocabulary." even if the input matches an element in my list. How can I get this to work properly?
Here is my code, in it AI is where the list that is being compared is.
package Important;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JTextField;
import AI.*;
public class TextActions implements ActionListener{
private String hero;
private Vocabulary vocab1 = new Vocabulary();
public void actionPerformed(ActionEvent e) {
e.getSource();
hero = e.getActionCommand();
if (vocab1.Adjectives.equals(hero)){
System.out.println("This word is in my vocab");
}else{
System.out.println( hero + " is not in my vocab");
}
//CompareWords(hero);
}
public void CompareWords(String readme){
if (vocab1.Adjectives.contains(readme)){
//System.out.println("This word is in my vocab");
}
}
}
Here is the Vocabulary class as requested.
package AI;
import java.util.*;
public class Vocabulary {
//String[] thoughts;
public List<String> Adjectives = new ArrayList<String>();
public void AddWord(int ArrayListNumber, String WordEntered){
if(ArrayListNumber == 1){
Adjectives.add(WordEntered);
}
}
}
You are creating new object
Vocabulary vocab1 = new Vocabulary();
This would contain an empty Adjectives list.
So, you have to first populate the Adjectives list before you do the checking as follows
vocab1.Adjectives.contains(hero)
and use contains instead of equals.
I'm trying to create a text based game in Java that I will ask for a user's name and insert it into the game. I'm trying to evaluate with the string that they entered has any number. i.e 09452 or asdf1234.
Here is the code that is relative to the problem.
String name, choiceSelection;
int choice;
name = JOptionPane.showInputDialog(null, "Enter your name!");
//CHECKS IF USER ENTERED LETTERS ONLY
if (Pattern.matches("[0-9]+", name))
{
throw new NumberFormatException();
}
else if (Pattern.matches("[a-zA-Z]+", name))
{
if (Pattern.matches("[0-9]+", name))
{
throw new NumberFormatException();
}
}
I'm trying to figure out if any number is in the string and if so, throw a NumberFormatException so that they know they didn't follow the correct prompt.
What is the best way to make sure the user doesn't enter numbers in the name String?
You can use a simpler check:
if (!name.replaceAll("[0-9]", "").equals(name)) {
// name contains digits
}
The replaceAll call removes all digits from the name. The equals check would succeed only when there are no digits to remove.
Note that throwing NumberFormatException in this case would be misleading, because the exception has a very different meaning.
Consider using a JFormattedTextField or input verifier to prevent the user from entering numbers in the first place. It may not be worth losing the JOptionPane simplicity for a throwaway project, but it simplifies things for the end user.
Or, You simply don't let the User to put any numeric value in textField .For that you are needed to create Customized PlainDocument say NonNumericDocument and setting the Document of JTextField object as that customized NonNumericDocument
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.text.Document;
import javax.swing.text.PlainDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
class NonNumericDocument extends PlainDocument
{
#Override
public void insertString(int offs, String str, AttributeSet a) throws BadLocationException
{
if (str == null)
{
return;
}
char[] arr = str.toCharArray();
for (int i = 0; i < arr.length; i++)
{
if (Character.isDigit(arr[i]) || !Character.isLetter(arr[i]))//Checking for Numeric value or any special characters
{
return;
}
}
super.insertString(offs, new String(str), a);
}
}
//How to use NonNumericDocument
class TextFrame extends JFrame
{
JTextField tf ;
public void prepareAndShowGUI()
{
tf = new JTextField(30);
tf.setDocument(new NonNumericDocument());//Set Document here.
getContentPane().add(tf,BorderLayout.NORTH);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater ( new Runnable()
{
#Override
public void run()
{
TextFrame tFrame = new TextFrame();
tFrame.prepareAndShowGUI();
}
});
}
}
You can use this NonNumericDocument with any JTextField in your code without worrying about handling of non-numeric characters explicitly.
If you want to allow the user to only enter letters you can do this:
if (name.matches("\\p{Alpha}+")) {
// name contains only letters
}
Another way to check it is using parseInt( String s ) method:
private boolean isNumber( String s ) {
try {
Integer.parseInt( s );
} catch ( Exception e ) {
return false; // if it is not a number it throws an exception
}
return true;
}
I use a jFormattedTextField for a telephone number and only to accept the numeric values i
declare it as "new NumberFormatterFactory(Integer.class, false)" .
Now the problem is when the number starts with 0(zero) like 001345.. , after entered the value and moved to next column the entered value is trimmed as 1345.. here it not accepting the 0 as the starting number.
how can I enter the number starts with 0
Yeah, telephone numbers are slightly different from integers in that sense.
Following this example at you could solve it using regular expressions like this:
import java.text.ParseException;
import java.util.regex.*;
import javax.swing.*;
import javax.swing.text.DefaultFormatter;
class RegexFormatter extends DefaultFormatter {
private Pattern pattern;
private Matcher matcher;
public RegexFormatter() {
super();
}
public RegexFormatter(String pattern) throws PatternSyntaxException {
this();
setPattern(Pattern.compile(pattern));
}
public RegexFormatter(Pattern pattern) {
this();
setPattern(pattern);
}
public void setPattern(Pattern pattern) {
this.pattern = pattern;
}
public Pattern getPattern() {
return pattern;
}
protected void setMatcher(Matcher matcher) {
this.matcher = matcher;
}
protected Matcher getMatcher() {
return matcher;
}
public Object stringToValue(String text) throws ParseException {
Pattern pattern = getPattern();
if (pattern != null) {
Matcher matcher = pattern.matcher(text);
if (matcher.matches()) {
setMatcher(matcher);
return super.stringToValue(text);
}
throw new ParseException("Pattern did not match", 0);
}
return text;
}
}
public class Test {
public static void main(String[] a) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JFormattedTextField formattedField =
new JFormattedTextField(new RegexFormatter("\\d*"));
frame.add(formattedField, "North");
frame.add(new JTextField(), "South");
frame.setSize(300, 200);
frame.setVisible(true);
}
}
If you declare it as new NumberFormatterFactory(Integer.class, false) which is for only Integer, 0 wont be considered. You need to change it to other formatter and not store Phone Number as number (i.e. Integer).
Check this example for phone number formatting:
http://www.java2s.com/Code/Java/Swing-JFC/JFormattedTextFieldaninputmaskforatelephonenumber.htm
http://www.ibm.com/developerworks/java/library/j-mer0625/index.html