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/09/02 14:11:00 UTC

[38/51] [partial] incubator-juneau git commit: Add project hierarchies, part 1

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/75b0d8ee/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/html/CommonTest.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/html/CommonTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/html/CommonTest.java
new file mode 100755
index 0000000..e83900e
--- /dev/null
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/html/CommonTest.java
@@ -0,0 +1,459 @@
+// ***************************************************************************************************************************
+// * 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.html;
+
+import static org.apache.juneau.TestUtils.*;
+import static org.junit.Assert.*;
+
+import java.net.*;
+import java.net.URI;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.utils.*;
+import org.junit.*;
+
+@SuppressWarnings({"serial","javadoc"})
+public class CommonTest {
+
+	//====================================================================================================
+	// Trim nulls from beans
+	//====================================================================================================
+	@Test
+	public void testTrimNullsFromBeans() throws Exception {
+		HtmlSerializerBuilder s = new HtmlSerializerBuilder().sq().addKeyValueTableHeaders(true);
+		HtmlParser p = HtmlParser.DEFAULT;
+		A t1 = A.create(), t2;
+
+		s.trimNullProperties(false);
+		String r = s.build().serialize(t1);
+		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>s1</td><td><null/></td></tr><tr><td>s2</td><td>s2</td></tr></table>", r);
+		t2 = p.parse(r, A.class);
+		assertEqualObjects(t1, t2);
+
+		s.trimNullProperties(true);
+		r = s.build().serialize(t1);
+		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>s2</td><td>s2</td></tr></table>", r);
+		t2 = p.parse(r, A.class);
+		assertEqualObjects(t1, t2);
+	}
+
+	public static class A {
+		public String s1, s2;
+
+		public static A create() {
+			A t = new A();
+			t.s2 = "s2";
+			return t;
+		}
+	}
+
+	//====================================================================================================
+	// Trim empty maps
+	//====================================================================================================
+	@Test
+	public void testTrimEmptyMaps() throws Exception {
+		HtmlSerializerBuilder s = new HtmlSerializerBuilder().sq().addKeyValueTableHeaders(true);
+		HtmlParser p = HtmlParser.DEFAULT;
+		B t1 = B.create(), t2;
+		String r;
+
+		s.trimEmptyMaps(false);
+		r = s.build().serialize(t1);
+		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>f1</td><td><table><tr><th>key</th><th>value</th></tr></table></td></tr><tr><td>f2</td><td><table><tr><th>key</th><th>value</th></tr><tr><td>f2a</td><td><null/></td></tr><tr><td>f2b</td><td><table><tr><th>key</th><th>value</th></tr><tr><td>s2</td><td>s2</td></tr></table></td></tr></table></td></tr></table>", r);
+		t2 = p.parse(r, B.class);
+		assertEqualObjects(t1, t2);
+
+		s.trimEmptyMaps(true);
+		r = s.build().serialize(t1);
+		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>f2</td><td><table><tr><th>key</th><th>value</th></tr><tr><td>f2a</td><td><null/></td></tr><tr><td>f2b</td><td><table><tr><th>key</th><th>value</th></tr><tr><td>s2</td><td>s2</td></tr></table></td></tr></table></td></tr></table>", r);
+		t2 = p.parse(r, B.class);
+		assertNull(t2.f1);
+	}
+
+	public static class B {
+		public TreeMap<String,A> f1, f2;
+
+		public static B create() {
+			B t = new B();
+			t.f1 = new TreeMap<String,A>();
+			t.f2 = new TreeMap<String,A>(){{put("f2a",null);put("f2b",A.create());}};
+			return t;
+		}
+	}
+
+	//====================================================================================================
+	// Trim empty lists
+	//====================================================================================================
+	@Test
+	public void testTrimEmptyLists() throws Exception {
+		HtmlSerializerBuilder s = new HtmlSerializerBuilder().sq().addKeyValueTableHeaders(true);
+		HtmlParser p = HtmlParser.DEFAULT;
+		C t1 = C.create(), t2;
+		String r;
+
+		s.trimEmptyCollections(false);
+		r = s.build().serialize(t1);
+		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>f1</td><td><ul></ul></td></tr><tr><td>f2</td><td><table _type='array'><tr><th>s2</th></tr><tr><null/></tr><tr><td>s2</td></tr></table></td></tr></table>", r);
+		t2 = p.parse(r, C.class);
+		assertEqualObjects(t1, t2);
+
+		s.trimEmptyCollections(true);
+		r = s.build().serialize(t1);
+		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>f2</td><td><table _type='array'><tr><th>s2</th></tr><tr><null/></tr><tr><td>s2</td></tr></table></td></tr></table>", r);
+		t2 = p.parse(r, C.class);
+		assertNull(t2.f1);
+	}
+
+	public static class C {
+		public List<A> f1, f2;
+
+		public static C create() {
+			C t = new C();
+			t.f1 = new AList<A>();
+			t.f2 = new AList<A>().append(null).append(A.create());
+			return t;
+		}
+	}
+
+	//====================================================================================================
+	// Trim empty arrays
+	//====================================================================================================
+	@Test
+	public void testTrimEmptyArrays() throws Exception {
+		HtmlSerializerBuilder s = new HtmlSerializerBuilder().sq().addKeyValueTableHeaders(true);
+		HtmlParser p = HtmlParser.DEFAULT;
+		D t1 = D.create(), t2;
+		String r;
+
+		s.trimEmptyCollections(false);
+		r = s.build().serialize(t1);
+		assertEquals(
+			"<table>"
+				+"<tr><th>key</th><th>value</th></tr>"
+				+"<tr>"
+					+"<td>f1</td>"
+					+"<td><ul></ul></td>"
+				+"</tr>"
+				+"<tr>"
+					+"<td>f2</td>"
+					+"<td>"
+						+"<table _type='array'>"
+							+"<tr><th>s2</th></tr>"
+							+"<tr><null/></tr>"
+							+"<tr><td>s2</td></tr>"
+						+"</table>"
+					+"</td>"
+				+"</tr>"
+			+"</table>",
+			r);
+		t2 = p.parse(r, D.class);
+		assertEqualObjects(t1, t2);
+
+		s.trimEmptyCollections(true);
+		r = s.build().serialize(t1);
+		assertEquals(
+			"<table>"
+				+"<tr><th>key</th><th>value</th></tr>"
+				+"<tr>"
+					+"<td>f2</td>"
+					+"<td>"
+						+"<table _type='array'>"
+							+"<tr><th>s2</th></tr>"
+							+"<tr><null/></tr>"
+							+"<tr><td>s2</td></tr>"
+						+"</table>"
+					+"</td>"
+				+"</tr>"
+			+"</table>",
+			r);
+		t2 = p.parse(r, D.class);
+		assertNull(t2.f1);
+	}
+
+	public static class D {
+		public A[] f1, f2;
+
+		public static D create() {
+			D t = new D();
+			t.f1 = new A[]{};
+			t.f2 = new A[]{null, A.create()};
+			return t;
+		}
+	}
+
+	//====================================================================================================
+	// @BeanProperty.properties annotation.
+	//====================================================================================================
+	@Test
+	public void testBeanPropertyProperties() throws Exception {
+		HtmlSerializer s = new HtmlSerializerBuilder().sq().addKeyValueTableHeaders(true).build();
+		E1 t = new E1();
+		String r;
+
+		r = s.serialize(t);
+		assertEquals(
+			"<table>"
+				+"<tr>"
+					+"<th>key</th>"
+					+"<th>value</th>"
+				+"</tr>"
+				+"<tr>"
+					+"<td>x1</td>"
+					+"<td>"
+						+"<table>"
+							+"<tr><th>key</th><th>value</th></tr>"
+							+"<tr><td>f1</td><td>1</td></tr>"
+						+"</table>"
+					+"</td>"
+				+"</tr>"
+				+"<tr>"
+					+"<td>x2</td>"
+					+"<td>"
+						+"<table>"
+							+"<tr><th>key</th><th>value</th></tr>"
+							+"<tr><td>f1</td><td>3</td></tr>"
+						+"</table>"
+					+"</td>"
+				+"</tr>"
+				+"<tr>"
+					+"<td>x3</td>"
+					+"<td>"
+						+"<table _type='array'>"
+							+"<tr><th>f1</th></tr>"
+							+"<tr><td>1</td></tr>"
+						+"</table>"
+					+"</td>"
+				+"</tr>"
+				+"<tr>"
+					+"<td>x4</td>"
+					+"<td>"
+						+"<table _type='array'>"
+							+"<tr><th>f1</th></tr>"
+							+"<tr><td>1</td></tr>"
+						+"</table>"
+					+"</td>"
+				+"</tr>"
+				+"<tr>"
+					+"<td>x5</td>"
+					+"<td>"
+						+"<table _type='array'>"
+							+"<tr><th>f1</th></tr>"
+							+"<tr><td><number>5</number></td></tr>"
+						+"</table>"
+					+"</td>"
+				+"</tr>"
+				+"<tr>"
+					+"<td>x6</td>"
+					+"<td>"
+						+"<table _type='array'>"
+							+"<tr><th>f1</th></tr>"
+							+"<tr><td><number>7</number></td></tr>"
+						+"</table>"
+					+"</td>"
+				+"</tr>"
+			+"</table>",
+		r);
+		r = s.getSchemaSerializer().serialize(new E1());
+		assertTrue(r.indexOf("f2") == -1);
+	}
+
+	public static class E1 {
+		@BeanProperty(properties="f1") public E2 x1 = new E2();
+		@BeanProperty(properties="f1") public Map<String,Integer> x2 = new AMap<String,Integer>().append("f1",3).append("f2",4);
+		@BeanProperty(properties="f1") public E2[] x3 = {new E2()};
+		@BeanProperty(properties="f1") public List<E2> x4 = new AList<E2>().append(new E2());
+		@BeanProperty(properties="f1") public ObjectMap[] x5 = {new ObjectMap().append("f1",5).append("f2",6)};
+		@BeanProperty(properties="f1") public List<ObjectMap> x6 = new AList<ObjectMap>().append(new ObjectMap().append("f1",7).append("f2",8));
+	}
+
+	public static class E2 {
+		public int f1 = 1;
+		public int f2 = 2;
+	}
+
+	//====================================================================================================
+	// @BeanProperty.properties annotation on list of beans.
+	//====================================================================================================
+	@Test
+	public void testBeanPropertyPropertiesOnListOfBeans() throws Exception {
+		HtmlSerializer s = HtmlSerializer.DEFAULT_SQ;
+		List<F> l = new LinkedList<F>();
+		F t = new F();
+		t.x1.add(new F());
+		l.add(t);
+		String html = s.serialize(l);
+		assertEquals(
+			"<table _type='array'>"
+				+"<tr><th>x1</th><th>x2</th></tr>"
+				+"<tr>"
+					+"<td>"
+						+"<table _type='array'>"
+							+"<tr><th>x2</th></tr>"
+							+"<tr><td>2</td></tr>"
+						+"</table>"
+					+"</td>"
+					+"<td>2</td>"
+				+"</tr>"
+			+"</table>", html);
+	}
+
+	public static class F {
+		@BeanProperty(properties="x2") public List<F> x1 = new LinkedList<F>();
+		public int x2 = 2;
+	}
+
+	//====================================================================================================
+	// Test that URLs and URIs are serialized and parsed correctly.
+	//====================================================================================================
+	@Test
+	public void testURIAttr() throws Exception {
+		HtmlSerializer s = HtmlSerializer.DEFAULT_SQ;
+		HtmlParser p = HtmlParser.DEFAULT;
+
+		G t = new G();
+		t.uri = new URI("http://uri");
+		t.f1 = new URI("http://f1");
+		t.f2 = new URL("http://f2");
+
+		String html = s.serialize(t);
+		t = p.parse(html, G.class);
+		assertEquals("http://uri", t.uri.toString());
+		assertEquals("http://f1", t.f1.toString());
+		assertEquals("http://f2", t.f2.toString());
+	}
+
+	public static class G {
+		public URI uri;
+		public URI f1;
+		public URL f2;
+	}
+
+
+	//====================================================================================================
+	// Recursion
+	//====================================================================================================
+	@Test
+	public void testRecursion() throws Exception {
+		HtmlSerializerBuilder s = new HtmlSerializerBuilder().sq().addKeyValueTableHeaders(true);
+
+		R1 r1 = new R1();
+		R2 r2 = new R2();
+		R3 r3 = new R3();
+		r1.r2 = r2;
+		r2.r3 = r3;
+		r3.r1 = r1;
+
+		// No recursion detection
+		try {
+			s.build().serialize(r1);
+			fail("Exception expected!");
+		} catch (Exception e) {
+			String msg = e.getLocalizedMessage();
+			assertTrue(msg.contains("It's recommended you use the SerializerContext.SERIALIZER_detectRecursions setting to help locate the loop."));
+		}
+
+		// Recursion detection, no ignore
+		s.detectRecursions(true);
+		try {
+			s.build().serialize(r1);
+			fail("Exception expected!");
+		} catch (Exception e) {
+			String msg = e.getLocalizedMessage();
+			assertTrue(msg.contains("[0]<noname>:org.apache.juneau.html.CommonTest$R1"));
+			assertTrue(msg.contains("->[1]r2:org.apache.juneau.html.CommonTest$R2"));
+			assertTrue(msg.contains("->[2]r3:org.apache.juneau.html.CommonTest$R3"));
+			assertTrue(msg.contains("->[3]r1:org.apache.juneau.html.CommonTest$R1"));
+		}
+
+		s.ignoreRecursions(true);
+		assertEquals(
+			"<table><tr><th>key</th><th>value</th></tr><tr><td>name</td><td>foo</td></tr><tr><td>r2</td><td><table><tr><th>key</th><th>value</th></tr><tr><td>name</td><td>bar</td></tr><tr><td>r3</td><td><table><tr><th>key</th><th>value</th></tr><tr><td>name</td><td>baz</td></tr></table></td></tr></table></td></tr></table>",
+			s.build().serialize(r1)
+		);
+
+		// Make sure this doesn't blow up.
+		s.build().getSchemaSerializer().serialize(r1);
+	}
+
+	public static class R1 {
+		public String name = "foo";
+		public R2 r2;
+	}
+	public static class R2 {
+		public String name = "bar";
+		public R3 r3;
+	}
+	public static class R3 {
+		public String name = "baz";
+		public R1 r1;
+	}
+
+	//====================================================================================================
+	// Basic bean
+	//====================================================================================================
+	@Test
+	public void testBasicBean() throws Exception {
+		WriterSerializer s = new HtmlSerializerBuilder().sq().trimNullProperties(false).sortProperties(true).addKeyValueTableHeaders(true).build();
+
+		J a = new J();
+		a.setF1("J");
+		a.setF2(100);
+		a.setF3(true);
+		assertEquals(
+			"<table>"
+				+"<tr><th>key</th><th>value</th></tr>"
+				+"<tr><td>f1</td><td>J</td></tr>"
+				+"<tr><td>f2</td><td>100</td></tr>"
+				+"<tr><td>f3</td><td>true</td></tr>"
+			+"</table>",
+			s.serialize(a));
+	}
+
+	public static class J {
+		private String f1 = null;
+		private int f2 = -1;
+		private boolean f3 = false;
+
+		public String getF1() {
+			return this.f1;
+		}
+
+		public void setF1(String f1) {
+			this.f1 = f1;
+		}
+
+		public int getF2() {
+			return this.f2;
+		}
+
+		public void setF2(int f2) {
+			this.f2 = f2;
+		}
+
+		public boolean isF3() {
+			return this.f3;
+		}
+
+		public void setF3(boolean f3) {
+			this.f3 = f3;
+		}
+
+		@Override /* Object */
+		public String toString() {
+			return ("J(f1: " + this.getF1() + ", f2: " + this.getF2() + ")");
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/75b0d8ee/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/html/HtmlTest.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/html/HtmlTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/html/HtmlTest.java
new file mode 100755
index 0000000..9d76201
--- /dev/null
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/html/HtmlTest.java
@@ -0,0 +1,287 @@
+// ***************************************************************************************************************************
+// * 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.html;
+
+import static org.apache.juneau.html.HtmlSerializerContext.*;
+import static org.junit.Assert.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.html.annotation.*;
+import org.apache.juneau.testbeans.*;
+import org.junit.*;
+
+@SuppressWarnings({"javadoc","unchecked","rawtypes","serial"})
+public class HtmlTest {
+
+	//====================================================================================================
+	// Verifies that lists of maps/beans are converted to tables correctly.
+	//====================================================================================================
+	@Test
+	public void testTables1() throws Exception {
+		HtmlSerializer s = HtmlSerializer.DEFAULT_SQ;
+		Object[] t;
+		String html;
+
+		t = new Object[] {new A1(), new A1()};
+		html = s.serialize(t);
+		assertEquals("<table _type='array'><tr><th>f1</th></tr><tr><td>f1</td></tr><tr><td>f1</td></tr></table>", html);
+
+	}
+
+	public static class A1 {
+		public String f1 = "f1";
+	}
+
+	//====================================================================================================
+	// Test URI_ANCHOR_SET options
+	//====================================================================================================
+	@Test
+	public void testAnchorTextOptions() throws Exception {
+		HtmlSerializerBuilder s = new HtmlSerializerBuilder().sq().addKeyValueTableHeaders(true).uriResolution(UriResolution.NONE);
+		TestURI t = new TestURI();
+		String r;
+		String expected = null;
+
+		s.uriAnchorText(TO_STRING);
+		r = strip(s.build().serialize(t));
+		expected = ""
+			+"\n[f0]=<a href='f0/x0'>f0/x0</a>"
+			+"\n[f1]=<a href='f1/x1'>f1/x1</a>"
+			+"\n[f2]=<a href='/f2/x2'>/f2/x2</a>"
+			+"\n[f3]=<a href='http://www.apache.org/f3/x3'>http://www.apache.org/f3/x3</a>"
+			+"\n[f4]=<a href='f4/x4'>f4/x4</a>"
+			+"\n[f5]=<a href='/f5/x5'>/f5/x5</a>"
+			+"\n[f6]=<a href='http://www.apache.org/f6/x6'>http://www.apache.org/f6/x6</a>"
+			+"\n[f7]=<a href='http://www.apache.org/f7/x7'>http://www.apache.org/f7/x7</a>"
+			+"\n[f8]=<a href='f8/x8'>f8/x8</a>"
+			+"\n[f9]=<a href='f9/x9'>f9/x9</a>"
+			+"\n[fa]=<a href='http://www.apache.org/fa/xa#MY_LABEL'>http://www.apache.org/fa/xa#MY_LABEL</a>"
+			+"\n[fb]=<a href='http://www.apache.org/fb/xb?label=MY_LABEL&amp;foo=bar'>MY_LABEL</a>"
+			+"\n[fc]=<a href='http://www.apache.org/fc/xc?foo=bar&amp;label=MY_LABEL'>MY_LABEL</a>"
+			+"\n[fd]=<a href='http://www.apache.org/fd/xd?label2=MY_LABEL&amp;foo=bar'>http://www.apache.org/fd/xd?label2=MY_LABEL&amp;foo=bar</a>"
+			+"\n[fe]=<a href='http://www.apache.org/fe/xe?foo=bar&amp;label2=MY_LABEL'>http://www.apache.org/fe/xe?foo=bar&amp;label2=MY_LABEL</a>";
+		assertEquals(expected, r);
+
+		s.uriAnchorText(URI);
+		r = strip(s.build().serialize(t));
+		expected = ""
+			+"\n[f0]=<a href='f0/x0'>f0/x0</a>"
+			+"\n[f1]=<a href='f1/x1'>f1/x1</a>"
+			+"\n[f2]=<a href='/f2/x2'>/f2/x2</a>"
+			+"\n[f3]=<a href='http://www.apache.org/f3/x3'>http://www.apache.org/f3/x3</a>"
+			+"\n[f4]=<a href='f4/x4'>f4/x4</a>"
+			+"\n[f5]=<a href='/f5/x5'>/f5/x5</a>"
+			+"\n[f6]=<a href='http://www.apache.org/f6/x6'>http://www.apache.org/f6/x6</a>"
+			+"\n[f7]=<a href='http://www.apache.org/f7/x7'>http://www.apache.org/f7/x7</a>"
+			+"\n[f8]=<a href='f8/x8'>f8/x8</a>"
+			+"\n[f9]=<a href='f9/x9'>f9/x9</a>"
+			+"\n[fa]=<a href='http://www.apache.org/fa/xa#MY_LABEL'>http://www.apache.org/fa/xa#MY_LABEL</a>"
+			+"\n[fb]=<a href='http://www.apache.org/fb/xb?label=MY_LABEL&amp;foo=bar'>MY_LABEL</a>"
+			+"\n[fc]=<a href='http://www.apache.org/fc/xc?foo=bar&amp;label=MY_LABEL'>MY_LABEL</a>"
+			+"\n[fd]=<a href='http://www.apache.org/fd/xd?label2=MY_LABEL&amp;foo=bar'>http://www.apache.org/fd/xd?label2=MY_LABEL&amp;foo=bar</a>"
+			+"\n[fe]=<a href='http://www.apache.org/fe/xe?foo=bar&amp;label2=MY_LABEL'>http://www.apache.org/fe/xe?foo=bar&amp;label2=MY_LABEL</a>";
+		assertEquals(expected, r);
+
+		s.uriAnchorText(LAST_TOKEN);
+		r = strip(s.build().serialize(t));
+		expected = ""
+			+"\n[f0]=<a href='f0/x0'>x0</a>"
+			+"\n[f1]=<a href='f1/x1'>x1</a>"
+			+"\n[f2]=<a href='/f2/x2'>x2</a>"
+			+"\n[f3]=<a href='http://www.apache.org/f3/x3'>x3</a>"
+			+"\n[f4]=<a href='f4/x4'>x4</a>"
+			+"\n[f5]=<a href='/f5/x5'>x5</a>"
+			+"\n[f6]=<a href='http://www.apache.org/f6/x6'>x6</a>"
+			+"\n[f7]=<a href='http://www.apache.org/f7/x7'>x7</a>"
+			+"\n[f8]=<a href='f8/x8'>x8</a>"
+			+"\n[f9]=<a href='f9/x9'>x9</a>"
+			+"\n[fa]=<a href='http://www.apache.org/fa/xa#MY_LABEL'>xa</a>"
+			+"\n[fb]=<a href='http://www.apache.org/fb/xb?label=MY_LABEL&amp;foo=bar'>MY_LABEL</a>"
+			+"\n[fc]=<a href='http://www.apache.org/fc/xc?foo=bar&amp;label=MY_LABEL'>MY_LABEL</a>"
+			+"\n[fd]=<a href='http://www.apache.org/fd/xd?label2=MY_LABEL&amp;foo=bar'>xd</a>"
+			+"\n[fe]=<a href='http://www.apache.org/fe/xe?foo=bar&amp;label2=MY_LABEL'>xe</a>";
+		assertEquals(expected, r);
+
+		s.uriAnchorText(URI_ANCHOR);
+		r = strip(s.build().serialize(t));
+		expected = ""
+			+"\n[f0]=<a href='f0/x0'>f0/x0</a>"
+			+"\n[f1]=<a href='f1/x1'>f1/x1</a>"
+			+"\n[f2]=<a href='/f2/x2'>/f2/x2</a>"
+			+"\n[f3]=<a href='http://www.apache.org/f3/x3'>http://www.apache.org/f3/x3</a>"
+			+"\n[f4]=<a href='f4/x4'>f4/x4</a>"
+			+"\n[f5]=<a href='/f5/x5'>/f5/x5</a>"
+			+"\n[f6]=<a href='http://www.apache.org/f6/x6'>http://www.apache.org/f6/x6</a>"
+			+"\n[f7]=<a href='http://www.apache.org/f7/x7'>http://www.apache.org/f7/x7</a>"
+			+"\n[f8]=<a href='f8/x8'>f8/x8</a>"
+			+"\n[f9]=<a href='f9/x9'>f9/x9</a>"
+			+"\n[fa]=<a href='http://www.apache.org/fa/xa#MY_LABEL'>MY_LABEL</a>"
+			+"\n[fb]=<a href='http://www.apache.org/fb/xb?label=MY_LABEL&amp;foo=bar'>MY_LABEL</a>"
+			+"\n[fc]=<a href='http://www.apache.org/fc/xc?foo=bar&amp;label=MY_LABEL'>MY_LABEL</a>"
+			+"\n[fd]=<a href='http://www.apache.org/fd/xd?label2=MY_LABEL&amp;foo=bar'>http://www.apache.org/fd/xd?label2=MY_LABEL&amp;foo=bar</a>"
+			+"\n[fe]=<a href='http://www.apache.org/fe/xe?foo=bar&amp;label2=MY_LABEL'>http://www.apache.org/fe/xe?foo=bar&amp;label2=MY_LABEL</a>";
+		assertEquals(expected, r);
+
+		s.labelParameter("label2");
+		r = strip(s.build().serialize(t));
+		expected = ""
+			+"\n[f0]=<a href='f0/x0'>f0/x0</a>"
+			+"\n[f1]=<a href='f1/x1'>f1/x1</a>"
+			+"\n[f2]=<a href='/f2/x2'>/f2/x2</a>"
+			+"\n[f3]=<a href='http://www.apache.org/f3/x3'>http://www.apache.org/f3/x3</a>"
+			+"\n[f4]=<a href='f4/x4'>f4/x4</a>"
+			+"\n[f5]=<a href='/f5/x5'>/f5/x5</a>"
+			+"\n[f6]=<a href='http://www.apache.org/f6/x6'>http://www.apache.org/f6/x6</a>"
+			+"\n[f7]=<a href='http://www.apache.org/f7/x7'>http://www.apache.org/f7/x7</a>"
+			+"\n[f8]=<a href='f8/x8'>f8/x8</a>"
+			+"\n[f9]=<a href='f9/x9'>f9/x9</a>"
+			+"\n[fa]=<a href='http://www.apache.org/fa/xa#MY_LABEL'>MY_LABEL</a>"
+			+"\n[fb]=<a href='http://www.apache.org/fb/xb?label=MY_LABEL&amp;foo=bar'>http://www.apache.org/fb/xb?label=MY_LABEL&amp;foo=bar</a>"
+			+"\n[fc]=<a href='http://www.apache.org/fc/xc?foo=bar&amp;label=MY_LABEL'>http://www.apache.org/fc/xc?foo=bar&amp;label=MY_LABEL</a>"
+			+"\n[fd]=<a href='http://www.apache.org/fd/xd?label2=MY_LABEL&amp;foo=bar'>MY_LABEL</a>"
+			+"\n[fe]=<a href='http://www.apache.org/fe/xe?foo=bar&amp;label2=MY_LABEL'>MY_LABEL</a>";
+		assertEquals(expected, r);
+
+		s.detectLinksInStrings(false);
+		r = strip(s.build().serialize(t));
+		expected = ""
+			+"\n[f0]=<a href='f0/x0'>f0/x0</a>"
+			+"\n[f1]=<a href='f1/x1'>f1/x1</a>"
+			+"\n[f2]=<a href='/f2/x2'>/f2/x2</a>"
+			+"\n[f3]=<a href='http://www.apache.org/f3/x3'>http://www.apache.org/f3/x3</a>"
+			+"\n[f4]=<a href='f4/x4'>f4/x4</a>"
+			+"\n[f5]=<a href='/f5/x5'>/f5/x5</a>"
+			+"\n[f6]=<a href='http://www.apache.org/f6/x6'>http://www.apache.org/f6/x6</a>"
+			+"\n[f7]=<a href='http://www.apache.org/f7/x7'>http://www.apache.org/f7/x7</a>"
+			+"\n[f8]=<a href='f8/x8'>f8/x8</a>"
+			+"\n[f9]=<a href='f9/x9'>f9/x9</a>"
+			+"\n[fa]=http://www.apache.org/fa/xa#MY_LABEL"
+			+"\n[fb]=http://www.apache.org/fb/xb?label=MY_LABEL&amp;foo=bar"
+			+"\n[fc]=http://www.apache.org/fc/xc?foo=bar&amp;label=MY_LABEL"
+			+"\n[fd]=http://www.apache.org/fd/xd?label2=MY_LABEL&amp;foo=bar"
+			+"\n[fe]=http://www.apache.org/fe/xe?foo=bar&amp;label2=MY_LABEL";
+			assertEquals(expected, r);
+
+			s.detectLinksInStrings(true);
+			s.lookForLabelParameters(false);
+			r = strip(s.build().serialize(t));
+			expected = ""
+				+"\n[f0]=<a href='f0/x0'>f0/x0</a>"
+				+"\n[f1]=<a href='f1/x1'>f1/x1</a>"
+				+"\n[f2]=<a href='/f2/x2'>/f2/x2</a>"
+				+"\n[f3]=<a href='http://www.apache.org/f3/x3'>http://www.apache.org/f3/x3</a>"
+				+"\n[f4]=<a href='f4/x4'>f4/x4</a>"
+				+"\n[f5]=<a href='/f5/x5'>/f5/x5</a>"
+				+"\n[f6]=<a href='http://www.apache.org/f6/x6'>http://www.apache.org/f6/x6</a>"
+				+"\n[f7]=<a href='http://www.apache.org/f7/x7'>http://www.apache.org/f7/x7</a>"
+				+"\n[f8]=<a href='f8/x8'>f8/x8</a>"
+				+"\n[f9]=<a href='f9/x9'>f9/x9</a>"
+				+"\n[fa]=<a href='http://www.apache.org/fa/xa#MY_LABEL'>MY_LABEL</a>"
+				+"\n[fb]=<a href='http://www.apache.org/fb/xb?label=MY_LABEL&amp;foo=bar'>http://www.apache.org/fb/xb?label=MY_LABEL&amp;foo=bar</a>"
+				+"\n[fc]=<a href='http://www.apache.org/fc/xc?foo=bar&amp;label=MY_LABEL'>http://www.apache.org/fc/xc?foo=bar&amp;label=MY_LABEL</a>"
+				+"\n[fd]=<a href='http://www.apache.org/fd/xd?label2=MY_LABEL&amp;foo=bar'>http://www.apache.org/fd/xd?label2=MY_LABEL&amp;foo=bar</a>"
+				+"\n[fe]=<a href='http://www.apache.org/fe/xe?foo=bar&amp;label2=MY_LABEL'>http://www.apache.org/fe/xe?foo=bar&amp;label2=MY_LABEL</a>";
+			assertEquals(expected, r);
+	}
+
+	private String strip(String html) {
+		return html
+			.replace("<table><tr><th>key</th><th>value</th></tr>", "")
+			.replace("</table>", "")
+			.replace("<tr><td>", "\n[")
+			.replace("</td><td>", "]=")
+			.replace("</td></tr>", "");
+	}
+
+	//====================================================================================================
+	// Test @Html.asPlainText annotation on classes and fields
+	//====================================================================================================
+	@Test
+	public void testHtmlAnnotationAsPlainText() throws Exception {
+		HtmlSerializer s = new HtmlSerializerBuilder().sq().addKeyValueTableHeaders(true).build();
+		Object o = null;
+		String r;
+
+		o = new B1();
+		r = s.serialize(o);
+		assertEquals("<test>", r);
+
+		o = new B2();
+		r = s.serialize(o);
+		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>f1</td><td><f1></td></tr></table>", r);
+	}
+
+	@Html(asPlainText=true)
+	public static class B1 {
+		public String f1 = "<f1>";
+		@Override /* Object */
+		public String toString() {
+			return "<test>";
+		}
+	}
+
+	public static class B2 {
+		@Html(asPlainText=true)
+		public String f1 = "<f1>";
+	}
+
+	//====================================================================================================
+	// Test @Html.asXml annotation on classes and fields
+	//====================================================================================================
+	@Test
+	public void testHtmlAnnotationAsXml() throws Exception {
+		HtmlSerializer s = new HtmlSerializerBuilder().sq().addKeyValueTableHeaders(true).build();
+		Object o = null;
+		String r;
+
+		o = new C1();
+		r = s.serialize(o);
+		assertEquals("<object><f1>&lt;f1&gt;</f1></object>", r);
+
+		o = new C2();
+		r = s.serialize(o);
+		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>f1</td><td>&lt;f1&gt;</td></tr></table>", r);
+	}
+
+	@Html(asXml=true)
+	public static class C1 {
+		public String f1 = "<f1>";
+	}
+
+	public static class C2 {
+		@Html(asXml=true)
+		public String f1 = "<f1>";
+	}
+
+	//====================================================================================================
+	// Test @Html.noTableHeaders
+	//====================================================================================================
+	@Test
+	public void testNoTableHeaders() throws Exception {
+		HtmlSerializer s = HtmlSerializer.DEFAULT_SQ;
+		Object o = null;
+		String r;
+
+		Map m = new MyMap();
+		m.put("foo", "bar");
+		o = new ObjectList().append(m);
+		r = s.serialize(o);
+		assertEquals("<ul><li><table><tr><td>foo</td><td>bar</td></tr></table></li></ul>", r);
+	}
+
+	@Html(noTables=true, noTableHeaders=true)
+	public static class MyMap extends LinkedHashMap<String,String> {}
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/75b0d8ee/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/https/AcceptExtensionsTest.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/https/AcceptExtensionsTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/https/AcceptExtensionsTest.java
new file mode 100644
index 0000000..c2759d4
--- /dev/null
+++ b/juneau-core/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.asRanges().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.asRanges().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.asRanges().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.asRanges().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.asRanges().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.asRanges().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.asRanges().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.asRanges().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.asRanges().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/75b0d8ee/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/https/AcceptTest.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/https/AcceptTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/https/AcceptTest.java
new file mode 100644
index 0000000..6f44506
--- /dev/null
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/https/AcceptTest.java
@@ -0,0 +1,127 @@
+// ***************************************************************************************************************************
+// * 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 },
+
+			// 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/75b0d8ee/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/https/ContentTypeTest.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/https/ContentTypeTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/https/ContentTypeTest.java
new file mode 100644
index 0000000..ecf7b93
--- /dev/null
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/https/ContentTypeTest.java
@@ -0,0 +1,90 @@
+// ***************************************************************************************************************************
+// * 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 Content-Type class handles matching correctly.
+ */
+@RunWith(Parameterized.class)
+public class ContentTypeTest {
+	@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 },
+
+			{ "XmlAndRdf-1", "text/xml+rdf", "['text/xml','text/xml+rdf']", 1 },
+			{ "XmlAndRdf-2", "text/xml+rdf", "['text/xml+rdf','text/xml']", 0 },
+			
+			// 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 },
+		});
+	}
+
+	private String label, contentType, mediaTypes;
+	private int expected;
+
+	public ContentTypeTest(String label, String contentType, String mediaTypes, int expected) {
+		this.label = label;
+		this.contentType = contentType;
+		this.mediaTypes = mediaTypes;
+		this.expected = expected;
+	}
+
+	@Test
+	public void test() throws Exception {
+		ContentType ct = ContentType.forString(this.contentType);
+		MediaType[] mt = JsonParser.DEFAULT.parse(mediaTypes, MediaType[].class);
+		int r = ct.findMatch(mt);
+		TestUtils.assertEquals(expected, r, "{0} failed", label);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/75b0d8ee/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/https/MediaRangeTest.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/https/MediaRangeTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/https/MediaRangeTest.java
new file mode 100644
index 0000000..8ba7acb
--- /dev/null
+++ b/juneau-core/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/75b0d8ee/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileBuilderTest.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileBuilderTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileBuilderTest.java
new file mode 100755
index 0000000..f780f64
--- /dev/null
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileBuilderTest.java
@@ -0,0 +1,198 @@
+// ***************************************************************************************************************************
+// * 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.ini;
+
+import static org.apache.juneau.TestUtils.*;
+import static org.junit.Assert.*;
+import static org.apache.juneau.internal.FileUtils.*;
+import static org.apache.juneau.internal.StringUtils.*;
+import static org.apache.juneau.internal.IOUtils.*;
+
+import java.io.*;
+
+import org.apache.juneau.svl.*;
+import org.junit.*;
+
+@SuppressWarnings("javadoc")
+public class ConfigFileBuilderTest {
+
+	private static File tempDir;
+	private static String[] TEMP_DIR;
+
+	@BeforeClass
+	public static void setup() {
+		tempDir = new File(System.getProperty("java.io.tmpdir"), generateUUID(12));
+		mkdirs(tempDir, true);
+		TEMP_DIR = new String[]{tempDir.getAbsolutePath()};
+	}
+
+	@AfterClass
+	public static void teardown() {
+		delete(tempDir);
+	}
+
+	/**
+	 *
+	 * @throws Exception
+	 */
+	@Test
+	public void testGet() throws Exception {
+		File f;
+		ConfigFileBuilder b1 = new ConfigFileBuilder().paths(TEMP_DIR).createIfNotExists();
+
+		ConfigFile cf = b1.build("TestGet.cfg");
+		cf.put("Test/A", "a");
+
+		f = new File(tempDir, "TestGet.cfg");
+		assertTrue(f.exists());
+
+		cf.save();
+		assertTextEquals("[Test]|A = a|", read(f));
+
+		cf = b1.build("TestGet.cfg");
+		assertObjectEquals("{'default':{},Test:{A:'a'}}", cf);
+
+		String NL = System.getProperty("line.separator");
+		cf = b1.build(new StringReader(("[Test]"+NL+"A = a"+NL)));
+		assertObjectEquals("{'default':{},Test:{A:'a'}}", cf);
+
+		b1.charset(UTF8);
+		cf = b1.build("TestGet.cfg");
+		assertObjectEquals("{'default':{},Test:{A:'a'}}", cf);
+	}
+
+	/**
+	 * Retrieving config file should fail if the file doesn't exist and createIfNotExist == false.
+	 */
+	@Test
+	public void testFailOnNonExistentFiles() throws Exception {
+		ConfigFileBuilder b = new ConfigFileBuilder().paths(new String[]{tempDir.getAbsolutePath()});
+		try { b.build("TestGet2.cfg"); fail(); } catch (FileNotFoundException e) {}
+		try { b.build(tempDir.getAbsolutePath() + "TestGet2.cfg"); fail(); } catch (FileNotFoundException e) {}
+
+		b = new ConfigFileBuilder().paths().createIfNotExists();
+		try { b.build("TestGet.cfg"); fail(); } catch (FileNotFoundException e) {}
+	}
+
+
+	//====================================================================================================
+	// loadIfModified()
+	//====================================================================================================
+	@Test
+	public void testLoadIfModified() throws Exception {
+		ConfigFileBuilder b = new ConfigFileBuilder().paths(TEMP_DIR).createIfNotExists();
+		File f;
+		ConfigFile cf = b.build("TestGet.cfg");
+		cf.put("Test/A", "a");
+
+		f = new File(tempDir, "TestGet.cfg");
+		String NL = System.getProperty("line.separator");
+		write(f, new StringReader("[Test]"+NL+"A = b"+NL));
+		modifyTimestamp(f);
+
+		cf.loadIfModified();
+		assertEquals("b", cf.getString("Test/A"));
+		cf.loadIfModified();
+		assertEquals("b", cf.getString("Test/A"));
+
+		// Config file with no backing file.
+		cf = b.build();
+		cf.put("Test/B", "b");
+		cf.loadIfModified();
+		cf.loadIfModified();
+		assertEquals("b", cf.getString("Test/B"));
+	}
+
+	//====================================================================================================
+	// read only
+	//====================================================================================================
+	@Test
+	public void testReadOnly() throws Exception {
+		ConfigFileBuilder cm = new ConfigFileBuilder().paths(TEMP_DIR).createIfNotExists().readOnly();
+		ConfigFile cf = cm.build("TestGet.cfg");
+
+		// All these should fail.
+		try { cf.loadIfModified(); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.load(); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.load(new StringReader("")); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.put("A","b"); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.put("A","b",true); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.put("A","b"); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.put("A","b",true); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.removeString("A"); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.addLines("A","b=c"); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.addHeaderComments("A", "b=c"); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.clearHeaderComments("A"); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.addSection("A"); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.setSection("A",null); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.removeSection("A"); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.save(); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.merge(cf); fail(); } catch (UnsupportedOperationException e) {}
+		try { cf.addListener(new ConfigFileListener(){}); fail(); } catch (UnsupportedOperationException e) {}
+
+		// All these should succeed.
+		cf.getObject("A", String.class);
+		cf.getObject("A", "a", String.class);
+		cf.getString("A");
+		cf.getString("A","a");
+		cf.getObject("A", String.class);
+		cf.getObject("A", "a", String.class);
+		cf.getObject("A", String[].class);
+		cf.getStringArray("A");
+		cf.getStringArray("A", null);
+		cf.getInt("A");
+		cf.getInt("A", 0);
+		cf.getBoolean("A");
+		cf.getBoolean("A", true);
+		cf.containsNonEmptyValue("A");
+		cf.getSectionMap("A");
+		cf.serializeTo(new StringWriter());
+		cf.serializeTo(new StringWriter(), ConfigFileFormat.INI);
+		cf.getResolving(VarResolver.DEFAULT);
+		cf.toWritable();
+	}
+
+	//====================================================================================================
+	// main(String[] args)
+	//====================================================================================================
+	@Test
+	public void testMain() throws Exception {
+		System.setProperty("exit.2", "0");
+		ConfigFileBuilder cm = new ConfigFileBuilder().paths(TEMP_DIR).createIfNotExists();
+
+		ConfigFile cf = cm.build("Test.cfg")
+			.addLines(null, "# c1", "\t# c2", " c3 ", "  ", "x1=1", "x2=true", "x3=null")
+			.addLines("s1", "#c4", "k1=1", "#c5 foo=bar", "k2 = true", "k3  = \tnull");
+		cf.save();
+
+		File configFile = new File(tempDir, "Test.cfg");
+		File envFile = new File(tempDir, "Test.bat");
+
+		ConfigFileBuilder.main(new String[]{"createBatchEnvFile", "-configFile", configFile.getAbsolutePath(), "-envFile", envFile.getAbsolutePath()});
+		String expected = "rem c1|rem c2|rem c3||set x1 = 1|set x2 = true|set x3 = null|rem c4|set s1_k1 = 1|rem c5 foo=bar|set s1_k2 = true|set s1_k3 = null|";
+		String actual = read(envFile);
+		assertTextEquals(expected, actual);
+
+		ConfigFileBuilder.main(new String[]{"createShellEnvFile", "-configFile", configFile.getAbsolutePath(), "-envFile", envFile.getAbsolutePath()});
+		expected = "# c1|# c2|# c3||export x1=\"1\"|export x2=\"true\"|export x3=\"null\"|# c4|export s1_k1=\"1\"|# c5 foo=bar|export s1_k2=\"true\"|export s1_k3=\"null\"|";
+		actual = read(envFile);
+		assertTextEquals(expected, actual);
+
+		ConfigFileBuilder.main(new String[]{"setVals", "-configFile", configFile.getAbsolutePath(), "-vals", "x1=2", "s1/k1=2", "s2/k1=3"});
+		modifyTimestamp(configFile);
+		cf.loadIfModified();
+		assertObjectEquals("{'default':{x1:'2',x2:'true',x3:'null'},s1:{k1:'2',k2:'true',k3:'null'},s2:{k1:'3'}}", cf);
+
+		ConfigFileBuilder.main(new String[]{});
+	}
+}
\ No newline at end of file