@Override
    public String mask(final Object plainValue) {
        String result = null == plainValue ? null : String.valueOf(plainValue);
        if (Strings.isNullOrEmpty(result)) {
            return result;
        }
        char[] chars = result.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            char c = chars[i];
            if ('A' <= c && c <= 'Z') {
                chars[i] = uppercaseLetterCodes.get(random.nextInt(uppercaseLetterCodes.size()));
            } else if ('a' <= c && c <= 'z') {
                chars[i] = lowercaseLetterCodes.get(random.nextInt(lowercaseLetterCodes.size()));
            } else if ('0' <= c && c <= '9') {
                chars[i] = digitalCodes.get(random.nextInt(digitalCodes.size()));
            } else {
                chars[i] = specialCodes.get(random.nextInt(specialCodes.size()));
            }
        }
        return new String(chars);
    }
@Test
    void assertMask() {
        GenericTableRandomReplaceAlgorithm maskAlgorithm = (GenericTableRandomReplaceAlgorithm) TypedSPILoader.getService(MaskAlgorithm.class, "GENERIC_TABLE_RANDOM_REPLACE",
                PropertiesBuilder.build(new Property("uppercase-letter-codes", "A,B,C,D"),
                        new Property("lowercase-letter-codes", "a,b,c,d"), new Property("digital-codes", "1,2,3,4"), new Property("special-codes", "~!@#")));
        assertThat(maskAlgorithm.mask(""), is(""));
        assertThat(maskAlgorithm.mask("Ab1!").charAt(0), anyOf(is('A'), is('B'), is('C'), is('D')));
        assertThat(maskAlgorithm.mask("Ab1!").charAt(1), anyOf(is('a'), is('b'), is('c'), is('d')));
        assertThat(maskAlgorithm.mask("Ab1!").charAt(2), anyOf(is('1'), is('2'), is('3'), is('4')));
        assertThat(maskAlgorithm.mask("Ab1!").charAt(3), anyOf(is('~'), is('!'), is('@'), is('#')));
    }