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 2018/01/29 02:14:04 UTC
[juneau] branch master updated: Support for POJO builders.
This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new a65aedf Support for POJO builders.
a65aedf is described below
commit a65aedfdace05de4aae83e8c963bdb798a52ee72
Author: JamesBognar <ja...@apache.org>
AuthorDate: Sun Jan 28 21:13:22 2018 -0500
Support for POJO builders.
---
.../a/rttests/RoundTripBeanInheritanceTest.java | 11 +-
.../apache/juneau/transforms/BuilderComboTest.java | 446 +++++++++++++++++++++
.../org/apache/juneau/jena/RdfParserSession.java | 15 +-
.../src/main/java/org/apache/juneau/BeanMap.java | 30 +-
.../src/main/java/org/apache/juneau/BeanMeta.java | 34 +-
.../java/org/apache/juneau/BeanPropertyMeta.java | 39 +-
.../src/main/java/org/apache/juneau/ClassMeta.java | 25 +-
.../java/org/apache/juneau/annotation/Builder.java | 71 ++++
.../apache/juneau/csv/CsvSerializerSession.java | 16 +-
.../org/apache/juneau/html/HtmlParserSession.java | 18 +-
.../apache/juneau/html/HtmlSerializerSession.java | 48 +--
.../org/apache/juneau/internal/ClassUtils.java | 41 +-
.../org/apache/juneau/json/JsonParserSession.java | 12 +-
.../juneau/json/JsonSchemaSerializerSession.java | 3 +-
.../apache/juneau/json/JsonSerializerSession.java | 28 +-
.../juneau/msgpack/MsgPackParserSession.java | 15 +-
.../juneau/msgpack/MsgPackSerializerSession.java | 20 +-
.../main/java/org/apache/juneau/parser/Parser.java | 8 +-
.../java/org/apache/juneau/transform/Builder.java | 30 ++
.../org/apache/juneau/transform/BuilderSwap.java | 231 +++++++++++
.../org/apache/juneau/uon/UonParserSession.java | 14 +-
.../apache/juneau/uon/UonSerializerSession.java | 28 +-
.../urlencoding/UrlEncodingParserSession.java | 13 +-
.../urlencoding/UrlEncodingSerializerSession.java | 58 +--
.../org/apache/juneau/xml/XmlParserSession.java | 20 +-
.../juneau/xml/XmlSchemaSerializerSession.java | 118 +++---
.../apache/juneau/xml/XmlSerializerSession.java | 138 ++++---
.../juneau/yaml/proto/YamlParserSession.java | 12 +-
.../juneau/yaml/proto/YamlSerializerSession.java | 28 +-
juneau-doc/src/main/javadoc/overview.html | 140 ++++++-
30 files changed, 1398 insertions(+), 312 deletions(-)
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanInheritanceTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanInheritanceTest.java
index dfe8dd6..925fd90 100755
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanInheritanceTest.java
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanInheritanceTest.java
@@ -13,9 +13,7 @@
package org.apache.juneau.a.rttests;
import static org.apache.juneau.TestUtils.*;
-import static org.junit.Assert.*;
-import org.apache.juneau.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.serializer.*;
import org.junit.*;
@@ -48,14 +46,7 @@ public class RoundTripBeanInheritanceTest extends RoundTripTest {
A3 t3 = new A3();
t3.init();
- try {
- ClassMeta<?> cm = BeanContext.DEFAULT.getClassMeta(A3.class);
- assertEquals("No properties detected on bean class", cm.getNotABeanReason());
- roundTrip(t3, A3.class);
- fail("Exception expected");
- } catch (ParseException e) {
- } catch (SerializeException e) {
- } catch (InvalidDataConversionException e) {}
+ roundTrip(t3, A3.class);
}
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/transforms/BuilderComboTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/transforms/BuilderComboTest.java
new file mode 100644
index 0000000..e0a14cf
--- /dev/null
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/transforms/BuilderComboTest.java
@@ -0,0 +1,446 @@
+// ***************************************************************************************************************************
+// * 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.apache.juneau.TestUtils.*;
+import static org.junit.Assert.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.serializer.*;
+import org.junit.runner.*;
+import org.junit.runners.*;
+
+/**
+ * Exhaustive serialization tests for the CalendarSwap class.
+ */
+@RunWith(Parameterized.class)
+@SuppressWarnings({"javadoc"})
+public class BuilderComboTest extends ComboRoundTripTest {
+
+ @Parameterized.Parameters
+ public static Collection<Object[]> getParameters() {
+ return Arrays.asList(new Object[][] {
+ { /* 0 */
+ new ComboInput<A>(
+ "A",
+ A.class,
+ new A(null).init(),
+ /* Json */ "{a:1}",
+ /* JsonT */ "{a:1}",
+ /* JsonR */ "{\n\ta: 1\n}",
+ /* Xml */ "<object><a>1</a></object>",
+ /* XmlT */ "<object><a>1</a></object>",
+ /* XmlR */ "<object>\n\t<a>1</a>\n</object>\n",
+ /* XmlNs */ "<object><a>1</a></object>",
+ /* Html */ "<table><tr><td>a</td><td>1</td></tr></table>",
+ /* HtmlT */ "<table><tr><td>a</td><td>1</td></tr></table>",
+ /* HtmlR */ "<table>\n\t<tr>\n\t\t<td>a</td>\n\t\t<td>1</td>\n\t</tr>\n</table>\n",
+ /* Uon */ "(a=1)",
+ /* UonT */ "(a=1)",
+ /* UonR */ "(\n\ta=1\n)",
+ /* UrlEnc */ "a=1",
+ /* UrlEncT */ "a=1",
+ /* UrlEncR */ "a=1",
+ /* MsgPack */ "81A16101",
+ /* MsgPackT */ "81A16101",
+ /* RdfXml */ "<rdf:RDF>\n<rdf:Description>\n<jp:a>1</jp:a>\n</rdf:Description>\n</rdf:RDF>\n",
+ /* RdfXmlT */ "<rdf:RDF>\n<rdf:Description>\n<jp:a>1</jp:a>\n</rdf:Description>\n</rdf:RDF>\n",
+ /* RdfXmlR */ "<rdf:RDF>\n <rdf:Description>\n <jp:a>1</jp:a>\n </rdf:Description>\n</rdf:RDF>\n"
+ )
+ {
+ @Override
+ public void verify(A o) {
+ assertType(A.class, o);
+ assertTrue(o.createdByBuilder);
+ }
+ }
+ },
+ { /* 1 */
+ new ComboInput<B>(
+ "B",
+ B.class,
+ new B(null).init(),
+ /* Json */ "{a:1}",
+ /* JsonT */ "{a:1}",
+ /* JsonR */ "{\n\ta: 1\n}",
+ /* Xml */ "<object><a>1</a></object>",
+ /* XmlT */ "<object><a>1</a></object>",
+ /* XmlR */ "<object>\n\t<a>1</a>\n</object>\n",
+ /* XmlNs */ "<object><a>1</a></object>",
+ /* Html */ "<table><tr><td>a</td><td>1</td></tr></table>",
+ /* HtmlT */ "<table><tr><td>a</td><td>1</td></tr></table>",
+ /* HtmlR */ "<table>\n\t<tr>\n\t\t<td>a</td>\n\t\t<td>1</td>\n\t</tr>\n</table>\n",
+ /* Uon */ "(a=1)",
+ /* UonT */ "(a=1)",
+ /* UonR */ "(\n\ta=1\n)",
+ /* UrlEnc */ "a=1",
+ /* UrlEncT */ "a=1",
+ /* UrlEncR */ "a=1",
+ /* MsgPack */ "81A16101",
+ /* MsgPackT */ "81A16101",
+ /* RdfXml */ "<rdf:RDF>\n<rdf:Description>\n<jp:a>1</jp:a>\n</rdf:Description>\n</rdf:RDF>\n",
+ /* RdfXmlT */ "<rdf:RDF>\n<rdf:Description>\n<jp:a>1</jp:a>\n</rdf:Description>\n</rdf:RDF>\n",
+ /* RdfXmlR */ "<rdf:RDF>\n <rdf:Description>\n <jp:a>1</jp:a>\n </rdf:Description>\n</rdf:RDF>\n"
+ )
+ {
+ @Override
+ public void verify(B o) {
+ assertType(B.class, o);
+ assertTrue(o.createdByBuilder);
+ }
+ }
+ },
+ { /* 2 */
+ new ComboInput<C>(
+ "C",
+ C.class,
+ new C(null).init(),
+ /* Json */ "{a:1}",
+ /* JsonT */ "{a:1}",
+ /* JsonR */ "{\n\ta: 1\n}",
+ /* Xml */ "<object><a>1</a></object>",
+ /* XmlT */ "<object><a>1</a></object>",
+ /* XmlR */ "<object>\n\t<a>1</a>\n</object>\n",
+ /* XmlNs */ "<object><a>1</a></object>",
+ /* Html */ "<table><tr><td>a</td><td>1</td></tr></table>",
+ /* HtmlT */ "<table><tr><td>a</td><td>1</td></tr></table>",
+ /* HtmlR */ "<table>\n\t<tr>\n\t\t<td>a</td>\n\t\t<td>1</td>\n\t</tr>\n</table>\n",
+ /* Uon */ "(a=1)",
+ /* UonT */ "(a=1)",
+ /* UonR */ "(\n\ta=1\n)",
+ /* UrlEnc */ "a=1",
+ /* UrlEncT */ "a=1",
+ /* UrlEncR */ "a=1",
+ /* MsgPack */ "81A16101",
+ /* MsgPackT */ "81A16101",
+ /* RdfXml */ "<rdf:RDF>\n<rdf:Description>\n<jp:a>1</jp:a>\n</rdf:Description>\n</rdf:RDF>\n",
+ /* RdfXmlT */ "<rdf:RDF>\n<rdf:Description>\n<jp:a>1</jp:a>\n</rdf:Description>\n</rdf:RDF>\n",
+ /* RdfXmlR */ "<rdf:RDF>\n <rdf:Description>\n <jp:a>1</jp:a>\n </rdf:Description>\n</rdf:RDF>\n"
+ )
+ {
+ @Override
+ public void verify(C o) {
+ assertType(C.class, o);
+ assertTrue(o.createdByBuilder);
+ }
+ }
+ },
+ { /* 3 */
+ new ComboInput<D>(
+ "D",
+ D.class,
+ new D(null).init(),
+ /* Json */ "{a:1}",
+ /* JsonT */ "{a:1}",
+ /* JsonR */ "{\n\ta: 1\n}",
+ /* Xml */ "<object><a>1</a></object>",
+ /* XmlT */ "<object><a>1</a></object>",
+ /* XmlR */ "<object>\n\t<a>1</a>\n</object>\n",
+ /* XmlNs */ "<object><a>1</a></object>",
+ /* Html */ "<table><tr><td>a</td><td>1</td></tr></table>",
+ /* HtmlT */ "<table><tr><td>a</td><td>1</td></tr></table>",
+ /* HtmlR */ "<table>\n\t<tr>\n\t\t<td>a</td>\n\t\t<td>1</td>\n\t</tr>\n</table>\n",
+ /* Uon */ "(a=1)",
+ /* UonT */ "(a=1)",
+ /* UonR */ "(\n\ta=1\n)",
+ /* UrlEnc */ "a=1",
+ /* UrlEncT */ "a=1",
+ /* UrlEncR */ "a=1",
+ /* MsgPack */ "81A16101",
+ /* MsgPackT */ "81A16101",
+ /* RdfXml */ "<rdf:RDF>\n<rdf:Description>\n<jp:a>1</jp:a>\n</rdf:Description>\n</rdf:RDF>\n",
+ /* RdfXmlT */ "<rdf:RDF>\n<rdf:Description>\n<jp:a>1</jp:a>\n</rdf:Description>\n</rdf:RDF>\n",
+ /* RdfXmlR */ "<rdf:RDF>\n <rdf:Description>\n <jp:a>1</jp:a>\n </rdf:Description>\n</rdf:RDF>\n"
+ )
+ {
+ @Override
+ public void verify(D o) {
+ assertType(D.class, o);
+ assertTrue(o.createdByBuilder);
+ }
+ }
+ },
+ { /* 4 */
+ new ComboInput<E>(
+ "E",
+ E.class,
+ new E(null).init(),
+ /* Json */ "{a:1}",
+ /* JsonT */ "{a:1}",
+ /* JsonR */ "{\n\ta: 1\n}",
+ /* Xml */ "<object><a>1</a></object>",
+ /* XmlT */ "<object><a>1</a></object>",
+ /* XmlR */ "<object>\n\t<a>1</a>\n</object>\n",
+ /* XmlNs */ "<object><a>1</a></object>",
+ /* Html */ "<table><tr><td>a</td><td>1</td></tr></table>",
+ /* HtmlT */ "<table><tr><td>a</td><td>1</td></tr></table>",
+ /* HtmlR */ "<table>\n\t<tr>\n\t\t<td>a</td>\n\t\t<td>1</td>\n\t</tr>\n</table>\n",
+ /* Uon */ "(a=1)",
+ /* UonT */ "(a=1)",
+ /* UonR */ "(\n\ta=1\n)",
+ /* UrlEnc */ "a=1",
+ /* UrlEncT */ "a=1",
+ /* UrlEncR */ "a=1",
+ /* MsgPack */ "81A16101",
+ /* MsgPackT */ "81A16101",
+ /* RdfXml */ "<rdf:RDF>\n<rdf:Description>\n<jp:a>1</jp:a>\n</rdf:Description>\n</rdf:RDF>\n",
+ /* RdfXmlT */ "<rdf:RDF>\n<rdf:Description>\n<jp:a>1</jp:a>\n</rdf:Description>\n</rdf:RDF>\n",
+ /* RdfXmlR */ "<rdf:RDF>\n <rdf:Description>\n <jp:a>1</jp:a>\n </rdf:Description>\n</rdf:RDF>\n"
+ )
+ {
+ @Override
+ public void verify(E o) {
+ assertType(E.class, o);
+ assertTrue(o.createdByBuilder);
+ }
+ }
+ },
+ { /* 5 */
+ new ComboInput<H>(
+ "H",
+ H.class,
+ new H(null).init(),
+ /* Json */ "{fooBar:1}",
+ /* JsonT */ "{fooBar:1}",
+ /* JsonR */ "{\n\tfooBar: 1\n}",
+ /* Xml */ "<object><fooBar>1</fooBar></object>",
+ /* XmlT */ "<object><fooBar>1</fooBar></object>",
+ /* XmlR */ "<object>\n\t<fooBar>1</fooBar>\n</object>\n",
+ /* XmlNs */ "<object><fooBar>1</fooBar></object>",
+ /* Html */ "<table><tr><td>fooBar</td><td>1</td></tr></table>",
+ /* HtmlT */ "<table><tr><td>fooBar</td><td>1</td></tr></table>",
+ /* HtmlR */ "<table>\n\t<tr>\n\t\t<td>fooBar</td>\n\t\t<td>1</td>\n\t</tr>\n</table>\n",
+ /* Uon */ "(fooBar=1)",
+ /* UonT */ "(fooBar=1)",
+ /* UonR */ "(\n\tfooBar=1\n)",
+ /* UrlEnc */ "fooBar=1",
+ /* UrlEncT */ "fooBar=1",
+ /* UrlEncR */ "fooBar=1",
+ /* MsgPack */ "81A6666F6F42617201",
+ /* MsgPackT */ "81A6666F6F42617201",
+ /* RdfXml */ "<rdf:RDF>\n<rdf:Description>\n<jp:fooBar>1</jp:fooBar>\n</rdf:Description>\n</rdf:RDF>\n",
+ /* RdfXmlT */ "<rdf:RDF>\n<rdf:Description>\n<jp:fooBar>1</jp:fooBar>\n</rdf:Description>\n</rdf:RDF>\n",
+ /* RdfXmlR */ "<rdf:RDF>\n <rdf:Description>\n <jp:fooBar>1</jp:fooBar>\n </rdf:Description>\n</rdf:RDF>\n"
+ )
+ {
+ @Override
+ public void verify(H o) {
+ assertType(H.class, o);
+ assertTrue(o.createdByBuilder);
+ }
+ }
+ },
+ });
+ }
+
+ public BuilderComboTest(ComboInput<?> comboInput) {
+ super(comboInput);
+ }
+
+ @Override
+ protected Serializer applySettings(Serializer s) throws Exception {
+ return s.builder().trimNullProperties(false).build();
+ }
+
+ @Override
+ protected Parser applySettings(Parser p) throws Exception {
+ return (Parser) p.builder().build();
+ }
+
+ //--------------------------------------------------------------------------------
+ // Typical builder scenario
+ //--------------------------------------------------------------------------------
+
+ public static class A {
+ public int a;
+ boolean createdByBuilder;
+
+ private A(ABuilder x) {
+ if (x != null)
+ this.a = x.a;
+ }
+
+ public A init() {
+ a = 1;
+ return this;
+ }
+
+ public static ABuilder create() {
+ return new ABuilder();
+ }
+ }
+
+ public static class ABuilder {
+ public int a;
+
+ public A build() {
+ A x = new A(this);
+ x.createdByBuilder = true;
+ return x;
+ }
+ }
+
+ //--------------------------------------------------------------------------------
+ // Builder detected through POJO constructor.
+ //--------------------------------------------------------------------------------
+ public static class B {
+ public int a;
+ boolean createdByBuilder;
+
+ public B(BBuilder x) {
+ if (x != null) {
+ this.a = x.a;
+ createdByBuilder = true;
+ }
+ }
+
+ public B init() {
+ a = 1;
+ return this;
+ }
+ }
+
+ public static class BBuilder implements org.apache.juneau.transform.Builder<B> {
+ public int a;
+ }
+
+ //--------------------------------------------------------------------------------
+ // Same as B, but should Builder.build() method.
+ //--------------------------------------------------------------------------------
+
+ public static class C {
+ public int a;
+ boolean createdByBuilder;
+
+ public C(CBuilder x) {
+ if (x != null) {
+ this.a = x.a;
+ }
+ }
+
+ public C init() {
+ a = 1;
+ return this;
+ }
+ }
+
+ public static class CBuilder implements org.apache.juneau.transform.Builder<B> {
+ public int a;
+
+ public C build() {
+ C x = new C(this);
+ x.createdByBuilder = true;
+ return x;
+ }
+ }
+
+ //--------------------------------------------------------------------------------
+ // @Builder annotation on POJO class.
+ //--------------------------------------------------------------------------------
+
+ @org.apache.juneau.annotation.Builder(DBuilder.class)
+ public static class D {
+ public int a;
+ boolean createdByBuilder;
+
+ public D(DBuilder x) {
+ if (x != null) {
+ this.a = x.a;
+ createdByBuilder = true;
+ }
+ }
+
+ public D init() {
+ a = 1;
+ return this;
+ }
+ }
+
+ public static class DBuilder {
+ public int a;
+ }
+
+ //--------------------------------------------------------------------------------
+ // @Builder annotation on POJO class, but uses build() method on builder.
+ //--------------------------------------------------------------------------------
+
+ @org.apache.juneau.annotation.Builder(EBuilder.class)
+ public static class E {
+ public int a;
+ boolean createdByBuilder;
+
+ public E(EBuilder x) {
+ if (x != null) {
+ this.a = x.a;
+ }
+ }
+
+ public E init() {
+ a = 1;
+ return this;
+ }
+ }
+
+ public static class EBuilder {
+ public int a;
+
+ public E build() {
+ E x = new E(this);
+ x.createdByBuilder = true;
+ return x;
+ }
+ }
+
+ //--------------------------------------------------------------------------------
+ // Builder with typical method setters.
+ //--------------------------------------------------------------------------------
+
+ public static class H {
+ public int fooBar;
+ boolean createdByBuilder;
+
+ private H(HBuilder x) {
+ if (x != null)
+ this.fooBar = x.fooBar;
+ }
+
+ public H init() {
+ fooBar = 1;
+ return this;
+ }
+
+ public static HBuilder create() {
+ return new HBuilder();
+ }
+ }
+
+ public static class HBuilder {
+ private int fooBar;
+
+ public H build() {
+ H x = new H(this);
+ x.createdByBuilder = true;
+ return x;
+ }
+
+ @BeanProperty
+ public HBuilder fooBar(int fooBar) {
+ this.fooBar = fooBar;
+ return this;
+ }
+ }
+}
diff --git a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfParserSession.java b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfParserSession.java
index bf43e7b..c779c57 100644
--- a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfParserSession.java
+++ b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfParserSession.java
@@ -231,7 +231,14 @@ public class RdfParserSession extends ReaderParserSession {
if (eType == null)
eType = object();
PojoSwap<T,Object> swap = (PojoSwap<T,Object>)eType.getPojoSwap(this);
- ClassMeta<?> sType = swap == null ? eType : swap.getSwapClassMeta(this);
+ BuilderSwap<T,Object> builder = (BuilderSwap<T,Object>)eType.getBuilderSwap(this);
+ ClassMeta<?> sType = null;
+ if (builder != null)
+ sType = builder.getBuilderClassMeta(this);
+ else if (swap != null)
+ sType = swap.getSwapClassMeta(this);
+ else
+ sType = eType;
setCurrentClass(sType);
if (! sType.canCreateNewInstance(outer)) {
@@ -319,6 +326,12 @@ public class RdfParserSession extends ReaderParserSession {
}
if (sType.isArray() || sType.isArgs())
o = toArray(sType, (Collection)o);
+ } else if (builder != null) {
+ Resource r = n.asResource();
+ if (! urisVisited.add(r))
+ return null;
+ BeanMap<?> bm = toBeanMap(builder.create(this, eType));
+ o = builder.build(this, parseIntoBeanMap(r, bm).getBean(), eType);
} else if (sType.canCreateNewBean(outer)) {
Resource r = n.asResource();
if (! urisVisited.add(r))
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 611f6e8..7e74345 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
@@ -447,23 +447,25 @@ public class BeanMap<T> extends AbstractMap<String,Object> implements Delegate<T
if (v != null)
l.add(v);
for (BeanPropertyMeta bpm : properties) {
- try {
- if (bpm.isDyna()) {
- for (String pName : bpm.getDynaMap(bean).keySet()) {
- Object val = bpm.get(this, pName);
+ if (bpm.canRead()) {
+ try {
+ if (bpm.isDyna()) {
+ for (String pName : bpm.getDynaMap(bean).keySet()) {
+ Object val = bpm.get(this, pName);
+ if (val != null || ! ignoreNulls)
+ l.add(new BeanPropertyValue(bpm, pName, val, null));
+ }
+ } else {
+ Object val = bpm.get(this, null);
if (val != null || ! ignoreNulls)
- l.add(new BeanPropertyValue(bpm, pName, val, null));
+ l.add(new BeanPropertyValue(bpm, bpm.getName(), val, null));
}
- } else {
- Object val = bpm.get(this, null);
- if (val != null || ! ignoreNulls)
- l.add(new BeanPropertyValue(bpm, bpm.getName(), val, null));
+ } catch (Error e) {
+ // Errors should always be uncaught.
+ throw e;
+ } catch (Throwable t) {
+ l.add(new BeanPropertyValue(bpm, bpm.getName(), null, t));
}
- } catch (Error e) {
- // Errors should always be uncaught.
- throw e;
- } catch (Throwable t) {
- l.add(new BeanPropertyValue(bpm, bpm.getName(), null, t));
}
}
if (meta.sortProperties && meta.dynaProperty != null)
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
index c9d86b9..4b8bdd8 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
@@ -129,7 +129,7 @@ public class BeanMeta<T> {
this.extMeta = b.extMeta;
this.beanRegistry = b.beanRegistry;
this.typePropertyName = b.typePropertyName;
- this.typeProperty = BeanPropertyMeta.builder(this, typePropertyName).rawMetaType(ctx.string()).beanRegistry(beanRegistry).build();
+ this.typeProperty = BeanPropertyMeta.builder(this, typePropertyName).canRead().canWrite().rawMetaType(ctx.string()).beanRegistry(beanRegistry).build();
this.sortProperties = b.sortProperties;
}
@@ -443,7 +443,7 @@ public class BeanMeta<T> {
private String findPropertyName(Field f, Set<String> fixedBeanProps) {
BeanProperty bp = f.getAnnotation(BeanProperty.class);
String name = bpName(bp);
- if (! name.isEmpty()) {
+ if (name != null && ! name.isEmpty()) {
if (fixedBeanProps.isEmpty() || fixedBeanProps.contains(name))
return name;
return null; // Could happen if filtered via BEAN_includeProperties/BEAN_excludeProperties.
@@ -522,9 +522,9 @@ public class BeanMeta<T> {
else if (b.field != null)
pt = b.field.getType();
- // Doesn't match if no getter/field defined.
+ // Matches if only a setter is defined.
if (pt == null)
- return false;
+ return true;
// Doesn't match if not same type or super type as getter/field.
if (! isParentClass(type, pt))
@@ -585,17 +585,31 @@ public class BeanMeta<T> {
} else if (n.startsWith("is") && (rt.equals(Boolean.TYPE) || rt.equals(Boolean.class))) {
isGetter = true;
n = n.substring(2);
- } else if (! bpName.isEmpty()) {
+ } else if (bpName != null) {
isGetter = true;
- n = bpName;
+ if (bpName.isEmpty()) {
+ if (n.startsWith("get"))
+ n = n.substring(3);
+ else if (n.startsWith("is"))
+ n = n.substring(2);
+ bpName = n;
+ } else {
+ n = bpName;
+ }
}
} else if (pt.length == 1) {
if (n.startsWith("set") && (isParentClass(rt, c) || rt.equals(Void.TYPE))) {
isSetter = true;
n = n.substring(3);
- } else if (! bpName.isEmpty()) {
+ } else if (bpName != null) {
isSetter = true;
- n = bpName;
+ if (bpName.isEmpty()) {
+ if (n.startsWith("set"))
+ n = n.substring(3);
+ bpName = n;
+ } else {
+ n = bpName;
+ }
}
} else if (pt.length == 2) {
if ("*".equals(bpName)) {
@@ -605,7 +619,7 @@ public class BeanMeta<T> {
}
n = pn.getPropertyName(n);
if (isGetter || isSetter) {
- if (! bpName.isEmpty()) {
+ if (bpName != null && ! bpName.isEmpty()) {
n = bpName;
if (! fixedBeanProps.isEmpty())
if (! fixedBeanProps.contains(n))
@@ -789,7 +803,7 @@ public class BeanMeta<T> {
static final String bpName(BeanProperty bp) {
if (bp == null)
- return "";
+ return null;
if (! bp.name().isEmpty())
return bp.name();
return bp.value();
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 56f360d..1f9c64a 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
@@ -68,7 +68,8 @@ public final class BeanPropertyMeta {
private final Object overrideValue; // The bean property value (if it's an overridden delegate).
private final BeanPropertyMeta delegateFor; // The bean property that this meta is a delegate for.
-
+ private final boolean canRead, canWrite;
+
/**
* Creates a builder for {@link #BeanPropertyMeta} objects.
*
@@ -97,6 +98,7 @@ public final class BeanPropertyMeta {
Object overrideValue;
BeanPropertyMeta delegateFor;
MetadataMap extMeta = new MetadataMap();
+ boolean canRead, canWrite;
Builder(BeanMeta<?> beanMeta, String name) {
this.beanMeta = beanMeta;
@@ -148,16 +150,29 @@ public final class BeanPropertyMeta {
this.delegateFor = delegateFor;
return this;
}
+
+ Builder canRead() {
+ this.canRead = true;
+ return this;
+ }
+
+ Builder canWrite() {
+ this.canWrite = true;
+ return this;
+ }
boolean validate(BeanContext f, BeanRegistry parentBeanRegistry, Map<Class<?>,Class<?>[]> typeVarImpls) throws Exception {
List<Class<?>> bdClasses = new ArrayList<>();
- if (field == null && getter == null)
+ if (field == null && getter == null && setter == null)
return false;
if (field == null && setter == null && f.beansRequireSettersForGetters && ! isConstructorArg)
return false;
+
+ canRead |= (field != null || getter != null);
+ canWrite |= (field != null || setter != null);
if (field != null) {
BeanProperty p = field.getAnnotation(BeanProperty.class);
@@ -340,6 +355,8 @@ public final class BeanPropertyMeta {
this.delegateFor = b.delegateFor;
this.extMeta = b.extMeta;
this.isDyna = b.isDyna;
+ this.canRead = b.canRead;
+ this.canWrite = b.canWrite;
}
/**
@@ -1095,4 +1112,22 @@ public final class BeanPropertyMeta {
public String toString() {
return name + ": " + this.rawTypeMeta.getInnerClass().getName() + ", field=["+field+"], getter=["+getter+"], setter=["+setter+"]";
}
+
+ /**
+ * Returns <jk>true</jk> if this property can be read.
+ *
+ * @return <jk>true</jk> if this property can be read.
+ */
+ public boolean canRead() {
+ return canRead;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this property can be written.
+ *
+ * @return <jk>true</jk> if this property can be written.
+ */
+ public boolean canWrite() {
+ return canWrite;
+ }
}
\ No newline at end of file
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 bf53232..f5576bb 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
@@ -97,6 +97,7 @@ public final class ClassMeta<T> implements Type {
childUnswapMap; // Maps swap subclasses to PojoSwaps.
private final PojoSwap<T,?>[] pojoSwaps; // The object POJO swaps associated with this bean (if it has any).
private final BeanFilter beanFilter; // The bean filter associated with this bean (if it has one).
+ private final BuilderSwap<T,?> builderSwap; // The builder swap associated with this bean (if it has one).
private final MetadataMap extMeta; // Extended metadata
private final BeanContext beanContext; // The bean context that created this object.
private final ClassMeta<?>
@@ -169,6 +170,7 @@ public final class ClassMeta<T> implements Type {
this.remoteableMethods = builder.remoteableMethods;
this.beanFilter = beanFilter;
this.pojoSwaps = builder.pojoSwaps.isEmpty() ? null : builder.pojoSwaps.toArray(new PojoSwap[builder.pojoSwaps.size()]);
+ this.builderSwap = builder.builderSwap;
this.extMeta = new MetadataMap();
this.keyType = builder.keyType;
this.valueType = builder.valueType;
@@ -240,6 +242,7 @@ public final class ClassMeta<T> implements Type {
this.dictionaryName = mainType.dictionaryName;
this.notABeanReason = mainType.notABeanReason;
this.pojoSwaps = mainType.pojoSwaps;
+ this.builderSwap = mainType.builderSwap;
this.beanFilter = mainType.beanFilter;
this.extMeta = mainType.extMeta;
this.initException = mainType.initException;
@@ -286,6 +289,7 @@ public final class ClassMeta<T> implements Type {
this.dictionaryName = null;
this.notABeanReason = null;
this.pojoSwaps = null;
+ this.builderSwap = null;
this.beanFilter = null;
this.extMeta = new MetadataMap();
this.initException = null;
@@ -324,8 +328,7 @@ public final class ClassMeta<T> implements Type {
ClassMeta<?>
keyType = null,
valueType = null,
- elementType = null,
- serializedClassMeta = null;
+ elementType = null;
String
typePropertyName = null,
notABeanReason = null,
@@ -333,6 +336,7 @@ public final class ClassMeta<T> implements Type {
Throwable initException = null;
BeanMeta beanMeta = null;
List<PojoSwap> pojoSwaps = new ArrayList<>();
+ BuilderSwap builderSwap;
InvocationHandler invocationHandler = null;
BeanRegistry beanRegistry = null;
PojoSwap<?,?>[] childPojoSwaps;
@@ -583,6 +587,9 @@ public final class ClassMeta<T> implements Type {
if (pojoSwaps != null)
this.pojoSwaps.addAll(Arrays.asList(pojoSwaps));
+
+ if (beanContext != null)
+ this.builderSwap = BuilderSwap.findSwapFromPojoClass(c, beanContext.beanConstructorVisibility, beanContext.beanMethodVisibility);
findPojoSwaps(this.pojoSwaps);
@@ -645,10 +652,6 @@ public final class ClassMeta<T> implements Type {
if (beanMeta != null)
dictionaryName = beanMeta.getDictionaryName();
- serializedClassMeta = (this.pojoSwaps.isEmpty() ? ClassMeta.this : findClassMeta(this.pojoSwaps.get(0).getSwapClass()));
- if (serializedClassMeta == null)
- serializedClassMeta = ClassMeta.this;
-
if (beanMeta != null && beanContext != null && beanContext.useInterfaceProxies && innerClass.isInterface())
invocationHandler = new BeanProxyInvocationHandler<T>(beanMeta);
@@ -1278,6 +1281,16 @@ public final class ClassMeta<T> implements Type {
}
return null;
}
+
+ /**
+ * Returns the builder swap associated with this class.
+ *
+ * @param session The current bean session.
+ * @return The builder swap associated with this class, or <jk>null</jk> if it doesn't exist.
+ */
+ public BuilderSwap<T,?> getBuilderSwap(BeanSession session) {
+ return builderSwap;
+ }
/**
* Returns the {@link BeanMeta} associated with this class.
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Builder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Builder.java
new file mode 100644
index 0000000..a5c1fea
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Builder.java
@@ -0,0 +1,71 @@
+// ***************************************************************************************************************************
+// * 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.annotation;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.*;
+
+/**
+ * Identifies a class as a builder for a POJO class.
+ *
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ *
+ * <jc>// POJO class.</jc>
+ * <ja>@Builder</ja>(MyBeanBuilder.<jk>class</jk>)
+ * <jk>public class</jk> MyBean {
+ *
+ * <jc>// Read-only properties.</jc>
+ * <jk>public final</jk> String <jf>foo</jf>;
+ * <jk>public final int</jk> <jf>bar</jf>;
+ *
+ * <jc>// Constructor that takes in a builder.</jc>
+ * <jk>public</jk> MyBean(MyBeanBuilder b) {
+ * <jk>this</jk>.<jf>foo</jf> = b.foo;
+ * <jk>this</jk>.<jf>bar</jf> = b.bar;
+ * }
+ * }
+ *
+ * <jc>// Builder class.</jc>
+ * <jk>public class</jk> MyBeanBuilder {
+ * <jk>public</jk> String <jf>foo</jf>;
+ * <jk>public int</jk> <jf>bar</jf>;
+ *
+ * <jc>// Method that creates the bean.</jc>
+ * <jk>public</jk> MyBean build() {
+ * <jk>return new</jk> MyBean(<jk>this</jk>);
+ * }
+ *
+ * <jc>// Bean property setters.</jc>
+ * }
+ * </p>
+ *
+ * <h5 class='topic'>Documentation</h5>
+ * <ul>
+ * <li><a class="doclink" href="../../../../overview-summary.html#juneau-marshall.PojoBuilders">Overview > POJO Builders</a>
+ * </ul>
+ */
+@Documented
+@Target({TYPE})
+@Retention(RUNTIME)
+@Inherited
+public @interface Builder {
+
+ /**
+ * The builder for this class.
+ */
+ Class<?> value() default Null.class;
+}
\ No newline at end of file
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/csv/CsvSerializerSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/csv/CsvSerializerSession.java
index fcac49a..76dcda8 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/csv/CsvSerializerSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/csv/CsvSerializerSession.java
@@ -60,18 +60,22 @@ public final class CsvSerializerSession extends WriterSerializerSession {
BeanMeta<?> bm = entryType.getBeanMeta();
int i = 0;
for (BeanPropertyMeta pm : bm.getPropertyMetas()) {
- if (i++ > 0)
- w.append(',');
- append(w, pm.getName());
+ if (pm.canRead()) {
+ if (i++ > 0)
+ w.append(',');
+ append(w, pm.getName());
+ }
}
w.append('\n');
for (Object o2 : l) {
i = 0;
BeanMap<?> bean = toBeanMap(o2);
for (BeanPropertyMeta pm : bm.getPropertyMetas()) {
- if (i++ > 0)
- w.append(',');
- append(w, pm.get(bean, pm.getName()));
+ if (pm.canRead()) {
+ if (i++ > 0)
+ w.append(',');
+ append(w, pm.get(bean, pm.getName()));
+ }
}
w.append('\n');
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParserSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParserSession.java
index f7d5d84..b1d2779 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParserSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParserSession.java
@@ -83,8 +83,15 @@ public final class HtmlParserSession extends XmlParserSession {
if (eType == null)
eType = (ClassMeta<T>)object();
- PojoSwap<T,Object> transform = (PojoSwap<T,Object>)eType.getPojoSwap(this);
- ClassMeta<?> sType = transform == null ? eType : transform.getSwapClassMeta(this);
+ PojoSwap<T,Object> swap = (PojoSwap<T,Object>)eType.getPojoSwap(this);
+ BuilderSwap<T,Object> builder = (BuilderSwap<T,Object>)eType.getBuilderSwap(this);
+ ClassMeta<?> sType = null;
+ if (builder != null)
+ sType = builder.getBuilderClassMeta(this);
+ else if (swap != null)
+ sType = swap.getSwapClassMeta(this);
+ else
+ sType = eType;
setCurrentClass(sType);
int event = r.getEventType();
@@ -208,6 +215,9 @@ public final class HtmlParserSession extends XmlParserSession {
} else if (sType.isMap()) {
o = parseIntoMap(r, (Map)(sType.canCreateNewInstance(outer) ? sType.newInstance(outer)
: new ObjectMap(this)), sType.getKeyType(), sType.getValueType(), pMeta);
+ } else if (builder != null) {
+ BeanMap m = toBeanMap(builder.create(this, eType));
+ o = builder.build(this, parseIntoBean(r, m).getBean(), eType);
} else if (sType.canCreateNewBean(outer)) {
BeanMap m = newBeanMap(outer, sType.getInnerClass());
o = parseIntoBean(r, m).getBean();
@@ -256,8 +266,8 @@ public final class HtmlParserSession extends XmlParserSession {
if (! isValid)
throw new XmlParseException(r.getLocation(), "Unexpected tag ''{0}'' for type ''{1}''", tag, eType);
- if (transform != null && o != null)
- o = transform.unswap(this, o, eType);
+ if (swap != null && o != null)
+ o = swap.unswap(this, o, eType);
if (outer != null)
setParent(eType, o, outer);
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java
index 067f12b..75cfbf5 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java
@@ -600,30 +600,32 @@ public class HtmlSerializerSession extends XmlSerializerSession {
for (Object k : th) {
BeanMapEntry p = m2.getProperty(toString(k));
BeanPropertyMeta pMeta = p.getMeta();
- Object value = p.getValue();
-
- String link = null, anchorText = null;
- if (! pMeta.getClassMeta().isCollectionOrArray()) {
- link = m2.resolveVars(getLink(pMeta));
- anchorText = m2.resolveVars(getAnchorText(pMeta));
+ if (pMeta.canRead()) {
+ Object value = p.getValue();
+
+ String link = null, anchorText = null;
+ if (! pMeta.getClassMeta().isCollectionOrArray()) {
+ link = m2.resolveVars(getLink(pMeta));
+ anchorText = m2.resolveVars(getAnchorText(pMeta));
+ }
+
+ if (anchorText != null)
+ value = anchorText;
+
+ String style = getStyle(this, pMeta, value);
+ out.oTag(i+2, "td");
+ if (style != null)
+ out.attr("style", style);
+ out.cTag();
+ if (link != null)
+ out.oTag("a").attrUri("href", link).cTag();
+ ContentResult cr = serializeAnything(out, value, pMeta.getClassMeta(), p.getKey().toString(), 2, pMeta, false);
+ if (cr == CR_NORMAL)
+ out.i(i+2);
+ if (link != null)
+ out.eTag("a");
+ out.eTag("td").nl(i+2);
}
-
- if (anchorText != null)
- value = anchorText;
-
- String style = getStyle(this, pMeta, value);
- out.oTag(i+2, "td");
- if (style != null)
- out.attr("style", style);
- out.cTag();
- if (link != null)
- out.oTag("a").attrUri("href", link).cTag();
- ContentResult cr = serializeAnything(out, value, pMeta.getClassMeta(), p.getKey().toString(), 2, pMeta, false);
- if (cr == CR_NORMAL)
- out.i(i+2);
- if (link != null)
- out.eTag("a");
- out.eTag("td").nl(i+2);
}
}
out.ie(i+1).eTag("tr").nl(i+1);
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
index 936f80d..3b7c237 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
@@ -584,20 +584,39 @@ public final class ClassUtils {
* Can be subtypes of the actual constructor argument types.
* @return The matching constructor, or <jk>null</jk> if constructor could not be found.
*/
- @SuppressWarnings("unchecked")
public static <T> Constructor<T> findPublicConstructor(Class<T> c, boolean fuzzyArgs, Class<?>...argTypes) {
+ return findConstructor(c, Visibility.PUBLIC, fuzzyArgs, argTypes);
+ }
+
+ /**
+ * Finds a constructor with the specified parameters without throwing an exception.
+ *
+ * @param c The class to search for a constructor.
+ * @param vis The minimum visibility.
+ * @param fuzzyArgs
+ * Use fuzzy-arg matching.
+ * Find a constructor that best matches the specified args.
+ * @param argTypes
+ * The argument types in the constructor.
+ * Can be subtypes of the actual constructor argument types.
+ * @return The matching constructor, or <jk>null</jk> if constructor could not be found.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> Constructor<T> findConstructor(Class<T> c, Visibility vis, boolean fuzzyArgs, Class<?>...argTypes) {
ConstructorCacheEntry cce = CONSTRUCTOR_CACHE.get(c);
- if (cce != null && argsMatch(cce.paramTypes, argTypes))
+ if (cce != null && argsMatch(cce.paramTypes, argTypes) && cce.isVisible(vis))
return (Constructor<T>)cce.constructor;
if (fuzzyArgs) {
int bestCount = -1;
Constructor<?> bestMatch = null;
- for (Constructor<?> n : c.getConstructors()) {
- int m = fuzzyArgsMatch(n.getParameterTypes(), argTypes);
- if (m > bestCount) {
- bestCount = m;
- bestMatch = n;
+ for (Constructor<?> n : c.getDeclaredConstructors()) {
+ if (vis.isVisible(n)) {
+ int m = fuzzyArgsMatch(n.getParameterTypes(), argTypes);
+ if (m > bestCount) {
+ bestCount = m;
+ bestMatch = n;
+ }
}
}
if (bestCount >= 0)
@@ -606,7 +625,7 @@ public final class ClassUtils {
}
for (Constructor<?> n : c.getConstructors()) {
- if (argsMatch(n.getParameterTypes(), argTypes)) {
+ if (argsMatch(n.getParameterTypes(), argTypes) && vis.isVisible(n)) {
CONSTRUCTOR_CACHE.put(c, new ConstructorCacheEntry(c, n));
return (Constructor<T>)n;
}
@@ -615,6 +634,8 @@ public final class ClassUtils {
return null;
}
+
+
private static final class ConstructorCacheEntry {
final Constructor<?> constructor;
final Class<?>[] paramTypes;
@@ -623,6 +644,10 @@ public final class ClassUtils {
this.constructor = constructor;
this.paramTypes = constructor.getParameterTypes();
}
+
+ boolean isVisible(Visibility vis) {
+ return vis.isVisible(constructor);
+ }
}
/**
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParserSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParserSession.java
index a42b712..b3fc575 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParserSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParserSession.java
@@ -117,7 +117,14 @@ public final class JsonParserSession extends ReaderParserSession {
if (eType == null)
eType = object();
PojoSwap<T,Object> swap = (PojoSwap<T,Object>)eType.getPojoSwap(this);
- ClassMeta<?> sType = swap == null ? eType : swap.getSwapClassMeta(this);
+ BuilderSwap<T,Object> builder = (BuilderSwap<T,Object>)eType.getBuilderSwap(this);
+ ClassMeta<?> sType = null;
+ if (builder != null)
+ sType = builder.getBuilderClassMeta(this);
+ else if (swap != null)
+ sType = swap.getSwapClassMeta(this);
+ else
+ sType = eType;
setCurrentClass(sType);
String wrapperAttr = sType.getExtendedMeta(JsonClassMeta.class).getWrapperAttr();
@@ -178,6 +185,9 @@ public final class JsonParserSession extends ReaderParserSession {
Collection l = (sType.canCreateNewInstance(outer) ? (Collection)sType.newInstance() : new ObjectList(this));
o = parseIntoCollection2(r, l, sType, pMeta);
}
+ } else if (builder != null) {
+ BeanMap m = toBeanMap(builder.create(this, eType));
+ o = builder.build(this, parseIntoBeanMap2(r, m).getBean(), eType);
} else if (sType.canCreateNewBean(outer)) {
BeanMap m = newBeanMap(outer, sType.getInnerClass());
o = parseIntoBeanMap2(r, m).getBean();
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSchemaSerializerSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSchemaSerializerSession.java
index 0054fbf..95aab8b 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSchemaSerializerSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSchemaSerializerSession.java
@@ -109,7 +109,8 @@ public class JsonSchemaSerializerSession extends JsonSerializerSession {
bm = new BeanMetaFiltered(bm, pNames);
for (Iterator<BeanPropertyMeta> i = bm.getPropertyMetas().iterator(); i.hasNext();) {
BeanPropertyMeta p = i.next();
- properties.put(p.getName(), getSchema(p.getClassMeta(), p.getName(), p.getProperties()));
+ if (p.canRead())
+ properties.put(p.getName(), getSchema(p.getClassMeta(), p.getName(), p.getProperties()));
}
out.put("properties", properties);
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSerializerSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSerializerSession.java
index 3396055..d792f6c 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSerializerSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonSerializerSession.java
@@ -189,24 +189,26 @@ public class JsonSerializerSession extends WriterSerializerSession {
boolean addComma = false;
for (BeanPropertyValue p : m.getValues(isTrimNulls(), typeName != null ? createBeanTypeNameProperty(m, typeName) : null)) {
BeanPropertyMeta pMeta = p.getMeta();
- ClassMeta<?> cMeta = p.getClassMeta();
- String key = p.getName();
- Object value = p.getValue();
- Throwable t = p.getThrown();
- if (t != null)
- onBeanGetterException(pMeta, t);
+ if (pMeta.canRead()) {
+ ClassMeta<?> cMeta = p.getClassMeta();
+ String key = p.getName();
+ Object value = p.getValue();
+ Throwable t = p.getThrown();
+ if (t != null)
+ onBeanGetterException(pMeta, t);
- if (canIgnoreValue(cMeta, key, value))
- continue;
+ if (canIgnoreValue(cMeta, key, value))
+ continue;
- if (addComma)
- out.append(',').smi(i);
+ if (addComma)
+ out.append(',').smi(i);
- out.cr(i).attr(key).append(':').s(i);
+ out.cr(i).attr(key).append(':').s(i);
- serializeAnything(out, value, cMeta, key, pMeta);
+ serializeAnything(out, value, cMeta, key, pMeta);
- addComma = true;
+ addComma = true;
+ }
}
out.cre(i-1).append('}');
return out;
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/msgpack/MsgPackParserSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/msgpack/MsgPackParserSession.java
index 400818c..e537bb9 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/msgpack/MsgPackParserSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/msgpack/MsgPackParserSession.java
@@ -58,7 +58,14 @@ public final class MsgPackParserSession extends InputStreamParserSession {
if (eType == null)
eType = object();
PojoSwap<T,Object> swap = (PojoSwap<T,Object>)eType.getPojoSwap(this);
- ClassMeta<?> sType = swap == null ? eType : swap.getSwapClassMeta(this);
+ BuilderSwap<T,Object> builder = (BuilderSwap<T,Object>)eType.getBuilderSwap(this);
+ ClassMeta<?> sType = null;
+ if (builder != null)
+ sType = builder.getBuilderClassMeta(this);
+ else if (swap != null)
+ sType = swap.getSwapClassMeta(this);
+ else
+ sType = eType;
setCurrentClass(sType);
Object o = null;
@@ -110,9 +117,9 @@ public final class MsgPackParserSession extends InputStreamParserSession {
} else {
throw new ParseException(loc(is), "Invalid data type {0} encountered for parse type {1}", dt, sType);
}
- } else if (sType.canCreateNewBean(outer)) {
+ } else if (builder != null || sType.canCreateNewBean(outer)) {
if (dt == MAP) {
- BeanMap m = newBeanMap(outer, sType.getInnerClass());
+ BeanMap m = builder == null ? newBeanMap(outer, sType.getInnerClass()) : toBeanMap(builder.create(this, eType));
for (int i = 0; i < length; i++) {
String pName = parseAnything(string(), is, m.getBean(false), null);
BeanPropertyMeta bpm = m.getPropertyMeta(pName);
@@ -128,7 +135,7 @@ public final class MsgPackParserSession extends InputStreamParserSession {
bpm.set(m, pName, value);
}
}
- o = m.getBean();
+ o = builder == null ? m.getBean() : builder.build(this, m.getBean(), eType);
} else {
throw new ParseException(loc(is), "Invalid data type {0} encountered for parse type {1}", dt, sType);
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerSession.java
index a836d8c..a60aa5b 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerSession.java
@@ -195,15 +195,17 @@ public final class MsgPackSerializerSession extends OutputStreamSerializerSessio
for (BeanPropertyValue p : values) {
BeanPropertyMeta pMeta = p.getMeta();
- ClassMeta<?> cMeta = p.getClassMeta();
- String key = p.getName();
- Object value = p.getValue();
- Throwable t = p.getThrown();
- if (t != null)
- onBeanGetterException(pMeta, t);
- else {
- serializeAnything(out, key, null, null, null);
- serializeAnything(out, value, cMeta, key, pMeta);
+ if (pMeta.canRead()) {
+ ClassMeta<?> cMeta = p.getClassMeta();
+ String key = p.getName();
+ Object value = p.getValue();
+ Throwable t = p.getThrown();
+ if (t != null)
+ onBeanGetterException(pMeta, t);
+ else {
+ serializeAnything(out, key, null, null, null);
+ serializeAnything(out, value, cMeta, key, pMeta);
+ }
}
}
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/Parser.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/Parser.java
index aa36e1e..2b7be72 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/Parser.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/Parser.java
@@ -475,10 +475,14 @@ public abstract class Parser extends BeanContext {
* .set(<jsf>PARSER_unbuffered</jsf>, <jk>true</jk>)
* .build();
*
+ * <jc>// If you're calling parse on the same input multiple times, use a session instead of the parser directly.</jc>
+ * <jc>// It's more efficient because we don't need to recalc the session settings again. </jc>
+ * ReaderParserSession s = p.createSession();
+ *
* <jc>// Read input with multiple POJOs</jc>
* Reader json = <jk>new</jk> StringReader(<js>"{foo:'bar'}{foo:'baz'}"</js>);
- * MyBean myBean1 = p.parse(json, MyBean.<jk>class</jk>);
- * MyBean myBean2 = p.parse(json, MyBean.<jk>class</jk>);
+ * MyBean myBean1 = s.parse(json, MyBean.<jk>class</jk>);
+ * MyBean myBean2 = s.parse(json, MyBean.<jk>class</jk>);
* </p>
*
* <h5 class='section'>Notes:</h5>
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/Builder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/Builder.java
new file mode 100644
index 0000000..fd0e4fa
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/Builder.java
@@ -0,0 +1,30 @@
+// ***************************************************************************************************************************
+// * 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;
+
+/**
+ * Identifies a class as being a builder for a bean class.
+ *
+ * <p>
+ * This interface has no methods to implement.
+ * <br>It's purpose is to identify a class as a builder when it's used on a constructor of the built class.
+ *
+ * <h5 class='topic'>Documentation</h5>
+ * <ul>
+ * <li><a class="doclink" href="../../../../overview-summary.html#juneau-marshall.PojoBuilders">Overview > POJO Builders</a>
+ * </ul>
+ *
+ * @param <T> The type of objects that this builder creates.
+ */
+public interface Builder<T> {
+}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/BuilderSwap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/BuilderSwap.java
new file mode 100644
index 0000000..645cfbb
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/BuilderSwap.java
@@ -0,0 +1,231 @@
+// ***************************************************************************************************************************
+// * 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 static org.apache.juneau.internal.ClassUtils.*;
+
+import java.lang.reflect.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.annotation.*;
+
+/**
+ * Specialized transform for builder classes.
+ *
+ * <h5 class='topic'>Documentation</h5>
+ * <ul>
+ * <li><a class="doclink" href="../../../../overview-summary.html#juneau-marshall.PojoBuilders">Overview > POJO Builders</a>
+ * </ul>
+ *
+ * @param <T> The bean class.
+ * @param <B> The builder class.
+ */
+@SuppressWarnings("unchecked")
+public class BuilderSwap<T,B> {
+
+ private final Class<T> pojoClass;
+ private final Class<B> builderClass;
+ private final Constructor<T> pojoConstructor; // public Pojo(Builder);
+ private final Constructor<B> builderConstructor; // public Builder();
+ private final Method createBuilderMethod; // Builder create();
+ private final Method createPojoMethod; // Pojo build();
+ private ClassMeta<?> builderClassMeta;
+
+ /**
+ * Constructor.
+ *
+ * @param pojoClass The POJO class created by the builder class.
+ * @param builderClass The builder class.
+ * @param pojoConstructor The POJO constructor that takes in a builder as a parameter.
+ * @param builderConstructor The builder no-arg constructor.
+ * @param createBuilderMethod The static create() method on the POJO class.
+ * @param createPojoMethod The build() method on the builder class.
+ */
+ protected BuilderSwap(Class<T> pojoClass, Class<B> builderClass, Constructor<T> pojoConstructor, Constructor<B> builderConstructor, Method createBuilderMethod, Method createPojoMethod) {
+ this.pojoClass = pojoClass;
+ this.builderClass = builderClass;
+ this.pojoConstructor = pojoConstructor;
+ this.builderConstructor = builderConstructor;
+ this.createBuilderMethod = createBuilderMethod;
+ this.createPojoMethod = createPojoMethod;
+ }
+
+ /**
+ * The POJO class.
+ *
+ * @return The POJO class.
+ */
+ public Class<T> getPojoClass() {
+ return pojoClass;
+ }
+
+ /**
+ * The builder class.
+ *
+ * @return The builder class.
+ */
+ public Class<B> getBuilderClass() {
+ return builderClass;
+ }
+
+ /**
+ * Returns the {@link ClassMeta} of the transformed class type.
+ *
+ * <p>
+ * This value is cached for quick lookup.
+ *
+ * @param session
+ * The bean context to use to get the class meta.
+ * This is always going to be the same bean context that created this swap.
+ * @return The {@link ClassMeta} of the transformed class type.
+ */
+ public ClassMeta<?> getBuilderClassMeta(BeanSession session) {
+ if (builderClassMeta == null)
+ builderClassMeta = session.getClassMeta(getBuilderClass());
+ return builderClassMeta;
+ }
+
+ /**
+ * Creates a new builder object.
+ *
+ * @param session The current bean session.
+ * @param hint A hint about the class type.
+ * @return A new POJO.
+ * @throws Exception
+ */
+ public B create(BeanSession session, ClassMeta<?> hint) throws Exception {
+ if (createBuilderMethod != null)
+ return (B)createBuilderMethod.invoke(null);
+ return builderConstructor.newInstance();
+ }
+
+ /**
+ * Creates a new POJO from the specified builder.
+ *
+ * @param session The current bean session.
+ * @param builder The POJO builder.
+ * @param hint A hint about the class type.
+ * @return A new POJO.
+ * @throws Exception
+ */
+ public T build(BeanSession session, B builder, ClassMeta<?> hint) throws Exception {
+ if (createPojoMethod != null)
+ return (T)createPojoMethod.invoke(builder);
+ return pojoConstructor.newInstance(builder);
+ }
+
+ /**
+ * Creates a BuilderSwap from the specified builder class if it qualifies as one.
+ *
+ * @param builderClass The potential builder class.
+ * @param cVis Minimum constructor visibility.
+ * @param mVis Minimum method visibility.
+ * @return A new swap instance, or <jk>null</jk> if class wasn't a builder class.
+ */
+ @SuppressWarnings("rawtypes")
+ public static BuilderSwap<?,?> findSwapFromBuilderClass(Class<?> builderClass, Visibility cVis, Visibility mVis) {
+ if (! isPublic(builderClass))
+ return null;
+
+ Class<?> pojoClass = resolveParameterType(Builder.class, 0, builderClass);
+
+ Method createPojoMethod, createBuilderMethod;
+ Constructor<?> pojoConstructor, builderConstructor;
+
+ createPojoMethod = findCreatePojoMethod(builderClass);
+ if (createPojoMethod != null)
+ pojoClass = createPojoMethod.getReturnType();
+
+ if (pojoClass == null)
+ return null;
+
+ pojoConstructor = findConstructor(pojoClass, cVis, false, builderClass);
+ if (pojoConstructor == null)
+ return null;
+
+ builderConstructor = findNoArgConstructor(builderClass, cVis);
+ createBuilderMethod = findBuilderCreateMethod(pojoClass);
+ if (builderConstructor == null && createBuilderMethod == null)
+ return null;
+
+ return new BuilderSwap(pojoClass, builderClass, pojoConstructor, builderConstructor, createBuilderMethod, createPojoMethod);
+ }
+
+
+ /**
+ * Creates a BuilderSwap from the specified POJO class if it has one.
+ *
+ * @param pojoClass The POJO class to check.
+ * @param cVis Minimum constructor visibility.
+ * @param mVis Minimum method visibility.
+ * @return A new swap instance, or <jk>null</jk> if class didn't have a builder class.
+ */
+ @SuppressWarnings("rawtypes")
+ public static BuilderSwap<?,?> findSwapFromPojoClass(Class<?> pojoClass, Visibility cVis, Visibility mVis) {
+ Class<?> builderClass = null;
+ Method pojoCreateMethod, builderCreateMethod;
+ Constructor<?> pojoConstructor = null, builderConstructor;
+
+ org.apache.juneau.annotation.Builder b = pojoClass.getAnnotation(org.apache.juneau.annotation.Builder.class);
+
+ if (b != null && b.value() != Null.class)
+ builderClass = b.value();
+
+ builderCreateMethod = findBuilderCreateMethod(pojoClass);
+
+ if (builderClass == null && builderCreateMethod != null)
+ builderClass = builderCreateMethod.getReturnType();
+
+ if (builderClass == null) {
+ for (Constructor cc : pojoClass.getConstructors()) {
+ if (cVis.isVisible(cc)) {
+ Class<?>[] pt = cc.getParameterTypes();
+ if (pt.length == 1 && isParentClass(Builder.class, pt[0])) {
+ pojoConstructor = cc;
+ builderClass = pt[0];
+ }
+ }
+ }
+ }
+
+ if (builderClass == null)
+ return null;
+
+ builderConstructor = findNoArgConstructor(builderClass, cVis);
+ if (builderConstructor == null && builderCreateMethod == null)
+ return null;
+
+ pojoCreateMethod = findCreatePojoMethod(builderClass);
+ if (pojoConstructor == null)
+ pojoConstructor = findConstructor(pojoClass, cVis, false, builderClass);
+
+ if (pojoConstructor == null && pojoCreateMethod == null)
+ return null;
+
+ return new BuilderSwap(pojoClass, builderClass, pojoConstructor, builderConstructor, builderCreateMethod, pojoCreateMethod);
+ }
+
+ private static Method findBuilderCreateMethod(Class<?> pojoClass) {
+ for (Method m : pojoClass.getDeclaredMethods())
+ if (isPublic(m) && isStatic(m) && m.getName().equals("create") && m.getReturnType() != Void.class)
+ return m;
+ return null;
+ }
+
+ private static Method findCreatePojoMethod(Class<?> builderClass) {
+ for (Method m : builderClass.getDeclaredMethods())
+ if ("build".equals(m.getName()) && ! (isStatic(m) || m.getReturnType() == Void.class))
+ return m;
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonParserSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonParserSession.java
index ad7906a..6f9e9ad 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonParserSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonParserSession.java
@@ -131,7 +131,15 @@ public class UonParserSession extends ReaderParserSession {
if (eType == null)
eType = object();
PojoSwap<T,Object> swap = (PojoSwap<T,Object>)eType.getPojoSwap(this);
- ClassMeta<?> sType = swap == null ? eType : swap.getSwapClassMeta(this);
+ BuilderSwap<T,Object> builder = (BuilderSwap<T,Object>)eType.getBuilderSwap(this);
+ ClassMeta<?> sType = null;
+ if (builder != null)
+ sType = builder.getBuilderClassMeta(this);
+ else if (swap != null)
+ sType = swap.getSwapClassMeta(this);
+ else
+ sType = eType;
+ setCurrentClass(sType);
Object o = null;
@@ -210,6 +218,10 @@ public class UonParserSession extends ReaderParserSession {
);
o = parseIntoCollection(r, l, sType, isUrlParamValue, pMeta);
}
+ } else if (builder != null) {
+ BeanMap m = toBeanMap(builder.create(this, eType));
+ m = parseIntoBeanMap(r, m);
+ o = m == null ? null : builder.build(this, m.getBean(), eType);
} else if (sType.canCreateNewBean(outer)) {
BeanMap m = newBeanMap(outer, sType.getInnerClass());
m = parseIntoBeanMap(r, m);
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonSerializerSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonSerializerSession.java
index 7730c9c..28e7b89 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonSerializerSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonSerializerSession.java
@@ -221,25 +221,27 @@ public class UonSerializerSession extends WriterSerializerSession {
for (BeanPropertyValue p : m.getValues(isTrimNulls(), typeName != null ? createBeanTypeNameProperty(m, typeName) : null)) {
BeanPropertyMeta pMeta = p.getMeta();
- ClassMeta<?> cMeta = p.getClassMeta();
+ if (pMeta.canRead()) {
+ ClassMeta<?> cMeta = p.getClassMeta();
- String key = p.getName();
- Object value = p.getValue();
- Throwable t = p.getThrown();
- if (t != null)
- onBeanGetterException(pMeta, t);
+ String key = p.getName();
+ Object value = p.getValue();
+ Throwable t = p.getThrown();
+ if (t != null)
+ onBeanGetterException(pMeta, t);
- if (canIgnoreValue(cMeta, key, value))
- continue;
+ if (canIgnoreValue(cMeta, key, value))
+ continue;
- if (addComma)
- out.append(',');
+ if (addComma)
+ out.append(',');
- out.cr(indent).appendObject(key, false).append('=');
+ out.cr(indent).appendObject(key, false).append('=');
- serializeAnything(out, value, cMeta, key, pMeta);
+ serializeAnything(out, value, cMeta, key, pMeta);
- addComma = true;
+ addComma = true;
+ }
}
if (m.size() > 0)
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParserSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParserSession.java
index 2207577..0134cea 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParserSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParserSession.java
@@ -96,7 +96,14 @@ public class UrlEncodingParserSession extends UonParserSession {
if (eType == null)
eType = (ClassMeta<T>)object();
PojoSwap<T,Object> swap = (PojoSwap<T,Object>)eType.getPojoSwap(this);
- ClassMeta<?> sType = swap == null ? eType : swap.getSwapClassMeta(this);
+ BuilderSwap<T,Object> builder = (BuilderSwap<T,Object>)eType.getBuilderSwap(this);
+ ClassMeta<?> sType = null;
+ if (builder != null)
+ sType = builder.getBuilderClassMeta(this);
+ else if (swap != null)
+ sType = swap.getSwapClassMeta(this);
+ else
+ sType = eType;
int c = r.peekSkipWs();
if (c == '?')
@@ -114,6 +121,10 @@ public class UrlEncodingParserSession extends UonParserSession {
} else if (sType.isMap()) {
Map m = (sType.canCreateNewInstance() ? (Map)sType.newInstance() : new ObjectMap(this));
o = parseIntoMap2(r, m, sType, m);
+ } else if (builder != null) {
+ BeanMap m = toBeanMap(builder.create(this, eType));
+ m = parseIntoBeanMap(r, m);
+ o = m == null ? null : builder.build(this, m.getBean(), eType);
} else if (sType.canCreateNewBean(outer)) {
BeanMap m = newBeanMap(outer, sType.getInnerClass());
m = parseIntoBeanMap(r, m);
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializerSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializerSession.java
index f0d3bfc..39b95a3 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializerSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializerSession.java
@@ -222,43 +222,45 @@ public class UrlEncodingSerializerSession extends UonSerializerSession {
for (BeanPropertyValue p : m.getValues(isTrimNulls(), typeName != null ? createBeanTypeNameProperty(m, typeName) : null)) {
BeanPropertyMeta pMeta = p.getMeta();
- ClassMeta<?> cMeta = p.getClassMeta();
- ClassMeta<?> sMeta = cMeta.getSerializedClassMeta(this);
-
- String key = p.getName();
- Object value = p.getValue();
- Throwable t = p.getThrown();
- if (t != null)
- onBeanGetterException(pMeta, t);
-
- if (canIgnoreValue(sMeta, key, value))
- continue;
-
- if (value != null && shouldUseExpandedParams(pMeta)) {
- // Transformed object array bean properties may be transformed resulting in ArrayLists,
- // so we need to check type if we think it's an array.
- Iterator i = (sMeta.isCollection() || value instanceof Collection) ? ((Collection)value).iterator() : iterator(value);
- while (i.hasNext()) {
+ if (pMeta.canRead()) {
+ ClassMeta<?> cMeta = p.getClassMeta();
+ ClassMeta<?> sMeta = cMeta.getSerializedClassMeta(this);
+
+ String key = p.getName();
+ Object value = p.getValue();
+ Throwable t = p.getThrown();
+ if (t != null)
+ onBeanGetterException(pMeta, t);
+
+ if (canIgnoreValue(sMeta, key, value))
+ continue;
+
+ if (value != null && shouldUseExpandedParams(pMeta)) {
+ // Transformed object array bean properties may be transformed resulting in ArrayLists,
+ // so we need to check type if we think it's an array.
+ Iterator i = (sMeta.isCollection() || value instanceof Collection) ? ((Collection)value).iterator() : iterator(value);
+ while (i.hasNext()) {
+ if (addAmp)
+ out.cr(indent).append('&');
+
+ out.appendObject(key, true).append('=');
+
+ super.serializeAnything(out, i.next(), cMeta.getElementType(), key, pMeta);
+
+ addAmp = true;
+ }
+ } else {
if (addAmp)
out.cr(indent).append('&');
out.appendObject(key, true).append('=');
- super.serializeAnything(out, i.next(), cMeta.getElementType(), key, pMeta);
+ super.serializeAnything(out, value, cMeta, key, pMeta);
addAmp = true;
}
- } else {
- if (addAmp)
- out.cr(indent).append('&');
-
- out.appendObject(key, true).append('=');
-
- super.serializeAnything(out, value, cMeta, key, pMeta);
-
- addAmp = true;
+
}
-
}
return out;
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlParserSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlParserSession.java
index 630bce6..f1bc15e 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlParserSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlParserSession.java
@@ -286,7 +286,14 @@ public class XmlParserSession extends ReaderParserSession {
if (eType == null)
eType = (ClassMeta<T>)object();
PojoSwap<T,Object> swap = (PojoSwap<T,Object>)eType.getPojoSwap(this);
- ClassMeta<?> sType = swap == null ? eType : swap.getSwapClassMeta(this);
+ BuilderSwap<T,Object> builder = (BuilderSwap<T,Object>)eType.getBuilderSwap(this);
+ ClassMeta<?> sType = null;
+ if (builder != null)
+ sType = builder.getBuilderClassMeta(this);
+ else if (swap != null)
+ sType = swap.getSwapClassMeta(this);
+ else
+ sType = eType;
setCurrentClass(sType);
String wrapperAttr = (isRoot && preserveRootElement) ? r.getName().getLocalPart() : null;
@@ -352,19 +359,20 @@ public class XmlParserSession extends ReaderParserSession {
o = parseIntoCollection(r, l, sType, pMeta);
} else if (sType.isNumber()) {
o = parseNumber(getElementText(r), (Class<? extends Number>)sType.getInnerClass());
- } else if (sType.canCreateNewBean(outer)) {
+ } else if (builder != null || sType.canCreateNewBean(outer)) {
if (sType.getExtendedMeta(XmlClassMeta.class).getFormat() == COLLAPSED) {
String fieldName = r.getLocalName();
- BeanMap<?> m = newBeanMap(outer, sType.getInnerClass());
+ BeanMap<?> m = builder != null ? toBeanMap(builder.create(this, eType)) : newBeanMap(outer, sType.getInnerClass());
BeanPropertyMeta bpm = m.getMeta().getExtendedMeta(XmlBeanMeta.class).getPropertyMeta(fieldName);
ClassMeta<?> cm = m.getMeta().getClassMeta();
Object value = parseAnything(cm, currAttr, r, m.getBean(false), false, null);
setName(cm, value, currAttr);
bpm.set(m, currAttr, value);
- o = m.getBean();
+ o = builder != null ? builder.build(this, m.getBean(), eType) : m.getBean();
} else {
- BeanMap m = newBeanMap(outer, sType.getInnerClass());
- o = parseIntoBean(r, m).getBean();
+ BeanMap m = builder != null ? toBeanMap(builder.create(this, eType)) : newBeanMap(outer, sType.getInnerClass());
+ m = parseIntoBean(r, m);
+ o = builder != null ? builder.build(this, m.getBean(), eType) : m.getBean();
}
} else if (sType.isArray() || sType.isArgs()) {
ArrayList l = (ArrayList)parseIntoCollection(r, new ArrayList(), sType, pMeta);
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSchemaSerializerSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSchemaSerializerSession.java
index 9a3762f..86c4f2b 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSchemaSerializerSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSchemaSerializerSession.java
@@ -349,9 +349,11 @@ public class XmlSchemaSerializerSession extends XmlSerializerSession {
boolean hasChildElements = false;
for (BeanPropertyMeta pMeta : bm.getPropertyMetas()) {
- XmlFormat pMetaFormat = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getXmlFormat();
- if (pMetaFormat != XmlFormat.ATTR)
- hasChildElements = true;
+ if (pMeta.canRead()) {
+ XmlFormat pMetaFormat = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getXmlFormat();
+ if (pMetaFormat != XmlFormat.ATTR)
+ hasChildElements = true;
+ }
}
XmlBeanMeta xbm2 = bm.getExtendedMeta(XmlBeanMeta.class);
@@ -369,17 +371,20 @@ public class XmlSchemaSerializerSession extends XmlSerializerSession {
boolean hasCollapsed = false;
for (BeanPropertyMeta pMeta : bm.getPropertyMetas()) {
- XmlBeanPropertyMeta xmlMeta = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
- if (xmlMeta.getXmlFormat() != ATTR) {
- if (xmlMeta.getNamespace() != null) {
- ClassMeta<?> ct2 = pMeta.getClassMeta();
- Namespace cNs = first(xmlMeta.getNamespace(), ct2.getExtendedMeta(XmlClassMeta.class).getNamespace(), cm.getExtendedMeta(XmlClassMeta.class).getNamespace(), defaultNs);
- // Child element is in another namespace.
- schemas.queueElement(cNs, pMeta.getName(), ct2);
- hasOtherNsElement = true;
+ if (pMeta.canRead()) {
+ XmlBeanPropertyMeta xmlMeta = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
+ if (xmlMeta.getXmlFormat() != ATTR) {
+ if (xmlMeta.getNamespace() != null) {
+ ClassMeta<?> ct2 = pMeta.getClassMeta();
+ Namespace cNs = first(xmlMeta.getNamespace(), ct2.getExtendedMeta(XmlClassMeta.class).getNamespace(), cm.getExtendedMeta(XmlClassMeta.class).getNamespace(), defaultNs);
+ // Child element is in another namespace.
+ schemas.queueElement(cNs, pMeta.getName(), ct2);
+ hasOtherNsElement = true;
+ }
+ if (xmlMeta.getXmlFormat() == COLLAPSED)
+ hasCollapsed = true;
}
- if (xmlMeta.getXmlFormat() == COLLAPSED)
- hasCollapsed = true;
+
}
}
@@ -396,30 +401,31 @@ public class XmlSchemaSerializerSession extends XmlSerializerSession {
} else {
w.sTag(i+1, "all").nl(i+1);
for (BeanPropertyMeta pMeta : bm.getPropertyMetas()) {
- XmlBeanPropertyMeta xmlMeta = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
- if (xmlMeta.getXmlFormat() != ATTR) {
- boolean isCollapsed = xmlMeta.getXmlFormat() == COLLAPSED;
- ClassMeta<?> ct2 = pMeta.getClassMeta();
- String childName = pMeta.getName();
- if (isCollapsed) {
- if (xmlMeta.getChildName() != null)
- childName = xmlMeta.getChildName();
- ct2 = pMeta.getClassMeta().getElementType();
+ if (pMeta.canRead()) {
+ XmlBeanPropertyMeta xmlMeta = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
+ if (xmlMeta.getXmlFormat() != ATTR) {
+ boolean isCollapsed = xmlMeta.getXmlFormat() == COLLAPSED;
+ ClassMeta<?> ct2 = pMeta.getClassMeta();
+ String childName = pMeta.getName();
+ if (isCollapsed) {
+ if (xmlMeta.getChildName() != null)
+ childName = xmlMeta.getChildName();
+ ct2 = pMeta.getClassMeta().getElementType();
+ }
+ Namespace cNs = first(xmlMeta.getNamespace(), ct2.getExtendedMeta(XmlClassMeta.class).getNamespace(), cm.getExtendedMeta(XmlClassMeta.class).getNamespace(), defaultNs);
+ if (xmlMeta.getNamespace() == null) {
+ w.oTag(i+2, "element")
+ .attr("name", XmlUtils.encodeElementName(childName), false)
+ .attr("type", getXmlType(cNs, ct2))
+ .attr("minOccurs", 0);
+
+ w.ceTag().nl(i+2);
+ } else {
+ // Child element is in another namespace.
+ schemas.queueElement(cNs, pMeta.getName(), ct2);
+ hasOtherNsElement = true;
+ }
}
- Namespace cNs = first(xmlMeta.getNamespace(), ct2.getExtendedMeta(XmlClassMeta.class).getNamespace(), cm.getExtendedMeta(XmlClassMeta.class).getNamespace(), defaultNs);
- if (xmlMeta.getNamespace() == null) {
- w.oTag(i+2, "element")
- .attr("name", XmlUtils.encodeElementName(childName), false)
- .attr("type", getXmlType(cNs, ct2))
- .attr("minOccurs", 0);
-
- w.ceTag().nl(i+2);
- } else {
- // Child element is in another namespace.
- schemas.queueElement(cNs, pMeta.getName(), ct2);
- hasOtherNsElement = true;
- }
-
}
}
w.eTag(i+1, "all").nl(i+1);
@@ -428,26 +434,28 @@ public class XmlSchemaSerializerSession extends XmlSerializerSession {
}
for (BeanPropertyMeta pMeta : bm.getExtendedMeta(XmlBeanMeta.class).getAttrProperties().values()) {
- Namespace pNs = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace();
- if (pNs == null)
- pNs = defaultNs;
-
- // If the bean attribute has a different namespace than the bean, then it needs to
- // be added as a top-level entry in the appropriate schema file.
- if (pNs != targetNs) {
- schemas.queueAttribute(pNs, pMeta.getName(), pMeta.getClassMeta());
- w.oTag(i+1, "attribute")
- //.attr("name", pMeta.getName(), true)
- .attr("ref", pNs.getName() + ':' + pMeta.getName())
- .ceTag().nl(i+1);
- }
+ if (pMeta.canRead()) {
+ Namespace pNs = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace();
+ if (pNs == null)
+ pNs = defaultNs;
+
+ // If the bean attribute has a different namespace than the bean, then it needs to
+ // be added as a top-level entry in the appropriate schema file.
+ if (pNs != targetNs) {
+ schemas.queueAttribute(pNs, pMeta.getName(), pMeta.getClassMeta());
+ w.oTag(i+1, "attribute")
+ //.attr("name", pMeta.getName(), true)
+ .attr("ref", pNs.getName() + ':' + pMeta.getName())
+ .ceTag().nl(i+1);
+ }
- // Otherwise, it's just a plain attribute of this bean.
- else {
- w.oTag(i+1, "attribute")
- .attr("name", pMeta.getName(), true)
- .attr("type", getXmlAttrType(pMeta.getClassMeta()))
- .ceTag().nl(i+1);
+ // Otherwise, it's just a plain attribute of this bean.
+ else {
+ w.oTag(i+1, "attribute")
+ .attr("name", pMeta.getName(), true)
+ .attr("type", getXmlAttrType(pMeta.getClassMeta()))
+ .ceTag().nl(i+1);
+ }
}
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
index 935dfd4..81583ea 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
@@ -210,9 +210,11 @@ public class XmlSerializerSession extends WriterSerializerSession {
if (innerType.isBean()) {
for (BeanPropertyMeta bpm : innerType.getBeanMeta().getPropertyMetas()) {
- ns = bpm.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace();
- if (ns != null && ns.uri != null)
- addNamespace(ns);
+ if (bpm.canRead()) {
+ ns = bpm.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace();
+ if (ns != null && ns.uri != null)
+ addNamespace(ns);
+ }
}
} else if (innerType.isMap()) {
@@ -562,40 +564,42 @@ public class XmlSerializerSession extends WriterSerializerSession {
String n = p.getName();
if (attrs.contains(n) || attrs.contains("*") || n.equals(attrsProperty)) {
BeanPropertyMeta pMeta = p.getMeta();
- ClassMeta<?> cMeta = p.getClassMeta();
-
- String key = p.getName();
- Object value = p.getValue();
- Throwable t = p.getThrown();
- if (t != null)
- onBeanGetterException(pMeta, t);
-
- if (canIgnoreValue(cMeta, key, value))
- continue;
-
- Namespace ns = (enableNamespaces && pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace() != elementNs ? pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace() : null);
-
- if (pMeta.isUri() ) {
- out.attrUri(ns, key, value);
- } else if (n.equals(attrsProperty)) {
- if (value instanceof BeanMap) {
- BeanMap<?> bm2 = (BeanMap)value;
- for (BeanPropertyValue p2 : bm2.getValues(true)) {
- String key2 = p2.getName();
- Object value2 = p2.getValue();
- Throwable t2 = p2.getThrown();
- if (t2 != null)
- onBeanGetterException(pMeta, t);
- out.attr(ns, key2, value2);
- }
- } else /* Map */ {
- Map m2 = (Map)value;
- for (Map.Entry e : (Set<Map.Entry>)(m2.entrySet())) {
- out.attr(ns, toString(e.getKey()), e.getValue());
+ if (pMeta.canRead()) {
+ ClassMeta<?> cMeta = p.getClassMeta();
+
+ String key = p.getName();
+ Object value = p.getValue();
+ Throwable t = p.getThrown();
+ if (t != null)
+ onBeanGetterException(pMeta, t);
+
+ if (canIgnoreValue(cMeta, key, value))
+ continue;
+
+ Namespace ns = (enableNamespaces && pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace() != elementNs ? pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace() : null);
+
+ if (pMeta.isUri() ) {
+ out.attrUri(ns, key, value);
+ } else if (n.equals(attrsProperty)) {
+ if (value instanceof BeanMap) {
+ BeanMap<?> bm2 = (BeanMap)value;
+ for (BeanPropertyValue p2 : bm2.getValues(true)) {
+ String key2 = p2.getName();
+ Object value2 = p2.getValue();
+ Throwable t2 = p2.getThrown();
+ if (t2 != null)
+ onBeanGetterException(pMeta, t);
+ out.attr(ns, key2, value2);
+ }
+ } else /* Map */ {
+ Map m2 = (Map)value;
+ for (Map.Entry e : (Set<Map.Entry>)(m2.entrySet())) {
+ out.attr(ns, toString(e.getKey()), e.getValue());
+ }
}
+ } else {
+ out.attr(ns, key, value);
}
- } else {
- out.attr(ns, key, value);
}
}
}
@@ -607,39 +611,41 @@ public class XmlSerializerSession extends WriterSerializerSession {
for (BeanPropertyValue p : lp) {
BeanPropertyMeta pMeta = p.getMeta();
- ClassMeta<?> cMeta = p.getClassMeta();
+ if (pMeta.canRead()) {
+ ClassMeta<?> cMeta = p.getClassMeta();
- String n = p.getName();
- if (n.equals(contentProperty)) {
- content = p.getValue();
- contentType = p.getClassMeta();
- hasContent = true;
- cf = xbm.getContentFormat();
- if (cf.isOneOf(MIXED,MIXED_PWS,TEXT,TEXT_PWS,XMLTEXT))
- isMixed = true;
- if (cf.isOneOf(MIXED_PWS, TEXT_PWS))
- preserveWhitespace = true;
- if (contentType.isCollection() && ((Collection)content).isEmpty())
- hasContent = false;
- else if (contentType.isArray() && Array.getLength(content) == 0)
- hasContent = false;
- } else if (elements.contains(n) || collapsedElements.contains(n) || elements.contains("*") || collapsedElements.contains("*") ) {
- String key = p.getName();
- Object value = p.getValue();
- Throwable t = p.getThrown();
- if (t != null)
- onBeanGetterException(pMeta, t);
-
- if (canIgnoreValue(cMeta, key, value))
- continue;
-
- if (! hasChildren) {
- hasChildren = true;
- out.appendIf(! isCollapsed, '>').nlIf(! isMixed, indent);
- }
+ String n = p.getName();
+ if (n.equals(contentProperty)) {
+ content = p.getValue();
+ contentType = p.getClassMeta();
+ hasContent = true;
+ cf = xbm.getContentFormat();
+ if (cf.isOneOf(MIXED,MIXED_PWS,TEXT,TEXT_PWS,XMLTEXT))
+ isMixed = true;
+ if (cf.isOneOf(MIXED_PWS, TEXT_PWS))
+ preserveWhitespace = true;
+ if (contentType.isCollection() && ((Collection)content).isEmpty())
+ hasContent = false;
+ else if (contentType.isArray() && Array.getLength(content) == 0)
+ hasContent = false;
+ } else if (elements.contains(n) || collapsedElements.contains(n) || elements.contains("*") || collapsedElements.contains("*") ) {
+ String key = p.getName();
+ Object value = p.getValue();
+ Throwable t = p.getThrown();
+ if (t != null)
+ onBeanGetterException(pMeta, t);
+
+ if (canIgnoreValue(cMeta, key, value))
+ continue;
+
+ if (! hasChildren) {
+ hasChildren = true;
+ out.appendIf(! isCollapsed, '>').nlIf(! isMixed, indent);
+ }
- XmlBeanPropertyMeta xbpm = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
- serializeAnything(out, value, cMeta, key, xbpm.getNamespace(), false, xbpm.getXmlFormat(), isMixed, false, pMeta);
+ XmlBeanPropertyMeta xbpm = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
+ serializeAnything(out, value, cMeta, key, xbpm.getNamespace(), false, xbpm.getXmlFormat(), isMixed, false, pMeta);
+ }
}
}
if (! hasContent)
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParserSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParserSession.java
index b494a41..2f33098 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParserSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParserSession.java
@@ -113,7 +113,14 @@ public final class YamlParserSession extends ReaderParserSession {
if (eType == null)
eType = object();
PojoSwap<T,Object> swap = (PojoSwap<T,Object>)eType.getPojoSwap(this);
- ClassMeta<?> sType = swap == null ? eType : swap.getSwapClassMeta(this);
+ BuilderSwap<T,Object> builder = (BuilderSwap<T,Object>)eType.getBuilderSwap(this);
+ ClassMeta<?> sType = null;
+ if (builder != null)
+ sType = builder.getBuilderClassMeta(this);
+ else if (swap != null)
+ sType = swap.getSwapClassMeta(this);
+ else
+ sType = eType;
setCurrentClass(sType);
Object o = null;
@@ -171,6 +178,9 @@ public final class YamlParserSession extends ReaderParserSession {
Collection l = (sType.canCreateNewInstance(outer) ? (Collection)sType.newInstance() : new ObjectList(this));
o = parseIntoCollection2(r, l, sType, pMeta);
}
+ } else if (builder != null) {
+ BeanMap m = toBeanMap(builder.create(this, eType));
+ o = builder.build(this, parseIntoBeanMap2(r, m).getBean(), eType);
} else if (sType.canCreateNewBean(outer)) {
BeanMap m = newBeanMap(outer, sType.getInnerClass());
o = parseIntoBeanMap2(r, m).getBean();
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlSerializerSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlSerializerSession.java
index f299a7a..21436d8 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlSerializerSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlSerializerSession.java
@@ -174,24 +174,26 @@ public class YamlSerializerSession extends WriterSerializerSession {
boolean addComma = false;
for (BeanPropertyValue p : m.getValues(isTrimNulls(), typeName != null ? createBeanTypeNameProperty(m, typeName) : null)) {
BeanPropertyMeta pMeta = p.getMeta();
- ClassMeta<?> cMeta = p.getClassMeta();
- String key = p.getName();
- Object value = p.getValue();
- Throwable t = p.getThrown();
- if (t != null)
- onBeanGetterException(pMeta, t);
+ if (pMeta.canRead()) {
+ ClassMeta<?> cMeta = p.getClassMeta();
+ String key = p.getName();
+ Object value = p.getValue();
+ Throwable t = p.getThrown();
+ if (t != null)
+ onBeanGetterException(pMeta, t);
- if (canIgnoreValue(cMeta, key, value))
- continue;
+ if (canIgnoreValue(cMeta, key, value))
+ continue;
- if (addComma)
- out.append(',').smi(i);
+ if (addComma)
+ out.append(',').smi(i);
- out.cr(i).attr(key).append(':').s(i);
+ out.cr(i).attr(key).append(':').s(i);
- serializeAnything(out, value, cMeta, key, pMeta);
+ serializeAnything(out, value, cMeta, key, pMeta);
- addComma = true;
+ addComma = true;
+ }
}
out.cre(i-1).append('}');
return out;
diff --git a/juneau-doc/src/main/javadoc/overview.html b/juneau-doc/src/main/javadoc/overview.html
index de1b32f..404ba88 100644
--- a/juneau-doc/src/main/javadoc/overview.html
+++ b/juneau-doc/src/main/javadoc/overview.html
@@ -105,6 +105,7 @@
<li><p><a class='doclink' href='#juneau-marshall.BeanIgnoreAnnotation'>@BeanIgnore Annotation</a></p>
<li><p><a class='doclink' href='#juneau-marshall.NamePropertyAnnotation'>@NameProperty Annotation</a></p>
<li><p><a class='doclink' href='#juneau-marshall.ParentPropertyAnnotation'>@ParentProperty Annotation</a></p>
+ <li><p><a class='doclink' href='#juneau-marshall.PojoBuilders'>POJO Builders</a></p>
<li><p><a class='doclink' href='#juneau-marshall.URIs'>URIs</a></p>
<li><p><a class='doclink' href='#juneau-marshall.BeanFilters'>BeanFilters</a></p>
<li><p><a class='doclink' href='#juneau-marshall.InterfaceFilters'>Interface filters</a></p>
@@ -2347,8 +2348,117 @@
</div>
<!-- =================================================================================================== -->
+ <a id="juneau-marshall.PojoBuilders"></a>
+ <h4 class='topic' onclick='toggle(this)'>2.1.7.14 - POJO Builders</h4>
+ <div class='topic'>
+ <p>
+ Juneau parsers can use builders to instantiate POJOs.
+ <br>This is useful in cases where you want to create beans with read-only properties.
+ <br>Note that while it's possible to do this using the {@link org.apache.juneau.annotation.BeanConstructor @BeanConstructor}
+ annotation, using builders can often be cleaner.
+ </p>
+ <p>
+ A typical builder usage is shown below:
+ </p>
+ <p class='bcode'>
+ MyBean b = MyBean.<jsm>create</jsm>().foo(<js>"foo"</js>).bar(123).build();
+ </p>
+ <p>
+ The code for such a builder is shown below:
+ </p>
+ <p class='bcode'>
+ <jk>public class</jk> MyBean {
+
+ <jc>// Read-only properties.</jc>
+ <jk>public final</jk> String <jf>foo</jf>;
+ <jk>public final int</jk> <jf>bar</jf>;
+
+ <jc>// Private constructor.</jc>
+ <jk>private</jk> MyBean(MyBeanBuilder b) {
+ <jk>this</jk>.<jf>foo</jf> = b.foo;
+ <jk>this</jk>.<jf>bar</jf> = b.bar;
+ }
+
+ <jc>// Static method that creates a builder.</jc>
+ <jk>public static</jk> MyBeanBuilder <jsm>create</jsm>() {
+ <jk>return new</jk> MyBeanBuilder();
+ }
+
+ <jc>// Builder class.</jc>
+ <jk>public static class</jk> MyBeanBuilder {
+ <jk>private</jk> String <jf>foo</jf>;
+ <jk>private int</jk> <jf>bar</jf>;
+
+ <jc>// Method that creates the bean.</jc>
+ <jk>public</jk> MyBean build() {
+ <jk>return new</jk> MyBean(<jk>this</jk>);
+ }
+
+ <jc>// Bean property setters.</jc>
+
+ <ja>@BeanProperty</ja>
+ <jk>public</jk> MyBeanBuilder foo(String foo) {
+ <jk>this</jk>.<jf>foo</jf> = foo;
+ <jk>return this</jk>;
+ }
+
+ <ja>@BeanProperty</ja>
+ <jk>public</jk> MyBeanBuilder bar(<jk>int</jk> bar) {
+ <jk>this</jk>.<jf>bar</jf> = bar;
+ <jk>return this</jk>;
+ }
+ }
+ }
+ </p>
+ <p>
+ Builders MUST be beans with one or more writable properties.
+ <br>The bean properties themselves do not need to be readable (e.g. setters without getters).
+ </p>
+ <p>
+ Builders require two parts:
+ </p>
+ <ol>
+ <li>A way to detect and instantiate a builder using reflection.
+ <li>A way to instantiate a POJO from a builder.
+ </ol>
+ <p>
+ The first can be accomplished through any of the following:
+ </p>
+ <ul class='spaced-list'>
+ <li>A static <code>create()</code> method on the POJO class that returns a builder instance.
+ <p class='bcode'>
+ <jk>public static</jk> MyBuilder <jsm>create</jsm>() {...}
+ </p>
+ <li>A public constructor on the POJO class that takes in a single parameter that implements the {@link org.apache.juneau.transform.Builder} interface.
+ <br>The builder class must have a public no-arg constructor.
+ <p class='bcode'>
+ <jk>public</jk> MyPojo(MyBuilder b) {...}
+ </p>
+ <li>A {@link org.apache.juneau.annotation.Builder @Builder} annotation on the POJO class.
+ <br>The builder class must have a public no-arg constructor.
+ <p class='bcode'>
+ <ja>@Builder</ja>(MyBuilder.<jk>class</jk>)
+ <jk>public class</jk> MyPojo {...}
+ </p>
+ </ul>
+ <p>
+ The second can be accomplished through any of the following:
+ </p>
+ <ul class='spaced-list'>
+ <li>The existence of a <code>build()</code> method on the builder class.
+ <p class='bcode'>
+ <jk>public</jk> MyPojo build() {...}
+ </p>
+ <li>The existence of a public constructor on the POJO class that takes in the builder instance.
+ <p class='bcode'>
+ <jk>public</jk> MyPojo(MyBuilder b) {...}
+ </p>
+ </ul>
+ </div>
+
+ <!-- =================================================================================================== -->
<a id="juneau-marshall.URIs"></a>
- <h4 class='topic' onclick='toggle(this)'>2.1.7.14 - URIs</h4>
+ <h4 class='topic' onclick='toggle(this)'>2.1.7.15 - URIs</h4>
<div class='topic'>
<p>
Juneau serializers have sophisticated support for transforming relative URIs to absolute form.
@@ -2477,7 +2587,7 @@
<!-- ======================================================================================================= -->
<a id="juneau-marshall.BeanFilters"></a>
- <h4 class='topic' onclick='toggle(this)'>2.1.7.15 - BeanFilter Class</h4>
+ <h4 class='topic' onclick='toggle(this)'>2.1.7.16 - BeanFilter Class</h4>
<div class='topic'>
<p>
The {@link org.apache.juneau.transform.BeanFilter} class is the programmatic equivalent to the
@@ -2566,7 +2676,7 @@
<!-- =================================================================================================== -->
<a id="juneau-marshall.InterfaceFilters"></a>
- <h4 class='topic' onclick='toggle(this)'>2.1.7.16 - Interface Filters</h4>
+ <h4 class='topic' onclick='toggle(this)'>2.1.7.17 - Interface Filters</h4>
<div class='topic'>
<p>
Occasionally, you may want to limit bean properties to only those defined on a parent class or interface.
@@ -2670,7 +2780,7 @@
<!-- =================================================================================================== -->
<a id="juneau-marshall.StopClasses"></a>
- <h4 class='topic' onclick='toggle(this)'>2.1.7.17 - Stop Classes</h4>
+ <h4 class='topic' onclick='toggle(this)'>2.1.7.18 - Stop Classes</h4>
<div class='topic'>
<p>
Whereas interface filters limit properties defined on child classes, stop filters
@@ -2712,7 +2822,7 @@
<!-- =================================================================================================== -->
<a id="juneau-marshall.BypassSerialization"></a>
- <h4 class='topic' onclick='toggle(this)'>2.1.7.18 - Bypass Serialization using Readers and InputStreams</h4>
+ <h4 class='topic' onclick='toggle(this)'>2.1.7.19 - Bypass Serialization using Readers and InputStreams</h4>
<div class='topic'>
<p>
Juneau serializers treat instances of <code>Readers</code> and <code>InputStreams</code> special by
@@ -3054,7 +3164,8 @@
</p>
<h6 class='figure'>Examples:</h6>
<p class='bcode'>
- ReaderParser p = JsonParser.<jsm>create</jsm>().unbuffered().build();
+ <jc>// If you're calling parse on the same input multiple times, use a session instead of the parser directly.</jc>
+ ReaderParserSession p = JsonParser.<jsm>create</jsm>().unbuffered().build().createSession();
Object x;
Reader r;
@@ -12595,9 +12706,17 @@
<li>
New {@link org.apache.juneau.parser.Parser#PARSER_autoCloseStreams} setting allows input streams and
readers passed into parsers to be automatically closed after parsing.
+ <li>
+ Syntax changed on unswap method on {@link org.apache.juneau.transform.Surrogate} classes.
+ <br>It's now a regular method instead of a static method.
+ <li>
+ {@link org.apache.juneau.annotation.Swap @Swap} annotation can now be used with
+ {@link org.apache.juneau.transform.Surrogate} classes.
+ <li>
+ New support for <a class='doclink' href='#juneau-marshall.PojoBuilders'>POJO Builders</a>.
</ul>
- <h6 class='topic'>juneau-marshall</h6>
+ <h6 class='topic'>juneau-dto</h6>
<ul class='spaced-list'>
<li>
Enhancements to Swagger DTO:
@@ -12614,12 +12733,6 @@
Setter methods that take in beans and collections of beans can now take in
JSON strings.
</ul>
- <li>
- Syntax changed on unswap method on {@link org.apache.juneau.transform.Surrogate} classes.
- <br>It's now a regular method instead of a static method.
- <li>
- {@link org.apache.juneau.annotation.Swap @Swap} annotation can now be used with
- {@link org.apache.juneau.transform.Surrogate} classes.
</ul>
<h6 class='topic'>juneau-rest-server</h6>
@@ -17738,4 +17851,3 @@
</div>
</body>
-
--
To stop receiving notification emails like this one, please contact
jamesbognar@apache.org.