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

Reply via email to