* Parses the input string into components separated by the delimiter. Unlike
* String.split(), this method will ignore occurrences of the delimiter which
* are nested within single quotes in name-value pair values, e.g. a='b,c'.
+ * New implementation to avoid Pattern for jalviewjs.
*
* @param input
* @param delimiter
public static String[] separatorListToArray(String input,
String delimiter)
{
- int seplen = delimiter.length();
- if (input == null || input.equals("") || input.equals(delimiter))
+ if (input == null
+ // these two shouldn't return null (one or two "" respectively)
+ || input.equals("") || input.equals(delimiter))
{
return null;
}
- List<String> jv = new ArrayList<>();
- int cp = 0, pos, escape;
- boolean wasescaped = false, wasquoted = false;
- String lstitem = null;
- while ((pos = input.indexOf(delimiter, cp)) >= cp)
+
+ final char escapeChar = '\\';
+ final char quoteChar = '\'';
+ int ilength = input.length();
+ int dlength = delimiter.length();
+ List<String> values = new ArrayList<>();
+
+ boolean escape = false;
+ boolean inquote = false;
+
+ int start = 0;
+ for (int i = 0; i < ilength; i++)
{
- escape = (pos > 0 && input.charAt(pos - 1) == '\\') ? -1 : 0;
- if (wasescaped || wasquoted)
+ if (!escape && !inquote && ilength >= i + dlength
+ && input.substring(i, i + dlength).equals(delimiter))
{
- // append to previous pos
- jv.set(jv.size() - 1, lstitem = lstitem + delimiter
- + input.substring(cp, pos + escape));
+ // found a delimiter
+ values.add(input.substring(start, i));
+ i += dlength;
+ start = i;
+ continue;
}
- else
+ char c = input.charAt(i);
+ if (c == escapeChar)
{
- jv.add(lstitem = input.substring(cp, pos + escape));
- }
- cp = pos + seplen;
- wasescaped = escape == -1;
- // last separator may be in an unmatched quote
- // private static final Pattern DELIMITERS_PATTERN =
- // Pattern.compile(".*='[^']*(?!')");
- wasquoted = DELIMITERS_PATTERN.matcher(lstitem).matches();
- }
- if (cp < input.length())
- {
- String c = input.substring(cp);
- if (wasescaped || wasquoted)
- {
- // append final separator
- jv.set(jv.size() - 1, lstitem + delimiter + c);
+ escape = !escape;
+ continue;
}
- else
+ if (escape)
{
- if (!c.equals(delimiter))
- {
- jv.add(c);
- }
+ escape = false;
+ continue;
}
- }
- if (jv.size() > 0)
- {
- String[] v = jv.toArray(new String[jv.size()]);
- jv.clear();
- if (DEBUG)
+ if (c == quoteChar)
{
- jalview.bin.Console.errPrintln("Array from '" + delimiter
- + "' separated List:\n" + v.length);
- for (int i = 0; i < v.length; i++)
- {
- jalview.bin.Console.errPrintln("item " + i + " '" + v[i] + "'");
- }
+ inquote = !inquote;
}
- return v;
}
- if (DEBUG)
- {
- jalview.bin.Console.errPrintln(
- "Empty Array from '" + delimiter + "' separated List");
- }
- return null;
+ // add the last value
+ values.add(input.substring(start, ilength));
+
+ return values.toArray(new String[values.size()]);
}
/**
import static org.testng.AssertJUnit.assertNull;
import static org.testng.AssertJUnit.assertTrue;
-import jalview.gui.JvOptionPane;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
+import jalview.gui.JvOptionPane;
+
public class StringUtilsTest
{
*/
assertEquals("[abc='|'d, ef, g]", Arrays.toString(
StringUtils.separatorListToArray("abc='|'d|ef|g", "|")));
+
+ /*
+ * edge cases
+ */
+ assertEquals("[abc=';'d, ef, g, key='val;ue;key2=what]",
+ Arrays.toString(StringUtils.separatorListToArray(
+ "abc=';'d;ef;g;key='val;ue;key2=what", ";")));
+ assertEquals("[hello\\world, jello'\\world', mello'wo\\'rld']",
+ Arrays.toString(StringUtils.separatorListToArray(
+ "hello\\world;jello'\\world';mello'wo\\'rld'", ";")));
}
@Test(groups = { "Functional" })