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:58 UTC
[2/2] incubator-juneau git commit: Templated and per-media-type swaps.
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<Object,Reader> {
+ *
+ * <jk>public</jk> MediaType[] forMediaTypes() {
+ * <jk>return</jk> MediaType.<jsm>forStrings</jsm>(<js>"*/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>"*/json"</js>)
+ * <jk>public class</jk> MyBean { ... }
+ *
+ * <jk>public class</jk> ToStringSwap <jk>extends</jk> PojoSwap<Object,String> {
+ * <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<?> 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<MyPojo,String> { ... }
+ *
+ * <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<MyPojo,String> {
+ *
+ * <jk>public</jk> MediaType[] forMediaTypes() {
+ * <jk>return</jk> MediaType.<jsm>forStrings</jsm>(<js>"*/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<MyPojo,String> {
+ *
+ * <jk>public</jk> MediaType[] forMediaTypes() {
+ * <jk>return</jk> MediaType.<jsm>forStrings</jsm>(<js>"*/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<MyPojo,String> {
+ *
+ * <jk>public</jk> MediaType[] forMediaTypes() {
+ * <jk>return</jk> MediaType.<jsm>forStrings</jsm>(<js>"*/*"</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>"<string>It's XML!</string>"</js>, xml);
+ *
+ * String html = g.getWriterSerializer(<js>"text/html"</js>).serialize(myPojo);
+ * <jsm>assertEquals</jsm>(<js>"<string>It's something else!</string>"</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<MyPojo,Reader> {
+ *
+ * <jk>public</jk> MediaType[] forMediaTypes() {
+ * <jk>return</jk> MediaType.<jsm>forStrings</jsm>(<js>"*/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<Object,Reader> {
+ *
+ * <jk>public</jk> MediaType[] forMediaTypes() {
+ * <jk>return</jk> MediaType.<jsm>forStrings</jsm>(<js>"*/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>"*/json"</js> = <code>5,100</code>
+ * <li><js>"*/*"</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<Normal,MySurrogate> {
+ * <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<?> hint) <jk>throws</jk> ParseException {
+ * <jk>return</jk> MySurrogate.<jsm>toNormal</jsm>(s);
+ * }
+ * }
+ * </p>
+ *
+ */
+public interface Surrogate {}