diff --git a/app/src/main/java/com/hippo/yorozuya/StringUtils.java b/app/src/main/java/com/hippo/yorozuya/StringUtils.java index 2313d8e95..260c37199 100644 --- a/app/src/main/java/com/hippo/yorozuya/StringUtils.java +++ b/app/src/main/java/com/hippo/yorozuya/StringUtils.java @@ -18,6 +18,8 @@ package com.hippo.yorozuya; import android.text.TextUtils; +import org.jsoup.parser.Parser; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -30,25 +32,6 @@ public final class StringUtils { '\u00A0', // NO-BREAK SPACE '\u3000', // IDEOGRAPHIC SPACE }; - private static final String[] ESCAPE_CHARATER_LIST = { - "&", - "<", - ">", - """, - "'", - "×", - " " - }; - - private static final String[] UNESCAPE_CHARATER_LIST = { - "&", - "<", - ">", - "\"", - "'", - "×", - " " - }; private StringUtils() { } @@ -57,7 +40,7 @@ public final class StringUtils { * Unescape xml. It do not work perfectly. */ public static String unescapeXml(String str) { - return replaceEach(str, ESCAPE_CHARATER_LIST, UNESCAPE_CHARATER_LIST); + return Parser.unescapeEntities(str, true); } /** @@ -141,218 +124,6 @@ public final class StringUtils { return buf.toString(); } - /** - *

- * Replaces all occurrences of Strings within another String. - *

- * - *

- * A {@code null} reference passed to this method is a no-op, or if - * any "search string" or "string to replace" is null, that replace will be - * ignored. This will not repeat. For repeating replaces, call the - * overloaded method. - *

- * - *
-     *  StringUtils.replaceEach(null, *, *)        = null
-     *  StringUtils.replaceEach("", *, *)          = ""
-     *  StringUtils.replaceEach("aba", null, null) = "aba"
-     *  StringUtils.replaceEach("aba", new String[0], null) = "aba"
-     *  StringUtils.replaceEach("aba", null, new String[0]) = "aba"
-     *  StringUtils.replaceEach("aba", new String[]{"a"}, null)  = "aba"
-     *  StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""})  = "b"
-     *  StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"})  = "aba"
-     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"})  = "wcte"
-     *  (example of how it does not repeat)
-     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"})  = "dcte"
-     * 
- * - * @param text text to search and replace in, no-op if null - * @param searchList the Strings to search for, no-op if null - * @param replacementList the Strings to replace them with, no-op if null - * @return the text with any replacements processed, {@code null} if - * null String input - * @throws IllegalArgumentException if the lengths of the arrays are not the same (null is ok, - * and/or size 0) - */ - // Get from org.apache.commons.lang3.StringUtils - public static String replaceEach(final String text, final String[] searchList, - final String[] replacementList) { - return replaceEach(text, searchList, replacementList, false, 0); - } - - /** - *

- * Replaces all occurrences of Strings within another String. - *

- * - *

- * A {@code null} reference passed to this method is a no-op, or if - * any "search string" or "string to replace" is null, that replace will be - * ignored. - *

- * - *
-     *  StringUtils.replaceEach(null, *, *, *) = null
-     *  StringUtils.replaceEach("", *, *, *) = ""
-     *  StringUtils.replaceEach("aba", null, null, *) = "aba"
-     *  StringUtils.replaceEach("aba", new String[0], null, *) = "aba"
-     *  StringUtils.replaceEach("aba", null, new String[0], *) = "aba"
-     *  StringUtils.replaceEach("aba", new String[]{"a"}, null, *) = "aba"
-     *  StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}, *) = "b"
-     *  StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}, *) = "aba"
-     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}, *) = "wcte"
-     *  (example of how it repeats)
-     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, false) = "dcte"
-     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, true) = "tcte"
-     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, *) = IllegalStateException
-     * 
- * - * @param text text to search and replace in, no-op if null - * @param searchList the Strings to search for, no-op if null - * @param replacementList the Strings to replace them with, no-op if null - * @param repeat if true, then replace repeatedly - * until there are no more possible replacements or timeToLive < 0 - * @param timeToLive if less than 0 then there is a circular reference and endless - * loop - * @return the text with any replacements processed, {@code null} if - * null String input - * @throws IllegalStateException if the search is repeating and there is an endless loop due - * to outputs of one being inputs to another - * @throws IllegalArgumentException if the lengths of the arrays are not the same (null is ok, - * and/or size 0) - */ - // Get from org.apache.commons.lang3.StringUtils - private static String replaceEach( - final String text, final String[] searchList, - final String[] replacementList, final boolean repeat, - final int timeToLive) { - - // mchyzer Performance note: This creates very few new objects (one major goal) - // let me know if there are performance requests, we can create a harness to measure - - if (text == null || TextUtils.isEmpty(text) || searchList == null || - searchList.length == 0 || replacementList == null || replacementList.length == 0) { - return text; - } - - // if recursing, this shouldn't be less than 0 - if (timeToLive < 0) { - throw new IllegalStateException("Aborting to protect against StackOverflowError - " + - "output of one loop is the input of another"); - } - - final int searchLength = searchList.length; - final int replacementLength = replacementList.length; - - // make sure lengths are ok, these need to be equal - if (searchLength != replacementLength) { - throw new IllegalArgumentException("Search and Replace array lengths don't match: " - + searchLength - + " vs " - + replacementLength); - } - - // keep track of which still have matches - final boolean[] noMoreMatchesForReplIndex = new boolean[searchLength]; - - // index on index that the match was found - int textIndex = -1; - int replaceIndex = -1; - int tempIndex = -1; - - // index of replace array that will replace the search string found - // NOTE: logic duplicated below START - for (int i = 0; i < searchLength; i++) { - if (noMoreMatchesForReplIndex[i] || searchList[i] == null || - TextUtils.isEmpty(searchList[i]) || replacementList[i] == null) { - continue; - } - tempIndex = text.indexOf(searchList[i]); - - // see if we need to keep searching for this - if (tempIndex == -1) { - noMoreMatchesForReplIndex[i] = true; - } else { - if (textIndex == -1 || tempIndex < textIndex) { - textIndex = tempIndex; - replaceIndex = i; - } - } - } - // NOTE: logic mostly below END - - // no search strings found, we are done - if (textIndex == -1) { - return text; - } - - int start = 0; - - // get a good guess on the size of the result buffer so it doesn't have to double if it goes over a bit - int increase = 0; - - // count the replacement text elements that are larger than their corresponding text being replaced - for (int i = 0; i < searchList.length; i++) { - if (searchList[i] == null || replacementList[i] == null) { - continue; - } - final int greater = replacementList[i].length() - searchList[i].length(); - if (greater > 0) { - increase += 3 * greater; // assume 3 matches - } - } - // have upper-bound at 20% increase, then let Java take over - increase = Math.min(increase, text.length() / 5); - - final StringBuilder buf = new StringBuilder(text.length() + increase); - - while (textIndex != -1) { - - for (int i = start; i < textIndex; i++) { - buf.append(text.charAt(i)); - } - buf.append(replacementList[replaceIndex]); - - start = textIndex + searchList[replaceIndex].length(); - - textIndex = -1; - replaceIndex = -1; - tempIndex = -1; - // find the next earliest match - // NOTE: logic mostly duplicated above START - for (int i = 0; i < searchLength; i++) { - if (noMoreMatchesForReplIndex[i] || searchList[i] == null || - TextUtils.isEmpty(searchList[i]) || replacementList[i] == null) { - continue; - } - tempIndex = text.indexOf(searchList[i], start); - - // see if we need to keep searching for this - if (tempIndex == -1) { - noMoreMatchesForReplIndex[i] = true; - } else { - if (textIndex == -1 || tempIndex < textIndex) { - textIndex = tempIndex; - replaceIndex = i; - } - } - } - // NOTE: logic duplicated above END - - } - final int textLength = text.length(); - for (int i = start; i < textLength; i++) { - buf.append(text.charAt(i)); - } - final String result = buf.toString(); - if (!repeat) { - return result; - } - - return replaceEach(result, searchList, replacementList, repeat, timeToLive - 1); - } - public static boolean endsWith(String string, String[] suffixs) { for (int i = 0, n = suffixs.length; i < n; i++) { if (string.endsWith(suffixs[i])) { @@ -464,38 +235,6 @@ public final class StringUtils { return null == str ? 0 : str.length(); } - /** - *

Counts how many times the char appears in the given string.

- * - *

A {@code null} or empty ("") String input returns {@code 0}.

- * - *
-     * StringUtils.countMatches(null, *)       = 0
-     * StringUtils.countMatches("", *)         = 0
-     * StringUtils.countMatches("abba", 0)  = 0
-     * StringUtils.countMatches("abba", 'a')   = 2
-     * StringUtils.countMatches("abba", 'b')  = 2
-     * StringUtils.countMatches("abba", 'x') = 0
-     * 
- * - * @param str the CharSequence to check, may be null - * @param ch the char to count - * @return the number of occurrences, 0 if the CharSequence is {@code null} - */ - public static int countMatches(final CharSequence str, final char ch) { - if (TextUtils.isEmpty(str)) { - return 0; - } - int count = 0; - // We could also call str.toCharArray() for faster look ups but that would generate more garbage. - for (int i = 0; i < str.length(); i++) { - if (ch == str.charAt(i)) { - count++; - } - } - return count; - } - /** * All null or empty, or all not */ diff --git a/app/src/main/java/com/hippo/yorozuya/StringUtils.kt b/app/src/main/java/com/hippo/yorozuya/StringUtils.kt index dd1138e1f..37c3f4f46 100644 --- a/app/src/main/java/com/hippo/yorozuya/StringUtils.kt +++ b/app/src/main/java/com/hippo/yorozuya/StringUtils.kt @@ -1,5 +1,7 @@ package com.hippo.yorozuya +import org.jsoup.parser.Parser + fun String.unescapeXml(): String { - return StringUtils.unescapeXml(this) + return Parser.unescapeEntities(this, true) }