@VisibleForTesting
  static String trimFieldsAndRemoveEmptyFields(String str) {
    char[] chars = str.toCharArray();
    char[] res = new char[chars.length];
    /*
     * set when reading the first non trimmable char after a separator char (or the beginning of the string)
     * unset when reading a separator
     */
    boolean inField = false;
    boolean inQuotes = false;
    int i = 0;
    int resI = 0;
    for (; i < chars.length; i++) {
      boolean isSeparator = chars[i] == ',';
      if (!inQuotes && isSeparator) {
        // exiting field (may already be unset)
        inField = false;
        if (resI > 0) {
          resI = retroTrim(res, resI);
        }
      } else {
        boolean isTrimmed = !inQuotes && istrimmable(chars[i]);
        if (isTrimmed && !inField) {
          // we haven't meet any non trimmable char since the last separator yet
          continue;
        }

        boolean isEscape = isEscapeChar(chars[i]);
        if (isEscape) {
          inQuotes = !inQuotes;
        }

        // add separator as we already had one field
        if (!inField && resI > 0) {
          res[resI] = ',';
          resI++;
        }

        // register in field (may already be set)
        inField = true;
        // copy current char
        res[resI] = chars[i];
        resI++;
      }
    }
    // inQuotes can only be true at this point if quotes are unbalanced
    if (!inQuotes) {
      // trim end of str
      resI = retroTrim(res, resI);
    }
    return new String(res, 0, resI);
  }
@ VisibleForTesting static String trimFieldsAndRemoveEmptyFields ( String str ) { char [ ] chars = str . toCharArray ( ) ; char [ ] res = new char [ chars . length ] ; boolean inField = false ; boolean inQuotes = false ; int i = 0 ; int resI = 0 ; for ( ; i < chars . length ; i ++ ) { boolean isSeparator = chars [ i ] == ',' ; if ( ! inQuotes && isSeparator ) { inField = false ; if ( resI > 0 ) { resI = retroTrim ( res , resI ) ; } } else { boolean isTrimmed = ! inQuotes && istrimmable ( chars [ i ] ) ; if ( isTrimmed && ! inField ) { continue ; } boolean isEscape = isEscapeChar ( chars [ i ] ) ; if ( isEscape ) { inQuotes = ! inQuotes ; } if ( ! inField && resI > 0 ) { res [ resI ] = ',' ; resI ++ ; } inField = true ; res [ resI ] = chars [ i ] ; resI ++ ; } } if ( ! inQuotes ) { resI = retroTrim ( res , resI ) ; } return new String ( res , 0 , resI ) ; }
Removes the empty fields from the value of a multi-value property from empty fields, including trimming each field.
<p>
Quotes can be used to prevent an empty field to be removed (as it is used to preserve empty spaces).
<ul>
<li>{@code "" => ""}</li>
<li>{@code " " => ""}</li>
<li>{@code "," => ""}</li>
<li>{@code ",," => ""}</li>
<li>{@code ",,," => ""}</li>
<li>{@code ",a" => "a"}</li>
<li>{@code "a," => "a"}</li>
<li>{@code ",a," => "a"}</li>
<li>{@code "a,,b" => "a,b"}</li>
<li>{@code "a,   ,b" => "a,b"}</li>
<li>{@code "a,\"\",b" => "a,b"}</li>
<li>{@code "\"a\",\"b\"" => "\"a\",\"b\""}</li>
<li>{@code "\"  a  \",\"b \"" => "\"  a  \",\"b \""}</li>
<li>{@code "\"a\",\"\",\"b\"" => "\"a\",\"\",\"b\""}</li>
<li>{@code "\"a\",\"  \",\"b\"" => "\"a\",\"  \",\"b\""}</li>
<li>{@code "\"  a,,b,c  \",\"d \"" => "\"  a,,b,c  \",\"d \""}</li>
<li>{@code "a,\"  \",b" => "ab"]}</li>
</ul>