This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 80b8200  Tests.
80b8200 is described below

commit 80b8200ec3a02498e51425d8654be629e3777285
Author: JamesBognar <[email protected]>
AuthorDate: Fri Jul 6 17:46:27 2018 -0400

    Tests.
---
 .../juneau/httppart/OpenApiPartParserTest.java     |  30 ++++
 .../juneau/httppart/OpenApiPartSerializerTest.java |  77 +++++----
 .../org/apache/juneau/utils/StringUtilsTest.java   |  59 +++----
 .../org/apache/juneau/http/HeaderStringArray.java  |   2 +-
 .../juneau/httppart/OpenApiPartSerializer.java     |   4 +-
 .../java/org/apache/juneau/internal/AsciiSet.java  |  13 ++
 .../org/apache/juneau/internal/StringUtils.java    | 177 +++++++++++++++------
 .../org/apache/juneau/svl/VarResolverSession.java  |  14 +-
 8 files changed, 254 insertions(+), 122 deletions(-)

diff --git 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/OpenApiPartParserTest.java
 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/OpenApiPartParserTest.java
index 6ab32fa..f3fb701 100644
--- 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/OpenApiPartParserTest.java
+++ 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/OpenApiPartParserTest.java
@@ -364,6 +364,36 @@ public class OpenApiPartParserTest {
                
assertObjectEquals("['C3-[\\'foo\\',\\'bar\\']','C3-[\\'baz\\']']", p.parse(s, 
"foo,bar|baz", List.class, C3.class));
        }
 
+       @Test
+       public void c12a_stringType_nullKeyword_plain() throws Exception {
+               HttpPartSchema s = schema("string").build();
+               assertEquals("null", p.parse(s, "null", String.class));
+       }
+
+       @Test
+       public void c12b_stringType_nullKeyword_plain_2d() throws Exception {
+               HttpPartSchema s = 
schema("array").items(schema("string")).build();
+               assertObjectEquals("['null']", p.parse(s, "null", 
String[].class));
+               assertObjectEquals("[null]", p.parse(s, "@(null)", 
String[].class));
+       }
+
+       @Test
+       public void c12c_stringType_nullKeyword_uon() throws Exception {
+               HttpPartSchema s = schema("string","uon").build();
+               assertEquals(null, p.parse(s, "null", String.class));
+               assertEquals("null", p.parse(s, "'null'", String.class));
+       }
+
+       @Test
+       public void c12d_stringType_nullKeyword_uon_2d() throws Exception {
+               HttpPartSchema s = 
schema("array").items(schema("string","uon")).build();
+               assertObjectEquals("[null,'x']", p.parse(s, "null,x", 
String[].class));
+               assertObjectEquals("[null]", p.parse(s, "null", 
String[].class));
+               assertObjectEquals("[null]", p.parse(s, "@(null)", 
String[].class));
+               assertObjectEquals("['null']", p.parse(s, "'null'", 
String[].class));
+               assertObjectEquals("['null']", p.parse(s, "@('null')", 
String[].class));
+       }
+
        
//-----------------------------------------------------------------------------------------------------------------
        // type = array
        
//-----------------------------------------------------------------------------------------------------------------
diff --git 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/OpenApiPartSerializerTest.java
 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/OpenApiPartSerializerTest.java
index 840f561..ee43059 100644
--- 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/OpenApiPartSerializerTest.java
+++ 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/httppart/OpenApiPartSerializerTest.java
@@ -339,25 +339,27 @@ public class OpenApiPartSerializerTest {
        @Test
        public void d01_arrayType_collectionFormatCsv() throws Exception {
                HttpPartSchema ps = 
schema("array").collectionFormat("csv").build();
-               assertEquals("foo,bar", s.serialize(ps, new 
String[]{"foo","bar"}));
-               assertEquals("foo,bar", s.serialize(ps, new 
Object[]{"foo","bar"}));
-               assertEquals("D-foo,D-bar", s.serialize(ps, new D[]{new 
D("foo"),new D("bar")}));
-               assertEquals("foo,bar", s.serialize(ps, 
AList.create("foo","bar")));
-               assertEquals("foo,bar", s.serialize(ps, 
AList.<Object>create("foo","bar")));
-               assertEquals("D-foo,D-bar", s.serialize(ps, AList.create(new 
D("foo"),new D("bar"))));
-               assertEquals("foo,bar", s.serialize(ps, new 
ObjectList().append("foo","bar")));
+               assertEquals("foo,bar,null", s.serialize(ps, new 
String[]{"foo","bar",null}));
+               assertEquals("foo,bar,null", s.serialize(ps, new 
Object[]{"foo","bar",null}));
+               assertEquals("D-foo,D-bar,null", s.serialize(ps, new D[]{new 
D("foo"),new D("bar"),null}));
+               assertEquals("foo,bar,null", s.serialize(ps, 
AList.create("foo","bar",null)));
+               assertEquals("foo,bar,null", s.serialize(ps, 
AList.<Object>create("foo","bar",null)));
+               assertEquals("D-foo,D-bar,null", s.serialize(ps, 
AList.create(new D("foo"),new D("bar"),null)));
+               assertEquals("foo,bar,null", s.serialize(ps, new 
ObjectList().append("foo","bar",null)));
+
+               assertEquals("foo\\,bar,null", s.serialize(ps, new 
String[]{"foo,bar",null}));
        }
 
        @Test
        public void d02_arrayType_collectionFormatPipes() throws Exception {
                HttpPartSchema ps = 
schema("array").collectionFormat("pipes").build();
-               assertEquals("foo|bar", s.serialize(ps, new 
String[]{"foo","bar"}));
-               assertEquals("foo|bar", s.serialize(ps, new 
Object[]{"foo","bar"}));
-               assertEquals("D-foo|D-bar", s.serialize(ps, new D[]{new 
D("foo"),new D("bar")}));
-               assertEquals("foo|bar", s.serialize(ps, 
AList.create("foo","bar")));
-               assertEquals("foo|bar", s.serialize(ps, 
AList.<Object>create("foo","bar")));
-               assertEquals("D-foo|D-bar", s.serialize(ps, AList.create(new 
D("foo"),new D("bar"))));
-               assertEquals("foo|bar", s.serialize(ps, new 
ObjectList().append("foo","bar")));
+               assertEquals("foo|bar|null", s.serialize(ps, new 
String[]{"foo","bar",null}));
+               assertEquals("foo|bar|null", s.serialize(ps, new 
Object[]{"foo","bar",null}));
+               assertEquals("D-foo|D-bar|null", s.serialize(ps, new D[]{new 
D("foo"),new D("bar"),null}));
+               assertEquals("foo|bar|null", s.serialize(ps, 
AList.create("foo","bar",null)));
+               assertEquals("foo|bar|null", s.serialize(ps, 
AList.<Object>create("foo","bar",null)));
+               assertEquals("D-foo|D-bar|null", s.serialize(ps, 
AList.create(new D("foo"),new D("bar"),null)));
+               assertEquals("foo|bar|null", s.serialize(ps, new 
ObjectList().append("foo","bar",null)));
        }
 
        @Test
@@ -477,7 +479,7 @@ public class OpenApiPartSerializerTest {
        public void e02_booleanType_2d() throws Exception {
                HttpPartSchema ps = 
schema("array").items(schema("boolean")).build();
                assertEquals("true,true", s.serialize(ps, new 
boolean[]{true,true}));
-               assertEquals("true,true", s.serialize(ps, new 
Boolean[]{true,true}));
+               assertEquals("true,null", s.serialize(ps, new 
Boolean[]{true,null}));
                assertEquals("true,true", s.serialize(ps, 
AList.create(true,true)));
                assertEquals("true,true", s.serialize(ps, new 
String[]{"true","true"}));
                assertEquals("true,true", s.serialize(ps, 
AList.create("true","true")));
@@ -488,30 +490,27 @@ public class OpenApiPartSerializerTest {
                assertEquals("true,true", s.serialize(ps, new E2(true,true)));
        }
 
-       //      @Test
-//     public void e03_booleanType_3d() throws Exception {
-//             HttpPartSchema ps = 
schema("array").collectionFormat("pipes").items(schema("array").items(schema("boolean"))).build();
-//             assertEquals("[[true,true],[false]]", s.serialize(ps, 
"true,true|false", boolean[][].class));
-//             assertEquals("[[true,true],[false]]", s.serialize(ps, 
"true,true|false", List.class, boolean[].class));
-//             assertEquals("[[true,true],[false]]", s.serialize(ps, 
"true,true|false", Boolean[][].class));
-//             assertEquals("[[true,true],[false]]", s.serialize(ps, 
"true,true|false", List.class, Boolean[].class));
-//             assertEquals("[[true,true],[false]]", s.serialize(ps, 
"true,true|false", List.class, List.class, Boolean.class));
-//             assertEquals("[['true','true'],['false']]", s.serialize(ps, 
"true,true|false", String[][].class));
-//             assertEquals("[['true','true'],['false']]", s.serialize(ps, 
"true,true|false", List.class, List.class, String.class));
-//             assertEquals("[['true','true'],['false']]", s.serialize(ps, 
"true,true|false", List.class, String[].class));
-//             assertEquals("[[true,true],[false]]", s.serialize(ps, 
"true,true|false", Object[][].class));
-//             assertEquals("[[true,true],[false]]", s.serialize(ps, 
"true,true|false", List.class, List.class, Object.class));
-//             assertEquals("[[true,true],[false]]", s.serialize(ps, 
"true,true|false", List.class, Object[].class));
-//             assertEquals("[['E1-true','E1-true'],['E1-false']]", 
s.serialize(ps, "true,true|false", E1[][].class));
-//             assertEquals("[['E1-true','E1-true'],['E1-false']]", 
s.serialize(ps, "true,true|false", List.class, List.class, E1.class));
-//             assertEquals("[['E1-true','E1-true'],['E1-false']]", 
s.serialize(ps, "true,true|false", List.class, E1[].class));
-//             assertEquals("['E2-[true,true]','E2-[false]']", s.serialize(ps, 
"true,true|false", E2[].class));
-//             assertEquals("['E2-[true,true]','E2-[false]']", s.serialize(ps, 
"true,true|false", List.class, E2.class));
-//
-//             assertEquals("[[true,true],[false]]", s.serialize(ps, 
"True,true|false", boolean[][].class));
-//             assertEquals("[[true,true],[false]]", s.serialize(ps, 
"TRUE,true|false", boolean[][].class));
-//     }
-//
+       @Test
+       public void e03_booleanType_3d() throws Exception {
+               HttpPartSchema ps = 
schema("array").collectionFormat("pipes").items(schema("array").items(schema("boolean"))).build();
+               assertEquals("true,true|false", s.serialize(ps, new 
boolean[][]{{true,true},{false}}));
+               assertEquals("true,true|false", s.serialize(ps, 
AList.create(new boolean[]{true,true},new boolean[]{false})));
+               assertEquals("true,true|false,null", s.serialize(ps, new 
Boolean[][]{{true,true},{false,null}}));
+               assertEquals("true,true|false,null", s.serialize(ps, 
AList.create(new Boolean[]{true,true},new Boolean[]{false,null})));
+               assertEquals("true,true|false,null", s.serialize(ps, 
AList.create(AList.create(true,true),AList.create(false,null))));
+               assertEquals("true,true|false,null", s.serialize(ps, new 
String[][]{{"true","true"},{"false",null}}));
+               assertEquals("true,true|false,null", s.serialize(ps, 
AList.create(AList.create("true","true"),AList.create("false",null))));
+               assertEquals("true,true|false,null", s.serialize(ps, 
AList.create(new String[]{"true","true"},new String[]{"false",null})));
+               assertEquals("true,true|false,null", s.serialize(ps, new 
Object[][]{{true,true},{false,null}}));
+               assertEquals("true,true|false,null", s.serialize(ps, 
AList.create(AList.create((Object)true,(Object)true),AList.create((Object)false,null))));
+               assertEquals("true,true|false,null", s.serialize(ps, 
AList.create(new Object[]{true,true},new Object[]{false,null})));
+               assertEquals("true,true|false,null", s.serialize(ps, new 
E1[][]{{new E1(true),new E1(true)},{new E1(false),new E1(null)}}));
+               assertEquals("true,true|false,null", s.serialize(ps, 
AList.create(AList.create(new E1(true),new E1(true)), AList.create(new 
E1(false),new E1(null)))));
+               assertEquals("true,true|false,null", s.serialize(ps, 
AList.create(new E1[]{new E1(true),new E1(true)},new E1[]{new E1(false),new 
E1(null)})));
+               assertEquals("true,true|false,null", s.serialize(ps, new 
E2[]{new E2(true,true),new E2(false,null)}));
+               assertEquals("true,true|false,null", s.serialize(ps, 
AList.create(new E2(true,true),new E2(false,null))));
+       }
+
 //     
//-----------------------------------------------------------------------------------------------------------------
 //     // type = integer
 //     
//-----------------------------------------------------------------------------------------------------------------
diff --git 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java
 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java
index 0e7086a..657f4a7 100755
--- 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java
+++ 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java
@@ -21,6 +21,7 @@ import java.util.*;
 import java.util.concurrent.atomic.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.internal.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.parser.*;
 import org.apache.juneau.serializer.*;
@@ -464,8 +465,10 @@ public class StringUtilsTest {
                assertEquals("1,2", join(Arrays.asList(new Integer[]{1,2}), 
","));
 
                assertNull(join((Object[])null, ','));
-               assertEquals("1", join(new Object[]{1}, ','));
-               assertEquals("1,2", join(new Object[]{1,2}, ','));
+               assertEquals("x,y,z", join(new Object[]{"x,y","z"}, ','));
+
+               assertNull(joine((Object[])null, ','));
+               assertEquals("x\\,y,z", joine(new Object[]{"x,y","z"}, ','));
 
                assertNull(join((int[])null, ','));
                assertEquals("1", join(new int[]{1}, ','));
@@ -474,6 +477,9 @@ public class StringUtilsTest {
                assertNull(join((Collection<?>)null, ','));
                assertEquals("1", join(Arrays.asList(new Integer[]{1}), ','));
                assertEquals("1,2", join(Arrays.asList(new Integer[]{1,2}), 
','));
+
+               assertNull(joine((Collection<?>)null, ','));
+               assertEquals("x\\,y,z", joine(Arrays.asList(new 
String[]{"x,y","z"}), ','));
        }
 
        
//====================================================================================================
@@ -555,25 +561,22 @@ public class StringUtilsTest {
        
//====================================================================================================
        @Test
        public void testUnescapeChars() throws Exception {
-               char[] toEscape = {'\\',',','|'};
-               char escape = '\\';
+               AsciiSet escape = AsciiSet.create("\\,|");
 
-               assertNull(unEscapeChars(null, toEscape, escape));
-               assertEquals("xxx", unEscapeChars("xxx", new char[0], escape));
-               assertEquals("xxx", unEscapeChars("xxx", null, escape));
-               assertEquals("xxx", unEscapeChars("xxx", toEscape, (char)0));
+               assertNull(unEscapeChars(null, escape));
+               assertEquals("xxx", unEscapeChars("xxx", escape));
 
-               assertEquals("xxx", unEscapeChars("xxx", toEscape, escape));
-               assertEquals("x,xx", unEscapeChars("x\\,xx", toEscape, escape));
-               assertEquals("x\\xx", unEscapeChars("x\\xx", toEscape, escape));
-               assertEquals("x\\,xx", unEscapeChars("x\\\\,xx", toEscape, 
escape));
-               assertEquals("x\\,xx", unEscapeChars("x\\\\\\,xx", toEscape, 
escape));
-               assertEquals("\\", unEscapeChars("\\", toEscape, escape));
-               assertEquals(",", unEscapeChars("\\,", toEscape, escape));
-               assertEquals("|", unEscapeChars("\\|", toEscape, escape));
+               assertEquals("xxx", unEscapeChars("xxx", escape));
+               assertEquals("x,xx", unEscapeChars("x\\,xx", escape));
+               assertEquals("x\\xx", unEscapeChars("x\\xx", escape));
+               assertEquals("x\\,xx", unEscapeChars("x\\\\,xx", escape));
+               assertEquals("x\\,xx", unEscapeChars("x\\\\\\,xx", escape));
+               assertEquals("\\", unEscapeChars("\\", escape));
+               assertEquals(",", unEscapeChars("\\,", escape));
+               assertEquals("|", unEscapeChars("\\|", escape));
 
-               toEscape = new char[] {',','|'};
-               assertEquals("x\\\\xx", unEscapeChars("x\\\\xx", toEscape, 
escape));
+               escape = AsciiSet.create(",|");
+               assertEquals("x\\\\xx", unEscapeChars("x\\\\xx", escape));
        }
 
        
//====================================================================================================
@@ -735,16 +738,16 @@ public class StringUtilsTest {
        
//====================================================================================================
        @Test
        public void testSplitMap() {
-               assertObjectEquals("{a:'1'}", splitMap("a=1", ',', '=', true));
-               assertObjectEquals("{a:'1',b:'2'}", splitMap("a=1,b=2", ',', 
'=', true));
-               assertObjectEquals("{a:'1',b:'2'}", splitMap(" a = 1 , b = 2 ", 
',', '=', true));
-               assertObjectEquals("{' a ':' 1 ',' b ':' 2 '}", splitMap(" a = 
1 , b = 2 ", ',', '=', false));
-               assertObjectEquals("{a:''}", splitMap("a", ',', '=', true));
-               assertObjectEquals("{a:'',b:''}", splitMap("a,b", ',', '=', 
true));
-               assertObjectEquals("{a:'1',b:''}", splitMap("a=1,b", ',', '=', 
true));
-               assertObjectEquals("{a:'',b:'1'}", splitMap("a,b=1", ',', '=', 
true));
-               assertObjectEquals("{'a=':'1'}", splitMap("a\\==1", ',', '=', 
true));
-               assertObjectEquals("{'a\\\\':'1'}", splitMap("a\\\\=1", ',', 
'=', true));
+               assertObjectEquals("{a:'1'}", splitMap("a=1", true));
+               assertObjectEquals("{a:'1',b:'2'}", splitMap("a=1,b=2", true));
+               assertObjectEquals("{a:'1',b:'2'}", splitMap(" a = 1 , b = 2 ", 
true));
+               assertObjectEquals("{' a ':' 1 ',' b ':' 2 '}", splitMap(" a = 
1 , b = 2 ", false));
+               assertObjectEquals("{a:''}", splitMap("a", true));
+               assertObjectEquals("{a:'',b:''}", splitMap("a,b", true));
+               assertObjectEquals("{a:'1',b:''}", splitMap("a=1,b", true));
+               assertObjectEquals("{a:'',b:'1'}", splitMap("a,b=1", true));
+               assertObjectEquals("{'a=':'1'}", splitMap("a\\==1", true));
+               assertObjectEquals("{'a\\\\':'1'}", splitMap("a\\\\=1", true));
        }
 
        
//====================================================================================================
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/HeaderStringArray.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/HeaderStringArray.java
index 58edeb4..cd2777f 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/HeaderStringArray.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/HeaderStringArray.java
@@ -50,7 +50,7 @@ public class HeaderStringArray {
         * @return This header as a simple string.
         */
        public String asString() {
-               return StringUtils.join(value, ',');
+               return StringUtils.joine(value, ',');
        }
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/OpenApiPartSerializer.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/OpenApiPartSerializer.java
index 29f6e97..5c7a688 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/OpenApiPartSerializer.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/OpenApiPartSerializer.java
@@ -190,7 +190,7 @@ public class OpenApiPartSerializer extends 
UonPartSerializer {
                        HttpPartSchema.CollectionFormat cf = 
schema.getCollectionFormat();
 
                        if (cf == PIPES)
-                               out = join(l, '|');
+                               out = joine(l, '|');
                        else if (cf == SSV)
                                out = join(l, ' ');
                        else if (cf == TSV)
@@ -198,7 +198,7 @@ public class OpenApiPartSerializer extends 
UonPartSerializer {
                        else if (cf == HttpPartSchema.CollectionFormat.UON)
                                out = super.serialize(partType, null, l);
                        else
-                               out = join(l, ',');
+                               out = joine(l, ',');
 
                } else if (t == BOOLEAN || t == INTEGER || t == NUMBER) {
                        out = value == null ? "null" : value.toString();
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/AsciiSet.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/AsciiSet.java
index ebdf79d..7014b99 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/AsciiSet.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/AsciiSet.java
@@ -106,6 +106,19 @@ public final class AsciiSet {
                }
 
                /**
+                * Adds a set of characters to this set.
+                *
+                * @param chars The characters to keep in this store.
+                * @return This object (for method chaining).
+                */
+               public Builder chars(char...chars) {
+                       for (int i = 0; i < chars.length; i++)
+                               if (chars[i] < 128)
+                                       store[chars[i]] = true;
+                       return this;
+               }
+
+               /**
                 * Create a new {@link AsciiSet} object with the contents of 
this builder.
                 *
                 * @return A new {link AsciiSet} object.
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
index 452fccf..40f620a 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
@@ -22,6 +22,7 @@ import java.nio.*;
 import java.nio.charset.*;
 import java.text.*;
 import java.util.*;
+import java.util.concurrent.*;
 import java.util.concurrent.atomic.*;
 import java.util.regex.*;
 
@@ -428,6 +429,29 @@ public final class StringUtils {
        }
 
        /**
+        * Same as {@link #join(Object[], char)} except escapes the delimiter 
character if found in the tokens.
+        *
+        * @param tokens The tokens to join.
+        * @param d The delimiter.
+        * @return The delimited string.  If <code>tokens</code> is 
<jk>null</jk>, returns <jk>null</jk>.
+        */
+       public static String joine(Object[] tokens, char d) {
+               if (tokens == null)
+                       return null;
+               return joine(tokens, d, new StringBuilder()).toString();
+       }
+
+       private static AsciiSet getEscapeSet(char c) {
+               AsciiSet s = ESCAPE_SETS.get(c);
+               if (s == null) {
+                       s = AsciiSet.create().chars(c, '\\').build();
+                       ESCAPE_SETS.put(c, s);
+               }
+               return s;
+       }
+       static Map<Character,AsciiSet> ESCAPE_SETS = new ConcurrentHashMap<>();
+
+       /**
         * Join the specified tokens into a delimited string and writes the 
output to the specified string builder.
         *
         * @param tokens The tokens to join.
@@ -447,6 +471,26 @@ public final class StringUtils {
        }
 
        /**
+        * Same as {@link #join(Object[], char, StringBuilder)} but escapes the 
delimiter character if found in the tokens.
+        *
+        * @param tokens The tokens to join.
+        * @param d The delimiter.
+        * @param sb The string builder to append the response to.
+        * @return The same string builder passed in as <code>sb</code>.
+        */
+       public static StringBuilder joine(Object[] tokens, char d, 
StringBuilder sb) {
+               if (tokens == null)
+                       return sb;
+               AsciiSet as = getEscapeSet(d);
+               for (int i = 0; i < tokens.length; i++) {
+                       if (i > 0)
+                               sb.append(d);
+                       sb.append(escapeChars(asString(tokens[i]), as));
+               }
+               return sb;
+       }
+
+       /**
         * Join the specified tokens into a delimited string.
         *
         * @param tokens The tokens to join.
@@ -485,6 +529,26 @@ public final class StringUtils {
        }
 
        /**
+        * Same as {@link #join(Collection, char)} but escapes the delimiter if 
found in the tokens.
+        *
+        * @param tokens The tokens to join.
+        * @param d The delimiter.
+        * @return The delimited string.  If <code>tokens</code> is 
<jk>null</jk>, returns <jk>null</jk>.
+        */
+       public static String joine(Collection<?> tokens, char d) {
+               if (tokens == null)
+                       return null;
+               AsciiSet as = getEscapeSet(d);
+               StringBuilder sb = new StringBuilder();
+               for (Iterator<?> iter = tokens.iterator(); iter.hasNext();) {
+                       sb.append(escapeChars(asString(iter.next()), as));
+                       if (iter.hasNext())
+                               sb.append(d);
+               }
+               return sb.toString();
+       }
+
+       /**
         * Joins tokens with newlines.
         *
         * @param tokens The tokens to concatenate.
@@ -541,7 +605,7 @@ public final class StringUtils {
         */
        public static String[] split(String s, char c, int limit) {
 
-               char[] unEscapeChars = new char[]{'\\', c};
+               AsciiSet escapeChars = getEscapeSet(c);
 
                if (s == null)
                        return null;
@@ -558,7 +622,7 @@ public final class StringUtils {
                        if (sArray[i] == '\\') escapeCount++;
                        else if (sArray[i]==c && escapeCount % 2 == 0) {
                                String s2 = new String(sArray, x1, i-x1);
-                               String s3 = unEscapeChars(s2, unEscapeChars);
+                               String s3 = unEscapeChars(s2, escapeChars);
                                l.add(s3.trim());
                                limit--;
                                x1 = i+1;
@@ -566,7 +630,7 @@ public final class StringUtils {
                        if (sArray[i] != '\\') escapeCount = 0;
                }
                String s2 = new String(sArray, x1, sArray.length-x1);
-               String s3 = unEscapeChars(s2, unEscapeChars);
+               String s3 = unEscapeChars(s2, escapeChars);
                l.add(s3.trim());
 
                return l.toArray(new String[l.size()]);
@@ -603,14 +667,10 @@ public final class StringUtils {
         * </p>
         *
         * @param s The string to split.
-        * @param delim The delimiter between the key-value pairs.
-        * @param eq The delimiter between the key and value.
         * @param trim Trim strings after parsing.
         * @return The parsed map.  Never <jk>null</jk>.
         */
-       public static Map<String,String> splitMap(String s, char delim, char 
eq, boolean trim) {
-
-               char[] unEscapeChars = new char[]{'\\', delim, eq};
+       public static Map<String,String> splitMap(String s, boolean trim) {
 
                if (s == null)
                        return null;
@@ -629,33 +689,33 @@ public final class StringUtils {
                int x1 = 0, escapeCount = 0;
                String key = null;
                for (int i = 0; i < sArray.length + 1; i++) {
-                       char c = i == sArray.length ? delim : sArray[i];
+                       char c = i == sArray.length ? ',' : sArray[i];
                        if (c == '\\')
                                escapeCount++;
                        if (escapeCount % 2 == 0) {
                                if (state == S1) {
-                                       if (c == eq) {
+                                       if (c == '=') {
                                                key = s.substring(x1, i);
                                                if (trim)
                                                        key = trim(key);
-                                               key = unEscapeChars(key, 
unEscapeChars);
+                                               key = unEscapeChars(key, 
MAP_ESCAPE_SET);
                                                state = S2;
                                                x1 = i+1;
-                                       } else if (c == delim) {
+                                       } else if (c == ',') {
                                                key = s.substring(x1, i);
                                                if (trim)
                                                        key = trim(key);
-                                               key = unEscapeChars(key, 
unEscapeChars);
+                                               key = unEscapeChars(key, 
MAP_ESCAPE_SET);
                                                m.put(key, "");
                                                state = S1;
                                                x1 = i+1;
                                        }
                                } else if (state == S2) {
-                                       if (c == delim) {
+                                       if (c == ',') {
                                                String val = s.substring(x1, i);
                                                if (trim)
                                                        val = trim(val);
-                                               val = unEscapeChars(val, 
unEscapeChars);
+                                               val = unEscapeChars(val, 
MAP_ESCAPE_SET);
                                                m.put(key, val);
                                                key = null;
                                                x1 = i+1;
@@ -669,6 +729,8 @@ public final class StringUtils {
                return m;
        }
 
+       private static final AsciiSet MAP_ESCAPE_SET = AsciiSet.create(",=\\");
+
        /**
         * Returns <jk>true</jk> if the specified string contains any of the 
specified characters.
         *
@@ -710,7 +772,6 @@ public final class StringUtils {
         *      <br>An empty string results in an empty array.
         */
        public static String[] splitQuoted(String s) {
-               char[] unEscapeChars = new char[]{'\\', '\'', '"'};
 
                if (s == null)
                        return null;
@@ -757,7 +818,7 @@ public final class StringUtils {
                                        if (c == (state == S2 ? '\'' : '"')) {
                                                String s2 = s.substring(mark, 
i);
                                                if (needsUnescape)
-                                                       s2 = unEscapeChars(s2, 
unEscapeChars, '\\');
+                                                       s2 = unEscapeChars(s2, 
QUOTE_ESCAPE_SET);
                                                l.add(s2);
                                                state = S1;
                                                isInEscape = needsUnescape = 
false;
@@ -779,6 +840,8 @@ public final class StringUtils {
                return l.toArray(new String[l.size()]);
        }
 
+       private static final AsciiSet QUOTE_ESCAPE_SET = 
AsciiSet.create("\"'\\");
+
        /**
         * Returns <jk>true</jk> if specified string is <jk>null</jk> or empty.
         *
@@ -848,47 +911,65 @@ public final class StringUtils {
        }
 
        /**
-        * Removes escape characters (\) from the specified characters.
+        * Removes escape characters from the specified characters.
         *
         * @param s The string to remove escape characters from.
-        * @param toEscape The characters escaped.
+        * @param escaped The characters escaped.
         * @return A new string if characters were removed, or the same string 
if not or if the input was <jk>null</jk>.
         */
-       public static String unEscapeChars(String s, char[] toEscape) {
-               return unEscapeChars(s, toEscape, '\\');
-       }
+       public static String unEscapeChars(String s, AsciiSet escaped) {
+               if (s == null || s.length() == 0)
+                       return s;
+               int count = 0;
+               for (int i = 0; i < s.length(); i++)
+                       if (escaped.contains(s.charAt(i)))
+                               count++;
+               if (count == 0)
+                       return s;
+               StringBuffer sb = new StringBuffer(s.length()-count);
+               for (int i = 0; i < s.length(); i++) {
+                       char c = s.charAt(i);
 
-       /**
-        * Removes escape characters (specified by escapeChar) from the 
specified characters.
-        *
-        * @param s The string to remove escape characters from.
-        * @param toEscape The characters escaped.
-        * @param escapeChar The escape character.
-        * @return A new string if characters were removed, or the same string 
if not or if the input was <jk>null</jk>.
-        */
-       public static String unEscapeChars(String s, char[] toEscape, char 
escapeChar) {
-               if (s == null) return null;
-               if (s.length() == 0 || toEscape == null || toEscape.length == 0 
|| escapeChar == 0) return s;
-               StringBuffer sb = new StringBuffer(s.length());
-               char[] sArray = s.toCharArray();
-               for (int i = 0; i < sArray.length; i++) {
-                       char c = sArray[i];
-
-                       if (c == escapeChar) {
-                               if (i+1 != sArray.length) {
-                                       char c2 = sArray[i+1];
-                                       boolean isOneOf = false;
-                                       for (int j = 0; j < toEscape.length && 
! isOneOf; j++)
-                                               isOneOf = (c2 == toEscape[j]);
-                                       if (isOneOf) {
+                       if (c == '\\') {
+                               if (i+1 != s.length()) {
+                                       char c2 = s.charAt(i+1);
+                                       if (escaped.contains(c2)) {
                                                i++;
-                                       } else if (c2 == escapeChar) {
-                                               sb.append(escapeChar);
+                                       } else if (c2 == '\\') {
+                                               sb.append('\\');
                                                i++;
                                        }
                                }
                        }
-                       sb.append(sArray[i]);
+                       sb.append(s.charAt(i));
+               }
+               return sb.toString();
+       }
+
+       /**
+        * Escapes the specified characters in the string.
+        *
+        * @param s The string with characters to escape.
+        * @param escaped The characters to escape.
+        * @return The string with characters escaped, or the same string if no 
escapable characters were found.
+        */
+       public static String escapeChars(String s, AsciiSet escaped) {
+               if (s == null || s.length() == 0)
+                       return s;
+
+               int count = 0;
+               for (int i = 0; i < s.length(); i++)
+                       if (escaped.contains(s.charAt(i)))
+                               count++;
+               if (count == 0)
+                       return s;
+
+               StringBuffer sb = new StringBuffer(s.length() + count);
+               for (int i = 0; i < s.length(); i++) {
+                       char c = s.charAt(i);
+                       if (escaped.contains(c))
+                               sb.append('\\');
+                       sb.append(c);
                }
                return sb.toString();
        }
diff --git 
a/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/VarResolverSession.java
 
b/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/VarResolverSession.java
index b386e85..fb8c6f0 100644
--- 
a/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/VarResolverSession.java
+++ 
b/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/VarResolverSession.java
@@ -18,6 +18,8 @@ import java.io.*;
 import java.lang.reflect.*;
 import java.util.*;
 
+import org.apache.juneau.internal.*;
+
 /**
  * A var resolver session that combines a {@link VarResolver} with one or more 
session objects.
  *
@@ -306,7 +308,7 @@ public class VarResolverSession {
                                        state = S3;
                                } else if (c < 'A' || c > 'z' || (c > 'Z' && c 
< 'a')) {  // False trigger "$X "
                                        if (hasInnerEscapes)
-                                               
out.append(unEscapeChars(s.substring(x, i+1), new char[]{'\\','{'}));
+                                               
out.append(unEscapeChars(s.substring(x, i+1), AS1));
                                        else
                                                out.append(s, x, i+1);
                                        x = i + 1;
@@ -330,7 +332,7 @@ public class VarResolverSession {
                                                Var r = getVar(varType);
                                                if (r == null) {
                                                        if (hasInnerEscapes)
-                                                               
out.append(unEscapeChars(s.substring(x2, i+1), new char[]{'\\','$','{','}'}));
+                                                               
out.append(unEscapeChars(s.substring(x2, i+1), AS2));
                                                        else
                                                                out.append(s, 
x2, i+1);
                                                        x = i+1;
@@ -364,12 +366,16 @@ public class VarResolverSession {
                if (isInEscape)
                        out.append('\\');
                else if (state == S2)
-                       out.append('$').append(unEscapeChars(s.substring(x+1), 
new char[]{'{', '\\'}));
+                       out.append('$').append(unEscapeChars(s.substring(x+1), 
AS1));
                else if (state == S3)
-                       
out.append('$').append(varType).append('{').append(unEscapeChars(s.substring(x+1),
 new char[]{'\\','$','{','}'}));
+                       
out.append('$').append(varType).append('{').append(unEscapeChars(s.substring(x+1),
 AS2));
                return out;
        }
 
+       private static final AsciiSet
+               AS1 = AsciiSet.create("\\{"),
+               AS2 = AsciiSet.create("\\${}")
+       ;
 
        /**
         * Returns the session object with the specified name.

Reply via email to