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 9a7d1a05f7 Marshall module improvements
9a7d1a05f7 is described below

commit 9a7d1a05f79c8e8f90f6c4bd166403f99626471f
Author: James Bognar <[email protected]>
AuthorDate: Fri Dec 12 17:20:15 2025 -0500

    Marshall module improvements
---
 .../org/apache/juneau/commons/function/Tuple1.java |  2 +-
 .../org/apache/juneau/commons/function/Tuple2.java |  2 +-
 .../org/apache/juneau/commons/function/Tuple3.java |  2 +-
 .../org/apache/juneau/commons/function/Tuple4.java |  2 +-
 .../org/apache/juneau/commons/function/Tuple5.java |  2 +-
 .../org/apache/juneau/commons/io/LocalDir.java     |  2 +-
 .../org/apache/juneau/commons/utils/HashCode.java  | 36 ++++++++-
 .../org/apache/juneau/commons/utils/Utils.java     | 26 ++++---
 .../java/org/apache/juneau/BeanPropertyMeta.java   | 86 +++++++++++-----------
 .../java/org/apache/juneau/cp/BasicFileFinder.java |  4 +-
 .../juneau/rest/staticfile/BasicStaticFiles.java   |  4 +-
 .../org/apache/juneau/rest/stats/ThrownStats.java  |  4 +-
 12 files changed, 104 insertions(+), 68 deletions(-)

diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/Tuple1.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/Tuple1.java
index 0e00ba0feb..7edc1872a3 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/Tuple1.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/Tuple1.java
@@ -112,7 +112,7 @@ public class Tuple1<A> {
         */
        public Tuple1(A a) {
                this.a = a;
-               this.hashCode = HashCode.of(a);
+               this.hashCode = hash(a);
        }
 
        @Override /* Overridden from Object */
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/Tuple2.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/Tuple2.java
index b49e791d3a..fcdf0aca1d 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/Tuple2.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/Tuple2.java
@@ -120,7 +120,7 @@ public class Tuple2<A,B> {
        public Tuple2(A a, B b) {
                this.a = a;
                this.b = b;
-               this.hashCode = HashCode.of(a, b);
+               this.hashCode = hash(a, b);
        }
 
        @Override /* Overridden from Object */
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/Tuple3.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/Tuple3.java
index c7a7496a69..c8f151b690 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/Tuple3.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/Tuple3.java
@@ -79,7 +79,7 @@ public class Tuple3<A,B,C> {
                this.a = a;
                this.b = b;
                this.c = c;
-               this.hashCode = HashCode.of(a, b, c);
+               this.hashCode = hash(a, b, c);
        }
 
        @Override /* Overridden from Object */
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/Tuple4.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/Tuple4.java
index 5127cb6efc..5a67f01e9f 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/Tuple4.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/Tuple4.java
@@ -85,7 +85,7 @@ public class Tuple4<A,B,C,D> {
                this.b = b;
                this.c = c;
                this.d = d;
-               this.hashCode = HashCode.of(a, b, c, d);
+               this.hashCode = hash(a, b, c, d);
        }
 
        @Override /* Overridden from Object */
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/Tuple5.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/Tuple5.java
index cdab0c8ad6..717bf85a13 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/Tuple5.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/function/Tuple5.java
@@ -91,7 +91,7 @@ public class Tuple5<A,B,C,D,E> {
                this.c = c;
                this.d = d;
                this.e = e;
-               this.hashCode = HashCode.of(a, b, c, d, e);
+               this.hashCode = hash(a, b, c, d, e);
        }
 
        @Override /* Overridden from Object */
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/io/LocalDir.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/io/LocalDir.java
index a09bc08fd4..4ebebe3b89 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/io/LocalDir.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/io/LocalDir.java
@@ -146,7 +146,7 @@ public class LocalDir {
                this.clazz = assertArgNotNull("clazz", clazz);
                this.clazzPath = "/".equals(clazzPath) ? "/" : 
StringUtils.nullIfEmpty(trimTrailingSlashes(clazzPath));
                this.path = null;
-               this.hashCode = HashCode.of(clazz, clazzPath);
+               this.hashCode = hash(clazz, clazzPath);
        }
 
        /**
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/HashCode.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/HashCode.java
index 7f63192b08..5d291a7ec2 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/HashCode.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/HashCode.java
@@ -16,6 +16,8 @@
  */
 package org.apache.juneau.commons.utils;
 
+import java.lang.annotation.*;
+
 /**
  * Utility class for generating integer hash codes.
  *
@@ -40,8 +42,24 @@ public class HashCode {
        /**
         * Calculates a hash code over the specified objects.
         *
+        * <p>
+        * Uses the same algorithm as {@link java.util.Objects#hash(Object...)} 
(31 * result + element hash).
+        *
+        * <p>
+        * Special handling is provided for:
+        * <ul>
+        *      <li><b>Annotations:</b> Uses {@link 
AnnotationUtils#hash(Annotation)} to ensure consistent hashing
+        *              according to the {@link 
java.lang.annotation.Annotation#hashCode()} contract.
+        *      <li><b>Arrays:</b> Uses content-based hashing via {@link 
java.util.Arrays#hashCode(Object[])}
+        *              instead of identity-based hashing.
+        *      <li><b>Null values:</b> Treated as 0 in the hash calculation.
+        * </ul>
+        *
         * @param objects The objects to calculate a hashcode over.
         * @return A numerical hashcode value.
+        * @see #add(Object)
+        * @see AnnotationUtils#hash(Annotation)
+        * @see java.util.Objects#hash(Object...)
         */
        public static final int of(Object...objects) {
                HashCode x = create();
@@ -70,16 +88,30 @@ public class HashCode {
         * Hashes the hashcode of the specified object into this object.
         *
         * <p>
-        * Arrays are handled specially to use content-based hashing via {@link 
java.util.Arrays#hashCode(Object[])}
-        * instead of identity-based hashing.
+        * The formula is <c>hashCode = 31*hashCode + elementHash;</c>
+        *
+        * <p>
+        * Special handling is provided for:
+        * <ul>
+        *      <li><b>Null values:</b> Adds 0 to the hash code.
+        *      <li><b>Annotations:</b> Uses {@link 
AnnotationUtils#hash(Annotation)} to ensure consistent hashing
+        *              according to the {@link 
java.lang.annotation.Annotation#hashCode()} contract.
+        *      <li><b>Arrays:</b> Uses content-based hashing via {@link 
java.util.Arrays#hashCode(Object[])}
+        *              instead of identity-based hashing. Supports all 
primitive array types and object arrays.
+        *      <li><b>Other objects:</b> Uses the object's {@link 
Object#hashCode()} method.
+        * </ul>
         *
         * @param o The object whose hashcode will be hashed with this object.
         * @return This object.
+        * @see AnnotationUtils#hash(Annotation)
+        * @see java.util.Arrays#hashCode(Object[])
         */
        public HashCode add(Object o) {
                o = unswap(o);
                if (o == null) {
                        add(0);
+               } else if (o instanceof Annotation a) {
+                       add(AnnotationUtils.hash(a));
                } else if (o.getClass().isArray()) {
                        // Use content-based hashcode for arrays
                        if (o instanceof Object[])
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 510fe76bdc..0774097927 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
@@ -662,10 +662,18 @@ public class Utils {
         * Calculates a hash code for the specified values.
         *
         * <p>
-        * This method combines multiple values into a single hash code using 
the same algorithm as
-        * {@link Objects#hash(Object...)}. It handles annotations specially by 
delegating to
-        * {@link AnnotationUtils#hash(Annotation)} to ensure consistent 
hashing according to the
-        * {@link java.lang.annotation.Annotation#hashCode()} contract.
+        * This method delegates to {@link HashCode#of(Object...)} to combine 
multiple values into a single hash code.
+        * It uses the same algorithm as {@link Objects#hash(Object...)} (31 * 
result + element hash).
+        *
+        * <p>
+        * Special handling is provided for:
+        * <ul>
+        *      <li><b>Annotations:</b> Uses {@link 
AnnotationUtils#hash(Annotation)} to ensure consistent hashing
+        *              according to the {@link 
java.lang.annotation.Annotation#hashCode()} contract.
+        *      <li><b>Arrays:</b> Uses content-based hashing via {@link 
java.util.Arrays#hashCode(Object[])}
+        *              instead of identity-based hashing.
+        *      <li><b>Null values:</b> Treated as 0 in the hash calculation.
+        * </ul>
         *
         * <h5 class='section'>Example:</h5>
         * <p class='bjava'>
@@ -675,6 +683,9 @@ public class Utils {
         *      <jc>// Hash with annotations</jc>
         *      <jk>int</jk> <jv>hash2</jv> = hash(<jv>myAnnotation</jv>, 
<js>"value"</js>);
         *
+        *      <jc>// Hash with arrays (content-based)</jc>
+        *      <jk>int</jk> <jv>hash3</jv> = hash(<jk>new</jk> 
<jk>int</jk>[]{1, 2, 3});
+        *
         *      <jc>// Use in hashCode() implementation</jc>
         *      <jk>public</jk> <jk>int</jk> hashCode() {
         *              <jk>return</jk> hash(id, name, created);
@@ -683,16 +694,13 @@ public class Utils {
         *
         * @param values The values to hash.
         * @return A hash code value for the given values.
+        * @see HashCode#of(Object...)
         * @see AnnotationUtils#hash(Annotation)
         * @see Objects#hash(Object...)
         */
        public static final int hash(Object...values) {
                assertArgNotNull("values", values);
-               var result = 1;
-               for (var value : values) {
-                       result = 31 * result + (value instanceof 
java.lang.annotation.Annotation ? 
AnnotationUtils.hash((java.lang.annotation.Annotation)value) : 
Objects.hashCode(value));
-               }
-               return result;
+               return HashCode.of(values);
        }
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
index 69e7d93e52..39445898c5 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
@@ -35,7 +35,6 @@ import org.apache.juneau.annotation.*;
 import org.apache.juneau.collections.*;
 import org.apache.juneau.commons.collections.*;
 import org.apache.juneau.commons.reflect.*;
-import org.apache.juneau.commons.utils.*;
 import org.apache.juneau.cp.*;
 import org.apache.juneau.internal.*;
 import org.apache.juneau.parser.*;
@@ -433,33 +432,30 @@ public class BeanPropertyMeta implements 
Comparable<BeanPropertyMeta> {
                return new Builder(beanMeta, name);
        }
 
-       final BeanMeta<?> beanMeta;                               // The bean 
that this property belongs to.
-       private final BeanContext bc;                    // The context that 
created this meta.
-       private final AnnotationProvider ap;
-       private final String name;                                // The name 
of the property.
-       private final FieldInfo field;                                // The 
bean property field (if it has one).
-       private final FieldInfo innerField;                                // 
The bean property field (if it has one).
-
-       private final MethodInfo getter;           // The bean property getter.
-       private final MethodInfo setter;           // The bean property setter.
-       private final MethodInfo extraKeys;           // The bean property 
extraKeys.
-
-       private final boolean isUri;                              // True if 
this is a URL/URI or annotated with @URI.
-       private final boolean isDyna, isDynaGetterMap;            // This is a 
dyna property (i.e. name="*")
-
-       private final ClassMeta<?> rawTypeMeta,                                 
          // The real class type of the bean property.
-       typeMeta;                                              // The 
transformed class type of the bean property.
-
-       private final List<String> properties;                        // The 
value of the @Beanp(properties) annotation (unmodifiable).
-       private final ObjectSwap swap;                              // 
ObjectSwap defined only via @Beanp annotation.
-       private final BeanRegistry beanRegistry;
-       private final Object overrideValue;                       // The bean 
property value (if it's an overridden delegate).
-
-       private final BeanPropertyMeta delegateFor;               // The bean 
property that this meta is a delegate for.
-
-       private final boolean canRead, canWrite, readOnly, writeOnly;
-
-       private final int hashCode;
+       private final AnnotationProvider ap;                   // Annotation 
provider for finding annotations on this property.
+       private final BeanContext bc;                          // The context 
that created this meta.
+       private final BeanMeta<?> beanMeta;                    // The bean that 
this property belongs to.
+       private final BeanRegistry beanRegistry;               // Bean registry 
for resolving bean types in this property.
+       private final boolean canRead;                         // True if this 
property can be read.
+       private final boolean canWrite;                        // True if this 
property can be written.
+       private final BeanPropertyMeta delegateFor;            // The bean 
property that this meta is a delegate for.
+       private final MethodInfo extraKeys;                    // The bean 
property extraKeys method.
+       private final FieldInfo field;                         // The bean 
property field (if it has one).
+       private final MethodInfo getter;                       // The bean 
property getter.
+       private final int hashCode;                            // Cached hash 
code for this property meta.
+       private final FieldInfo innerField;                    // The bean 
property field even if private (if it has one).
+       private final boolean isDyna;                          // True if this 
is a dyna property (i.e. name="*").
+       private final boolean isDynaGetterMap;                 // True if this 
is a dyna property where the getter returns a Map directly.
+       private final boolean isUri;                           // True if this 
is a URL/URI or annotated with @URI.
+       private final String name;                             // The name of 
the property.
+       private final Object overrideValue;                    // The bean 
property value (if it's an overridden delegate).
+       private final List<String> properties;                 // The value of 
the @Beanp(properties) annotation (unmodifiable).
+       private final ClassMeta<?> rawTypeMeta;                // The real 
class type of the bean property.
+       private final boolean readOnly;                        // True if this 
property is read-only.
+       private final MethodInfo setter;                       // The bean 
property setter.
+       private final ObjectSwap swap;                         // ObjectSwap 
defined only via @Beanp annotation.
+       private final ClassMeta<?> typeMeta;                   // The 
transformed class type of the bean property.
+       private final boolean writeOnly;                       // True if this 
property is write-only.
 
        /**
         * Creates a new BeanPropertyMeta using the contents of the specified 
builder.
@@ -467,30 +463,30 @@ public class BeanPropertyMeta implements 
Comparable<BeanPropertyMeta> {
         * @param b The builder to copy fields from.
         */
        protected BeanPropertyMeta(Builder b) {
-               field = b.field;
-               innerField = b.innerField;
-               getter = b.getter;
-               setter = b.setter;
-               extraKeys = b.extraKeys;
-               isUri = b.isUri;
-               beanMeta = b.beanMeta;
                bc = b.bc;
-               ap = bc.getAnnotationProvider();
-               name = b.name;
-               rawTypeMeta = b.rawTypeMeta;
-               typeMeta = b.typeMeta;
-               properties = b.properties == null ? null : u(b.properties);
-               swap = b.swap;
+               beanMeta = b.beanMeta;
                beanRegistry = b.beanRegistry;
-               overrideValue = b.overrideValue;
+               canRead = b.canRead;
+               canWrite = b.canWrite;
                delegateFor = b.delegateFor;
+               extraKeys = b.extraKeys;
+               field = b.field;
+               getter = b.getter;
+               innerField = b.innerField;
                isDyna = b.isDyna;
                isDynaGetterMap = b.isDynaGetterMap;
-               canRead = b.canRead;
-               canWrite = b.canWrite;
+               isUri = b.isUri;
+               name = b.name;
+               overrideValue = b.overrideValue;
+               properties = u(b.properties);
+               rawTypeMeta = b.rawTypeMeta;
                readOnly = b.readOnly;
+               setter = b.setter;
+               swap = b.swap;
+               typeMeta = b.typeMeta;
                writeOnly = b.writeOnly;
-               hashCode = HashCode.of(beanMeta, name);
+               ap = bc.getAnnotationProvider();
+               hashCode = hash(beanMeta, name);
        }
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BasicFileFinder.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BasicFileFinder.java
index 19f52ff52f..6996d02e12 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BasicFileFinder.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/BasicFileFinder.java
@@ -76,7 +76,7 @@ public class BasicFileFinder implements FileFinder {
                this.exclude = builder.exclude;
                this.includePatterns = 
l(include).stream().map(Pattern::pattern).toArray(String[]::new);
                this.excludePatterns = 
l(exclude).stream().map(Pattern::pattern).toArray(String[]::new);
-               this.hashCode = HashCode.of(getClass(), roots, cachingLimit, 
includePatterns, excludePatterns);
+               this.hashCode = hash(getClass(), roots, cachingLimit, 
includePatterns, excludePatterns);
        }
 
        /**
@@ -92,7 +92,7 @@ public class BasicFileFinder implements FileFinder {
                this.exclude = new Pattern[0];
                this.includePatterns = new String[0];
                this.excludePatterns = new String[0];
-               this.hashCode = HashCode.of(getClass(), roots, cachingLimit, 
includePatterns, excludePatterns);
+               this.hashCode = hash(getClass(), roots, cachingLimit, 
includePatterns, excludePatterns);
        }
 
        @Override /* Overridden from Object */
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/staticfile/BasicStaticFiles.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/staticfile/BasicStaticFiles.java
index a29d2ff2aa..1a04340f54 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/staticfile/BasicStaticFiles.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/staticfile/BasicStaticFiles.java
@@ -91,7 +91,7 @@ public class BasicStaticFiles implements StaticFiles {
        public BasicStaticFiles(StaticFiles.Builder builder) {
                this.headers = builder.headers.toArray(new 
Header[builder.headers.size()]);
                this.mimeTypes = builder.mimeTypes;
-               this.hashCode = HashCode.of(hashCode(), headers);
+               this.hashCode = hash(hashCode(), headers);
                this.fileFinder = builder.fileFinder.build();
        }
 
@@ -104,7 +104,7 @@ public class BasicStaticFiles implements StaticFiles {
        protected BasicStaticFiles() {
                this.headers = new Header[0];
                this.mimeTypes = null;
-               this.hashCode = HashCode.of(hashCode(), headers);
+               this.hashCode = hash(hashCode(), headers);
                this.fileFinder = null;
        }
 
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/stats/ThrownStats.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/stats/ThrownStats.java
index 475c09cab6..e41102e614 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/stats/ThrownStats.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/stats/ThrownStats.java
@@ -148,7 +148,7 @@ public class ThrownStats implements Cloneable {
                this.guid = x.guid;
                this.thrownClass = x.thrownClass;
                this.firstMessage = x.firstMessage;
-               this.stackTrace = x.stackTrace == null ? null : 
u(copyOf(x.stackTrace));
+               this.stackTrace = u(copyOf(x.stackTrace));
                this.causedBy = opt(x.causedBy.isPresent() ? 
x.causedBy.get().clone() : null);
                this.hash = x.hash;
                this.count = new AtomicInteger(x.count.get());
@@ -165,7 +165,7 @@ public class ThrownStats implements Cloneable {
                this.guid = new Random().nextLong();
                this.thrownClass = builder.throwable.getClass();
                this.firstMessage = builder.throwable.getMessage();
-               this.stackTrace = builder.stackTrace == null ? null : 
u(copyOf(builder.stackTrace));
+               this.stackTrace = u(copyOf(builder.stackTrace));
                this.causedBy = opt(builder.causedBy);
                this.hash = builder.hash;
                this.count = new AtomicInteger(0);

Reply via email to