You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2017/02/05 17:48:49 UTC
incubator-juneau git commit: Eliminate support for toObjectMap()
method on classes.
Repository: incubator-juneau
Updated Branches:
refs/heads/master 88d1d38ca -> 21fcc1197
Eliminate support for toObjectMap() method on classes.
Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/21fcc119
Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/21fcc119
Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/21fcc119
Branch: refs/heads/master
Commit: 21fcc1197e3d954ebb0fa60b895f43e347b6b19f
Parents: 88d1d38
Author: JamesBognar <ja...@apache.org>
Authored: Sun Feb 5 12:48:40 2017 -0500
Committer: JamesBognar <ja...@apache.org>
Committed: Sun Feb 5 12:48:40 2017 -0500
----------------------------------------------------------------------
.../java/org/apache/juneau/jena/RdfParser.java | 2 +-
.../org/apache/juneau/jena/RdfSerializer.java | 5 -
.../a/rttests/RoundTripToObjectMapsTest.java | 2 +-
.../juneau/dto/html5/HtmlTemplatesTest.java | 19 +--
.../java/org/apache/juneau/BeanSession.java | 2 +-
.../main/java/org/apache/juneau/ClassMeta.java | 102 ++++++--------
.../java/org/apache/juneau/html/HtmlParser.java | 2 +-
.../org/apache/juneau/html/HtmlSerializer.java | 4 -
.../java/org/apache/juneau/json/JsonParser.java | 2 +-
.../org/apache/juneau/json/JsonSerializer.java | 2 -
.../apache/juneau/msgpack/MsgPackParser.java | 2 +-
.../juneau/msgpack/MsgPackSerializer.java | 2 -
.../apache/juneau/urlencoding/UonParser.java | 2 +-
.../juneau/urlencoding/UonSerializer.java | 2 -
.../juneau/urlencoding/UrlEncodingParser.java | 2 +-
.../urlencoding/UrlEncodingSerializer.java | 2 -
.../java/org/apache/juneau/xml/XmlParser.java | 2 +-
.../apache/juneau/xml/XmlSchemaSerializer.java | 6 +-
.../org/apache/juneau/xml/XmlSerializer.java | 10 +-
juneau-core/src/main/javadoc/overview.html | 141 ++++++++++++++++++-
20 files changed, 206 insertions(+), 107 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/21fcc119/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParser.java
----------------------------------------------------------------------
diff --git a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParser.java b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParser.java
index 26a9697..b18ab15 100644
--- a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParser.java
+++ b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParser.java
@@ -329,7 +329,7 @@ public class RdfParser extends ReaderParser {
return null;
Map m = new ObjectMap(session);
parseIntoMap(session, r, m, eType.getKeyType(), eType.getValueType());
- o = sType.newInstanceFromObjectMap(outer, (ObjectMap)m);
+ o = sType.newInstanceFromObjectMap(session, outer, (ObjectMap)m);
} else if (sType.canCreateNewBean(outer)) {
Resource r = n.asResource();
if (session.wasAlreadyProcessed(r))
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/21fcc119/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializer.java b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializer.java
index c579001..461decb 100644
--- a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializer.java
+++ b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializer.java
@@ -225,11 +225,6 @@ public class RdfSerializer extends WriterSerializer {
serializeMap(session, m2, (Resource)n, sType);
}
- } else if (sType.hasToObjectMapMethod()) {
- Map m2 = sType.toObjectMap(o);
- n = m.createResource();
- serializeMap(session, m2, (Resource)n, sType);
-
} else if (sType.isBean()) {
BeanMap bm = session.toBeanMap(o);
Object uri = null;
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/21fcc119/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripToObjectMapsTest.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripToObjectMapsTest.java b/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripToObjectMapsTest.java
index 4a9ada9..3c7b33a 100755
--- a/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripToObjectMapsTest.java
+++ b/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripToObjectMapsTest.java
@@ -69,7 +69,7 @@ public class RoundTripToObjectMapsTest extends RoundTripTest {
this.f1 = m.getString("f1");
this.f2 = m.getInt("f2");
}
- public ObjectMap toObjectMap() {
+ public ObjectMap swap(BeanSession session) {
return new ObjectMap().append("f1",f1).append("f2",f2);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/21fcc119/juneau-core-test/src/test/java/org/apache/juneau/dto/html5/HtmlTemplatesTest.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/dto/html5/HtmlTemplatesTest.java b/juneau-core-test/src/test/java/org/apache/juneau/dto/html5/HtmlTemplatesTest.java
index 93073bc..6606228 100644
--- a/juneau-core-test/src/test/java/org/apache/juneau/dto/html5/HtmlTemplatesTest.java
+++ b/juneau-core-test/src/test/java/org/apache/juneau/dto/html5/HtmlTemplatesTest.java
@@ -48,11 +48,11 @@ public class HtmlTemplatesTest {
return Arrays.asList(new Object[][] {
{
"FormTemplate-1",
- new FormTemplate("myaction", "foo", "bar"),
- "<form action='myaction'><input type='text' name='v1' value='foo'/><input type='text' name='v2' value='bar'/></form>",
- "<form action='myaction'><input type='text' name='v1' value='foo'/><input type='text' name='v2' value='bar'/></form>\n",
- "<form action='myaction'><input type='text' name='v1' value='foo'/><input type='text' name='v2' value='bar'/></form>",
- "<form action='myaction'><input type='text' name='v1' value='foo'/><input type='text' name='v2' value='bar'/></form>\n",
+ new FormTemplate("myaction", 123, true),
+ "<form action='myaction'><input type='text' name='v1' value='123'/><input type='text' name='v2' value='true'/></form>",
+ "<form action='myaction'><input type='text' name='v1' value='123'/><input type='text' name='v2' value='true'/></form>\n",
+ "<form action='myaction'><input type='text' name='v1' value='123'/><input type='text' name='v2' value='true'/></form>",
+ "<form action='myaction'><input type='text' name='v1' value='123'/><input type='text' name='v2' value='true'/></form>\n",
},
});
}
@@ -62,15 +62,16 @@ public class HtmlTemplatesTest {
public static class FormTemplate {
private String action;
- private String value1, value2;
+ private int value1;
+ private boolean value2;
public FormTemplate(Form f) {
this.action = f.getAttr("action");
- this.value1 = ((Input)f.getChildren().get(0)).getAttr("value");
- this.value2 = ((Input)f.getChildren().get(1)).getAttr("value");
+ this.value1 = f.getChild(Input.class, 0).getAttr(int.class, "value");
+ this.value2 = f.getChild(Input.class, 1).getAttr(boolean.class, "value");
}
- public FormTemplate(String action, String value1, String value2) {
+ public FormTemplate(String action, int value1, boolean value2) {
this.action = action;
this.value1 = value1;
this.value2 = value2;
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/21fcc119/juneau-core/src/main/java/org/apache/juneau/BeanSession.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanSession.java b/juneau-core/src/main/java/org/apache/juneau/BeanSession.java
index 2f01534..e7a405d 100644
--- a/juneau-core/src/main/java/org/apache/juneau/BeanSession.java
+++ b/juneau-core/src/main/java/org/apache/juneau/BeanSession.java
@@ -555,7 +555,7 @@ public class BeanSession extends Session {
return newBeanMap(tc).load((Map<?,?>) value).getBean();
if (type.canCreateNewInstanceFromObjectMap(outer) && value instanceof ObjectMap)
- return type.newInstanceFromObjectMap(outer, (ObjectMap)value);
+ return type.newInstanceFromObjectMap(this, outer, (ObjectMap)value);
if (type.canCreateNewInstanceFromNumber(outer) && value instanceof Number)
return type.newInstanceFromNumber(this, outer, (Number)value);
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/21fcc119/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java b/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java
index fe22629..74027e6 100644
--- a/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java
+++ b/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java
@@ -67,14 +67,13 @@ public final class ClassMeta<T> implements Type {
private final Constructor<T>
stringConstructor, // The X(String) constructor (if it has one).
numberConstructor, // The X(Number) constructor (if it has one).
- swapConstructor, // The X(Swappable) constructor (if it has one).
- objectMapConstructor; // The X(ObjectMap) constructor (if it has one).
+ swapConstructor; // The X(Swappable) constructor (if it has one).
private final Class<?>
swapMethodType, // The class type of the object in the number constructor.
numberConstructorType;
private final Method
- toObjectMapMethod, // The toObjectMap() method (if it has one).
swapMethod, // The swap() method (if it has one).
+ unswapMethod, // The unswap() method (if it has one).
namePropertyMethod, // The method to set the name on an object (if it has one).
parentPropertyMethod; // The method to set the parent on an object (if it has one).
private final boolean
@@ -139,7 +138,7 @@ public final class ClassMeta<T> implements Type {
// We always immediately add this class meta to the bean context cache so that we can resolve recursive references.
if (beanContext != null && beanContext.cmCache != null)
- beanContext.cmCache.putIfAbsent(innerClass, this);
+ beanContext.cmCache.put(innerClass, this);
this.implClass = implClass;
this.childPojoSwaps = childPojoSwaps;
@@ -151,14 +150,13 @@ public final class ClassMeta<T> implements Type {
boolean _isDelegate = false;
Method
_fromStringMethod = null,
- _toObjectMapMethod = null,
_swapMethod = null,
+ _unswapMethod = null,
_parentPropertyMethod = null,
_namePropertyMethod = null;
Constructor<T>
_noArgConstructor = null,
_stringConstructor = null,
- _objectMapConstructor = null,
_swapConstructor = null,
_numberConstructor = null;
Class<?>
@@ -272,17 +270,13 @@ public final class ClassMeta<T> implements Type {
_fromStringMethod = LocaleAsString.class.getMethod("fromString", String.class);
} catch (NoSuchMethodException e1) {}
- // Find toObjectMap() method if present.
+ // Find swap() method if present.
for (Method m : c.getMethods()) {
if (isPublic(m) && isNotDeprecated(m) && ! isStatic(m)) {
String mName = m.getName();
- if (mName.equals("toObjectMap")) {
- if (m.getParameterTypes().length == 0 && m.getReturnType() == ObjectMap.class) {
- _toObjectMapMethod = m;
- break;
- }
- } else if (mName.equals("swap")) {
- if (m.getParameterTypes().length == 1 && m.getParameterTypes()[0] == BeanSession.class) {
+ if (mName.equals("swap")) {
+ Class<?>[] pt = m.getParameterTypes();
+ if (pt.length == 1 && pt[0] == BeanSession.class) {
_swapMethod = m;
_swapMethodType = m.getReturnType();
break;
@@ -290,6 +284,21 @@ public final class ClassMeta<T> implements Type {
}
}
}
+ // Find unswap() method if present.
+ if (_swapMethod != null) {
+ for (Method m : c.getMethods()) {
+ if (isPublic(m) && isNotDeprecated(m) && isStatic(m)) {
+ String mName = m.getName();
+ if (mName.equals("unswap")) {
+ Class<?>[] pt = m.getParameterTypes();
+ if (pt.length == 2 && pt[0] == BeanSession.class && pt[1] == _swapMethodType) {
+ _unswapMethod = m;
+ break;
+ }
+ }
+ }
+ }
+ }
// Find @NameProperty and @ParentProperty methods if present.
for (Method m : c.getDeclaredMethods()) {
@@ -316,8 +325,6 @@ public final class ClassMeta<T> implements Type {
Class<?> arg = args[(isMemberClass ? 1 : 0)];
if (arg == String.class)
_stringConstructor = cs;
- else if (ObjectMap.class.isAssignableFrom(arg))
- _objectMapConstructor = cs;
else if (_swapMethodType != null && _swapMethodType.isAssignableFrom(arg))
_swapConstructor = cs;
else if (_cc != NUMBER && (Number.class.isAssignableFrom(arg) || (arg.isPrimitive() && (arg == int.class || arg == short.class || arg == long.class || arg == float.class || arg == double.class)))) {
@@ -388,11 +395,14 @@ public final class ClassMeta<T> implements Type {
beanFilter = findBeanFilter();
if (_swapMethod != null) {
+ final Method fSwapMethod = _swapMethod;
+ final Method fUnswapMethod = _unswapMethod;
+ final Constructor<T> fSwapConstructor = _swapConstructor;
_pojoSwap = new PojoSwap<T,Object>(c, _swapMethod.getReturnType()) {
@Override
public Object swap(BeanSession session, Object o) throws SerializeException {
try {
- return swapMethod.invoke(o, session);
+ return fSwapMethod.invoke(o, session);
} catch (Exception e) {
throw new SerializeException(e);
}
@@ -400,8 +410,10 @@ public final class ClassMeta<T> implements Type {
@Override
public T unswap(BeanSession session, Object f, ClassMeta<?> hint) throws ParseException {
try {
- if (swapConstructor != null)
- return swapConstructor.newInstance(f);
+ if (fUnswapMethod != null)
+ return (T)fUnswapMethod.invoke(null, session, f);
+ if (fSwapConstructor != null)
+ return fSwapConstructor.newInstance(f);
return super.unswap(session, f, hint);
} catch (Exception e) {
throw new ParseException(e);
@@ -484,14 +496,13 @@ public final class ClassMeta<T> implements Type {
this.cc = _cc;
this.isDelegate = _isDelegate;
this.fromStringMethod = _fromStringMethod;
- this.toObjectMapMethod = _toObjectMapMethod;
this.swapMethod = _swapMethod;
+ this.unswapMethod = _unswapMethod;
this.swapMethodType = _swapMethodType;
this.parentPropertyMethod = _parentPropertyMethod;
this.namePropertyMethod =_namePropertyMethod;
this.noArgConstructor = _noArgConstructor;
this.stringConstructor = _stringConstructor;
- this.objectMapConstructor =_objectMapConstructor;
this.swapConstructor = _swapConstructor;
this.numberConstructor = _numberConstructor;
this.numberConstructorType = _numberConstructorType;
@@ -529,11 +540,10 @@ public final class ClassMeta<T> implements Type {
this.stringConstructor = mainType.stringConstructor;
this.numberConstructor = mainType.numberConstructor;
this.swapConstructor = mainType.swapConstructor;
- this.objectMapConstructor = mainType.objectMapConstructor;
this.swapMethodType = mainType.swapMethodType;
this.numberConstructorType = mainType.numberConstructorType;
- this.toObjectMapMethod = mainType.toObjectMapMethod;
this.swapMethod = mainType.swapMethod;
+ this.unswapMethod = mainType.unswapMethod;
this.namePropertyMethod = mainType.namePropertyMethod;
this.parentPropertyMethod = mainType.parentPropertyMethod;
this.isDelegate = mainType.isDelegate;
@@ -1204,24 +1214,16 @@ public final class ClassMeta<T> implements Type {
* @return <jk>true</jk> if this class has a no-arg constructor or invocation handler.
*/
public boolean canCreateNewInstanceFromObjectMap(Object outer) {
- if (objectMapConstructor != null) {
+ // TODO - Get rid of?
+ if (swapMethodType == ObjectMap.class && (swapConstructor != null || unswapMethod != null)) {
if (isMemberClass)
- return outer != null && objectMapConstructor.getParameterTypes()[0] == outer.getClass();
+ return outer != null && swapConstructor.getParameterTypes()[0] == outer.getClass();
return true;
}
return false;
}
/**
- * Returns <jk>true</jk> if this class has an <code>ObjectMap toObjectMap()</code> method.
- *
- * @return <jk>true</jk> if class has a <code>toObjectMap()</code> method.
- */
- public boolean hasToObjectMapMethod() {
- return toObjectMapMethod != null;
- }
-
- /**
* Returns the method annotated with {@link NameProperty @NameProperty}.
*
* @return The method annotated with {@link NameProperty @NameProperty} or <jk>null</jk> if method does not exist.
@@ -1240,23 +1242,6 @@ public final class ClassMeta<T> implements Type {
}
/**
- * Converts an instance of this class to an {@link ObjectMap}.
- *
- * @param t The object to convert to a map.
- * @return The converted object, or <jk>null</jk> if method does not have a <code>toObjectMap()</code> method.
- * @throws BeanRuntimeException Thrown by <code>toObjectMap()</code> method invocation.
- */
- public ObjectMap toObjectMap(Object t) throws BeanRuntimeException {
- try {
- if (toObjectMapMethod != null)
- return (ObjectMap)toObjectMapMethod.invoke(t);
- return null;
- } catch (Exception e) {
- throw new BeanRuntimeException(e);
- }
- }
-
- /**
* Returns the reason why this class is not a bean, or <jk>null</jk> if it is a bean.
*
* @return The reason why this class is not a bean, or <jk>null</jk> if it is a bean.
@@ -1363,6 +1348,7 @@ public final class ClassMeta<T> implements Type {
* <li><code><jk>public</jk> T(ObjectMap in);</code>
* </ul>
*
+ * @param session The current bean session.
* @param outer The outer class object for non-static member classes. Can be <jk>null</jk> for non-member or static classes.
* @param arg The input argument value.
* @return A new instance of the object.
@@ -1372,12 +1358,16 @@ public final class ClassMeta<T> implements Type {
* does not have one of the methods described above.
* @throws InvocationTargetException If the underlying constructor throws an exception.
*/
- public T newInstanceFromObjectMap(Object outer, ObjectMap arg) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException {
- Constructor<T> c = objectMapConstructor;
- if (c != null) {
+ @SuppressWarnings("unchecked")
+ public T newInstanceFromObjectMap(BeanSession session, Object outer, ObjectMap arg) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException {
+ // TODO - Get rid of?
+ if (swapConstructor != null) {
if (isMemberClass)
- return c.newInstance(outer, arg);
- return c.newInstance(arg);
+ return swapConstructor.newInstance(outer, arg);
+ return swapConstructor.newInstance(arg);
+ }
+ if (unswapMethod != null) {
+ return (T)unswapMethod.invoke(null, session, arg);
}
throw new InstantiationError("No map constructor method found for class '"+getInnerClass().getName()+"'");
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/21fcc119/juneau-core/src/main/java/org/apache/juneau/html/HtmlParser.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlParser.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlParser.java
index 1a88144..7757386 100644
--- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlParser.java
+++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlParser.java
@@ -177,7 +177,7 @@ public final class HtmlParser extends XmlParser {
} else if (sType.canCreateNewInstanceFromObjectMap(outer)) {
ObjectMap m = new ObjectMap(session);
parseIntoMap(session, r, m, string(), object(), pMeta);
- o = sType.newInstanceFromObjectMap(outer, m);
+ o = sType.newInstanceFromObjectMap(session, outer, m);
} else if (sType.canCreateNewBean(outer)) {
BeanMap m = session.newBeanMap(outer, sType.getInnerClass());
o = parseIntoBean(session, r, m).getBean();
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/21fcc119/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java
index 995015a..8828409 100644
--- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java
@@ -261,10 +261,6 @@ public class HtmlSerializer extends XmlSerializer {
out.sTag("boolean").append(o).eTag("boolean");
cr = CR_SIMPLE;
- } else if (sType.hasToObjectMapMethod()) {
- out.nlIf(! isRoot);
- serializeMap(session, out, sType.toObjectMap(o), sType, null, null, typeName, pMeta);
-
} else if (sType.isMap() || (wType != null && wType.isMap())) {
out.nlIf(! isRoot);
if (o instanceof BeanMap)
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/21fcc119/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java b/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java
index e141dd0..3289abe 100644
--- a/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java
+++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java
@@ -166,7 +166,7 @@ public final class JsonParser extends ReaderParser {
} else if (sType.canCreateNewInstanceFromObjectMap(outer)) {
ObjectMap m = new ObjectMap(session);
parseIntoMap2(session, r, m, string(), object(), pMeta);
- o = sType.newInstanceFromObjectMap(outer, m);
+ o = sType.newInstanceFromObjectMap(session, outer, m);
} else if (sType.canCreateNewBean(outer)) {
BeanMap m = session.newBeanMap(outer, sType.getInnerClass());
o = parseIntoBeanMap2(session, r, m).getBean();
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/21fcc119/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java
index 43ba2b5..3559590 100644
--- a/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java
@@ -200,8 +200,6 @@ public class JsonSerializer extends WriterSerializer {
out.append("null");
else if (sType.isNumber() || sType.isBoolean())
out.append(o);
- else if (sType.hasToObjectMapMethod())
- serializeMap(session, out, sType.toObjectMap(o), sType);
else if (sType.isBean())
serializeBeanMap(session, out, session.toBeanMap(o), addTypeProperty);
else if (sType.isUri() || (pMeta != null && pMeta.isUri()))
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/21fcc119/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java
index a3f731a..acb5a3c 100644
--- a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java
+++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java
@@ -109,7 +109,7 @@ public final class MsgPackParser extends InputStreamParser {
ObjectMap m = new ObjectMap(session);
for (int i = 0; i < length; i++)
m.put(parseAnything(session, string(), is, outer, pMeta), parseAnything(session, object(), is, m, pMeta));
- o = sType.newInstanceFromObjectMap(outer, m);
+ o = sType.newInstanceFromObjectMap(session, outer, m);
} else if (sType.canCreateNewBean(outer)) {
if (dt == MAP) {
BeanMap m = session.newBeanMap(outer, sType.getInnerClass());
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/21fcc119/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java
index 93fa938..4e4ed0d 100644
--- a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java
@@ -91,8 +91,6 @@ public class MsgPackSerializer extends OutputStreamSerializer {
out.appendBoolean((Boolean)o);
else if (sType.isNumber())
out.appendNumber((Number)o);
- else if (sType.hasToObjectMapMethod())
- serializeMap(session, out, sType.toObjectMap(o), sType);
else if (sType.isBean())
serializeBeanMap(session, out, session.toBeanMap(o), addTypeProperty);
else if (sType.isUri() || (pMeta != null && pMeta.isUri()))
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/21fcc119/juneau-core/src/main/java/org/apache/juneau/urlencoding/UonParser.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UonParser.java b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UonParser.java
index 79feb29..c6a3b97 100644
--- a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UonParser.java
+++ b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UonParser.java
@@ -157,7 +157,7 @@ public class UonParser extends ReaderParser {
} else if (sType.canCreateNewInstanceFromObjectMap(outer)) {
ObjectMap m = new ObjectMap(session);
parseIntoMap(session, r, m, string(), object(), pMeta);
- o = sType.newInstanceFromObjectMap(outer, m);
+ o = sType.newInstanceFromObjectMap(session, outer, m);
} else if (sType.canCreateNewBean(outer)) {
BeanMap m = session.newBeanMap(outer, sType.getInnerClass());
m = parseIntoBeanMap(session, r, m);
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/21fcc119/juneau-core/src/main/java/org/apache/juneau/urlencoding/UonSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UonSerializer.java b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UonSerializer.java
index c65f674..9c752f2 100644
--- a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UonSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UonSerializer.java
@@ -280,8 +280,6 @@ public class UonSerializer extends WriterSerializer {
// '\0' characters are considered null.
if (o == null || (sType.isChar() && ((Character)o).charValue() == 0))
out.appendObject(null, false, false, isTop);
- else if (sType.hasToObjectMapMethod())
- serializeMap(session, out, sType.toObjectMap(o), eType);
else if (sType.isBean())
serializeBeanMap(session, out, session.toBeanMap(o), addTypeProperty);
else if (sType.isUri() || (pMeta != null && pMeta.isUri()))
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/21fcc119/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java
index fac17c9..acb6827 100644
--- a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java
+++ b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java
@@ -91,7 +91,7 @@ public class UrlEncodingParser extends UonParser {
} else if (sType.canCreateNewInstanceFromObjectMap(outer)) {
ObjectMap m = new ObjectMap(session);
parseIntoMap(session, r, m, string(), object());
- o = sType.newInstanceFromObjectMap(outer, m);
+ o = sType.newInstanceFromObjectMap(session, outer, m);
} else if (sType.canCreateNewBean(outer)) {
BeanMap m = session.newBeanMap(outer, sType.getInnerClass());
m = parseIntoBeanMap(session, r, m);
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/21fcc119/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializer.java b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializer.java
index 99862d0..e96ef9b 100644
--- a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializer.java
@@ -249,8 +249,6 @@ public class UrlEncodingSerializer extends UonSerializer {
serializeBeanMap(session, out, (BeanMap)o, addTypeProperty);
else
serializeMap(session, out, (Map)o, sType);
- } else if (sType.hasToObjectMapMethod()) {
- serializeMap(session, out, sType.toObjectMap(o), sType);
} else if (sType.isBean()) {
serializeBeanMap(session, out, session.toBeanMap(o), addTypeProperty);
} else if (sType.isCollection()) {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/21fcc119/juneau-core/src/main/java/org/apache/juneau/xml/XmlParser.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlParser.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlParser.java
index b5ec875..0e39e89 100644
--- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlParser.java
+++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlParser.java
@@ -144,7 +144,7 @@ public class XmlParser extends ReaderParser {
} else if (sType.canCreateNewInstanceFromObjectMap(outer)) {
ObjectMap m = new ObjectMap(session);
parseIntoMap(session, r, m, string(), object(), pMeta);
- o = sType.newInstanceFromObjectMap(outer, m);
+ o = sType.newInstanceFromObjectMap(session, outer, m);
} else if (sType.canCreateNewBean(outer)) {
if (sType.getExtendedMeta(XmlClassMeta.class).getFormat() == COLLAPSED) {
String fieldName = r.getLocalName();
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/21fcc119/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java
index 3d9782e..35effd0 100644
--- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java
@@ -341,7 +341,7 @@ public class XmlSchemaSerializer extends XmlSerializer {
w.cTag().nl();
- if (! (cm.isMapOrBean() || cm.hasToObjectMapMethod() || cm.isCollectionOrArray() || (cm.isAbstract() && ! cm.isNumber()) || cm.isObject())) {
+ if (! (cm.isMapOrBean() || cm.isCollectionOrArray() || (cm.isAbstract() && ! cm.isNumber()) || cm.isObject())) {
w.oTag(i+1, "attribute").attr("name", session.getBeanTypePropertyName()).attr("type", "string").ceTag().nl();
} else {
@@ -479,7 +479,7 @@ public class XmlSchemaSerializer extends XmlSerializer {
}
//----- Map -----
- } else if (cm.isMap() || cm.hasToObjectMapMethod() || cm.isAbstract() || cm.isObject()) {
+ } else if (cm.isMap() || cm.isAbstract() || cm.isObject()) {
w.sTag(i+1, "sequence").nl();
w.oTag(i+2, "any")
.attr("processContents", "skip")
@@ -512,7 +512,7 @@ public class XmlSchemaSerializer extends XmlSerializer {
name = "number";
else if (cm.isCollectionOrArray())
name = "array";
- else if (! (cm.isMapOrBean() || cm.hasToObjectMapMethod() || cm.isCollectionOrArray() || cm.isObject() || cm.isAbstract()))
+ else if (! (cm.isMapOrBean() || cm.isCollectionOrArray() || cm.isObject() || cm.isAbstract()))
name = "string";
else
name = "object";
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/21fcc119/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java
index 006b32d..e8e0bbf 100644
--- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java
@@ -373,7 +373,7 @@ public class XmlSerializer extends WriterSerializer {
type = NUMBER;
} else if (sType.isBoolean()) {
type = BOOLEAN;
- } else if (sType.isMapOrBean() || sType.hasToObjectMapMethod()) {
+ } else if (sType.isMapOrBean()) {
isCollapsed = sType.getExtendedMeta(XmlClassMeta.class).getFormat() == COLLAPSED;
type = OBJECT;
} else if (sType.isCollectionOrArray()) {
@@ -406,7 +406,7 @@ public class XmlSerializer extends WriterSerializer {
}
// Do we need a carriage return after the start tag?
- boolean cr = o != null && (sType.isMapOrBean() || sType.isCollectionOrArray() || sType.hasToObjectMapMethod()) && ! isMixed;
+ boolean cr = o != null && (sType.isMapOrBean() || sType.isCollectionOrArray()) && ! isMixed;
String en = elementName;
if (en == null) {
@@ -443,10 +443,10 @@ public class XmlSerializer extends WriterSerializer {
o = sType.getPrimitiveDefault();
}
- if (o != null && ! (sType.isMapOrBean() || sType.hasToObjectMapMethod()))
+ if (o != null && ! (sType.isMapOrBean()))
out.append('>');
- if (cr && ! (sType.isMapOrBean() || sType.hasToObjectMapMethod()))
+ if (cr && ! (sType.isMapOrBean()))
out.nl();
}
@@ -468,8 +468,6 @@ public class XmlSerializer extends WriterSerializer {
rc = serializeBeanMap(session, out, (BeanMap)o, elementNamespace, isCollapsed, isMixed);
else
rc = serializeMap(session, out, (Map)o, sType, eType.getKeyType(), eType.getValueType(), isMixed);
- } else if (sType.hasToObjectMapMethod()) {
- rc = serializeMap(session, out, sType.toObjectMap(o), sType, null, null, isMixed);
} else if (sType.isBean()) {
rc = serializeBeanMap(session, out, session.toBeanMap(o), elementNamespace, isCollapsed, isMixed);
} else if (sType.isCollection() || (wType != null && wType.isCollection())) {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/21fcc119/juneau-core/src/main/javadoc/overview.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/javadoc/overview.html b/juneau-core/src/main/javadoc/overview.html
index 10431b6..b91970d 100644
--- a/juneau-core/src/main/javadoc/overview.html
+++ b/juneau-core/src/main/javadoc/overview.html
@@ -77,6 +77,7 @@
<li><p><a class='doclink' href='#Core.Transforms'>Transforms</a></p>
<ol>
<li><p><a class='doclink' href='#Core.PojoSwaps'>PojoSwaps</a></p>
+ <li><p><a class='doclink' href='#Core.SwapMethods'>Swap methods</a></p>
<li><p><a class='doclink' href='#Core.BeanFilters'>BeanFilters and @Bean annotations</a></p>
</ol>
<li><p><a class='doclink' href='#Core.BeanDictionaries'>Bean Name and Dictionaries</a></p>
@@ -742,8 +743,131 @@
</div>
<!-- ======================================================================================================== -->
+ <a id="Core.SwapMethods"></a>
+ <h4 class='topic' onclick='toggle(this)'>2.6.2 - Swap methods</h4>
+ <div class='topic'>
+ <p>
+ Various methods can be defined on a class directly to affect how it gets serialized.
+ </p>
+ <p>
+ Objects normally serialized to <code>Strings</code> can be parsed back into their original objects by implementing
+ one of the following methods on the class:
+ </p>
+ <ul>
+ <li><code><jk>public static</jk> T fromString(String)</code> method.<br>
+ Any of the following method names also work:
+ <ul>
+ <li><code>valueOf(String)</code>
+ <li><code>parse(String)</code>
+ <li><code>parseString(String)</code>
+ <li><code>forName(String)</code>
+ <li><code>forString(String)</code>
+ </ul>
+ <li><code><jk>public</jk> T(String)</code> constructor.
+ </ul>
+ <p>
+ Note that these methods cover conversion from several built-in Java types, meaning these can be constructed directly by the parser from strings:
+ </p>
+ <ul>
+ <li><code>fromString(String)</code> - {@link java.util.UUID}
+ <li><code>valueOf(String)</code> - {@link java.lang.Boolean}, {@link java.lang.Byte}, {@link java.lang.Double}, {@link java.lang.Float},
+ {@link java.lang.Integer}, {@link java.lang.Long}, {@link java.lang.Short}, {@link java.sql.Date}, {@link java.sql.Time}, {@link java.sql.Timestamp}
+ <li><code>parse(String)</code> - {@link java.text.DateFormat}, {@link java.text.MessageFormat}, {@link java.text.NumberFormat}, {@link java.util.Date}, {@link java.util.logging.Level}
+ <li><code>parseString(String)</code> - {@link javax.xml.bind.DatatypeConverter}
+ <li><code>forName(String)</code> - {@link java.lang.Class}
+ </ul>
+ <p>
+ If you want to force a bean-like class to be serialized as a string, you can use the {@link org.apache.juneau.annotation.BeanIgnore @BeanIgnore}
+ annotation on the class to force it to be serialized to a string using the <code>toString()</code> method.
+ </p>
+ <p>
+ Serializing to other intermediate objects can be accomplished by defining a swap method directly on the class:
+ </p>
+ <ul>
+ <li><code><jk>public</jk> X swap(BeanSession)</code> method, where <code>X</code> is any serializable object.
+ </ul>
+ <p>
+ The <code>BeanSession</code> parameter allows you access to various information about the current serialization session.
+ For example, you could provide customized results based on the media type being produced ({@link org.apache.juneau.BeanSession#getMediaType()}).
+ </p>
+ <p>
+ The following example shows how an HTML5 form template object can be created that gets serialized as a populated HTML5 {@link org.apache.juneau.dto.html5.Form} bean.
+ </p>
+ <p class='bcode'>
+ <jk>import static</jk> org.apache.juneau.dto.html5.HtmlBuilder.*;
+
+ <jd>/**
+ * A simple HTML form template whose serialized form is an HTML5 Form object.
+ */</jd>
+ <jk>public class</jk> FormTemplate {
+
+ <jk>private</jk> String <jf>action</jf>;
+ <jk>private int</jk> <jf>value1</jf>;
+ <jk>private boolean</jk> <jf>value2</jf>;
+
+ <jk>public</jk> FormTemplate(String action, <jk>int</jk> value1, <jk>boolean</jk> value2) {
+ <jk>this</jk>.<jf>action</jf> = action;
+ <jk>this</jk>.<jf>value1</jf> = value1;
+ <jk>this</jk>.<jf>value2</jf> = value2;
+ }
+
+ <jk>public</jk> Form swap(BeanSession session) {
+ <jk>return</jk> <jsm>form</jsm>(<jf>action</jf>,
+ <jsm>input</jsm>(<js>"text"</js>).name(<js>"v1"</js>).value(<jf>value1</jf>),
+ <jsm>input</jsm>(<js>"text"</js>).name(<js>"v2"</js>).value(<jf>value2</jf>)
+ );
+ }
+ }
+ </p>
+ <p>
+ Swapped objects can be converted back into their original form by the parsers by specifying one of the following methods:
+ </p>
+ <ul>
+ <li><code><jk>public static</jk> T unswap(BeanSession, X)</code> method where <code>X</code> is the swap class type.
+ <li><code><jk>public</jk> T(X)</code> constructor where <code>X</code> is the swap class type.
+ </ul>
+ <p>
+ The following shows how our form template class can be modified to allow the parsers to reconstruct our original object:
+ </p>
+ <p class='bcode'>
+ <jk>import static</jk> org.apache.juneau.dto.html5.HtmlBuilder.*;
+
+ <jd>/**
+ * A simple HTML form template whose serialized form is an HTML5 Form object.
+ * This time with parsing support.
+ */</jd>
+ <ja>@Bean</ja>(beanDictionary=HtmlBeanDictionary.<jk>class</jk>)
+ <jk>public class</jk> FormTemplate {
+
+ <jk>private</jk> String <jf>action</jf>;
+ <jk>private int</jk> <jf>value1</jf>;
+ <jk>private boolean</jk> <jf>value2</jf>;
+
+ <jc>// Our 'unswap' constructor</jc>
+ <jk>public</jk> FormTemplate(Form f) {
+ <jk>this</jk>.<jf>action</jf> = f.getAttr(<js>"action"</js>);
+ <jk>this</jk>.<jf>value1</jf> = f.getChild(Input.<jk>class</jk>, 0).getAttr(<jk>int</jk>.<jk>class</jk>, <js>"value"</js>);
+ <jk>this</jk>.<jf>value2</jf> = f.getChild(Input.<jk>class</jk>, 1).getAttr(<jk>boolean</jk>.<jk>class</jk>, <js>"value"</js>);
+ }
+
+ <jk>public</jk> FormTemplate(String action, <jk>int</jk> value1, <jk>boolean</jk> value2) {
+ <jk>this</jk>.<jf>action</jf> = action;
+ <jk>this</jk>.<jf>value1</jf> = value1;
+ <jk>this</jk>.<jf>value2</jf> = value2;
+ }
+
+ <jk>public</jk> Form swap(BeanSession session) {
+ <jk>return</jk> <jsm>form</jsm>(<jf>action</jf>,
+ <jsm>input</jsm>(<js>"text"</js>).name(<js>"v1"</js>).value(<jf>value1</jf>),
+ <jsm>input</jsm>(<js>"text"</js>).name(<js>"v2"</js>).value(<jf>value2</jf>)
+ );
+ }
+ }
+ </div>
+
+ <!-- ======================================================================================================== -->
<a id="Core.BeanFilters"></a>
- <h4 class='topic' onclick='toggle(this)'>2.6.2 - BeanFilters and @Bean annotations</h4>
+ <h4 class='topic' onclick='toggle(this)'>2.6.3 - BeanFilters and @Bean annotations</h4>
<div class='topic'>
<p>
{@link org.apache.juneau.transform.BeanFilter BeanFilters} are used to control aspects of how beans are handled during serialization and parsing.
@@ -1127,10 +1251,10 @@
<tr class='light bb' style='background-color:lightyellow'>
<td style='text-align:center'>5b</td>
<td>
- <b>Objects with standardized <code>static T valueOf(ObjectMap)</code>/<code>static T fromObjectMap(ObjectMap)</code> methods, or constructors with an <code>ObjectMap</code> argument,
- and having a <code>toObjectMap()</code> method.</b><br>
- During serialization, objects are converted to {@link org.apache.juneau.ObjectMap ObjectMaps} using the <code>toObjectMap()</code> method.
- During parsing, maps are converted to objects using one of these static methods or constructors.
+ <b>Objects with standardized <code>Object swap(BeanSession)</code>/<code>static T unswap(BeanSession,Object)</code> methods, or constructors with an <code>Object</code> argument
+ where the objects are any object on this list.</b><br>
+ During serialization, normal objects are converted to swapped objects using the <code>swap()</code> method.
+ During parsing, swapped objects are converted to normal objects using the static method or constructor.
</td>
<td> </td>
<td style='background-color:lightgreen;text-align:center'><b>yes</b></td>
@@ -5344,6 +5468,9 @@
<li>{@link org.apache.juneau.transform.PojoSwap#swap(BeanSession,Object)}
<li>{@link org.apache.juneau.transform.PojoSwap#unswap(BeanSession,Object,ClassMeta)}
</ul>
+ <li>Replaced support for <code>toObjectMap()</code> and <code>fromObjectMap()/T(ObjectMap)</code> methods with
+ generalized <code>swap(BeanSession)</code>/<code>unswap(BeanSession,X)</code>/<code>T(BeanSession,X)</code> methods.<br>
+ See new section <a class='doclink' href='#Core.SwapMethods'>Swap methods</a> for information.
<li>Session-level media type now available through {@link org.apache.juneau.BeanSession#getMediaType()} method.
Allows for swaps and serializer/parser behavior to be tailored to individual media types.
<li>Several new {@link java.util.Calendar} and {@link java.util.Date} swaps:
@@ -6994,7 +7121,7 @@
<h6 class='topic'>Other changes</h6>
<ul class='spaced-list'>
<li>Various new methods added to {@link org.apache.juneau.internal.StringUtils} and {@link org.apache.juneau.internal.ClassUtils}.
- <li>Improved support on {@link org.apache.juneau.BeanContext#getClassMetaFromString(String)}.<br>
+ <li>Improved support on <code><del>BeanContext.getClassMetaFromString(String)</del></code>.<br>
Now supports resolving <code>"long[]"</code>, and so forth.
<li>{@link org.apache.juneau.rest.labels.ResourceDescription} name parameter is now automatically URL-encoded in links.
<li>{@link org.apache.juneau.rest.RestRequest} now correctly handles cases involving URL-encoded characters in the
@@ -7028,7 +7155,7 @@
<li>Support for URL-matching and path info containing encoded characters (e.g. <js>'/'</js>) now supported.
<li>Removed some lazy-initialization of bean information in {@link org.apache.juneau.ClassMeta} that allowed the removal of
some synchronized blocks.
- <li>Improved support of {@link org.apache.juneau.BeanContext#getClassMetaFromString(String)}.
+ <li>Improved support of <code><del>BeanContext.getClassMetaFromString(String)</del></code>.
Now supports primitive arrays such as <js>"long[]"</js> in addition to the previous support for the equivalent <js>"[J"</js>.
<li>Various new convenience methods added to {@link org.apache.juneau.internal.StringUtils} and {@link org.apache.juneau.internal.ClassUtils}.
</ul>