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/10 20:30:57 UTC

[1/2] incubator-juneau git commit: Templated and per-media-type swaps.

Repository: incubator-juneau
Updated Branches:
  refs/heads/master 1bb80a053 -> f5f5edfb6


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/SurrogateSwap.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/SurrogateSwap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/SurrogateSwap.java
index fa8b57f..36a1e71 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/SurrogateSwap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/SurrogateSwap.java
@@ -21,116 +21,7 @@ import org.apache.juneau.parser.*;
 import org.apache.juneau.serializer.*;
 
 /**
- * Specialized {@link PojoSwap} for surrogate classes.
- *
- * <p>
- * Surrogate classes are used in place of other classes during serialization.
- * For example, you may want to use a surrogate class to change the names or order of bean properties on a bean.
- *
- * <p>
- * The following is an example of a surrogate class change changes a property name:
- * <p class='bcode'>
- * 	<jk>public class</jk> SurrogateClass {
- * 		<jk>public</jk> String surrogateField;  <jc>// New bean property</jc>
- *
- * 		<jk>public</jk> SurrogateClass(NormalClass normalClass) {
- * 			<jk>this</jk>.surrogateField = normalClass.normalField;
- * 		}
- * 	}
- * </p>
- *
- * <p>
- * Optionally, a public static method can be used to un-transform a class during parsing:
- * <p class='bcode'>
- * 	<jk>public class</jk> SurrogateClass {
- * 		...
- * 		<jk>public static</jk> NormalClass <jsm>toNormalClass</jsm>(SurrogateClass surrogateClass) {
- * 			<jk>return new</jk> NormalClass(surrogateClass.transformedField);
- * 		}
- * 	}
- * </p>
- *
- * <p>
- * Surrogate classes must conform to the following:
- * <ul class='spaced-list'>
- * 	<li>
- * 		It must have a one or more public constructors that take in a single parameter whose type is the normal types.
- * 		(It is possible to define a class as a surrogate for multiple class types by using multiple constructors with
- * 		different parameter types).
- * 	<li>
- * 		It optionally can have a public static method that takes in a single parameter whose type is the transformed
- * 		type and returns an instance of the normal type.
- * 		This is called the un-transform method.
- * 		The method can be called anything.
- * 	<li>
- * 		If an un-transform method is present, the class must also contain a no-arg constructor (so that the
- * 		transformed class can be instantiated by the parser before being converted into the normal class by the
- * 		un-transform method).
- * </ul>
- *
- * <p>
- * Surrogate classes are associated with serializers and parsers using the {@link CoreObjectBuilder#pojoSwaps(Class...)}
- * method.
- * <p class='bcode'>
- * 	<ja>@Test</ja>
- * 	<jk>public void</jk> test() <jk>throws</jk> Exception {
- * 		JsonSerializer s = <jk>new</jk> JsonSerializerBuilder().simple().pojoSwaps(Surrogate.<jk>class</jk>).build();
- * 		JsonParser p = <jk>new</jk> JsonParserBuilder().pojoSwaps(Surrogate.<jk>class</jk>).build();
- * 		String r;
- * 		Normal n = Normal.<jsm>create</jsm>();
- *
- * 		r = s.serialize(n);
- * 		assertEquals(<js>"{f2:'f1'}"</js>, r);
- *
- * 		n = p.parse(r, Normal.<jk>class</jk>);
- * 		assertEquals(<js>"f1"</js>, n.f1);
- * 	}
- *
- * 	<jc>// The normal class</jc>
- * 	<jk>public class</jk> Normal {
- * 		<jk>public</jk> String f1;
- *
- * 		<jk>public static</jk> Normal <jsm>create</jsm>() {
- * 			Normal n = <jk>new</jk> Normal();
- * 			n.f1 = <js>"f1"</js>;
- * 			<jk>return</jk> n;
- * 		}
- * 	}
- *
- * 	<jc>// The surrogate class</jc>
- * 	<jk>public static class</jk> Surrogate {
- * 		<jk>public</jk> String f2;
- *
- * 		<jc>// Surrogate constructor</jc>
- * 		<jk>public</jk> Surrogate(Normal n) {
- * 			f2 = n.f1;
- * 		}
- *
- * 		<jc>// Constructor used during parsing (only needed if un-transform method specified)</jc>
- * 		<jk>public</jk> Surrogate() {}
- *
- * 		<jc>// Un-transform method (optional)</jc>
- * 		<jk>public static</jk> Normal <jsm>toNormal</jsm>(Surrogate f) {
- * 			Normal n = <jk>new</jk> Normal();
- * 			n.f1 = f.f2;
- * 			<jk>return</jk> n;
- * 		}
- * 	}
- * </p>
- *
- * <p>
- * It should be noted that a surrogate class is functionally equivalent to the following {@link PojoSwap}
- * implementation:
- * <p class='bcode'>
- * 	<jk>public static class</jk> SurrogateSwap <jk>extends</jk> PojoSwap&lt;Normal,Surrogate&gt; {
- * 		<jk>public</jk> Surrogate swap(Normal n) <jk>throws</jk> SerializeException {
- * 			<jk>return new</jk> Surrogate(n);
- * 		}
- * 		<jk>public</jk> Normal unswap(Surrogate s, ClassMeta&lt;?&gt; hint) <jk>throws</jk> ParseException {
- * 			<jk>return</jk> Surrogate.<jsm>toNormal</jsm>(s);
- * 		}
- * 	}
- * </p>
+ * Specialized {@link PojoSwap} for {@link Surrogate} classes.
  *
  * @param <T> The class type that this transform applies to.
  * @param <F> The transformed class type.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/package.html
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/package.html b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/package.html
index 124bc10..9c4be10 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/package.html
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/package.html
@@ -114,7 +114,7 @@
 			{@link org.apache.juneau.rest.jaxrs.JuneauProvider#beanFilters() @JuneauProvider.beanFilters()} / {@link org.apache.juneau.rest.jaxrs.JuneauProvider#pojoSwaps() @JuneauProvider.pojoSwaps()} - On all serializers and parsers defined on a JAX-RS provider.
 	</ul>
 	<p>
-		Swaps can also be associated with classes through the {@link org.apache.juneau.annotation.Pojo#swap @Pojo.swap()} annotation. 
+		Swaps can also be associated with classes through the {@link org.apache.juneau.annotation.Swap @Swap} annotation. 
 	</p>
 		
 	<!-- ======================================================================================================== -->

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/ByteArrayBase64Swap.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/ByteArrayBase64Swap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/ByteArrayBase64Swap.java
index 5183c5b..bff57ad 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/ByteArrayBase64Swap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/ByteArrayBase64Swap.java
@@ -15,8 +15,6 @@ package org.apache.juneau.transforms;
 import static org.apache.juneau.internal.StringUtils.*;
 
 import org.apache.juneau.*;
-import org.apache.juneau.parser.*;
-import org.apache.juneau.serializer.*;
 import org.apache.juneau.transform.*;
 
 /**
@@ -28,23 +26,15 @@ public class ByteArrayBase64Swap extends StringSwap<byte[]> {
 	 * Converts the specified <code><jk>byte</jk>[]</code> to a {@link String}.
 	 */
 	@Override /* PojoSwap */
-	public String swap(BeanSession session, byte[] b) throws SerializeException {
-		try {
-			return base64Encode(b);
-		} catch (Exception e) {
-			throw new SerializeException(e);
-		}
+	public String swap(BeanSession session, byte[] b) throws Exception {
+		return base64Encode(b);
 	}
 
 	/**
 	 * Converts the specified {@link String} to a <code><jk>byte</jk>[]</code>.
 	 */
 	@Override /* PojoSwap */
-	public byte[] unswap(BeanSession session, String s, ClassMeta<?> hint) throws ParseException {
-		try {
-			return base64Decode(s);
-		} catch (Exception e) {
-			throw new ParseException(e);
-		}
+	public byte[] unswap(BeanSession session, String s, ClassMeta<?> hint) throws Exception {
+		return base64Decode(s);
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/CalendarLongSwap.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/CalendarLongSwap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/CalendarLongSwap.java
index daed18c..7da3952 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/CalendarLongSwap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/CalendarLongSwap.java
@@ -15,7 +15,6 @@ package org.apache.juneau.transforms;
 import java.util.*;
 
 import org.apache.juneau.*;
-import org.apache.juneau.parser.*;
 import org.apache.juneau.transform.*;
 
 /**
@@ -36,17 +35,13 @@ public class CalendarLongSwap extends PojoSwap<Calendar,Long> {
 	 */
 	@Override /* PojoSwap */
 	@SuppressWarnings("unchecked")
-	public Calendar unswap(BeanSession session, Long o, ClassMeta<?> hint) throws ParseException {
+	public Calendar unswap(BeanSession session, Long o, ClassMeta<?> hint) throws Exception {
 		ClassMeta<? extends Calendar> tt;
-		try {
-			if (hint == null || ! hint.canCreateNewInstance())
-				hint = session.getClassMeta(GregorianCalendar.class);
-			tt = (ClassMeta<? extends Calendar>)hint;
-			Calendar c = tt.newInstance();
-			c.setTimeInMillis(o);
-			return c;
-		} catch (Exception e) {
-			throw new ParseException(e);
-		}
+		if (hint == null || ! hint.canCreateNewInstance())
+			hint = session.getClassMeta(GregorianCalendar.class);
+		tt = (ClassMeta<? extends Calendar>)hint;
+		Calendar c = tt.newInstance();
+		c.setTimeInMillis(o);
+		return c;
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/CalendarMapSwap.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/CalendarMapSwap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/CalendarMapSwap.java
index ec4afe4..bcabe1a 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/CalendarMapSwap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/CalendarMapSwap.java
@@ -15,7 +15,6 @@ package org.apache.juneau.transforms;
 import java.util.*;
 
 import org.apache.juneau.*;
-import org.apache.juneau.parser.*;
 import org.apache.juneau.transform.*;
 
 /**
@@ -40,21 +39,17 @@ public class CalendarMapSwap extends PojoSwap<Calendar,Map> {
 	 */
 	@Override /* PojoSwap */
 	@SuppressWarnings("unchecked")
-	public Calendar unswap(BeanSession session, Map o, ClassMeta<?> hint) throws ParseException {
+	public Calendar unswap(BeanSession session, Map o, ClassMeta<?> hint) throws Exception {
 		ClassMeta<? extends Calendar> tt;
-		try {
-			if (hint == null || ! hint.canCreateNewInstance())
-				hint = session.getClassMeta(GregorianCalendar.class);
-			tt = (ClassMeta<? extends Calendar>)hint;
-			long time = Long.parseLong(o.get("time").toString());
-			String timeZone = o.get("timeZone").toString();
-			Date d = new Date(time);
-			Calendar c = tt.newInstance();
-			c.setTime(d);
-			c.setTimeZone(TimeZone.getTimeZone(timeZone));
-			return c;
-		} catch (Exception e) {
-			throw new ParseException(e);
-		}
+		if (hint == null || ! hint.canCreateNewInstance())
+			hint = session.getClassMeta(GregorianCalendar.class);
+		tt = (ClassMeta<? extends Calendar>)hint;
+		long time = Long.parseLong(o.get("time").toString());
+		String timeZone = o.get("timeZone").toString();
+		Date d = new Date(time);
+		Calendar c = tt.newInstance();
+		c.setTime(d);
+		c.setTimeZone(TimeZone.getTimeZone(timeZone));
+		return c;
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/DateLongSwap.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/DateLongSwap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/DateLongSwap.java
index 9316280..27c373f 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/DateLongSwap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/DateLongSwap.java
@@ -37,7 +37,7 @@ public class DateLongSwap extends PojoSwap<Date,Long> {
 	 * Converts the specified {@link Long} to a {@link Date}.
 	 */
 	@Override /* PojoSwap */
-	public Date unswap(BeanSession session, Long o, ClassMeta<?> hint) throws ParseException {
+	public Date unswap(BeanSession session, Long o, ClassMeta<?> hint) throws Exception {
 		Class<?> c = (hint == null ? java.util.Date.class : hint.getInnerClass());
 		if (c == java.util.Date.class)
 			return new java.util.Date(o);

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/DateMapSwap.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/DateMapSwap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/DateMapSwap.java
index fcaafe0..0893bbb 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/DateMapSwap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/DateMapSwap.java
@@ -40,7 +40,7 @@ public class DateMapSwap extends PojoSwap<Date,Map> {
 	 * Converts the specified {@link Map} to a {@link Date}.
 	 */
 	@Override /* PojoSwap */
-	public Date unswap(BeanSession session, Map o, ClassMeta<?> hint) throws ParseException {
+	public Date unswap(BeanSession session, Map o, ClassMeta<?> hint) throws Exception {
 		Class<?> c = (hint == null ? java.util.Date.class : hint.getInnerClass());
 		long l = Long.parseLong(((Map<?,?>)o).get("time").toString());
 		if (c == java.util.Date.class)

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/ReaderSwap.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/ReaderSwap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/ReaderSwap.java
index 9c8a0a1..47a6f51 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/ReaderSwap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/ReaderSwap.java
@@ -20,7 +20,6 @@ import org.apache.juneau.*;
 import org.apache.juneau.html.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.parser.*;
-import org.apache.juneau.serializer.*;
 import org.apache.juneau.transform.*;
 import org.apache.juneau.xml.*;
 
@@ -96,16 +95,9 @@ public class ReaderSwap extends PojoSwap<Reader,Object> {
 	 * Converts the specified {@link Reader} to an {@link Object} whose type is determined by the contents of the reader.
 	 */
 	@Override /* PojoSwap */
-	public Object swap(BeanSession session, Reader o) throws SerializeException {
-		try {
-			if (parser == null)
-				return read(o);
-			return parser.parse(o, Object.class);
-		} catch (IOException e) {
-			return e.getLocalizedMessage();
-		} catch (Exception e) {
-			throw new SerializeException("ReaderSwap could not transform object of type ''{0}''",
-				o == null ? null : o.getClass().getName()).initCause(e);
-		}
+	public Object swap(BeanSession session, Reader o) throws Exception {
+		if (parser == null)
+			return read(o);
+		return parser.parse(o, Object.class);
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/StringFormatSwap.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/StringFormatSwap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/StringFormatSwap.java
index bb05ab1..29c4900 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/StringFormatSwap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/StringFormatSwap.java
@@ -14,8 +14,6 @@ package org.apache.juneau.transforms;
 
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
-import org.apache.juneau.parser.*;
-import org.apache.juneau.serializer.*;
 import org.apache.juneau.transform.*;
 
 /**
@@ -35,12 +33,12 @@ public class StringFormatSwap extends StringSwap<Object> {
 	}
 
 	@Override /* PojoSwap */
-	public String swap(BeanSession session, Object o) throws SerializeException {
+	public String swap(BeanSession session, Object o) throws Exception {
 		return String.format(format, o);
 	}
 
 	@Override /* PojoSwap */
-	public Object unswap(BeanSession session, String f, ClassMeta<?> hint) throws ParseException {
+	public Object unswap(BeanSession session, String f, ClassMeta<?> hint) throws Exception {
 		return session.convertToType(f, hint);
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/XMLGregorianCalendarSwap.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/XMLGregorianCalendarSwap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/XMLGregorianCalendarSwap.java
index ec0d9dc..446e150 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/XMLGregorianCalendarSwap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transforms/XMLGregorianCalendarSwap.java
@@ -17,8 +17,6 @@ import static org.apache.juneau.internal.StringUtils.*;
 import javax.xml.datatype.*;
 
 import org.apache.juneau.*;
-import org.apache.juneau.parser.*;
-import org.apache.juneau.serializer.*;
 import org.apache.juneau.transform.*;
 
 /**
@@ -49,7 +47,7 @@ public class XMLGregorianCalendarSwap extends StringSwap<XMLGregorianCalendar> {
 	 * Converts the specified <code>XMLGregorianCalendar</code> to a {@link String}.
 	 */
 	@Override /* PojoSwap */
-	public String swap(BeanSession session, XMLGregorianCalendar b) throws SerializeException {
+	public String swap(BeanSession session, XMLGregorianCalendar b) throws Exception {
 		return b.toXMLFormat();
 	}
 
@@ -57,7 +55,7 @@ public class XMLGregorianCalendarSwap extends StringSwap<XMLGregorianCalendar> {
 	 * Converts the specified {@link String} to an <code>XMLGregorianCalendar</code>.
 	 */
 	@Override /* PojoSwap */
-	public XMLGregorianCalendar unswap(BeanSession session, String s, ClassMeta<?> hint) throws ParseException {
+	public XMLGregorianCalendar unswap(BeanSession session, String s, ClassMeta<?> hint) throws Exception {
 		if (isEmpty(s))
 			return null;
 		return dtf.newXMLGregorianCalendar(s);

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/javadoc/overview.html
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/javadoc/overview.html b/juneau-core/juneau-marshall/src/main/javadoc/overview.html
index 03fed7b..7f6a0f7 100644
--- a/juneau-core/juneau-marshall/src/main/javadoc/overview.html
+++ b/juneau-core/juneau-marshall/src/main/javadoc/overview.html
@@ -84,7 +84,7 @@
 		<li><p><a class='doclink' href='#Core.Transforms'>Transforms</a></p>
 		<ol>
 			<li><p><a class='doclink' href='#Core.PojoSwaps'>PojoSwaps</a></p>
-			<li><p><a class='doclink' href='#Core.PojoAnnotation'>@Pojo annotation</a></p>
+			<li><p><a class='doclink' href='#Core.SwapAnnotation'>@Swap annotation</a></p>
 			<li><p><a class='doclink' href='#Core.SwapMethods'>Swap methods</a></p>
 			<li><p><a class='doclink' href='#Core.BeanFilters'>BeanFilters and @Bean annotations</a></p>
 			<li><p><a class='doclink' href='#Core.SerializingReadersAndInputStreams'>Serializing Readers and InputStreams</a></p>
@@ -773,7 +773,7 @@
 		</p>
 		<ul class='doctree'>
 			<li class='ja'>
-				{@link org.apache.juneau.annotation.Pojo @Pojo} 
+				{@link org.apache.juneau.annotation.Swap @Swap} 
 				- Used to tailor how non-bean POJOs get interpreted by the framework.
 			<li class='ja'>
 				{@link org.apache.juneau.annotation.Bean @Bean} 
@@ -900,17 +900,17 @@
 		</div>
 	
 		<!-- ======================================================================================================== -->
-		<a id="Core.PojoAnnotation"></a>
-		<h4 class='topic' onclick='toggle(this)'>2.6.2 - @Pojo annotation</h4>
+		<a id="Core.SwapAnnotation"></a>
+		<h4 class='topic' onclick='toggle(this)'>2.6.2 - @Swap annotation</h4>
 		<div class='topic'>
 			<p>
-				{@link org.apache.juneau.annotation.Pojo @Pojo} can be used to associate a swap class using an 
+				{@link org.apache.juneau.annotation.Swap @Swap} can be used to associate a swap class using an 
 				annotation.
 				This is often cleaner than using the builder <code>pojoSwaps()</code> method since you can keep
 				your swap class near your POJO class. 
 			</p>
 			<p class='bcode'>
-	<ja>@Pojo</ja>(swap=MyPojoSwap.<jk>class</jk>)
+	<ja>@Swap</ja>(MyPojoSwap.<jk>class</jk>)
 	<jk>public class</jk> MyPojo {
 		...
 	}
@@ -1181,7 +1181,7 @@
 				all other renditions as-is:
 			</p>			
 			<p class='bcode'>
-	<ja>@Pojo</ja>(swap=MyBeanSwap.<jk>class</jk>)
+	<ja>@Swap</ja>(MyBeanSwap.<jk>class</jk>)
 	<jk>public class</jk> MyBean {...}
 	
 	<jk>public class</jk> MyBeanSwap <jk>extends</jk> PojoSwap&lt;MyBean,Object&gt; {
@@ -2090,8 +2090,7 @@
 				</td>
 				<td>
 					Juneau uses swaps to convert non-serializable object to serializable forms:
-					<br>{@link org.apache.juneau.annotation.BeanProperty#swap() @BeanProperty(swap=...)}
-					<br>{@link org.apache.juneau.annotation.Pojo#swap() @Pojo(swap=...)}
+					<br>{@link org.apache.juneau.annotation.Swap @Swap}
 				</td>
 			</tr>
 			<tr>
@@ -6740,7 +6739,7 @@
 	
 	<h5 class='toc'>What's new in each release</h5>
 	<ul class='toc'>
-		<li><p><a class='doclink' href='#6.3.2'>6.3.2 (TBD)</a></p>
+		<li><p><a class='doclink' href='#6.4.0'>6.4.0 (TBD)</a></p>
 		<li><p><a class='doclink' href='#6.3.1'>6.3.1 (Aug 1, 2017)</a></p>
 		<li><p><a class='doclink' href='#6.3.0'>6.3.0 (Jun 30, 2017)</a></p>
 		<li><p><a class='doclink' href='#6.2.0'>6.2.0 (Apr 28, 2017)</a></p>
@@ -6810,8 +6809,8 @@
 	</ul>
 
 	<!-- ======================================================================================================== -->
-	<a id="6.3.2"></a>
-	<h3 class='topic' onclick='toggle(this)'>6.3.2 (TBD)</h3>
+	<a id="6.4.0"></a>
+	<h3 class='topic' onclick='toggle(this)'>6.4.0 (TBD)</h3>
 	<div class='topic'>
 		<p>
 			The major change in this release is the project structure
@@ -6824,8 +6823,8 @@
 				<th>Category</th><th>Maven Artifacts</th><th>Description</th><th>Prereqs</th>
 			</tr>
 			<tr class='dark bb'>
-				<td rowspan="5" style='text-align:center;font-weight:bold;padding:20px;'><a class='doclink' href='#JuneauCore'>Juneau Core</a></td>
-				<td class='code'><a class='doclink' href='#juneau-marshall'>juneau-marshall</a></td>
+				<td rowspan="5" style='text-align:center;font-weight:bold;padding:20px;'>Juneau Core</td>
+				<td class='code'>juneau-marshall</td>
 				<td>Serializers and parsers for:
 					<ul style='margin:0px 10px;'>
 						<li>JSON
@@ -6848,7 +6847,7 @@
 				</td>
 			</tr>
 			<tr class='dark bb'>
-				<td class='code'><a class='doclink' href='#juneau-marshall-rdf'>juneau-marshall-rdf</a></td>
+				<td class='code'>juneau-marshall-rdf</td>
 				<td>
 					Serializers and parsers for:
 					<ul style='margin:0px 10px;'>
@@ -6867,7 +6866,7 @@
 				</td>
 			</tr>
 			<tr class='dark bb'>
-				<td class='code'><a class='doclink' href='#juneau-dto'>juneau-dto</a></td>
+				<td class='code'>juneau-dto</td>
 				<td>
 					Data Transfer Objects for:
 					<ul style='margin:0px 10px;'>
@@ -6881,22 +6880,22 @@
 				<td><ul style='margin:0px 10px;'><li>Java 6</li></ul></td>
 			</tr>
 			<tr class='dark bb'>
-				<td class='code'><a class='doclink' href='#juneau-svl'>juneau-svl</a></td>
+				<td class='code'>juneau-svl</td>
 				<td>
 					Simple Variable Language API
 				</td>
 				<td><ul style='margin:0px 10px;'><li>Java 6</li></ul></td>
 			</tr>
 			<tr class='dark bb'>
-				<td class='code'><a class='doclink' href='#juneau-config'>juneau-config</a></td>
+				<td class='code'>juneau-config</td>
 				<td>
 					Configuration file API
 				</td>
 				<td><ul style='margin:0px 10px;'><li>Java 6</li></ul></td>
 			</tr>
 			<tr class='light bb'>
-				<td rowspan="5" style='text-align:center;font-weight:bold;padding:20px;'><a class='doclink' href='#JuneauRest'>Juneau REST</a></td>
-				<td class='code'><a class='doclink' href='#juneau-rest-server'>juneau-rest-server</a></td>
+				<td rowspan="3" style='text-align:center;font-weight:bold;padding:20px;'>Juneau REST</td>
+				<td class='code'>juneau-rest-server</td>
 				<td>
 					REST Servlet API
 				</td>
@@ -6908,7 +6907,7 @@
 				</td>
 			</tr>
 			<tr class='light bb'>
-				<td class='code'><a class='doclink' href='#juneau-rest-server-jaxrs'>juneau-rest-server-jaxrs</a></td>
+				<td class='code'>juneau-rest-server-jaxrs</td>
 				<td>
 					Optional JAX-RS support
 				</td>
@@ -6920,7 +6919,7 @@
 				</td>
 			</tr>
 			<tr class='light bb'>
-				<td class='code'><a class='doclink' href='#juneau-rest-client'>juneau-rest-client</a></td>
+				<td class='code'>juneau-rest-client</td>
 				<td>
 					REST Client API
 				</td>
@@ -6931,10 +6930,11 @@
 					</ul>
 				</td>
 			</tr>
-			<tr class='light bb'>
-				<td class='code'><a class='doclink' href='#juneau-microservice'>juneau-microservice</a></td>
+			<tr class='dark bb'>
+				<td rowspan="2" style='text-align:center;font-weight:bold;padding:20px;'>Juneau Microservice</td>
+				<td class='code'>juneau-microservice-server</td>
 				<td>
-					REST Microservice API
+					REST Microservice Server API
 				</td>
 				<td>
 					<ul style='margin:0px 10px;'>
@@ -6943,8 +6943,8 @@
 					</ul>
 				</td>
 			</tr>
-			<tr class='light bb'>
-				<td class='code'><a class='doclink' href='#juneau-microservice-template'>juneau-microservice-template</a></td>
+			<tr class='dark bb'>
+				<td class='code'>juneau-microservice-template</td>
 				<td>
 					Developer template project
 				</td>
@@ -6955,23 +6955,23 @@
 					</ul>
 				</td>
 			</tr>
-			<tr class='dark bb'>
-				<td rowspan="2" style='text-align:center;font-weight:bold;padding:20px;'><a class='doclink' href='#Examples'>Examples</a></td>
+			<tr class='light bb'>
+				<td rowspan="2" style='text-align:center;font-weight:bold;padding:20px;'>Examples</td>
 				<td class='code'><code>juneau-examples-core</code></td>
 				<td>
 					Core code examples
 				</td>
 				<td></td>
 			</tr>
-			<tr class='dark bb'>
+			<tr class='light bb'>
 				<td class='code'><code>juneau-examples-rest</code></td>
 				<td>
 					REST code examples
 				</td>
 				<td></td>
 			</tr>
-			<tr class='light bb'>
-				<td rowspan="1" style='text-align:center;font-weight:bold;padding:20px;'><a class='doclink' href='#JuneauAll'>Juneau All</a></td>
+			<tr class='dark bb'>
+				<td rowspan="1" style='text-align:center;font-weight:bold;padding:20px;'>Juneau All</td>
 				<td class='code'><code>juneau-all</code></td>
 				<td>
 					Combination of the following:
@@ -6997,6 +6997,72 @@
 		<h6 class='topic'>juneau-marshall</h6>
 		<ul class='spaced-list'>
 			<li>
+				Improvements to swap support.
+				<ul>
+					<li>New {@link org.apache.juneau.annotation.Swap @Swap} annotation.
+						<br>Replaces the <code>@Pojo</code> and <code>@BeanProperty.swap()</code> annotations.
+					<li>Support for per-media-type swaps.
+						<br>Programmatic example:
+						<p class='bcode'>
+	<ja>@Swap</ja>(MyJsonOnlySwap.<jk>class</jk>)			
+	<jk>public class</jk> MyPojo {}
+
+	<jk>public class</jk> MyJsonOnlySwap <jk>extends</jk> PojoSwap&lt;MyPojo,String&gt; {
+
+			<jk>public</jk> MediaType[] forMediaTypes() {
+				<jk>return</jk> MediaType.<jsm>forStrings</jsm>(<js>"&#42;/json"</js>);
+			}
+
+			<jk>public</jk> String swap(BeanSession session, MyPojo o) <jk>throws</jk> Exception {
+				<jk>return</jk> <js>"It's JSON!"</js>;
+ 		}
+						</p>
+						<br>Annotated example:
+						<p class='bcode'>
+	<ja>@Swap</ja>(impl=ToStringSwap.<jk>class</jk>, mediaTypes=<js>"&#42;/json"</js>)
+	<jk>public class</jk> MyBean { ... }
+
+	<jk>public class</jk> ToStringSwap <jk>extends</jk> PojoSwap&lt;Object,String&gt; {
+		<jk>public</jk> String swap(BeanSession session, Object o) <jk>throws</jk> Exception {
+			<jk>return</jk> o.toString();
+		}
+	}
+						</p>
+					<li>Support for templated swaps which provide additional context information for a swap.
+						<br>The following is an example of a templated swap class used to serialize POJOs to HTML using FreeMarker:
+						<p class='bcode'>
+	<jc>// Our abstracted templated swap class.</jc>
+	<jk>public abstract class</jk> FreeMarkerSwap <jk>extends</jk> PojoSwap&lt;Object,Reader&gt; {
+
+		<jk>public</jk> MediaType[] forMediaTypes() {
+			<jk>return</jk> MediaType.<jsm>forStrings</jsm>(<js>"&#42;/html"</js>);
+		}
+
+		<jk>public</jk> Reader swap(BeanSession session, Object o, String template) <jk>throws</jk> Exception {
+			<jk>return</jk> getFreeMarkerReader(template, o);  <jc>// Some method that creates raw HTML.</jc>
+		}
+	}
+						</p>
+						<p class='bcode'>
+		<ja>@Swap</ja>(impl=FreeMarkerSwap.<jk>class</jk>, template=<js>"MyPojo.div.ftl"</js>)
+		<jk>public class</jk> MyPojo {}
+						</p>
+					<li>New {@link org.apache.juneau.annotation.Swaps @Swaps} annotation for defining multiple swaps
+						against the same POJO when they're differentiated by media types:
+						<p class='bcode'>
+	<ja>@Swaps</ja>(
+		{
+			<ja>@Swap</ja>(MyJsonSwap.<jk>class</jk>),
+			<ja>@Swap</ja>(MyXmlSwap.<jk>class</jk>),
+			<ja>@Swap</ja>(MyOtherSwap.<jk>class</jk>)
+		}
+	)
+	<jk>public class</jk> MyPojo {}
+						</p>
+				</ul>
+			<li>
+				New {@link org.apache.juneau.transform.Surrogate} interface for identifying surrogate classes.
+			<li>
 				Serializers can now serialize to {@link java.util.StringBuilder StringBuilders}.
 			<li>
 				Serializers now serialize the contents of {@link java.io.Reader Readers} and {@link java.io.InputStream InputStreams}
@@ -7032,10 +7098,10 @@
 	<jc>// Same, but using a session object</jc>
 	SerializerSession session = JsonSerializer.<jsf>DEFAULT</jsf>.createSession();
 	<jk>try</jk> {
-	  session.serialize(writer1, pojo1);
-	  session.serialize(writer2, pojo2);
+		session.serialize(writer1, pojo1);
+		session.serialize(writer2, pojo2);
 	} <jk>finally</jk> {
-	  session.close();
+		session.close();
 	}  			
 				</p>
 				This is mostly an internal change and doesn't affect the existing APIs.
@@ -8472,7 +8538,7 @@
 					<li>{@link org.apache.juneau.BeanPropertyMetaExtended} / {@link org.apache.juneau.BeanPropertyMeta#getExtendedMeta(Class)} 
 				</ul>
 			</li>
-			<li>Renamed <code>@Transform</code> annotation to {@link org.apache.juneau.annotation.Pojo @Pojo} so that it can be used for various POJO-related behavior, not just associating transforms.  
+			<li>Renamed <code>@Transform</code> annotation to <code><del>@Pojo</del></code> so that it can be used for various POJO-related behavior, not just associating transforms.  
 			<li>Introduced {@link org.apache.juneau.dto.swagger Swagger DTOs}.
 		</ul>		
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/addressbook/CreatePerson.java
----------------------------------------------------------------------
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/addressbook/CreatePerson.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/addressbook/CreatePerson.java
index d064fd7..03cc416 100755
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/addressbook/CreatePerson.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/addressbook/CreatePerson.java
@@ -29,7 +29,10 @@ public class CreatePerson {
 
 	// Bean properties
 	public String name;
-	@BeanProperty(swap=CalendarSwap.DateMedium.class) public Calendar birthDate;
+	
+	@Swap(CalendarSwap.DateMedium.class) 
+	public Calendar birthDate;
+	
 	public LinkedList<CreateAddress> addresses = new LinkedList<CreateAddress>();
 
 	/** Bean constructor - Needed for instantiating on server side */

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/addressbook/Person.java
----------------------------------------------------------------------
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/addressbook/Person.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/addressbook/Person.java
index e95806a..c298333 100755
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/addressbook/Person.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/addressbook/Person.java
@@ -31,11 +31,18 @@ public class Person {
 	private static int nextPersonId = 1;
 
 	// Bean properties
-	@Rdf(beanUri=true) public URI uri;
+	@Rdf(beanUri=true) 
+	public URI uri;
+	
 	private URI addressBookUri;
+	
 	public int id;
+	
 	public String name;
-	@BeanProperty(swap=CalendarSwap.DateMedium.class) public Calendar birthDate;
+	
+	@Swap(CalendarSwap.DateMedium.class) 
+	public Calendar birthDate;
+	
 	public LinkedList<Address> addresses = new LinkedList<Address>();
 
 	/** Bean constructor - Needed for instantiating on server side */

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PetStoreResource.java
----------------------------------------------------------------------
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PetStoreResource.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PetStoreResource.java
index 868c205..532b554 100644
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PetStoreResource.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PetStoreResource.java
@@ -137,7 +137,7 @@ public class PetStoreResource extends ResourceJena {
 		@BeanProperty(format="$%.2f")  // Renders price in dollars.
 		public float price;
 
-		@BeanProperty(swap=DateSwap.ISO8601D.class)  // Renders dates in ISO8601 format.
+		@Swap(DateSwap.ISO8601D.class)  // Renders dates in ISO8601 format.
 		public Date birthDate;
 
 		public int getAge() {

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SystemPropertiesResource.java
----------------------------------------------------------------------
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SystemPropertiesResource.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SystemPropertiesResource.java
index b191358..497a4b1 100644
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SystemPropertiesResource.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SystemPropertiesResource.java
@@ -217,9 +217,17 @@ public class SystemPropertiesResource extends Resource {
 	)
 	public Form getFormPage() {
 		return form().method("POST").action("servlet:/formPagePost").children(
-			h4("Set system property"),
-			"Name: ", input("text").name("name"), br(),
-			"Value: ", input("text").name("value"), br(), br(),
+			table(
+				tr(
+					th("Set system property").colspan(2)
+				),
+				tr(
+					td("Name: "), td(input("text").name("name"))
+				),
+				tr(
+					td("Value: "), td(input("text").name("value"))
+				)
+			),
 			button("submit","Click me!").style("float:right")
 		);
 	}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/UrlEncodedFormResource.java
----------------------------------------------------------------------
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/UrlEncodedFormResource.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/UrlEncodedFormResource.java
index a6d98fe..4d18d87 100644
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/UrlEncodedFormResource.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/UrlEncodedFormResource.java
@@ -106,7 +106,7 @@ public class UrlEncodedFormResource extends Resource {
 	public static class FormInputBean {
 		public String aString;
 		public int aNumber;
-		@BeanProperty(swap=CalendarSwap.ISO8601DT.class)
+		@Swap(CalendarSwap.ISO8601DT.class)
 		public Calendar aDate;
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java
----------------------------------------------------------------------
diff --git a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java
index ea3dce8..0c0edc4 100755
--- a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java
+++ b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java
@@ -321,7 +321,7 @@ public class DirectoryResource extends Resource {
 		/**
 		 * @return The file last modified timestamp.
 		 */
-		@BeanProperty(swap=DateSwap.ISO8601DTP.class)
+		@Swap(DateSwap.ISO8601DTP.class)
 		public Date getLastModified() {
 			return new Date(f.lastModified());
 		}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java
----------------------------------------------------------------------
diff --git a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java
index 1a813d9..d98b77d 100755
--- a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java
+++ b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java
@@ -308,7 +308,7 @@ public class LogsResource extends Resource {
 		public String type;
 		public Object name;
 		public Long size;
-		@BeanProperty(swap=DateSwap.DateTimeMedium.class) public Date lastModified;
+		@Swap(DateSwap.DateTimeMedium.class) public Date lastModified;
 		public URI view, highlighted, parsed, download, delete;
 
 		public FileResource(File f, URI uri) throws Exception {



[2/2] incubator-juneau git commit: Templated and per-media-type swaps.

Posted by ja...@apache.org.
Templated and per-media-type swaps.

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

Branch: refs/heads/master
Commit: f5f5edfb6de63cb66adb847d49df1ab3d9c5f91b
Parents: 1bb80a0
Author: JamesBognar <ja...@apache.org>
Authored: Sun Sep 10 16:30:51 2017 -0400
Committer: JamesBognar <ja...@apache.org>
Committed: Sun Sep 10 16:30:51 2017 -0400

----------------------------------------------------------------------
 eclipse-preferences/user-dictionary.txt         |   1 +
 .../org/apache/juneau/DynaBeanComboTest.java    |   3 +-
 .../a/rttests/RoundTripTransformBeansTest.java  |   2 +-
 .../org/apache/juneau/jena/RdfParserTest.java   |   4 +-
 .../juneau/transforms/CalendarSwapTest.java     |   6 +-
 .../apache/juneau/transforms/PojoSwapTest.java  |  86 ++++
 .../transforms/SwapsAnnotationComboTest.java    | 467 +++++++++++++++++--
 .../org/apache/juneau/dto/atom/CommonEntry.java |   2 +-
 .../java/org/apache/juneau/dto/atom/Entry.java  |   2 +-
 .../apache/juneau/dto/jsonschema/Schema.java    |   8 +-
 .../java/org/apache/juneau/BeanContext.java     |  28 +-
 .../main/java/org/apache/juneau/BeanMap.java    |   4 +-
 .../java/org/apache/juneau/BeanMapEntry.java    |   4 +-
 .../org/apache/juneau/BeanPropertyMeta.java     |  37 +-
 .../main/java/org/apache/juneau/ClassMeta.java  |  27 +-
 .../org/apache/juneau/CoreObjectBuilder.java    |   2 +-
 .../apache/juneau/annotation/BeanProperty.java  |  24 -
 .../java/org/apache/juneau/annotation/Swap.java |  91 +++-
 .../org/apache/juneau/annotation/Swaps.java     |  19 +-
 .../juneau/serializer/SerializerGroup.java      |  45 +-
 .../org/apache/juneau/transform/PojoSwap.java   | 313 +++++++++++--
 .../org/apache/juneau/transform/Surrogate.java  | 134 ++++++
 .../apache/juneau/transform/SurrogateSwap.java  | 111 +----
 .../org/apache/juneau/transform/package.html    |   2 +-
 .../juneau/transforms/ByteArrayBase64Swap.java  |  18 +-
 .../juneau/transforms/CalendarLongSwap.java     |  19 +-
 .../juneau/transforms/CalendarMapSwap.java      |  27 +-
 .../apache/juneau/transforms/DateLongSwap.java  |   2 +-
 .../apache/juneau/transforms/DateMapSwap.java   |   2 +-
 .../apache/juneau/transforms/ReaderSwap.java    |  16 +-
 .../juneau/transforms/StringFormatSwap.java     |   6 +-
 .../transforms/XMLGregorianCalendarSwap.java    |   6 +-
 .../src/main/javadoc/overview.html              | 138 ++++--
 .../examples/addressbook/CreatePerson.java      |   5 +-
 .../juneau/examples/addressbook/Person.java     |  11 +-
 .../juneau/examples/rest/PetStoreResource.java  |   2 +-
 .../examples/rest/SystemPropertiesResource.java |  14 +-
 .../examples/rest/UrlEncodedFormResource.java   |   2 +-
 .../resources/DirectoryResource.java            |   2 +-
 .../microservice/resources/LogsResource.java    |   2 +-
 40 files changed, 1332 insertions(+), 362 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/eclipse-preferences/user-dictionary.txt
----------------------------------------------------------------------
diff --git a/eclipse-preferences/user-dictionary.txt b/eclipse-preferences/user-dictionary.txt
index 6401d9c..712d0c0 100644
--- a/eclipse-preferences/user-dictionary.txt
+++ b/eclipse-preferences/user-dictionary.txt
@@ -483,3 +483,4 @@ hyperlinked
 bpx
 bpi
 tooltip
+templated

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/DynaBeanComboTest.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/DynaBeanComboTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/DynaBeanComboTest.java
index 9afaaf8..b8bcc2a 100644
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/DynaBeanComboTest.java
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/DynaBeanComboTest.java
@@ -317,7 +317,8 @@ public class DynaBeanComboTest extends ComboRoundTripTest {
 
 	@Bean(sort=true)
 	public static class BeanWithDynaFieldSwapped {
-		@BeanProperty(name="*", swap=CalendarSwap.ISO8601DTZ.class)
+		@BeanProperty(name="*")
+		@Swap(CalendarSwap.ISO8601DTZ.class)
 		public Map<String,Calendar> f1 = new LinkedHashMap<String,Calendar>();
 
 		public BeanWithDynaFieldSwapped init() {

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripTransformBeansTest.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripTransformBeansTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripTransformBeansTest.java
index f05026c..bde5202 100755
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripTransformBeansTest.java
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripTransformBeansTest.java
@@ -358,7 +358,7 @@ public class RoundTripTransformBeansTest extends RoundTripTest {
 		}
 	}
 
-	public static class D2 {
+	public static class D2 implements Surrogate {
 		public String f2;
 		public D2(D1 d1) {
 			f2 = d1.f1;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/jena/RdfParserTest.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/jena/RdfParserTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/jena/RdfParserTest.java
index 20bb71b..1f1002c 100755
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/jena/RdfParserTest.java
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/jena/RdfParserTest.java
@@ -116,7 +116,7 @@ public class RdfParserTest {
 		public String f2;
 		@Rdf(beanUri=true) public URI f3;
 		public URI f4a, f4b;
-		@BeanProperty(swap=CalendarSwap.ISO8601DTZ.class) public Calendar f5;
+		@Swap(CalendarSwap.ISO8601DTZ.class) public Calendar f5;
 		public LinkedList<A1> f6 = new LinkedList<A1>();
 
 		public A init() throws Exception {
@@ -141,7 +141,7 @@ public class RdfParserTest {
 		public String f2;
 		@Rdf(beanUri=true) public URI f3;
 		public URI f4a, f4b;
-		@BeanProperty(swap=CalendarSwap.ISO8601DTZ.class) public Calendar f5;
+		@Swap(CalendarSwap.ISO8601DTZ.class) public Calendar f5;
 
 		public A1 init() throws Exception {
 			f1 = 1;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/transforms/CalendarSwapTest.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/transforms/CalendarSwapTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/transforms/CalendarSwapTest.java
index 7ea6697..e7a9b27 100755
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/transforms/CalendarSwapTest.java
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/transforms/CalendarSwapTest.java
@@ -261,7 +261,7 @@ public class CalendarSwapTest {
 
 	@Bean(sort=true)
 	public static class A {
-		@BeanProperty(swap=CalendarSwap.ISO8601DTZ.class)
+		@Swap(CalendarSwap.ISO8601DTZ.class)
 		public Calendar d1;
 		private Calendar d2, d3;
 		public A(Calendar date) {
@@ -270,7 +270,7 @@ public class CalendarSwapTest {
 
 		public A() {}
 
-		@BeanProperty(swap=CalendarSwap.RFC2822DTZ.class)
+		@Swap(CalendarSwap.RFC2822DTZ.class)
 		public Calendar getD2() {
 			return d2;
 		}
@@ -281,7 +281,7 @@ public class CalendarSwapTest {
 		public Calendar getD3() {
 			return d3;
 		}
-		@BeanProperty(swap=CalendarLongSwap.class)
+		@Swap(CalendarLongSwap.class)
 		public void setD3(Calendar d3) {
 			this.d3 = d3;
 		}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/transforms/PojoSwapTest.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/transforms/PojoSwapTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/transforms/PojoSwapTest.java
new file mode 100644
index 0000000..df9b756
--- /dev/null
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/transforms/PojoSwapTest.java
@@ -0,0 +1,86 @@
+// ***************************************************************************************************************************
+// * 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.transforms;
+
+import static org.junit.Assert.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.html.*;
+import org.apache.juneau.http.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.transform.*;
+import org.apache.juneau.xml.*;
+import org.junit.*;
+
+/**
+ * Tests the example code in the PojoSwap class.
+ */
+public class PojoSwapTest {
+	
+	public static class MyPojo {}
+	
+	public static class MyJsonSwap extends PojoSwap<MyPojo,String> {
+			
+		public MediaType[] forMediaTypes() {
+			return MediaType.forStrings("*/json");
+		}
+		
+		public String swap(BeanSession session, MyPojo o) throws Exception {
+			return "It's JSON!";
+		}
+	}
+	
+	public static class MyXmlSwap extends PojoSwap<MyPojo,String> {
+		
+		public MediaType[] forMediaTypes() {
+			return MediaType.forStrings("*/xml");
+		}
+		
+		public String swap(BeanSession session, MyPojo o) throws Exception {
+			return "It's XML!";
+		}
+	}
+
+	public static class MyOtherSwap extends PojoSwap<MyPojo,String> {
+		
+		public MediaType[] forMediaTypes() {
+			return MediaType.forStrings("*/*");
+		}
+		
+		public String swap(BeanSession session, MyPojo o) throws Exception {
+			return "It's something else!";
+		}
+	}
+	
+	@Test
+	public void doTest() throws Exception {
+		
+		SerializerGroup g = new SerializerGroupBuilder()
+			.append(JsonSerializer.class, XmlSerializer.class, HtmlSerializer.class)
+			.sq()
+			.pojoSwaps(MyJsonSwap.class, MyXmlSwap.class, MyOtherSwap.class)
+			.build();
+		
+		MyPojo myPojo = new MyPojo();
+		
+		String json = g.getWriterSerializer("text/json").serialize(myPojo);
+		assertEquals("'It\\'s JSON!'", json);
+		
+		String xml = g.getWriterSerializer("text/xml").serialize(myPojo);
+		assertEquals("<string>It's XML!</string>", xml);
+
+		String html = g.getWriterSerializer("text/html").serialize(myPojo);
+		assertEquals("<string>It's something else!</string>", html);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/transforms/SwapsAnnotationComboTest.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/transforms/SwapsAnnotationComboTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/transforms/SwapsAnnotationComboTest.java
index c873ebe..0152ea5 100644
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/transforms/SwapsAnnotationComboTest.java
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/transforms/SwapsAnnotationComboTest.java
@@ -12,17 +12,19 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.transforms;
 
+import java.io.*;
 import java.util.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
+import org.apache.juneau.http.*;
 import org.apache.juneau.serializer.*;
 import org.apache.juneau.transform.*;
 import org.junit.runner.*;
 import org.junit.runners.*;
 
 /**
- * Exhaustive serialization tests for the CalendarSwap class.
+ * Exhaustive serialization tests Swap annotation.
  */
 @RunWith(Parameterized.class)
 @SuppressWarnings({"javadoc"})
@@ -288,34 +290,202 @@ public class SwapsAnnotationComboTest extends ComboSerializeTest {
 					/* RdfXmlR */	"<rdf:RDF>\n  <rdf:Description>\n    <j:value>XML</j:value>\n  </rdf:Description>\n</rdf:RDF>\n"
 				)
 			},
-//			{ 	/* 0 */
-//				new ComboInput<byte[]>(
-//					"ByteArray1d",
-//					byte[].class,
-//					new byte[] {1,2,3},
-//					/* Json */		"xxx",
-//					/* JsonT */		"xxx",
-//					/* JsonR */		"xxx",
-//					/* Xml */		"xxx",
-//					/* XmlT */		"xxx",
-//					/* XmlR */		"xxx",
-//					/* XmlNs */		"xxx",
-//					/* Html */		"xxx",
-//					/* HtmlT */		"xxx",
-//					/* HtmlR */		"xxx",
-//					/* Uon */		"xxx",
-//					/* UonT */		"xxx",
-//					/* UonR */		"xxx",
-//					/* UrlEnc */	"xxx",
-//					/* UrlEncT */	"xxx",
-//					/* UrlEncR */	"xxx",
-//					/* MsgPack */	"xxx",
-//					/* MsgPackT */	"xxx",
-//					/* RdfXml */	"xxx",
-//					/* RdfXmlT */	"xxx",
-//					/* RdfXmlR */	"xxx"
-//				)
-//			},
+			{ 	/* 9 */
+				new ComboInput<TestTemplate>(
+					"TestTemplate",
+					TestTemplate.class,
+					new TestTemplate(),
+					/* Json */		"foo",
+					/* JsonT */		"foo",
+					/* JsonR */		"foo",
+					/* Xml */		"foo",
+					/* XmlT */		"foo",
+					/* XmlR */		"foo\n",
+					/* XmlNs */		"foo",
+					/* Html */		"foo",
+					/* HtmlT */		"foo",
+					/* HtmlR */		"foo",
+					/* Uon */		"foo",
+					/* UonT */		"foo",
+					/* UonR */		"foo",
+					/* UrlEnc */	"foo",
+					/* UrlEncT */	"foo",
+					/* UrlEncR */	"foo",
+					/* MsgPack */	"666F6F",
+					/* MsgPackT */	"666F6F",
+					/* RdfXml */	"<rdf:RDF>\n<rdf:Description>\n<j:value>foo</j:value>\n</rdf:Description>\n</rdf:RDF>\n",
+					/* RdfXmlT */	"<rdf:RDF>\n<rdf:Description>\n<j:value>foo</j:value>\n</rdf:Description>\n</rdf:RDF>\n",
+					/* RdfXmlR */	"<rdf:RDF>\n  <rdf:Description>\n    <j:value>foo</j:value>\n  </rdf:Description>\n</rdf:RDF>\n"
+				)
+			},
+			{ 	/* 10 */
+				new ComboInput<TestTemplates>(
+					"TestTemplates",
+					TestTemplates.class,
+					new TestTemplates(),
+					/* Json */		"JSON",
+					/* JsonT */		"JSON",
+					/* JsonR */		"JSON",
+					/* Xml */		"XML",
+					/* XmlT */		"XML",
+					/* XmlR */		"XML\n",
+					/* XmlNs */		"XML",
+					/* Html */		"HTML",
+					/* HtmlT */		"HTML",
+					/* HtmlR */		"HTML",
+					/* Uon */		"UON",
+					/* UonT */		"UON",
+					/* UonR */		"UON",
+					/* UrlEnc */	"URLENCODING",
+					/* UrlEncT */	"URLENCODING",
+					/* UrlEncR */	"URLENCODING",
+					/* MsgPack */	"4D53475041434B",
+					/* MsgPackT */	"4D53475041434B",
+					/* RdfXml */	"<rdf:RDF>\n<rdf:Description>\n<j:value>RDFXML</j:value>\n</rdf:Description>\n</rdf:RDF>\n",
+					/* RdfXmlT */	"<rdf:RDF>\n<rdf:Description>\n<j:value>RDFXML</j:value>\n</rdf:Description>\n</rdf:RDF>\n",
+					/* RdfXmlR */	"<rdf:RDF>\n  <rdf:Description>\n    <j:value>RDFXML</j:value>\n  </rdf:Description>\n</rdf:RDF>\n"
+				)
+			},
+			{ 	/* 11 */
+				new ComboInput<TestProgrammaticTemplates>(
+					"TestProgrammaticTemplates",
+					TestProgrammaticTemplates.class,
+					new TestProgrammaticTemplates(),
+					/* Json */		"JSON",
+					/* JsonT */		"JSON",
+					/* JsonR */		"JSON",
+					/* Xml */		"XML",
+					/* XmlT */		"XML",
+					/* XmlR */		"XML\n",
+					/* XmlNs */		"XML",
+					/* Html */		"HTML",
+					/* HtmlT */		"HTML",
+					/* HtmlR */		"HTML",
+					/* Uon */		"UON",
+					/* UonT */		"UON",
+					/* UonR */		"UON",
+					/* UrlEnc */	"URLENCODING",
+					/* UrlEncT */	"URLENCODING",
+					/* UrlEncR */	"URLENCODING",
+					/* MsgPack */	"4D53475041434B",
+					/* MsgPackT */	"4D53475041434B",
+					/* RdfXml */	"<rdf:RDF>\n<rdf:Description>\n<j:value>RDFXML</j:value>\n</rdf:Description>\n</rdf:RDF>\n",
+					/* RdfXmlT */	"<rdf:RDF>\n<rdf:Description>\n<j:value>RDFXML</j:value>\n</rdf:Description>\n</rdf:RDF>\n",
+					/* RdfXmlR */	"<rdf:RDF>\n  <rdf:Description>\n    <j:value>RDFXML</j:value>\n  </rdf:Description>\n</rdf:RDF>\n"
+				)
+			},
+			{ 	/* 12 */
+				new ComboInput<TestContextSwap>(
+					"TestContextSwap",
+					TestContextSwap.class,
+					new TestContextSwap(),
+					/* Json */		"TEMPLATE",
+					/* JsonT */		"TEMPLATE",
+					/* JsonR */		"TEMPLATE",
+					/* Xml */		"TEMPLATE",
+					/* XmlT */		"TEMPLATE",
+					/* XmlR */		"TEMPLATE\n",
+					/* XmlNs */		"TEMPLATE",
+					/* Html */		"TEMPLATE",
+					/* HtmlT */		"TEMPLATE",
+					/* HtmlR */		"TEMPLATE",
+					/* Uon */		"TEMPLATE",
+					/* UonT */		"TEMPLATE",
+					/* UonR */		"TEMPLATE",
+					/* UrlEnc */	"TEMPLATE",
+					/* UrlEncT */	"TEMPLATE",
+					/* UrlEncR */	"TEMPLATE",
+					/* MsgPack */	"54454D504C415445",
+					/* MsgPackT */	"54454D504C415445",
+					/* RdfXml */	"<rdf:RDF>\n<rdf:Description>\n<j:value>TEMPLATE</j:value>\n</rdf:Description>\n</rdf:RDF>\n",
+					/* RdfXmlT */	"<rdf:RDF>\n<rdf:Description>\n<j:value>TEMPLATE</j:value>\n</rdf:Description>\n</rdf:RDF>\n",
+					/* RdfXmlR */	"<rdf:RDF>\n  <rdf:Description>\n    <j:value>TEMPLATE</j:value>\n  </rdf:Description>\n</rdf:RDF>\n"
+				)
+			},
+			{ 	/* 13 */
+				new ComboInput<TestContextSwaps>(
+					"TestContextSwaps",
+					TestContextSwaps.class,
+					new TestContextSwaps(),
+					/* Json */		"JSON",
+					/* JsonT */		"JSON",
+					/* JsonR */		"JSON",
+					/* Xml */		"XML",
+					/* XmlT */		"XML",
+					/* XmlR */		"XML\n",
+					/* XmlNs */		"XML",
+					/* Html */		"HTML",
+					/* HtmlT */		"HTML",
+					/* HtmlR */		"HTML",
+					/* Uon */		"UON",
+					/* UonT */		"UON",
+					/* UonR */		"UON",
+					/* UrlEnc */	"URLENCODING",
+					/* UrlEncT */	"URLENCODING",
+					/* UrlEncR */	"URLENCODING",
+					/* MsgPack */	"4D53475041434B",
+					/* MsgPackT */	"4D53475041434B",
+					/* RdfXml */	"<rdf:RDF>\n<rdf:Description>\n<j:value>RDFXML</j:value>\n</rdf:Description>\n</rdf:RDF>\n",
+					/* RdfXmlT */	"<rdf:RDF>\n<rdf:Description>\n<j:value>RDFXML</j:value>\n</rdf:Description>\n</rdf:RDF>\n",
+					/* RdfXmlR */	"<rdf:RDF>\n  <rdf:Description>\n    <j:value>RDFXML</j:value>\n  </rdf:Description>\n</rdf:RDF>\n"
+				)
+			},
+			{ 	/* 14 */
+				new ComboInput<BeanA>(
+					"BeanA",
+					BeanA.class,
+					new BeanA(),
+					/* Json */		"SWAPPED",
+					/* JsonT */		"SWAPPED",
+					/* JsonR */		"SWAPPED",
+					/* Xml */		"SWAPPED",
+					/* XmlT */		"SWAPPED",
+					/* XmlR */		"SWAPPED\n",
+					/* XmlNs */		"SWAPPED",
+					/* Html */		"SWAPPED",
+					/* HtmlT */		"SWAPPED",
+					/* HtmlR */		"SWAPPED",
+					/* Uon */		"(f=1)",
+					/* UonT */		"(f=1)",
+					/* UonR */		"(\n\tf=1\n)",
+					/* UrlEnc */	"f=1",
+					/* UrlEncT */	"f=1",
+					/* UrlEncR */	"f=1",
+					/* MsgPack */	"81A16601",
+					/* MsgPackT */	"81A16601",
+					/* RdfXml */	"<rdf:RDF>\n<rdf:Description>\n<jp:f>1</jp:f>\n</rdf:Description>\n</rdf:RDF>\n",
+					/* RdfXmlT */	"<rdf:RDF>\n<rdf:Description>\n<jp:f>1</jp:f>\n</rdf:Description>\n</rdf:RDF>\n",
+					/* RdfXmlR */	"<rdf:RDF>\n  <rdf:Description>\n    <jp:f>1</jp:f>\n  </rdf:Description>\n</rdf:RDF>\n"
+				)
+			},
+			{ 	/* 15 */
+				new ComboInput<BeanB>(
+					"BeanB",
+					BeanB.class,
+					new BeanB(),
+					/* Json */		"{f:1}",
+					/* JsonT */		"{f:1}",
+					/* JsonR */		"{\n\tf: 1\n}",
+					/* Xml */		"<object><f>1</f></object>",
+					/* XmlT */		"<object><f>1</f></object>",
+					/* XmlR */		"<object>\n\t<f>1</f>\n</object>\n",
+					/* XmlNs */		"<object><f>1</f></object>",
+					/* Html */		"<table><tr><td>f</td><td>1</td></tr></table>",
+					/* HtmlT */		"<table><tr><td>f</td><td>1</td></tr></table>",
+					/* HtmlR */		"<table>\n\t<tr>\n\t\t<td>f</td>\n\t\t<td>1</td>\n\t</tr>\n</table>\n",
+					/* Uon */		"SWAPPED",
+					/* UonT */		"SWAPPED",
+					/* UonR */		"SWAPPED",
+					/* UrlEnc */	"SWAPPED",
+					/* UrlEncT */	"SWAPPED",
+					/* UrlEncR */	"SWAPPED",
+					/* MsgPack */	"53574150504544",
+					/* MsgPackT */	"53574150504544",
+					/* RdfXml */	"<rdf:RDF>\n<rdf:Description>\n<j:value>SWAPPED</j:value>\n</rdf:Description>\n</rdf:RDF>\n",
+					/* RdfXmlT */	"<rdf:RDF>\n<rdf:Description>\n<j:value>SWAPPED</j:value>\n</rdf:Description>\n</rdf:RDF>\n",
+					/* RdfXmlR */	"<rdf:RDF>\n  <rdf:Description>\n    <j:value>SWAPPED</j:value>\n  </rdf:Description>\n</rdf:RDF>\n"
+				)
+			},
 		});
 	}
 
@@ -325,7 +495,16 @@ public class SwapsAnnotationComboTest extends ComboSerializeTest {
 
 	@Override
 	protected Serializer applySettings(Serializer s) throws Exception {
-		return s.builder().pojoSwaps(ByteArrayBase64Swap.class).trimNullProperties(false).build();
+		return s.builder().pojoSwaps(
+				ContextSwap.class, 
+				ContextSwapJson.class, 
+				ContextSwapXml.class, 
+				ContextSwapHtml.class, 
+				ContextSwapUon.class, 
+				ContextSwapUrlEncoding.class, 
+				ContextSwapMsgPack.class, 
+				ContextSwapRdfXml.class 
+			).build();
 	}
 
 	@Swaps(
@@ -440,39 +619,253 @@ public class SwapsAnnotationComboTest extends ComboSerializeTest {
 		}
 	}
 
-	public static class SwapJson extends PojoSwap {
+	public static class SwapJson extends PojoSwap<Object,Object> {
 		public Object swap(BeanSession session, Object o) throws Exception {
 			return "JSON";
 		}
 	}
-	public static class SwapXml extends PojoSwap {
+	public static class SwapXml extends PojoSwap<Object,Object> {
 		public Object swap(BeanSession session, Object o) throws Exception {
 			return "XML";
 		}
 	}
-	public static class SwapHtml extends PojoSwap {
+	public static class SwapHtml extends PojoSwap<Object,Object> {
 		public Object swap(BeanSession session, Object o) throws Exception {
 			return "HTML";
 		}
 	}
-	public static class SwapUon extends PojoSwap {
+	public static class SwapUon extends PojoSwap<Object,Object> {
 		public Object swap(BeanSession session, Object o) throws Exception {
 			return "UON";
 		}
 	}
-	public static class SwapUrlEncoding extends PojoSwap {
+	public static class SwapUrlEncoding extends PojoSwap<Object,Object> {
 		public Object swap(BeanSession session, Object o) throws Exception {
 			return "URLENCODING";
 		}
 	}
-	public static class SwapMsgPack extends PojoSwap {
+	public static class SwapMsgPack extends PojoSwap<Object,Object> {
 		public Object swap(BeanSession session, Object o) throws Exception {
 			return "MSGPACK";
 		}
 	}
-	public static class SwapRdfXml extends PojoSwap {
+	public static class SwapRdfXml extends PojoSwap<Object,Object> {
 		public Object swap(BeanSession session, Object o) throws Exception {
 			return "RDFXML";
 		}
 	}
+	
+	@Swap(impl=TemplateSwap.class,template="foo")
+	public static class TestTemplate {}
+
+	@Swaps(
+		{
+			@Swap(value=TemplateSwap.class, mediaTypes={"*/json"}, template="JSON"),
+			@Swap(value=TemplateSwap.class, mediaTypes={"*/xml"}, template="XML"),
+			@Swap(value=TemplateSwap.class, mediaTypes={"*/html"}, template="HTML"),
+			@Swap(value=TemplateSwap.class, mediaTypes={"*/uon"}, template="UON"),
+			@Swap(value=TemplateSwap.class, mediaTypes={"*/x-www-form-urlencoded"}, template="URLENCODING"),
+			@Swap(value=TemplateSwap.class, mediaTypes={"*/msgpack"}, template="MSGPACK"),
+			@Swap(value=TemplateSwap.class, mediaTypes={"*/xml+rdf"}, template="RDFXML"),
+		}
+	)
+	public static class TestTemplates {}
+	
+	
+	public static class TemplateSwap extends PojoSwap<Object,Object> {
+		public Object swap(BeanSession session, Object o, String template) throws Exception {
+			return new StringReader(template);
+		}
+	}
+
+	@Swaps(
+		{
+			@Swap(TemplateSwapJson.class),
+			@Swap(TemplateSwapXml.class),
+			@Swap(TemplateSwapHtml.class),
+			@Swap(TemplateSwapUon.class),
+			@Swap(TemplateSwapUrlEncoding.class),
+			@Swap(TemplateSwapMsgPack.class),
+			@Swap(TemplateSwapRdfXml.class),
+		}
+	)
+	public static class TestProgrammaticTemplates {}
+	
+	public static class TemplateSwapJson extends TemplateSwap {
+		public MediaType[] forMediaTypes() {
+			return MediaType.forStrings("*/json");
+		}
+		public String withTemplate() {
+			return "JSON";
+		}
+	}
+	public static class TemplateSwapXml extends TemplateSwap {
+		public MediaType[] forMediaTypes() {
+			return MediaType.forStrings("*/xml");
+		}
+		public String withTemplate() {
+			return "XML";
+		}
+	}
+	public static class TemplateSwapHtml extends TemplateSwap {
+		public MediaType[] forMediaTypes() {
+			return MediaType.forStrings("*/html");
+		}
+		public String withTemplate() {
+			return "HTML";
+		}
+	}
+	public static class TemplateSwapUon extends TemplateSwap {
+		public MediaType[] forMediaTypes() {
+			return MediaType.forStrings("*/uon");
+		}
+		public String withTemplate() {
+			return "UON";
+		}
+	}
+	public static class TemplateSwapUrlEncoding extends TemplateSwap {
+		public MediaType[] forMediaTypes() {
+			return MediaType.forStrings("*/x-www-form-urlencoded");
+		}
+		public String withTemplate() {
+			return "URLENCODING";
+		}
+	}
+	public static class TemplateSwapMsgPack extends TemplateSwap {
+		public MediaType[] forMediaTypes() {
+			return MediaType.forStrings("*/msgpack");
+		}
+		public String withTemplate() {
+			return "MSGPACK";
+		}
+	}
+	public static class TemplateSwapRdfXml extends TemplateSwap {
+		public MediaType[] forMediaTypes() {
+			return MediaType.forStrings("*/xml+rdf");
+		}
+		public String withTemplate() {
+			return "RDFXML";
+		}
+	}
+	
+	
+	public static class TestContextSwap {}
+	
+	public static class ContextSwap extends PojoSwap<TestContextSwap,Object> {
+		public Object swap(BeanSession session, TestContextSwap o, String template) throws Exception {
+			return new StringReader(template);
+		}
+		public String withTemplate() {
+			return "TEMPLATE";
+		}
+	}
+	
+	public static class TestContextSwaps {}
+	
+	public static class ContextSwapJson extends PojoSwap<TestContextSwaps,Object> {
+		public Object swap(BeanSession session, TestContextSwaps o, String template) throws Exception {
+			return new StringReader(template);
+		}
+		public MediaType[] forMediaTypes() {
+			return MediaType.forStrings("*/json");
+		}
+		public String withTemplate() {
+			return "JSON";
+		}
+	}
+	public static class ContextSwapXml extends PojoSwap<TestContextSwaps,Object> {
+		public Object swap(BeanSession session, TestContextSwaps o, String template) throws Exception {
+			return new StringReader(template);
+		}
+		public MediaType[] forMediaTypes() {
+			return MediaType.forStrings("*/xml");
+		}
+		public String withTemplate() {
+			return "XML";
+		}
+	}
+	public static class ContextSwapHtml extends PojoSwap<TestContextSwaps,Object> {
+		public Object swap(BeanSession session, TestContextSwaps o, String template) throws Exception {
+			return new StringReader(template);
+		}
+		public MediaType[] forMediaTypes() {
+			return MediaType.forStrings("*/html");
+		}
+		public String withTemplate() {
+			return "HTML";
+		}
+	}
+	public static class ContextSwapUon extends PojoSwap<TestContextSwaps,Object> {
+		public Object swap(BeanSession session, TestContextSwaps o, String template) throws Exception {
+			return new StringReader(template);
+		}
+		public MediaType[] forMediaTypes() {
+			return MediaType.forStrings("*/uon");
+		}
+		public String withTemplate() {
+			return "UON";
+		}
+	}
+	public static class ContextSwapUrlEncoding extends PojoSwap<TestContextSwaps,Object> {
+		public Object swap(BeanSession session, TestContextSwaps o, String template) throws Exception {
+			return new StringReader(template);
+		}
+		public MediaType[] forMediaTypes() {
+			return MediaType.forStrings("*/x-www-form-urlencoded");
+		}
+		public String withTemplate() {
+			return "URLENCODING";
+		}
+	}
+	public static class ContextSwapMsgPack extends PojoSwap<TestContextSwaps,Object> {
+		public Object swap(BeanSession session, TestContextSwaps o, String template) throws Exception {
+			return new StringReader(template);
+		}
+		public MediaType[] forMediaTypes() {
+			return MediaType.forStrings("*/msgpack");
+		}
+		public String withTemplate() {
+			return "MSGPACK";
+		}
+	}
+	public static class ContextSwapRdfXml extends PojoSwap<TestContextSwaps,Object> {
+		public Object swap(BeanSession session, TestContextSwaps o, String template) throws Exception {
+			return new StringReader(template);
+		}
+		public MediaType[] forMediaTypes() {
+			return MediaType.forStrings("*/xml+rdf");
+		}
+		public String withTemplate() {
+			return "RDFXML";
+		}
+	}
+	
+	@Swaps(
+		{
+			@Swap(value=BeanSwap.class, mediaTypes={"*/json"}),
+			@Swap(value=BeanSwap.class, mediaTypes={"*/xml"}),
+			@Swap(value=BeanSwap.class, mediaTypes={"*/html"}),
+		}
+	)
+	public static class BeanA {
+		public int f = 1;
+	}
+	
+	@Swaps(
+		{
+			@Swap(value=BeanSwap.class, mediaTypes={"*/uon"}),
+			@Swap(value=BeanSwap.class, mediaTypes={"*/x-www-form-urlencoded"}),
+			@Swap(value=BeanSwap.class, mediaTypes={"*/msgpack"}),
+			@Swap(value=BeanSwap.class, mediaTypes={"*/xml+rdf"}),
+		}
+	)
+	public static class BeanB {
+		public int f = 1;
+	}
+
+	public static class BeanSwap extends PojoSwap<Object,Object> {
+		public Object swap(BeanSession session, Object o, String template) throws Exception {
+			return new StringReader("SWAPPED");
+		}
+	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/atom/CommonEntry.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/atom/CommonEntry.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/atom/CommonEntry.java
index 32e34d2..a82e196 100644
--- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/atom/CommonEntry.java
+++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/atom/CommonEntry.java
@@ -271,7 +271,7 @@ public class CommonEntry extends Common {
 	 *
 	 * @return The update timestamp of this object.
 	 */
-	@BeanProperty(swap=CalendarSwap.ISO8601DT.class)
+	@Swap(CalendarSwap.ISO8601DT.class)
 	public Calendar getUpdated() {
 		return updated;
 	}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/atom/Entry.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/atom/Entry.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/atom/Entry.java
index 2bd640b..290e53c 100644
--- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/atom/Entry.java
+++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/atom/Entry.java
@@ -123,7 +123,7 @@ public class Entry extends CommonEntry {
 	 *
 	 * @return The publish timestamp of this entry.
 	 */
-	@BeanProperty(swap=CalendarSwap.ISO8601DT.class)
+	@Swap(CalendarSwap.ISO8601DT.class)
 	public Calendar getPublished() {
 		return published;
 	}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/jsonschema/Schema.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/jsonschema/Schema.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/jsonschema/Schema.java
index 2e65494..37b68e8 100644
--- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/jsonschema/Schema.java
+++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/jsonschema/Schema.java
@@ -226,7 +226,7 @@ public class Schema {
 	 * 	The value of the <property>type</property> property on this bean, or <jk>null</jk> if it is not set.
 	 * 	Can be either a {@link JsonType} or {@link JsonTypeArray} depending on what value was used to set it.
 	 */
-	@BeanProperty(swap=JsonTypeOrJsonTypeArraySwap.class)
+	@Swap(JsonTypeOrJsonTypeArraySwap.class)
 	public Object getType() {
 		if (typeJsonType != null)
 			return typeJsonType;
@@ -550,7 +550,7 @@ public class Schema {
 	 * 	The value of the <property>items</property> property on this bean, or <jk>null</jk> if it is not set.
 	 * 	Can be either a {@link Schema} or {@link SchemaArray} depending on what value was used to set it.
 	 */
-	@BeanProperty(swap=SchemaOrSchemaArraySwap.class)
+	@Swap(SchemaOrSchemaArraySwap.class)
 	public Object getItems() {
 		if (itemsSchema != null)
 			return itemsSchema;
@@ -827,7 +827,7 @@ public class Schema {
 	 * 	not set.
 	 * 	Can be either a {@link Boolean} or {@link SchemaArray} depending on what value was used to set it.
 	 */
-	@BeanProperty(swap=BooleanOrSchemaArraySwap.class)
+	@Swap(BooleanOrSchemaArraySwap.class)
 	public Object getAdditionalItems() {
 		if (additionalItemsBoolean != null)
 			return additionalItemsBoolean;
@@ -1107,7 +1107,7 @@ public class Schema {
 	 * 	is not set.
 	 * 	Can be either a {@link Boolean} or {@link SchemaArray} depending on what value was used to set it.
 	 */
-	@BeanProperty(swap=BooleanOrSchemaSwap.class)
+	@Swap(BooleanOrSchemaSwap.class)
 	public Object getAdditionalProperties() {
 		if (additionalPropertiesBoolean != null)
 			return additionalItemsBoolean;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
index 70ffaf3..d73abbf 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
@@ -644,6 +644,11 @@ public class BeanContext extends Context {
 	 * 	<li>Subclasses of {@link PojoSwap}.
 	 * 	<li>Surrogate classes.  A shortcut for defining a {@link SurrogateSwap}.
 	 * </ul>
+	 *
+	 * <p>
+	 * Multiple POJO swaps can be associated with a single class.
+	 * When multiple swaps are applicable to the same class, the media type pattern defined by
+	 * {@link PojoSwap#forMediaTypes()} or {@link Swap#mediaTypes()} are used to come up with the best match.
 	 */
 	public static final String BEAN_pojoSwaps = "BeanContext.pojoSwaps.list";
 
@@ -1039,8 +1044,10 @@ public class BeanContext extends Context {
 		for (Class<?> c : pm.get(BEAN_pojoSwaps, Class[].class, new Class[0])) {
 			if (isParentClass(PojoSwap.class, c))
 				lpf.add(newInstance(PojoSwap.class, c));
-			else
+			else if (isParentClass(Surrogate.class, c))
 				lpf.addAll(SurrogateSwap.findPojoSwaps(c));
+			else
+				throw new FormattedRuntimeException("Invalid class {0} specified in Bean.pojoSwaps property.  Must be a subclass of PojoSwap or Surrogate.", c);
 		}
 		pojoSwaps = lpf.toArray(new PojoSwap[0]);
 
@@ -1063,8 +1070,8 @@ public class BeanContext extends Context {
 
 		if (! cmCacheCache.containsKey(hashCode)) {
 			ConcurrentHashMap<Class,ClassMeta> cm = new ConcurrentHashMap<Class,ClassMeta>();
-			cm.putIfAbsent(String.class, new ClassMeta(String.class, this, null, null, findPojoSwap(String.class), findChildPojoSwaps(String.class)));
-			cm.putIfAbsent(Object.class, new ClassMeta(Object.class, this, null, null, findPojoSwap(Object.class), findChildPojoSwaps(Object.class)));
+			cm.putIfAbsent(String.class, new ClassMeta(String.class, this, null, null, findPojoSwaps(String.class), findChildPojoSwaps(String.class)));
+			cm.putIfAbsent(Object.class, new ClassMeta(Object.class, this, null, null, findPojoSwaps(Object.class), findChildPojoSwaps(Object.class)));
 			cmCacheCache.putIfAbsent(hashCode, cm);
 		}
 		this.cmCache = cmCacheCache.get(hashCode);
@@ -1208,8 +1215,8 @@ public class BeanContext extends Context {
 		// If this is an array, then we want it wrapped in an uncached ClassMeta object.
 		// Note that if it has a pojo swap, we still want to cache it so that
 		// we can cache something like byte[] with ByteArrayBase64Swap.
-		if (type.isArray() && findPojoSwap(type) == null)
-			return new ClassMeta(type, this, findImplClass(type), findBeanFilter(type), findPojoSwap(type), findChildPojoSwaps(type));
+		if (type.isArray() && findPojoSwaps(type) == null)
+			return new ClassMeta(type, this, findImplClass(type), findBeanFilter(type), findPojoSwaps(type), findChildPojoSwaps(type));
 
 		// This can happen if we have transforms defined against String or Object.
 		if (cmCache == null)
@@ -1222,7 +1229,7 @@ public class BeanContext extends Context {
 				// Make sure someone didn't already set it while this thread was blocked.
 				cm = cmCache.get(type);
 				if (cm == null)
-					cm = new ClassMeta<T>(type, this, findImplClass(type), findBeanFilter(type), findPojoSwap(type), findChildPojoSwaps(type));
+					cm = new ClassMeta<T>(type, this, findImplClass(type), findBeanFilter(type), findPojoSwaps(type), findChildPojoSwaps(type));
 			}
 		}
 		if (waitForInit)
@@ -1527,12 +1534,15 @@ public class BeanContext extends Context {
 	 * @param c The class associated with the swap.
 	 * @return The swap associated with the class, or null if there is no association.
 	 */
-	private final <T> PojoSwap findPojoSwap(Class<T> c) {
+	private final <T> PojoSwap[] findPojoSwaps(Class<T> c) {
 		// Note:  On first
-		if (c != null)
+		if (c != null) {
+			List<PojoSwap> l = new ArrayList<PojoSwap>();
 			for (PojoSwap f : pojoSwaps)
 				if (isParentClass(f.getNormalClass(), c))
-					return f;
+					l.add(f);
+			return l.size() == 0 ? null : l.toArray(new PojoSwap[l.size()]);
+		}
 		return null;
 	}
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java
index c093c98..e8a95cb 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java
@@ -182,7 +182,7 @@ public class BeanMap<T> extends AbstractMap<String,Object> implements Delegate<T
 	 * in a transformed value.
 	 * For example, if the bean property type class is a {@link Date} and the bean property has the
 	 * {@link org.apache.juneau.transforms.DateSwap.ISO8601DT} swap associated with it through the
-	 * {@link BeanProperty#swap() @BeanProperty.swap()} annotation, the value being passed in must be
+	 * {@link Swap#value() @Swap.value()} annotation, the value being passed in must be
 	 * a String containing an ISO8601 date-time string value.
 	 *
 	 * <h5 class='section'>Example:</h5>
@@ -261,7 +261,7 @@ public class BeanMap<T> extends AbstractMap<String,Object> implements Delegate<T
 	 * will return the transformed value.
 	 * For example, if the bean property type class is a {@link Date} and the bean property has the
 	 * {@link org.apache.juneau.transforms.DateSwap.ISO8601DT} swap associated with it through the
-	 * {@link BeanProperty#swap() @BeanProperty.swap()} annotation, this method will return a String containing an
+	 * {@link Swap#value() @Swap.value()} annotation, this method will return a String containing an
 	 * ISO8601 date-time string value.
 	 *
 	 * <h5 class='section'>Example:</h5>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMapEntry.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMapEntry.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMapEntry.java
index 0818b33..a5a77d2 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMapEntry.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMapEntry.java
@@ -72,7 +72,7 @@ public class BeanMapEntry implements Map.Entry<String,Object> {
 	 * will return the transformed value.
 	 * For example, if the bean property type class is a {@link Date} and the bean property has the
 	 * {@link org.apache.juneau.transforms.DateSwap.ISO8601DT} swap associated with it through the
-	 * {@link BeanProperty#swap() @BeanProperty.swap()} annotation, this method will return a String containing an
+	 * {@link Swap#value() @Swap.value()} annotation, this method will return a String containing an
 	 * ISO8601 date-time string value.
 	 */
 	@Override /* Map.Entry */
@@ -95,7 +95,7 @@ public class BeanMapEntry implements Map.Entry<String,Object> {
 	 * in a transformed value.
 	 * For example, if the bean property type class is a {@link Date} and the bean property has the
 	 * {@link org.apache.juneau.transforms.DateSwap.ISO8601DT} swap associated with it through the
-	 * {@link BeanProperty#swap() @BeanProperty.swap()} annotation, the value being passed in must be a String
+	 * {@link Swap#value() @Swap.value()} annotation, the value being passed in must be a String
 	 * containing an ISO8601 date-time string value.
 	 *
 	 * @return  The set value after it's been converted.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
index 5464f52..fe2a88b 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
@@ -129,11 +129,14 @@ public class BeanPropertyMeta {
 				rawTypeMeta = f.resolveClassMeta(p, field.getGenericType(), typeVarImpls);
 				isUri |= (rawTypeMeta.isUri() || field.isAnnotationPresent(org.apache.juneau.annotation.URI.class));
 				if (p != null) {
-					swap = getPropertyPojoSwap(p);
 					if (! p.properties().isEmpty())
 						properties = split(p.properties());
 					bdClasses.addAll(Arrays.asList(p.beanDictionary()));
 				}
+				Swap s = field.getAnnotation(Swap.class);
+				if (s != null) {
+					swap = getPropertyPojoSwap(s);
+				}
 			}
 
 			if (getter != null) {
@@ -142,12 +145,14 @@ public class BeanPropertyMeta {
 					rawTypeMeta = f.resolveClassMeta(p, getter.getGenericReturnType(), typeVarImpls);
 				isUri |= (rawTypeMeta.isUri() || getter.isAnnotationPresent(org.apache.juneau.annotation.URI.class));
 				if (p != null) {
-					if (swap == null)
-						swap = getPropertyPojoSwap(p);
 					if (properties != null && ! p.properties().isEmpty())
 						properties = split(p.properties());
 					bdClasses.addAll(Arrays.asList(p.beanDictionary()));
 				}
+				Swap s = getter.getAnnotation(Swap.class);
+				if (s != null && swap == null) {
+					swap = getPropertyPojoSwap(s);
+				}
 			}
 
 			if (setter != null) {
@@ -162,6 +167,10 @@ public class BeanPropertyMeta {
 						properties = split(p.properties());
 					bdClasses.addAll(Arrays.asList(p.beanDictionary()));
 				}
+				Swap s = setter.getAnnotation(Swap.class);
+				if (s != null && swap == null) {
+					swap = getPropertyPojoSwap(s);
+				}
 			}
 
 			if (rawTypeMeta == null)
@@ -227,12 +236,26 @@ public class BeanPropertyMeta {
 		private static PojoSwap getPropertyPojoSwap(BeanProperty p) throws Exception {
 			if (! p.format().isEmpty())
 				return newInstance(PojoSwap.class, StringFormatSwap.class, p.format());
-			Class<?> c = p.swap();
+			return null;
+		}
+
+		private static PojoSwap getPropertyPojoSwap(Swap s) throws Exception {
+			Class<?> c = s.value();
+			if (c == Null.class)
+				c = s.impl();
 			if (c == Null.class)
 				return null;
-			if (isParentClass(PojoSwap.class, c))
-				return newInstance(PojoSwap.class, c);
-			throw new RuntimeException("TODO - Surrogate swaps not yet supported.");
+			if (isParentClass(PojoSwap.class, c)) {
+				PojoSwap ps = newInstance(PojoSwap.class, c);
+				if (ps.forMediaTypes() != null)
+					throw new RuntimeException("TODO - Media types on swaps not yet supported on bean properties.");
+				if (ps.withTemplate() != null)
+					throw new RuntimeException("TODO - Templates on swaps not yet supported on bean properties.");
+				return ps;
+			}
+			if (isParentClass(Surrogate.class, c))
+				throw new RuntimeException("TODO - Surrogate swaps not yet supported on bean properties.");
+			throw new FormattedRuntimeException("Invalid class used in @Swap annotation.  Must be a subclass of PojoSwap or Surrogate.", c);
 		}
 
 		BeanPropertyMeta.Builder setGetter(Method getter) {

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
index 036c4ae..32336fb 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
@@ -139,7 +139,7 @@ public final class ClassMeta<T> implements Type {
 	 * 	Used for delayed initialization when the possibility of class reference loops exist.
 	 */
 	@SuppressWarnings({ "rawtypes", "unchecked" })
-	ClassMeta(Class<T> innerClass, BeanContext beanContext, Class<? extends T> implClass, BeanFilter beanFilter, PojoSwap<T,?> pojoSwap, PojoSwap<?,?>[] childPojoSwaps) {
+	ClassMeta(Class<T> innerClass, BeanContext beanContext, Class<? extends T> implClass, BeanFilter beanFilter, PojoSwap<T,?>[] pojoSwaps, PojoSwap<?,?>[] childPojoSwaps) {
 		this.innerClass = innerClass;
 		this.beanContext = beanContext;
 
@@ -149,7 +149,7 @@ public final class ClassMeta<T> implements Type {
 			if (beanContext != null && beanContext.cmCache != null)
 				beanContext.cmCache.put(innerClass, this);
 
-			ClassMetaBuilder<T> builder = new ClassMetaBuilder(innerClass, beanContext, implClass, beanFilter, pojoSwap, childPojoSwaps);
+			ClassMetaBuilder<T> builder = new ClassMetaBuilder(innerClass, beanContext, implClass, beanFilter, pojoSwaps, childPojoSwaps);
 
 			this.cc = builder.cc;
 			this.isDelegate = builder.isDelegate;
@@ -340,7 +340,7 @@ public final class ClassMeta<T> implements Type {
 			childSwapMap,
 			childUnswapMap;
 
-		private ClassMetaBuilder(Class<T> innerClass, BeanContext beanContext, Class<? extends T> implClass, BeanFilter beanFilter, PojoSwap<T,?> pojoSwap, PojoSwap<?,?>[] childPojoSwaps) {
+		private ClassMetaBuilder(Class<T> innerClass, BeanContext beanContext, Class<? extends T> implClass, BeanFilter beanFilter, PojoSwap<T,?>[] pojoSwaps, PojoSwap<?,?>[] childPojoSwaps) {
 			this.innerClass = innerClass;
 			this.beanContext = beanContext;
 
@@ -581,8 +581,8 @@ public final class ClassMeta<T> implements Type {
 				);
 			}
 
-			if (pojoSwap != null)
-				this.pojoSwaps.add(pojoSwap);
+			if (pojoSwaps != null)
+				this.pojoSwaps.addAll(Arrays.asList(pojoSwaps));
 
 			findPojoSwaps(this.pojoSwaps);
 
@@ -680,11 +680,22 @@ public final class ClassMeta<T> implements Type {
 
 		private PojoSwap<T,?> createPojoSwap(Swap s) {
 			Class<?> c = s.value();
+			if (c == Null.class)
+				c = s.impl();
+
+			if (isParentClass(PojoSwap.class, c)) {
+				PojoSwap ps = ClassUtils.newInstance(PojoSwap.class, c);
+				if (s.mediaTypes().length > 0)
+					ps.forMediaTypes(MediaType.forStrings(s.mediaTypes()));
+				if (! s.template().isEmpty())
+					ps.withTemplate(s.template());
+				return ps;
+			}
 
-			if (! isParentClass(PojoSwap.class, c))
-				throw new FormattedRuntimeException("Invalid swap class ''{0}'' specified.  Must extend from PojoSwap.", c);
+			if (isParentClass(SurrogateSwap.class, c))
+				throw new FormattedRuntimeException("TODO - Surrogate classes currently not supported in @Swap annotation", c);
 
-			return ClassUtils.newInstance(PojoSwap.class, c).forMediaTypes(MediaType.forStrings(s.mediaTypes())).withTemplate(s.template());
+			throw new FormattedRuntimeException("Invalid swap class ''{0}'' specified.  Must extend from PojoSwap or Surrogate.", c);
 		}
 
 		private ClassMeta<?> findClassMeta(Class<?> c) {

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/CoreObjectBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/CoreObjectBuilder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/CoreObjectBuilder.java
index 296aca1..2049e25 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/CoreObjectBuilder.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/CoreObjectBuilder.java
@@ -1054,7 +1054,7 @@ public abstract class CoreObjectBuilder {
 	 * There are two category of classes that can be passed in through this method:
 	 * <ul>
 	 * 	<li>Subclasses of {@link PojoSwap}.
-	 * 	<li>Surrogate classes.  A shortcut for defining a {@link SurrogateSwap}.
+	 * 	<li>Implementations of {@link Surrogate}.
 	 * </ul>
 	 *
 	 * <h5 class='section'>Notes:</h5>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/BeanProperty.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/BeanProperty.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/BeanProperty.java
index 764b5c2..fc3b4c7 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/BeanProperty.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/BeanProperty.java
@@ -16,10 +16,8 @@ import static java.lang.annotation.ElementType.*;
 import static java.lang.annotation.RetentionPolicy.*;
 
 import java.lang.annotation.*;
-import java.util.*;
 
 import org.apache.juneau.*;
-import org.apache.juneau.transform.*;
 
 /**
  * Used tailor how bean properties get interpreted by the framework.
@@ -204,28 +202,6 @@ public @interface BeanProperty {
 	Class<?>[] params() default {};
 
 	/**
-	 * Associates a {@link PojoSwap} or {@link SurrogateSwap} with this bean property that will swap the value object
-	 * with another object during serialization and parsing.
-	 *
-	 * <p>
-	 * This annotation supersedes any swaps associated with the bean property type class itself.
-	 *
-	 * <p>
-	 * Typically used for rendering {@link Date Dates} and {@link Calendar Calendars} as a particular string format.
-	 *
-	 * <h5 class='section'>Example:</h5>
-	 * <p class='bcode'>
-	 * 	<jk>public class</jk> MyClass {
-	 *
-	 * 		<jc>// During serialization, convert to ISO8601 date-time string.</jc>
-	 * 		<ja>@BeanProperty</ja>(pojoSwap=CalendarSwap.ISO8601DT.<jk>class</jk>)
-	 * 		<jk>public</jk> Calendar getTime();
-	 * 	}
-	 * </p>
-	 */
-	Class<?> swap() default Null.class;
-
-	/**
 	 * Used to limit which child properties are rendered by the serializers.
 	 *
 	 * <p>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Swap.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Swap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Swap.java
index 1daa3e9..88ccb1d 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Swap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Swap.java
@@ -16,16 +16,105 @@ import static java.lang.annotation.ElementType.*;
 import static java.lang.annotation.RetentionPolicy.*;
 
 import java.lang.annotation.*;
+import java.util.*;
 
+import org.apache.juneau.transform.*;
+
+/**
+ * Associates {@link PojoSwap} and {@link Surrogate} classes with POJOs and bean properties.
+ *
+ * <p>
+ * A typical example is for rendering {@link Date Dates} and {@link Calendar Calendars} as a formatted string:
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode'>
+ * 	<jk>public class</jk> MyClass {
+ *
+ * 		<jc>// During serialization, convert to ISO8601 date-time string.</jc>
+ * 		<ja>@Swap</ja>(CalendarSwap.ISO8601DT.<jk>class</jk>)
+ * 		<jk>public</jk> Calendar getTime();
+ * 	}
+ * </p>
+ *
+ * <p>
+ * This annotation can be used in the following locations:
+ * <ul>
+ * 	<li>Classes.
+ * 	<li>Bean getters/setters/fields.
+ * 	<li>Inside the {@link Swaps @Swaps} annotation.
+ * </ul>
+ */
 @Documented
-@Target({TYPE,ANNOTATION_TYPE})
+@Target({TYPE,ANNOTATION_TYPE,FIELD,METHOD})
 @Retention(RUNTIME)
 @Inherited
 public @interface Swap {
 
+	/**
+	 * The {@link PojoSwap} and {@link Surrogate} class.
+	 *
+	 * <p>
+	 * A synonym for {@link #impl()}.
+	 */
 	Class<?> value() default Null.class;
 
+	/**
+	 * The {@link PojoSwap} and {@link Surrogate} class.
+	 *
+	 * <p>
+	 * A synonym for {@link #value()}.
+	 */
+	Class<?> impl() default Null.class;
+
+
+	/**
+	 * Identifies a template string along with this swap.
+	 *
+	 * <p>
+	 * Template strings are arbitrary strings associated with swaps that help provide additional context information
+	 * for the swap class.
+	 * They're called 'templates' because their primary purpose is for providing template names, such as Apache FreeMarker
+	 * template names.
+	 *
+	 * <p>
+	 * The following is an example of a templated swap class used to serialize POJOs to HTML using FreeMarker:
+	 *
+	 * <p class='bcode'>
+	 * 	<jc>// Our abstracted templated swap class.</jc>
+	 * 	<jk>public abstract class</jk> FreeMarkerSwap <jk>extends</jk> PojoSwap&lt;Object,Reader&gt; {
+	 *
+	 * 		<jk>public</jk> MediaType[] forMediaTypes() {
+	 * 			<jk>return</jk> MediaType.<jsm>forStrings</jsm>(<js>"&#42;/html"</js>);
+	 * 		}
+	 *
+	 * 		<jk>public</jk> Reader swap(BeanSession session, Object o, String template) <jk>throws</jk> Exception {
+	 * 			<jk>return</jk> getFreeMarkerReader(template, o);  <jc>// Some method that creates raw HTML.</jc>
+	 * 		}
+	 * 	}
+	 * </p>
+	 * <p class='bcode'>
+	 * 	<ja>@Swap</ja>(impl=FreeMarkerSwap.<jk>class</jk>, template=<js>"MyPojo.div.ftl"</js>)
+	 * 	<jk>public class</jk> MyPojo {}
+	 * </p>
+	 */
 	String template() default "";
 
+	/**
+	 * Identifies the media types that this swap is applicable for.
+	 *
+	 * <p>
+	 * In the following example, the swap is only invoked by the JSON serializer:
+	 *
+	 * <p class='bcode'>
+	 * 	<ja>@Swap</ja>(impl=ToStringSwap.<jk>class</jk>, mediaTypes=<js>"&#42;/json"</js>)
+	 * 	<jk>public class</jk> MyBean { ... }
+	 *
+	 * 	<jk>public class</jk> ToStringSwap <jk>extends</jk> PojoSwap&lt;Object,String&gt; {
+	 * 			<jk>public</jk> String swap(BeanSession session, Object o) <jk>throws</jk> Exception {
+	 * 				<jk>return</jk> o.toString();
+	 * 			}
+	 * 		}
+	 * </p>
+	 */
 	String[] mediaTypes() default {};
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Swaps.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Swaps.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Swaps.java
index 4795ce8..d384c17 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Swaps.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Swaps.java
@@ -17,11 +17,28 @@ import static java.lang.annotation.RetentionPolicy.*;
 
 import java.lang.annotation.*;
 
+/**
+ * Used to associate multiple swaps with the same POJO class.
+ *
+ * <p class='bcode'>
+ * 	<ja>@Swaps</ja>(
+ * 		{
+ * 			<ja>@Swap</ja>(MyJsonSwap.<jk>class</jk>),
+ * 			<ja>@Swap</ja>(MyXmlSwap.<jk>class</jk>),
+ * 			<ja>@Swap</ja>(MyOtherSwap.<jk>class</jk>)
+ * 		}
+ * 	)
+ * 	<jk>public class</jk> MyPojo {}
+ * </p>
+ */
 @Documented
-@Target({TYPE,ANNOTATION_TYPE})
+@Target({TYPE})
 @Retention(RUNTIME)
 @Inherited
 public @interface Swaps {
 
+	/**
+	 * The swaps to apply to this POJO class.
+	 */
 	Swap[] value() default {};
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerGroup.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerGroup.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerGroup.java
index 1fd700c..17d83f1 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerGroup.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerGroup.java
@@ -30,8 +30,6 @@ import org.apache.juneau.http.*;
  * 	<li>
  * 		Sets common properties on all serializers in a single method call.
  * 	<li>
- * 		Locks all serializers in a single method call.
- * 	<li>
  * 		Clones existing groups and all serializers within the group in a single method call.
  * </ul>
  *
@@ -56,8 +54,7 @@ import org.apache.juneau.http.*;
  * 		.build();
  *
  * 	<jc>// Find the appropriate serializer by Accept type</jc>
- * 	String mediaTypeMatch = g.findMatch(<js>"text/foo, text/json;q=0.8, text/*;q:0.6, *\/*;q=0.0"</js>);
- * 	WriterSerializer s = g.getWriterSerializer(mediaTypeMatch);
+ * 	WriterSerializer s = g.getWriterSerializer(<js>"text/foo, text/json;q=0.8, text/*;q:0.6, *\/*;q=0.0"</js>);
  *
  * 	<jc>// Serialize a bean to JSON text </jc>
  * 	AddressBook addressBook = <jk>new</jk> AddressBook();  <jc>// Bean to serialize.</jc>
@@ -178,6 +175,46 @@ public final class SerializerGroup {
 	}
 
 	/**
+	 * Same as {@link #getSerializer(String)}, but casts it to a {@link WriterSerializer}.
+	 *
+	 * @param acceptHeader The HTTP <l>Accept</l> header string.
+	 * @return The serializer that matched the accept header, or <jk>null</jk> if no match was made.
+	 */
+	public WriterSerializer getWriterSerializer(String acceptHeader) {
+		return (WriterSerializer)getSerializer(acceptHeader);
+	}
+
+	/**
+	 * Same as {@link #getSerializer(MediaType)}, but casts it to a {@link WriterSerializer}.
+	 *
+	 * @param mediaType The HTTP media type.
+	 * @return The serializer that matched the accept header, or <jk>null</jk> if no match was made.
+	 */
+	public WriterSerializer getWriterSerializer(MediaType mediaType) {
+		return (WriterSerializer)getSerializer(mediaType);
+	}
+
+	/**
+	 * Same as {@link #getSerializer(String)}, but casts it to an {@link OutputStreamSerializer}.
+	 *
+	 * @param acceptHeader The HTTP <l>Accept</l> header string.
+	 * @return The serializer that matched the accept header, or <jk>null</jk> if no match was made.
+	 */
+	public OutputStreamSerializer getStreamSerializer(String acceptHeader) {
+		return (OutputStreamSerializer)getSerializer(acceptHeader);
+	}
+
+	/**
+	 * Same as {@link #getSerializer(MediaType)}, but casts it to a {@link OutputStreamSerializer}.
+	 *
+	 * @param mediaType The HTTP media type.
+	 * @return The serializer that matched the accept header, or <jk>null</jk> if no match was made.
+	 */
+	public OutputStreamSerializer getStreamSerializer(MediaType mediaType) {
+		return (OutputStreamSerializer)getSerializer(mediaType);
+	}
+
+	/**
 	 * Returns the media types that all serializers in this group can handle.
 	 *
 	 * <p>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/PojoSwap.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/PojoSwap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/PojoSwap.java
index d3d7859..4f63411 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/PojoSwap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/PojoSwap.java
@@ -17,19 +17,22 @@ import static org.apache.juneau.internal.ClassUtils.*;
 import java.util.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.annotation.*;
 import org.apache.juneau.http.*;
-import org.apache.juneau.internal.*;
 import org.apache.juneau.parser.*;
 import org.apache.juneau.serializer.*;
 
 /**
- * Used to swap out non-serializable objects with serializable replacements during serialization, and vis-versa during parsing.
+ * Used to swap out non-serializable objects with serializable replacements during serialization, and vis-versa during
+ * parsing.
  *
- * <h5 class='section'>Description:</h5>
  *
+ * <h6 class='section'>Description:</h6>
+ *
+ * <p>
  * <code>PojoSwaps</code> are used to extend the functionality of the serializers and parsers to be able to handle
- * POJOs that aren't automatically handled by the serializers or parsers.  For example, JSON does not have a standard
- * representation for rendering dates.
+ * POJOs that aren't automatically handled by the serializers or parsers.
+ * <br>For example, JSON does not have a standard representation for rendering dates.
  * By defining a special {@code Date} swap and associating it with a serializer and parser, you can convert a
  * {@code Date} object to a {@code String} during serialization, and convert that {@code String} object back into a
  * {@code Date} object during parsing.
@@ -39,7 +42,7 @@ import org.apache.juneau.serializer.*;
  *
  * <p>
  * <code>PojoSwaps</code> are associated with instances of {@link BeanContext BeanContexts} by passing the swap
- * class to the {@link CoreObjectBuilder#pojoSwaps(Class...)} method.
+ * class to the {@link SerializerBuilder#pojoSwaps(Class...)} and {@link ParserBuilder#pojoSwaps(Class...)} methods.
  * <br>When associated with a bean context, fields of the specified type will automatically be converted when the
  * {@link BeanMap#get(Object)} or {@link BeanMap#put(String, Object)} methods are called.
  *
@@ -54,6 +57,7 @@ import org.apache.juneau.serializer.*;
  * <br>{@link Parser Parsers} use swaps to convert objects of type S into objects of type T, and on calls to
  * {@link BeanMap#put(String,Object)}.
  *
+ *
  * <h6 class='topic'>Subtypes</h6>
  *
  * The following abstract subclasses are provided for common swap types:
@@ -62,13 +66,6 @@ import org.apache.juneau.serializer.*;
  * 	<li>{@link MapSwap} - Objects swapped with {@link ObjectMap ObjectMaps}.
  * </ol>
  *
- * <h6 class='topic'>Localization</h6>
- *
- * Swaps have access to the session locale and timezone through the {@link BeanSession#getLocale()} and
- * {@link BeanSession#getTimeZone()} methods.
- * This allows you to specify localized swap values when needed.
- * If using the REST server API, the locale and timezone are set based on the <code>Accept-Language</code> and
- * <code>Time-Zone</code> headers on the request.
  *
  * <h6 class='topic'>Swap Class Type {@code <S>}</h6>
  *
@@ -92,10 +89,60 @@ import org.apache.juneau.serializer.*;
  * 		An array of anything on this list.
  * </ul>
  *
+ *
  * <h6 class='topic'>Normal Class Type {@code <T>}</h6>
  *
  * The normal object representation of an object.
  *
+ *
+ * <h6 class='topic'>Overview</h6>
+ *
+ * The following is an example of a swap that replaces byte arrays with BASE-64 encoded strings:
+ *
+ * <p class='bcode'>
+ * 	<jk>public class</jk> ByteArrayBase64Swap <jk>extends</jk> PojoSwap<<jk>byte</jk>[],String> {
+ *
+ * 		<jk>public</jk> String swap(BeanSession session, <jk>byte</jk>[] b) <jk>throws</jk> SerializeException {
+ * 			<jk>return</jk> StringUtils.<jsm>base64Encode</jsm>(b);
+ * 		}
+ *
+ * 		<jk>public byte</jk>[] unswap(BeanSession session, String s, ClassMeta&lt;?&gt; hint) <jk>throws</jk> ParseException {
+ * 			<jk>return</jk> StringUtils.<jsm>base64Decode</jsm>(s);
+ * 		}
+ * 	}
+ * </p>
+ *
+ * <p class='bcode'>
+ * 	WriterSerializer s = JsonSerializer.<jsf>DEFAULT_LAX</jsf>.builder().pojoSwaps(ByteArrayBase64Swap.<jk>class</jk>).build();
+ * 	String json = s.serialize(<jk>new byte</jk>[] {1,2,3});  <jc>// Produces "'AQID'"</jc>
+ * </p>
+ *
+ *
+ * <h6 class='topic'>Swap annotation</h6>
+ *
+ * <p>
+ * Swap classes are often associated directly with POJOs using the {@link Swap @Swap} annotation.
+ *
+ * <p class='bcode'>
+ * 	<jk>public class</jk> MyPojoSwap <jk>extends</jk> PojoSwap&lt;MyPojo,String&gt; { ... }
+ *
+ * 	<ja>@Swap</ja>(MyPojoSwap.<jk>class</jk>)
+ * 	<jk>public class</jk> MyPojo { ... }
+ * </p>
+ *
+ * <p>
+ * The <ja>@Swap</ja> annotation is often simpler since you do not need to tell your serializers and parsers about them
+ * leading to less code.
+ *
+ * <p>
+ * Swaps can also be associated with getters and setters as well:
+ *
+ * <p class='bcode'>
+ * 	<ja>@BeanProperty</ja>(swap=MyPojo.<jk>class</jk>)
+ * 	<jk>public</jk> MyPojo getMyPojo();
+ * </p>
+ *
+ *
  * <h6 class='topic'>One-way vs. Two-way Serialization</h6>
  *
  * Note that while there is a unified interface for handling swaps during both serialization and parsing,
@@ -109,7 +156,162 @@ import org.apache.juneau.serializer.*;
  * possible to re-parse it back into a {@code Date}, since there is no way for the {@code Parser} to know it's a
  * {@code Date} from just the JSON or XML text.
  *
- * <h5 class='section'>Additional information:</h5>
+ *
+ * <h6 class='topic'>Per media-type swaps</h6>
+ * <p>
+ * The {@link #forMediaTypes()} method can be overridden to provide a set of media types that the swap is invoked on.
+ * It's also possible to define multiple swaps against the same POJO as long as they're differentiated by media type.
+ * When multiple swaps are defined, the best-match media type is used.
+ *
+ * <p>
+ * In the following example, we define 3 swaps against the same POJO.  One for JSON, one for XML, and one for all
+ * other types.
+ *
+ * <p class='bcode'>
+ * 	<jk>public class</jk> PojoSwapTest {
+ *
+ * 		<jk>public static class</jk> MyPojo {}
+ *
+ * 		<jk>public static class</jk> MyJsonSwap <jk>extends</jk> PojoSwap&lt;MyPojo,String&gt; {
+ *
+ * 			<jk>public</jk> MediaType[] forMediaTypes() {
+ * 				<jk>return</jk> MediaType.<jsm>forStrings</jsm>(<js>"&#42;/json"</js>);
+ * 			}
+ *
+ * 			<jk>public</jk> String swap(BeanSession session, MyPojo o) <jk>throws</jk> Exception {
+ * 				<jk>return</jk> <js>"It's JSON!"</js>;
+ * 			}
+ * 		}
+ *
+ * 		<jk>public static class</jk> MyXmlSwap <jk>extends</jk> PojoSwap&lt;MyPojo,String&gt; {
+ *
+ * 			<jk>public</jk> MediaType[] forMediaTypes() {
+ * 				<jk>return</jk> MediaType.<jsm>forStrings</jsm>(<js>"&#42;/xml"</js>);
+ * 			}
+ *
+ * 			<jk>public</jk> String swap(BeanSession session, MyPojo o) <jk>throws</jk> Exception {
+ * 				<jk>return</jk> <js>"It's XML!"</js>;
+ * 			}
+ * 		}
+ *
+ * 		<jk>public static class</jk> MyOtherSwap <jk>extends</jk> PojoSwap&lt;MyPojo,String&gt; {
+ *
+ * 			<jk>public</jk> MediaType[] forMediaTypes() {
+ * 				<jk>return</jk> MediaType.<jsm>forStrings</jsm>(<js>"&#42;/*"</js>);
+ * 			}
+ *
+ * 			<jk>public</jk> String swap(BeanSession session, MyPojo o) <jk>throws</jk> Exception {
+ * 				<jk>return</jk> <js>"It's something else!"</js>;
+ * 			}
+ * 		}
+ *
+ * 		<ja>@Test</ja>
+ * 		<jk>public void</jk> doTest() <jk>throws</jk> Exception {
+ *
+ * 			SerializerGroup g = <jk>new</jk> SerializerGroupBuilder()
+ * 				.append(JsonSerializer.<jk>class</jk>, XmlSerializer.<jk>class</jk>, HtmlSerializer.<jk>class</jk>)
+ * 				.sq()
+ * 				.pojoSwaps(MyJsonSwap.<jk>class</jk>, MyXmlSwap.<jk>class</jk>, MyOtherSwap.<jk>class</jk>)
+ * 				.build();
+ *
+ * 			MyPojo myPojo = <jk>new</jk> MyPojo();
+ *
+ * 			String json = g.getWriterSerializer(<js>"text/json"</js>).serialize(myPojo);
+ * 			<jsm>assertEquals</jsm>(<js>"'It\\'s JSON!'"</js>, json);
+ *
+ * 			String xml = g.getWriterSerializer(<js>"text/xml"</js>).serialize(myPojo);
+ * 			<jsm>assertEquals</jsm>(<js>"&lt;string&gt;It's XML!&lt;/string&gt;"</js>, xml);
+ *
+ * 			String html = g.getWriterSerializer(<js>"text/html"</js>).serialize(myPojo);
+ * 			<jsm>assertEquals</jsm>(<js>"&lt;string&gt;It's something else!&lt;/string&gt;"</js>, html);
+ * 		}
+ * 	}
+ * </p>
+ *
+ * <p>
+ * Multiple swaps can be associated with a POJO by using the {@link Swaps @Swaps} annotation:
+ *
+ * <p class='bcode'>
+ * 	<ja>@Swaps</ja>(
+ * 		{
+ * 			<ja>@Swap</ja>(MyJsonSwap.<jk>class</jk>),
+ * 			<ja>@Swap</ja>(MyXmlSwap.<jk>class</jk>),
+ * 			<ja>@Swap</ja>(MyOtherSwap.<jk>class</jk>)
+ * 		}
+ * 	)
+ * 	<jk>public class</jk> MyPojo {}
+ * </p>
+ *
+ * <p>
+ * Note that since <code>Readers</code> get serialized directly to the output of a serializer, it's possible to
+ * implement a swap that provides fully-customized output.
+ *
+ * <p class='bcode'>
+ * 	<jk>public class</jk> MyJsonSwap <jk>extends</jk> PojoSwap&lt;MyPojo,Reader&gt; {
+ *
+ * 		<jk>public</jk> MediaType[] forMediaTypes() {
+ * 			<jk>return</jk> MediaType.<jsm>forStrings</jsm>(<js>"&#42;/json"</js>);
+ * 		}
+ *
+ * 		<jk>public</jk> Reader swap(BeanSession session, MyPojo o) <jk>throws</jk> Exception {
+ * 			<jk>return new</jk> StringReader(<js>"{message:'Custom JSON!'}"</js>);
+ * 		}
+ * 	}
+ * </p>
+ *
+ *
+ * <h6 class='topic'>Templates</h6>
+ *
+ * <p>
+ * Template strings are arbitrary strings associated with swaps that help provide additional context information
+ * for the swap class.
+ * They're called 'templates' because their primary purpose is for providing template names, such as Apache FreeMarker
+ * template names.
+ *
+ * <p>
+ * The following is an example of a templated swap class used to serialize POJOs to HTML using FreeMarker:
+ *
+ * <p class='bcode'>
+ * 	<jc>// Our abstracted templated swap class.</jc>
+ * 	<jk>public abstract class</jk> FreeMarkerSwap <jk>extends</jk> PojoSwap&lt;Object,Reader&gt; {
+ *
+ * 		<jk>public</jk> MediaType[] forMediaTypes() {
+ * 			<jk>return</jk> MediaType.<jsm>forStrings</jsm>(<js>"&#42;/html"</js>);
+ * 		}
+ *
+ * 		<jk>public</jk> Reader swap(BeanSession session, Object o, String template) <jk>throws</jk> Exception {
+ * 			<jk>return</jk> getFreeMarkerReader(template, o);  <jc>// Some method that creates raw HTML.</jc>
+ * 		}
+ * 	}
+ *
+ * 	<jc>// An implementation of our templated swap class.</jc>
+ * 	<jk>public class</jk> MyPojoSwap <jk>extends</jk> FreeMarkerSwap {
+ * 		<jk>public</jk> String withTemplate() {
+ * 			<jk>return</jk> <js>"MyPojo.div.ftl"</js>;
+ * 		}
+ * 	}
+ * </p>
+ *
+ * <p>
+ * In practice however, the template is usually going to be defined on a <ja>@Swap</ja> annotation like the following
+ * example:
+ *
+ * <p class='bcode'>
+ * 	<ja>@Swap</ja>(impl=FreeMarkerSwap.<jk>class</jk>, template=<js>"MyPojo.div.ftl"</js>)
+ * 	<jk>public class</jk> MyPojo {}
+ * </p>
+ *
+ *
+ * <h6 class='topic'>Localization</h6>
+ *
+ * Swaps have access to the session locale and timezone through the {@link BeanSession#getLocale()} and
+ * {@link BeanSession#getTimeZone()} methods.
+ * This allows you to specify localized swap values when needed.
+ * If using the REST server API, the locale and timezone are set based on the <code>Accept-Language</code> and
+ * <code>Time-Zone</code> headers on the request.
+ *
+ *
+ * <h6 class='section'>Additional information:</h6>
  *
  * See <a class='doclink' href='package-summary.html#TOC'>org.apache.juneau.transform</a> for more information.
  *
@@ -129,7 +331,7 @@ public abstract class PojoSwap<T,S> {
 	private ClassMeta<?> swapClassMeta;
 
 	// Unfortunately these cannot be made final because we want to allow for PojoSwaps with no-arg constructors
-	// which simplifies
+	// which simplifies declarations.
 	private MediaType[] forMediaTypes;
 	private String template;
 
@@ -156,36 +358,83 @@ public abstract class PojoSwap<T,S> {
 		this.template = withTemplate();
 	}
 
-	protected MediaType[] forMediaTypes() {
+	/**
+	 * Returns the media types that this swap is applicable to.
+	 *
+	 * <p>
+	 * This method can be overridden to programmatically specify what media types it applies to.
+	 *
+	 * <p>
+	 * This method is the programmatic equivalent to the {@link Swap#mediaTypes()} annotation.
+	 *
+	 * @return The media types that this swap is applicable to, or <jk>null</jk> if it's applicable for all media types.
+	 */
+	public MediaType[] forMediaTypes() {
 		return null;
 	}
 
-	protected String withTemplate() {
+	/**
+	 * Returns additional context information for this swap.
+	 *
+	 * <p>
+	 * Typically this is going to be used to specify a template name, such as a FreeMarker template file name.
+	 *
+	 * <p>
+	 * This method can be overridden to programmatically specify a template value.
+	 *
+	 * <p>
+	 * This method is the programmatic equivalent to the {@link Swap#template()} annotation.
+	 *
+	 * @return Additional context information, or <jk>null</jk> if not specified.
+	 */
+	public String withTemplate() {
 		return null;
 	}
 
+	/**
+	 * Sets the media types that this swap is associated with.
+	 *
+	 * @param mediaTypes The media types that this swap is associated with.
+	 * @return This object (for method chaining).
+	 */
 	public PojoSwap<T,?> forMediaTypes(MediaType[] mediaTypes) {
-		if (mediaTypes != null && mediaTypes.length > 0)
-			this.forMediaTypes = mediaTypes;
+		this.forMediaTypes = mediaTypes;
 		return this;
 	}
 
+	/**
+	 * Sets the template string on this swap.
+	 *
+	 * @param template The template string on this swap.
+	 * @return This object (for method chaining).
+	 */
 	public PojoSwap<T,?> withTemplate(String template) {
-		if (! StringUtils.isEmpty(template))
-			this.template = template;
+		this.template = template;
 		return this;
 	}
 
-//	/**
-//	 * Returns <jk>true</jk> if this swap is valid for the specified session.
-//	 *
-//	 * <p>
-//	 * If the swap has a media type associated with it, this method ensures that the media type matches the media
-//	 * type defined on the session.
-//	 *
-//	 * @param session The bean session.
-//	 * @return <jk>true</jk> if this swap is valid for the specified session.
-//	 */
+	/**
+	 * Returns a number indicating how well this swap matches the specified session.
+	 *
+	 * <p>
+	 * Uses the {@link MediaType#match(MediaType, boolean)} method algorithm to produce a number whereby a
+	 * larger value indicates a "better match".
+	 * The idea being that if multiple swaps are associated with a given POJO, we want to pick the best one.
+	 *
+	 * <p>
+	 * For example, if the session media type is <js>"text/json"</js>, then the match values are shown below:
+	 *
+	 * <ul>
+	 * 	<li><js>"text/json"</js> = <code>100,000</code>
+	 * 	<li><js>"&#42;/json"</js> = <code>5,100</code>
+	 * 	<li><js>"&#42;/&#42;"</js> = <code>5,000</code>
+	 * 	<li>No media types specified on swap = <code>1</code>
+	 * 	<li><js>"text/xml"</js> = <code>0</code>
+	 * </ul>
+	 *
+	 * @param session The bean session.
+	 * @return Zero if swap doesn't match the session, or a positive number if it does.
+	 */
 	public int match(BeanSession session) {
 		if (forMediaTypes == null)
 			return 1;
@@ -193,7 +442,7 @@ public abstract class PojoSwap<T,S> {
 		MediaType mt = session.getMediaType();
 		if (forMediaTypes != null)
 			for (MediaType mt2 : forMediaTypes)
-				i = Math.max(i, mt2.match(mt, false)*2);
+				i = Math.max(i, mt2.match(mt, false));
 		return i;
 	}
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f5f5edfb/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/Surrogate.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/Surrogate.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/Surrogate.java
new file mode 100644
index 0000000..4db447e
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/Surrogate.java
@@ -0,0 +1,134 @@
+// ***************************************************************************************************************************
+// * 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.transform;
+
+import org.apache.juneau.*;
+
+/**
+ * Identifies a class as being a surrogate class.
+ *
+ * <p>
+ * Surrogate classes are used in place of other classes during serialization.
+ * For example, you may want to use a surrogate class to change the names or order of bean properties on a bean.
+ *
+ * <p>
+ * This interface has no methods to implement.
+ * It's simply used by the framework to identify the class as a surrogate class when specified as a swap.
+ *
+ * <p>
+ * The following is an example of a surrogate class change changes a property name:
+ * <p class='bcode'>
+ * 	<jk>public class</jk> MySurrogate <jk>implements</jk> Surrogate {
+ * 		<jk>public</jk> String surrogateField;  <jc>// New bean property</jc>
+ *
+ * 		<jk>public</jk> MySurrogate(NormalClass normalClass) {
+ * 			<jk>this</jk>.surrogateField = normalClass.normalField;
+ * 		}
+ * 	}
+ * </p>
+ *
+ * <p>
+ * Optionally, a public static method can be used to un-transform a class during parsing:
+ * <p class='bcode'>
+ * 	<jk>public class</jk> MySurrogate <jk>implements</jk> Surrogate {
+ * 		...
+ * 		<jk>public static</jk> NormalClass <jsm>toNormalClass</jsm>(SurrogateClass surrogateClass) {
+ * 			<jk>return new</jk> NormalClass(surrogateClass.transformedField);
+ * 		}
+ * 	}
+ * </p>
+ *
+ * <p>
+ * Surrogate classes must conform to the following:
+ * <ul class='spaced-list'>
+ * 	<li>
+ * 		It must have a one or more public constructors that take in a single parameter whose type is the normal types.
+ * 		(It is possible to define a class as a surrogate for multiple class types by using multiple constructors with
+ * 		different parameter types).
+ * 	<li>
+ * 		It optionally can have a public static method that takes in a single parameter whose type is the transformed
+ * 		type and returns an instance of the normal type.
+ * 		This is called the un-transform method.
+ * 		The method can be called anything.
+ * 	<li>
+ * 		If an un-transform method is present, the class must also contain a no-arg constructor (so that the
+ * 		transformed class can be instantiated by the parser before being converted into the normal class by the
+ * 		un-transform method).
+ * </ul>
+ *
+ * <p>
+ * Surrogate classes are associated with serializers and parsers using the {@link CoreObjectBuilder#pojoSwaps(Class...)}
+ * method.
+ * <p class='bcode'>
+ * 	<ja>@Test</ja>
+ * 	<jk>public void</jk> test() <jk>throws</jk> Exception {
+ * 		JsonSerializer s = <jk>new</jk> JsonSerializerBuilder().simple().pojoSwaps(MySurrogate.<jk>class</jk>).build();
+ * 		JsonParser p = <jk>new</jk> JsonParserBuilder().pojoSwaps(MySurrogate.<jk>class</jk>).build();
+ * 		String r;
+ * 		Normal n = Normal.<jsm>create</jsm>();
+ *
+ * 		r = s.serialize(n);
+ * 		assertEquals(<js>"{f2:'f1'}"</js>, r);
+ *
+ * 		n = p.parse(r, Normal.<jk>class</jk>);
+ * 		assertEquals(<js>"f1"</js>, n.f1);
+ * 	}
+ *
+ * 	<jc>// The normal class</jc>
+ * 	<jk>public class</jk> Normal {
+ * 		<jk>public</jk> String f1;
+ *
+ * 		<jk>public static</jk> Normal <jsm>create</jsm>() {
+ * 			Normal n = <jk>new</jk> Normal();
+ * 			n.f1 = <js>"f1"</js>;
+ * 			<jk>return</jk> n;
+ * 		}
+ * 	}
+ *
+ * 	<jc>// The surrogate class</jc>
+ * 	<jk>public class</jk> MySurrogate <jk>implements</jk> Surrogate {
+ * 		<jk>public</jk> String f2;
+ *
+ * 		<jc>// Surrogate constructor</jc>
+ * 		<jk>public</jk> MySurrogate(Normal n) {
+ * 			f2 = n.f1;
+ * 		}
+ *
+ * 		<jc>// Constructor used during parsing (only needed if un-transform method specified)</jc>
+ * 		<jk>public</jk> MySurrogate() {}
+ *
+ * 		<jc>// Un-transform method (optional)</jc>
+ * 		<jk>public static</jk> Normal <jsm>toNormal</jsm>(Surrogate f) {
+ * 			Normal n = <jk>new</jk> Normal();
+ * 			n.f1 = f.f2;
+ * 			<jk>return</jk> n;
+ * 		}
+ * 	}
+ * </p>
+ *
+ * <p>
+ * It should be noted that a surrogate class is functionally equivalent to the following {@link PojoSwap}
+ * implementation:
+ * <p class='bcode'>
+ * 	<jk>public static class</jk> MySurrogate <jk>extends</jk> PojoSwap&lt;Normal,MySurrogate&gt; {
+ * 		<jk>public</jk> MySurrogate swap(Normal n) <jk>throws</jk> SerializeException {
+ * 			<jk>return new</jk> MySurrogate(n);
+ * 		}
+ * 		<jk>public</jk> Normal unswap(MySurrogate s, ClassMeta&lt;?&gt; hint) <jk>throws</jk> ParseException {
+ * 			<jk>return</jk> MySurrogate.<jsm>toNormal</jsm>(s);
+ * 		}
+ * 	}
+ * </p>
+ *
+ */
+public interface Surrogate {}