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 831c0e7530 Marshall module improvements
831c0e7530 is described below
commit 831c0e75303b3978236cfa9ac3e5d83e72c60c89
Author: James Bognar <[email protected]>
AuthorDate: Sat Dec 6 18:20:48 2025 -0500
Marshall module improvements
---
.../juneau/commons/function/OptionalSupplier.java | 243 +++++++++++++++++++++
.../commons/function/ResettableSupplier.java | 14 +-
.../org/apache/juneau/commons/utils/Utils.java | 2 +-
.../src/main/java/org/apache/juneau/ClassMeta.java | 137 ++++++------
4 files changed, 331 insertions(+), 65 deletions(-)
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/OptionalSupplier.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/OptionalSupplier.java
new file mode 100644
index 0000000000..d182063e69
--- /dev/null
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/OptionalSupplier.java
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.commons.function;
+
+import static org.apache.juneau.commons.utils.AssertionUtils.*;
+import static org.apache.juneau.commons.utils.Utils.*;
+
+import java.util.*;
+import java.util.function.*;
+
+/**
+ * Combines the features of {@link Supplier} and {@link Optional} into a
single class.
+ *
+ * <p>
+ * This interface extends {@link Supplier} and provides convenience methods
for working with potentially null values,
+ * similar to {@link Optional}. The key difference is that this interface is
lazy - the value is only computed when
+ * {@link #get()} is called, and the Optional-like methods operate on that
computed value.
+ *
+ * <h5 class='section'>Features:</h5>
+ * <ul class='spaced-list'>
+ * <li>Extends Supplier - can be used anywhere a Supplier is expected
+ * <li>Optional-like API - provides isPresent(), isEmpty(), map(),
orElse(), etc.
+ * <li>Lazy evaluation - value is only computed when get() is called
+ * <li>Null-safe - handles null values gracefully
+ * </ul>
+ *
+ * <h5 class='section'>Usage:</h5>
+ * <p class='bjava'>
+ * <jc>// Create from a supplier</jc>
+ * OptionalSupplier<String> <jv>supplier</jv> =
OptionalSupplier.<jsm>of</jsm>(() -> <js>"value"</js>);
+ *
+ * <jc>// Check if value is present</jc>
+ * <jk>if</jk> (<jv>supplier</jv>.isPresent()) {
+ * String <jv>value</jv> = <jv>supplier</jv>.get();
+ * }
+ *
+ * <jc>// Map the value</jc>
+ * OptionalSupplier<Integer> <jv>length</jv> =
<jv>supplier</jv>.map(String::length);
+ *
+ * <jc>// Get value or default</jc>
+ * String <jv>result</jv> = <jv>supplier</jv>.orElse(<js>"default"</js>);
+ * </p>
+ *
+ * <h5 class='section'>See Also:</h5><ul>
+ * <li class='jc'>{@link Supplier} - Base functional interface
+ * <li class='jc'>{@link Optional} - Java's Optional class
+ * </ul>
+ *
+ * @param <T> The type of value supplied by this supplier.
+ */
+@FunctionalInterface
+public interface OptionalSupplier<T> extends Supplier<T> {
+
+ /**
+ * Creates an OptionalSupplier from a Supplier.
+ *
+ * @param <T> The value type.
+ * @param supplier The supplier. Must not be <jk>null</jk>.
+ * @return A new OptionalSupplier instance.
+ */
+ public static <T> OptionalSupplier<T> of(Supplier<T> supplier) {
+ assertArgNotNull("supplier", supplier);
+ return supplier::get;
+ }
+
+ /**
+ * Creates an OptionalSupplier that always returns the specified value.
+ *
+ * @param <T> The value type.
+ * @param value The value to return. Can be <jk>null</jk>.
+ * @return A new OptionalSupplier instance.
+ */
+ public static <T> OptionalSupplier<T> ofNullable(T value) {
+ return () -> value;
+ }
+
+ /**
+ * Creates an empty OptionalSupplier that always returns <jk>null</jk>.
+ *
+ * @param <T> The value type.
+ * @return A new OptionalSupplier instance that always returns
<jk>null</jk>.
+ */
+ public static <T> OptionalSupplier<T> empty() {
+ return () -> null;
+ }
+
+ /**
+ * Returns <jk>true</jk> if the supplied value is not <jk>null</jk>.
+ *
+ * @return <jk>true</jk> if the supplied value is not <jk>null</jk>.
+ */
+ default boolean isPresent() {
+ return nn(get());
+ }
+
+ /**
+ * Returns <jk>true</jk> if the supplied value is <jk>null</jk>.
+ *
+ * @return <jk>true</jk> if the supplied value is <jk>null</jk>.
+ */
+ default boolean isEmpty() {
+ return ! isPresent();
+ }
+
+ /**
+ * If a value is present, applies the provided mapping function to it
and returns an OptionalSupplier describing the result.
+ *
+ * @param <U> The type of the result of the mapping function.
+ * @param mapper A mapping function to apply to the value, if present.
Must not be <jk>null</jk>.
+ * @return An OptionalSupplier describing the result of applying a
mapping function to the value of this OptionalSupplier, if a value is present,
otherwise an empty OptionalSupplier.
+ */
+ default <U> OptionalSupplier<U> map(Function<? super T, ? extends U>
mapper) {
+ assertArgNotNull("mapper", mapper);
+ return () -> {
+ T value = get();
+ return nn(value) ? mapper.apply(value) : null;
+ };
+ }
+
+ /**
+ * If a value is present, returns the result of applying the given
OptionalSupplier-bearing mapping function to the value, otherwise returns an
empty OptionalSupplier.
+ *
+ * @param <U> The type parameter to the OptionalSupplier returned by
the mapping function.
+ * @param mapper A mapping function to apply to the value, if present.
Must not be <jk>null</jk>.
+ * @return The result of applying an OptionalSupplier-bearing mapping
function to the value of this OptionalSupplier, if a value is present,
otherwise an empty OptionalSupplier.
+ */
+ default <U> OptionalSupplier<U> flatMap(Function<? super T, ? extends
OptionalSupplier<? extends U>> mapper) {
+ assertArgNotNull("mapper", mapper);
+ return () -> {
+ T value = get();
+ if (nn(value)) {
+ OptionalSupplier<? extends U> result =
mapper.apply(value);
+ return result != null ? result.get() : null;
+ }
+ return null;
+ };
+ }
+
+ /**
+ * If a value is present, and the value matches the given predicate,
returns an OptionalSupplier describing the value, otherwise returns an empty
OptionalSupplier.
+ *
+ * @param predicate A predicate to apply to the value, if present. Must
not be <jk>null</jk>.
+ * @return An OptionalSupplier describing the value of this
OptionalSupplier if a value is present and the value matches the given
predicate, otherwise an empty OptionalSupplier.
+ */
+ default OptionalSupplier<T> filter(Predicate<? super T> predicate) {
+ assertArgNotNull("predicate", predicate);
+ return () -> {
+ T value = get();
+ return (nn(value) && predicate.test(value)) ? value :
null;
+ };
+ }
+
+ /**
+ * If a value is present, returns the value, otherwise returns
<jk>other</jk>.
+ *
+ * @param other The value to be returned if there is no value present.
Can be <jk>null</jk>.
+ * @return The value, if present, otherwise <jk>other</jk>.
+ */
+ default T orElse(T other) {
+ T value = get();
+ return nn(value) ? value : other;
+ }
+
+ /**
+ * If a value is present, returns the value, otherwise returns the
result produced by the supplying function.
+ *
+ * @param other A {@link Supplier} whose result is returned if no value
is present. Must not be <jk>null</jk>.
+ * @return The value, if present, otherwise the result of
<jk>other.get()</jk>.
+ */
+ default T orElseGet(Supplier<? extends T> other) {
+ assertArgNotNull("other", other);
+ T value = get();
+ return nn(value) ? value : other.get();
+ }
+
+ /**
+ * If a value is present, returns the value, otherwise throws an
exception produced by the exception supplying function.
+ *
+ * @param <X> Type of the exception to be thrown.
+ * @param exceptionSupplier The supplying function that produces an
exception to be thrown. Must not be <jk>null</jk>.
+ * @return The value, if present.
+ * @throws X If no value is present.
+ */
+ default <X extends Throwable> T orElseThrow(Supplier<? extends X>
exceptionSupplier) throws X {
+ assertArgNotNull("exceptionSupplier", exceptionSupplier);
+ T value = get();
+ if (nn(value))
+ return value;
+ throw exceptionSupplier.get();
+ }
+
+ /**
+ * If a value is present, performs the given action with the value,
otherwise does nothing.
+ *
+ * @param action The action to be performed, if a value is present.
Must not be <jk>null</jk>.
+ */
+ default void ifPresent(Consumer<? super T> action) {
+ assertArgNotNull("action", action);
+ T value = get();
+ if (nn(value))
+ action.accept(value);
+ }
+
+ /**
+ * If a value is present, performs the given action with the value,
otherwise performs the given empty-based action.
+ *
+ * @param action The action to be performed, if a value is present.
Must not be <jk>null</jk>.
+ * @param emptyAction The empty-based action to be performed, if no
value is present. Must not be <jk>null</jk>.
+ */
+ default void ifPresentOrElse(Consumer<? super T> action, Runnable
emptyAction) {
+ assertArgNotNull("action", action);
+ assertArgNotNull("emptyAction", emptyAction);
+ T value = get();
+ if (nn(value))
+ action.accept(value);
+ else
+ emptyAction.run();
+ }
+
+ /**
+ * Converts this OptionalSupplier to an {@link Optional}.
+ *
+ * @return An Optional containing the value if present, otherwise an
empty Optional.
+ */
+ default Optional<T> toOptional() {
+ return opt(get());
+ }
+}
+
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/ResettableSupplier.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/ResettableSupplier.java
index 68bece2bda..98b4022039 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/ResettableSupplier.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/ResettableSupplier.java
@@ -27,8 +27,9 @@ import java.util.function.*;
* A thread-safe supplier that caches the result of the first call and
supports resetting the cache.
*
* <p>
- * This class provides both standard {@link Supplier#get()} functionality and
a {@link #reset()} method
- * to clear the cache, forcing recomputation on the next call to {@link
#get()}.
+ * This class extends {@link OptionalSupplier} to provide both standard {@link
Supplier#get()} functionality
+ * and a {@link #reset()} method to clear the cache, forcing recomputation on
the next call to {@link #get()}.
+ * It also inherits all Optional-like convenience methods from {@link
OptionalSupplier}.
*
* <h5 class='section'>Usage:</h5>
* <p class='bjava'>
@@ -41,6 +42,11 @@ import java.util.function.*;
* <jc>// Subsequent calls return cached value</jc>
* String <jv>result2</jv> = <jv>supplier</jv>.get();
*
+ * <jc>// Use Optional-like methods</jc>
+ * <jk>if</jk> (<jv>supplier</jv>.isPresent()) {
+ * String <jv>upper</jv> =
<jv>supplier</jv>.map(String::toUpperCase).orElse(<js>"default"</js>);
+ * }
+ *
* <jc>// Reset forces recomputation on next get()</jc>
* <jv>supplier</jv>.reset();
* String <jv>result3</jv> = <jv>supplier</jv>.get(); <jc>//
Recomputes</jc>
@@ -58,16 +64,18 @@ import java.util.function.*;
* <li>The cached value can be <jk>null</jk> if the supplier returns
<jk>null</jk>.
* <li>After reset, the next get() will recompute the value.
* <li>This is particularly useful for testing when configuration changes
and cached values need to be recalculated.
+ * <li>Inherits all Optional-like methods from {@link OptionalSupplier}
(isPresent(), isEmpty(), map(), orElse(), etc.).
* </ul>
*
* <h5 class='section'>See Also:</h5>
* <ul>
* <li class='jm'>{@link
org.apache.juneau.commons.utils.Utils#memoizeResettable(Supplier)}
+ * <li class='jc'>{@link OptionalSupplier} - Base interface providing
Optional-like methods
* </ul>
*
* @param <T> The type of value supplied.
*/
-public class ResettableSupplier<T> implements Supplier<T> {
+public class ResettableSupplier<T> implements OptionalSupplier<T> {
private final Supplier<T> supplier;
private final AtomicReference<Optional<T>> cache = new
AtomicReference<>();
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/Utils.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/Utils.java
index 5598f1cb8d..3a67bf6090 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/Utils.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/Utils.java
@@ -1069,7 +1069,7 @@ public class Utils {
* @return A thread-safe memoizing wrapper around the supplier.
* @throws NullPointerException if supplier is <jk>null</jk>.
*/
- public static <T> Supplier<T> memoize(Supplier<T> supplier) {
+ public static <T> OptionalSupplier<T> memoize(Supplier<T> supplier) {
assertArgNotNull("supplier", supplier);
var cache = new AtomicReference<Optional<T>>();
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 6c5c903fb2..e1d0cb96e9 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
@@ -35,6 +35,7 @@ import java.util.function.*;
import org.apache.juneau.annotation.*;
import org.apache.juneau.commons.collections.*;
+import org.apache.juneau.commons.function.*;
import org.apache.juneau.commons.reflect.*;
import org.apache.juneau.commons.utils.*;
import org.apache.juneau.cp.*;
@@ -152,24 +153,24 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
private final ConcurrentHashMap<Class<?>,ObjectSwap<?,?>>
childUnswapMap; // Maps swap subclasses
to ObjectSwaps.
private final Supplier<String> dictionaryName;
// The dictionary name of this class if
it has one.
private final ClassMeta<?> elementType;
// If ARRAY or COLLECTION, the element
class type.
- private final Supplier<String> example;
// Example JSON.
- private final Supplier<FieldInfo> exampleField;
// The @Example-annotated field (if it
has one).
- private final Supplier<MethodInfo> exampleMethod;
// The example() or @Example-annotated
method (if it has one).
+ private final OptionalSupplier<String> example;
// Example JSON.
+ private final OptionalSupplier<FieldInfo> exampleField;
// The @Example-annotated field
(if it has one).
+ private final OptionalSupplier<MethodInfo> exampleMethod;
// The example() or
@Example-annotated method (if it has one).
private final Supplier<BidiMap<Object,String>> enumValues;
private final Map<Class<?>,Mutater<?,T>> fromMutaters = new
ConcurrentHashMap<>();
- private final Supplier<MethodInfo> fromStringMethod;
// Static fromString(String) or
equivalent method
- private final Supplier<ClassInfoTyped<? extends T>> implClass2;
// The implementation class to use if
this is an interface.
- private final InvocationHandler invocationHandler;
// The invocation handler for this
class (if it has one).
+ private final OptionalSupplier<MethodInfo> fromStringMethod;
// Static fromString(String) or
equivalent method
+ private final OptionalSupplier<ClassInfoTyped<? extends T>> implClass;
// The implementation class to
use if this is an interface.
+ private final OptionalSupplier<InvocationHandler>
proxyInvocationHandler;
// The invocation handler for this class (if it has one).
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,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 OptionalSupplier<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,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).
+ private final OptionalSupplier<ConstructorInfo> stringConstructor;
// The X(String) constructor (if
it has one).
private final ObjectSwap<T,?>[] swaps;
// The object POJO swaps associated
with this bean (if it has any).
private final Map<Class<?>,Mutater<T,?>> toMutaters = new
ConcurrentHashMap<>();
private final String typePropertyName;
// The property name of the _type
property for this class and subclasses.
@@ -264,7 +265,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
marshalledFilter = memoize(()->findMarshalledFilter());
builderSwap = memoize(()->findBuilderSwap());
example = memoize(()->findExample());
- implClass2 = memoize(()->findImplClass());
+ implClass = memoize(()->findImplClass());
var _elementType = (ClassMeta<?>)null;
var _keyType = (ClassMeta<?>)null;
@@ -292,14 +293,14 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
}
}
- BeanMeta<T> _beanMeta = null;
+ var _beanMeta = (BeanMeta<T>)null;
var _beanRegistry = new Value<BeanRegistry>();
if (! cat.isUnknown()) {
notABeanReason = "Known non-bean type";
} else {
try {
- _beanMeta = new
BeanMeta<>(ClassMeta.this, beanContext, beanFilter.get(), null,
implClass2.get() == null ? null : noArgConstructor.get());
+ _beanMeta = new
BeanMeta<>(ClassMeta.this, beanContext, beanFilter.get(), null, implClass.get()
== null ? null : noArgConstructor.get());
notABeanReason =
_beanMeta.notABeanReason;
_beanRegistry.set(_beanMeta.beanRegistry);
} catch (RuntimeException e) {
@@ -307,11 +308,6 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
}
}
- ap.find(Bean.class,
this).stream().map(AnnotationInfo::inner).forEach(x2 -> {
- if (x2.dictionary().length != 0)
- _beanRegistry.set(new
BeanRegistry(beanContext, null, x2.dictionary()));
- });
-
this.beanMeta = notABeanReason == null ? _beanMeta :
null;
if (nn(this.beanMeta))
cat.set(BEAN);
@@ -343,7 +339,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
this.swaps = _swaps.isEmpty() ? null :
_swaps.toArray(new ObjectSwap[_swaps.size()]);
this.typePropertyName = opt(beanMeta).map(x2 ->
x2.typePropertyName).orElse(null);
- this.invocationHandler = (nn(beanMeta) &&
beanContext.isUseInterfaceProxies() && isInterface()) ? new
BeanProxyInvocationHandler<>(beanMeta) : null;
+ this.proxyInvocationHandler = ()->(nn(beanMeta) &&
beanContext.isUseInterfaceProxies() && isInterface()) ? new
BeanProxyInvocationHandler<>(beanMeta) : null;
this.beanRegistry = _beanRegistry.get();
this.childSwaps = childSwaps;
@@ -370,7 +366,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
this.elementType = null;
this.keyType = null;
this.valueType = null;
- this.invocationHandler = null;
+ this.proxyInvocationHandler = null;
this.beanMeta = null;
this.typePropertyName = null;
this.notABeanReason = null;
@@ -388,7 +384,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
this.marshalledFilter = memoize(()->findMarshalledFilter());
this.builderSwap = memoize(()->findBuilderSwap());
this.example = memoize(()->findExample());
- this.implClass2 = memoize(()->findImplClass());
+ this.implClass = memoize(()->findImplClass());
this.enumValues = memoize(()->findEnumValues());
this.dictionaryName = memoize(()->findBeanDictionaryName());
}
@@ -410,7 +406,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
this.elementType = elementType;
this.keyType = keyType;
this.valueType = valueType;
- this.invocationHandler = mainType.invocationHandler;
+ this.proxyInvocationHandler = mainType.proxyInvocationHandler;
this.beanMeta = mainType.beanMeta;
this.typePropertyName = mainType.typePropertyName;
this.notABeanReason = mainType.notABeanReason;
@@ -428,10 +424,11 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
this.marshalledFilter = mainType.marshalledFilter;
this.builderSwap = mainType.builderSwap;
this.example = mainType.example;
- this.implClass2 = mainType.implClass2;
+ this.implClass = mainType.implClass;
this.enumValues = mainType.enumValues;
this.dictionaryName = mainType.dictionaryName;
}
+
/**
* Returns <jk>true</jk> if this class can be instantiated as a bean.
* Returns <jk>false</jk> if this is a non-static member class and the
outer object does not match the class type of
@@ -458,7 +455,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
public boolean canCreateNewInstance() {
if (isMemberClass() && isNotStatic())
return false;
- if (nn(noArgConstructor.get()) ||
nn(getProxyInvocationHandler()) || (super.isArray() &&
elementType.canCreateNewInstance()))
+ if (noArgConstructor.isPresent() ||
proxyInvocationHandler.isPresent() || (isArray() &&
elementType.canCreateNewInstance()))
return true;
return false;
}
@@ -475,7 +472,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
*/
public boolean canCreateNewInstance(Object outer) {
if (isMemberClass() && isNotStatic())
- return nn(outer) && nn(noArgConstructor.get()) &&
noArgConstructor.get().hasParameterTypes(outer.getClass());
+ return nn(outer) && noArgConstructor.map(x ->
x.hasParameterTypes(outer.getClass())).orElse(false);
return canCreateNewInstance();
}
@@ -488,11 +485,11 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
* @return <jk>true</jk> if this class has a no-arg constructor or
invocation handler.
*/
public boolean canCreateNewInstanceFromString(Object outer) {
- if (nn(fromStringMethod.get()))
+ if (fromStringMethod.isPresent())
return true;
- if (nn(stringConstructor.get())) {
+ if (stringConstructor.isPresent()) {
if (isMemberClass() && isNotStatic())
- return nn(outer) &&
stringConstructor.get().hasParameterTypes(outer.getClass(), String.class);
+ return nn(outer) && stringConstructor.map(x ->
x.hasParameterTypes(outer.getClass(), String.class)).orElse(false);
return true;
}
return false;
@@ -590,13 +587,6 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
return builderSwap.get();
}
- /**
- * Returns the no-arg constructor for this class.
- *
- * @return The no-arg constructor for this class, or <jk>null</jk> if
it does not exist.
- */
- public ConstructorInfo getConstructor() { return
noArgConstructor.get(); }
-
/**
* Returns the bean dictionary name associated with this class.
*
@@ -629,11 +619,11 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
@SuppressWarnings({ "unchecked" })
public T getExample(BeanSession session, JsonParserSession jpSession) {
try {
- if (nn(example.get()))
+ if (example.isPresent())
return jpSession.parse(example.get(), this);
- if (nn(exampleMethod.get()))
+ if (exampleMethod.isPresent())
return
(T)exampleMethod.get().invokeLenient(null, session);
- if (nn(exampleField.get()))
+ if (exampleField.isPresent())
return (T)exampleField.get().get(null);
if (isCollection()) {
@@ -700,9 +690,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
* @return The no-arg constructor for this class, or <jk>null</jk> if
it does not exist.
*/
public ConstructorInfo getImplClassConstructor(Visibility conVis) {
- if (nn(implClass2.get()))
- return
implClass2.get().getNoArgConstructor(conVis).orElse(null);
- return null;
+ return implClass.map(x ->
x.getNoArgConstructor(conVis).orElse(null)).orElse(null);
}
/**
@@ -778,12 +766,42 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
public Property<T,Object> getParentProperty() { return
parentProperty.get(); }
/**
- * Returns a calculated property on this context.
+ * Returns a lazily-computed, cached property value for this {@link
ClassMeta} instance.
*
- * @param <T2> The type to convert the property to.
- * @param name The name of the property.
- * @param function The function used to create this property.
- * @return The property value. Never <jk>null</jk>.
+ * <p>
+ * This method provides a memoization mechanism for expensive
computations. The property value is computed
+ * on the first call using the provided function, then cached for
subsequent calls with the same property name.
+ *
+ * <p>
+ * The function is only invoked once per property name per {@link
ClassMeta} instance. Subsequent calls
+ * with the same name will return the cached value without re-invoking
the function.
+ *
+ * <h5 class='section'>Thread Safety:</h5>
+ * <p>
+ * This method is thread-safe. If multiple threads call this method
simultaneously with the same property name,
+ * the function may be invoked multiple times, but only one result will
be cached and returned.
+ *
+ * <h5 class='section'>Usage:</h5>
+ * <p class='bjava'>
+ * <jc>// Compute and cache an expensive property</jc>
+ * Optional<String> <jv>computedValue</jv> =
classMeta.<jsm>getProperty</jsm>(<js>"expensiveProperty"</js>, cm -> {
+ * <jc>// Expensive computation that only runs once</jc>
+ * <jk>return</jk> performExpensiveComputation(cm);
+ * });
+ *
+ * <jc>// Subsequent calls return cached value</jc>
+ * Optional<String> <jv>cached</jv> =
classMeta.<jsm>getProperty</jsm>(<js>"expensiveProperty"</js>, cm -> {
+ * <jc>// This function is NOT called again</jc>
+ * <jk>return</jk> performExpensiveComputation(cm);
+ * });
+ * </p>
+ *
+ * @param <T2> The type of the property value.
+ * @param name The unique name identifying this property. Used as the
cache key.
+ * @param function The function that computes the property value.
Receives this {@link ClassMeta} instance as input.
+ * Only invoked if the property hasn't been computed yet. Can
return <jk>null</jk>.
+ * @return An {@link Optional} containing the property value if the
function returned a non-null value,
+ * otherwise an empty {@link Optional}. Never <jk>null</jk>.
*/
@SuppressWarnings("unchecked")
public <T2> Optional<T2> getProperty(String name,
Function<ClassMeta<?>,T2> function) {
@@ -800,7 +818,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
*
* @return The interface proxy invocation handler, or <jk>null</jk> if
it does not exist.
*/
- public InvocationHandler getProxyInvocationHandler() { return
invocationHandler; }
+ public InvocationHandler getProxyInvocationHandler() { return
proxyInvocationHandler.get(); }
/**
* Returns the transform for this class for creating instances from a
Reader.
@@ -1284,9 +1302,8 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
public T newInstance() throws ExecutableException {
if (super.isArray())
return (T)Array.newInstance(inner().getComponentType(),
0);
- var c = getConstructor();
- if (nn(c))
- return c.<T>newInstance();
+ if (noArgConstructor.isPresent())
+ return noArgConstructor.get().newInstance();
var h = getProxyInvocationHandler();
if (nn(h))
return
(T)Proxy.newProxyInstance(this.getClass().getClassLoader(), a(inner(),
java.io.Serializable.class), h);
@@ -1303,7 +1320,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
* @throws ExecutableException Exception occurred on invoked
constructor/method/field.
*/
public T newInstance(Object outer) throws ExecutableException {
- if (isMemberClass() && isNotStatic())
+ if (isMemberClass() && isNotStatic() &&
noArgConstructor.isPresent())
return noArgConstructor.get().<T>newInstance(outer);
return newInstance();
}
@@ -1335,15 +1352,13 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
return t;
}
- var m = fromStringMethod.get();
- if (nn(m)) {
- return (T)m.invoke(null, arg);
- }
- var c = stringConstructor.get();
- if (nn(c)) {
+ if (fromStringMethod.isPresent())
+ return (T)fromStringMethod.get().invoke(null, arg);
+
+ if (stringConstructor.isPresent()) {
if (isMemberClass() && isNotStatic())
- return c.<T>newInstance(outer, arg);
- return c.<T>newInstance(arg);
+ return
stringConstructor.get().<T>newInstance(outer, arg);
+ return stringConstructor.get().<T>newInstance(arg);
}
throw new ExecutableException("No string constructor or
valueOf(String) method found for class '" + inner().getName() + "'");
}
@@ -1649,8 +1664,8 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
if (is(Object.class))
return null;
- if (implClass2.get() != null)
- return implClass2.get().getPublicConstructor(x ->
x.hasNumParameters(0)).orElse(null);
+ if (implClass.isPresent())
+ return implClass.get().getPublicConstructor(x ->
x.hasNumParameters(0)).orElse(null);
if (isAbstract())
return null;
@@ -1741,8 +1756,8 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
if (is(Object.class) || isAbstract())
return null;
- if (implClass2.get() != null)
- return implClass2.get().getPublicConstructor(x ->
x.hasParameterTypes(String.class)).orElse(null);
+ if (implClass.isPresent())
+ return implClass.get().getPublicConstructor(x ->
x.hasParameterTypes(String.class)).orElse(null);
if (isAbstract())
return null;