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 cc6e60b47d Marshall module improvements
cc6e60b47d is described below
commit cc6e60b47db330c1d7e5b378ae2d878d276d6495
Author: James Bognar <[email protected]>
AuthorDate: Sat Dec 6 10:25:27 2025 -0500
Marshall module improvements
---
.../apache/juneau/commons/reflect/Property.java | 12 +-
.../src/main/java/org/apache/juneau/ClassMeta.java | 152 +++++++++++++++++----
.../org/apache/juneau/annotation/NameProperty.java | 4 +-
.../org/apache/juneau/parser/ParserSession.java | 2 +-
.../docs/topics/02.04.07.NamePropertyAnnotation.md | 4 +-
...ipTest.java => NameProperty_RoundTripTest.java} | 43 +-----
...Test.java => ParentProperty_RoundTripTest.java} | 53 +------
.../annotation/NamePropertyAnnotation_Test.java | 103 ++++++++++++++
.../annotation/ParentPropertyAnnotation_Test.java | 63 +++++++++
.../juneau/commons/reflect/Property_Test.java | 12 +-
10 files changed, 317 insertions(+), 131 deletions(-)
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/Property.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/Property.java
index 864a4a40ce..fc8785327c 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/Property.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/Property.java
@@ -146,20 +146,20 @@ public class Property<T, V> {
}
/**
- * Returns <jk>true</jk> if this property has a getter (producer).
+ * Returns <jk>true</jk> if this property can be read.
*
- * @return <jk>true</jk> if a producer is defined.
+ * @return <jk>true</jk> if this property can be read.
*/
- public boolean hasGetter() {
+ public boolean canRead() {
return producer != null;
}
/**
- * Returns <jk>true</jk> if this property has a setter (consumer).
+ * Returns <jk>true</jk> if this property can be written.
*
- * @return <jk>true</jk> if a consumer is defined.
+ * @return <jk>true</jk> if this property can be written.
*/
- public boolean hasSetter() {
+ public boolean canWrite() {
return consumer != null;
}
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 b3053fc349..d180f07f53 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
@@ -162,10 +162,10 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
private final ClassMeta<?> keyType;
// If MAP, the key class type.
private final SimpleReadWriteLock lock = new SimpleReadWriteLock(false);
private final Supplier<MarshalledFilter> marshalledFilter;
- private final Supplier<Property<T,String>> namePropertySetter;
// The method to set the name on an object
(if it has one).
+ private final Supplier<Property<T,Object>> nameProperty;
// The method to set the name on an object (if it
has one).
private final Supplier<ConstructorInfo> noArgConstructor;
// The no-arg constructor for this
class (if it has one).
private final String notABeanReason;
// If this isn't a bean, the reason why.
- private final Supplier<Property<T,?>> parentPropertySetter;
// The method to set the parent on an object (if
it has one).
+ private final Supplier<Property<T,Object>> parentProperty;
// The method to set the parent on an object (if it
has one).
private final Map<String,Optional<?>> properties = new
ConcurrentHashMap<>();
private final Mutater<String,T> stringMutater;
private final Supplier<ConstructorInfo> stringConstructor;
// The X(String) constructor (if it has
one).
@@ -254,8 +254,8 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
fromStringMethod = memoize(()->findFromStringMethod());
exampleMethod = memoize(()->findExampleMethod());
- parentPropertySetter =
memoize(()->findParentPropertySetter());
- namePropertySetter =
memoize(()->findNamePropertySetter());
+ parentProperty = memoize(()->findParentProperty());
+ nameProperty = memoize(()->findNameProperty());
exampleField = memoize(()->findExampleField());
noArgConstructor = memoize(()->findNoArgConstructor());
stringConstructor =
memoize(()->findStringConstructor());
@@ -376,8 +376,8 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
this.stringMutater = null;
this.fromStringMethod = memoize(()->findFromStringMethod());
this.exampleMethod = memoize(()->findExampleMethod());
- this.parentPropertySetter =
memoize(()->findParentPropertySetter());
- this.namePropertySetter = memoize(()->findNamePropertySetter());
+ this.parentProperty = memoize(()->findParentProperty());
+ this.nameProperty = memoize(()->findNameProperty());
this.exampleField = memoize(()->findExampleField());
this.noArgConstructor = memoize(()->findNoArgConstructor());
this.stringConstructor = memoize(()->findStringConstructor());
@@ -416,8 +416,8 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
this.exampleMethod = mainType.exampleMethod;
this.args = null;
this.stringMutater = mainType.stringMutater;
- this.parentPropertySetter = mainType.parentPropertySetter;
- this.namePropertySetter = mainType.namePropertySetter;
+ this.parentProperty = mainType.parentProperty;
+ this.nameProperty = mainType.nameProperty;
this.exampleField = mainType.exampleField;
this.noArgConstructor = mainType.noArgConstructor;
this.stringConstructor = mainType.stringConstructor;
@@ -742,7 +742,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
* The method or field annotated with {@link NameProperty
@NameProperty} or <jk>null</jk> if method does not
* exist.
*/
- public Property<T,String> getNameProperty() { return
namePropertySetter.get(); }
+ public Property<T,Object> getNameProperty() { return
nameProperty.get(); }
/**
* Returns the reason why this class is not a bean, or <jk>null</jk> if
it is a bean.
@@ -772,7 +772,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
* The method or field annotated with {@link ParentProperty
@ParentProperty} or <jk>null</jk> if method does not
* exist.
*/
- public Property<T,?> getParentProperty() { return
parentPropertySetter.get(); }
+ public Property<T,Object> getParentProperty() { return
parentProperty.get(); }
/**
* Returns a calculated property on this context.
@@ -1568,25 +1568,77 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
return
MarshalledFilter.create(inner()).applyAnnotations(reverse(l.stream().map(AnnotationInfo::inner).toList())).build();
}
- private Property<T,String> findNamePropertySetter() {
+ private Property<T,Object> findNameProperty() {
var ap = beanContext.getAnnotationProvider();
var s = getAllFields()
.stream()
- .filter(x -> x.getFieldType().is(String.class) &&
ap.has(NameProperty.class, x))
+ .filter(x -> ap.has(NameProperty.class, x))
.map(x -> x.accessible())
- .map(x -> Property.<T,String>create().field(x).build())
+ .map(x -> Property.<T,Object>create().field(x).build())
.findFirst();
if (s.isPresent()) return s.get();
- return getAllMethods()
+ var builder = Property.<T,Object>create();
+
+ // Look for setter method (1 parameter) with @NameProperty
+ var setterMethod = getAllMethods()
.stream()
.filter(x -> ap.has(NameProperty.class, x) &&
x.hasNumParameters(1))
- .map(x -> x.accessible())
- .map(x -> Property.<T,String>create().setter(x).build())
- .findFirst()
- .orElse(null);
+ .findFirst();
+
+ if (setterMethod.isPresent()) {
+ builder.setter(setterMethod.get().accessible());
+
+ // Try to find a corresponding getter method (even if
not annotated)
+ // If setter is "setName", look for "getName" or
"isName"
+ var setterName = setterMethod.get().getSimpleName();
+ if (setterName.startsWith("set") && setterName.length()
> 3) {
+ var propertyName = setterName.substring(3);
+ var getterName1 = "get" + propertyName;
+ var getterName2 = "is" + propertyName;
+
+ var getter = getAllMethods()
+ .stream()
+ .filter(x -> !x.isStatic() &&
x.hasNumParameters(0) &&
+ (x.hasName(getterName1) ||
x.hasName(getterName2)) &&
+
!x.getReturnType().is(Void.TYPE))
+ .findFirst();
+
+ if (getter.isPresent()) {
+
builder.getter(getter.get().accessible());
+ } else {
+ // Try to find a field with the
property name (lowercase first letter)
+ var fieldName =
Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1);
+ var field = getAllFields()
+ .stream()
+ .filter(x -> !x.isStatic() &&
x.hasName(fieldName))
+ .findFirst();
+
+ if (field.isPresent()) {
+ var f =
field.get().accessible();
+ builder.getter(obj ->
(Object)f.get(obj));
+ }
+ }
+ }
+ }
+
+ // Look for getter method (0 parameters, non-void return) with
@NameProperty
+ var getterMethod = getAllMethods()
+ .stream()
+ .filter(x -> ap.has(NameProperty.class, x) &&
x.hasNumParameters(0) && !x.getReturnType().is(Void.TYPE))
+ .findFirst();
+
+ if (getterMethod.isPresent()) {
+ builder.getter(getterMethod.get().accessible());
+ }
+
+ // Return null if neither setter nor getter was found
+ if (setterMethod.isEmpty() && getterMethod.isEmpty())
+ return null;
+
+ return builder.build();
}
private ConstructorInfo findNoArgConstructor() {
@@ -1608,7 +1660,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
.orElse(null);
}
- private Property<T,?> findParentPropertySetter() {
+ private Property<T,Object> findParentProperty() {
var ap = beanContext.getAnnotationProvider();
var s = getAllFields()
@@ -1620,13 +1672,65 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
if (s.isPresent()) return s.get();
- return getAllMethods()
+ var builder = Property.<T,Object>create();
+
+ // Look for setter method (1 parameter) with @ParentProperty
+ var setterMethod = getAllMethods()
.stream()
.filter(x -> ap.has(ParentProperty.class, x) &&
x.hasNumParameters(1))
- .map(x -> x.accessible())
- .map(x -> Property.<T,Object>create().setter(x).build())
- .findFirst()
- .orElse(null);
+ .findFirst();
+
+ if (setterMethod.isPresent()) {
+ builder.setter(setterMethod.get().accessible());
+
+ // Try to find a corresponding getter method (even if
not annotated)
+ // If setter is "setParent", look for "getParent" or
"isParent"
+ var setterName = setterMethod.get().getSimpleName();
+ if (setterName.startsWith("set") && setterName.length()
> 3) {
+ var propertyName = setterName.substring(3);
+ var getterName1 = "get" + propertyName;
+ var getterName2 = "is" + propertyName;
+
+ var getter = getAllMethods()
+ .stream()
+ .filter(x -> !x.isStatic() &&
x.hasNumParameters(0) &&
+ (x.hasName(getterName1) ||
x.hasName(getterName2)) &&
+
!x.getReturnType().is(Void.TYPE))
+ .findFirst();
+
+ if (getter.isPresent()) {
+
builder.getter(getter.get().accessible());
+ } else {
+ // Try to find a field with the
property name (lowercase first letter)
+ var fieldName =
Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1);
+ var field = getAllFields()
+ .stream()
+ .filter(x -> !x.isStatic() &&
x.hasName(fieldName))
+ .findFirst();
+
+ if (field.isPresent()) {
+ var f =
field.get().accessible();
+ builder.getter(obj ->
(Object)f.get(obj));
+ }
+ }
+ }
+ }
+
+ // Look for getter method (0 parameters, non-void return) with
@ParentProperty
+ var getterMethod = getAllMethods()
+ .stream()
+ .filter(x -> ap.has(ParentProperty.class, x) &&
x.hasNumParameters(0) && !x.getReturnType().is(Void.TYPE))
+ .findFirst();
+
+ if (getterMethod.isPresent()) {
+ builder.getter(getterMethod.get().accessible());
+ }
+
+ // Return null if neither setter nor getter was found
+ if (setterMethod.isEmpty() && getterMethod.isEmpty())
+ return null;
+
+ return builder.build();
}
private ConstructorInfo findStringConstructor() {
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/NameProperty.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/NameProperty.java
index 1120ac7bcd..5637c1e09a 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/NameProperty.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/NameProperty.java
@@ -32,8 +32,8 @@ import java.lang.annotation.*;
* <h5 class='section'>Requirements:</h5>
* <ul class='spaced-list'>
* <li>Must be an <strong>instance</strong> method or field (not static)
- * <li>For methods: Must accept exactly one parameter of type <c>String</c>
- * <li>For fields: Must be of type <c>String</c>
+ * <li>For methods: Must accept exactly one parameter (any type)
+ * <li>For fields: Can be any type
* <li>The method or field does not need to be public (will be made
accessible automatically)
* </ul>
*
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/ParserSession.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/ParserSession.java
index 4f22375eb8..3a32cf5f9a 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/ParserSession.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/ParserSession.java
@@ -217,7 +217,7 @@ public class ParserSession extends BeanSession {
* @param name The name to set.
* @throws ExecutableException Exception occurred on invoked
constructor/method/field.
*/
- protected static final void setName(ClassMeta<?> cm, Object o, Object
name) throws ExecutableException {
+ protected static final <T> void setName(ClassMeta<?> cm, Object o,
Object name) throws ExecutableException {
if (nn(cm)) {
Property m = cm.getNameProperty();
if (nn(m))
diff --git a/juneau-docs/docs/topics/02.04.07.NamePropertyAnnotation.md
b/juneau-docs/docs/topics/02.04.07.NamePropertyAnnotation.md
index 5e4f429acd..ecaf584f03 100644
--- a/juneau-docs/docs/topics/02.04.07.NamePropertyAnnotation.md
+++ b/juneau-docs/docs/topics/02.04.07.NamePropertyAnnotation.md
@@ -10,8 +10,8 @@ This annotation is used by parsers to automatically set the
name/key of an objec
## Requirements
- Must be an **instance** method or field (not static)
-- For methods: Must accept exactly one parameter of type `String`
-- For fields: Must be of type `String`
+- For methods: Must accept exactly one parameter (any type)
+- For fields: Can be any type
- The method or field does not need to be public (will be made accessible
automatically)
## Usage
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/a/rttests/ObjectsWithSpecialMethods_RoundTripTest.java
b/juneau-utest/src/test/java/org/apache/juneau/a/rttests/NameProperty_RoundTripTest.java
old mode 100755
new mode 100644
similarity index 71%
copy from
juneau-utest/src/test/java/org/apache/juneau/a/rttests/ObjectsWithSpecialMethods_RoundTripTest.java
copy to
juneau-utest/src/test/java/org/apache/juneau/a/rttests/NameProperty_RoundTripTest.java
index ff686269fa..e73c2c1608
---
a/juneau-utest/src/test/java/org/apache/juneau/a/rttests/ObjectsWithSpecialMethods_RoundTripTest.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/a/rttests/NameProperty_RoundTripTest.java
@@ -29,7 +29,7 @@ import org.junit.jupiter.params.provider.*;
* Tests designed to serialize and parse objects to make sure we end up
* with the same objects for all serializers and parsers.
*/
-class ObjectsWithSpecialMethods_RoundTripTest extends RoundTripTest_Base {
+class NameProperty_RoundTripTest extends RoundTripTest_Base {
//====================================================================================================
// @NameProperty method.
@@ -73,44 +73,5 @@ class ObjectsWithSpecialMethods_RoundTripTest extends
RoundTripTest_Base {
return this;
}
}
+}
-
//====================================================================================================
- // @ParentProperty method.
-
//====================================================================================================
-
- @ParameterizedTest
- @MethodSource("testers")
- void a02_parentProperty(RoundTrip_Tester t) throws Exception {
- var x = new B().init();
- x = t.roundTrip(x);
- if (t.isValidationOnly())
- return;
- assertEquals(x.f1, x.b2.parent.f1);
- }
-
- public static class B {
- public int f1;
- public B2 b2;
-
- B init() {
- f1 = 1;
- b2 = new B2().init();
- return this;
- }
-
- }
- public static class B2 {
- B parent;
- public int f2;
-
- @ParentProperty
- protected void setParent(B v) {
- parent = v;
- }
-
- B2 init() {
- f2 = 2;
- return this;
- }
- }
-}
\ No newline at end of file
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/a/rttests/ObjectsWithSpecialMethods_RoundTripTest.java
b/juneau-utest/src/test/java/org/apache/juneau/a/rttests/ParentProperty_RoundTripTest.java
old mode 100755
new mode 100644
similarity index 61%
rename from
juneau-utest/src/test/java/org/apache/juneau/a/rttests/ObjectsWithSpecialMethods_RoundTripTest.java
rename to
juneau-utest/src/test/java/org/apache/juneau/a/rttests/ParentProperty_RoundTripTest.java
index ff686269fa..c460a87f03
---
a/juneau-utest/src/test/java/org/apache/juneau/a/rttests/ObjectsWithSpecialMethods_RoundTripTest.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/a/rttests/ParentProperty_RoundTripTest.java
@@ -16,11 +16,8 @@
*/
package org.apache.juneau.a.rttests;
-import static org.apache.juneau.junit.bct.BctAssertions.*;
import static org.junit.jupiter.api.Assertions.*;
-import java.util.*;
-
import org.apache.juneau.annotation.*;
import org.junit.jupiter.params.*;
import org.junit.jupiter.params.provider.*;
@@ -29,50 +26,7 @@ import org.junit.jupiter.params.provider.*;
* Tests designed to serialize and parse objects to make sure we end up
* with the same objects for all serializers and parsers.
*/
-class ObjectsWithSpecialMethods_RoundTripTest extends RoundTripTest_Base {
-
-
//====================================================================================================
- // @NameProperty method.
-
//====================================================================================================
-
- @ParameterizedTest
- @MethodSource("testers")
- void a01_nameProperty(RoundTrip_Tester t) throws Exception {
- var x = new A().init();
- x = t.roundTrip(x);
- assertBean(x, "a2{f2},m{k1{f2}}", "{2},{{2}}");
- if (t.isValidationOnly())
- return;
- assertBean(x, "a2{name}", "{a2}");
- assertBean(x, "m{k1{name}}", "{{k1}}");
- }
-
- public static class A {
- public A2 a2;
- public Map<String,A2> m;
-
- A init() {
- a2 = new A2().init();
- m = new LinkedHashMap<>();
- m.put("k1", new A2().init());
- return this;
- }
-
- }
- public static class A2 {
- String name;
- public int f2;
-
- @NameProperty
- protected void setName(String name) {
- this.name = name;
- }
-
- A2 init() {
- f2 = 2;
- return this;
- }
- }
+class ParentProperty_RoundTripTest extends RoundTripTest_Base {
//====================================================================================================
// @ParentProperty method.
@@ -80,7 +34,7 @@ class ObjectsWithSpecialMethods_RoundTripTest extends
RoundTripTest_Base {
@ParameterizedTest
@MethodSource("testers")
- void a02_parentProperty(RoundTrip_Tester t) throws Exception {
+ void a01_parentProperty(RoundTrip_Tester t) throws Exception {
var x = new B().init();
x = t.roundTrip(x);
if (t.isValidationOnly())
@@ -113,4 +67,5 @@ class ObjectsWithSpecialMethods_RoundTripTest extends
RoundTripTest_Base {
return this;
}
}
-}
\ No newline at end of file
+}
+
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/annotation/NamePropertyAnnotation_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/annotation/NamePropertyAnnotation_Test.java
index be6be6407b..f8455e4cd0 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/annotation/NamePropertyAnnotation_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/annotation/NamePropertyAnnotation_Test.java
@@ -107,4 +107,107 @@ class NamePropertyAnnotation_Test extends TestBase {
assertNotEqualsAny(a1.hashCode(), 0, -1);
assertEqualsAll(a1.hashCode(), d1.hashCode(), d2.hashCode());
}
+
+
//------------------------------------------------------------------------------------------------------------------
+ // Property functionality tests.
+
//------------------------------------------------------------------------------------------------------------------
+
+ public static class TestBeanWithNamePropertyField {
+ @NameProperty
+ public String name;
+ }
+
+ public static class TestBeanWithNamePropertyMethod {
+ private String name;
+
+ @NameProperty
+ protected void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ @Test void e01_namePropertyField() throws Exception {
+ var bc = BeanContext.DEFAULT;
+ var cm = bc.getClassMeta(TestBeanWithNamePropertyField.class);
+ var prop = cm.getNameProperty();
+ assertNotNull(prop, "NameProperty should be found");
+ assertTrue(prop.canWrite(), "Should have setter");
+ assertTrue(prop.canRead(), "Should have getter");
+
+ var bean = new TestBeanWithNamePropertyField();
+ prop.set(bean, "testName");
+ assertEquals("testName", prop.get(bean));
+ assertEquals("testName", bean.name);
+ }
+
+ @Test void e02_namePropertyMethod() throws Exception {
+ var bc = BeanContext.DEFAULT;
+ var cm = bc.getClassMeta(TestBeanWithNamePropertyMethod.class);
+ var prop = cm.getNameProperty();
+ assertNotNull(prop, "NameProperty should be found");
+ assertTrue(prop.canWrite(), "Should have setter");
+ assertTrue(prop.canRead(), "Should have getter");
+
+ var bean = new TestBeanWithNamePropertyMethod();
+ prop.set(bean, "testName");
+ assertEquals("testName", prop.get(bean));
+ assertEquals("testName", bean.getName());
+ }
+
+ @Test void e03_namePropertyNotFound() {
+ var bc = BeanContext.DEFAULT;
+ var cm = bc.getClassMeta(String.class);
+ var prop = cm.getNameProperty();
+ assertNull(prop, "NameProperty should not be found on String
class");
+ }
+
+ public static class TestBeanWithNamePropertyIntegerField {
+ @NameProperty
+ public Integer id;
+ }
+
+ public static class TestBeanWithNamePropertyIntegerMethod {
+ private Integer id;
+
+ @NameProperty
+ protected void setId(Integer id) {
+ this.id = id;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+ }
+
+ @Test void e04_namePropertyWithIntegerField() throws Exception {
+ var bc = BeanContext.DEFAULT;
+ var cm =
bc.getClassMeta(TestBeanWithNamePropertyIntegerField.class);
+ var prop = cm.getNameProperty();
+ assertNotNull(prop, "NameProperty should be found");
+ assertTrue(prop.canWrite(), "Should have setter");
+ assertTrue(prop.canRead(), "Should have getter");
+
+ var bean = new TestBeanWithNamePropertyIntegerField();
+ prop.set(bean, 42);
+ assertEquals(42, prop.get(bean));
+ assertEquals(Integer.valueOf(42), bean.id);
+ }
+
+ @Test void e05_namePropertyWithIntegerMethod() throws Exception {
+ var bc = BeanContext.DEFAULT;
+ var cm =
bc.getClassMeta(TestBeanWithNamePropertyIntegerMethod.class);
+ var prop = cm.getNameProperty();
+ assertNotNull(prop, "NameProperty should be found");
+ assertTrue(prop.canWrite(), "Should have setter");
+ assertTrue(prop.canRead(), "Should have getter");
+
+ var bean = new TestBeanWithNamePropertyIntegerMethod();
+ prop.set(bean, 42);
+ assertEquals(42, prop.get(bean));
+ assertEquals(Integer.valueOf(42), bean.getId());
+ }
}
\ No newline at end of file
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/annotation/ParentPropertyAnnotation_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/annotation/ParentPropertyAnnotation_Test.java
index e8dab64323..51fd3565a2 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/annotation/ParentPropertyAnnotation_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/annotation/ParentPropertyAnnotation_Test.java
@@ -107,4 +107,67 @@ class ParentPropertyAnnotation_Test extends TestBase {
assertNotEqualsAny(a1.hashCode(), 0, -1);
assertEqualsAll(a1.hashCode(), d1.hashCode(), d2.hashCode());
}
+
+
//------------------------------------------------------------------------------------------------------------------
+ // Property functionality tests.
+
//------------------------------------------------------------------------------------------------------------------
+
+ public static class ParentBean {
+ public int value = 42;
+ }
+
+ public static class TestBeanWithParentPropertyField {
+ @ParentProperty
+ public ParentBean parent;
+ }
+
+ public static class TestBeanWithParentPropertyMethod {
+ private ParentBean parent;
+
+ @ParentProperty
+ protected void setParent(ParentBean parent) {
+ this.parent = parent;
+ }
+
+ public ParentBean getParent() {
+ return parent;
+ }
+ }
+
+ @Test void e01_parentPropertyField() throws Exception {
+ var bc = BeanContext.DEFAULT;
+ var cm = bc.getClassMeta(TestBeanWithParentPropertyField.class);
+ var prop = cm.getParentProperty();
+ assertNotNull(prop, "ParentProperty should be found");
+ assertTrue(prop.canWrite(), "Should have setter");
+ assertTrue(prop.canRead(), "Should have getter");
+
+ var bean = new TestBeanWithParentPropertyField();
+ var parent = new ParentBean();
+ prop.set(bean, parent);
+ assertSame(parent, prop.get(bean));
+ assertSame(parent, bean.parent);
+ }
+
+ @Test void e02_parentPropertyMethod() throws Exception {
+ var bc = BeanContext.DEFAULT;
+ var cm =
bc.getClassMeta(TestBeanWithParentPropertyMethod.class);
+ var prop = cm.getParentProperty();
+ assertNotNull(prop, "ParentProperty should be found");
+ assertTrue(prop.canWrite(), "Should have setter");
+ assertTrue(prop.canRead(), "Should have getter");
+
+ var bean = new TestBeanWithParentPropertyMethod();
+ var parent = new ParentBean();
+ prop.set(bean, parent);
+ assertSame(parent, prop.get(bean));
+ assertSame(parent, bean.getParent());
+ }
+
+ @Test void e03_parentPropertyNotFound() {
+ var bc = BeanContext.DEFAULT;
+ var cm = bc.getClassMeta(String.class);
+ var prop = cm.getParentProperty();
+ assertNull(prop, "ParentProperty should not be found on String
class");
+ }
}
\ No newline at end of file
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/Property_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/Property_Test.java
index 90835bc664..8249d78fc3 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/Property_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/Property_Test.java
@@ -90,8 +90,8 @@ class Property_Test extends TestBase {
var obj = new TestClass();
obj.setPublicField("testValue");
assertEquals("testValue", prop.get(obj));
- assertTrue(prop.hasGetter());
- assertFalse(prop.hasSetter());
+ assertTrue(prop.canRead());
+ assertFalse(prop.canWrite());
}
//====================================================================================================
@@ -106,8 +106,8 @@ class Property_Test extends TestBase {
var obj = new TestClass();
prop.set(obj, "testValue");
assertEquals("testValue", obj.getPublicField());
- assertFalse(prop.hasGetter());
- assertTrue(prop.hasSetter());
+ assertFalse(prop.canRead());
+ assertTrue(prop.canWrite());
}
//====================================================================================================
@@ -423,8 +423,8 @@ class Property_Test extends TestBase {
.build();
assertNotNull(prop);
- assertTrue(prop.hasGetter());
- assertTrue(prop.hasSetter());
+ assertTrue(prop.canRead());
+ assertTrue(prop.canWrite());
}
//====================================================================================================