http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e4dfdf81/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingParserTest.java
----------------------------------------------------------------------
diff --git 
a/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingParserTest.java
 
b/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingParserTest.java
new file mode 100755
index 0000000..03198f8
--- /dev/null
+++ 
b/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingParserTest.java
@@ -0,0 +1,1000 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.urlencoding;
+
+import static org.apache.juneau.TestUtils.*;
+import static org.junit.Assert.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.parser.*;
+import org.junit.*;
+
+@SuppressWarnings({"rawtypes","unchecked","javadoc"})
+public class UrlEncodingParserTest {
+
+       static UrlEncodingParser p = UrlEncodingParser.DEFAULT;
+
+       
//====================================================================================================
+       // Basic test
+       
//====================================================================================================
+       @Test
+       public void testBasic() throws Exception {
+
+               String t;
+               Map m;
+               List l;
+
+               // Simple string
+               // Top level
+               t = "_value=a";
+               assertEquals("a", p.parse(t, Object.class));
+               assertEquals("a", p.parse(t, String.class));
+               t = "_value=(a)";
+               assertEquals("a", p.parse(t, String.class));
+               assertEquals("a", p.parse(t, Object.class));
+               t = "_value=$s(a)";
+               assertEquals("a", p.parse(t, String.class));
+
+               t = "a";
+               assertEquals("a", p.parseParameter(t, Object.class));
+               assertEquals("a", p.parseParameter(t, String.class));
+               t = "(a)";
+               assertEquals("a", p.parseParameter(t, String.class));
+               assertEquals("a", p.parseParameter(t, Object.class));
+               t = "$s(a)";
+               assertEquals("a", p.parseParameter(t, String.class));
+
+               // 2nd level
+               t = "?a=a";
+               assertEquals("a", p.parse(t, Map.class).get("a"));
+
+               // Simple map
+               // Top level
+               t = "?a=b&c=$n(123)&d=$b(false)&e=$b(true)&f=%00";
+               m = p.parse(t, Map.class);
+               assertEquals("b", m.get("a"));
+               assertTrue(m.get("c") instanceof Number);
+               assertEquals(123, m.get("c"));
+               assertTrue(m.get("d") instanceof Boolean);
+               assertEquals(Boolean.FALSE, m.get("d"));
+               assertTrue(m.get("e") instanceof Boolean);
+               assertEquals(Boolean.TRUE, m.get("e"));
+               assertNull(m.get("f"));
+
+               t = "$o(a=b,c=$n(123),d=$b(false),e=$b(true),f=%00)";
+               m = p.parseParameter(t, Map.class);
+               assertEquals("b", m.get("a"));
+               assertTrue(m.get("c") instanceof Number);
+               assertEquals(123, m.get("c"));
+               assertTrue(m.get("d") instanceof Boolean);
+               assertEquals(Boolean.FALSE, m.get("d"));
+               assertTrue(m.get("e") instanceof Boolean);
+               assertEquals(Boolean.TRUE, m.get("e"));
+               assertEquals("%00", m.get("f"));
+
+               t = "$o(a=b,c=$n(123),d=$b(false),e=$b(true),f=\u0000)";
+               m = p.parseParameter(t, Map.class);
+               assertTrue(m.containsKey("f"));
+               assertNull(m.get("f"));
+
+               t = "?a=true";
+               m = p.parseMap(t, HashMap.class, String.class, Boolean.class);
+               assertTrue(m.get("a") instanceof Boolean);
+               assertEquals("true", m.get("a").toString());
+
+               // null
+               // Top level
+               t = "_value=%00";
+               assertNull(p.parse(t, Object.class));
+               t = "\u0000";
+               assertNull(p.parseParameter(t, Object.class));
+               t = "%00";
+               assertEquals("%00", p.parseParameter(t, Object.class));
+
+               // 2nd level
+               t = "?%00=%00";
+               m = p.parse(t, Map.class);
+               assertTrue(m.containsKey(null));
+               assertNull(m.get(null));
+
+               t = "?\u0000=\u0000";
+               m = p.parse(t, Map.class);
+               assertTrue(m.containsKey(null));
+               assertNull(m.get(null));
+
+               // 3rd level
+               t = "?%00=$o(%00=%00)";
+               m = p.parse(t, Map.class);
+               assertTrue(((Map)m.get(null)).containsKey(null));
+               assertNull(((Map)m.get(null)).get(null));
+
+               // Empty array
+               // Top level
+               t = "_value=$a()";
+               l = (List)p.parse(t, Object.class);
+               assertTrue(l.isEmpty());
+               t = "_value=()";
+               l = p.parse(t, List.class);
+               assertTrue(l.isEmpty());
+               t = "$a()";
+               l = (List)p.parseParameter(t, Object.class);
+               assertTrue(l.isEmpty());
+               t = "()";
+               l = p.parseParameter(t, List.class);
+               assertTrue(l.isEmpty());
+
+               // 2nd level in map
+               t = "?x=$a()";
+               m = p.parseMap(t, HashMap.class, String.class, List.class);
+               assertTrue(m.containsKey("x"));
+               assertTrue(((List)m.get("x")).isEmpty());
+               m = (Map)p.parse(t, Object.class);
+               assertTrue(m.containsKey("x"));
+               assertTrue(((List)m.get("x")).isEmpty());
+               t = "?x=()";
+               m = p.parseMap(t, HashMap.class, String.class, List.class);
+               assertTrue(m.containsKey("x"));
+               assertTrue(((List)m.get("x")).isEmpty());
+
+               // Empty 2 dimensional array
+               t = "_value=$a($a())";
+               l = (List)p.parse(t, Object.class);
+               assertTrue(l.size() == 1);
+               l = (List)l.get(0);
+               assertTrue(l.isEmpty());
+               t = "0=()";
+               l = p.parseCollection(t, LinkedList.class, List.class);
+               assertTrue(l.size() == 1);
+               l = (List)l.get(0);
+               assertTrue(l.isEmpty());
+               t = "$a($a())";
+               l = (List)p.parseParameter(t, Object.class);
+               assertTrue(l.size() == 1);
+               l = (List)l.get(0);
+               assertTrue(l.isEmpty());
+               t = "(())";
+               l = (List)p.parseParameter(t, 
p.getBeanContext().getCollectionClassMeta(LinkedList.class, List.class));
+               assertTrue(l.size() == 1);
+               l = (List)l.get(0);
+               assertTrue(l.isEmpty());
+
+               // Array containing empty string
+               // Top level
+               t = "_value=$a(())";
+               l = (List)p.parse(t, Object.class);
+               assertTrue(l.size() == 1);
+               assertEquals("", l.get(0));
+               t = "0=()";
+               l = p.parseCollection(t, List.class, String.class);
+               assertTrue(l.size() == 1);
+               assertEquals("", l.get(0));
+               t = "$a(())";
+               l = (List)p.parseParameter(t, Object.class);
+               assertTrue(l.size() == 1);
+               assertEquals("", l.get(0));
+               t = "(())";
+               l = (List)p.parseParameter(t, 
p.getBeanContext().getCollectionClassMeta(List.class, String.class));
+               assertTrue(l.size() == 1);
+               assertEquals("", l.get(0));
+
+               // 2nd level
+               t = "?()=$a(())";
+               m = (Map)p.parse(t, Object.class);
+               assertEquals("", ((List)m.get("")).get(0));
+               t = "?()=(())";
+               m = p.parseMap(t, HashMap.class, String.class, List.class);
+               assertEquals("", ((List)m.get("")).get(0));
+
+               // Array containing 3 empty strings
+               t = "_value=$a(,,)";
+               l = (List)p.parse(t, Object.class);
+               assertTrue(l.size() == 3);
+               assertEquals("", l.get(0));
+               assertEquals("", l.get(1));
+               assertEquals("", l.get(2));
+               t = "0=&1=&2=";
+               l = p.parseCollection(t, List.class, Object.class);
+               assertTrue(l.size() == 3);
+               assertEquals("", l.get(0));
+               assertEquals("", l.get(1));
+               assertEquals("", l.get(2));
+               t = "$a(,,)";
+               l = (List)p.parseParameter(t, Object.class);
+               assertTrue(l.size() == 3);
+               assertEquals("", l.get(0));
+               assertEquals("", l.get(1));
+               assertEquals("", l.get(2));
+               t = "(,,)";
+               l = (List)p.parseParameter(t, 
p.getBeanContext().getCollectionClassMeta(List.class, Object.class));
+               assertTrue(l.size() == 3);
+               assertEquals("", l.get(0));
+               assertEquals("", l.get(1));
+               assertEquals("", l.get(2));
+
+               // String containing \u0000
+               // Top level
+               t = "_value=$s(\u0000)";
+               assertEquals("\u0000", p.parse(t, Object.class));
+               t = "_value=(\u0000)";
+               assertEquals("\u0000", p.parse(t, String.class));
+               assertEquals("\u0000", p.parse(t, Object.class));
+               t = "$s(\u0000)";
+               assertEquals("\u0000", p.parseParameter(t, Object.class));
+               t = "(\u0000)";
+               assertEquals("\u0000", p.parseParameter(t, String.class));
+               assertEquals("\u0000", p.parseParameter(t, Object.class));
+
+               // 2nd level
+               t = "?(\u0000)=(\u0000)";
+               m = (Map)p.parse(t, Object.class);
+               assertTrue(m.size() == 1);
+               assertEquals("\u0000", m.get("\u0000"));
+               m = p.parseMap(t, HashMap.class, String.class, Object.class);
+               assertTrue(m.size() == 1);
+               assertEquals("\u0000", m.get("\u0000"));
+
+               // Boolean
+               // Top level
+               t = "_value=$b(false)";
+               Boolean b = (Boolean)p.parse(t, Object.class);
+               assertEquals(Boolean.FALSE, b);
+               b = p.parse(t, Boolean.class);
+               assertEquals(Boolean.FALSE, b);
+               t = "_value=false";
+               b = p.parse(t, Boolean.class);
+               assertEquals(Boolean.FALSE, b);
+               t = "$b(false)";
+               b = (Boolean)p.parseParameter(t, Object.class);
+               assertEquals(Boolean.FALSE, b);
+               b = p.parseParameter(t, Boolean.class);
+               assertEquals(Boolean.FALSE, b);
+               t = "false";
+               b = p.parseParameter(t, Boolean.class);
+               assertEquals(Boolean.FALSE, b);
+
+               // 2nd level
+               t = "?x=$b(false)";
+               m = (Map)p.parse(t, Object.class);
+               assertEquals(Boolean.FALSE, m.get("x"));
+               t = "?x=false";
+               m = p.parseMap(t, HashMap.class, String.class, Boolean.class);
+               assertEquals(Boolean.FALSE, m.get("x"));
+
+               // Number
+               // Top level
+               t = "_value=$n(123)";
+               Integer i = (Integer)p.parse(t, Object.class);
+               assertEquals(123, i.intValue());
+               i = p.parse(t, Integer.class);
+               assertEquals(123, i.intValue());
+               Double d = p.parse(t, Double.class);
+               assertEquals(123, d.intValue());
+               Float f = p.parse(t, Float.class);
+               assertEquals(123, f.intValue());
+               t = "_value=123";
+               i = p.parse(t, Integer.class);
+               assertEquals(123, i.intValue());
+               t = "$n(123)";
+               i = (Integer)p.parseParameter(t, Object.class);
+               assertEquals(123, i.intValue());
+               i = p.parseParameter(t, Integer.class);
+               assertEquals(123, i.intValue());
+               d = p.parseParameter(t, Double.class);
+               assertEquals(123, d.intValue());
+               f = p.parseParameter(t, Float.class);
+               assertEquals(123, f.intValue());
+               t = "123";
+               i = p.parseParameter(t, Integer.class);
+               assertEquals(123, i.intValue());
+
+               // 2nd level
+               t = "?x=$n(123)";
+               m = (Map)p.parse(t, Object.class);
+               assertEquals(123, ((Integer)m.get("x")).intValue());
+               m = p.parseMap(t, HashMap.class, String.class, Double.class);
+               assertEquals(123, ((Double)m.get("x")).intValue());
+
+               // Unencoded chars
+               // Top level
+               t = "_value=x;/?:@-_.!*'";
+               assertEquals("x;/?:@-_.!*'", p.parse(t, Object.class));
+               t = "x;/?:@-_.!*'";
+               assertEquals("x;/?:@-_.!*'", p.parseParameter(t, Object.class));
+
+               // 2nd level
+               t = "?x;/?:@-_.!*'=x;/?:@-_.!*'";
+               m = (Map)p.parse(t, Object.class);
+               assertEquals("x;/?:@-_.!*'", m.get("x;/?:@-_.!*'"));
+               m = p.parseMap(t, HashMap.class, String.class, Object.class);
+               assertEquals("x;/?:@-_.!*'", m.get("x;/?:@-_.!*'"));
+               m = p.parseMap(t, HashMap.class, String.class, String.class);
+               assertEquals("x;/?:@-_.!*'", m.get("x;/?:@-_.!*'"));
+
+               // Encoded chars
+               // Top level
+               t = "_value=x{}|\\^[]`<>#%\"&+";
+               try {
+                       assertEquals("x{}|\\^[]`<>#%\"&+", p.parse(t, 
Object.class));
+                       fail("Expected parse exception from invalid hex 
sequence.");
+               } catch (ParseException e) {
+                       // Good.
+               }
+               t = "_value=x%7B%7D%7C%5C%5E%5B%5D%60%3C%3E%23%25%22%26%2B";
+               assertEquals("x{}|\\^[]`<>#%\"&+", p.parse(t, Object.class));
+               assertEquals("x{}|\\^[]`<>#%\"&+", p.parse(t, String.class));
+               t = "x{}|\\^[]`<>#%\"&+";
+               assertEquals("x{}|\\^[]`<>#%\"&+", p.parseParameter(t, 
Object.class));
+               t = "x%7B%7D%7C%5C%5E%5B%5D%60%3C%3E%23%25%22%26%2B";
+               assertEquals("x%7B%7D%7C%5C%5E%5B%5D%60%3C%3E%23%25%22%26%2B", 
p.parseParameter(t, Object.class));
+               assertEquals("x%7B%7D%7C%5C%5E%5B%5D%60%3C%3E%23%25%22%26%2B", 
p.parseParameter(t, String.class));
+
+               // 2nd level
+               t = "?x{}|\\^[]`<>#%\"&+=x{}|\\^[]`<>#%\"&+";
+               try {
+                       m = (Map)p.parse(t, Object.class);
+                       fail("Expected parse exception from invalid hex 
sequence.");
+               } catch (ParseException e) {
+                       // Good.
+               }
+               t = 
"?x%7B%7D%7C%5C%5E%5B%5D%60%3C%3E%23%25%22%26%2B=x%7B%7D%7C%5C%5E%5B%5D%60%3C%3E%23%25%22%26%2B";
+               m = (Map)p.parse(t, Object.class);
+               assertEquals("x{}|\\^[]`<>#%\"&+", m.get("x{}|\\^[]`<>#%\"&+"));
+
+               // Special chars
+               // These characters are escaped and not encoded.
+               // Top level
+               t = "_value=x~$~,~(~)";
+               assertEquals("x$,()", p.parse(t, Object.class));
+               t = "x~$~,~(~)";
+               assertEquals("x$,()", p.parseParameter(t, Object.class));
+               t = "_value=x~~$~~,~~(~~)";
+               assertEquals("x~$~,~(~)", p.parse(t, Object.class));
+               t = "x~~$~~,~~(~~)";
+               assertEquals("x~$~,~(~)", p.parseParameter(t, Object.class));
+
+               // At secondary levels, these characters are escaped and not 
encoded.
+               // 2nd level
+               t = "?x~$~,~(~)=x~$~,~(~)";
+               m = (Map)p.parse(t, Object.class);
+               assertEquals("x$,()", m.get("x$,()"));
+               t = "?x~~$~~,~~(~~)=x~~$~~,~~(~~)";
+               m = (Map)p.parse(t, Object.class);
+               assertEquals("x~$~,~(~)", m.get("x~$~,~(~)"));
+
+               // Equals sign
+               // Gets encoded at top level, and encoded+escaped at 2nd level.
+               // Top level
+               t = "_value=x=";
+               assertEquals("x=", p.parse(t, Object.class));
+               t = "_value=x%3D";
+               assertEquals("x=", p.parse(t, Object.class));
+               t = "x=";
+               assertEquals("x=", p.parseParameter(t, Object.class));
+               t = "x%3D";
+               assertEquals("x%3D", p.parseParameter(t, Object.class));
+
+               // 2nd level
+               t = "?x~%3D=x~%3D";
+               m = (Map)p.parse(t, Object.class);
+               assertEquals("x=", m.get("x="));
+               t = "?x~~%3D=x~~%3D";
+               m = (Map)p.parse(t, Object.class);
+               assertEquals("x~=", m.get("x~="));
+
+               // String starting with parenthesis
+               // Top level
+               t = "_value=~(~)";
+               assertEquals("()", p.parse(t, Object.class));
+               assertEquals("()", p.parse(t, String.class));
+               t = "_value=(~(~))";
+               assertEquals("()", p.parse(t, Object.class));
+               assertEquals("()", p.parse(t, String.class));
+               t = "_value=(~(~))";
+               assertEquals("()", p.parse(t, Object.class));
+               assertEquals("()", p.parse(t, String.class));
+               t = "~(~)";
+               assertEquals("()", p.parseParameter(t, Object.class));
+               assertEquals("()", p.parseParameter(t, String.class));
+               t = "(~(~))";
+               assertEquals("()", p.parseParameter(t, Object.class));
+               assertEquals("()", p.parseParameter(t, String.class));
+               t = "(~(~))";
+               assertEquals("()", p.parseParameter(t, Object.class));
+               assertEquals("()", p.parseParameter(t, String.class));
+
+               // 2nd level
+               t = "?(~(~))=(~(~))";
+               m = (Map)p.parse(t, Object.class);
+               assertEquals("()", m.get("()"));
+               t = "?(~(~))=(~(~))";
+               m = p.parseMap(t, HashMap.class, String.class, Object.class);
+               assertEquals("()", m.get("()"));
+
+               // String starting with $
+               // Top level
+               t = "_value=(~$a)";
+               assertEquals("$a", p.parse(t, Object.class));
+               t = "_value=(~$a)";
+               assertEquals("$a", p.parse(t, Object.class));
+               t = "(~$a)";
+               assertEquals("$a", p.parseParameter(t, Object.class));
+               t = "(~$a)";
+               assertEquals("$a", p.parseParameter(t, Object.class));
+
+               // 2nd level
+               t = "?(~$a)=(~$a)";
+               m = (Map)p.parse(t, Object.class);
+               assertEquals("$a", m.get("$a"));
+               m = p.parseMap(t, HashMap.class, String.class, Object.class);
+               assertEquals("$a", m.get("$a"));
+
+               // Blank string
+               // Top level
+               t = "_value=";
+               assertEquals("", p.parse(t, Object.class));
+               t = "";
+               assertEquals("", p.parseParameter(t, Object.class));
+
+               // 2nd level
+               t = "?=";
+               m = (Map)p.parse(t, Object.class);
+               assertEquals("", m.get(""));
+               m = p.parseMap(t, HashMap.class, String.class, Object.class);
+               assertEquals("", m.get(""));
+
+               // 3rd level
+               t = "?=$o(=)";
+               m = (Map)p.parse(t, Object.class);
+               assertEquals("", ((Map)m.get("")).get(""));
+               t = "?=(=)";
+               m = p.parseMap(t, HashMap.class, String.class, HashMap.class);
+               assertEquals("", ((Map)m.get("")).get(""));
+
+               // Newline character
+               // Top level
+               t = "_value=(%0A)";
+               assertEquals("\n", p.parse(t, Object.class));
+               t = "(%0A)";
+               assertEquals("%0A", p.parseParameter(t, Object.class));
+               t = "(\n)";
+               assertEquals("\n", p.parseParameter(t, Object.class));
+
+               // 2nd level
+               t = "?%0A=(%0A)";
+               m = (Map)p.parse(t, Object.class);
+               assertEquals("\n", m.get("\n"));
+
+               // 3rd level
+               t = "?%0A=$o((%0A)=(%0A))";
+               m = (Map)p.parse(t, Object.class);
+               assertEquals("\n", ((Map)m.get("\n")).get("\n"));
+       }
+
+       
//====================================================================================================
+       // Unicode character test
+       
//====================================================================================================
+       @Test
+       public void testUnicodeChars() throws Exception {
+               String t;
+               Map m;
+
+               // 2-byte UTF-8 character
+               // Top level
+               t = "_value=¢";
+               assertEquals("¢", p.parse(t, Object.class));
+               assertEquals("¢", p.parse(t, String.class));
+               t = "_value=%C2%A2";
+               assertEquals("¢", p.parse(t, Object.class));
+               assertEquals("¢", p.parse(t, String.class));
+               t = "¢";
+               assertEquals("¢", p.parseParameter(t, Object.class));
+               assertEquals("¢", p.parseParameter(t, String.class));
+               t = "%C2%A2";
+               assertEquals("%C2%A2", p.parseParameter(t, Object.class));
+               assertEquals("%C2%A2", p.parseParameter(t, String.class));
+
+               // 2nd level
+               t = "?%C2%A2=%C2%A2";
+               m = (Map)p.parse(t, Object.class);
+               assertEquals("¢", m.get("¢"));
+
+               // 3rd level
+               t = "?%C2%A2=$o(%C2%A2=%C2%A2)";
+               m = (Map)p.parse(t, Object.class);
+               assertEquals("¢", ((Map)m.get("¢")).get("¢"));
+
+               // 3-byte UTF-8 character
+               // Top level
+               t = "_value=€";
+               assertEquals("€", p.parse(t, Object.class));
+               assertEquals("€", p.parse(t, String.class));
+               t = "_value=%E2%82%AC";
+               assertEquals("€", p.parse(t, Object.class));
+               assertEquals("€", p.parse(t, String.class));
+               t = "€";
+               assertEquals("€", p.parseParameter(t, Object.class));
+               assertEquals("€", p.parseParameter(t, String.class));
+               t = "%E2%82%AC";
+               assertEquals("%E2%82%AC", p.parseParameter(t, Object.class));
+               assertEquals("%E2%82%AC", p.parseParameter(t, String.class));
+
+               // 2nd level
+               t = "?%E2%82%AC=%E2%82%AC";
+               m = (Map)p.parse(t, Object.class);
+               assertEquals("€", m.get("€"));
+
+               // 3rd level
+               t = "?%E2%82%AC=$o(%E2%82%AC=%E2%82%AC)";
+               m = (Map)p.parse(t, Object.class);
+               assertEquals("€", ((Map)m.get("€")).get("€"));
+
+               // 4-byte UTF-8 character
+               // Top level
+               t = "_value=𤭢";
+               assertEquals("𤭢", p.parse(t, Object.class));
+               assertEquals("𤭢", p.parse(t, String.class));
+               t = "_value=%F0%A4%AD%A2";
+               assertEquals("𤭢", p.parse(t, Object.class));
+               assertEquals("𤭢", p.parse(t, String.class));
+               t = "𤭢";
+               assertEquals("𤭢", p.parseParameter(t, Object.class));
+               assertEquals("𤭢", p.parseParameter(t, String.class));
+               t = "%F0%A4%AD%A2";
+               assertEquals("%F0%A4%AD%A2", p.parseParameter(t, Object.class));
+               assertEquals("%F0%A4%AD%A2", p.parseParameter(t, String.class));
+
+               // 2nd level
+               t = "?%F0%A4%AD%A2=%F0%A4%AD%A2";
+               m = (Map)p.parse(t, Object.class);
+               assertEquals("𤭢", m.get("𤭢"));
+
+               // 3rd level
+               t = "?%F0%A4%AD%A2=$o(%F0%A4%AD%A2=%F0%A4%AD%A2)";
+               m = (Map)p.parse(t, Object.class);
+               assertEquals("𤭢", ((Map)m.get("𤭢")).get("𤭢"));
+       }
+
+       
//====================================================================================================
+       // Test simple bean
+       
//====================================================================================================
+       @Test
+       public void testSimpleBean() throws Exception {
+               UrlEncodingParser p = UrlEncodingParser.DEFAULT;
+               A t;
+
+               String s = "?f1=foo&f2=123";
+               t = p.parse(s, A.class);
+               assertEquals("foo", t.f1);
+               assertEquals(123, t.f2);
+
+               s = "(f1=foo,f2=123)";
+               t = p.parseParameter(s, A.class);
+               assertEquals("foo", t.f1);
+               assertEquals(123, t.f2);
+
+               s = "$o(f1=foo,f2=123)";
+               t = p.parseParameter(s, A.class);
+               assertEquals("foo", t.f1);
+               assertEquals(123, t.f2);
+       }
+
+       public static class A {
+               public String f1;
+               public int f2;
+       }
+
+       
//====================================================================================================
+       // Test URL-encoded strings with no-value parameters.
+       
//====================================================================================================
+       @Test
+       public void testNoValues() throws Exception {
+               UrlEncodingParser p = UrlEncodingParser.DEFAULT;
+               ObjectMap m;
+
+               String s = "?f1";
+               m = p.parse(s, ObjectMap.class);
+               assertTrue(m.containsKey("f1"));
+               assertNull(m.get("f1"));
+               s = "?f1=f2&f3";
+               m = p.parse(s, ObjectMap.class);
+               assertEquals("f2", m.get("f1"));
+               assertTrue(m.containsKey("f3"));
+               assertNull(m.get("f3"));
+       }
+
+       
//====================================================================================================
+       // Test URL-encoded strings parsed into plain-text values using 
UrlEncodingParser.parseIntoSimpleMap().
+       
//====================================================================================================
+       @Test
+       public void testParseIntoSimpleMap() throws Exception {
+               UrlEncodingParser p = UrlEncodingParser.DEFAULT;
+               Map<String,String[]> m;
+
+               String s = 
"?f1=,()=&f2a=$b(true)&f2b=true&f3a=$n(123)&f3b=123&f4=$s(foo)";
+               m = p.parseIntoSimpleMap(s);
+               assertEquals(",()=", m.get("f1")[0]);
+               assertEquals("$b(true)", m.get("f2a")[0]);
+               assertEquals("true", m.get("f2b")[0]);
+               assertEquals("$n(123)", m.get("f3a")[0]);
+               assertEquals("123", m.get("f3b")[0]);
+               assertEquals("$s(foo)", m.get("f4")[0]);
+
+               s = "f1=v1&=";
+               m = p.parseIntoSimpleMap(s);
+               assertEquals("v1", m.get("f1")[0]);
+               assertEquals("", m.get("")[0]);
+
+               s = "f1=v1&f2&f3";
+               m = p.parseIntoSimpleMap(s);
+               assertEquals("v1", m.get("f1")[0]);
+               assertTrue(m.containsKey("f2"));
+               assertTrue(m.containsKey("f3"));
+               assertNull(m.get("f2"));
+               assertNull(m.get("f3"));
+       }
+
+       
//====================================================================================================
+       // Test parsing URL-encoded strings with multiple values.
+       
//====================================================================================================
+       @Test
+       public void testParseIntoSimpleMapMultiValues() throws Exception {
+               UrlEncodingParser p = UrlEncodingParser.DEFAULT;
+               Map<String,String[]> m;
+
+               String s = "?f1&f1&f2&f2=abc&f2=def&f2";
+               m = p.parseIntoSimpleMap(s);
+               assertObjectEquals("{f1:null,f2:['abc','def']}", m);
+       }
+
+       @Test
+       public void testEmptyString() throws Exception {
+               UrlEncodingParser p = UrlEncodingParser.DEFAULT;
+
+               String s = "";
+               B b = p.parse(s, B.class);
+               assertEquals("f1", b.f1);
+       }
+
+       public static class B {
+               public String f1 = "f1";
+       }
+
+       
//====================================================================================================
+       // Test comma-delimited list parameters.
+       
//====================================================================================================
+       @Test
+       public void testCommaDelimitedLists() throws Exception {
+               UrlEncodingParser p = UrlEncodingParser.DEFAULT;
+
+               String s = "?f1=1,2,3&f2=a,b,c&f3=true,false&f4=&f5";
+               C c = p.parse(s, C.class);
+               
assertObjectEquals("{f1:[1,2,3],f2:['a','b','c'],f3:[true,false],f4:[]}", c);
+       }
+
+       public static class C {
+               public int[] f1;
+               public String[] f2;
+               public boolean[] f3;
+               public String[] f4;
+               public String[] f5;
+       }
+
+       
//====================================================================================================
+       // Test comma-delimited list parameters with special characters.
+       
//====================================================================================================
+       @Test
+       public void testCommaDelimitedListsWithSpecialChars() throws Exception {
+               UrlEncodingParser p = UrlEncodingParser.DEFAULT;
+               String s;
+               C1 c;
+
+               // In the string below, the ~ character should not be 
interpreted as an escape.
+               s = "?f1=a~b,a~b";
+               c = p.parse(s, C1.class);
+               assertObjectEquals("{f1:['a~b','a~b']}", c);
+
+               s = "?f1=(a~b,a~b)";
+               c = p.parse(s, C1.class);
+               assertObjectEquals("{f1:['a~b','a~b']}", c);
+
+               s = "?f1=((a~b),(a~b))";
+               c = p.parse(s, C1.class);
+               assertObjectEquals("{f1:['a~b','a~b']}", c);
+
+               s = "?f1=($s(a~b),$s(a~b))";
+               c = p.parse(s, C1.class);
+               assertObjectEquals("{f1:['a~b','a~b']}", c);
+
+               s = "?f1=$a($s(a~b),$s(a~b))";
+               c = p.parse(s, C1.class);
+               assertObjectEquals("{f1:['a~b','a~b']}", c);
+
+               s = "?f1=~~,~~";
+               c = p.parse(s, C1.class);
+               assertObjectEquals("{f1:['~','~']}", c);
+
+               s = "?f1=(~~,~~)";
+               c = p.parse(s, C1.class);
+               assertObjectEquals("{f1:['~','~']}", c);
+
+               s = "?f1=(~~~~~~,~~~~~~)";
+               c = p.parse(s, C1.class);
+               assertObjectEquals("{f1:['~~~','~~~']}", c);
+
+               s = "?f1=((~~~~~~),(~~~~~~))";
+               c = p.parse(s, C1.class);
+               assertObjectEquals("{f1:['~~~','~~~']}", c);
+
+               // The ~ should be treated as an escape if followed by any of 
the following characters:  ,()~=
+               s = "?f1=~,~(~)~~~=~$,~,~(~)~~~=~$";
+               c = p.parse(s, C1.class);
+               assertObjectEquals("{f1:[',()~=$',',()~=$']}", c);
+
+               s = "?f1=(~,~(~)~~~=~$,~,~(~)~~~=~$)";
+               c = p.parse(s, C1.class);
+               assertObjectEquals("{f1:[',()~=$',',()~=$']}", c);
+
+               s = "?f1=((~,~(~)~~~=~$),(~,~(~)~~~=~$))";
+               c = p.parse(s, C1.class);
+               assertObjectEquals("{f1:[',()~=$',',()~=$']}", c);
+
+               s = "?a~b=a~b";
+               ObjectMap m = p.parse(s, ObjectMap.class);
+               assertEquals("{'a~b':'a~b'}", m.toString());
+
+               s = "?(a~b)=(a~b)";
+               m = p.parse(s, ObjectMap.class);
+               assertEquals("{'a~b':'a~b'}", m.toString());
+
+               s = "?$s(a~b)=$s(a~b)";
+               m = p.parse(s, ObjectMap.class);
+               assertEquals("{'a~b':'a~b'}", m.toString());
+
+               s = "?~~=~~";
+               m = p.parse(s, ObjectMap.class);
+               assertEquals("{'~':'~'}", m.toString());
+
+               s = "?(~~)=(~~)";
+               m = p.parse(s, ObjectMap.class);
+               assertEquals("{'~':'~'}", m.toString());
+
+               s = "?~~~~~~=~~~~~~";
+               m = p.parse(s, ObjectMap.class);
+               assertEquals("{'~~~':'~~~'}", m.toString());
+
+               s = "?(~~~~~~)=(~~~~~~)";
+               m = p.parse(s, ObjectMap.class);
+               assertEquals("{'~~~':'~~~'}", m.toString());
+
+               s = "?$s(~~~~~~)=$s(~~~~~~)";
+               m = p.parse(s, ObjectMap.class);
+               assertEquals("{'~~~':'~~~'}", m.toString());
+
+               s = "?~,~(~)~~~=~$=~,~(~)~~~=~$";
+               m = p.parse(s, ObjectMap.class);
+               assertEquals("{',()~=$':',()~=$'}", m.toString());
+
+               s = "?(~,~(~)~~~=~$)=(~,~(~)~~~=~$)";
+               m = p.parse(s, ObjectMap.class);
+               assertEquals("{',()~=$':',()~=$'}", m.toString());
+
+               s = "?$s(~,~(~)~~~=~$)=$s(~,~(~)~~~=~$)";
+               m = p.parse(s, ObjectMap.class);
+               assertEquals("{',()~=$':',()~=$'}", m.toString());
+
+               s = 
"?%7E%2C%7E%28%7E%29%7E%7E%7E%3D%7E%24=%7E%2C%7E%28%7E%29%7E%7E%7E%3D%7E%24";
+               m = p.parse(s, ObjectMap.class);
+               assertEquals("{',()~=$':',()~=$'}", m.toString());
+
+               s = 
"?(%7E%2C%7E%28%7E%29%7E%7E%7E%3D%7E%24)=(%7E%2C%7E%28%7E%29%7E%7E%7E%3D%7E%24)";
+               m = p.parse(s, ObjectMap.class);
+               assertEquals("{',()~=$':',()~=$'}", m.toString());
+
+               s = 
"?$s(%7E%2C%7E%28%7E%29%7E%7E%7E%3D%7E%24)=$s(%7E%2C%7E%28%7E%29%7E%7E%7E%3D%7E%24)";
+               m = p.parse(s, ObjectMap.class);
+               assertEquals("{',()~=$':',()~=$'}", m.toString());
+       }
+
+       public static class C1 {
+               public String[] f1;
+       }
+
+       
//====================================================================================================
+       // Test comma-delimited list parameters.
+       
//====================================================================================================
+       @Test
+       public void testWhitespace() throws Exception {
+               UrlEncodingParser p = UrlEncodingParser.DEFAULT_WS_AWARE;
+               String s;
+               ObjectMap m;
+
+               s = "?f1=foo\n\t&f2=bar\n\t";
+               m = p.parse(s, ObjectMap.class);
+               assertEquals("{f1:'foo',f2:'bar'}", m.toString());
+
+               s = "?f1=(\n\t)&f2=(\n\t)";
+               m = p.parse(s, ObjectMap.class);
+               assertEquals("\n\t", m.getString("f1"));
+               assertEquals("\n\t", m.getString("f2"));
+
+               s = "?f1=(\n\t)\n\t&f2=(\n\t)\n\t";
+               m = p.parse(s, ObjectMap.class);
+               assertEquals("\n\t", m.getString("f1"));
+               assertEquals("\n\t", m.getString("f2"));
+               assertEquals("{f1:'\\n\\t',f2:'\\n\\t'}", m.toString());  // 
Note that JsonSerializer escapes newlines and tabs.
+
+               s = "?f1=$s(\n\t)\n\t&f2=$s(\n\t)\n\t";
+               m = p.parse(s, ObjectMap.class);
+               assertEquals("\n\t", m.getString("f1"));
+               assertEquals("\n\t", m.getString("f2"));
+               assertEquals("{f1:'\\n\\t',f2:'\\n\\t'}", m.toString());  // 
Note that JsonSerializer escapes newlines and tabs.
+
+               s = 
"?f1=$o(\n\tf1a=a,\n\tf1b=b\n\t)\n\t&f2=$o(\n\tf2a=a,\n\tf2b=b\n\t)\n\t";
+               m = p.parse(s, ObjectMap.class);
+               assertEquals("{f1:{f1a:'a',f1b:'b'},f2:{f2a:'a',f2b:'b'}}", 
m.toString());  // Note that JsonSerializer escapes newlines and tabs.
+               D d = p.parse(s, D.class);
+               
assertObjectEquals("{f1:{f1a:'a',f1b:'b'},f2:{f2a:'a',f2b:'b'}}", d);  // Note 
that JsonSerializer escapes newlines and tabs.
+
+               s = 
"?f1=$o(\n\tf1a=(\n\t),\n\tf1b=(\n\t)\n\t)\n\t&f2=$o(\n\tf2a=(\n\t),\n\tf2b=(\n\t)\n\t)\n\t";
+               m = p.parse(s, ObjectMap.class);
+               
assertEquals("{f1:{f1a:'\\n\\t',f1b:'\\n\\t'},f2:{f2a:'\\n\\t',f2b:'\\n\\t'}}", 
m.toString());  // Note that JsonSerializer escapes newlines and tabs.
+               d = p.parse(s, D.class);
+               
assertObjectEquals("{f1:{f1a:'\\n\\t',f1b:'\\n\\t'},f2:{f2a:'\\n\\t',f2b:'\\n\\t'}}",
 d);  // Note that JsonSerializer escapes newlines and tabs.
+
+               s = 
"?f1=$a(\n\tfoo,\n\tbar\n\t)\n\t&f2=$a(\n\tfoo,\n\tbar\n\t)\n\t";
+               m = p.parse(s, ObjectMap.class);
+               assertEquals("{f1:['foo','bar'],f2:['foo','bar']}", 
m.toString());  // Note that JsonSerializer escapes newlines and tabs.
+
+               s = 
"f1=a,\n\tb,\n\tc\n\t&f2=1,\n\t2,\n\t3\n\t&f3=true,\n\tfalse\n\t";
+               E e = p.parse(s, E.class);
+               
assertObjectEquals("{f1:['a','b','c'],f2:[1,2,3],f3:[true,false]}", e);
+
+               s = 
"f1=a%2C%0D%0Ab%2C%0D%0Ac%0D%0A&f2=1%2C%0D%0A2%2C%0D%0A3%0D%0A&f3=true%2C%0D%0Afalse%0D%0A";
+               e = p.parse(s, E.class);
+               
assertObjectEquals("{f1:['a','b','c'],f2:[1,2,3],f3:[true,false]}", e);
+       }
+
+       public static class D {
+               public D1 f1;
+               public D2 f2;
+       }
+
+       public static class D1 {
+               public String f1a, f1b;
+       }
+
+       public static class D2 {
+               public String f2a, f2b;
+       }
+
+       public static class E {
+               public String[] f1;
+               public int[] f2;
+               public boolean[] f3;
+       }
+
+       
//====================================================================================================
+       // Multi-part parameters on beans via URLENC_expandedParams
+       
//====================================================================================================
+       @Test
+       public void testMultiPartParametersOnBeansViaProperty() throws 
Exception {
+               UrlEncodingParser p;
+               String in;
+
+               p = 
UrlEncodingParser.DEFAULT.clone().setProperty(UrlEncodingContext.URLENC_expandedParams,
 true);
+               in = ""
+                       + "f01=a&f01=b"
+                       + "&f02=c&f02=d"
+                       + "&f03=1&f03=2"
+                       + "&f04=3&f04=4"
+                       + "&f05=(e,f)&f05=(g,h)"
+                       + "&f06=(i,j)&f06=(k,l)"
+                       + "&f07=(a=a,b=1,c=true)&f07=(a=b,b=2,c=false)"
+                       + "&f08=(a=a,b=1,c=true)&f08=(a=b,b=2,c=false)"
+                       + "&f09=((a=a,b=1,c=true))&f09=((a=b,b=2,c=false))"
+                       + "&f10=((a=a,b=1,c=true))&f10=((a=b,b=2,c=false))"
+                       + "&f11=a&f11=b"
+                       + "&f12=c&f12=d"
+                       + "&f13=1&f13=2"
+                       + "&f14=3&f14=4"
+                       + "&f15=(e,f)&f15=(g,h)"
+                       + "&f16=(i,j)&f16=(k,l)"
+                       + "&f17=(a=a,b=1,c=true)&f17=(a=b,b=2,c=false)"
+                       + "&f18=(a=a,b=1,c=true)&f18=(a=b,b=2,c=false)"
+                       + "&f19=((a=a,b=1,c=true))&f19=((a=b,b=2,c=false))"
+                       + "&f20=((a=a,b=1,c=true))&f20=((a=b,b=2,c=false))";
+
+               DTOs.B t = p.parse(in, DTOs.B.class);
+               String e = "{"
+                       + "f01:['a','b'],"
+                       + "f02:['c','d'],"
+                       + "f03:[1,2],"
+                       + "f04:[3,4],"
+                       + "f05:[['e','f'],['g','h']],"
+                       + "f06:[['i','j'],['k','l']],"
+                       + "f07:[{a:'a',b:1,c:true},{a:'b',b:2,c:false}],"
+                       + "f08:[{a:'a',b:1,c:true},{a:'b',b:2,c:false}],"
+                       + "f09:[[{a:'a',b:1,c:true}],[{a:'b',b:2,c:false}]],"
+                       + "f10:[[{a:'a',b:1,c:true}],[{a:'b',b:2,c:false}]],"
+                       + "f11:['a','b'],"
+                       + "f12:['c','d'],"
+                       + "f13:[1,2],"
+                       + "f14:[3,4],"
+                       + "f15:[['e','f'],['g','h']],"
+                       + "f16:[['i','j'],['k','l']],"
+                       + "f17:[{a:'a',b:1,c:true},{a:'b',b:2,c:false}],"
+                       + "f18:[{a:'a',b:1,c:true},{a:'b',b:2,c:false}],"
+                       + "f19:[[{a:'a',b:1,c:true}],[{a:'b',b:2,c:false}]],"
+                       + "f20:[[{a:'a',b:1,c:true}],[{a:'b',b:2,c:false}]]"
+               +"}";
+               assertSortedObjectEquals(e, t);
+       }
+
+       
//====================================================================================================
+       // Multi-part parameters on beans via @UrlEncoding.expandedParams on 
class
+       
//====================================================================================================
+       @Test
+       public void testMultiPartParametersOnBeansViaAnnotationOnClass() throws 
Exception {
+               UrlEncodingParser p;
+               String in;
+               p = UrlEncodingParser.DEFAULT;
+               in = ""
+                       + "f01=a&f01=b"
+                       + "&f02=c&f02=d"
+                       + "&f03=1&f03=2"
+                       + "&f04=3&f04=4"
+                       + "&f05=(e,f)&f05=(g,h)"
+                       + "&f06=(i,j)&f06=(k,l)"
+                       + "&f07=(a=a,b=1,c=true)&f07=(a=b,b=2,c=false)"
+                       + "&f08=(a=a,b=1,c=true)&f08=(a=b,b=2,c=false)"
+                       + "&f09=((a=a,b=1,c=true))&f09=((a=b,b=2,c=false))"
+                       + "&f10=((a=a,b=1,c=true))&f10=((a=b,b=2,c=false))"
+                       + "&f11=a&f11=b"
+                       + "&f12=c&f12=d"
+                       + "&f13=1&f13=2"
+                       + "&f14=3&f14=4"
+                       + "&f15=(e,f)&f15=(g,h)"
+                       + "&f16=(i,j)&f16=(k,l)"
+                       + "&f17=(a=a,b=1,c=true)&f17=(a=b,b=2,c=false)"
+                       + "&f18=(a=a,b=1,c=true)&f18=(a=b,b=2,c=false)"
+                       + "&f19=((a=a,b=1,c=true))&f19=((a=b,b=2,c=false))"
+                       + "&f20=((a=a,b=1,c=true))&f20=((a=b,b=2,c=false))";
+
+               DTOs.C t = p.parse(in, DTOs.C.class);
+               String e = "{"
+                       + "f01:['a','b'],"
+                       + "f02:['c','d'],"
+                       + "f03:[1,2],"
+                       + "f04:[3,4],"
+                       + "f05:[['e','f'],['g','h']],"
+                       + "f06:[['i','j'],['k','l']],"
+                       + "f07:[{a:'a',b:1,c:true},{a:'b',b:2,c:false}],"
+                       + "f08:[{a:'a',b:1,c:true},{a:'b',b:2,c:false}],"
+                       + "f09:[[{a:'a',b:1,c:true}],[{a:'b',b:2,c:false}]],"
+                       + "f10:[[{a:'a',b:1,c:true}],[{a:'b',b:2,c:false}]],"
+                       + "f11:['a','b'],"
+                       + "f12:['c','d'],"
+                       + "f13:[1,2],"
+                       + "f14:[3,4],"
+                       + "f15:[['e','f'],['g','h']],"
+                       + "f16:[['i','j'],['k','l']],"
+                       + "f17:[{a:'a',b:1,c:true},{a:'b',b:2,c:false}],"
+                       + "f18:[{a:'a',b:1,c:true},{a:'b',b:2,c:false}],"
+                       + "f19:[[{a:'a',b:1,c:true}],[{a:'b',b:2,c:false}]],"
+                       + "f20:[[{a:'a',b:1,c:true}],[{a:'b',b:2,c:false}]]"
+               +"}";
+               assertSortedObjectEquals(e, t);
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e4dfdf81/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingSerializerTest.java
----------------------------------------------------------------------
diff --git 
a/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingSerializerTest.java
 
b/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingSerializerTest.java
new file mode 100755
index 0000000..8bcb6ce
--- /dev/null
+++ 
b/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingSerializerTest.java
@@ -0,0 +1,495 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.urlencoding;
+
+import static org.junit.Assert.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@SuppressWarnings("javadoc")
+public class UrlEncodingSerializerTest {
+
+       static UrlEncodingSerializer s = UrlEncodingSerializer.DEFAULT;
+       static UrlEncodingSerializer ss = UrlEncodingSerializer.DEFAULT_SIMPLE;
+       static UrlEncodingSerializer sr = 
UrlEncodingSerializer.DEFAULT_READABLE;
+
+
+       
//====================================================================================================
+       // Basic test
+       
//====================================================================================================
+       @Test
+       public void testBasic() throws Exception {
+
+               Object t;
+
+               // Simple string
+               // Top level
+               t = "a";
+               assertEquals("_value=a", s.serialize(t));
+
+               // 2nd level
+               t = new ObjectMap("{a:'a'}");
+               assertEquals("a=a", s.serialize(t));
+               assertEquals("a=a", ss.serialize(t));
+               assertEquals("a=a", sr.serialize(t));
+
+               // Simple map
+               // Top level
+               t = new ObjectMap("{a:'b',c:123,d:false,e:true,f:null}");
+               assertEquals("a=b&c=$n(123)&d=$b(false)&e=$b(true)&f=%00", 
s.serialize(t));
+               assertEquals("a=b&c=123&d=false&e=true&f=%00", ss.serialize(t));
+               
assertEquals("a=b\n&c=$n(123)\n&d=$b(false)\n&e=$b(true)\n&f=%00", 
sr.serialize(t));
+
+               // 2nd level
+               t = new ObjectMap("{a:{a:'b',c:123,d:false,e:true,f:null}}");
+               
assertEquals("a=$o(a=b,c=$n(123),d=$b(false),e=$b(true),f=%00)", 
s.serialize(t));
+               assertEquals("a=(a=b,c=123,d=false,e=true,f=%00)", 
ss.serialize(t));
+               
assertEquals("a=$o(\n\ta=b,\n\tc=$n(123),\n\td=$b(false),\n\te=$b(true),\n\tf=%00\n)",
 sr.serialize(t));
+
+               // Simple map with primitives as literals
+               t = new 
ObjectMap("{a:'b',c:'123',d:'false',e:'true',f:'null'}");
+               assertEquals("a=b&c=123&d=false&e=true&f=null", s.serialize(t));
+               assertEquals("a=b&c=123&d=false&e=true&f=null", 
ss.serialize(t));
+               assertEquals("a=b\n&c=123\n&d=false\n&e=true\n&f=null", 
sr.serialize(t));
+
+               // null
+               // Note that serializeParams is always encoded.
+               // Top level
+               t = null;
+               assertEquals("_value=%00", s.serialize(t));
+               assertEquals("_value=%00", ss.serialize(t));
+               assertEquals("_value=%00", sr.serialize(t));
+
+               // 2nd level
+               t = new ObjectMap("{null:null}");
+               assertEquals("%00=%00", s.serialize(t));
+               assertEquals("%00=%00", ss.serialize(t));
+               assertEquals("%00=%00", sr.serialize(t));
+
+               // 3rd level
+               t = new ObjectMap("{null:{null:null}}");
+               assertEquals("%00=$o(%00=%00)", s.serialize(t));
+               assertEquals("%00=(%00=%00)", ss.serialize(t));
+               assertEquals("%00=$o(\n\t%00=%00\n)", sr.serialize(t));
+
+               // Empty array
+               // Top level
+               t = new String[0];
+               assertEquals("_value=$a()", s.serialize(t));
+               assertEquals("_value=()", ss.serialize(t));
+               assertEquals("_value=$a()", sr.serialize(t));
+
+               // 2nd level in map
+               t = new ObjectMap("{x:[]}");
+               assertEquals("x=$a()", s.serialize(t));
+               assertEquals("x=()", ss.serialize(t));
+               assertEquals("x=$a()", sr.serialize(t));
+
+               // Empty 2 dimensional array
+               t = new String[1][0];
+               assertEquals("_value=$a($a())", s.serialize(t));
+               assertEquals("_value=(())", ss.serialize(t));
+               assertEquals("_value=$a(\n\t$a()\n)", sr.serialize(t));
+
+               // Array containing empty string
+               // Top level
+               t = new String[]{""};
+               assertEquals("_value=$a(())", s.serialize(t));
+               assertEquals("_value=(())", ss.serialize(t));
+               assertEquals("_value=$a(\n\t()\n)", sr.serialize(t));
+
+               // 2nd level
+               t = new ObjectMap("{x:['']}");
+               assertEquals("x=$a(())", s.serialize(t));
+               assertEquals("x=(())", ss.serialize(t));
+               assertEquals("x=$a(\n\t()\n)", sr.serialize(t));
+
+               // Array containing 3 empty strings
+               t = new String[]{"","",""};
+               assertEquals("_value=$a(,,)", s.serialize(t));
+               assertEquals("_value=(,,)", ss.serialize(t));
+               assertEquals("_value=$a(\n\t(),\n\t(),\n\t()\n)", 
sr.serialize(t));
+
+               // String containing \u0000
+               // Top level
+               t = "\u0000";
+               assertEquals("_value=(%00)", s.serialize(t));
+               assertEquals("_value=(%00)", ss.serialize(t));
+               assertEquals("_value=(%00)", sr.serialize(t));
+
+               // 2nd level
+               t = new ObjectMap("{'\u0000':'\u0000'}");
+               assertEquals("(%00)=(%00)", s.serialize(t));
+               assertEquals("(%00)=(%00)", ss.serialize(t));
+               assertEquals("(%00)=(%00)", sr.serialize(t));
+
+               // Boolean
+               // Top level
+               t = false;
+               assertEquals("_value=$b(false)", s.serialize(t));
+               assertEquals("_value=false", ss.serialize(t));
+               assertEquals("_value=$b(false)", sr.serialize(t));
+
+               // 2nd level
+               t = new ObjectMap("{x:false}");
+               assertEquals("x=$b(false)", s.serialize(t));
+               assertEquals("x=false", ss.serialize(t));
+               assertEquals("x=$b(false)", sr.serialize(t));
+
+               // Number
+               // Top level
+               t = 123;
+               assertEquals("_value=$n(123)", s.serialize(t));
+               assertEquals("_value=123", ss.serialize(t));
+               assertEquals("_value=$n(123)", sr.serialize(t));
+
+               // 2nd level
+               t = new ObjectMap("{x:123}");
+               assertEquals("x=$n(123)", s.serialize(t));
+               assertEquals("x=123", ss.serialize(t));
+               assertEquals("x=$n(123)", sr.serialize(t));
+
+               // Unencoded chars
+               // Top level
+               t = "x;/?:@-_.!*'";
+               assertEquals("_value=x;/?:@-_.!*'", s.serialize(t));
+               assertEquals("_value=x;/?:@-_.!*'", ss.serialize(t));
+               assertEquals("_value=x;/?:@-_.!*'", sr.serialize(t));
+
+               // 2nd level
+               t = new ObjectMap("{x:'x;/?:@-_.!*\\''}");
+               assertEquals("x=x;/?:@-_.!*'", s.serialize(t));
+               assertEquals("x=x;/?:@-_.!*'", ss.serialize(t));
+               assertEquals("x=x;/?:@-_.!*'", sr.serialize(t));
+
+               // Encoded chars
+               // Top level
+               t = "x{}|\\^[]`<>#%\"&+";
+               
assertEquals("_value=x%7B%7D%7C%5C%5E%5B%5D%60%3C%3E%23%25%22%26%2B", 
s.serialize(t));
+               
assertEquals("_value=x%7B%7D%7C%5C%5E%5B%5D%60%3C%3E%23%25%22%26%2B", 
ss.serialize(t));
+               
assertEquals("_value=x%7B%7D%7C%5C%5E%5B%5D%60%3C%3E%23%25%22%26%2B", 
sr.serialize(t));
+
+               // 2nd level
+               t = new 
ObjectMap("{'x{}|\\\\^[]`<>#%\"&+':'x{}|\\\\^[]`<>#%\"&+'}");
+               
assertEquals("x%7B%7D%7C%5C%5E%5B%5D%60%3C%3E%23%25%22%26%2B=x%7B%7D%7C%5C%5E%5B%5D%60%3C%3E%23%25%22%26%2B",
 s.serialize(t));
+               
assertEquals("x%7B%7D%7C%5C%5E%5B%5D%60%3C%3E%23%25%22%26%2B=x%7B%7D%7C%5C%5E%5B%5D%60%3C%3E%23%25%22%26%2B",
 ss.serialize(t));
+               
assertEquals("x%7B%7D%7C%5C%5E%5B%5D%60%3C%3E%23%25%22%26%2B=x%7B%7D%7C%5C%5E%5B%5D%60%3C%3E%23%25%22%26%2B",
 sr.serialize(t));
+
+               // Escaped chars
+               // Top level
+               t = "x$,()~";
+               assertEquals("_value=x$,()~", s.serialize(t));
+               assertEquals("_value=x$,()~", ss.serialize(t));
+               assertEquals("_value=x$,()~", sr.serialize(t));
+
+               // 2nd level
+               t = new ObjectMap("{'x$,()~':'x$,()~'}");
+               assertEquals("x$,()~=x$,()~", s.serialize(t));
+               assertEquals("x$,()~=x$,()~", ss.serialize(t));
+               assertEquals("x$,()~=x$,()~", sr.serialize(t));
+
+               // 3rd level
+               t = new ObjectMap("{'x$,()~':{'x$,()~':'x$,()~'}}");
+               assertEquals("x$,()~=$o(x$~,~(~)~~=x$~,~(~)~~)", 
s.serialize(t));
+               assertEquals("x$,()~=(x$~,~(~)~~=x$~,~(~)~~)", ss.serialize(t));
+               assertEquals("x$,()~=$o(\n\tx$~,~(~)~~=x$~,~(~)~~\n)", 
sr.serialize(t));
+
+               // Equals sign
+               // Gets encoded at top level, and encoded+escaped at 2nd level.
+               // Top level
+               t = "x=";
+               assertEquals("_value=x=", s.serialize(t));
+               assertEquals("_value=x=", ss.serialize(t));
+               assertEquals("_value=x=", sr.serialize(t));
+
+               // 2nd level
+               t = new ObjectMap("{'x=':'x='}");
+               assertEquals("x%3D=x=", s.serialize(t));
+               assertEquals("x%3D=x=", ss.serialize(t));
+               assertEquals("x%3D=x=", sr.serialize(t));
+
+               // 3rd level
+               t = new ObjectMap("{'x=':{'x=':'x='}}");
+               assertEquals("x%3D=$o(x~==x~=)", s.serialize(t));
+               assertEquals("x%3D=(x~==x~=)", ss.serialize(t));
+               assertEquals("x%3D=$o(\n\tx~==x~=\n)", sr.serialize(t));
+
+               // String starting with parenthesis
+               // Top level
+               t = "()";
+               assertEquals("_value=(~(~))", s.serialize(t));
+               assertEquals("_value=(~(~))", ss.serialize(t));
+               assertEquals("_value=(~(~))", sr.serialize(t));
+
+               // 2nd level
+               t = new ObjectMap("{'()':'()'}");
+               assertEquals("(~(~))=(~(~))", s.serialize(t));
+               assertEquals("(~(~))=(~(~))", ss.serialize(t));
+               assertEquals("(~(~))=(~(~))", sr.serialize(t));
+
+               // String starting with $
+               // Top level
+               t = "$a";
+               assertEquals("_value=($a)", s.serialize(t));
+               assertEquals("_value=($a)", ss.serialize(t));
+               assertEquals("_value=($a)", sr.serialize(t));
+
+               // 2nd level
+               t = new ObjectMap("{$a:'$a'}");
+               assertEquals("($a)=($a)", s.serialize(t));
+               assertEquals("($a)=($a)", ss.serialize(t));
+               assertEquals("($a)=($a)", sr.serialize(t));
+
+               // Blank string
+               // Top level
+               t = "";
+               assertEquals("_value=", s.serialize(t));
+               assertEquals("_value=", ss.serialize(t));
+               assertEquals("_value=", sr.serialize(t));
+
+               // 2nd level
+               t = new ObjectMap("{'':''}");
+               assertEquals("=", s.serialize(t));
+               assertEquals("=", ss.serialize(t));
+               assertEquals("=", sr.serialize(t));
+
+               // 3rd level
+               t = new ObjectMap("{'':{'':''}}");
+               assertEquals("=$o(=)", s.serialize(t));
+               assertEquals("=(=)", ss.serialize(t));
+               assertEquals("=$o(\n\t()=()\n)", sr.serialize(t));
+
+               // Newline character
+               // Top level
+               t = "\n";
+               assertEquals("_value=%0A", s.serialize(t));
+               assertEquals("_value=%0A", ss.serialize(t));
+               assertEquals("_value=(%0A)", sr.serialize(t));
+
+               // 2nd level
+               t = new ObjectMap("{'\n':'\n'}");
+               assertEquals("%0A=%0A", s.serialize(t));
+               assertEquals("%0A=%0A", ss.serialize(t));
+               assertEquals("(%0A)=(%0A)", sr.serialize(t));
+
+               // 3rd level
+               t = new ObjectMap("{'\n':{'\n':'\n'}}");
+               assertEquals("%0A=$o(%0A=%0A)", s.serialize(t));
+               assertEquals("%0A=(%0A=%0A)", ss.serialize(t));
+               assertEquals("(%0A)=$o(\n\t(%0A)=(%0A)\n)", sr.serialize(t));
+       }
+
+       
//====================================================================================================
+       // Unicode characters test
+       
//====================================================================================================
+       @Test
+       public void testUnicodeChars() throws Exception {
+               Object t;
+
+               // 2-byte UTF-8 character
+               // Top level
+               t = "¢";
+               assertEquals("_value=%C2%A2", s.serialize(t));
+               assertEquals("_value=%C2%A2", ss.serialize(t));
+               assertEquals("_value=%C2%A2", sr.serialize(t));
+
+               // 2nd level
+               t = new ObjectMap("{'¢':'¢'}");
+               assertEquals("%C2%A2=%C2%A2", s.serialize(t));
+               assertEquals("%C2%A2=%C2%A2", ss.serialize(t));
+               assertEquals("%C2%A2=%C2%A2", sr.serialize(t));
+
+               // 3rd level
+               t = new ObjectMap("{'¢':{'¢':'¢'}}");
+               assertEquals("%C2%A2=$o(%C2%A2=%C2%A2)", s.serialize(t));
+               assertEquals("%C2%A2=(%C2%A2=%C2%A2)", ss.serialize(t));
+               assertEquals("%C2%A2=$o(\n\t%C2%A2=%C2%A2\n)", sr.serialize(t));
+
+               // 3-byte UTF-8 character
+               // Top level
+               t = "€";
+               assertEquals("_value=%E2%82%AC", s.serialize(t));
+               assertEquals("_value=%E2%82%AC", ss.serialize(t));
+               assertEquals("_value=%E2%82%AC", sr.serialize(t));
+
+               // 2nd level
+               t = new ObjectMap("{'€':'€'}");
+               assertEquals("%E2%82%AC=%E2%82%AC", s.serialize(t));
+               assertEquals("%E2%82%AC=%E2%82%AC", ss.serialize(t));
+               assertEquals("%E2%82%AC=%E2%82%AC", sr.serialize(t));
+
+               // 3rd level
+               t = new ObjectMap("{'€':{'€':'€'}}");
+               assertEquals("%E2%82%AC=$o(%E2%82%AC=%E2%82%AC)", 
s.serialize(t));
+               assertEquals("%E2%82%AC=(%E2%82%AC=%E2%82%AC)", 
ss.serialize(t));
+               assertEquals("%E2%82%AC=$o(\n\t%E2%82%AC=%E2%82%AC\n)", 
sr.serialize(t));
+
+               // 4-byte UTF-8 character
+               // Top level
+               t = "𤭢";
+               assertEquals("_value=%F0%A4%AD%A2", s.serialize(t));
+               assertEquals("_value=%F0%A4%AD%A2", ss.serialize(t));
+               assertEquals("_value=%F0%A4%AD%A2", sr.serialize(t));
+
+               // 2nd level
+               t = new ObjectMap("{'𤭢':'𤭢'}");
+               assertEquals("%F0%A4%AD%A2=%F0%A4%AD%A2", s.serialize(t));
+               assertEquals("%F0%A4%AD%A2=%F0%A4%AD%A2", ss.serialize(t));
+               assertEquals("%F0%A4%AD%A2=%F0%A4%AD%A2", sr.serialize(t));
+
+               // 3rd level
+               t = new ObjectMap("{'𤭢':{'𤭢':'𤭢'}}");
+               assertEquals("%F0%A4%AD%A2=$o(%F0%A4%AD%A2=%F0%A4%AD%A2)", 
s.serialize(t));
+               assertEquals("%F0%A4%AD%A2=(%F0%A4%AD%A2=%F0%A4%AD%A2)", 
ss.serialize(t));
+               
assertEquals("%F0%A4%AD%A2=$o(\n\t%F0%A4%AD%A2=%F0%A4%AD%A2\n)", 
sr.serialize(t));
+       }
+
+       
//====================================================================================================
+       // Multi-part parameters on beans via URLENC_expandedParams
+       
//====================================================================================================
+       @Test
+       public void testMultiPartParametersOnBeansViaProperty() throws 
Exception {
+               UrlEncodingSerializer s;
+               DTOs.B t = DTOs.B.create();
+               String r;
+
+               s = UrlEncodingSerializer.DEFAULT_SIMPLE;
+               r = s.serialize(t);
+               String e = ""
+                       + "f01=(a,b)"
+                       + "&f02=(c,d)"
+                       + "&f03=(1,2)"
+                       + "&f04=(3,4)"
+                       + "&f05=((e,f),(g,h))"
+                       + "&f06=((i,j),(k,l))"
+                       + "&f07=((a=a,b=1,c=true),(a=a,b=1,c=true))"
+                       + "&f08=((a=a,b=1,c=true),(a=a,b=1,c=true))"
+                       + "&f09=(((a=a,b=1,c=true)),((a=a,b=1,c=true)))"
+                       + "&f10=(((a=a,b=1,c=true)),((a=a,b=1,c=true)))"
+                       + "&f11=(a,b)"
+                       + "&f12=(c,d)"
+                       + "&f13=(1,2)"
+                       + "&f14=(3,4)"
+                       + "&f15=((e,f),(g,h))"
+                       + "&f16=((i,j),(k,l))"
+                       + "&f17=((a=a,b=1,c=true),(a=a,b=1,c=true))"
+                       + "&f18=((a=a,b=1,c=true),(a=a,b=1,c=true))"
+                       + "&f19=(((a=a,b=1,c=true)),((a=a,b=1,c=true)))"
+                       + "&f20=(((a=a,b=1,c=true)),((a=a,b=1,c=true)))";
+               assertEquals(e, r);
+
+               s = 
UrlEncodingSerializer.DEFAULT_SIMPLE.clone().setProperty(UrlEncodingContext.URLENC_expandedParams,
 true);
+               r = s.serialize(t);
+               e = ""
+                       + "f01=a&f01=b"
+                       + "&f02=c&f02=d"
+                       + "&f03=1&f03=2"
+                       + "&f04=3&f04=4"
+                       + "&f05=(e,f)&f05=(g,h)"
+                       + "&f06=(i,j)&f06=(k,l)"
+                       + "&f07=(a=a,b=1,c=true)&f07=(a=a,b=1,c=true)"
+                       + "&f08=(a=a,b=1,c=true)&f08=(a=a,b=1,c=true)"
+                       + "&f09=((a=a,b=1,c=true))&f09=((a=a,b=1,c=true))"
+                       + "&f10=((a=a,b=1,c=true))&f10=((a=a,b=1,c=true))"
+                       + "&f11=a&f11=b"
+                       + "&f12=c&f12=d"
+                       + "&f13=1&f13=2"
+                       + "&f14=3&f14=4"
+                       + "&f15=(e,f)&f15=(g,h)"
+                       + "&f16=(i,j)&f16=(k,l)"
+                       + "&f17=(a=a,b=1,c=true)&f17=(a=a,b=1,c=true)"
+                       + "&f18=(a=a,b=1,c=true)&f18=(a=a,b=1,c=true)"
+                       + "&f19=((a=a,b=1,c=true))&f19=((a=a,b=1,c=true))"
+                       + "&f20=((a=a,b=1,c=true))&f20=((a=a,b=1,c=true))";
+               assertEquals(e, r);
+       }
+
+
+       
//====================================================================================================
+       // Multi-part parameters on beans via @UrlEncoding.expandedParams on 
class
+       
//====================================================================================================
+       @Test
+       public void testMultiPartParametersOnBeansViaAnnotationOnClass() throws 
Exception {
+               UrlEncodingSerializer s;
+               DTOs.C t = DTOs.C.create();
+               String r;
+
+               s = UrlEncodingSerializer.DEFAULT_SIMPLE;
+               r = s.serialize(t);
+               String e = ""
+                       + "f01=a&f01=b"
+                       + "&f02=c&f02=d"
+                       + "&f03=1&f03=2"
+                       + "&f04=3&f04=4"
+                       + "&f05=(e,f)&f05=(g,h)"
+                       + "&f06=(i,j)&f06=(k,l)"
+                       + "&f07=(a=a,b=1,c=true)&f07=(a=a,b=1,c=true)"
+                       + "&f08=(a=a,b=1,c=true)&f08=(a=a,b=1,c=true)"
+                       + "&f09=((a=a,b=1,c=true))&f09=((a=a,b=1,c=true))"
+                       + "&f10=((a=a,b=1,c=true))&f10=((a=a,b=1,c=true))"
+                       + "&f11=a&f11=b"
+                       + "&f12=c&f12=d"
+                       + "&f13=1&f13=2"
+                       + "&f14=3&f14=4"
+                       + "&f15=(e,f)&f15=(g,h)"
+                       + "&f16=(i,j)&f16=(k,l)"
+                       + "&f17=(a=a,b=1,c=true)&f17=(a=a,b=1,c=true)"
+                       + "&f18=(a=a,b=1,c=true)&f18=(a=a,b=1,c=true)"
+                       + "&f19=((a=a,b=1,c=true))&f19=((a=a,b=1,c=true))"
+                       + "&f20=((a=a,b=1,c=true))&f20=((a=a,b=1,c=true))";
+               assertEquals(e, r);
+
+               s = 
UrlEncodingSerializer.DEFAULT_SIMPLE.clone().setProperty(UrlEncodingContext.URLENC_expandedParams,
 true);
+               r = s.serialize(t);
+               e = ""
+                       + "f01=a&f01=b"
+                       + "&f02=c&f02=d"
+                       + "&f03=1&f03=2"
+                       + "&f04=3&f04=4"
+                       + "&f05=(e,f)&f05=(g,h)"
+                       + "&f06=(i,j)&f06=(k,l)"
+                       + "&f07=(a=a,b=1,c=true)&f07=(a=a,b=1,c=true)"
+                       + "&f08=(a=a,b=1,c=true)&f08=(a=a,b=1,c=true)"
+                       + "&f09=((a=a,b=1,c=true))&f09=((a=a,b=1,c=true))"
+                       + "&f10=((a=a,b=1,c=true))&f10=((a=a,b=1,c=true))"
+                       + "&f11=a&f11=b"
+                       + "&f12=c&f12=d"
+                       + "&f13=1&f13=2"
+                       + "&f14=3&f14=4"
+                       + "&f15=(e,f)&f15=(g,h)"
+                       + "&f16=(i,j)&f16=(k,l)"
+                       + "&f17=(a=a,b=1,c=true)&f17=(a=a,b=1,c=true)"
+                       + "&f18=(a=a,b=1,c=true)&f18=(a=a,b=1,c=true)"
+                       + "&f19=((a=a,b=1,c=true))&f19=((a=a,b=1,c=true))"
+                       + "&f20=((a=a,b=1,c=true))&f20=((a=a,b=1,c=true))";
+               assertEquals(e, r);
+       }
+
+       @Test
+       public void testMultiPartParametersOnMapOfStringArrays() throws 
Exception {
+               UrlEncodingSerializer s;
+               String r;
+
+               Map<String,String[]> t = new LinkedHashMap<String,String[]>();
+               t.put("f1", new String[]{"bar"});
+               t.put("f2", new String[]{"bar","baz"});
+               t.put("f3", new String[]{});
+               s = UrlEncodingSerializer.DEFAULT_SIMPLE_EXPANDED;
+               r = s.serialize(t);
+               String e = "f1=bar&f2=bar&f2=baz";
+               assertEquals(e, r);
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e4dfdf81/juneau-core-test/src/test/java/org/apache/juneau/utils/ArgsTest.java
----------------------------------------------------------------------
diff --git 
a/juneau-core-test/src/test/java/org/apache/juneau/utils/ArgsTest.java 
b/juneau-core-test/src/test/java/org/apache/juneau/utils/ArgsTest.java
new file mode 100755
index 0000000..28fc6b2
--- /dev/null
+++ b/juneau-core-test/src/test/java/org/apache/juneau/utils/ArgsTest.java
@@ -0,0 +1,71 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.utils;
+
+import static org.junit.Assert.*;
+
+import org.junit.*;
+
+@SuppressWarnings("javadoc")
+public class ArgsTest {
+
+       
//====================================================================================================
+       // test - Basic tests
+       
//====================================================================================================
+       @Test
+       public void test() throws Exception {
+               Args a;
+
+               // Empty args
+               a = new Args(new String[]{});
+               assertNull(a.getArg(0));
+               assertNull(a.getArg(1));
+               assertNull(a.getArg(-1));
+               assertNull(a.getArg("foo"));
+               assertEquals(0, a.getArgs("foo").size());
+               assertFalse(a.containsKey("foo"));
+
+               a = new Args(new String[]{"foo"});
+               assertEquals("foo", a.getArg(0));
+               assertNull(a.getArg(1));
+               assertNull(a.getArg(-1));
+               assertNull(a.getArg("foo"));
+               assertEquals(0, a.getArgs("foo").size());
+               assertFalse(a.containsKey("foo"));
+
+               a = new Args(new String[]{"foo", "bar bar"});
+               assertEquals("foo", a.getArg(0));
+               assertEquals("bar bar", a.getArg(1));
+               assertNull(a.getArg(-1));
+               assertNull(a.getArg("foo"));
+               assertEquals(0, a.getArgs("foo").size());
+               assertFalse(a.containsKey("foo"));
+
+               a = new Args(new String[]{"foo", "bar bar", "-foo"});
+               assertEquals("foo", a.getArg(0));
+               assertEquals("bar bar", a.getArg(1));
+               assertNull(a.getArg(-1));
+               assertNull(a.getArg("foo"));
+               assertEquals(0, a.getArgs("foo").size());
+               assertTrue(a.containsKey("foo"));
+
+               a = new Args(new String[]{"foo", "bar bar", "-foo", "bar bar"});
+               assertEquals("foo", a.getArg(0));
+               assertEquals("bar bar", a.getArg(1));
+               assertNull(a.getArg(-1));
+               assertEquals("bar bar", a.getArg("foo"));
+               assertEquals(1, a.getArgs("foo").size());
+               assertEquals("bar bar", a.getArgs("foo").get(0));
+               assertTrue(a.containsKey("foo"));
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e4dfdf81/juneau-core-test/src/test/java/org/apache/juneau/utils/ArrayUtilsTest.java
----------------------------------------------------------------------
diff --git 
a/juneau-core-test/src/test/java/org/apache/juneau/utils/ArrayUtilsTest.java 
b/juneau-core-test/src/test/java/org/apache/juneau/utils/ArrayUtilsTest.java
new file mode 100755
index 0000000..249f8d3
--- /dev/null
+++ b/juneau-core-test/src/test/java/org/apache/juneau/utils/ArrayUtilsTest.java
@@ -0,0 +1,161 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.utils;
+
+import static org.apache.juneau.TestUtils.*;
+import static org.apache.juneau.internal.ArrayUtils.*;
+import static org.junit.Assert.*;
+
+import java.util.*;
+
+import org.junit.*;
+
+@SuppressWarnings("javadoc")
+public class ArrayUtilsTest {
+
+       
//====================================================================================================
+       // iterator(T[])
+       
//====================================================================================================
+       @Test
+       public void testArrayIterator() throws Exception {
+               assertObjectEquals("[1,2,3]", iterator(new Object[]{1,2,3}));
+               assertObjectEquals("[1,2,3]", iterator(new int[]{1,2,3}));
+               assertObjectEquals("[]", iterator(null));
+       }
+
+       
//====================================================================================================
+       // append(T[], T...)
+       
//====================================================================================================
+       @Test
+       public void testAppendArrayToArray() throws Exception {
+               String[] s = new String[0];
+
+               s = append(s, "a", "b");
+               assertObjectEquals("['a','b']", s);
+
+               s = append(s, "c");
+               assertObjectEquals("['a','b','c']", s);
+
+               s = append(s);
+               assertObjectEquals("['a','b','c']", s);
+
+               Object[] o = append((Object[])null);
+               assertObjectEquals("[]", o);
+
+               s = append((String[])null, "a", "b");
+               assertObjectEquals("['a','b']", s);
+       }
+
+       
//====================================================================================================
+       // append(T[], Collection)
+       
//====================================================================================================
+       @Test
+       public void testAppendCollectionToArray() throws Exception {
+               String[] s = new String[0];
+
+               s = append(s, Arrays.asList(new String[]{"a","b"}));
+               assertObjectEquals("['a','b']", s);
+
+               s = append(s, Arrays.asList(new String[]{"c"}));
+               assertObjectEquals("['a','b','c']", s);
+
+               s = append(s, Arrays.asList(new String[0]));
+               assertObjectEquals("['a','b','c']", s);
+
+               try {
+                       append((Object[])null, Collections.emptyList());
+                       fail();
+               } catch (IllegalArgumentException e) {}
+       }
+
+       
//====================================================================================================
+       // reverse(T[] array)
+       
//====================================================================================================
+       @Test
+       public void testReverse() throws Exception {
+               String[] s = new String[0];
+
+               s = new String[]{"a","b"};
+               assertObjectEquals("['b','a']", reverse(s));
+
+               try {
+                       reverse((Object[])null);
+                       fail();
+               } catch (IllegalArgumentException e) {}
+       }
+
+       
//====================================================================================================
+       // asSet(T[])
+       
//====================================================================================================
+       @Test
+       public void testAsSet() throws Exception {
+               String[] s = null;
+
+               try {
+                       asSet(s);
+                       fail();
+               } catch (IllegalArgumentException e) {}
+
+               s = new String[]{"a"};
+               Iterator<String> i = asSet(s).iterator();
+               assertEquals("a", i.next());
+
+               try {
+                       i.remove();
+                       fail();
+               } catch (UnsupportedOperationException e) {}
+
+               try {
+                       i.next();
+                       fail();
+               } catch (NoSuchElementException e) {}
+       }
+
+       
//====================================================================================================
+       // iterator(T[])
+       
//====================================================================================================
+       @Test
+       public void testIterator() throws Exception {
+               String[] s = null;
+
+               s = new String[]{"a"};
+               Iterator<Object> i = iterator(s);
+               assertEquals("a", i.next());
+
+               try {
+                       i.remove();
+                       fail();
+               } catch (UnsupportedOperationException e) {}
+
+               try {
+                       i.next();
+                       fail();
+               } catch (NoSuchElementException e) {}
+       }
+
+       
//====================================================================================================
+       // combine(T[]...)
+       
//====================================================================================================
+       @Test
+       public void testCombine() throws Exception {
+               String[] s1 = new String[]{"a"}, s2 = new String[]{"b"};
+
+               assertObjectEquals("['a','b']", combine(s1, s2));
+               assertObjectEquals("['a']", combine(s1));
+               assertObjectEquals("['b']", combine(s2));
+               assertObjectEquals("['a']", combine(s1,null));
+               assertObjectEquals("['b']", combine(null,s2));
+               assertNull(combine(null,null));
+               assertNull(combine());
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e4dfdf81/juneau-core-test/src/test/java/org/apache/juneau/utils/ByteArrayCacheTest.java
----------------------------------------------------------------------
diff --git 
a/juneau-core-test/src/test/java/org/apache/juneau/utils/ByteArrayCacheTest.java
 
b/juneau-core-test/src/test/java/org/apache/juneau/utils/ByteArrayCacheTest.java
new file mode 100755
index 0000000..85c5ad1
--- /dev/null
+++ 
b/juneau-core-test/src/test/java/org/apache/juneau/utils/ByteArrayCacheTest.java
@@ -0,0 +1,61 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.utils;
+
+import static org.apache.juneau.TestUtils.*;
+import static org.junit.Assert.*;
+
+import java.io.*;
+
+import org.apache.juneau.internal.*;
+import org.junit.*;
+
+@SuppressWarnings("javadoc")
+public class ByteArrayCacheTest {
+
+       
//====================================================================================================
+       // testBasic
+       
//====================================================================================================
+       @Test
+       public void testBasic() throws Exception {
+               ByteArrayCache bac = new ByteArrayCache();
+               byte[] b;
+
+               b = bac.cache(new byte[]{1,2,3});
+               assertObjectEquals("[1,2,3]", b);
+               assertEquals(1, bac.size());
+
+               b = bac.cache(new byte[]{1,2,3});
+               assertObjectEquals("[1,2,3]", b);
+               assertEquals(1, bac.size());
+
+               b = bac.cache(new byte[]{1,2,3,4});
+               assertObjectEquals("[1,2,3,4]", b);
+               b = bac.cache(new byte[]{1,2});
+               assertObjectEquals("[1,2]", b);
+               assertEquals(3, bac.size());
+
+               b = bac.cache(new byte[]{});
+               assertObjectEquals("[]", b);
+
+               b = bac.cache((byte[])null);
+               assertNull(b);
+
+               b = bac.cache((InputStream)null);
+               assertNull(b);
+
+               b = bac.cache(new ByteArrayInputStream(new byte[]{1,2,3}));
+               assertObjectEquals("[1,2,3]", b);
+               assertEquals(4, bac.size());
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e4dfdf81/juneau-core-test/src/test/java/org/apache/juneau/utils/ByteArrayInOutStreamTest.java
----------------------------------------------------------------------
diff --git 
a/juneau-core-test/src/test/java/org/apache/juneau/utils/ByteArrayInOutStreamTest.java
 
b/juneau-core-test/src/test/java/org/apache/juneau/utils/ByteArrayInOutStreamTest.java
new file mode 100755
index 0000000..0a72c16
--- /dev/null
+++ 
b/juneau-core-test/src/test/java/org/apache/juneau/utils/ByteArrayInOutStreamTest.java
@@ -0,0 +1,35 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.utils;
+
+import static org.junit.Assert.*;
+
+import java.io.*;
+
+import org.apache.juneau.internal.*;
+import org.junit.*;
+
+@SuppressWarnings("javadoc")
+public class ByteArrayInOutStreamTest {
+
+       
//====================================================================================================
+       // testBasic
+       
//====================================================================================================
+       @Test
+       public void testBasic() throws Exception {
+               InputStream is = new 
ByteArrayInputStream("foobar".getBytes(IOUtils.UTF8));
+               ByteArrayInOutStream baios = new ByteArrayInOutStream();
+               IOPipe.create(is, baios).run();
+               assertEquals("foobar", IOUtils.read(baios.getInputStream()));
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e4dfdf81/juneau-core-test/src/test/java/org/apache/juneau/utils/CharSetTest.java
----------------------------------------------------------------------
diff --git 
a/juneau-core-test/src/test/java/org/apache/juneau/utils/CharSetTest.java 
b/juneau-core-test/src/test/java/org/apache/juneau/utils/CharSetTest.java
new file mode 100755
index 0000000..71bfdb2
--- /dev/null
+++ b/juneau-core-test/src/test/java/org/apache/juneau/utils/CharSetTest.java
@@ -0,0 +1,35 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.utils;
+
+import static org.junit.Assert.*;
+
+import org.apache.juneau.internal.*;
+import org.junit.*;
+
+@SuppressWarnings("javadoc")
+public class CharSetTest {
+
+       
//====================================================================================================
+       // test - Basic tests
+       
//====================================================================================================
+       @Test
+       public void test() throws Exception {
+               AsciiSet cs = new AsciiSet("abc\u1234");
+               assertTrue(cs.contains('a'));
+               assertFalse(cs.contains('d'));
+               assertFalse(cs.contains('\u1234'));
+               assertFalse(cs.contains((char)-1));
+               assertFalse(cs.contains((char)128));
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e4dfdf81/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java
----------------------------------------------------------------------
diff --git 
a/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java 
b/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java
new file mode 100755
index 0000000..ce2d30e
--- /dev/null
+++ b/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java
@@ -0,0 +1,115 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.utils;
+
+import static org.apache.juneau.internal.ClassUtils.*;
+import static org.junit.Assert.*;
+
+import org.junit.*;
+
+@SuppressWarnings("javadoc")
+public class ClassUtilsTest {
+
+       
//====================================================================================================
+       // getReadableClassName(Class)
+       // getReadableClassName(String)
+       
//====================================================================================================
+       @Test
+       public void testGetReadableClassName() throws Exception {
+               assertEquals("java.lang.Object", 
getReadableClassName(Object.class));
+               assertEquals("java.lang.Object[]", 
getReadableClassName(Object[].class));
+               assertEquals("java.lang.Object[][]", 
getReadableClassName(Object[][].class));
+
+               assertEquals("boolean", getReadableClassName(boolean.class));
+               assertEquals("boolean[]", 
getReadableClassName(boolean[].class));
+               assertEquals("boolean[][]", 
getReadableClassName(boolean[][].class));
+
+               assertEquals("byte", getReadableClassName(byte.class));
+               assertEquals("byte[]", getReadableClassName(byte[].class));
+               assertEquals("byte[][]", getReadableClassName(byte[][].class));
+
+               assertEquals("char", getReadableClassName(char.class));
+               assertEquals("char[]", getReadableClassName(char[].class));
+               assertEquals("char[][]", getReadableClassName(char[][].class));
+
+               assertEquals("double", getReadableClassName(double.class));
+               assertEquals("double[]", getReadableClassName(double[].class));
+               assertEquals("double[][]", 
getReadableClassName(double[][].class));
+
+               assertEquals("float", getReadableClassName(float.class));
+               assertEquals("float[]", getReadableClassName(float[].class));
+               assertEquals("float[][]", 
getReadableClassName(float[][].class));
+
+               assertEquals("int", getReadableClassName(int.class));
+               assertEquals("int[]", getReadableClassName(int[].class));
+               assertEquals("int[][]", getReadableClassName(int[][].class));
+
+               assertEquals("long", getReadableClassName(long.class));
+               assertEquals("long[]", getReadableClassName(long[].class));
+               assertEquals("long[][]", getReadableClassName(long[][].class));
+
+               assertEquals("short", getReadableClassName(short.class));
+               assertEquals("short[]", getReadableClassName(short[].class));
+               assertEquals("short[][]", 
getReadableClassName(short[][].class));
+
+               assertNull(getReadableClassName((Class<?>)null));
+               assertNull(getReadableClassName((String)null));
+       }
+
+       
//====================================================================================================
+       // isParentClass(Class, Class)
+       
//====================================================================================================
+       @Test
+       public void testIsParentClass() throws Exception {
+
+               // Strict
+               assertTrue(isParentClass(A.class, A1.class, true));
+               assertTrue(isParentClass(A1.class, A2.class, true));
+               assertTrue(isParentClass(Object.class, A2.class, true));
+               assertFalse(isParentClass(A.class, A.class, true));
+               assertFalse(isParentClass(A1.class, A1.class, true));
+               assertFalse(isParentClass(A2.class, A2.class, true));
+               assertFalse(isParentClass(A2.class, A1.class, true));
+               assertFalse(isParentClass(A1.class, A.class, true));
+               assertFalse(isParentClass(A2.class, Object.class, true));
+
+               // Not strict
+               assertTrue(isParentClass(A.class, A1.class, false));
+               assertTrue(isParentClass(A1.class, A2.class, false));
+               assertTrue(isParentClass(Object.class, A2.class, false));
+               assertTrue(isParentClass(A.class, A.class, false));
+               assertTrue(isParentClass(A1.class, A1.class, false));
+               assertTrue(isParentClass(A2.class, A2.class, false));
+               assertFalse(isParentClass(A2.class, A1.class, false));
+               assertFalse(isParentClass(A1.class, A.class, false));
+               assertFalse(isParentClass(A2.class, Object.class, false));
+       }
+
+       public interface A {}
+
+       public static class A1 implements A {}
+
+       public static class A2 extends A1 {}
+
+       
//====================================================================================================
+       // getReadableClassNames(Object[])
+       
//====================================================================================================
+       @Test
+       public void testGetReadableClassNames() throws Exception {
+               
assertEquals("['java.lang.String','java.lang.Integer','java.lang.Boolean','null']",
 getReadableClassNames(new Object[]{"a",1,true,null}).toString());
+       }
+
+       public void getClassFromReadableName() throws Exception {
+               fail("Not implemented");
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e4dfdf81/juneau-core-test/src/test/java/org/apache/juneau/utils/CollectionUtilsTest.java
----------------------------------------------------------------------
diff --git 
a/juneau-core-test/src/test/java/org/apache/juneau/utils/CollectionUtilsTest.java
 
b/juneau-core-test/src/test/java/org/apache/juneau/utils/CollectionUtilsTest.java
new file mode 100755
index 0000000..bc4b94e
--- /dev/null
+++ 
b/juneau-core-test/src/test/java/org/apache/juneau/utils/CollectionUtilsTest.java
@@ -0,0 +1,35 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.utils;
+
+import static org.apache.juneau.TestUtils.*;
+import static org.apache.juneau.internal.CollectionUtils.*;
+import static org.junit.Assert.*;
+
+import org.apache.juneau.*;
+import org.junit.*;
+
+@SuppressWarnings("javadoc")
+public class CollectionUtilsTest {
+
+       
//====================================================================================================
+       // reverse(LinkedHashMap)
+       
//====================================================================================================
+       @Test
+       public void testReverse() throws Exception {
+               assertNull(reverse(null));
+
+               assertObjectEquals("{b:2,a:1}", reverse(new 
ObjectMap("{a:1,b:2}")));
+               assertObjectEquals("{}", reverse(new ObjectMap("{}")));
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e4dfdf81/juneau-core-test/src/test/java/org/apache/juneau/utils/FilteredMapTest.java
----------------------------------------------------------------------
diff --git 
a/juneau-core-test/src/test/java/org/apache/juneau/utils/FilteredMapTest.java 
b/juneau-core-test/src/test/java/org/apache/juneau/utils/FilteredMapTest.java
new file mode 100755
index 0000000..ec75da3
--- /dev/null
+++ 
b/juneau-core-test/src/test/java/org/apache/juneau/utils/FilteredMapTest.java
@@ -0,0 +1,50 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.utils;
+
+import static org.apache.juneau.TestUtils.*;
+import static org.junit.Assert.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.internal.*;
+import org.junit.*;
+
+@SuppressWarnings("javadoc")
+public class FilteredMapTest {
+
+       Map<?,?> m3;
+
+       
//====================================================================================================
+       // testBasic
+       
//====================================================================================================
+       @Test
+       @SuppressWarnings("unchecked")
+       public void testBasic() throws Exception {
+               ObjectMap m = new ObjectMap("{a:'1',b:'2'}");
+
+               ClassMeta<Map<String,Object>> cm = 
BeanContext.DEFAULT.getMapClassMeta(Map.class, String.class, Object.class);
+               ClassMeta<Map<String,String>> cm2 = 
BeanContext.DEFAULT.getMapClassMeta(Map.class, String.class, String.class);
+
+               FilteredMap<String,Object> m2 = new 
FilteredMap<String,Object>(cm, m, new String[]{"a"});
+
+               assertObjectEquals("{a:'1'}", m2);
+
+               m2.entrySet().iterator().next().setValue("3");
+               assertObjectEquals("{a:'3'}", m2);
+
+               try { m3 = new FilteredMap<String,String>(cm2, null, new 
String[0]); fail(); } catch (IllegalArgumentException e) {}
+               try { m3 = new FilteredMap<String,Object>(cm, m, null); fail(); 
} catch (IllegalArgumentException e) {}
+       }
+}

Reply via email to