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 2016/08/30 20:12:05 UTC
incubator-juneau git commit: Simplify @Bean(subTypes) annotation.
Repository: incubator-juneau
Updated Branches:
refs/heads/master 4b2719c0d -> 4fb5136f8
Simplify @Bean(subTypes) annotation.
Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/4fb5136f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/4fb5136f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/4fb5136f
Branch: refs/heads/master
Commit: 4fb5136f8fe6fa806c86a5779d52c37185e2c3ea
Parents: 4b2719c
Author: jamesbognar <ja...@gmail.com>
Authored: Tue Aug 30 16:11:57 2016 -0400
Committer: jamesbognar <ja...@gmail.com>
Committed: Tue Aug 30 16:11:57 2016 -0400
----------------------------------------------------------------------
.../java/org/apache/juneau/BeanContext.java | 11 +++
.../main/java/org/apache/juneau/ClassMeta.java | 12 +++-
.../main/java/org/apache/juneau/ObjectMap.java | 13 +++-
.../java/org/apache/juneau/annotation/Bean.java | 73 +++++++++++++++++++-
.../java/org/apache/juneau/annotation/Pojo.java | 72 +++++++++++++++++++
.../apache/juneau/internal/ReflectionUtils.java | 13 ++++
.../juneau/transform/AnnotationBeanFilter.java | 14 +++-
.../java/org/apache/juneau/BeanFilterTest.java | 14 ++--
.../juneau/a/rttests/RoundTripBeanMapsTest.java | 16 ++---
.../a/rttests/RoundTripTransformBeansTest.java | 5 +-
10 files changed, 217 insertions(+), 26 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb5136f/juneau-core/src/main/java/org/apache/juneau/BeanContext.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanContext.java b/juneau-core/src/main/java/org/apache/juneau/BeanContext.java
index 8b260bc..9b9ce4a 100644
--- a/juneau-core/src/main/java/org/apache/juneau/BeanContext.java
+++ b/juneau-core/src/main/java/org/apache/juneau/BeanContext.java
@@ -446,6 +446,10 @@ public class BeanContext extends Context {
*/
public static final String BEAN_implClasses_put = "BeanContext.implClasses.map.put";
+ public static final String BEAN_classLexicon = "BeanContext.pojoSwaps.list";
+ public static final String BEAN_classLexicon_add = "BeanContext.pojoSwaps.list.add";
+ public static final String BEAN_classLexicon_remove = "BeanContext.pojoSwaps.list.remove";
+
/**
* Specifies the default parser to use when converting <code>Strings</code> to POJOs in the {@link BeanContext#convertToType(Object, Class)} method (<code>Class</code>).
*/
@@ -525,6 +529,7 @@ public class BeanContext extends Context {
final String[] notBeanPackageNames, notBeanPackagePrefixes;
final BeanFilter<?>[] beanFilters;
final PojoSwap<?,?>[] pojoSwaps;
+ final ClassLexicon classLexicon;
final Map<Class<?>,Class<?>> implClasses;
final Class<?>[] implKeyClasses, implValueClasses;
final ClassLoader classLoader;
@@ -615,6 +620,8 @@ public class BeanContext extends Context {
}
pojoSwaps = lpf.toArray(new PojoSwap[0]);
+ classLexicon = new ClassLexicon(pm.get(BEAN_pojoSwaps, Class[].class, new Class[0]));
+
implClasses = new TreeMap<Class<?>,Class<?>>(new ClassComparator());
Map<Class,Class> m = pm.getMap(BEAN_implClasses, Class.class, Class.class, null);
if (m != null)
@@ -1456,6 +1463,10 @@ public class BeanContext extends Context {
return null;
}
+ protected ClassLexicon getClassLexicon() {
+ return classLexicon;
+ }
+
/**
* Gets the no-arg constructor for the specified class.
*
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb5136f/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 eb60730..ba31b41 100644
--- a/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java
+++ b/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java
@@ -83,6 +83,7 @@ public final class ClassMeta<T> implements Type {
isMemberClass; // True if this is a non-static member class.
private MetadataMap extMeta = new MetadataMap(); // Extended metadata
+ private ClassLexicon classLexicon;
private Throwable initException; // Any exceptions thrown in the init() method.
private boolean hasChildPojoSwaps; // True if this class or any subclass of this class has a PojoSwap associated with it.
@@ -128,6 +129,15 @@ public final class ClassMeta<T> implements Type {
try {
beanFilter = findBeanFilter(beanContext);
pojoSwap = findPojoSwap(beanContext);
+
+ classLexicon = beanContext.getClassLexicon();
+ for (Pojo p : ReflectionUtils.findAnnotationsParentFirst(Pojo.class, innerClass))
+ if (p.lexicon().length > 0)
+ classLexicon = new ClassLexicon(classLexicon, p.lexicon());
+ for (Bean b : ReflectionUtils.findAnnotationsParentFirst(Bean.class, innerClass))
+ if (b.lexicon().length > 0)
+ classLexicon = new ClassLexicon(classLexicon, b.lexicon());
+
serializedClassMeta = (pojoSwap == null ? this : beanContext.getClassMeta(pojoSwap.getSwapClass()));
if (serializedClassMeta == null)
serializedClassMeta = this;
@@ -394,7 +404,7 @@ public final class ClassMeta<T> implements Type {
}
/**
- * Returns <jk>true</jk> if this class as subtypes defined through {@link Bean#subTypes} or {@link BeanFilter#getSubTypes()}.
+ * Returns <jk>true</jk> if this class as subtypes defined through {@link Bean#subTypes}.
*
* @return <jk>true</jk> if this class has subtypes.
*/
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb5136f/juneau-core/src/main/java/org/apache/juneau/ObjectMap.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/ObjectMap.java b/juneau-core/src/main/java/org/apache/juneau/ObjectMap.java
index 93dad9d..6edebf5 100644
--- a/juneau-core/src/main/java/org/apache/juneau/ObjectMap.java
+++ b/juneau-core/src/main/java/org/apache/juneau/ObjectMap.java
@@ -1150,12 +1150,21 @@ public class ObjectMap extends LinkedHashMap<String,Object> {
* same object if entry does not exist.
*/
public Object cast() {
+ return cast((ClassLexicon)null);
+ }
+
+ public Object cast(ClassLexicon classLexicon) {
String c = (String)get("_class");
if (c == null) {
if (containsKey("_value"))
return get("_value");
return this;
}
+ if (classLexicon != null) {
+ Class<?> c2 = classLexicon.getClassForName(c);
+ if (c2 != null)
+ return cast2(beanContext.getClassMeta(c2));
+ }
return cast2(beanContext.getClassMetaFromString(c));
}
@@ -1249,7 +1258,7 @@ public class ObjectMap extends LinkedHashMap<String,Object> {
// Attempt to recursively cast child maps.
if (v instanceof ObjectMap)
- v = ((ObjectMap)v).cast();
+ v = ((ObjectMap)v).cast(beanContext.classLexicon);
k = (kType.isString() ? k : beanContext.convertToType(k, kType));
v = (vType.isObject() ? v : beanContext.convertToType(v, vType));
@@ -1270,7 +1279,7 @@ public class ObjectMap extends LinkedHashMap<String,Object> {
// Attempt to recursively cast child maps.
if (v instanceof ObjectMap)
- v = ((ObjectMap)v).cast();
+ v = ((ObjectMap)v).cast(beanContext.classLexicon);
bm.put(k, v);
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb5136f/juneau-core/src/main/java/org/apache/juneau/annotation/Bean.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/annotation/Bean.java b/juneau-core/src/main/java/org/apache/juneau/annotation/Bean.java
index dd436a2..34b6414 100644
--- a/juneau-core/src/main/java/org/apache/juneau/annotation/Bean.java
+++ b/juneau-core/src/main/java/org/apache/juneau/annotation/Bean.java
@@ -42,6 +42,77 @@ import org.apache.juneau.transform.*;
public @interface Bean {
/**
+ * An identifying name for this class.
+ * <p>
+ * The name is used to identify the class type during parsing when it cannot be inferred through reflection.
+ * For example, if a bean property is of type <code>Object</code>, then the serializer will add the name to the
+ * output so that the class can be determined during parsing.
+ * It is also used to specify element names in XML.
+ * <p>
+ * The name is used in combination with the lexicon defined through {@link #lexicon()}. Together, they make up
+ * a simple name/value mapping of names to classes.
+ * Names do not need to be universally unique. However, they must be unique within a lexicon.
+ *
+ * <dl>
+ * <dt>Example:</dt>
+ * <dd>
+ * <p class='bcode'>
+ * <ja>@Bean</ja>(name=<js>"foo"</js>)
+ * <jk>public class</jk> Foo {
+ * <jc>// A bean property where the object types cannot be inferred since it's an Object[].</jc>
+ * <ja>@BeanProperty</ja>(lexicon={Bar.<jk>class</jk>,Baz.<jk>class</jk>})
+ * <jk>public</jk> Object[] x = <jk>new</jk> Object[]{<jk>new</jk> Bar(), <jk>new</jk> Baz()};
+ * }
+ *
+ * <ja>@Bean</ja>(name=<js>"bar"</js>)
+ * <jk>public class</jk> Bar {}
+ *
+ * <ja>@Bean</ja>(name=<js>"baz"</js>)
+ * <jk>public class</jk> Baz {}
+ * </p>
+ * <p>
+ * When serialized as XML, the bean is rendered as:
+ * </p>
+ * <p class='bcode'>
+ * <xt><foo></xt>
+ * <xt><x></xt>
+ * <xt><bar/>v
+ * <xt><baz/></xt>
+ * <xt></x></xt>
+ * <xt></foo></xt>
+ * </p>
+ * <p>
+ * When serialized as JSON, <js>'n'</js> attributes would be added when needed to infer the type during parsing:
+ * </p>
+ * <p class='bcode'>
+ * {
+ * <jsa>x</jsa>: [
+ * {<jsa>n</jsa>:<jss>'bar'</jss>},
+ * {<jsa>n</jsa>:<jss>'baz'</jss>}
+ * ]
+ * } *
+ * </dd>
+ * </dl>
+ */
+ String name() default "";
+
+ /**
+ * The list of classes that make up the class lexicon for this class.
+ * <p>
+ * The lexicon is a name/class mapping used to find class types during parsing when they cannot be inferred through reflection.
+ * The names are defined through the {@link #name()} annotation defined on the bean or POJO classes.
+ * <p>
+ * This list can consist of the following class types:
+ * <ul>
+ * <li>Any bean class that specifies a value for {@link Bean#name() @Bean.name()};
+ * <li>Any POJO class that specifies a value for {@link Pojo#name() @Pojo.name()};
+ * <li>Any subclass of {@link ClassLexicon} that defines an entire set of mappings.
+ * Note that the subclass MUST implement a no-arg constructor so that it can be instantiated.
+ * </ul>
+ */
+ Class<?>[] lexicon() default {};
+
+ /**
* The set and order of names of properties associated with a bean class.
* <p>
* The order specified is the same order that the entries will be returned by the {@link BeanMap#entrySet()} and related methods.
@@ -177,7 +248,7 @@ public @interface Bean {
/**
* Used in conjunction with {@link #subTypeProperty()} to set up bean subtypes.
*/
- BeanSubType[] subTypes() default {};
+ Class<?>[] subTypes() default {};
/**
* Identifies a class to be used as the interface class for this and all subclasses.
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb5136f/juneau-core/src/main/java/org/apache/juneau/annotation/Pojo.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/annotation/Pojo.java b/juneau-core/src/main/java/org/apache/juneau/annotation/Pojo.java
index f200622..9d615c1 100644
--- a/juneau-core/src/main/java/org/apache/juneau/annotation/Pojo.java
+++ b/juneau-core/src/main/java/org/apache/juneau/annotation/Pojo.java
@@ -17,6 +17,7 @@ import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
+import org.apache.juneau.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.transform.*;
@@ -32,6 +33,77 @@ import org.apache.juneau.transform.*;
public @interface Pojo {
/**
+ * An identifying name for this class.
+ * <p>
+ * The name is used to identify the class type during parsing when it cannot be inferred through reflection.
+ * For example, if a bean property is of type <code>Object</code>, then the serializer will add the name to the
+ * output so that the class can be determined during parsing.
+ * It is also used to specify element names in XML.
+ * <p>
+ * The name is used in combination with the lexicon defined through {@link #lexicon()}. Together, they make up
+ * a simple name/value mapping of names to classes.
+ * Names do not need to be universally unique. However, they must be unique within a lexicon.
+ *
+ * <dl>
+ * <dt>Example:</dt>
+ * <dd>
+ * <p class='bcode'>
+ * <ja>@Bean</ja>(name=<js>"foo"</js>)
+ * <jk>public class</jk> Foo {
+ * <jc>// A bean property where the object types cannot be inferred since it's an Object[].</jc>
+ * <ja>@BeanProperty</ja>(lexicon={Bar.<jk>class</jk>,Baz.<jk>class</jk>})
+ * <jk>public</jk> Object[] x = <jk>new</jk> Object[]{<jk>new</jk> Bar(), <jk>new</jk> Baz()};
+ * }
+ *
+ * <ja>@Pojo</ja>(name=<js>"bar"</js>)
+ * <jk>public class</jk> Bar <jk>extends</jk> HashMap {}
+ *
+ * <ja>@Pojo</ja>(name=<js>"baz"</js>)
+ * <jk>public class</jk> Baz <jk>extends</jk> HashMap {}
+ * </p>
+ * <p>
+ * When serialized as XML, the bean is rendered as:
+ * </p>
+ * <p class='bcode'>
+ * <xt><foo></xt>
+ * <xt><x></xt>
+ * <xt><bar/>v
+ * <xt><baz/></xt>
+ * <xt></x></xt>
+ * <xt></foo></xt>
+ * </p>
+ * <p>
+ * When serialized as JSON, <js>'n'</js> attributes would be added when needed to infer the type during parsing:
+ * </p>
+ * <p class='bcode'>
+ * {
+ * <jsa>x</jsa>: [
+ * {<jsa>n</jsa>:<jss>'bar'</jss>},
+ * {<jsa>n</jsa>:<jss>'baz'</jss>}
+ * ]
+ * } *
+ * </dd>
+ * </dl>
+ */
+ String name() default "";
+
+ /**
+ * The list of classes that make up the class lexicon for this class.
+ * <p>
+ * The lexicon is a name/class mapping used to find class types during parsing when they cannot be inferred through reflection.
+ * The names are defined through the {@link #name()} annotation defined on the bean or POJO classes.
+ * <p>
+ * This list can consist of the following class types:
+ * <ul>
+ * <li>Any bean class that specifies a value for {@link Bean#name() @Bean.name()};
+ * <li>Any POJO class that specifies a value for {@link Pojo#name() @Pojo.name()};
+ * <li>Any subclass of {@link ClassLexicon} that defines an entire set of mappings.
+ * Note that the subclass MUST implement a no-arg constructor so that it can be instantiated.
+ * </ul>
+ */
+ Class<?>[] lexicon() default {};
+
+ /**
* Associate a {@link PojoSwap} or {@link SurrogateSwap} with this class type.
*
* <p>
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb5136f/juneau-core/src/main/java/org/apache/juneau/internal/ReflectionUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/ReflectionUtils.java b/juneau-core/src/main/java/org/apache/juneau/internal/ReflectionUtils.java
index 0936d91..c1686ab 100644
--- a/juneau-core/src/main/java/org/apache/juneau/internal/ReflectionUtils.java
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/ReflectionUtils.java
@@ -91,6 +91,19 @@ public final class ReflectionUtils {
}
/**
+ * Same as {@link #findAnnotations(Class, Class)} but returns the list in parent-to-child order.
+ *
+ * @param a The annotation class type.
+ * @param c The class being searched.
+ * @return The found matches, or an empty array if annotation was not found.
+ */
+ public static <T extends Annotation> List<T> findAnnotationsParentFirst(Class<T> a, Class<?> c) {
+ List<T> l = findAnnotations(a, c);
+ Collections.reverse(l);
+ return l;
+ }
+
+ /**
* Sames as {@link #findAnnotations(Class, Class)} except returns the annotations as a map
* with the keys being the class on which the annotation was found.
* <p>
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb5136f/juneau-core/src/main/java/org/apache/juneau/transform/AnnotationBeanFilter.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/transform/AnnotationBeanFilter.java b/juneau-core/src/main/java/org/apache/juneau/transform/AnnotationBeanFilter.java
index dd8cb4d..24c6d5a 100644
--- a/juneau-core/src/main/java/org/apache/juneau/transform/AnnotationBeanFilter.java
+++ b/juneau-core/src/main/java/org/apache/juneau/transform/AnnotationBeanFilter.java
@@ -88,8 +88,18 @@ public final class AnnotationBeanFilter<T> extends BeanFilter<T> {
if (! b.subTypeProperty().isEmpty()) {
subTypeProperty = b.subTypeProperty();
- for (BeanSubType bst : b.subTypes())
- subTypes.put(bst.type(), bst.id());
+ for (Class<?> bst : b.subTypes()) {
+ Bean b2 = bst.getAnnotation(Bean.class);
+ Pojo p2 = bst.getAnnotation(Pojo.class);
+ String name = null;
+ if (! b2.name().isEmpty())
+ name = b2.name();
+ else if (! p2.name().isEmpty())
+ name = p2.name();
+ else
+ name = bst.getName();
+ subTypes.put(bst, name);
+ }
}
}
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb5136f/juneau-core/src/test/java/org/apache/juneau/BeanFilterTest.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/test/java/org/apache/juneau/BeanFilterTest.java b/juneau-core/src/test/java/org/apache/juneau/BeanFilterTest.java
index 78fe620..c2da6ab 100755
--- a/juneau-core/src/test/java/org/apache/juneau/BeanFilterTest.java
+++ b/juneau-core/src/test/java/org/apache/juneau/BeanFilterTest.java
@@ -55,39 +55,37 @@ public class BeanFilterTest {
@Bean(
subTypeProperty="subType",
- subTypes={
- @BeanSubType(type=A1.class, id="A1"),
- @BeanSubType(type=A2.class, id="A2")
- }
+ subTypes={A1.class,A2.class}
)
public static abstract class A {
public String f0 = "f0";
public B fb;
}
+ @Bean(name="A1")
public static class A1 extends A {
public String f1;
}
+ @Bean(name="A2")
public static class A2 extends A {
public String f2;
}
@Bean(
subTypeProperty="subType",
- subTypes={
- @BeanSubType(type=B1.class, id="B1"),
- @BeanSubType(type=B2.class, id="B2")
- }
+ subTypes={B1.class,B2.class}
)
public static abstract class B {
public String f0b = "f0b";
}
+ @Bean(name="B1")
public static class B1 extends B {
public String f1;
}
+ @Bean(name="B2")
public static class B2 extends B {
public String f2;
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb5136f/juneau-core/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanMapsTest.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanMapsTest.java b/juneau-core/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanMapsTest.java
index 143c677..060f52a 100755
--- a/juneau-core/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanMapsTest.java
+++ b/juneau-core/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanMapsTest.java
@@ -249,16 +249,13 @@ public class RoundTripBeanMapsTest extends RoundTripTest {
@Bean(
subTypeProperty="subType",
- subTypes={
- @BeanSubType(type=B1.class, id="B1"),
- @BeanSubType(type=B2.class, id="B2"),
- @BeanSubType(type=B3.class, id="B3")
- }
+ subTypes={B1.class,B2.class,B3.class}
)
public abstract static class B {
public String f0 = "f0";
}
+ @Bean(name="B1")
public static class B1 extends B {
public String f1;
public static B1 create() {
@@ -269,6 +266,7 @@ public class RoundTripBeanMapsTest extends RoundTripTest {
}
}
+ @Bean(name="B2")
public static class B2 extends B {
public int f2;
public static B2 create() {
@@ -279,6 +277,7 @@ public class RoundTripBeanMapsTest extends RoundTripTest {
}
}
+ @Bean(name="B3")
public static class B3 extends B {
public XMLGregorianCalendar f3;
public static B3 create() throws Exception {
@@ -387,15 +386,13 @@ public class RoundTripBeanMapsTest extends RoundTripTest {
@Bean(
subTypeProperty="subType",
- subTypes={
- @BeanSubType(type=BA1.class, id="BA1"),
- @BeanSubType(type=BA2.class, id="BA2")
- }
+ subTypes={BA1.class,BA2.class}
)
public abstract static class BA {
public String f0a, subType, f0b;
}
+ @Bean(name="BA1")
public static class BA1 extends BA {
public String f1;
public static BA1 create() {
@@ -408,6 +405,7 @@ public class RoundTripBeanMapsTest extends RoundTripTest {
}
}
+ @Bean(name="BA2")
public static class BA2 extends BA {
public String f2;
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb5136f/juneau-core/src/test/java/org/apache/juneau/a/rttests/RoundTripTransformBeansTest.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/test/java/org/apache/juneau/a/rttests/RoundTripTransformBeansTest.java b/juneau-core/src/test/java/org/apache/juneau/a/rttests/RoundTripTransformBeansTest.java
index 14414e8..4ac7270 100755
--- a/juneau-core/src/test/java/org/apache/juneau/a/rttests/RoundTripTransformBeansTest.java
+++ b/juneau-core/src/test/java/org/apache/juneau/a/rttests/RoundTripTransformBeansTest.java
@@ -281,9 +281,7 @@ public class RoundTripTransformBeansTest extends RoundTripTest {
@Bean(
subTypeProperty="type",
- subTypes={
- @BeanSubType(id="C3", type=C3.class)
- }
+ subTypes={C3.class}
)
public static interface C1<T> extends Serializable {
void setF1(T f1);
@@ -304,6 +302,7 @@ public class RoundTripTransformBeansTest extends RoundTripTest {
}
}
+ @Bean(name="C3")
public static class C3<T> extends C2<T> {
public static C3 create() {