Skip to content

Commit

Permalink
Merge pull request #155 from nulab/refactoring-Feedback
Browse files Browse the repository at this point in the history
refactor: Refactoring Feedback
  • Loading branch information
vvatanabe committed Aug 25, 2023
2 parents 2cc6230 + 6b040c4 commit ccc9cfb
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 105 deletions.
121 changes: 16 additions & 105 deletions src/main/java/com/nulabinc/zxcvbn/Feedback.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package com.nulabinc.zxcvbn;

import com.nulabinc.zxcvbn.guesses.DictionaryGuess;
import com.nulabinc.zxcvbn.matchers.Match;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
Expand Down Expand Up @@ -68,7 +66,7 @@ public class Feedback {
private final String warning;
private final String[] suggestions;

private Feedback(String warning, String... suggestions) {
Feedback(String warning, String... suggestions) {
this.warning = warning;
this.suggestions = suggestions;
}
Expand Down Expand Up @@ -101,10 +99,11 @@ public List<String> getSuggestions(Locale locale) {
protected ResourceBundle resolveResourceBundle(Locale locale) {
try {
return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale, CONTROL);
} catch (MissingResourceException e) {
} catch (MissingResourceException | UnsupportedOperationException e) {
// MissingResourceException:
// Fix for issue of Android refs: https://github.com/nulab/zxcvbn4j/issues/21
return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale);
} catch (UnsupportedOperationException e) {
//
// UnsupportedOperationException:
// Fix for issue of JDK 9 refs: https://github.com/nulab/zxcvbn4j/issues/45
// ResourceBundle.Control is not supported in named modules.
// See https://docs.oracle.com/javase/9/docs/api/java/util/ResourceBundle.html#bundleprovider
Expand All @@ -127,113 +126,26 @@ private String l10n(ResourceBundle messages, String messageId) {

static Feedback getFeedback(int score, List<Match> sequence) {
if (sequence.size() == 0) {
return getFeedbackWithoutWarnings(
return FeedbackFactory.getFeedbackWithoutWarnings(
DEFAULT_SUGGESTIONS_USE_FEW_WORDS, DEFAULT_SUGGESTIONS_NO_NEED_SYMBOLS);
}
if (score > 2) {
return getEmptyFeedback();
return FeedbackFactory.getEmptyFeedback();
}
Match longestMatch = sequence.get(0);
if (sequence.size() > 1) {
for (Match match : sequence.subList(1, sequence.size())) {
if (match.tokenLength() > longestMatch.tokenLength()) longestMatch = match;
}
}

return getMatchFeedback(longestMatch, sequence.size() == 1);
}

private static Feedback getFeedbackWithoutWarnings(String... suggestions) {
return new Feedback(null, suggestions);
}

private static Feedback getEmptyFeedback() {
return new Feedback(null);
}

private static Feedback getMatchFeedback(Match match, boolean isSoleMatch) {
switch (match.pattern) {
case Dictionary:
return getDictionaryMatchFeedback(match, isSoleMatch);
case Spatial:
return new Feedback(
match.turns == 1
? SPATIAL_WARNING_STRAIGHT_ROWS_OF_KEYS
: SPATIAL_WARNING_SHORT_KEYBOARD_PATTERNS,
EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD,
SPATIAL_SUGGESTIONS_USE_LONGER_KEYBOARD_PATTERN);
case Repeat:
return new Feedback(
match.baseToken.length() == 1 ? REPEAT_WARNING_LIKE_AAA : REPEAT_WARNING_LIKE_ABCABCABC,
EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD,
REPEAT_SUGGESTIONS_AVOID_REPEATED_WORDS);
case Sequence:
return new Feedback(
SEQUENCE_WARNING_LIKE_ABCOR6543,
EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD,
SEQUENCE_SUGGESTIONS_AVOID_SEQUENCES);
case Regex:
return new Feedback(
"recent_year".equals(match.regexName) ? REGEX_WARNING_RECENT_YEARS : null,
EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD,
REGEX_SUGGESTIONS_AVOID_RECENT_YEARS);
case Date:
return new Feedback(
DATE_WARNING_DATES, EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, DATE_SUGGESTIONS_AVOID_DATES);
default:
return getFeedbackWithoutWarnings(EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD);
}
}

private static Feedback getDictionaryMatchFeedback(Match match, boolean isSoleMatch) {
String warning = null;
if ("passwords".equals(match.dictionaryName)) {
if (isSoleMatch && !match.l33t && !match.reversed) {
if (match.rank <= 10) {
warning = DICTIONARY_WARNING_PASSWORDS_TOP10;
} else if (match.rank <= 100) {
warning = DICTIONARY_WARNING_PASSWORDS_TOP100;
} else {
warning = DICTIONARY_WARNING_PASSWORDS_VERY_COMMON;
if (match.tokenLength() > longestMatch.tokenLength()) {
longestMatch = match;
}
} else if (match.guessesLog10 <= 4) {
warning = DICTIONARY_WARNING_PASSWORDS_SIMILAR;
}
} else if ("english_wikipedia".equals(match.dictionaryName)) {
if (isSoleMatch) {
warning = DICTIONARY_WARNING_ENGLISH_WIKIPEDIA_ITSELF;
}
} else if (Arrays.asList(new String[] {"surnames", "male_names", "female_names"})
.contains(match.dictionaryName)) {
if (isSoleMatch) {
warning = DICTIONARY_WARNING_ETC_NAMES_THEMSELVES;
} else {
warning = DICTIONARY_WARNING_ETC_NAMES_COMMON;
}
}

List<String> suggestions = new ArrayList<>();
suggestions.add(EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD);

CharSequence word = match.token;
WipeableString lower = WipeableString.lowerCase(word);
if (DictionaryGuess.START_UPPER.matcher(word).find()) {
suggestions.add(DICTIONARY_SUGGESTIONS_CAPITALIZATION);
} else if (DictionaryGuess.ALL_UPPER.matcher(word).find() && !lower.equals(word)) {
suggestions.add(DICTIONARY_SUGGESTIONS_ALL_UPPERCASE);
}
if (match.reversed && match.tokenLength() >= 4) {
suggestions.add(DICTIONARY_SUGGESTIONS_REVERSED);
}
if (match.l33t) {
suggestions.add(DICTIONARY_SUGGESTIONS_L33T);
}
lower.wipe();
return new Feedback(warning, suggestions.toArray(new String[suggestions.size()]));
boolean isSoleMatch = sequence.size() == 1;
return FeedbackFactory.createMatchFeedback(longestMatch, isSoleMatch);
}

private static class ResourceBundleFeedback extends Feedback {
private ResourceBundle messages;
private final ResourceBundle messages;

private ResourceBundleFeedback(ResourceBundle messages, String warning, String... suggestions) {
super(warning, suggestions);
Expand All @@ -258,13 +170,12 @@ private ReplacedMessagesFeedback(
@Override
protected ResourceBundle resolveResourceBundle(Locale locale) {
try {
if (messages.containsKey(locale)) {
return messages.get(locale);
ResourceBundle resource = messages.get(locale);
if (resource != null) {
return resource;
}
return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale, CONTROL);
} catch (MissingResourceException e) {
return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale);
} catch (UnsupportedOperationException e) {
} catch (MissingResourceException | UnsupportedOperationException e) {
return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale);
}
}
Expand Down
157 changes: 157 additions & 0 deletions src/main/java/com/nulabinc/zxcvbn/FeedbackFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package com.nulabinc.zxcvbn;

import com.nulabinc.zxcvbn.guesses.DictionaryGuess;
import com.nulabinc.zxcvbn.matchers.Match;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

class FeedbackFactory {

private static final List<String> NAME_DICTIONARIES =
Arrays.asList("surnames", "male_names", "female_names");

static Feedback getFeedbackWithoutWarnings(String... suggestions) {
return new Feedback(null, suggestions);
}

static Feedback getEmptyFeedback() {
return new Feedback(null);
}

static Feedback createMatchFeedback(Match match, boolean isSoleMatch) {
switch (match.pattern) {
case Dictionary:
return createDictionaryMatchFeedback(match, isSoleMatch);
case Spatial:
return createSpatialMatchFeedback(match);
case Repeat:
return createRepeatMatchFeedback(match);
case Sequence:
return createSequenceMatchFeedback();
case Regex:
return createRegexMatchFeedback(match);
case Date:
return createDateMatchFeedback();
default:
return getFeedbackWithoutWarnings(Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD);
}
}

private static Feedback createSpatialMatchFeedback(Match match) {
String warning =
match.turns == 1
? Feedback.SPATIAL_WARNING_STRAIGHT_ROWS_OF_KEYS
: Feedback.SPATIAL_WARNING_SHORT_KEYBOARD_PATTERNS;
return new Feedback(
warning,
Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD,
Feedback.SPATIAL_SUGGESTIONS_USE_LONGER_KEYBOARD_PATTERN);
}

private static Feedback createRepeatMatchFeedback(Match match) {
String warning =
match.baseToken.length() == 1
? Feedback.REPEAT_WARNING_LIKE_AAA
: Feedback.REPEAT_WARNING_LIKE_ABCABCABC;
return new Feedback(
warning,
Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD,
Feedback.REPEAT_SUGGESTIONS_AVOID_REPEATED_WORDS);
}

private static Feedback createSequenceMatchFeedback() {
return new Feedback(
Feedback.SEQUENCE_WARNING_LIKE_ABCOR6543,
Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD,
Feedback.SEQUENCE_SUGGESTIONS_AVOID_SEQUENCES);
}

private static Feedback createRegexMatchFeedback(Match match) {
String warning =
"recent_year".equals(match.regexName) ? Feedback.REGEX_WARNING_RECENT_YEARS : null;
return new Feedback(
warning,
Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD,
Feedback.REGEX_SUGGESTIONS_AVOID_RECENT_YEARS);
}

private static Feedback createDateMatchFeedback() {
return new Feedback(
Feedback.DATE_WARNING_DATES,
Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD,
Feedback.DATE_SUGGESTIONS_AVOID_DATES);
}

private static Feedback createDictionaryMatchFeedback(Match match, boolean isSoleMatch) {
String warning = getWarningBasedOnMatch(match, isSoleMatch);
List<String> suggestions = generateSuggestions(match);
return new Feedback(warning, suggestions.toArray(new String[0]));
}

private static String getWarningBasedOnMatch(Match match, boolean isSoleMatch) {
if ("passwords".equals(match.dictionaryName)) {
return getPasswordWarning(match, isSoleMatch);
}

if ("english_wikipedia".equals(match.dictionaryName) && isSoleMatch) {
return Feedback.DICTIONARY_WARNING_ENGLISH_WIKIPEDIA_ITSELF;
}

if (NAME_DICTIONARIES.contains(match.dictionaryName)) {
return getNameDictionaryWarning(isSoleMatch);
}

return null;
}

private static String getPasswordWarning(Match match, boolean isSoleMatch) {
if (isSoleMatch && !match.l33t && !match.reversed) {
if (match.rank <= 10) {
return Feedback.DICTIONARY_WARNING_PASSWORDS_TOP10;
}
if (match.rank <= 100) {
return Feedback.DICTIONARY_WARNING_PASSWORDS_TOP100;
}
return Feedback.DICTIONARY_WARNING_PASSWORDS_VERY_COMMON;
}
if (match.guessesLog10 <= 4) {
return Feedback.DICTIONARY_WARNING_PASSWORDS_SIMILAR;
}
return null;
}

private static String getNameDictionaryWarning(boolean isSoleMatch) {
if (isSoleMatch) {
return Feedback.DICTIONARY_WARNING_ETC_NAMES_THEMSELVES;
}
return Feedback.DICTIONARY_WARNING_ETC_NAMES_COMMON;
}

private static List<String> generateSuggestions(Match match) {
List<String> suggestions = new ArrayList<>();
suggestions.add(Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD);

CharSequence word = match.token;
WipeableString lower = WipeableString.lowerCase(word);

if (DictionaryGuess.START_UPPER.matcher(word).find()) {
suggestions.add(Feedback.DICTIONARY_SUGGESTIONS_CAPITALIZATION);
}

if (DictionaryGuess.ALL_UPPER.matcher(word).find() && !lower.equals(word)) {
suggestions.add(Feedback.DICTIONARY_SUGGESTIONS_ALL_UPPERCASE);
}

if (match.reversed && match.tokenLength() >= 4) {
suggestions.add(Feedback.DICTIONARY_SUGGESTIONS_REVERSED);
}

if (match.l33t) {
suggestions.add(Feedback.DICTIONARY_SUGGESTIONS_L33T);
}

lower.wipe();
return suggestions;
}
}

0 comments on commit ccc9cfb

Please sign in to comment.