There is nothing wrong about being "too Java 6"!
First few notes about naming and naming convention:
username
is, in fact, the email, so should be email
getCensoredUsername
for the same reason should be getCensoredEmail
but in Java get
, by convention, is used only for getter or when you're applying very little logic; thus is better to use an "action-subject" type of name like censorEmail
- Being Java a type language, calling a variable
chars
is not helpful, that should be emailChars
or even just email
if that is not colliding with other variables, the compiler will tell you the type.
Biggest problem though, is the data validation, you should check at least that email is not null
.
Normally you would check that the email is "valid" but if you take a look at the email syntax you might notice that's almost impossible to do it without writing a formal parser (or using some library). So let's say we want just censor the "reasonably valid" emails.
Your approach to go char by char in the username part is not very flexible: what if tomorrow you want to have f****o.f*****i@gmail.com?
You need to add other flags, and wouldn't be very enjoyable.
I suggest you to start from the censor method and build bottom up from that.
Here is my solution
public static String hideLastChars(String name) {
Objects.requireNonNull(name, "String should not be null");
if (name.length() <= 1) {
return name;
} else {
return name.charAt(0) + IntStream.range(1, name.length()).mapToObj(i -> "*").collect(Collectors.joining());
}
}
Now that we have our "logic" we need to do a bit of input validation.
public static String censorEmail(String email) {
Objects.requireNonNull(email, "Email should not be null");
String[] splitEmail = email.split("@");
if (splitEmail.length > 1 && splitEmail[0].length() > 0) {
return censorUsername(splitEmail[0]) + "@" + splitEmail[1];
}
throw new IllegalArgumentException("Please provide a valid email");
}
Now we know that the email is not null, it does contain a '@' and user is there.
We just need to apply our hideLastChars
to each piece.
We have a little problem here because if you just split on each .
you will end up losing trailing dots, for example in an email like john.doe....@gmail.com
.
A solution is to use Lookahead regexp to split on each .
but keeping it in the split result, this is the regexp: ((?<=\.)|(?=\.))
, for example "john.doe...".split("((?<=\.)|(?=\.))")
will return { "john", ".", "doe", ".", ".", "." }
Now we know how to map each piece and how to extract it.
private static String censorUsername(String username) {
return Arrays
.stream(username.split("((?<=\\.)|(?=\\.))"))
.map(scratch_1::hideLastChars)
.collect(Collectors.joining(""));
}
I suggest you to create a unit test to test the logic so every time you find a new email that creates problem you can add in the test and see what happens (What about p.g@curry.com
?).
I've run few examples using your code and mine. Here's the result
INPUT |
ORIGINAL |
REVIEWED |
name.surname@gmail.com |
n***.s******@gmail.com |
n***.s******@gmail.com |
name.surname.p99@gmail.com |
n***.s******.p**@gmail.com |
n***.s******.p**@gmail.com |
name.surname.p99.@gmail.com |
n***.s******.p**.@gmail.com |
n***.s******.p**.@gmail.com |
@gmail.com |
@*****.c** |
Exception: Please provide a valid email |
user.com |
user.com |
Exception: Please provide a valid email |
user%example.com@example.org |
u***********.c**@example.org |
u***********.c**@example.org |
"john..doe..."@example.org |
"****..d**..."@example.org |
"****..d**..."@example.org |
" "@example.org |
"**@example.org |
"**@example.org |
null |
Exception: Cannot invoke "String.contains(java.lang.CharSequence)" because "username" is null |
Exception: Email should not be null |
...p...g...@gmail.com |
...p...g...@gmail.com |
...p...g...@gmail.com |
p.g@curryfication.com |
p.g@curryfication.com |
p.g@curryfication.com |