package io.ebean.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

Utility String class that supports String manipulation functions.
/** * Utility String class that supports String manipulation functions. */
public class StringHelper { private static final char SINGLE_QUOTE = '\''; private static final char DOUBLE_QUOTE = '"'; private static final Pattern SPLIT_NAMES = Pattern.compile("[\\s,;]+"); private static final String[] EMPTY_STRING_ARRAY = new String[0];
parses a String of the form name1='value1' name2='value2'. Note that you can use either single or double quotes for any particular name value pair and the end quote must match the begin quote.
/** * parses a String of the form name1='value1' name2='value2'. Note that you * can use either single or double quotes for any particular name value pair * and the end quote must match the begin quote. */
public static HashMap<String, String> parseNameQuotedValue(String tag) throws RuntimeException { if (tag == null || tag.length() < 1) { return null; } // make sure that the quotes are matched... // int remainer = countOccurances(tag, ""+quote) % 2; // if (remainer == 1) { // dp("remainder = "+remainer); // throw new StringParsingException("Unmatched quote in "+tag); // } // make sure that th last character is not an equals... // (check now so I don't need to check this every time..) if (tag.charAt(tag.length() - 1) == '=') { throw new RuntimeException("missing quoted value at the end of " + tag); } HashMap<String, String> map = new HashMap<>(); // recursively parse out the name value pairs... return parseNameQuotedValue(map, tag, 0); }
recursively parse out name value pairs (where the value is quoted, with either single or double quotes).
/** * recursively parse out name value pairs (where the value is quoted, with * either single or double quotes). */
private static HashMap<String, String> parseNameQuotedValue(HashMap<String, String> map, String tag, int pos) throws RuntimeException { while (true) { int equalsPos = tag.indexOf('=', pos); if (equalsPos > -1) { // check for begin quote... char firstQuote = tag.charAt(equalsPos + 1); if (firstQuote != SINGLE_QUOTE && firstQuote != DOUBLE_QUOTE) { throw new RuntimeException("missing begin quote at " + (equalsPos) + "[" + tag.charAt(equalsPos + 1) + "] in [" + tag + "]"); } // check for end quote... int endQuotePos = tag.indexOf(firstQuote, equalsPos + 2); if (endQuotePos == -1) { throw new RuntimeException("missing end quote [" + firstQuote + "] after " + pos + " in [" + tag + "]"); } // we have a valid name and value... // dp("pos="+pos+" equalsPos="+equalsPos+" // endQuotePos="+endQuotePos); String name = tag.substring(pos, equalsPos); // dp("name="+name+"; value="+value+";"); // trim off any whitespace from the front of name... name = trimFront(name, " "); if ((name.indexOf(SINGLE_QUOTE) > -1) || (name.indexOf(DOUBLE_QUOTE) > -1)) { throw new RuntimeException("attribute name contains a quote [" + name + "]"); } String value = tag.substring(equalsPos + 2, endQuotePos); map.put(name, value); pos = endQuotePos + 1; } else { // no more equals... stop parsing... return map; } } }
Returns the number of times a particular String occurs in another String. e.g. count the number of single quotes.
/** * Returns the number of times a particular String occurs in another String. * e.g. count the number of single quotes. */
public static int countOccurances(String content, String occurs) { return countOccurances(content, occurs, 0, 0); } private static int countOccurances(String content, String occurs, int pos, int countSoFar) { while (true) { int equalsPos = content.indexOf(occurs, pos); if (equalsPos > -1) { countSoFar += 1; pos = equalsPos + occurs.length(); // dp("countSoFar="+countSoFar+" pos="+pos); } else { return countSoFar; } } }
Parses out a list of Name Value pairs that are delimited together. Will always return a StringMap. If allNameValuePairs is null, or no name values can be parsed out an empty StringMap is returned.
Params:
  • allNameValuePairs – the entire string to be parsed.
  • listDelimiter – (typically ';') the delimited between the list
  • nameValueSeparator – (typically '=') the separator between the name and value
/** * Parses out a list of Name Value pairs that are delimited together. Will * always return a StringMap. If allNameValuePairs is null, or no name values * can be parsed out an empty StringMap is returned. * * @param allNameValuePairs the entire string to be parsed. * @param listDelimiter (typically ';') the delimited between the list * @param nameValueSeparator (typically '=') the separator between the name and value */
public static Map<String, String> delimitedToMap(String allNameValuePairs, String listDelimiter, String nameValueSeparator) { HashMap<String, String> params = new HashMap<>(); if ((allNameValuePairs == null) || (allNameValuePairs.isEmpty())) { return params; } // trim off any leading listDelimiter... allNameValuePairs = trimFront(allNameValuePairs, listDelimiter); return getKeyValue(params, 0, allNameValuePairs, listDelimiter, nameValueSeparator); }
Trims off recurring strings from the front of a string.
Params:
  • source – the source string
  • trim – the string to trim off the front
/** * Trims off recurring strings from the front of a string. * * @param source the source string * @param trim the string to trim off the front */
public static String trimFront(String source, String trim) { while (true) { if (source == null) { return null; } if (source.indexOf(trim) == 0) { // dp("trim ..."); source = source.substring(trim.length()); } else { return source; } } }
Return true if the value is null or an empty string.
/** * Return true if the value is null or an empty string. */
public static boolean isNull(String value) { return value == null || value.trim().isEmpty(); }
Recursively pulls out the key value pairs from a raw string.
/** * Recursively pulls out the key value pairs from a raw string. */
private static HashMap<String, String> getKeyValue(HashMap<String, String> map, int pos, String allNameValuePairs, String listDelimiter, String nameValueSeparator) { while (true) { if (pos >= allNameValuePairs.length()) { // dp("end as "+pos+" >= "+allNameValuePairs.length() ); return map; } int equalsPos = allNameValuePairs.indexOf(nameValueSeparator, pos); int delimPos = allNameValuePairs.indexOf(listDelimiter, pos); if (delimPos == -1) { delimPos = allNameValuePairs.length(); } if (equalsPos == -1) { // dp("no more equals..."); return map; } if (delimPos == (equalsPos + 1)) { // dp("Ignoring as nothing between delim and equals... // delim:"+delimPos+" eq:"+equalsPos); pos = delimPos + 1; continue; } if (equalsPos > delimPos) { // there is a key without a value? String key = allNameValuePairs.substring(pos, delimPos); key = key.trim(); if (!key.isEmpty()) { map.put(key, null); } pos = delimPos + 1; continue; } String key = allNameValuePairs.substring(pos, equalsPos); if (delimPos > -1) { String value = allNameValuePairs.substring(equalsPos + 1, delimPos); // dp("cont "+key+","+value+" pos:"+pos+" // len:"+allNameValuePairs.length()); key = key.trim(); map.put(key, value); pos = delimPos + 1; // recurse the rest of the values... } else { // dp("ERROR: delimPos < 0 ???"); return map; } } }
Convert a string that has delimited values (say comma delimited) in a String[]. You must explicitly choose whether or not to include empty values (say two commas that a right beside each other.

e.g. "alpha,beta,,theta"
With keepEmpties true, this results in a String[] of size 4 with the third one having a String of 0 length. With keepEmpties false, this results in a String[] of size 3.

e.g. ",alpha,beta,,theta,"
With keepEmpties true, this results in a String[] of size 6 with the 1st,4th and 6th one having a String of 0 length. With keepEmpties false, this results in a String[] of size 3.

/** * Convert a string that has delimited values (say comma delimited) in a * String[]. You must explicitly choose whether or not to include empty values * (say two commas that a right beside each other. * <p> * e.g. "alpha,beta,,theta"<br> * With keepEmpties true, this results in a String[] of size 4 with the third * one having a String of 0 length. With keepEmpties false, this results in a * String[] of size 3. * <p> * e.g. ",alpha,beta,,theta,"<br> * With keepEmpties true, this results in a String[] of size 6 with the * 1st,4th and 6th one having a String of 0 length. With keepEmpties false, * this results in a String[] of size 3. */
public static String[] delimitedToArray(String str, String delimiter, boolean keepEmpties) { ArrayList<String> list = new ArrayList<>(); int startPos = 0; delimiter(str, delimiter, keepEmpties, startPos, list); String[] result = new String[list.size()]; return list.toArray(result); } private static void delimiter(String str, String delimiter, boolean keepEmpties, int startPos, ArrayList<String> list) { int endPos = str.indexOf(delimiter, startPos); if (endPos == -1) { if (startPos <= str.length()) { String lastValue = str.substring(startPos, str.length()); if (keepEmpties || !lastValue.isEmpty()) { list.add(lastValue); } } // we have finished parsing the string... } else { // get the delimited value... add it.. String value = str.substring(startPos, endPos); if (keepEmpties || !value.isEmpty()) { list.add(value); } // recursively search as we are not at the end yet... delimiter(str, delimiter, keepEmpties, endPos + 1, list); } }
This method takes a String and will replace all occurrences of the match String with that of the replace String.
Params:
  • source – the source string
  • match – the string used to find a match
  • replace – the string used to replace match with
Returns:the source string after the search and replace
/** * This method takes a String and will replace all occurrences of the match * String with that of the replace String. * * @param source the source string * @param match the string used to find a match * @param replace the string used to replace match with * @return the source string after the search and replace */
public static String replaceString(String source, String match, String replace) { if (source == null) { return null; } if (replace == null) { return source; } if (match == null) { throw new NullPointerException("match is null?"); } if (match.equals(replace)) { return source; } return replaceString(source, match, replace, 30, 0, source.length()); }
Additionally specify the additionalSize to add to the buffer. This will make the buffer bigger so that it doesn't have to grow when replacement occurs.
/** * Additionally specify the additionalSize to add to the buffer. This will * make the buffer bigger so that it doesn't have to grow when replacement * occurs. */
public static String replaceString(String source, String match, String replace, int additionalSize, int startPos, int endPos) { if (source == null) { return null; } char match0 = match.charAt(0); int matchLength = match.length(); if (matchLength == 1 && replace.length() == 1) { char replace0 = replace.charAt(0); return source.replace(match0, replace0); } if (matchLength >= replace.length()) { additionalSize = 0; } int sourceLength = source.length(); int lastMatch = endPos - matchLength; StringBuilder sb = new StringBuilder(sourceLength + additionalSize); if (startPos > 0) { sb.append(source.substring(0, startPos)); } char sourceChar; boolean isMatch; int sourceMatchPos; for (int i = startPos; i < sourceLength; i++) { sourceChar = source.charAt(i); if (i > lastMatch || sourceChar != match0) { sb.append(sourceChar); } else { // check to see if this is a match isMatch = true; sourceMatchPos = i; // check each following character... for (int j = 1; j < matchLength; j++) { sourceMatchPos++; if (source.charAt(sourceMatchPos) != match.charAt(j)) { isMatch = false; break; } } if (isMatch) { i = i + matchLength - 1; sb.append(replace); } else { // was not a match sb.append(sourceChar); } } } return sb.toString(); }
A search and replace with multiple matching strings.

Useful when converting CRNL CR and NL all to a BR tag for example.


String[] multi = { "\r\n", "\r", "\n" };
content = StringHelper.replaceStringMulti(content, multi, "<br/>");
/** * A search and replace with multiple matching strings. * <p> * Useful when converting CRNL CR and NL all to a BR tag for example. * <pre>{@code * * String[] multi = { "\r\n", "\r", "\n" }; * content = StringHelper.replaceStringMulti(content, multi, "<br/>"); * * }</pre> */
public static String replaceStringMulti(String source, String[] match, String replace) { if (source == null) { return null; } return replaceStringMulti(source, match, replace, 30, 0, source.length()); }
Additionally specify an additional size estimate for the buffer plus start and end positions.

The start and end positions can limit the search and replace. Otherwise these default to startPos = 0 and endPos = source.length().

/** * Additionally specify an additional size estimate for the buffer plus start * and end positions. * <p> * The start and end positions can limit the search and replace. Otherwise * these default to startPos = 0 and endPos = source.length(). * </p> */
public static String replaceStringMulti(String source, String[] match, String replace, int additionalSize, int startPos, int endPos) { if (source == null) { return null; } int shortestMatch = match[0].length(); char[] match0 = new char[match.length]; for (int i = 0; i < match0.length; i++) { match0[i] = match[i].charAt(0); if (match[i].length() < shortestMatch) { shortestMatch = match[i].length(); } } StringBuilder sb = new StringBuilder(source.length() + additionalSize); char sourceChar; int len = source.length(); int lastMatch = endPos - shortestMatch; if (startPos > 0) { sb.append(source.substring(0, startPos)); } int matchCount; for (int i = startPos; i < len; i++) { sourceChar = source.charAt(i); if (i > lastMatch) { sb.append(sourceChar); } else { matchCount = 0; for (int k = 0; k < match0.length; k++) { if (matchCount == 0 && sourceChar == match0[k]) { if (match[k].length() + i <= len) { ++matchCount; int j = 1; for (; j < match[k].length(); j++) { if (source.charAt(i + j) != match[k].charAt(j)) { --matchCount; break; } } if (matchCount > 0) { i = i + j - 1; sb.append(replace); break; } } } } if (matchCount == 0) { sb.append(sourceChar); } } } return sb.toString(); }
Splits at any whitespace "," or ";" and trims the result. It does not return empty entries.
/** * Splits at any whitespace "," or ";" and trims the result. * It does not return empty entries. */
public static String[] splitNames(String names) { if (names == null || names.isEmpty()) { return EMPTY_STRING_ARRAY; } String[] result = SPLIT_NAMES.split(names); if (result.length == 0) { return EMPTY_STRING_ARRAY; // don't know if this ever can happen } if ("".equals(result[0])) { // = input string starts with whitespace if (result.length == 1) { // = input string contains only whitespace return EMPTY_STRING_ARRAY; } else { String ret[] = new String[result.length-1]; // remove first entry System.arraycopy(result, 1, ret, 0, ret.length); return ret; } } else { return result; } } }