ResourceUsageAnalyzer { @VisibleForTesting static String convertFormatStringToRegexp(String formatString) { StringBuilder regexp = new StringBuilder(); int from = 0; boolean hasEscapedLetters = false; Matcher matcher = StringFormatDetector.FORMAT.matcher(formatString); int length = formatString.length(); while (matcher.find(from)) { int start = matcher.start(); int end = matcher.end(); if (start == 0 && end == length) { return NO_MATCH; } if (start > from) { hasEscapedLetters |= appendEscapedPattern(formatString, regexp, from, start); } String pattern = ".*"; String conversion = matcher.group(6); String timePrefix = matcher.group(5); if (timePrefix != null) { } else if (conversion != null && conversion.length() == 1) { char type = conversion.charAt(0); switch (type) { case 's': case 'S': case 't': case 'T': break; case '%': pattern = "%"; break; case 'n': pattern = "\n"; break; case 'c': case 'C': pattern = "."; break; case 'x': case 'X': pattern = "\\p{XDigit}+"; break; case 'd': case 'o': pattern = "\\p{Digit}+"; break; case 'b': pattern = "(true|false)"; break; case 'B': pattern = "(TRUE|FALSE)"; break; case 'h': case 'H': pattern = "(null|\\p{XDigit}+)"; break; case 'f': pattern = "-?[\\p{XDigit},.]+"; break; case 'e': pattern = "-?\\p{Digit}+[,.]\\p{Digit}+e\\+?\\p{Digit}+"; break; case 'E': pattern = "-?\\p{Digit}+[,.]\\p{Digit}+E\\+?\\p{Digit}+"; break; case 'a': pattern = "0x[\\p{XDigit},.+p]+"; break; case 'A': pattern = "0X[\\p{XDigit},.+P]+"; break; case 'g': case 'G': pattern = "-?[\\p{XDigit},.+eE]+"; break; } if (!".*".equals(pattern)) { String width = matcher.group(3); if (width != null) { String flags = matcher.group(2); if ("0".equals(flags)) { pattern = "0*" + pattern; } else { pattern = " " + pattern; } } } int regexLength = regexp.length(); if (!".*".equals(pattern) || regexLength < 2 || regexp.charAt(regexLength - 1) != '*' || regexp.charAt(regexLength - 2) != '.') { regexp.append(pattern); } } from = end; } if (from < length) { hasEscapedLetters |= appendEscapedPattern(formatString, regexp, from, length); } if (!hasEscapedLetters) { return NO_MATCH; } return regexp.toString(); } ResourceUsageAnalyzer(
            @NonNull File rDir,
            @NonNull File classes,
            @NonNull File manifest,
            @Nullable File mapping,
            @NonNull File resources,
            @Nullable File reportFile); void dispose(); void analyze(); boolean isDryRun(); void setDryRun(boolean dryRun); boolean isVerbose(); void setVerbose(boolean verbose); boolean isDebug(); void setDebug(boolean verbose); void rewriteResourceZip(@NonNull File source, @NonNull File dest); void removeUnused(@Nullable File destination); int getUnusedResourceCount(); static final boolean REPLACE_DELETED_WITH_EMPTY; @SuppressWarnings("SpellCheckingInspection") // arsc
    static final boolean TWO_PASS_AAPT; static final byte[] TINY_PNG; static final long TINY_PNG_CRC; static final byte[] TINY_9PNG; static final long TINY_9PNG_CRC; static final byte[] TINY_XML; static final long TINY_XML_CRC; }
@Test public void testFormatStringRegexp() { assertEquals(NO_MATCH, convertFormatStringToRegexp("")); assertEquals("\\Qfoo_\\E", convertFormatStringToRegexp("foo_")); assertEquals("\\Qfoo\\E.*\\Q_\\E.*\\Qend\\E", convertFormatStringToRegexp("foo%s_%1$send")); assertEquals("\\Qescape!.()\\E", convertFormatStringToRegexp("escape!.()")); assertEquals(NO_MATCH, convertFormatStringToRegexp("%c%c%c%d")); assertEquals(NO_MATCH, convertFormatStringToRegexp("%d%s")); assertEquals(NO_MATCH, convertFormatStringToRegexp("%s%s")); assertEquals(NO_MATCH, convertFormatStringToRegexp("%d_%d")); assertEquals(NO_MATCH, convertFormatStringToRegexp("%s%s%s%s")); assertEquals(NO_MATCH, convertFormatStringToRegexp("%s_%s_%s")); assertEquals(NO_MATCH, convertFormatStringToRegexp("%.0f%s")); assertEquals(".*\\Qabc\\E", convertFormatStringToRegexp("%sabc")); assertTrue("abc".matches(convertFormatStringToRegexp("%sabc"))); assertTrue("somethingabc".matches(convertFormatStringToRegexp("%sabc"))); assertEquals("\\Qa\\E\\p{Digit}+.*", convertFormatStringToRegexp("a%d%s")); assertTrue("a52hello".matches(convertFormatStringToRegexp("a%d%s"))); assertEquals("\\Qprefix\\E0*\\p{Digit}+\\Qsuffix\\E", convertFormatStringToRegexp("prefix%05dsuffix")); assertTrue("prefix012345suffix".matches(convertFormatStringToRegexp("prefix%05dsuffix"))); assertEquals("\\p{Digit}+\\Qk\\E", convertFormatStringToRegexp("%dk")); assertTrue("1234k".matches(convertFormatStringToRegexp("%dk"))); assertFalse("ic_shield_dark".matches(convertFormatStringToRegexp("%dk"))); assertTrue("foo_".matches(convertFormatStringToRegexp("foo_"))); assertTrue("fooA_BBend".matches(convertFormatStringToRegexp("foo%s_%1$send"))); assertFalse("A_BBend".matches(convertFormatStringToRegexp("foo%s_%1$send"))); String p = "prefix%s%%%n%c%x%d%o%b%h%f%e%a%g%C%X%5B%E%A%G"; String s = String.format(p, "", 'c', 1, 1, 1, true, 1, 1f, 1f, 1f, 1f, 'c', 1, 1, 1f, 1f, 1f); assertTrue(s.matches(convertFormatStringToRegexp(p))); }