I have been writing code for my Computer Science Internal assessment, I am close to finishing it but keep finding this error, I am unsure how to solve it.
Here is my code for that section:
JButton createbutton = new JButton("Create");
createbutton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String[] characters = { "!", "#", "$", "%", "^", "&", "*" };
boolean passcontains = false;
for (int i = 0; i < characters.length; i ) {
if (new String(passwordfield.getPassword()).contains(new String(characters[i]))) {
passcontains = true;
}
}
boolean emcontains = false;
for (int i = 0; i < characters.length; i ) {
if (new String(emailfield.getText()).contains(new String(characters[i]))) {
emcontains = true;
}
}
loop: if (passwordfield.getPassword().length == 0
|| passwordfield.getPassword().length < 8
|| passcontains == false) {
message("INCORRECT PASSWORD\ncheck README");
} else {
if (emailfield.getText().length() == 0 || emcontains
|| emailfield.getText().contains("@") == false
|| emailfield.getText().contains(".") == false) {
message("INCORRECT EMAIL\ncheck README");
} else {
boolean pass = true;
for (int i = 0; i < database.size(); i ) {
if (database.get(i).getEmail() == emailfield.getText()) {
pass = false;
}
}
if (pass == true) {
database.add(
create(emailfield.getText(), new String(passwordfield.getPassword())));
write();
frame.setVisible(false);
message("SUCCESSFULLY MADE ACCOUNT");
frame.dispose();
} else {
message("AN ACCOUNT WITH THAT EMAIL ALREADY EXISTS\n GO TO LOGIN");
emailfield.setText("");
passwordfield.setText("");
break loop;
}
}
}
}
});
EDIT: I have resolved the issue myself, I had a function which went to 'create(input1, input2)' but due to my previous version of the code the checking algorithm was included in there, therefore it completed the checking twice and entered a while loop. Thank you for the response(s).
CodePudding user response:
Don't do that!
Follow Separation of Concerns and Single Responsibility Principle.
Don't put such logic to the code that is responsible for showing things to the user. Keep the business logic of your application away from GUI code. Create some classes and put the code with logic there. Then in your GUI you will call this code in a simple way and everything will be better. The fact this is a school project, does not mean that you deliver the code that "just works".
Check my approach:
public class Account {
private final Email email;
private final Password password;
public Account(Email email, Password password) {
Objects.requireNonNull(email, "An account cannot have null email");
Objects.requireNonNull(email, "An account cannot have null password");
this.email = email;
this.password = password;
}
public Email getEmail() {
return email;
}
public Password getPassword() {
return password;
}
public static class InvalidAccountInformationException extends RuntimeException {
public InvalidAccountInformationException(String message) {
super(message);
}
}
}
.
public class Email {
private final String value;
private Email(String value) {
this.value = value;
}
public String value() {
return value;
}
// Eclipse generated
@Override
public int hashCode() {
return Objects.hash(value);
}
// Eclipse generated
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Email other = (Email) obj;
return Objects.equals(value, other.value);
}
public static Email of(final String email) {
if (email == null)
throw new InvalidAccountInformationException("Email cannot be empty.");
String trimmedEmail = email.trim();
if (trimmedEmail.isEmpty())
throw new InvalidAccountInformationException("Email cannot be empty.");
if (!isValidEmailAddress(trimmedEmail))
throw new InvalidAccountInformationException("Email is not a valid email address.");
return new Email(trimmedEmail);
}
/*
* Shamelessly copy-pasta from https://stackoverflow.com/a/16058059/6579265
*/
private static boolean isValidEmailAddress(String email) {
String ePattern = "^[a-zA-Z0-9.!#$%&'* /=?^_`{|}~-] @((\\[[0-9]{1,3}\\.[0-9]{1,3}"
"\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9] \\.) [a-zA-Z]{2,}))$";
java.util.regex.Pattern p = java.util.regex.Pattern.compile(ePattern);
java.util.regex.Matcher m = p.matcher(email);
return m.matches();
}
}
.
public class Password {
private static final String[] MANDATORY_CHARACTERS = { "!", "#", "$", "%", "^", "&", "*" };
private final String value;
private Password(String value) {
this.value = value;
}
public String value() {
return value;
}
public static Password of(final String pass) {
if (pass == null || pass.trim().isEmpty())
throw new InvalidAccountInformationException("Password cannot be empty.");
if (!containsAtLeastOneMandatoryCharacter(pass))
throw new InvalidAccountInformationException("Password is too weak.");
return new Password(pass);
}
private static boolean containsAtLeastOneMandatoryCharacter(String pass) {
return Stream.of(MANDATORY_CHARACTERS).anyMatch(pass::contains);
}
// Eclipse generated
@Override
public int hashCode() {
return Objects.hash(value);
}
// Eclipse generated
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Password other = (Password) obj;
return Objects.equals(value, other.value);
}
}
.
public class AccountDatabase {
private final List<Account> accounts = new ArrayList<>();
public void save(Account account) {
Objects.requireNonNull(account, "Account should not be null");
if (emailIsAlreadyTaken(account))
throw new InvalidAccountInformationException("Email is already taken by another user.");
accounts.add(account);
}
private boolean emailIsAlreadyTaken(Account account) {
return accounts.stream().map(Account::getEmail).anyMatch(account::equals);
}
}
And then, in the GUI (ActionListener) the code looks like:
AccountDatabase database = ...
try {
Email email = Email.of(emailField.getText());
Password pass = Password.of(passwordField.getText());
Account account = new Account(email, pass);
database.save(account);
} catch (InvalidAccountInformationException ex) {
showToUser(ex.getMessage());
resetTextFieldsToEmptyTextSoUserCanFillThemAgain();
}
What did you achieve? To always be in a consistent state! Your application will never allow a non-valid password. You want to add or remove password validations, like length and etc? You edit the Password class. Not some weird, full of noise class. Your GUI does simple calls to this classes that contain the actual logic.
In addition, you achieve testability. How would you test in an automated-way your GUI code? This is a complex topic and for sure it is not the best way to test application logic through the UI. Extracting logic and rules to other classes, you can "just test them":
public class PasswordShould {
@Test
void not_be_valid_when_is_empty() {
assertThrows(RuntimeException.class, () -> Password.of(null));
assertThrows(RuntimeException.class, () -> Password.of(""));
assertThrows(RuntimeException.class, () -> Password.of(" "));
}
@Test
void not_be_valid_when_is_weak() {
assertThrows(RuntimeException.class, () -> Password.of("1234"));
assertThrows(RuntimeException.class, () -> Password.of("GGfdsfds12312"));
}
@Test
void be_valid_when_is_strong() {
assertDoesNotThrow(() -> Password.of("GGf##dsfds12312"));
}
}
My code might be a lot to digest, but I suggest to give it a try to understand it. First of all, take a look at the readability. You came here, because you can't debug your own code. Well...Neither can we.