{"id":1936,"date":"2025-11-24T10:00:07","date_gmt":"2025-11-24T15:00:07","guid":{"rendered":"http:\/\/www.mymiller.name\/wordpress\/?p=1936"},"modified":"2025-11-24T10:00:07","modified_gmt":"2025-11-24T15:00:07","slug":"advancedstring-java-lang-string-steroids","status":"publish","type":"post","link":"https:\/\/www.mymiller.name\/wordpress\/java\/advancedstring-java-lang-string-steroids\/","title":{"rendered":"AdvancedString &#8211; java.lang.String on steroids!"},"content":{"rendered":"\n<p>Every need an additional method on the String Class? &nbsp;Well I have and it would have made life much easier. &nbsp;Unfortunately you can&#8217;t subclass String as it is Final. &nbsp;So what are you to do? &nbsp;Well you wrap the String class. &nbsp;I have created AdvancedString, which contains additional functionality and does a complete wrap. &nbsp;You can easily subclass it if you wish. &nbsp;I have added these additional abilities to AdvancedString:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>containsHTML() &#8211; returns true if the string contains HTML<\/li><li>paragraphFormat() &#8211; formats the string to a width of X and enters line feeds.<\/li><li>removeHTML()<\/li><\/ul>\n\n\n\n<p>I didn&#8217;t need to add many new methods, and I certainly could have created these as utility classes or methods. &nbsp;However I wanted to be able to have a single class and one that I could expand when I wanted to in the future. &nbsp;So I created AdvancedString, while it wraps the String class, all returns are converter from String \/ String[] to AdvancedString \/ AdvancedString[]. &nbsp;Once you start using this, it&#8217;s just like using String.<\/p>\n\n\n\n<p>Here is my example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package name.mymiller.extensions.lang;\n\nimport java.io.IOException;\nimport java.io.Reader;\nimport java.io.Serializable;\nimport java.io.StringReader;\nimport java.io.UnsupportedEncodingException;\nimport java.nio.charset.Charset;\nimport java.text.ParseException;\nimport java.util.ArrayList;\nimport java.util.Locale;\nimport java.util.Scanner;\nimport java.util.logging.Level;\nimport java.util.regex.Pattern;\nimport java.util.stream.IntStream;\n\nimport javax.swing.text.html.HTMLEditorKit;\nimport javax.swing.text.html.parser.ParserDelegator;\n\nimport name.mymiller.extensions.log.LogManager;\n\n\/**\n * @author jmiller\n *\n *\/\npublic class AdvancedString extends HTMLEditorKit.ParserCallback implements Serializable {\n\t\/**\n\t * Serial Version UID\n\t *\/\n\tprivate static final long serialVersionUID = -6540815077831838076L;\n\n\t\/**\n\t * Regex contain possible tags for Starting\n\t *\/\n\tprivate final static String HTML_TAG_START = \"\\\\&lt;\\\\w+((\\\\s+\\\\w+(\\\\s*\\\\=\\\\s*(?:\\\".*?\\\"|'.*?'|&#91;^'\\\"\\\\>\\\\s]+))?)+\\\\s*|\\\\s*)\\\\>\";\n\n\t\/**\n\t * Regex containing possible tags for ending\n\t *\/\n\tprivate final static String HTML_TAG_END = \"\\\\&lt;\/\\\\w+\\\\>\";\n\n\t\/**\n\t * Regex containing possible tags for Self Closing\n\t *\/\n\tprivate final static String HTML_TAG_SELF_CLOSING = \"\\\\&lt;\\\\w+((\\\\s+\\\\w+(\\\\s*\\\\=\\\\s*(?:\\\".*?\\\"|'.*?'|&#91;^'\\\"\\\\>\\\\s]+))?)+\\\\s*|\\\\s*)\/\\\\>\";\n\n\t\/**\n\t * Regext to match HTML Entity\n\t *\/\n\tprivate final static String HTML_ENTITY = \"&amp;&#91;a-zA-Z]&#91;a-zA-Z0-9]+;\";\n\n\t\/**\n\t * Pattern including all of the tags.\n\t *\/\n\tprivate final static Pattern htmlPattern = Pattern\n\t\t\t.compile(\n\t\t\t\t\t\"(\" + AdvancedString.HTML_TAG_START + \".*\" + AdvancedString.HTML_TAG_END + \")|(\"\n\t\t\t\t\t\t\t+ AdvancedString.HTML_TAG_SELF_CLOSING + \")|(\" + AdvancedString.HTML_ENTITY + \")\",\n\t\t\t\t\tPattern.DOTALL);\n\n\t\/**\n\t * Creates a new AdvancedString\n\t *\n\t * @param string\n\t *            Value to set as String\n\t * @return reference to this AdvancedString\n\t *\/\n\tpublic static AdvancedString create(final String string) {\n\t\treturn new AdvancedString(string);\n\t}\n\n\t\/**\n\t * Convert an array of strings to AdvancedStrings\n\t *\n\t * @param strings\n\t *            Strings to convert\n\t * @return Array of AdvancedStrings\n\t *\/\n\tpublic static AdvancedString&#91;] create(final String&#91;] strings) {\n\t\tfinal AdvancedString&#91;] array = new AdvancedString&#91;strings.length];\n\n\t\tfor (int i = 0; i &lt; strings.length; i++) {\n\t\t\tarray&#91;i] = AdvancedString.create(strings&#91;i]);\n\t\t}\n\n\t\treturn array;\n\t}\n\n\t\/**\n\t * Returns a formatted string using the specified format string and arguments.\n\t *\n\t * @param format\n\t *            A format Strings\n\t * @param args\n\t *            Arguments referenced by the format specifiers in the format\n\t *            string. If there are more arguments than format specifiers, the\n\t *            extra arguments are ignored. The number of arguments is variable\n\t *            and may be zero. The maximum number of arguments is limited by the\n\t *            maximum dimension of a Java array. The behaviour on a null\n\t *            argument depends on the conversion.\n\t * @return A formatted AdvancedString\n\t *\/\n\tpublic static AdvancedString format(final String format, Object... args) {\n\t\treturn new AdvancedString(String.format(format, args));\n\t}\n\n\t\/**\n\t * Internal reference to the String\n\t *\/\n\tprivate String string = null;\n\n\t\/**\n\t * @param string\n\t *            String to base the Advanced String on.\n\t *\/\n\tpublic AdvancedString(final String string) {\n\t\tsuper();\n\t\tthis.string = string;\n\t}\n\n\t\/**\n\t * Formatable string to append to existing string\n\t *\n\t * @param format\n\t *            Format of string to append\n\t * @return reference to this AdvancedString\n\t *\/\n\tpublic AdvancedString append(String format) {\n\t\tthis.string = this.string + format;\n\n\t\treturn this;\n\t}\n\n\t\/**\n\t * Formatable string to append to existing string\n\t *\n\t * @param format\n\t *            Format of string to append\n\t * @param args\n\t *            Arguments for format specification\n\t * @return reference to this AdvancedString\n\t *\/\n\tpublic AdvancedString append(String format, Object args) {\n\t\tfinal String formattedString = String.format(format, args);\n\t\tthis.string = this.string + formattedString;\n\t\treturn this;\n\t}\n\n\t\/**\n\t * Assigns a new String value to this object\n\t *\n\t * @param string\n\t *            New value to set as String\n\t * @return reference to this AdvancedString\n\t *\/\n\tpublic AdvancedString assign(final String string) {\n\t\tthis.string = string;\n\n\t\treturn this;\n\t}\n\n\t\/**\n\t * @param index\n\t *            the index of the char value.\n\t * @return the char value at the specified index of this string. The first char\n\t *         value is at index 0.\n\t * @see String#charAt(int)\n\t *\/\n\tpublic char charAt(final int index) {\n\t\treturn this.string.charAt(index);\n\t}\n\n\t\/**\n\t * @return an IntStream of char values from this sequence\n\t * @see CharSequence#chars()\n\t *\/\n\tpublic IntStream chars() {\n\t\treturn this.string.chars();\n\t}\n\n\t\/**\n\t * @param index\n\t *            the index of the char value.\n\t * @return the code point value of the character at the index\n\t * @see String#codePointAt(int)\n\t *\/\n\tpublic int codePointAt(final int index) {\n\t\treturn this.string.codePointAt(index);\n\t}\n\n\t\/**\n\t * @param index\n\t *            the index of the char value.\n\t * @return the Unicode code point value before the given index.\n\t * @see String#codePointBefore(int)\n\t *\/\n\tpublic int codePointBefore(final int index) {\n\t\treturn this.string.codePointBefore(index);\n\t}\n\n\t\/**\n\t * @param beginIndex\n\t *            the index to the first char of the text range.\n\t * @param endIndex\n\t *            the index after the last char of the text range.\n\t * @return the number of Unicode code points in the specified text range\n\t * @see String#codePointCount(int, int)\n\t *\/\n\tpublic int codePointCount(final int beginIndex, final int endIndex) {\n\t\treturn this.string.codePointCount(beginIndex, endIndex);\n\t}\n\n\t\/**\n\t * @return an IntStream of Unicode code points from this sequence\n\t * @see CharSequence#codePoints()\n\t *\/\n\tpublic IntStream codePoints() {\n\t\treturn this.string.codePoints();\n\t}\n\n\t\/**\n\t * @param anotherString\n\t *            the String to be compared.\n\t * @return the value 0 if the argument string is equal to this string; a value\n\t *         less than 0 if this string is lexicographically less than the string\n\t *         argument; and a value greater than 0 if this string is\n\t *         lexicographically greater than the string argument.\n\t * @see String#compareTo(String)\n\t *\/\n\tpublic int compareTo(final String anotherString) {\n\t\treturn this.string.compareTo(anotherString);\n\t}\n\n\t\/**\n\t * @param str\n\t *            the String to be compared.\n\t * @return a negative integer, zero, or a positive integer as the specified\n\t *         String is greater than, equal to, or less than this String, ignoring\n\t *         case considerations.\n\t *\n\t * @see String#compareToIgnoreCase(String)\n\t *\/\n\tpublic int compareToIgnoreCase(final String str) {\n\t\treturn this.string.compareToIgnoreCase(str);\n\t}\n\n\t\/**\n\t * @param str\n\t *            the String that is concatenated to the end of this String.\n\t * @return the String that is concatenated to the end of this String.\n\t * @see String#concat(String)\n\t *\/\n\tpublic AdvancedString concat(final String str) {\n\t\treturn AdvancedString.create(this.string.concat(str));\n\t}\n\n\t\/**\n\t * @param str\n\t *            he sequence to search for\n\t * @return true if this string contains s, false otherwise\n\t * @see String#contains(CharSequence)\n\t *\/\n\tpublic boolean contains(final CharSequence str) {\n\t\treturn this.string.contains(str);\n\t}\n\n\t\/**\n\t * Will return true if s contains HTML markup tags or entities.\n\t *\n\t * @return true if string contains HTML\n\t *\/\n\tpublic boolean containsHtml() {\n\t\tboolean ret = false;\n\t\tif (this.string != null) {\n\t\t\tret = AdvancedString.htmlPattern.matcher(this.string).find();\n\t\t}\n\t\treturn ret;\n\t}\n\n\t\/**\n\t * @param cs\n\t *            The sequence to compare this String against\n\t * @return true if this String represents the same sequence of char values as\n\t *         the specified sequence, false otherwise\n\t * @see String#contentEquals(CharSequence)\n\t *\/\n\tpublic boolean contentEquals(final CharSequence cs) {\n\t\treturn this.string.contentEquals(cs);\n\t}\n\n\t\/**\n\t * @param sb\n\t *            The StringBuffer to compare this String against\n\t * @return true if this String represents the same sequence of characters as the\n\t *         specified StringBuffer, false otherwise\n\t * @see String#contentEquals(StringBuffer)\n\t *\/\n\tpublic boolean contentEquals(final StringBuffer sb) {\n\t\treturn this.string.contentEquals(sb);\n\t}\n\n\t\/**\n\t * @param suffix\n\t *            the suffix.\n\t * @return true if the character sequence represented by the argument is a\n\t *         suffix of the character sequence represented by this object; false\n\t *         otherwise. Note that the result will be true if the argument is the\n\t *         empty string or is equal to this String object as determined by the\n\t *         equals(Object) method.\n\t * @see String#endsWith(String)\n\t *\/\n\tpublic boolean endsWith(final String suffix) {\n\t\treturn this.string.endsWith(suffix);\n\t}\n\n\t\/**\n\t * Compares this string to the specified object. The result is true if and only\n\t * if the argument is not null and is a String object that represents the same\n\t * sequence of characters as this object.\n\t *\n\t * @param anObject\n\t *            The object to compare this String against\n\t * @return true if the given object represents a String equivalent to this\n\t *         string, false otherwise\n\t * @see String#equals(Object)\n\t *\/\n\t@Override\n\tpublic boolean equals(final Object anObject) {\n\t\treturn this.string.equals(anObject);\n\t}\n\n\t\/**\n\t * Compares this String to another String, ignoring case considerations. Two\n\t * strings are considered equal ignoring case if they are of the same length and\n\t * corresponding characters in the two strings are equal ignoring case. Two\n\t * characters c1 and c2 are considered the same ignoring case if at least one of\n\t * the following is true:\n\t *\n\t * The two characters are the same (as compared by the == operator) Applying the\n\t * method Character.toUpperCase(char) to each character produces the same result\n\t * Applying the method Character.toLowerCase(char) to each character produces\n\t * the same result\n\t *\n\t * @param anotherString\n\t *            The String to compare this String against\n\t * @return true if the argument is not null and it represents an equivalent\n\t *         String ignoring case; false otherwise\n\t * @see String#equalsIgnoreCase(String)\n\t *\/\n\tpublic boolean equalsIgnoreCase(final String anotherString) {\n\t\treturn this.string.equalsIgnoreCase(anotherString);\n\t}\n\n\t\/**\n\t * @return The resultant byte array\n\t * @see String#getBytes()\n\t *\/\n\tpublic byte&#91;] getBytes() {\n\t\treturn this.string.getBytes();\n\t}\n\n\t\/**\n\t * Encodes this String into a sequence of bytes using the given charset, storing\n\t * the result into a new byte array. This method always replaces malformed-input\n\t * and unmappable-character sequences with this charset's default replacement\n\t * byte array. The CharsetEncoder class should be used when more control over\n\t * the encoding process is required.\n\t *\n\t * @param charset\n\t *            The Charset to be used to encode the String\n\t * @return The resultant byte array\n\t * @see String#getBytes(Charset)\n\t *\/\n\tpublic byte&#91;] getBytes(final Charset charset) {\n\t\treturn this.string.getBytes(charset);\n\t}\n\n\t\/**\n\t * @param charsetName\n\t *            The name of a supported charset\n\t * @return The resultant byte array\n\t * @throws UnsupportedEncodingException\n\t *             If the named charset is not supported\n\t * @see String#getBytes(String)\n\t *\/\n\tpublic byte&#91;] getBytes(final String charsetName) throws UnsupportedEncodingException {\n\t\treturn this.string.getBytes(charsetName);\n\t}\n\n\t\/**\n\t * @param srcBegin\n\t *            index of the first character in the string to copy.\n\t * @param srcEnd\n\t *            index after the last character in the string to copy.\n\t * @param dst\n\t *            the destination array.\n\t * @param dstBegin\n\t *            the start offset in the destination array.\n\t * @see String#getChars(int, int, char&#91;], int)\n\t *\/\n\tpublic void getChars(final int srcBegin, final int srcEnd, final char&#91;] dst, final int dstBegin) {\n\t\tthis.string.getChars(srcBegin, srcEnd, dst, dstBegin);\n\t}\n\n\t\/**\n\t * Split the string into paragraphs based on blank lines between paragraphs.\n\t *\n\t * @return Array of AdvancedString containing each paragraph.\n\t *\/\n\tpublic AdvancedString&#91;] getParagraphs() {\n\t\tfinal ArrayList&lt;AdvancedString> list = new ArrayList&lt;>();\n\n\t\ttry (Scanner scanner = new Scanner(this.string)) {\n\t\t\tscanner.useDelimiter(\"(?m:^$)\");\n\t\t\twhile (scanner.hasNext()) {\n\t\t\t\tlist.add(new AdvancedString(scanner.next()));\n\t\t\t}\n\t\t}\n\n\t\treturn list.toArray(new AdvancedString&#91;list.size()]);\n\t}\n\n\t\/**\n\t * Determines the number of words this string contains, based on whitespace.\n\t *\n\t * @return int for the number of words in this string.\n\t *\/\n\tpublic int getWordCount() {\n\t\tint wordCount = 0;\n\t\ttry (Scanner scanner = new Scanner(this.string)) {\n\t\t\twhile (scanner.hasNext()) {\n\t\t\t\tscanner.next();\n\t\t\t\twordCount++;\n\t\t\t}\n\t\t}\n\t\treturn wordCount;\n\t}\n\n\t\/**\n\t * Method used by the Remove Text to process calls for removing HTML.\n\t *\/\n\t@Override\n\tpublic void handleText(final char&#91;] text, final int pos) {\n\t\tfinal StringBuffer buffer = new StringBuffer(this.string);\n\t\tbuffer.append(text);\n\t\tthis.string = buffer.toString();\n\t}\n\n\t\/**\n\t * @param ch\n\t *            a character (Unicode code point).\n\t * @return the index of the first occurrence of the character in the character\n\t *         sequence represented by this object, or -1 if the character does not\n\t *         occur.\n\t * @see String#indexOf(int)\n\t *\/\n\tpublic int indexOf(final int ch) {\n\t\treturn this.string.indexOf(ch);\n\t}\n\n\t\/**\n\t * @param ch\n\t *            a character (Unicode code point).\n\t * @param fromIndex\n\t *            the index to start the search from.\n\t * @return the index of the first occurrence of the character in the character\n\t *         sequence represented by this object that is greater than or equal to\n\t *         fromIndex, or -1 if the character does not occur.\n\t * @see String#indexOf(int, int)\n\t *\/\n\tpublic int indexOf(final int ch, final int fromIndex) {\n\t\treturn this.string.indexOf(ch, fromIndex);\n\t}\n\n\t\/**\n\t * @param str\n\t *            the substring to search for.\n\t * @return the index of the first occurrence of the specified substring, or -1\n\t *         if there is no such occurrence.\n\t * @see String#indexOf(String)\n\t *\/\n\tpublic int indexOf(final String str) {\n\t\treturn this.string.indexOf(str);\n\t}\n\n\t\/**\n\t * @param str\n\t *            the substring to search for.\n\t * @param fromIndex\n\t *            the index from which to start the search.\n\t * @return the index of the first occurrence of the specified substring,\n\t *         starting at the specified index, or -1 if there is no such\n\t *         occurrence.\n\t * @see String#indexOf(String, int)\n\t *\/\n\tpublic int indexOf(final String str, final int fromIndex) {\n\t\treturn this.string.indexOf(str, fromIndex);\n\t}\n\n\t\/**\n\t * @return a string that has the same contents as this string, but is guaranteed\n\t *         to be from a pool of unique strings.\n\t * @see String#intern()\n\t *\/\n\tpublic AdvancedString intern() {\n\t\treturn AdvancedString.create(this.string.intern());\n\t}\n\n\t\/**\n\t * @return true if length() is 0, otherwise false\n\t * @see String#isEmpty()\n\t *\/\n\tpublic boolean isEmpty() {\n\t\treturn this.string.isEmpty();\n\t}\n\n\t\/**\n\t * @param ch\n\t *            a character (Unicode code point).\n\t * @return the index of the last occurrence of the character in the character\n\t *         sequence represented by this object, or -1 if the character does not\n\t *         occur.\n\t * @see String#lastIndexOf(int)\n\t *\/\n\tpublic int lastIndexOf(final int ch) {\n\t\treturn this.string.lastIndexOf(ch);\n\t}\n\n\t\/**\n\t * @param ch\n\t *            a character (Unicode code point).\n\t * @param fromIndex\n\t *            the index to start the search from. There is no restriction on the\n\t *            value of fromIndex. If it is greater than or equal to the length\n\t *            of this string, it has the same effect as if it were equal to one\n\t *            less than the length of this string: this entire string may be\n\t *            searched. If it is negative, it has the same effect as if it were\n\t *            -1: -1 is returned.\n\t * @return the index of the last occurrence of the character in the character\n\t *         sequence represented by this object that is less than or equal to\n\t *         fromIndex, or -1 if the character does not occur before that point.\n\t * @see String#lastIndexOf(int, int)\n\t *\/\n\tpublic int lastIndexOf(final int ch, final int fromIndex) {\n\t\treturn this.string.lastIndexOf(ch, fromIndex);\n\t}\n\n\t\/**\n\t * @param str\n\t *            the substring to search for.\n\t * @return the index of the last occurrence of the specified substring, or -1 if\n\t *         there is no such occurrence.\n\t * @see String#lastIndexOf(String)\n\t *\/\n\tpublic int lastIndexOf(final String str) {\n\t\treturn this.string.lastIndexOf(str);\n\t}\n\n\t\/**\n\t * @param str\n\t *            the substring to search for.\n\t * @param fromIndex\n\t *            the index to start the search from.\n\t * @return the index of the last occurrence of the specified substring,\n\t *         searching backward from the specified index, or -1 if there is no\n\t *         such occurrence.\n\t * @see String#lastIndexOf(String, int)\n\t *\/\n\tpublic int lastIndexOf(final String str, final int fromIndex) {\n\t\treturn this.string.lastIndexOf(str, fromIndex);\n\t}\n\n\t\/**\n\t * @return the length of the sequence of characters represented by this object.\n\t * @see String#length()\n\t *\/\n\tpublic int length() {\n\t\treturn this.string.length();\n\t}\n\n\t\/**\n\t * @param regex\n\t *            the regular expression to which this string is to be matched\n\t * @return true if, and only if, this string matches the given regular\n\t *         expression\n\t * @see String#matches(String)\n\t *\/\n\tpublic boolean matches(final String regex) {\n\t\treturn this.string.matches(regex);\n\t}\n\n\t\/**\n\t * @param index\n\t *            the index to be offset\n\t * @param codePointOffset\n\t *            the offset in code points\n\t * @return the index within this String\n\t * @see String#offsetByCodePoints(int, int)\n\t *\/\n\tpublic int offsetByCodePoints(final int index, final int codePointOffset) {\n\t\treturn this.string.offsetByCodePoints(index, codePointOffset);\n\t}\n\n\t\/**\n\t * Method to format strings into paragraphs.\n\t *\n\t * @param maxWidth\n\t *            Maximum width to make paragraph.\n\t * @return Formatted String with \\n\n\t *\/\n\tpublic AdvancedString paragraphFormat(final int maxWidth) {\n\t\tString formatted = new String();\n\n\t\tif (this.string.length() &lt; maxWidth) {\n\t\t\tformatted = this.string;\n\t\t} else {\n\t\t\tfinal String work = this.string.trim();\n\t\t\tint charCount = 0;\n\n\t\t\tfor (final char c : work.toCharArray()) {\n\t\t\t\tif (c != '\\n') {\n\t\t\t\t\tif (charCount &lt; maxWidth) {\n\t\t\t\t\t\tformatted += c;\n\t\t\t\t\t\tcharCount++;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tformatted += '\\n';\n\t\t\t\t\t\tformatted += c;\n\t\t\t\t\t\tcharCount = 0;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tformatted += c;\n\t\t\t\t\tcharCount = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn AdvancedString.create(formatted);\n\t}\n\n\t\/**\n\t * @param ignoreCase\n\t *            if true, ignore case when comparing characters.\n\t * @param toffset\n\t *            the starting offset of the subregion in this string.\n\t * @param other\n\t *            the string argument.\n\t * @param ooffset\n\t *            the starting offset of the subregion in the string argument.\n\t * @param len\n\t *            the number of characters to compare.\n\t * @return true if the specified subregion of this string matches the specified\n\t *         subregion of the string argument; false otherwise. Whether the\n\t *         matching is exact or case insensitive depends on the ignoreCase\n\t *         argument. startsWith\n\t * @see String#regionMatches(boolean, int, String, int, int)\n\t *\/\n\tpublic boolean regionMatches(final boolean ignoreCase, final int toffset, final String other, final int ooffset,\n\t\t\tfinal int len) {\n\t\treturn this.string.regionMatches(ignoreCase, toffset, other, ooffset, len);\n\t}\n\n\t\/**\n\t * @param toffset\n\t *            the starting offset of the subregion in this string.\n\t * @param other\n\t *            the string argument.\n\t * @param ooffset\n\t *            the starting offset of the subregion in the string argument.\n\t * @param len\n\t *            the number of characters to compare.\n\t * @return true if the specified subregion of this string exactly matches the\n\t *         specified subregion of the string argument; false otherwise.\n\t * @see String#regionMatches(int, String, int, int)\n\t *\/\n\tpublic boolean regionMatches(final int toffset, final String other, final int ooffset, final int len) {\n\t\treturn this.string.regionMatches(toffset, other, ooffset, len);\n\t}\n\n\t\/**\n\t * Remove any HTML from this string.\n\t *\/\n\tpublic void removeHTML() {\n\t\tif (this.containsHtml()) {\n\t\t\tfinal String original = this.string;\n\t\t\tfinal Reader in = new StringReader(original);\n\n\t\t\ttry {\n\t\t\t\tfinal ParserDelegator delegator = new ParserDelegator();\n\t\t\t\tdelegator.parse(in, this, Boolean.TRUE);\n\t\t\t\tin.close();\n\t\t\t} catch (final IOException e) {\n\t\t\t\tLogManager.getLogger(this.getClass()).log(Level.SEVERE, \"Failed to remove HTML from: \" + original, e);\n\t\t\t}\n\t\t}\n\t}\n\n\t\/**\n\t * @param oldChar\n\t *            the old character.\n\t * @param newChar\n\t *            the new character.\n\t * @return a string derived from this string by replacing every occurrence of\n\t *         oldChar with newChar.\n\t * @see String#replace(char, char)\n\t *\/\n\tpublic AdvancedString replace(final char oldChar, final char newChar) {\n\t\treturn AdvancedString.create(this.string.replace(oldChar, newChar));\n\t}\n\n\t\/**\n\t * @param target\n\t *            The sequence of char values to be replaced\n\t * @param replacement\n\t *            The replacement sequence of char values\n\t * @return The resulting string\n\t * @see String#replace(CharSequence, CharSequence)\n\t *\/\n\tpublic AdvancedString replace(final CharSequence target, final CharSequence replacement) {\n\t\treturn AdvancedString.create(this.string.replace(target, replacement));\n\t}\n\n\t\/**\n\t * @param regex\n\t *            the regular expression to which this string is to be matched\n\t * @param replacement\n\t *            the string to be substituted for each match\n\t * @return The resulting String\n\t * @see String#replaceAll(String, String)\n\t *\/\n\tpublic AdvancedString replaceAll(final String regex, final String replacement) {\n\t\treturn AdvancedString.create(this.string.replaceAll(regex, replacement));\n\t}\n\n\t\/**\n\t * @param regex\n\t *            the regular expression to which this string is to be matched\n\t * @param replacement\n\t *            the string to be substituted for each match\n\t * @return The resulting String\n\t * @see String#replaceFirst(String, String)\n\t *\/\n\tpublic AdvancedString replaceFirst(final String regex, final String replacement) {\n\t\treturn AdvancedString.create(this.string.replaceFirst(regex, replacement));\n\t}\n\n\t\/**\n\t * @param regex\n\t *            the delimiting regular expression\n\t * @return the array of strings computed by splitting this string around matches\n\t *         of the given regular expression\n\t * @see String#split(String)\n\t *\/\n\tpublic AdvancedString&#91;] split(final String regex) {\n\t\treturn AdvancedString.create(this.string.split(regex));\n\t}\n\n\t\/**\n\t * Splits this string around matches of the given regular expression. The array\n\t * returned by this method contains each substring of this string that is\n\t * terminated by another substring that matches the given expression or is\n\t * terminated by the end of the string. The substrings in the array are in the\n\t * order in which they occur in this string. If the expression does not match\n\t * any part of the input then the resulting array has just one element, namely\n\t * this string.\n\t *\n\t * When there is a positive-width match at the beginning of this string then an\n\t * empty leading substring is included at the beginning of the resulting array.\n\t * A zero-width match at the beginning however never produces such empty leading\n\t * substring.\n\t *\n\t * The limit parameter controls the number of times the pattern is applied and\n\t * therefore affects the length of the resulting array. If the limit n is\n\t * greater than zero then the pattern will be applied at most n - 1 times, the\n\t * array's length will be no greater than n, and the array's last entry will\n\t * contain all input beyond the last matched delimiter. If n is non-positive\n\t * then the pattern will be applied as many times as possible and the array can\n\t * have any length. If n is zero then the pattern will be applied as many times\n\t * as possible, the array can have any length, and trailing empty strings will\n\t * be discarded.\n\t *\n\t *\n\t * @param regex\n\t *            the delimiting regular expression\n\t * @param limit\n\t *            the result threshold, as described above\n\t * @return the array of strings computed by splitting this string around matches\n\t *         of the given regular expression\n\t * @see String#split(String, int)\n\t *\/\n\tpublic AdvancedString&#91;] split(final String regex, final int limit) {\n\t\treturn AdvancedString.create(this.string.split(regex, limit));\n\t}\n\n\t\/**\n\t * @param prefix\n\t *            the prefix.\n\t * @return true if the character sequence represented by the argument is a\n\t *         prefix of the character sequence represented by this string; false\n\t *         otherwise. Note also that true will be returned if the argument is an\n\t *         empty string or is equal to this String object as determined by the\n\t *         equals(Object) method.\n\t * @see String#startsWith(String)\n\t *\/\n\tpublic boolean startsWith(final String prefix) {\n\t\treturn this.string.startsWith(prefix);\n\t}\n\n\t\/**\n\t * @param prefix\n\t *            the prefix.\n\t * @param toffset\n\t *            where to begin looking in this string.\n\t * @return true if the character sequence represented by the argument is a\n\t *         prefix of the substring of this object starting at index toffset;\n\t *         false otherwise. The result is false if toffset is negative or\n\t *         greater than the length of this String object; otherwise the result\n\t *         is the same as the result of the expression\n\t *         this.substring(toffset).startsWith(prefix)\n\t *\n\t * @see String#startsWith(String, int)\n\t *\/\n\tpublic boolean startsWith(final String prefix, final int toffset) {\n\t\treturn this.string.startsWith(prefix, toffset);\n\t}\n\n\t\/**\n\t * @param beginIndex\n\t *            the begin index, inclusive.\n\t * @param endIndex\n\t *            the end index, exclusive.\n\t * @return the specified subsequence.\n\t * @see String#subSequence(int, int)\n\t *\/\n\tpublic CharSequence subSequence(final int beginIndex, final int endIndex) {\n\t\treturn this.string.subSequence(beginIndex, endIndex);\n\t}\n\n\t\/**\n\t * @param beginIndex\n\t *            the beginning index, inclusive.\n\t * @return the specified substring.\n\t * @see String#substring(int)\n\t *\/\n\tpublic AdvancedString substring(final int beginIndex) {\n\t\treturn new AdvancedString(this.string.substring(beginIndex));\n\t}\n\n\t\/**\n\t * @param beginIndex\n\t *            the beginning index, inclusive.\n\t * @param endIndex\n\t *            the ending index, exclusive.\n\t * @return the specified substring.\n\t * @see String#substring(int, int)\n\t *\/\n\tpublic AdvancedString substring(final int beginIndex, final int endIndex) {\n\t\treturn new AdvancedString(this.string.substring(beginIndex, endIndex));\n\t}\n\n\t\/**\n\t * Convert a string with Date\/Time stamp to an AdvancedCalendar object.\n\t *\n\t * @param pattern\n\t *            String containing the Pattern to parse the dateTime\n\t * @return AdvancedCalendar specifying the date.\n\t * @throws ParseException\n\t *             Error parsing the string by the described pattern.\n\t *\/\n\tpublic AdvancedCalendar toAdvancedCalendar(String pattern) throws ParseException {\n\t\treturn AdvancedCalendar.parse(pattern, this.string);\n\t}\n\n\t\/**\n\t * @return a newly allocated character array whose length is the length of this\n\t *         string and whose contents are initialized to contain the character\n\t *         sequence represented by this string.\n\t * @see String#toCharArray()\n\t *\/\n\tpublic char&#91;] toCharArray() {\n\t\treturn this.string.toCharArray();\n\t}\n\n\t\/**\n\t * @return the String, converted to lowercase.\n\t * @see String#toLowerCase()\n\t *\/\n\tpublic AdvancedString toLowerCase() {\n\t\treturn new AdvancedString(this.string.toLowerCase());\n\t}\n\n\t\/**\n\t * @param locale\n\t *            use the case transformation rules for this locale\n\t * @return the String, converted to lowercase.\n\t * @see String#toLowerCase(Locale)\n\t *\/\n\tpublic AdvancedString toLowerCase(final Locale locale) {\n\t\treturn new AdvancedString(this.string.toLowerCase(locale));\n\t}\n\n\t\/**\n\t * @return the string\n\t * @see String#toString()\n\t *\/\n\t@Override\n\tpublic String toString() {\n\t\treturn this.string;\n\t}\n\n\t\/**\n\t * @return the String, converted to uppercase.\n\t * @see String#toUpperCase()\n\t *\/\n\tpublic AdvancedString toUpperCase() {\n\t\treturn new AdvancedString(this.string.toUpperCase());\n\t}\n\n\t\/**\n\t * @param locale\n\t *            use the case transformation rules for this locale\n\t * @return the String, converted to uppercase.\n\t * @see String#toUpperCase(Locale)\n\t *\/\n\tpublic AdvancedString toUpperCase(final Locale locale) {\n\t\treturn new AdvancedString(this.string.toUpperCase(locale));\n\t}\n\n\t\/**\n\t * @return A string whose value is this string, with any leading and trailing\n\t *         white space removed, or this string if it has no leading or trailing\n\t *         white space.\n\t * @see String#trim()\n\t *\/\n\tpublic AdvancedString trim() {\n\t\treturn new AdvancedString(this.string.trim());\n\t}\n}\n<\/code><\/pre>\n\n\n\n<p>If you come up with any cool enhancements please share them back here!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Every need an additional method on the String Class? &nbsp;Well I have and it would have made life much easier. &nbsp;Unfortunately you can&#8217;t subclass String as it is Final. &nbsp;So what are you to do? &nbsp;Well you wrap the String class. &nbsp;I have created AdvancedString, which contains additional functionality and does a complete wrap. &nbsp;You [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1937,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_coblocks_attr":"","_coblocks_dimensions":"","_coblocks_responsive_height":"","_coblocks_accordion_ie_support":"","jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":true,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[280],"tags":[69],"series":[248],"class_list":["post-1936","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-java","tag-java-2","series-advanced-classes"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2016\/09\/rope-1379561_640.jpg?fit=640%2C425&ssl=1","jetpack-related-posts":[{"id":3848,"url":"https:\/\/www.mymiller.name\/wordpress\/java\/level-up-your-testing-structuring-unit-tests-with-subclasses\/","url_meta":{"origin":1936,"position":0},"title":"Level Up Your Testing: Structuring Unit Tests with Subclasses","author":"Jeffery Miller","date":"December 24, 2025","format":false,"excerpt":"When your project grows, unit test classes can become repetitive. You often find yourself duplicating setup code, utility methods, or common assertions across multiple test suites. Subclassing provides a powerful way to eliminate this redundancy, promote code reuse, and create a more organized and maintainable testing structure. Why Use Subclasses\u2026","rel":"","context":"In &quot;JAVA&quot;","block_context":{"text":"JAVA","link":"https:\/\/www.mymiller.name\/wordpress\/category\/java\/"},"img":{"alt_text":"","src":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/10\/ai-generated-8686283_1280-jpg.avif","width":350,"height":200,"srcset":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/10\/ai-generated-8686283_1280-jpg.avif 1x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/10\/ai-generated-8686283_1280-jpg.avif 1.5x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/10\/ai-generated-8686283_1280-jpg.avif 2x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2024\/10\/ai-generated-8686283_1280-jpg.avif 3x"},"classes":[]},{"id":3111,"url":"https:\/\/www.mymiller.name\/wordpress\/java_tips\/java-tips-part-1\/","url_meta":{"origin":1936,"position":1},"title":"Java Tips Part 1","author":"Jeffery Miller","date":"December 24, 2025","format":false,"excerpt":"Over the years I have done a number of code reviews there have been a number of common mistakes that I have found. So here are my Java tips a series of posts going over these tips. Tip 1: Throw original Exception Many times in our code we will catch\u2026","rel":"","context":"In &quot;Java Tips&quot;","block_context":{"text":"Java Tips","link":"https:\/\/www.mymiller.name\/wordpress\/category\/java_tips\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2021\/12\/sam-dan-truong-rF4kuvgHhU-unsplash-scaled-e1640791434235.jpg?fit=640%2C427&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2021\/12\/sam-dan-truong-rF4kuvgHhU-unsplash-scaled-e1640791434235.jpg?fit=640%2C427&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2021\/12\/sam-dan-truong-rF4kuvgHhU-unsplash-scaled-e1640791434235.jpg?fit=640%2C427&ssl=1&resize=525%2C300 1.5x"},"classes":[]},{"id":3462,"url":"https:\/\/www.mymiller.name\/wordpress\/java_extra\/ansi-colors-simpler\/","url_meta":{"origin":1936,"position":2},"title":"ANSI Colors simpler","author":"Jeffery Miller","date":"December 24, 2025","format":false,"excerpt":"When it comes to enhancing the visual appeal of console applications, one powerful and straightforward technique is to use ANSI color codes. These codes allow you to apply various colors to your text and backgrounds, making your output more vibrant and readable. In Java, you can achieve this by utilizing\u2026","rel":"","context":"In &quot;Java Extras&quot;","block_context":{"text":"Java Extras","link":"https:\/\/www.mymiller.name\/wordpress\/category\/java_extra\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2019\/04\/coding-924920_640.jpg?fit=640%2C426&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2019\/04\/coding-924920_640.jpg?fit=640%2C426&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2019\/04\/coding-924920_640.jpg?fit=640%2C426&ssl=1&resize=525%2C300 1.5x"},"classes":[]},{"id":3060,"url":"https:\/\/www.mymiller.name\/wordpress\/java_extra\/string-cache\/","url_meta":{"origin":1936,"position":3},"title":"String Cache","author":"Jeffery Miller","date":"December 24, 2025","format":false,"excerpt":"Sometimes an application will have a large number of Strings. Due to memory we certainly do not want to keep multiple instances of duplicate strings in memory. A string cache can greatly reduce the number of Strings in memory. I had a case where I had millions of strings, and\u2026","rel":"","context":"In &quot;Java Extras&quot;","block_context":{"text":"Java Extras","link":"https:\/\/www.mymiller.name\/wordpress\/category\/java_extra\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2021\/05\/geocache-398016_640.jpg?fit=640%2C480&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2021\/05\/geocache-398016_640.jpg?fit=640%2C480&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2021\/05\/geocache-398016_640.jpg?fit=640%2C480&ssl=1&resize=525%2C300 1.5x"},"classes":[]},{"id":3370,"url":"https:\/\/www.mymiller.name\/wordpress\/java_new_features\/sealed-classes\/","url_meta":{"origin":1936,"position":4},"title":"Sealed Classes","author":"Jeffery Miller","date":"December 24, 2025","format":false,"excerpt":"Java 15 introduced a new feature called Sealed Classes, which allows developers to restrict the types that can extend a class or implement an interface. This feature is designed to improve code safety and maintainability by enforcing a set of rules that limit the hierarchy of classes and interfaces. In\u2026","rel":"","context":"In &quot;Java New Features&quot;","block_context":{"text":"Java New Features","link":"https:\/\/www.mymiller.name\/wordpress\/category\/java_new_features\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2023\/06\/letter-g26b7a9ac7_640.jpg?fit=640%2C427&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2023\/06\/letter-g26b7a9ac7_640.jpg?fit=640%2C427&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2023\/06\/letter-g26b7a9ac7_640.jpg?fit=640%2C427&ssl=1&resize=525%2C300 1.5x"},"classes":[]},{"id":3148,"url":"https:\/\/www.mymiller.name\/wordpress\/java_tips\/java-tips-part-2\/","url_meta":{"origin":1936,"position":5},"title":"Java Tips Part 2","author":"Jeffery Miller","date":"December 24, 2025","format":false,"excerpt":"Continuing my Java Tips from experience that I have learned from code reviews to different programming tasks. Tip 6: Don't call .toString() on slf4j logging calls. So if you're following my other tips and using the formatting option of the logging messages like this: list.parallelStream().filter(item -> !item.contains(\"BOB\")).forEach(item -> logger.debug(\"Item: {}\u2026","rel":"","context":"In &quot;Java Tips&quot;","block_context":{"text":"Java Tips","link":"https:\/\/www.mymiller.name\/wordpress\/category\/java_tips\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2021\/12\/sam-dan-truong-rF4kuvgHhU-unsplash-scaled-e1640791434235.jpg?fit=640%2C427&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2021\/12\/sam-dan-truong-rF4kuvgHhU-unsplash-scaled-e1640791434235.jpg?fit=640%2C427&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2021\/12\/sam-dan-truong-rF4kuvgHhU-unsplash-scaled-e1640791434235.jpg?fit=640%2C427&ssl=1&resize=525%2C300 1.5x"},"classes":[]}],"jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"_links":{"self":[{"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/posts\/1936","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/comments?post=1936"}],"version-history":[{"count":6,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/posts\/1936\/revisions"}],"predecessor-version":[{"id":2912,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/posts\/1936\/revisions\/2912"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/media\/1937"}],"wp:attachment":[{"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/media?parent=1936"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/categories?post=1936"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/tags?post=1936"},{"taxonomy":"series","embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/series?post=1936"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}