6
\$\begingroup\$

Hide email characters:

  1. When email only has one letter then just cover that letter with the * symbol e.g. *@gmail.com

  2. When email has two letters then cover second letter with the symbol * e.g. t*@gmail.com

  3. When email has three letters then cover second and third with the symbol * e.g. t**gmail.com

  4. When email has four or more letters then show first three letters and add 4 stars at the ends - e.g.

    tim****@gmail.com
    tom****@gmail.com
    

My code works but I feel like its way too much hard-coding and can be done much cleaner

public static String hideSomeOfEmailValue(String privateEmail) {
StringBuilder stringBuilder = new StringBuilder(privateEmail);
int atSignIndex = privateEmail.indexOf("@");
String emailName = privateEmail.substring(0, atSignIndex);

if (emailName.length() == 1) {
    return stringBuilder.replace(0, emailName.length(), "*").toString();
}
if (emailName.length() > 1 && emailName.length() < 4) {
    return stringBuilder.replace(1, atSignIndex, StringUtils.repeat("*", emailName.length() - 1)).toString();
} else {
    stringBuilder = new StringBuilder(privateEmail.substring(0, 2));
    String emailProvider = privateEmail.substring(atSignIndex);
    stringBuilder.append("****").append(emailProvider);
    return stringBuilder.toString();
}
  1. locate at what index at sign located
  2. trim everything until at sign which gives us name of the email only so we can easily.
\$\endgroup\$
5
\$\begingroup\$

Bugs:

  1. privateEmail.substring(0, 2) needs to change to privateEmail.substring(0, 3) in the else, since end index is exclusive and we want to include 3 characters, not 2.

  2. Method assumes that privateEmail contains a "@", and does not check if atSignIndex results in -1, resulting in a StringIndexOutOfBoundsException if there is no "@". Return privateEmail (with no hiding applied) early if you expect this method be handed any generic string which could potentially be an email, or throw an IllegalArgumentException if privateEmail is always expected to be an email. I consider the latter to be better design. Note that both these situations handle empty strings as well.

Improvements:

  1. Use var for type inference where the type is obvious from the right side, like new StringBuilder(...), or String#substring. This is as of Java 10.

  2. 1 < emailName.length() is redundant because length of 0 and 1 are handled by earlier cases.

  3. StringUtils.repeat can be replaced with String#repeat as of Java 11. Rids of need for external dependency here.

  4. Instantiating a new StringBuilder in the else can be replaced with mutating the current one, namely with the delete and insert instance methods.

  5. Move stringBuilder declaration lower, to only when it is needed.

  6. Replace "*" with a static final variable, such that we could change it to "-" or "_" down the road and remain consistent across all the ifs and the else. This, despite being a character, will have to be a String to remain type-compatible with String#repeat and StringBuilder#replace. Final code:

private static final String HIDE_CHAR = "*";

public static String asHiddenEmail(String privateEmail) {
    int atSignIndex = privateEmail.indexOf("@");

    if (atSignIndex == -1) {
        throw new IllegalArgumentException("`privateEmail` must be an email.");
    }

    var stringBuilder = new StringBuilder(privateEmail);
    var emailName = privateEmail.substring(0, atSignIndex);

    if (emailName.length() == 1) {
        return stringBuilder.replace(0, emailName.length(), HIDE_CHAR).toString();
    }
    if (emailName.length() < 4) {
        var hiddenNameEnd = HIDE_CHAR.repeat(emailName.length() - 1);
        return stringBuilder.replace(1, atSignIndex, hiddenNameEnd).toString();
    }
    else {
        stringBuilder.delete(3, atSignIndex);
        stringBuilder.insert(3, HIDE_CHAR.repeat(4));
        return stringBuilder.toString();
    }
}
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thanks man, regarding the check if it is even an email not necessary UI will not lend send it to back its not properly formatted. Sadly i cannot use Java 11 hence i cannot use repeat function that String have in Java 11 \$\endgroup\$ – Artjom Sep 5 at 19:41
  • \$\begingroup\$ @Artjom you're welcome. I still recommend keeping the throw new IllegalArgumentException(...) and the check because even if the UI will not let the email be improperly typed, it is good to keep this sanity check. What if down the road you have the email entered some other way, but forgot to check it there? \$\endgroup\$ – Mario Ishac Sep 5 at 20:33
  • \$\begingroup\$ @MarioIshac I find it a bit confusing that ``HIDE_CHAR` is not of type char, as the name suggests. \$\endgroup\$ – Dorian Gray Sep 7 at 11:20
  • \$\begingroup\$ @DorianGray Yeah that is confusing, I'll clarify it in answer. But it comes down to char / Character not being compatible with StringBuilder#replace and String#repeat, there are no character equivalents for those. We would have to insert String.valueOf everywhere. \$\endgroup\$ – Mario Ishac Sep 7 at 11:31
0
\$\begingroup\$

I have refactored the code a bit, using a switch statement, commenting the code (important!) and simplifying the single cases. Maybe a name like obfuscateEMail would be more understandable.

The "Shouldn't happen" case could be replaced by throwing an Exception.

public class EMail {
    public static void main(String[] args) {
        System.out.println(hideSomeOfEmailValue("a@gmail.com")); //$NON-NLS-1$
        System.out.println(hideSomeOfEmailValue("ab@gmail.com")); //$NON-NLS-1$
        System.out.println(hideSomeOfEmailValue("abc@gmail.com")); //$NON-NLS-1$
        System.out.println(hideSomeOfEmailValue("abcd@gmail.com")); //$NON-NLS-1$
        System.out.println(hideSomeOfEmailValue("abcde@gmail.com")); //$NON-NLS-1$
        System.out.println(hideSomeOfEmailValue("abcdefghijklmnopqrst@gmail.com")); //$NON-NLS-1$
    }
    
    /**
     * 
     * @param privateEmail
     * @return
     */
    public static String hideSomeOfEmailValue(String privateEmail) {
        final int atSignIndex = privateEmail.indexOf('@');
        if (atSignIndex <= 0) {
            // Not an E-Mail address.
            // Shouldn't happen. Return the input.
            return privateEmail;
        }
        
        final StringBuilder obfuscatedEmail = new StringBuilder(privateEmail);
        switch (atSignIndex) {
        case 1:
            // When email only has one letter then just cover that letter with the * symbol e.g. *@gmail.com
            obfuscatedEmail.setCharAt(0, '*');
            return obfuscatedEmail.toString();

        case 2:
            // When email has two letters then cover second letter with the * symbol e.g. t*@gmail.com
            obfuscatedEmail.setCharAt(1, '*');
            return obfuscatedEmail.toString();
            
        case 3:
            // When email has three letters then cover second and third with the symbol * e.g. t**gmail.com
            return obfuscatedEmail.replace(1, 3, "**").toString(); //$NON-NLS-1$
            
        default:
            // When email has four or more letters then show first three letters and add 4 stars at the ends - e.g. tim****@gmail.com
            return obfuscatedEmail.replace(3, atSignIndex, "****").toString(); //$NON-NLS-1$
        }
    }
}

Output of the main methid is as follows (this shall be a JUnit test):

*@gmail.com
a*@gmail.com
a**@gmail.com
abc****@gmail.com
abc****@gmail.com
abc****@gmail.com
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Good job also buddy \$\endgroup\$ – Artjom Sep 7 at 12:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.