From ff6185f6e662d66eca0af9e9c403d08c7ae008f1 Mon Sep 17 00:00:00 2001 From: Ben Soares Date: Tue, 31 Oct 2023 12:57:25 +0000 Subject: [PATCH] JAL-4059 simpler reimplementation of StringUtils.separatorListToArray to avoid unnecessary use of Pattern and regex because of possible jalviewjs Pattern incompatibility --- src/jalview/util/StringUtils.java | 89 +++++++++++++------------------- test/jalview/util/StringUtilsTest.java | 14 ++++- 2 files changed, 48 insertions(+), 55 deletions(-) diff --git a/src/jalview/util/StringUtils.java b/src/jalview/util/StringUtils.java index 9212c8e..1b2cbaf 100644 --- a/src/jalview/util/StringUtils.java +++ b/src/jalview/util/StringUtils.java @@ -144,6 +144,7 @@ public class StringUtils * 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 @@ -152,72 +153,54 @@ public class StringUtils 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 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 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()]); } /** diff --git a/test/jalview/util/StringUtilsTest.java b/test/jalview/util/StringUtilsTest.java index 22324fc..dc7ed92 100644 --- a/test/jalview/util/StringUtilsTest.java +++ b/test/jalview/util/StringUtilsTest.java @@ -24,8 +24,6 @@ import static org.testng.AssertJUnit.assertEquals; 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; @@ -33,6 +31,8 @@ import java.util.List; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import jalview.gui.JvOptionPane; + public class StringUtilsTest { @@ -121,6 +121,16 @@ 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" }) -- 1.7.10.2