ValueXmlHelper { @SuppressWarnings("UnnecessaryContinue") @Nullable public static String unescapeResourceString( @Nullable String s, boolean escapeEntities, boolean trim) { if (s == null) { return null; } int i = 0; int n = s.length(); if (trim) { while (i < n) { char c = s.charAt(i); if (!Character.isWhitespace(c)) { break; } i++; } while (n > i) { char c = s.charAt(n - 1); if (!Character.isWhitespace(c)) { if (n < s.length() && isEscaped(s, n)) { n++; } break; } n--; } } boolean rewriteWhitespace = false; boolean prevSpace = false; boolean hasEscape = false; boolean hasQuotes = false; for (int curr = i; curr < n; curr++) { char c = s.charAt(curr); if (c == '\\' || c == '&') { hasEscape = true; } if (c == '"') { hasQuotes = true; } boolean isSpace = Character.isWhitespace(c); if (c == '\n' || (isSpace && prevSpace)) { rewriteWhitespace = true; } prevSpace = isSpace; } if (!trim) { rewriteWhitespace = false; hasQuotes = false; } if (!rewriteWhitespace && !hasEscape && !hasQuotes && i == 0 && n == s.length()) { return s; } boolean quoted = false; StringBuilder sb = new StringBuilder(n - i); prevSpace = false; for (; i < n; i++) { char c = s.charAt(i); while (c == '"' && trim) { quoted = !quoted; i++; if (i == n) { break; } c = s.charAt(i); } if (i == n) { break; } if (c == '\\' && i < n - 1) { prevSpace = false; char next = s.charAt(i + 1); if (next == 'u' && i < n - 5) { String hex = s.substring(i + 2, i + 6); try { int unicodeValue = Integer.parseInt(hex, 16); sb.append((char) unicodeValue); i += 5; continue; } catch (NumberFormatException e) { sb.append(c); } } else if (next == 'n') { sb.append('\n'); i++; continue; } else if (next == 't') { sb.append('\t'); i++; continue; } else { sb.append(next); i++; continue; } } else { if (c == '&' && escapeEntities) { prevSpace = false; if (s.regionMatches(true, i, LT_ENTITY, 0, LT_ENTITY.length())) { sb.append('<'); i += LT_ENTITY.length() - 1; continue; } else if (s.regionMatches(true, i, AMP_ENTITY, 0, AMP_ENTITY.length())) { sb.append('&'); i += AMP_ENTITY.length() - 1; continue; } else if (s.regionMatches(true, i, QUOT_ENTITY, 0, QUOT_ENTITY.length())) { sb.append('"'); i += QUOT_ENTITY.length() - 1; continue; } else if (s.regionMatches(true, i, APOS_ENTITY, 0, APOS_ENTITY.length())) { sb.append('\''); i += APOS_ENTITY.length() - 1; continue; } else if (s.regionMatches(true, i, GT_ENTITY, 0, GT_ENTITY.length())) { sb.append('>'); i += GT_ENTITY.length() - 1; continue; } else if (i < n - 2 && s.charAt(i + 1) == '#') { int end = s.indexOf(';', i + 1); if (end != -1) { char first = s.charAt(i + 2); boolean hex = first == 'x' || first == 'X'; String number = s.substring(i + (hex ? 3 : 2), end); try { int unicodeValue = Integer.parseInt(number, hex ? 16 : 10); sb.append((char) unicodeValue); i = end; continue; } catch (NumberFormatException e) { sb.append(c); } } else { sb.append(c); } } } if (trim && !quoted) { boolean isSpace = Character.isWhitespace(c); if (isSpace) { if (!prevSpace) { sb.append(' '); } } else { sb.append(c); } prevSpace = isSpace; } else { sb.append(c); } } } s = sb.toString(); return s; }  @SuppressWarnings("UnnecessaryContinue") @Nullable static String unescapeResourceString(
            @Nullable String s,
            boolean escapeEntities, boolean trim); @NonNull static String escapeResourceString(@NonNull String string); @NonNull static String escapeResourceString(@NonNull String string,
            boolean escapeMarkupDelimiters); @NonNull static String escapeResourceStringAsXml(@NonNull String xml);  }
@Test public void trim() { assertEquals("", unescapeResourceString("", false, true)); assertEquals("", unescapeResourceString(" \n ", false, true)); assertEquals("test", unescapeResourceString(" test ", false, true)); assertEquals(" test ", unescapeResourceString("\" test \"", false, true)); assertEquals("test", unescapeResourceString("\n\t test \t\n ", false, true)); assertEquals("test\n", unescapeResourceString(" test\\n ", false, true)); assertEquals(" test\n ", unescapeResourceString("\" test\\n \"", false, true)); assertEquals("te\\st", unescapeResourceString("\n\t te\\\\st \t\n ", false, true)); assertEquals("te\\st", unescapeResourceString(" te\\\\st ", false, true)); assertEquals("test", unescapeResourceString("\"\"\"test\"\" ", false, true)); assertEquals("t t", unescapeResourceString(" \"\"t t\" ", false, true)); assertEquals("t t", unescapeResourceString(" \"\"\"t t\" ", false, true)); assertEquals("\"test\"", unescapeResourceString("\"\"\\\"test\\\"\" ", false, true)); assertEquals("test ", unescapeResourceString("test\\ ", false, true)); assertEquals("\\\\\\", unescapeResourceString("\\\\\\\\\\\\ ", false, true)); assertEquals("\\\\\\ ", unescapeResourceString("\\\\\\\\\\\\\\ ", false, true)); }
@Test public void noTrim() { assertEquals("", unescapeResourceString("", false, false)); assertEquals(" \n ", unescapeResourceString(" \n ", false, false)); assertEquals(" test ", unescapeResourceString(" test ", false, false)); assertEquals("\" test \"", unescapeResourceString("\" test \"", false, false)); assertEquals("\n\t test \t\n ", unescapeResourceString("\n\t test \t\n ", false, false)); assertEquals(" test\n ", unescapeResourceString(" test\\n ", false, false)); assertEquals("\" test\n \"", unescapeResourceString("\" test\\n \"", false, false)); assertEquals("\n\t te\\st \t\n ", unescapeResourceString("\n\t te\\\\st \t\n ", false, false)); assertEquals(" te\\st ", unescapeResourceString(" te\\\\st ", false, false)); assertEquals("\"\"\"test\"\" ", unescapeResourceString("\"\"\"test\"\" ", false, false)); assertEquals("\"\"\"test\"\" ", unescapeResourceString("\"\"\\\"test\\\"\" ", false, false)); assertEquals("test ", unescapeResourceString("test\\ ", false, false)); assertEquals("\\\\\\ ", unescapeResourceString("\\\\\\\\\\\\ ", false, false)); assertEquals("\\\\\\ ", unescapeResourceString("\\\\\\\\\\\\\\ ", false, false)); }
@Test public void unescapeStringShouldUnescapeXmlSpecialCharacters() { assertEquals("&lt;", unescapeResourceString("&lt;", false, true)); assertEquals("&gt;", unescapeResourceString("&gt;", false, true)); assertEquals("<", unescapeResourceString("&lt;", true, true)); assertEquals("<", unescapeResourceString(" &lt; ", true, true)); assertEquals("\"", unescapeResourceString(" &quot; ", true, true)); assertEquals("'", unescapeResourceString(" &apos; ", true, true)); assertEquals(">", unescapeResourceString(" &gt; ", true, true)); assertEquals("&amp;", unescapeResourceString("&amp;", false, true)); assertEquals("&", unescapeResourceString("&amp;", true, true)); assertEquals("&", unescapeResourceString(" &amp; ", true, true)); assertEquals("!<", unescapeResourceString("!&lt;", true, true)); }
@Test public void unescapeStringShouldUnescapeQuotes() { assertEquals("'", unescapeResourceString("\\'", false, true)); assertEquals("\"", unescapeResourceString("\\\"", false, true)); assertEquals(" ' ", unescapeResourceString("\" ' \"", false, true)); }
@Test public void unescapeStringShouldPreserveWhitespace() { assertEquals("at end ", unescapeResourceString("\"at end \"", false, true)); assertEquals(" at begin", unescapeResourceString("\" at begin\"", false, true)); }
@Test public void unescapeStringShouldUnescapeAtSignAndQuestionMarkOnlyAtBeginning() { assertEquals("@text", unescapeResourceString("\\@text", false, true)); assertEquals("a@text", unescapeResourceString("a@text", false, true)); assertEquals("?text", unescapeResourceString("\\?text", false, true)); assertEquals("a?text", unescapeResourceString("a?text", false, true)); assertEquals(" ?text", unescapeResourceString("\" ?text\"", false, true)); }
@Test public void unescapeStringShouldUnescapeJavaUnescapeSequences() { assertEquals("\n", unescapeResourceString("\\n", false, true)); assertEquals("\t", unescapeResourceString("\\t", false, true)); assertEquals("\\", unescapeResourceString("\\\\", false, true)); }
@Test public void rewriteSpaces() { assertEquals("This is a test", unescapeResourceString("This is\na test", true, true)); assertEquals("This is a test", unescapeResourceString("This is\n a test\n ", true, true)); assertEquals("This is\na test", unescapeResourceString("\"This is\na test\"", true, true)); assertEquals("Multiple words", unescapeResourceString("Multiple words", true, true)); assertEquals("Multiple words", unescapeResourceString("\"Multiple words\"", true, true)); assertEquals("This is a\n test", unescapeResourceString("This is a\\n test", true, true)); assertEquals("This is a\n test", unescapeResourceString("This is\n a\\n test", true, true)); }
@Test public void htmlEntities() { assertEquals("Entity \u00a9 \u00a9 Copyright", unescapeResourceString("Entity &#169; &#xA9; Copyright", true, true)); }
@Test public void markupConcatenation() { assertEquals("<b>Sign in</b> or register", unescapeResourceString("\n <b>Sign in</b>\n or register\n", true, true)); }