@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 assertMaskWithEmptyProps() {
        GenericTableRandomReplaceAlgorithm maskAlgorithm = (GenericTableRandomReplaceAlgorithm) TypedSPILoader.getService(MaskAlgorithm.class, "GENERIC_TABLE_RANDOM_REPLACE", new Properties());
        assertThat(maskAlgorithm.mask("Ab1!").substring(0, 1), anyOf(Arrays.stream("ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("")).map(CoreMatchers::is).collect(Collectors.toList())));
        assertThat(maskAlgorithm.mask("Ab1!").substring(1, 2), anyOf(Arrays.stream("abcdefghijklmnopqrstuvwxyz".split("")).map(CoreMatchers::is).collect(Collectors.toList())));
        assertThat(maskAlgorithm.mask("Ab1!").substring(2, 3), anyOf(Arrays.stream("0123456789".split("")).map(CoreMatchers::is).collect(Collectors.toList())));
        assertThat(maskAlgorithm.mask("Ab1!").substring(3, 4), anyOf(Arrays.stream("~!@#$%^&*:<>|".split("")).map(CoreMatchers::is).collect(Collectors.toList())));
    }