001package io.ebean.util;
002
003import java.util.HashMap;
004import java.util.Map;
005import java.util.regex.Pattern;
006
007/**
008 * Utility String class that supports String manipulation functions.
009 */
010public class StringHelper {
011
012  private static final Pattern SPLIT_NAMES = Pattern.compile("[\\s,;]+");
013
014  private static final String[] EMPTY_STRING_ARRAY = new String[0];
015
016  /**
017   * Return true if the value is null or an empty string.
018   */
019  public static boolean isNull(String value) {
020    return value == null || value.trim().isEmpty();
021  }
022
023  /**
024   * Parses out a list of Name Value pairs that are delimited together. Will
025   * always return a StringMap. If allNameValuePairs is null, or no name values
026   * can be parsed out an empty StringMap is returned.
027   *
028   * @param source  the entire string to be parsed.
029   * @param listDelimiter      (typically ';') the delimited between the list
030   * @param nameValueSeparator (typically '=') the separator between the name and value
031   */
032  public static Map<String, String> delimitedToMap(String source, String listDelimiter, String nameValueSeparator) {
033    Map<String, String> params = new HashMap<>();
034    if (source == null || source.isEmpty()) {
035      return params;
036    }
037    // trim off any leading listDelimiter...
038    source = trimFront(source, listDelimiter);
039    return delimitedToMap(params, source, listDelimiter, nameValueSeparator);
040  }
041
042  /**
043   * Trims off recurring strings from the front of a string.
044   *
045   * @param source the source string
046   * @param trim   the string to trim off the front
047   */
048  private static String trimFront(String source, String trim) {
049    while (true) {
050      if (source.indexOf(trim) == 0) {
051        source = source.substring(trim.length());
052      } else {
053        return source;
054      }
055    }
056  }
057
058  /**
059   * Recursively pulls out the key value pairs from a raw string.
060   */
061  private static Map<String, String> delimitedToMap(Map<String, String> map, String source, String listDelimiter, String nameValueSeparator) {
062    int pos = 0;
063    while (true) {
064      if (pos >= source.length()) {
065        return map;
066      }
067      int equalsPos = source.indexOf(nameValueSeparator, pos);
068      int delimPos = source.indexOf(listDelimiter, pos);
069      if (delimPos == -1) {
070        delimPos = source.length();
071      }
072      if (equalsPos == -1) {
073        return map;
074      }
075      if (delimPos == (equalsPos + 1)) {
076        pos = delimPos + 1;
077        continue;
078      }
079      if (equalsPos > delimPos) {
080        // there is a key without a value?
081        String key = source.substring(pos, delimPos);
082        key = key.trim();
083        if (!key.isEmpty()) {
084          map.put(key, null);
085        }
086        pos = delimPos + 1;
087        continue;
088      }
089      String key = source.substring(pos, equalsPos);
090      String value = source.substring(equalsPos + 1, delimPos);
091      map.put(key.trim(), value);
092      pos = delimPos + 1;
093    }
094  }
095
096  /**
097   * This method takes a String and will replace all occurrences of the match
098   * String with that of the replace String.
099   *
100   * @param source  the source string
101   * @param match   the string used to find a match
102   * @param replace the string used to replace match with
103   * @return the source string after the search and replace
104   */
105  public static String replace(String source, String match, String replace) {
106    if (source == null) {
107      return null;
108    }
109    if (replace == null) {
110      return source;
111    }
112    return source.replace(match, replace);
113  }
114
115  /**
116   * Return new line and carriage return with space.
117   */
118  public static String removeNewLines(String source) {
119    source = source.replace('\n', ' ');
120    return source.replace('\r', ' ');
121  }
122
123  /**
124   * Splits at any whitespace "," or ";" and trims the result.
125   * It does not return empty entries.
126   */
127  public static String[] splitNames(String names) {
128    if (names == null || names.isEmpty()) {
129      return EMPTY_STRING_ARRAY;
130    }
131    String[] result = SPLIT_NAMES.split(names);
132    if (result.length == 0) {
133      return EMPTY_STRING_ARRAY;
134    }
135    if ("".equals(result[0])) { //  input string starts with whitespace
136      if (result.length == 1) { // input string contains only whitespace
137        return EMPTY_STRING_ARRAY;
138      } else {
139        String[] ret = new String[result.length-1]; // remove first entry
140        System.arraycopy(result, 1, ret, 0, ret.length);
141        return ret;
142      }
143    } else {
144      return result;
145    }
146  }
147}