You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2017/05/02 01:11:48 UTC

[3/3] incubator-juneau git commit: New Accept/AcceptEncoding/ContentType classes.

New Accept/AcceptEncoding/ContentType classes.

Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/c3609d05
Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/c3609d05
Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/c3609d05

Branch: refs/heads/master
Commit: c3609d0518c07d1e96b62dee42eda40050a9fe02
Parents: 2ebf5ca
Author: JamesBognar <ja...@apache.org>
Authored: Mon May 1 21:11:38 2017 -0400
Committer: JamesBognar <ja...@apache.org>
Committed: Mon May 1 21:11:38 2017 -0400

----------------------------------------------------------------------
 .../java/org/apache/juneau/jena/RdfParser.java  |   1 +
 .../apache/juneau/jena/RdfParserBuilder.java    |   1 +
 .../apache/juneau/jena/RdfParserSession.java    |   1 +
 .../org/apache/juneau/jena/RdfSerializer.java   |   1 +
 .../juneau/jena/RdfSerializerBuilder.java       |   1 +
 .../juneau/jena/RdfSerializerSession.java       |   1 +
 .../java/org/apache/juneau/MediaRangeTest.java  |  65 ----
 .../test/java/org/apache/juneau/TestUtils.java  |  24 +-
 .../juneau/encoders/EncoderGroupTest.java       | 116 +++++++
 .../juneau/https/AcceptExtensionsTest.java      | 118 +++++++
 .../org/apache/juneau/https/AcceptTest.java     | 128 ++++++++
 .../org/apache/juneau/https/MediaRangeTest.java |  66 ++++
 .../apache/juneau/parser/ParserGroupTest.java   | 125 ++++++++
 .../juneau/serializer/SerializerGroupTest.java  | 166 ++++++++++
 .../juneau/utils/CollectionUtilsTest.java       |   4 +-
 .../apache/juneau/utils/StringUtilsTest.java    |  17 +
 .../java/org/apache/juneau/BeanContext.java     |   1 +
 .../java/org/apache/juneau/BeanSession.java     |   1 +
 .../org/apache/juneau/CoreObjectBuilder.java    |   1 +
 .../main/java/org/apache/juneau/MediaRange.java | 321 -------------------
 .../main/java/org/apache/juneau/MediaType.java  | 158 ---------
 .../main/java/org/apache/juneau/Streamable.java |   2 +
 .../main/java/org/apache/juneau/Writable.java   |   2 +
 .../org/apache/juneau/csv/CsvParserBuilder.java |   1 +
 .../org/apache/juneau/csv/CsvParserSession.java |   1 +
 .../apache/juneau/csv/CsvSerializerBuilder.java |   1 +
 .../apache/juneau/csv/CsvSerializerSession.java |   1 +
 .../apache/juneau/dto/swagger/Operation.java    |   2 +-
 .../org/apache/juneau/dto/swagger/Swagger.java  |   2 +-
 .../apache/juneau/encoders/EncoderGroup.java    |  82 ++---
 .../juneau/encoders/EncoderGroupBuilder.java    |  16 +-
 .../apache/juneau/encoders/EncoderMatch.java    |  10 +-
 .../apache/juneau/html/HtmlDocSerializer.java   |   1 +
 .../juneau/html/HtmlDocSerializerSession.java   |   1 +
 .../java/org/apache/juneau/html/HtmlParser.java |   1 +
 .../apache/juneau/html/HtmlParserBuilder.java   |   1 +
 .../apache/juneau/html/HtmlParserSession.java   |   1 +
 .../juneau/html/HtmlSchemaDocSerializer.java    |   1 +
 .../org/apache/juneau/html/HtmlSerializer.java  |   1 +
 .../juneau/html/HtmlSerializerBuilder.java      |   1 +
 .../juneau/html/HtmlSerializerSession.java      |   1 +
 .../java/org/apache/juneau/http/Accept.java     | 231 +++++++++++++
 .../org/apache/juneau/http/AcceptEncoding.java  | 147 +++++++++
 .../org/apache/juneau/http/ContentType.java     |  90 ++++++
 .../java/org/apache/juneau/http/MediaType.java  | 277 ++++++++++++++++
 .../org/apache/juneau/http/MediaTypeRange.java  | 270 ++++++++++++++++
 .../java/org/apache/juneau/http/TypeRange.java  | 276 ++++++++++++++++
 .../apache/juneau/ini/ConfigFileWritable.java   |   1 +
 .../org/apache/juneau/internal/ArrayUtils.java  |  33 ++
 .../apache/juneau/internal/CollectionUtils.java |  32 ++
 .../org/apache/juneau/internal/StringUtils.java |  79 +++++
 .../org/apache/juneau/jso/JsoParserBuilder.java |   1 +
 .../apache/juneau/jso/JsoSerializerBuilder.java |   1 +
 .../java/org/apache/juneau/json/JsonParser.java |   1 +
 .../apache/juneau/json/JsonParserBuilder.java   |   1 +
 .../apache/juneau/json/JsonParserSession.java   |   1 +
 .../juneau/json/JsonSchemaSerializer.java       |   1 +
 .../json/JsonSchemaSerializerBuilder.java       |   1 +
 .../org/apache/juneau/json/JsonSerializer.java  |   1 +
 .../juneau/json/JsonSerializerBuilder.java      |   1 +
 .../juneau/json/JsonSerializerSession.java      |   1 +
 .../apache/juneau/msgpack/MsgPackParser.java    |   1 +
 .../juneau/msgpack/MsgPackParserBuilder.java    |   1 +
 .../juneau/msgpack/MsgPackParserSession.java    |   1 +
 .../juneau/msgpack/MsgPackSerializer.java       |   1 +
 .../msgpack/MsgPackSerializerBuilder.java       |   1 +
 .../msgpack/MsgPackSerializerSession.java       |   1 +
 .../java/org/apache/juneau/parser/Parser.java   |   1 +
 .../org/apache/juneau/parser/ParserBuilder.java |   1 +
 .../org/apache/juneau/parser/ParserGroup.java   |  68 ++--
 .../juneau/parser/ParserGroupBuilder.java       |  12 +-
 .../org/apache/juneau/parser/ParserMatch.java   |   2 +-
 .../org/apache/juneau/parser/ParserSession.java |   1 +
 .../juneau/plaintext/PlainTextParser.java       |   2 +-
 .../plaintext/PlainTextParserBuilder.java       |   1 +
 .../juneau/plaintext/PlainTextSerializer.java   |   2 +-
 .../plaintext/PlainTextSerializerBuilder.java   |   1 +
 .../apache/juneau/serializer/Serializer.java    |   1 +
 .../juneau/serializer/SerializerBuilder.java    |   1 +
 .../juneau/serializer/SerializerGroup.java      |  68 ++--
 .../serializer/SerializerGroupBuilder.java      |  12 +-
 .../juneau/serializer/SerializerMatch.java      |   2 +-
 .../juneau/serializer/SerializerSession.java    |   1 +
 .../juneau/serializer/WriterSerializer.java     |   1 +
 .../juneau/soap/SoapXmlSerializerBuilder.java   |   1 +
 .../java/org/apache/juneau/uon/UonParser.java   |   1 +
 .../org/apache/juneau/uon/UonParserBuilder.java |   1 +
 .../org/apache/juneau/uon/UonParserSession.java |   1 +
 .../org/apache/juneau/uon/UonSerializer.java    |   1 +
 .../apache/juneau/uon/UonSerializerBuilder.java |   1 +
 .../apache/juneau/uon/UonSerializerSession.java |   1 +
 .../juneau/urlencoding/UrlEncodingParser.java   |   1 +
 .../urlencoding/UrlEncodingParserBuilder.java   |   1 +
 .../urlencoding/UrlEncodingParserSession.java   |   1 +
 .../urlencoding/UrlEncodingSerializer.java      |   1 +
 .../UrlEncodingSerializerBuilder.java           |   1 +
 .../UrlEncodingSerializerSession.java           |   1 +
 .../org/apache/juneau/utils/StringMessage.java  |   1 +
 .../org/apache/juneau/utils/StringObject.java   |   1 +
 .../java/org/apache/juneau/xml/XmlParser.java   |   1 +
 .../org/apache/juneau/xml/XmlParserBuilder.java |   1 +
 .../org/apache/juneau/xml/XmlParserSession.java |   1 +
 .../apache/juneau/xml/XmlSchemaSerializer.java  |   1 +
 .../juneau/xml/XmlSchemaSerializerBuilder.java  |   1 +
 .../org/apache/juneau/xml/XmlSerializer.java    |   1 +
 .../apache/juneau/xml/XmlSerializerBuilder.java |   1 +
 .../apache/juneau/xml/XmlSerializerSession.java |   1 +
 juneau-core/src/main/javadoc/overview.html      |  47 ++-
 .../juneau/rest/client/RestClientBuilder.java   |   1 +
 .../apache/juneau/rest/jaxrs/BaseProvider.java  |   3 +-
 .../juneau/rest/test/HeadersResource.java       |  74 +++++
 .../java/org/apache/juneau/rest/test/Root.java  |   1 +
 .../org/apache/juneau/rest/test/GzipTest.java   |   4 -
 .../apache/juneau/rest/test/HeadersTest.java    |  56 ++++
 .../apache/juneau/rest/test/ParsersTest.java    |   2 +-
 .../java/org/apache/juneau/rest/CallMethod.java |  17 +-
 .../org/apache/juneau/rest/ReaderResource.java  |   1 +
 .../java/org/apache/juneau/rest/RestConfig.java |   7 +-
 .../org/apache/juneau/rest/RestContext.java     |   1 +
 .../apache/juneau/rest/RestInfoProvider.java    |   2 +-
 .../org/apache/juneau/rest/RestRequest.java     |  54 ++--
 .../org/apache/juneau/rest/RestResponse.java    |   3 +-
 .../org/apache/juneau/rest/StreamResource.java  |   1 +
 .../java/org/apache/juneau/rest/package.html    |  52 ++-
 .../rest/remoteable/RemoteableServlet.java      |   2 +-
 .../juneau/rest/response/DefaultHandler.java    |   1 +
 .../juneau/rest/response/StreamableHandler.java |   1 +
 .../juneau/rest/response/WritableHandler.java   |   1 +
 128 files changed, 2673 insertions(+), 756 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParser.java
----------------------------------------------------------------------
diff --git a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParser.java b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParser.java
index 9bc336e..ffe2453 100644
--- a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParser.java
+++ b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParser.java
@@ -21,6 +21,7 @@ import java.util.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
+import org.apache.juneau.http.*;
 import org.apache.juneau.parser.*;
 import org.apache.juneau.transform.*;
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParserBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParserBuilder.java b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParserBuilder.java
index 1a71a2b..03dbc48 100644
--- a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParserBuilder.java
+++ b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParserBuilder.java
@@ -18,6 +18,7 @@ import static org.apache.juneau.jena.RdfParserContext.*;
 import java.util.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.http.*;
 import org.apache.juneau.jena.annotation.*;
 import org.apache.juneau.parser.*;
 import org.apache.juneau.xml.*;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParserSession.java
----------------------------------------------------------------------
diff --git a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParserSession.java b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParserSession.java
index db1cbdc..f699443 100644
--- a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParserSession.java
+++ b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParserSession.java
@@ -20,6 +20,7 @@ import java.lang.reflect.*;
 import java.util.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.http.*;
 import org.apache.juneau.parser.*;
 import org.apache.juneau.xml.*;
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializer.java b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializer.java
index 9b254a9..ecbc618 100644
--- a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializer.java
+++ b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializer.java
@@ -20,6 +20,7 @@ import java.util.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
+import org.apache.juneau.http.*;
 import org.apache.juneau.internal.*;
 import org.apache.juneau.serializer.*;
 import org.apache.juneau.transform.*;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerBuilder.java b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerBuilder.java
index 6b8e5bb..da3b59a 100644
--- a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerBuilder.java
+++ b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerBuilder.java
@@ -18,6 +18,7 @@ import static org.apache.juneau.jena.RdfSerializerContext.*;
 import java.util.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.http.*;
 import org.apache.juneau.jena.annotation.*;
 import org.apache.juneau.serializer.*;
 import org.apache.juneau.xml.*;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java
----------------------------------------------------------------------
diff --git a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java
index df13cea..199f144 100644
--- a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java
+++ b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java
@@ -20,6 +20,7 @@ import java.lang.reflect.*;
 import java.util.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.http.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.msgpack.*;
 import org.apache.juneau.serializer.*;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core-test/src/test/java/org/apache/juneau/MediaRangeTest.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/MediaRangeTest.java b/juneau-core-test/src/test/java/org/apache/juneau/MediaRangeTest.java
deleted file mode 100644
index 882ca6a..0000000
--- a/juneau-core-test/src/test/java/org/apache/juneau/MediaRangeTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-// ***************************************************************************************************************************
-// * 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;
-
-import static org.junit.Assert.*;
-
-import java.util.*;
-
-import org.apache.juneau.json.*;
-import org.junit.*;
-import org.junit.runner.*;
-import org.junit.runners.*;
-
-/**
- * Verifies that the MediaRange and MediaType classes parse and sort Accept headers correctly.
- */
-@RunWith(Parameterized.class)
-public class MediaRangeTest {
-	@Parameterized.Parameters
-	public static Collection<Object[]> getParameters() {
-		return Arrays.asList(new Object[][] {
-			{ "0", "text/json", "['text/json']" },
-			{ "1", "text/json,text/*", "['text/json','text/*']" },
-			{ "2", "text/*,text/json", "['text/json','text/*']" },
-			{ "3", "text/*,text/*", "['text/*']" },
-			{ "4", "*/text,text/*", "['text/*','*/text']" },
-			{ "5", "text/*,*/text", "['text/*','*/text']" },
-			{ "6", "A;q=0.9,B;q=0.1", "['a;q=0.9','b;q=0.1']" },
-			{ "7", "B;q=0.9,A;q=0.1", "['b;q=0.9','a;q=0.1']" },
-			{ "8", "A,B;q=0.9,C;q=0.1,D;q=0", "['a','b;q=0.9','c;q=0.1','d;q=0.0']" },
-			{ "9", "D;q=0,C;q=0.1,B;q=0.9,A", "['a','b;q=0.9','c;q=0.1','d;q=0.0']" },
-			{ "10", "A;q=1,B;q=0.9,C;q=0.1,D;q=0", "['a','b;q=0.9','c;q=0.1','d;q=0.0']" },
-			{ "11", "D;q=0,C;q=0.1,B;q=0.9,A;q=1", "['a','b;q=0.9','c;q=0.1','d;q=0.0']" },
-			{ "12", "A;q=0,B;q=0.1,C;q=0.9,D;q=1", "['d','c;q=0.9','b;q=0.1','a;q=0.0']" },
-			{ "13", "*", "['*']" },
-			{ "14", "", "['*/*']" },
-			{ "15", null, "['*/*']" },
-			{ "16", "foo/bar/baz", "['foo/bar/baz']" },
-		});
-	}
-	
-	private String label, mediaRange, expected;
-	
-	public MediaRangeTest(String label, String mediaRange, String expected) {
-		this.label = label;
-		this.mediaRange = mediaRange;
-		this.expected = expected;
-	}
-	
-	@Test
-	public void test() {
-		MediaRange[] r = MediaRange.parse(mediaRange);
-		assertEquals(label + " failed", expected, JsonSerializer.DEFAULT_LAX.toString(r));
-	}
-}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core-test/src/test/java/org/apache/juneau/TestUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/TestUtils.java b/juneau-core-test/src/test/java/org/apache/juneau/TestUtils.java
index eaeb4e7..4405efd 100755
--- a/juneau-core-test/src/test/java/org/apache/juneau/TestUtils.java
+++ b/juneau-core-test/src/test/java/org/apache/juneau/TestUtils.java
@@ -437,14 +437,13 @@ public class TestUtils {
 	 * If it's a byte[], convert it to a UTF-8 encoded String.
 	 */
 	public static String toString(Object o) {
+		if (o == null)
+			return null;
 		if (o instanceof String)
 			return (String)o;
-		try {
-			return new String((byte[])o, "UTF-8");
-		} catch (UnsupportedEncodingException e) {
-			e.printStackTrace();
-		}
-		return null;
+		if (o instanceof byte[])
+			return new String((byte[])o, IOUtils.UTF8);
+		return o.toString();
 	}
 
 	private static ThreadLocal<TimeZone> systemTimeZone = new ThreadLocal<TimeZone>();
@@ -497,9 +496,9 @@ public class TestUtils {
 	/**
 	 * Same as {@link Assert#assertEquals(String,String,String) except takes in a MessageFormat-style message.
 	 */
-	public static void assertEquals(String expected, String actual, String msg, Object...args) {
-		if (! StringUtils.isEquals(expected, actual))
-			throw new ComparisonFailure(MessageFormat.format(msg, args), expected, actual);			
+	public static void assertEquals(Object expected, Object actual, String msg, Object...args) {
+		if (! isEquals(expected, actual))
+			throw new ComparisonFailure(MessageFormat.format(msg, args), toString(expected), toString(actual));			
 	}
 	
 	/**
@@ -518,4 +517,11 @@ public class TestUtils {
 		throw new AssertionError(new StringMessage("Expected type {0} but was {1}", type, (o == null ? null : o.getClass())));
 	}
 	
+	private static boolean isEquals(Object o1, Object o2) {
+		if (o1 == null)
+			return o2 == null;
+		if (o2 == null)
+			return false;
+		return o1.equals(o2);
+	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core-test/src/test/java/org/apache/juneau/encoders/EncoderGroupTest.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/encoders/EncoderGroupTest.java b/juneau-core-test/src/test/java/org/apache/juneau/encoders/EncoderGroupTest.java
new file mode 100755
index 0000000..e6e2ec1
--- /dev/null
+++ b/juneau-core-test/src/test/java/org/apache/juneau/encoders/EncoderGroupTest.java
@@ -0,0 +1,116 @@
+// ***************************************************************************************************************************
+// * 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.encoders;
+
+import static org.apache.juneau.TestUtils.*;
+
+import org.junit.*;
+
+@SuppressWarnings({"javadoc"})
+public class EncoderGroupTest {
+
+	//====================================================================================================
+	// Test matching
+	//====================================================================================================
+	@Test
+	public void testEncoderGroupMatching() throws Exception {
+		
+		EncoderGroup g = new EncoderGroupBuilder().append(Encoder1.class, Encoder2.class, Encoder3.class).build();
+		assertType(Encoder1.class, g.getEncoder("gzip1"));
+		assertType(Encoder2.class, g.getEncoder("gzip2"));
+		assertType(Encoder2.class, g.getEncoder("gzip2a"));
+		assertType(Encoder3.class, g.getEncoder("gzip3"));
+		assertType(Encoder3.class, g.getEncoder("gzip3a"));
+		assertType(Encoder3.class, g.getEncoder("gzip3,gzip2,gzip1"));
+		assertType(Encoder1.class, g.getEncoder("gzip3;q=0.9,gzip2;q=0.1,gzip1"));
+		assertType(Encoder3.class, g.getEncoder("gzip2;q=0.9,gzip1;q=0.1,gzip3"));
+		assertType(Encoder2.class, g.getEncoder("gzip1;q=0.9,gzip3;q=0.1,gzip2"));
+	}
+	
+	public static class Encoder1 extends GzipEncoder {
+		@Override /* Encoder */
+		public String[] getCodings() {
+			return new String[]{"gzip1"};
+		}
+	}
+	
+	public static class Encoder2 extends GzipEncoder {
+		@Override /* Encoder */
+		public String[] getCodings() {
+			return new String[]{"gzip2","gzip2a"};
+		}
+	}
+
+	public static class Encoder3 extends GzipEncoder {
+		@Override /* Encoder */
+		public String[] getCodings() {
+			return new String[]{"gzip3","gzip3a"};
+		}
+	}
+	
+	//====================================================================================================
+	// Test inheritence
+	//====================================================================================================
+	@Test
+	public void testInheritence() throws Exception {
+		EncoderGroupBuilder gb = null;
+		EncoderGroup g = null;
+		
+		gb = new EncoderGroupBuilder().append(E1.class, E2.class);
+		g = gb.build();
+		assertObjectEquals("['E1','E2','E2a']", g.getSupportedEncodings());
+		
+		gb = new EncoderGroupBuilder(g).append(E3.class, E4.class);
+		g = gb.build();
+		assertObjectEquals("['E3','E4','E4a','E1','E2','E2a']", g.getSupportedEncodings());
+
+		gb = new EncoderGroupBuilder(g).append(E5.class);
+		g = gb.build();
+		assertObjectEquals("['E5','E3','E4','E4a','E1','E2','E2a']", g.getSupportedEncodings());
+	}
+	
+	public static class E1 extends GzipEncoder {
+		@Override /* Encoder */
+		public String[] getCodings() {
+			return new String[]{"E1"};
+		}
+	}
+	
+	public static class E2 extends GzipEncoder {
+		@Override /* Encoder */
+		public String[] getCodings() {
+			return new String[]{"E2","E2a"};
+		}
+	}
+
+	public static class E3 extends GzipEncoder {
+		@Override /* Encoder */
+		public String[] getCodings() {
+			return new String[]{"E3"};
+		}
+	}
+	
+	public static class E4 extends GzipEncoder {
+		@Override /* Encoder */
+		public String[] getCodings() {
+			return new String[]{"E4","E4a"};
+		}
+	}
+	
+	public static class E5 extends GzipEncoder {
+		@Override /* Encoder */
+		public String[] getCodings() {
+			return new String[]{"E5"};
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core-test/src/test/java/org/apache/juneau/https/AcceptExtensionsTest.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/https/AcceptExtensionsTest.java b/juneau-core-test/src/test/java/org/apache/juneau/https/AcceptExtensionsTest.java
new file mode 100644
index 0000000..be8b0f0
--- /dev/null
+++ b/juneau-core-test/src/test/java/org/apache/juneau/https/AcceptExtensionsTest.java
@@ -0,0 +1,118 @@
+// ***************************************************************************************************************************
+// * 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.https;
+
+import static org.apache.juneau.TestUtils.*;
+import static org.junit.Assert.*;
+
+import org.apache.juneau.http.*;
+import org.junit.*;
+
+/**
+ * Verifies that the Accept class handles parameters and extensions correctly.
+ */
+public class AcceptExtensionsTest {
+
+	//--------------------------------------------------------------------------------
+	// Verifies that media type parameters are distinguished from media range extensions.
+	//--------------------------------------------------------------------------------
+	@Test
+	public void testExtensions() throws Exception {
+		Accept accept;
+		MediaTypeRange mr;
+		
+		accept = Accept.forString("text/json");
+		mr = accept.getMediaRanges().get(0);
+		assertTextEquals("text/json", mr);
+		assertTextEquals("text/json", mr.getMediaType());
+		assertObjectEquals("{}", mr.getMediaType().getParameters());
+		assertTextEquals("1.0", mr.getQValue());
+		assertObjectEquals("{}", mr.getExtensions());
+
+		accept = Accept.forString("foo,bar");
+		mr = accept.getMediaRanges().get(0);
+		assertTextEquals("foo", mr);
+		assertTextEquals("foo", mr.getMediaType());
+		assertObjectEquals("{}", mr.getMediaType().getParameters());
+		assertTextEquals("1.0", mr.getQValue());
+		assertObjectEquals("{}", mr.getExtensions());
+
+		accept = Accept.forString(" foo , bar ");
+		mr = accept.getMediaRanges().get(0);
+		assertTextEquals("foo", mr);
+		assertTextEquals("foo", mr.getMediaType());
+		assertObjectEquals("{}", mr.getMediaType().getParameters());
+		assertTextEquals("1.0", mr.getQValue());
+		assertObjectEquals("{}", mr.getExtensions());
+
+		accept = Accept.forString("text/json;a=1;q=0.9;b=2");
+		mr = accept.getMediaRanges().get(0);
+		assertTextEquals("text/json;a=1;q=0.9;b=2", mr);
+		assertTextEquals("text/json;a=1", mr.getMediaType());
+		assertObjectEquals("{a:['1']}", mr.getMediaType().getParameters());
+		assertTextEquals("0.9", mr.getQValue());
+		assertObjectEquals("{b:['2']}", mr.getExtensions());
+		
+		accept = Accept.forString("text/json;a=1;a=2;q=0.9;b=3;b=4");
+		mr = accept.getMediaRanges().get(0);
+		assertTextEquals("text/json;a=1;a=2;q=0.9;b=3;b=4", mr);
+		assertTextEquals("text/json;a=1;a=2", mr.getMediaType());
+		assertObjectEquals("{a:['1','2']}", mr.getMediaType().getParameters());
+		assertTextEquals("0.9", mr.getQValue());
+		assertObjectEquals("{b:['3','4']}", mr.getExtensions());
+
+		accept = Accept.forString("text/json;a=1");
+		mr = accept.getMediaRanges().get(0);
+		assertTextEquals("text/json;a=1", mr);
+		assertTextEquals("text/json;a=1", mr.getMediaType());
+		assertObjectEquals("{a:['1']}", mr.getMediaType().getParameters());
+		assertTextEquals("1.0", mr.getQValue());
+		assertObjectEquals("{}", mr.getExtensions());
+
+		accept = Accept.forString("text/json;a=1;");
+		mr = accept.getMediaRanges().get(0);
+		assertTextEquals("text/json;a=1", mr);
+		assertTextEquals("text/json;a=1", mr.getMediaType());
+		assertObjectEquals("{a:['1']}", mr.getMediaType().getParameters());
+		assertTextEquals("1.0", mr.getQValue());
+		assertObjectEquals("{}", mr.getExtensions());
+		
+		accept = Accept.forString("text/json;q=0.9");
+		mr = accept.getMediaRanges().get(0);
+		assertTextEquals("text/json;q=0.9", mr);
+		assertTextEquals("text/json", mr.getMediaType());
+		assertObjectEquals("{}", mr.getMediaType().getParameters());
+		assertTextEquals("0.9", mr.getQValue());
+		assertObjectEquals("{}", mr.getExtensions());
+
+		accept = Accept.forString("text/json;q=0.9;");
+		mr = accept.getMediaRanges().get(0);
+		assertTextEquals("text/json;q=0.9", mr);
+		assertTextEquals("text/json", mr.getMediaType());
+		assertObjectEquals("{}", mr.getMediaType().getParameters());
+		assertTextEquals("0.9", mr.getQValue());
+		assertObjectEquals("{}", mr.getExtensions());
+	}
+	
+	//--------------------------------------------------------------------------------
+	// Tests the Accept.hasSubtypePart() method.
+	//--------------------------------------------------------------------------------
+	@Test
+	public void testHasSubtypePart() {
+		Accept accept = Accept.forString("text/json+x,text/foo+y;q=0.0");
+		assertTrue(accept.hasSubtypePart("json"));
+		assertTrue(accept.hasSubtypePart("x"));
+		assertFalse(accept.hasSubtypePart("foo"));
+		assertFalse(accept.hasSubtypePart("y"));
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core-test/src/test/java/org/apache/juneau/https/AcceptTest.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/https/AcceptTest.java b/juneau-core-test/src/test/java/org/apache/juneau/https/AcceptTest.java
new file mode 100644
index 0000000..dd905a6
--- /dev/null
+++ b/juneau-core-test/src/test/java/org/apache/juneau/https/AcceptTest.java
@@ -0,0 +1,128 @@
+// ***************************************************************************************************************************
+// * 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.https;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.http.*;
+import org.apache.juneau.json.*;
+import org.junit.*;
+import org.junit.runner.*;
+import org.junit.runners.*;
+
+/**
+ * Verifies that the Accept class handles matching correctly.
+ */
+@RunWith(Parameterized.class)
+public class AcceptTest {
+	@Parameterized.Parameters
+	public static Collection<Object[]> getParameters() {
+		return Arrays.asList(new Object[][] {
+			
+			// label, accept-header, media-types, expected-index
+			
+			// Simple matches
+			{ "SimpleMatch-1", "text/json", "['text/json']", 0 },
+			{ "SimpleMatch-2", "text/json", "['text/json','text/foo']", 0 },
+			{ "SimpleMatch-3", "text/json", "['text/foo','text/json']", 1 },
+			
+			// Simple no-matches
+			{ "SimpleNoMatch-1", "text/jsonx", "['text/json']", -1 },
+			{ "SimpleNoMatch-2", "text/jso", "['text/json']", -1 },
+			{ "SimpleNoMatch-3", "text/json", "['application/json']", -1 },
+			{ "SimpleNoMatch-4", "text/json", "[]", -1 },
+			{ "SimpleNoMatch-5", null, "['text/json']", -1 },
+			
+			// Meta-character matches
+			{ "MetaMatch-1", "text/*", "['text/a','text/b+c','text/b+d+e']", 2 },
+			{ "MetaMatch-2", "text/b+*", "['text/a','text/b+c','text/b+d+e']", 2 },
+			{ "MetaMatch-3", "text/c+*", "['text/a','text/b+c','text/b+d+e']", 1 },
+			{ "MetaMatch-4", "text/b+d+e", "['text/a','text/b+c','text/b+d']", 2 },
+			{ "MetaMatch-5", "text/b+*", "['text/a','text/b+c','text/b+d']", 1 },
+			{ "MetaMatch-6", "text/d+e+*", "['text/a','text/b+c','text/b+d+e']", 2 },
+
+			{ "MetaMatch-7", "*/a", "['text/a','application/a']", 0 },
+			{ "MetaMatch-8", "*/*", "['text/a','text/b+c']", 1 },
+			{ "MetaMatch-9", "*/*", "['text/b+c','text/a']", 0 },
+
+			// Reverse meta-character matches
+			{ "RevMetaMatch-1", "text/a", "['text/*']", 0 },
+			{ "RevMetaMatch-3", "text/a", "['*/a']", 0 },
+			{ "RevMetaMatch-3", "text/a", "['*/*']", 0 },
+			
+			// Meta-character mixture matches
+			{ "MixedMetaMatch-1", "text/*", "['text/*','text/a','text/a+b','text/b+c','text/d+*']", 0 },
+			{ "MixedMetaMatch-2", "*/a", "['text/*','text/a','text/a+b','text/b+c','text/d+*']", 1 },
+			{ "MixedMetaMatch-3", "*/*", "['text/*','text/a','text/a+b','text/b+c','text/d+*']", 0 },
+			{ "MixedMetaMatch-4", "text/a+*", "['text/*','text/a','text/a+b','text/b+c','text/d+*']", 2 },
+			{ "MixedMetaMatch-5", "text/c+*", "['text/*','text/a','text/a+b','text/b+c','text/d+*']", 3 },
+			{ "MixedMetaMatch-6", "text/d+*", "['text/*','text/a','text/a+b','text/b+c','text/d+*']", 4 },
+
+			// Fuzzy matches
+			{ "Fuzzy-1", "text/1+2", "['text/1+2']", 0 },
+			// Order of subtype parts shouldn't matter.
+			{ "Fuzzy-2", "text/2+1", "['text/1+2']", 0 },
+			// Should match if Accept has 'extra' subtypes.
+			// For example, "Accept: text/json+activity" should match against the "text/json" serializer.
+			{ "Fuzzy-3", "text/1+2", "['text/1']", 0 },
+			// Shouldn't match because the accept media type must be at least a subset of the real media type
+			// For example, "Accept: text/json" should not match against the "text/json+lax" serializer.
+			{ "Fuzzy-4", "text/1", "['text/1+2']", -1 },
+			{ "Fuzzy-5", "text/1+2", "['text/1','text/1+3']", 0 },
+			// "text/1+2" should be a better match than just "text/1"
+			{ "Fuzzy-6", "text/1+2", "['text/1','text/1+2','text/1+2+3']", 1 },
+			// Same as last, but mix up the order a bit.
+			{ "Fuzzy-7", "text/1+2", "['text/1+2+3','text/1','text/1+2']", 2 },
+			// Same as last, but mix up the order of the subtypes as well.
+			{ "Fuzzy-8", "text/1+2", "['text/3+2+1','text/1','text/2+1']", 2 },
+			{ "Fuzzy-9", "text/1+2+3+4", "['text/1+2','text/1+2+3']", 1 },
+			{ "Fuzzy-10", "text/1+2+3+4", "['text/1+2+3','text/1+2']", 0 },
+			{ "Fuzzy-11", "text/4+2+3+1", "['text/1+2+3','text/1+2']", 0 },
+			{ "Fuzzy-12", "text/4+2+3+1", "['text/1+2','text/1+2+3']", 1 },
+			
+			// Q metrics
+			{ "Q-1", "text/A;q=0.9,text/B;q=0.1", "['text/A','text/B']", 0 },
+			{ "Q-2", "text/A;q=0.9,text/B;q=0.1", "['text/B','text/A']", 1 },
+			{ "Q-3", "text/A+1;q=0.9,text/B;q=0.1", "['text/A','text/B']", 0 },
+			{ "Q-4", "text/A;q=0.9,text/B+1;q=0.1", "['text/A','text/B+1']", 0 },
+			{ "Q-5", "text/A;q=0.9,text/A+1;q=0.1", "['text/A+1','text/A']", 1 },
+
+			// Test q=0
+			{ "Q0-1", "text/A;q=0,text/B;q=0.1", "['text/A','text/B']", 1 },
+			{ "Q0-2", "text/A;q=0,text/B;q=0.1", "['text/A','text/A+1']", -1 },
+			
+			// Test media types with parameters
+			{ "Parms-1", "text/A", "['text/A;foo=bar','text/B']", 0 },
+			{ "Parms-2", "text/A;foo=bar", "['text/A','text/B']", 0 },
+		});
+	}
+	
+	private String label, accept, mediaTypes;
+	private int expected;
+	
+	public AcceptTest(String label, String accept, String mediaTypes, int expected) {
+		this.label = label;
+		this.accept = accept;
+		this.mediaTypes = mediaTypes;
+		this.expected = expected;
+	}
+	
+	@Test
+	public void test() throws Exception {
+		Accept accept = Accept.forString(this.accept);
+		MediaType[] mt = JsonParser.DEFAULT.parse(mediaTypes, MediaType[].class);
+		int r = accept.findMatch(mt);
+		TestUtils.assertEquals(expected, r, "{0} failed", label);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core-test/src/test/java/org/apache/juneau/https/MediaRangeTest.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/https/MediaRangeTest.java b/juneau-core-test/src/test/java/org/apache/juneau/https/MediaRangeTest.java
new file mode 100644
index 0000000..afeb782
--- /dev/null
+++ b/juneau-core-test/src/test/java/org/apache/juneau/https/MediaRangeTest.java
@@ -0,0 +1,66 @@
+// ***************************************************************************************************************************
+// * 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.https;
+
+import static org.junit.Assert.*;
+
+import java.util.*;
+
+import org.apache.juneau.http.*;
+import org.apache.juneau.json.*;
+import org.junit.*;
+import org.junit.runner.*;
+import org.junit.runners.*;
+
+/**
+ * Verifies that the MediaRange and MediaType classes parse and sort Accept headers correctly.
+ */
+@RunWith(Parameterized.class)
+public class MediaRangeTest {
+	@Parameterized.Parameters
+	public static Collection<Object[]> getParameters() {
+		return Arrays.asList(new Object[][] {
+			{ "0", "text/json", "['text/json']" },
+			{ "1", "text/json,text/*", "['text/json','text/*']" },
+			{ "2", "text/*,text/json", "['text/json','text/*']" },
+			{ "3", "text/*,text/*", "['text/*']" },
+			{ "4", "*/text,text/*", "['text/*','*/text']" },
+			{ "5", "text/*,*/text", "['text/*','*/text']" },
+			{ "6", "a;q=0.9,b;q=0.1", "['a;q=0.9','b;q=0.1']" },
+			{ "7", "b;q=0.9,a;q=0.1", "['b;q=0.9','a;q=0.1']" },
+			{ "8", "a,b;q=0.9,c;q=0.1,d;q=0", "['a','b;q=0.9','c;q=0.1','d;q=0.0']" },
+			{ "9", "d;q=0,c;q=0.1,b;q=0.9,a", "['a','b;q=0.9','c;q=0.1','d;q=0.0']" },
+			{ "10", "a;q=1,b;q=0.9,c;q=0.1,d;q=0", "['a','b;q=0.9','c;q=0.1','d;q=0.0']" },
+			{ "11", "d;q=0,c;q=0.1,b;q=0.9,a;q=1", "['a','b;q=0.9','c;q=0.1','d;q=0.0']" },
+			{ "12", "a;q=0,b;q=0.1,c;q=0.9,d;q=1", "['d','c;q=0.9','b;q=0.1','a;q=0.0']" },
+			{ "13", "*", "['*']" },
+			{ "14", "", "['*/*']" },
+			{ "15", null, "['*/*']" },
+			{ "16", "foo/bar/baz", "['foo/bar/baz']" },
+		});
+	}
+	
+	private String label, mediaRange, expected;
+	
+	public MediaRangeTest(String label, String mediaRange, String expected) {
+		this.label = label;
+		this.mediaRange = mediaRange;
+		this.expected = expected;
+	}
+	
+	@Test
+	public void test() {
+		MediaTypeRange[] r = MediaTypeRange.parse(mediaRange);
+		assertEquals(label + " failed", expected, JsonSerializer.DEFAULT_LAX.toString(r));
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core-test/src/test/java/org/apache/juneau/parser/ParserGroupTest.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/parser/ParserGroupTest.java b/juneau-core-test/src/test/java/org/apache/juneau/parser/ParserGroupTest.java
new file mode 100755
index 0000000..79d3b11
--- /dev/null
+++ b/juneau-core-test/src/test/java/org/apache/juneau/parser/ParserGroupTest.java
@@ -0,0 +1,125 @@
+// ***************************************************************************************************************************
+// * 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.parser;
+
+import static org.apache.juneau.TestUtils.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.json.*;
+import org.junit.*;
+
+@SuppressWarnings({"javadoc"})
+public class ParserGroupTest {
+
+	//====================================================================================================
+	// Test parser group matching
+	//====================================================================================================
+	@Test
+	public void testParserGroupMatching() throws Exception {
+		
+		ParserGroup g = new ParserGroupBuilder().append(Parser1.class, Parser2.class, Parser3.class).build();
+		assertType(Parser1.class, g.getParser("text/foo"));
+		assertType(Parser1.class, g.getParser("text/foo_a"));
+		assertType(Parser1.class, g.getParser("text/foo_a+xxx"));
+		assertType(Parser1.class, g.getParser("text/xxx+foo_a"));
+		assertType(Parser2.class, g.getParser("text/foo+bar"));
+		assertType(Parser2.class, g.getParser("text/foo+bar_a"));
+		assertType(Parser2.class, g.getParser("text/bar+foo"));
+		assertType(Parser2.class, g.getParser("text/bar+foo+xxx"));
+		assertType(Parser3.class, g.getParser("text/baz"));
+		assertType(Parser3.class, g.getParser("text/baz_a"));
+		assertType(Parser3.class, g.getParser("text/baz+yyy"));
+		assertType(Parser3.class, g.getParser("text/baz_a+yyy"));
+		assertType(Parser3.class, g.getParser("text/yyy+baz"));
+		assertType(Parser3.class, g.getParser("text/yyy+baz_a"));
+	}
+	
+	
+	@Consumes("text/foo,text/foo_a")
+	public static class Parser1 extends JsonParser {
+		public Parser1(PropertyStore propertyStore) {
+			super(propertyStore);
+		}
+	}
+	
+	@Consumes("text/foo+bar,text/foo+bar_a")
+	public static class Parser2 extends JsonParser {
+		public Parser2(PropertyStore propertyStore) {
+			super(propertyStore);
+		}
+	}
+
+	@Consumes("text/baz,text/baz_a")
+	public static class Parser3 extends JsonParser {
+		public Parser3(PropertyStore propertyStore) {
+			super(propertyStore);
+		}
+	}
+	
+	//====================================================================================================
+	// Test inheritence
+	//====================================================================================================
+	@Test
+	public void testInheritence() throws Exception {
+		ParserGroupBuilder gb = null;
+		ParserGroup g = null;
+		
+		gb = new ParserGroupBuilder().append(P1.class, P2.class);
+		g = gb.build();
+		assertObjectEquals("['text/1','text/2','text/2a']", g.getSupportedMediaTypes());
+		
+		gb = new ParserGroupBuilder(g).append(P3.class, P4.class);
+		g = gb.build();
+		assertObjectEquals("['text/3','text/4','text/4a','text/1','text/2','text/2a']", g.getSupportedMediaTypes());
+
+		gb = new ParserGroupBuilder(g).append(P5.class);
+		g = gb.build();
+		assertObjectEquals("['text/5','text/3','text/4','text/4a','text/1','text/2','text/2a']", g.getSupportedMediaTypes());
+	}
+	
+	@Consumes("text/1")
+	public static class P1 extends JsonParser {
+		public P1(PropertyStore propertyStore) {
+			super(propertyStore);
+		}
+	}
+	
+	@Consumes("text/2,text/2a")
+	public static class P2 extends JsonParser {
+		public P2(PropertyStore propertyStore) {
+			super(propertyStore);
+		}
+	}
+
+	@Consumes("text/3")
+	public static class P3 extends JsonParser {
+		public P3(PropertyStore propertyStore) {
+			super(propertyStore);
+		}
+	}
+	
+	@Consumes("text/4,text/4a")
+	public static class P4 extends JsonParser {
+		public P4(PropertyStore propertyStore) {
+			super(propertyStore);
+		}
+	}
+	
+	@Consumes("text/5")
+	public static class P5 extends JsonParser {
+		public P5(PropertyStore propertyStore) {
+			super(propertyStore);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core-test/src/test/java/org/apache/juneau/serializer/SerializerGroupTest.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/serializer/SerializerGroupTest.java b/juneau-core-test/src/test/java/org/apache/juneau/serializer/SerializerGroupTest.java
new file mode 100755
index 0000000..2bcf413
--- /dev/null
+++ b/juneau-core-test/src/test/java/org/apache/juneau/serializer/SerializerGroupTest.java
@@ -0,0 +1,166 @@
+// ***************************************************************************************************************************
+// * 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.serializer;
+
+import static org.apache.juneau.TestUtils.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.json.*;
+import org.junit.*;
+
+@SuppressWarnings({"javadoc"})
+public class SerializerGroupTest {
+
+	//====================================================================================================
+	// Trim nulls from beans
+	//====================================================================================================
+	@Test
+	public void testSerializerGroupMatching() throws Exception {
+		
+		SerializerGroup sg = new SerializerGroupBuilder().append(SA1.class, SA2.class, SA3.class).build();
+		assertType(SA1.class, sg.getSerializer("text/foo"));
+		assertType(SA1.class, sg.getSerializer("text/foo_a"));
+		assertType(SA1.class, sg.getSerializer("text/xxx+foo_a"));
+		assertType(SA1.class, sg.getSerializer("text/foo_a+xxx"));
+		assertType(SA2.class, sg.getSerializer("text/foo+bar"));
+		assertType(SA2.class, sg.getSerializer("text/foo+bar_a"));
+		assertType(SA2.class, sg.getSerializer("text/bar+foo"));
+		assertType(SA2.class, sg.getSerializer("text/bar_a+foo"));
+		assertType(SA2.class, sg.getSerializer("text/bar+foo+xxx"));
+		assertType(SA2.class, sg.getSerializer("text/bar_a+foo+xxx"));
+		assertType(SA3.class, sg.getSerializer("text/baz"));
+		assertType(SA3.class, sg.getSerializer("text/baz_a"));
+		assertType(SA3.class, sg.getSerializer("text/baz+yyy"));
+		assertType(SA3.class, sg.getSerializer("text/baz_a+yyy"));
+		assertType(SA3.class, sg.getSerializer("text/yyy+baz"));
+		assertType(SA3.class, sg.getSerializer("text/yyy+baz_a"));
+		
+		assertType(SA1.class, sg.getSerializer("text/foo;q=0.9,text/foo+bar;q=0.8"));
+		assertType(SA2.class, sg.getSerializer("text/foo;q=0.8,text/foo+bar;q=0.9"));
+	}
+	
+	
+	@Produces("text/foo,text/foo_a")
+	public static class SA1 extends JsonSerializer {
+		public SA1(PropertyStore propertyStore) {
+			super(propertyStore);
+		}
+	}
+	
+	@Produces("text/foo+bar,text/foo+bar_a")
+	public static class SA2 extends JsonSerializer {
+		public SA2(PropertyStore propertyStore) {
+			super(propertyStore);
+		}
+	}
+
+	@Produces("text/baz,text/baz_a")
+	public static class SA3 extends JsonSerializer {
+		public SA3(PropertyStore propertyStore) {
+			super(propertyStore);
+		}
+	}
+
+	//====================================================================================================
+	// Test inheritence
+	//====================================================================================================
+	@Test
+	public void testInheritence() throws Exception {
+		SerializerGroupBuilder gb = null;
+		SerializerGroup g = null;
+		
+		gb = new SerializerGroupBuilder().append(SB1.class, SB2.class);
+		g = gb.build();
+		assertObjectEquals("['text/1','text/2','text/2a']", g.getSupportedMediaTypes());
+		
+		gb = new SerializerGroupBuilder(g).append(SB3.class, SB4.class);
+		g = gb.build();
+		assertObjectEquals("['text/3','text/4','text/4a','text/1','text/2','text/2a']", g.getSupportedMediaTypes());
+
+		gb = new SerializerGroupBuilder(g).append(SB5.class);
+		g = gb.build();
+		assertObjectEquals("['text/5','text/3','text/4','text/4a','text/1','text/2','text/2a']", g.getSupportedMediaTypes());
+	}
+	
+	@Produces("text/1")
+	public static class SB1 extends JsonSerializer {
+		public SB1(PropertyStore propertyStore) {
+			super(propertyStore);
+		}
+	}
+	
+	@Produces("text/2,text/2a")
+	public static class SB2 extends JsonSerializer {
+		public SB2(PropertyStore propertyStore) {
+			super(propertyStore);
+		}
+	}
+
+	@Produces("text/3")
+	public static class SB3 extends JsonSerializer {
+		public SB3(PropertyStore propertyStore) {
+			super(propertyStore);
+		}
+	}
+	
+	@Produces("text/4,text/4a")
+	public static class SB4 extends JsonSerializer {
+		public SB4(PropertyStore propertyStore) {
+			super(propertyStore);
+		}
+	}
+	
+	@Produces("text/5")
+	public static class SB5 extends JsonSerializer {
+		public SB5(PropertyStore propertyStore) {
+			super(propertyStore);
+		}
+	}
+	
+	//====================================================================================================
+	// Test media type with meta-characters
+	//====================================================================================================
+	@Test
+	public void testMediaTypesWithMetaCharacters() throws Exception {
+		SerializerGroupBuilder gb = null;
+		SerializerGroup g = null;
+		
+		gb = new SerializerGroupBuilder().append(SC1.class, SC2.class, SC3.class);
+		g = gb.build();
+		assertType(SC1.class, g.getSerializer("text/foo"));
+		assertType(SC2.class, g.getSerializer("foo/json"));
+		assertType(SC3.class, g.getSerializer("foo/foo"));
+	}
+	
+	@Produces("text/*")
+	public static class SC1 extends JsonSerializer {
+		public SC1(PropertyStore propertyStore) {
+			super(propertyStore);
+		}
+	}
+	
+	@Produces("*/json")
+	public static class SC2 extends JsonSerializer {
+		public SC2(PropertyStore propertyStore) {
+			super(propertyStore);
+		}
+	}
+	
+	@Produces("*/*")
+	public static class SC3 extends JsonSerializer {
+		public SC3(PropertyStore propertyStore) {
+			super(propertyStore);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/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
index bc4b94e..7bd042e 100755
--- 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
@@ -16,6 +16,8 @@ import static org.apache.juneau.TestUtils.*;
 import static org.apache.juneau.internal.CollectionUtils.*;
 import static org.junit.Assert.*;
 
+import java.util.*;
+
 import org.apache.juneau.*;
 import org.junit.*;
 
@@ -27,7 +29,7 @@ public class CollectionUtilsTest {
 	//====================================================================================================
 	@Test
 	public void testReverse() throws Exception {
-		assertNull(reverse(null));
+		assertNull(reverse((Map<?,?>)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/c3609d05/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java b/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java
index 95c340b..f74d75e 100755
--- a/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java
+++ b/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java
@@ -700,4 +700,21 @@ public class StringUtilsTest {
 		in = null;
 		assertEquals("", getField(0, in, ','));
 	}
+
+	//====================================================================================================
+	// parseMap(String,char,char,boolean)
+	//====================================================================================================
+	@Test
+	public void testSplitMap() {
+		assertObjectEquals("{a:'1'}", splitMap("a=1", ',', '=', true));
+		assertObjectEquals("{a:'1',b:'2'}", splitMap("a=1,b=2", ',', '=', true));
+		assertObjectEquals("{a:'1',b:'2'}", splitMap(" a = 1 , b = 2 ", ',', '=', true));
+		assertObjectEquals("{' a ':' 1 ',' b ':' 2 '}", splitMap(" a = 1 , b = 2 ", ',', '=', false));
+		assertObjectEquals("{a:''}", splitMap("a", ',', '=', true));
+		assertObjectEquals("{a:'',b:''}", splitMap("a,b", ',', '=', true));
+		assertObjectEquals("{a:'1',b:''}", splitMap("a=1,b", ',', '=', true));
+		assertObjectEquals("{a:'',b:'1'}", splitMap("a,b=1", ',', '=', true));
+		assertObjectEquals("{'a=':'1'}", splitMap("a\\==1", ',', '=', true));
+		assertObjectEquals("{'a\\\\':'1'}", splitMap("a\\\\=1", ',', '=', true));
+	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/BeanContext.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanContext.java b/juneau-core/src/main/java/org/apache/juneau/BeanContext.java
index 0977ba0..c5df1d1 100644
--- a/juneau-core/src/main/java/org/apache/juneau/BeanContext.java
+++ b/juneau-core/src/main/java/org/apache/juneau/BeanContext.java
@@ -23,6 +23,7 @@ import java.util.*;
 import java.util.concurrent.*;
 
 import org.apache.juneau.annotation.*;
+import org.apache.juneau.http.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.parser.*;
 import org.apache.juneau.serializer.*;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/BeanSession.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanSession.java b/juneau-core/src/main/java/org/apache/juneau/BeanSession.java
index ad1cee6..525f0e3 100644
--- a/juneau-core/src/main/java/org/apache/juneau/BeanSession.java
+++ b/juneau-core/src/main/java/org/apache/juneau/BeanSession.java
@@ -20,6 +20,7 @@ import java.lang.reflect.*;
 import java.util.*;
 import java.util.concurrent.atomic.*;
 
+import org.apache.juneau.http.*;
 import org.apache.juneau.internal.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.parser.*;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/CoreObjectBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/CoreObjectBuilder.java b/juneau-core/src/main/java/org/apache/juneau/CoreObjectBuilder.java
index 2f01917..f566a92 100644
--- a/juneau-core/src/main/java/org/apache/juneau/CoreObjectBuilder.java
+++ b/juneau-core/src/main/java/org/apache/juneau/CoreObjectBuilder.java
@@ -20,6 +20,7 @@ import java.lang.reflect.*;
 import java.util.*;
 
 import org.apache.juneau.annotation.*;
+import org.apache.juneau.http.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.serializer.*;
 import org.apache.juneau.transform.*;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/MediaRange.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/MediaRange.java b/juneau-core/src/main/java/org/apache/juneau/MediaRange.java
deleted file mode 100644
index 5a21882..0000000
--- a/juneau-core/src/main/java/org/apache/juneau/MediaRange.java
+++ /dev/null
@@ -1,321 +0,0 @@
-// ***************************************************************************************************************************
-// * 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;
-
-import java.util.*;
-import java.util.Map.*;
-
-import org.apache.juneau.annotation.*;
-import org.apache.juneau.internal.*;
-
-/**
- * Describes a single type used in content negotiation between an HTTP client and server, as described in
- * Section 14.1 and 14.7 of RFC2616 (the HTTP/1.1 specification).
- */
-@BeanIgnore
-public final class MediaRange implements Comparable<MediaRange>  {
-
-	private static final MediaRange[] DEFAULT = new MediaRange[]{new MediaRange("*/*")};
-
-	private final MediaType mediaType;
-	private final Float qValue;
-	private final Map<String,Set<String>> parameters, extensions;
-
-	/**
-	 * Parses a media range fragement of an <code>Accept</code> header value into a single media range object..
-	 * <p>
-	 * The syntax expected to be found in the referenced <code>value</code> complies with the syntax described in RFC2616, Section 14.1, as described below:
-	 * <p class='bcode'>
-	 * 	media-range    = ( "*\/*"
-	 * 	                  | ( type "/" "*" )
-	 * 	                  | ( type "/" subtype )
-	 * 	                  ) *( ";" parameter )
-	 * 	accept-params  = ";" "q" "=" qvalue *( accept-extension )
-	 * 	accept-extension = ";" token [ "=" ( token | quoted-string ) ]
-	 * </p>
-	 * @param mediaRangeFragment The media range fragement string.
-	 */
-	private MediaRange(String mediaRangeFragment) {
-
-		String r = mediaRangeFragment;
-		Float _qValue = 1f;
-		MediaType _mediaType = null;
-		Map<String,Set<String>> _parameters = null;
-		Map<String,Set<String>> _extensions = null;
-
-		r = r.trim();
-
-		int i = r.indexOf(';');
-
-		if (i == -1) {
-			_mediaType = MediaType.forString(r);
-
-		} else {
-
-			_mediaType = MediaType.forString(r.substring(0, i));
-
-			String[] tokens = r.substring(i+1).split(";");
-
-			// Only the type of the range is specified
-			if (tokens.length > 0) {
-
-				boolean isInExtensions = false;
-				for (int j = 0; j < tokens.length; j++) {
-					String[] parm = tokens[j].split("=");
-					if (parm.length == 2) {
-						String k = parm[0], v = parm[1];
-						if (isInExtensions) {
-							if (_extensions == null)
-								_extensions = new TreeMap<String,Set<String>>();
-							if (! _extensions.containsKey(parm[0]))
-								_extensions.put(parm[0], new TreeSet<String>());
-							_extensions.get(parm[0]).add(parm[1]);
-						} else if (k.equals("q")) {
-							_qValue = new Float(v);
-							isInExtensions = true;
-						} else /*(! isInExtensions)*/ {
-							if (_parameters == null)
-								_parameters = new TreeMap<String,Set<String>>();
-							if (! _parameters.containsKey(parm[0]))
-								_parameters.put(parm[0], new TreeSet<String>());
-							_parameters.get(parm[0]).add(parm[1]);
-						}
-					}
-				}
-			}
-		}
-		if (_parameters == null)
-			_parameters = Collections.emptyMap();
-		if (_extensions == null)
-			_extensions = Collections.emptyMap();
-
-		this.mediaType = _mediaType;
-		this.parameters = _parameters;
-		this.qValue = _qValue;
-		this.extensions = _extensions;
-	}
-
-	/**
-	 * Returns the media type enclosed by this media range.
-	 *
-	 * <h5 class='section'>Examples:</h5>
-	 * <ul>
-	 * 	<li><js>"text/html"</js>
-	 * 	<li><js>"text/*"</js>
-	 * 	<li><js>"*\/*"</js>
-	 * </ul>
-	 *
-	 * @return The media type of this media range, lowercased, never <jk>null</jk>.
-	 */
-	public MediaType getMediaType() {
-		return mediaType;
-	}
-
-	/**
-	 * Returns the <js>'q'</js> (quality) value for this type, as described in Section 3.9 of RFC2616.
-	 * <p>
-	 * The quality value is a float between <code>0.0</code> (unacceptable) and <code>1.0</code> (most acceptable).
-	 * <p>
-	 * If 'q' value doesn't make sense for the context (e.g. this range was extracted from a <js>"content-*"</js> header, as opposed to <js>"accept-*"</js>
-	 * header, its value will always be <js>"1"</js>.
-	 *
-	 * @return The 'q' value for this type, never <jk>null</jk>.
-	 */
-	public Float getQValue() {
-		return qValue;
-	}
-
-	/**
-	 * Returns the optional set of parameters associated to the type as returned by {@link #getMediaType()}.
-	 * <p>
-	 * The parameters are those values as described in standardized MIME syntax.
-	 * An example of such a parameter in string form might be <js>"level=1"</js>.
-	 * <p>
-	 * Values are lowercase and never <jk>null</jk>.
-	 *
-	 * @return The optional list of parameters, never <jk>null</jk>.
-	 */
-	public Map<String,Set<String>> getParameters() {
-		return parameters;
-	}
-
-	/**
-	 * Returns the optional set of custom extensions defined for this type.
-	 * <p>
-	 * Values are lowercase and never <jk>null</jk>.
-	 *
-	 * @return The optional list of extensions, never <jk>null</jk>.
-	 */
-	public Map<String,Set<String>> getExtensions() {
-		return extensions;
-	}
-
-	/**
-	 * Provides a string representation of this media range, suitable for use as an <code>Accept</code> header value.
-	 * <p>
-	 * The literal text generated will be all lowercase.
-	 *
-	 * @return A media range suitable for use as an Accept header value, never <code>null</code>.
-	 */
-	@Override /* Object */
-	public String toString() {
-		StringBuffer sb = new StringBuffer().append(mediaType);
-
-		for (Entry<String,Set<String>> e : parameters.entrySet()) {
-			String k = e.getKey();
-			for (String v : e.getValue())
-				sb.append(';').append(k).append('=').append(v);
-		}
-
-		// '1' is equivalent to specifying no qValue. If there's no extensions, then we won't include a qValue.
-		if (qValue.floatValue() == 1.0) {
-			if (! extensions.isEmpty()) {
-				sb.append(";q=").append(qValue);
-				for (Entry<String,Set<String>> e : extensions.entrySet()) {
-					String k = e.getKey();
-					for (String v : e.getValue())
-						sb.append(';').append(k).append('=').append(v);
-				}
-			}
-		} else {
-			sb.append(";q=").append(qValue);
-			for (Entry<String,Set<String>> e : extensions.entrySet()) {
-				String k = e.getKey();
-				for (String v : e.getValue())
-					sb.append(';').append(k).append('=').append(v);
-			}
-		}
-		return sb.toString();
-	}
-
-	/**
-	 * Returns <jk>true</jk> if the specified object is also a <code>MediaType</code>, and has the same qValue, type, parameters, and extensions.
-	 *
-	 * @return <jk>true</jk> if object is equivalent.
-	 */
-	@Override /* Object */
-	public boolean equals(Object o) {
-
-		if (o == null || !(o instanceof MediaRange))
-			return false;
-
-		if (this == o)
-			return true;
-
-		MediaRange o2 = (MediaRange) o;
-		return qValue.equals(o2.qValue)
-			&& mediaType.equals(o2.mediaType)
-			&& parameters.equals(o2.parameters)
-			&& extensions.equals(o2.extensions);
-	}
-
-	/**
-	 * Returns a hash based on this instance's <code>media-type</code>.
-	 *
-	 * @return A hash based on this instance's <code>media-type</code>.
-	 */
-	@Override /* Object */
-	public int hashCode() {
-		return mediaType.hashCode();
-	}
-
-	/**
-	 * Parses an <code>Accept</code> header value into an array of media ranges.
-	 * <p>
-	 * The returned media ranges are sorted such that the most acceptable media is available at ordinal position <js>'0'</js>, and the least acceptable at position n-1.
-	 * <p>
-	 * The syntax expected to be found in the referenced <code>value</code> complies with the syntax described in RFC2616, Section 14.1, as described below:
-	 * <p class='bcode'>
-	 * 	Accept         = "Accept" ":"
-	 * 	                  #( media-range [ accept-params ] )
-	 *
-	 * 	media-range    = ( "*\/*"
-	 * 	                  | ( type "/" "*" )
-	 * 	                  | ( type "/" subtype )
-	 * 	                  ) *( ";" parameter )
-	 * 	accept-params  = ";" "q" "=" qvalue *( accept-extension )
-	 * 	accept-extension = ";" token [ "=" ( token | quoted-string ) ]
-	 * </p>
-	 * This method can also be used on other headers such as <code>Accept-Charset</code> and <code>Accept-Encoding</code>...
-	 * <p class='bcode'>
-	 * 	Accept-Charset = "Accept-Charset" ":"
-	 * 	1#( ( charset | "*" )[ ";" "q" "=" qvalue ] )
-	 * </p>
-	 *
-	 * @param value The value to parse.  If <jk>null</jk> or empty, returns a single <code>MediaRange</code> is returned that represents all types.
-	 * @return The media ranges described by the string.
-	 * The ranges are sorted such that the most acceptable media is available at ordinal position <js>'0'</js>, and the least acceptable at position n-1.
-	 */
-	public static MediaRange[] parse(String value) {
-
-		if (value == null || value.length() == 0)
-			return DEFAULT;
-
-		value = value.toLowerCase(Locale.ENGLISH);
-
-		if (value.indexOf(',') == -1)
-			return new MediaRange[]{new MediaRange(value)};
-
-		Set<MediaRange> ranges = new TreeSet<MediaRange>();
-
-		for (String r : StringUtils.split(value, ',')) {
-			r = r.trim();
-
-			if (r.isEmpty())
-				continue;
-
-			ranges.add(new MediaRange(r));
-		}
-
-		return ranges.toArray(new MediaRange[ranges.size()]);
-	}
-
-	/**
-	 * Compares two MediaRanges for equality.
-	 * <p>
-	 * The values are first compared according to <code>qValue</code> values.
-	 * Should those values be equal, the <code>type</code> is then lexicographically compared (case-insensitive) in ascending order,
-	 * 	with the <js>"*"</js> type demoted last in that order.
-	 * <code>MediaRanges</code> with the same type but different sub-types are compared - a more specific subtype is
-	 * 	promoted over the 'wildcard' subtype.
-	 * <code>MediaRanges</code> with the same types but with extensions are promoted over those same types with no extensions.
-	 *
-	 * @param o The range to compare to.  Never <jk>null</jk>.
-	 */
-	@Override /* Comparable */
-	public int compareTo(MediaRange o) {
-
-		// Compare q-values.
-		int qCompare = Float.compare(o.qValue, qValue);
-		if (qCompare != 0)
-			return qCompare;
-
-		// Compare media-types.
-		// Note that '*' comes alphabetically before letters, so just do a reverse-alphabetical comparison.
-		int i = o.mediaType.toString().compareTo(mediaType.toString());
-		return i;
-	}
-
-	/**
-	 * Matches the specified media type against this range and returns a q-value
-	 * between 0 and 1 indicating the quality of the match.
-	 *
-	 * @param o The media type to match against.
-	 * @return A float between 0 and 1.  1 is a perfect match.  0 is no match at all.
-	 */
-	public float matches(MediaType o) {
-		if (this.mediaType == o || mediaType.matches(o))
-			return qValue;
-		return 0;
-	}
-}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/MediaType.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/MediaType.java b/juneau-core/src/main/java/org/apache/juneau/MediaType.java
deleted file mode 100644
index 966f445..0000000
--- a/juneau-core/src/main/java/org/apache/juneau/MediaType.java
+++ /dev/null
@@ -1,158 +0,0 @@
-// ***************************************************************************************************************************
-// * 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;
-
-import java.util.*;
-import java.util.concurrent.*;
-
-import org.apache.juneau.annotation.*;
-
-
-/**
- * Describes a single media type used in content negotiation between an HTTP client and server, as described in
- * Section 14.1 and 14.7 of RFC2616 (the HTTP/1.1 specification).
- */
-@BeanIgnore
-public final class MediaType {
-
-	private static final ConcurrentHashMap<String,MediaType> cache = new ConcurrentHashMap<String,MediaType>();
-
-	/** Reusable predefined media type */
-	@SuppressWarnings("javadoc")
-	public static final MediaType
-		CSV = forString("text/csv"),
-		HTML = forString("text/html"),
-		JSON = forString("application/json"),
-		MSGPACK = forString("octal/msgpack"),
-		PLAIN = forString("text/plain"),
-		UON = forString("text/uon"),
-		URLENCODING = forString("application/x-www-form-urlencoded"),
-		XML = forString("text/xml"),
-		XMLSOAP = forString("text/xml+soap"),
-
-		RDF = forString("text/xml+rdf"),
-		RDFABBREV = forString("text/xml+rdf+abbrev"),
-		NTRIPLE = forString("text/n-triple"),
-		TURTLE = forString("text/turtle"),
-		N3 = forString("text/n3")
-	;
-
-	private final String mediaType;
-	private final String type;								// The media type (e.g. "text" for Accept, "utf-8" for Accept-Charset)
-	private final String subType;                   // The media sub-type (e.g. "json" for Accept, not used for Accept-Charset)
-
-	/**
-	 * Returns the media type for the specified string.
-	 * The same media type strings always return the same objects so that these objects
-	 * can be compared for equality using '=='.
-	 * <p>
-	 * <h5 class='section'>Notes:</h5>
-	 * <ul>
-	 * 	<li>Spaces are replaced with <js>'+'</js> characters.
-	 * 		This gets around the issue where passing media type strings with <js>'+'</js> as HTTP GET parameters
-	 * 		get replaced with spaces by your browser.  Since spaces aren't supported by the spec, this
-	 * 		is doesn't break anything.
-	 * 	<li>Anything including and following the <js>';'</js> character is ignored (e.g. <js>";charset=X"</js>).
-	 * </ul>
-	 *
-	 * @param s The media type string.  Will be lowercased.
-	 * 	<br>Returns <jk>null</jk> if input is null.
-	 * @return A cached media type object.
-	 */
-	public static MediaType forString(String s) {
-		if (s == null)
-			return null;
-		MediaType mt = cache.get(s);
-		if (mt == null) {
-			mt = new MediaType(s);
-			cache.putIfAbsent(s, mt);
-		}
-		return cache.get(s);
-	}
-
-	private MediaType(String mt) {
-		int i = mt.indexOf(';');
-		if (i != -1)
-			mt = mt.substring(0, i);
-
-		mt = mt.toLowerCase(Locale.ENGLISH);
-		this.mediaType = mt;
-		String _type = null, _subType = null;
-		if (mt != null) {
-			mt = mt.replace(' ', '+');
-			i = mt.indexOf('/');
-			_type = (i == -1 ? mt : mt.substring(0, i));
-			_subType = (i == -1 ? "*" : mt.substring(i+1));
-		}
-		this.type = _type;
-		this.subType = _subType;
-	}
-
-	/**
-	 * Returns the <js>'type'</js> fragment of the <js>'type/subType'</js> string.
-	 *
-	 * @return The media type.
-	 */
-	public String getType() {
-		return type;
-	}
-
-	/**
-	 * Returns the <js>'subType'</js> fragment of the <js>'type/subType'</js> string.
-	 *
-	 * @return The media subtype.
-	 */
-	public String getSubType() {
-		return subType;
-	}
-
-	/**
-	 * Returns <jk>true</jk> if this media type is a match for the specified media type.
-	 * <p>
-	 * Matches if any of the following is true:
-	 * <ul>
-	 * 	<li>Both type and subtype are the same.
-	 * 	<li>One or both types are <js>'*'</js> and the subtypes are the same.
-	 * 	<li>One or both subtypes are <js>'*'</js> and the types are the same.
-	 * 	<li>Either is <js>'*\/*'</js>.
-	 * </ul>
-	 *
-	 * @param o The media type to compare with.
-	 * @return <jk>true</jk> if the media types match.
-	 */
-	public final boolean matches(MediaType o) {
-		if (this == o)
-			return true;
-
-		if (type.equals(o.type) || (type.equals("*")) || (o.type.equals("*")))
-			if (subType.equals(o.subType) || subType.equals("*") || o.subType.equals("*"))
-				return true;
-
-		return false;
-	}
-
-	@Override /* Object */
-	public String toString() {
-		return mediaType;
-	}
-
-	@Override /* Object */
-	public int hashCode() {
-		return mediaType.hashCode();
-	}
-
-	@Override /* Object */
-	public boolean equals(Object o) {
-		return this == o;
-	}
-}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/Streamable.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/Streamable.java b/juneau-core/src/main/java/org/apache/juneau/Streamable.java
index 6cb8237..84fb539 100644
--- a/juneau-core/src/main/java/org/apache/juneau/Streamable.java
+++ b/juneau-core/src/main/java/org/apache/juneau/Streamable.java
@@ -14,6 +14,8 @@ package org.apache.juneau;
 
 import java.io.*;
 
+import org.apache.juneau.http.*;
+
 /**
  * Interface that identifies that an object can be serialized directly to an output stream.
  * <p>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/Writable.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/Writable.java b/juneau-core/src/main/java/org/apache/juneau/Writable.java
index 20dd7e0..bd9298f 100644
--- a/juneau-core/src/main/java/org/apache/juneau/Writable.java
+++ b/juneau-core/src/main/java/org/apache/juneau/Writable.java
@@ -14,6 +14,8 @@ package org.apache.juneau;
 
 import java.io.*;
 
+import org.apache.juneau.http.*;
+
 /**
  * Interface that identifies that an object can be serialized directly to a writer.
  * <p>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/csv/CsvParserBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/csv/CsvParserBuilder.java b/juneau-core/src/main/java/org/apache/juneau/csv/CsvParserBuilder.java
index a8d1fb1..addbbb7 100644
--- a/juneau-core/src/main/java/org/apache/juneau/csv/CsvParserBuilder.java
+++ b/juneau-core/src/main/java/org/apache/juneau/csv/CsvParserBuilder.java
@@ -15,6 +15,7 @@ package org.apache.juneau.csv;
 import java.util.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.http.*;
 import org.apache.juneau.parser.*;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/csv/CsvParserSession.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/csv/CsvParserSession.java b/juneau-core/src/main/java/org/apache/juneau/csv/CsvParserSession.java
index 151b8b6..548cbd2 100644
--- a/juneau-core/src/main/java/org/apache/juneau/csv/CsvParserSession.java
+++ b/juneau-core/src/main/java/org/apache/juneau/csv/CsvParserSession.java
@@ -17,6 +17,7 @@ import java.lang.reflect.*;
 import java.util.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.http.*;
 import org.apache.juneau.parser.*;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/csv/CsvSerializerBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/csv/CsvSerializerBuilder.java b/juneau-core/src/main/java/org/apache/juneau/csv/CsvSerializerBuilder.java
index 0715bb5..48d7ae3 100644
--- a/juneau-core/src/main/java/org/apache/juneau/csv/CsvSerializerBuilder.java
+++ b/juneau-core/src/main/java/org/apache/juneau/csv/CsvSerializerBuilder.java
@@ -15,6 +15,7 @@ package org.apache.juneau.csv;
 import java.util.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.http.*;
 import org.apache.juneau.serializer.*;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/csv/CsvSerializerSession.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/csv/CsvSerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/csv/CsvSerializerSession.java
index bee169f..163928b 100644
--- a/juneau-core/src/main/java/org/apache/juneau/csv/CsvSerializerSession.java
+++ b/juneau-core/src/main/java/org/apache/juneau/csv/CsvSerializerSession.java
@@ -16,6 +16,7 @@ import java.lang.reflect.*;
 import java.util.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.http.*;
 import org.apache.juneau.serializer.*;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/dto/swagger/Operation.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/dto/swagger/Operation.java b/juneau-core/src/main/java/org/apache/juneau/dto/swagger/Operation.java
index 8dc3e50..ff9a505 100644
--- a/juneau-core/src/main/java/org/apache/juneau/dto/swagger/Operation.java
+++ b/juneau-core/src/main/java/org/apache/juneau/dto/swagger/Operation.java
@@ -14,8 +14,8 @@ package org.apache.juneau.dto.swagger;
 
 import java.util.*;
 
-import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
+import org.apache.juneau.http.*;
 
 /**
  * Describes a single API operation on a path.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/dto/swagger/Swagger.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/dto/swagger/Swagger.java b/juneau-core/src/main/java/org/apache/juneau/dto/swagger/Swagger.java
index c40dbff..134d4e5 100644
--- a/juneau-core/src/main/java/org/apache/juneau/dto/swagger/Swagger.java
+++ b/juneau-core/src/main/java/org/apache/juneau/dto/swagger/Swagger.java
@@ -14,8 +14,8 @@ package org.apache.juneau.dto.swagger;
 
 import java.util.*;
 
-import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
+import org.apache.juneau.http.*;
 import org.apache.juneau.utils.*;
 
 /**