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 39d7b4ee4c org.apache.juneau.common.reflect API improvements
     new f14cff088d Merge branch 'master' of 
https://gitbox.apache.org/repos/asf/juneau
39d7b4ee4c is described below

commit 39d7b4ee4c763498a652f6c830cf76a801a1f7e1
Author: James Bognar <[email protected]>
AuthorDate: Fri Nov 21 11:23:45 2025 -0500

    org.apache.juneau.common.reflect API improvements
---
 .../juneau/common/collections/ReversedList.java    | 283 ++++++++++++++
 .../juneau/common/reflect/AnnotationProvider.java  | 121 ++++--
 .../common/collections/ReversedList_Test.java      | 433 +++++++++++++++++++++
 3 files changed, 802 insertions(+), 35 deletions(-)

diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/ReversedList.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/ReversedList.java
new file mode 100644
index 0000000000..ad9cd4def5
--- /dev/null
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/collections/ReversedList.java
@@ -0,0 +1,283 @@
+/*
+ * 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.common.collections;
+
+import java.util.*;
+
+/**
+ * A reversed view of a list that does not modify the underlying list.
+ *
+ * <p>
+ * This class provides a read-only reversed view of a list, where element 
access is transparently
+ * reversed without copying or modifying the original list. All read 
operations (get, iterator, etc.)
+ * operate on the underlying list in reverse order.
+ *
+ * <h5 class='section'>Features:</h5>
+ * <ul class='spaced-list'>
+ *     <li>Zero-copy reverse view - no data duplication
+ *     <li>Efficient random access via index translation
+ *     <li>Reflects changes in the underlying list automatically
+ *     <li>Read-only - modification operations throw {@link 
UnsupportedOperationException}
+ *     <li>Iterator and ListIterator support in reversed order
+ * </ul>
+ *
+ * <h5 class='section'>Usage:</h5>
+ * <p class='bjava'>
+ *     <jc>// Create a list</jc>
+ *     List&lt;String&gt; <jv>original</jv> = List.<jsm>of</jsm>(<js>"A"</js>, 
<js>"B"</js>, <js>"C"</js>);
+ *
+ *     <jc>// Create reversed view</jc>
+ *     List&lt;String&gt; <jv>reversed</jv> = <jk>new</jk> 
ReversedList&lt;&gt;(<jv>original</jv>);
+ *
+ *     <jc>// Access in reverse order</jc>
+ *     <jv>reversed</jv>.get(0);  <jc>// Returns "C"</jc>
+ *     <jv>reversed</jv>.get(1);  <jc>// Returns "B"</jc>
+ *     <jv>reversed</jv>.get(2);  <jc>// Returns "A"</jc>
+ *
+ *     <jc>// Iterate in reverse</jc>
+ *     <jk>for</jk> (String <jv>s</jv> : <jv>reversed</jv>) {
+ *             <jc>// Iterates: "C", "B", "A"</jc>
+ *     }
+ * </p>
+ *
+ * <h5 class='section'>Notes:</h5>
+ * <ul class='spaced-list'>
+ *     <li>The underlying list must not be null
+ *     <li>Changes to the underlying list are immediately visible in the 
reversed view
+ *     <li>All modification operations (add, remove, set, clear) throw {@link 
UnsupportedOperationException}
+ *     <li>Size changes in the underlying list are reflected in this view
+ * </ul>
+ *
+ * @param <E> The element type.
+ */
+public class ReversedList<E> extends AbstractList<E> implements RandomAccess {
+
+       private final List<E> list;
+
+       /**
+        * Creates a new reversed view of the specified list.
+        *
+        * @param list The list to reverse. Must not be <jk>null</jk>.
+        * @throws IllegalArgumentException if list is <jk>null</jk>.
+        */
+       public ReversedList(List<E> list) {
+               if (list == null)
+                       throw new IllegalArgumentException("List cannot be 
null");
+               this.list = list;
+       }
+
+       /**
+        * Returns the element at the specified position in this reversed view.
+        *
+        * <p>
+        * The position is translated to access the underlying list in reverse 
order.
+        * For example, index 0 returns the last element of the underlying list.
+        *
+        * @param index The index of the element to return (0-based, in 
reversed order).
+        * @return The element at the specified position in the reversed view.
+        * @throws IndexOutOfBoundsException if the index is out of range.
+        */
+       @Override
+       public E get(int index) {
+               return list.get(list.size() - 1 - index);
+       }
+
+       /**
+        * Returns the number of elements in this reversed view.
+        *
+        * <p>
+        * This is always equal to the size of the underlying list.
+        *
+        * @return The number of elements in this reversed view.
+        */
+       @Override
+       public int size() {
+               return list.size();
+       }
+
+       /**
+        * Returns an iterator over the elements in this reversed view in 
proper sequence.
+        *
+        * <p>
+        * The iterator traverses the underlying list in reverse order.
+        *
+        * @return An iterator over the elements in reversed order.
+        */
+       @Override
+       public Iterator<E> iterator() {
+               return new Iterator<E>() {
+                       private final ListIterator<E> it = 
list.listIterator(list.size());
+
+                       @Override
+                       public boolean hasNext() {
+                               return it.hasPrevious();
+                       }
+
+                       @Override
+                       public E next() {
+                               return it.previous();
+                       }
+
+                       @Override
+                       public void remove() {
+                               throw new 
UnsupportedOperationException("ReversedList is read-only");
+                       }
+               };
+       }
+
+       /**
+        * Returns a list iterator over the elements in this reversed view.
+        *
+        * <p>
+        * The iterator traverses the underlying list in reverse order.
+        *
+        * @return A list iterator over the elements in reversed order.
+        */
+       @Override
+       public ListIterator<E> listIterator() {
+               return listIterator(0);
+       }
+
+       /**
+        * Returns a list iterator over the elements in this reversed view, 
starting at the specified position.
+        *
+        * <p>
+        * The iterator traverses the underlying list in reverse order, 
starting from the translated position.
+        *
+        * @param index The index of the first element to be returned from the 
list iterator (in reversed order).
+        * @return A list iterator over the elements in reversed order.
+        * @throws IndexOutOfBoundsException if the index is out of range.
+        */
+       @Override
+       public ListIterator<E> listIterator(final int index) {
+               if (index < 0 || index > size())
+                       throw new IndexOutOfBoundsException("Index: " + index + 
", Size: " + size());
+
+               return new ListIterator<E>() {
+                       private final ListIterator<E> it = 
list.listIterator(list.size() - index);
+
+                       @Override
+                       public boolean hasNext() {
+                               return it.hasPrevious();
+                       }
+
+                       @Override
+                       public E next() {
+                               return it.previous();
+                       }
+
+                       @Override
+                       public boolean hasPrevious() {
+                               return it.hasNext();
+                       }
+
+                       @Override
+                       public E previous() {
+                               return it.next();
+                       }
+
+                       @Override
+                       public int nextIndex() {
+                               return list.size() - it.previousIndex() - 1;
+                       }
+
+                       @Override
+                       public int previousIndex() {
+                               return list.size() - it.nextIndex() - 1;
+                       }
+
+                       @Override
+                       public void remove() {
+                               throw new 
UnsupportedOperationException("ReversedList is read-only");
+                       }
+
+                       @Override
+                       public void set(E e) {
+                               throw new 
UnsupportedOperationException("ReversedList is read-only");
+                       }
+
+                       @Override
+                       public void add(E e) {
+                               throw new 
UnsupportedOperationException("ReversedList is read-only");
+                       }
+               };
+       }
+
+       /**
+        * Returns a view of the portion of this reversed list between the 
specified indices.
+        *
+        * <p>
+        * The returned sublist is also a reversed view and reflects changes in 
the underlying list.
+        *
+        * @param fromIndex Low endpoint (inclusive) of the subList.
+        * @param toIndex High endpoint (exclusive) of the subList.
+        * @return A view of the specified range within this reversed list.
+        * @throws IndexOutOfBoundsException if the indices are out of range.
+        */
+       @Override
+       public List<E> subList(int fromIndex, int toIndex) {
+               if (fromIndex < 0 || toIndex > size() || fromIndex > toIndex)
+                       throw new IndexOutOfBoundsException("fromIndex: " + 
fromIndex + ", toIndex: " + toIndex + ", size: " + size());
+
+               // Translate indices to the underlying list
+               int translatedFrom = list.size() - toIndex;
+               int translatedTo = list.size() - fromIndex;
+
+               return new ReversedList<>(list.subList(translatedFrom, 
translatedTo));
+       }
+
+       /**
+        * Not supported - this is a read-only view.
+        *
+        * @throws UnsupportedOperationException always
+        */
+       @Override
+       public void add(int index, E element) {
+               throw new UnsupportedOperationException("ReversedList is 
read-only");
+       }
+
+       /**
+        * Not supported - this is a read-only view.
+        *
+        * @throws UnsupportedOperationException always
+        */
+       @Override
+       public E remove(int index) {
+               throw new UnsupportedOperationException("ReversedList is 
read-only");
+       }
+
+       /**
+        * Not supported - this is a read-only view.
+        *
+        * @throws UnsupportedOperationException always
+        */
+       @Override
+       public E set(int index, E element) {
+               throw new UnsupportedOperationException("ReversedList is 
read-only");
+       }
+
+       /**
+        * Not supported - this is a read-only view.
+        *
+        * @throws UnsupportedOperationException always
+        */
+       @Override
+       public void clear() {
+               throw new UnsupportedOperationException("ReversedList is 
read-only");
+       }
+}
+
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationProvider.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationProvider.java
index 4b1a356fc1..bb2405e533 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationProvider.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationProvider.java
@@ -26,6 +26,7 @@ import static java.util.stream.Stream.*;
 import java.lang.annotation.*;
 import java.lang.reflect.*;
 import java.util.*;
+import java.util.function.*;
 import java.util.stream.*;
 
 import org.apache.juneau.common.collections.*;
@@ -195,7 +196,8 @@ public class AnnotationProvider {
 
        private static final Map<String, Long> methodCallCounts = new 
java.util.concurrent.ConcurrentHashMap<>();
        private static final boolean ENABLE_INSTRUMENTATION = 
Boolean.getBoolean("juneau.instrumentAnnotationProvider");
-       private static final boolean ENABLE_NEW_CODE = 
Boolean.getBoolean("juneau.annotationProvider.enableNewCode");
+//     private static final boolean ENABLE_NEW_CODE = 
Boolean.getBoolean("juneau.annotationProvider.enableNewCode");
+       private static final boolean ENABLE_NEW_CODE = true;
 
        static {
                if (ENABLE_INSTRUMENTATION) {
@@ -205,11 +207,11 @@ public class AnnotationProvider {
                                        pw.println("\n=== AnnotationProvider 
Method Call Statistics ===");
                                        pw.println(String.format("%-65s %12s", 
"Method", "Calls"));
                                        pw.println("=".repeat(80));
-                                       
+
                                        methodCallCounts.entrySet().stream()
                                                .sorted(Map.Entry.<String, 
Long>comparingByValue().reversed())
                                                .forEach(e -> 
pw.println(String.format("%-65s %,12d", e.getKey(), e.getValue())));
-                                       
+
                                        long totalCalls = 
methodCallCounts.values().stream().mapToLong(Long::longValue).sum();
                                        pw.println("=".repeat(80));
                                        pw.println(String.format("%-65s %,12d", 
"TOTAL", totalCalls));
@@ -441,10 +443,11 @@ public class AnnotationProvider {
                return new Builder();
        }
 
-       private final Cache<Class<?>,List<AnnotationInfo<Annotation>>> 
classAnnnotations;
-       private final Cache<Method,List<AnnotationInfo<Annotation>>> 
methodAnnotations;
-       private final Cache<Field,List<AnnotationInfo<Annotation>>> 
fieldAnnotations;
-       private final Cache<Constructor<?>,List<AnnotationInfo<Annotation>>> 
constructorAnnotations;
+       private final Cache<Class<?>,List<AnnotationInfo<Annotation>>> 
classRuntimeAnnotations;
+       private final Cache<Method,List<AnnotationInfo<Annotation>>> 
methodRuntimeAnnotations;
+       private final Cache<Field,List<AnnotationInfo<Annotation>>> 
fieldRuntimeAnnotations;
+       private final Cache<Constructor<?>,List<AnnotationInfo<Annotation>>> 
constructorRuntimeAnnotations;
+       private final Cache3<Class<?>,ElementInfo,AnnotationTraversal[],List> 
cache;
        private final ReflectionMap<Annotation> annotationMap;
 
        /**
@@ -453,20 +456,24 @@ public class AnnotationProvider {
         * @param builder The builder containing configuration settings.
         */
        protected AnnotationProvider(Builder builder) {
-               this.classAnnnotations = 
Cache.<Class<?>,List<AnnotationInfo<Annotation>>>create()
-                       .supplier(this::findClassAnnotations)
+               this.classRuntimeAnnotations = 
Cache.<Class<?>,List<AnnotationInfo<Annotation>>>create()
+                       .supplier(this::findClassRuntimeAnnotations)
+                       .disableCaching(builder.disableCaching)
+                       .build();
+               this.methodRuntimeAnnotations = 
Cache.<Method,List<AnnotationInfo<Annotation>>>create()
+                       .supplier(this::findMethodRuntimeAnnotations)
                        .disableCaching(builder.disableCaching)
                        .build();
-               this.methodAnnotations = 
Cache.<Method,List<AnnotationInfo<Annotation>>>create()
-                       .supplier(this::findMethodAnnotations)
+               this.fieldRuntimeAnnotations = 
Cache.<Field,List<AnnotationInfo<Annotation>>>create()
+                       .supplier(this::findFieldRuntimeAnnotations)
                        .disableCaching(builder.disableCaching)
                        .build();
-               this.fieldAnnotations = 
Cache.<Field,List<AnnotationInfo<Annotation>>>create()
-                       .supplier(this::findFieldAnnotations)
+               this.constructorRuntimeAnnotations = 
Cache.<Constructor<?>,List<AnnotationInfo<Annotation>>>create()
+                       .supplier(this::findConstructorRuntimeAnnotations)
                        .disableCaching(builder.disableCaching)
                        .build();
-               this.constructorAnnotations = 
Cache.<Constructor<?>,List<AnnotationInfo<Annotation>>>create()
-                       .supplier(this::findConstructorAnnotations)
+               this.cache = 
Cache3.<Class<?>,ElementInfo,AnnotationTraversal[],List>create()
+                       .supplier(this::findCached)
                        .disableCaching(builder.disableCaching)
                        .build();
                this.annotationMap = builder.runtimeAnnotations.build();
@@ -476,22 +483,22 @@ public class AnnotationProvider {
        // Private implementation
        
//-----------------------------------------------------------------------------------------------------------------
 
-       private List<AnnotationInfo<Annotation>> findClassAnnotations(Class<?> 
forClass) {
+       private List<AnnotationInfo<Annotation>> 
findClassRuntimeAnnotations(Class<?> forClass) {
                var ci = ClassInfo.of(forClass);
                return annotationMap.find(forClass).map(a -> ai(ci, 
a)).toList();
        }
 
-       private List<AnnotationInfo<Annotation>> findMethodAnnotations(Method 
forMethod) {
+       private List<AnnotationInfo<Annotation>> 
findMethodRuntimeAnnotations(Method forMethod) {
                var mi = MethodInfo.of(forMethod);
                return annotationMap.find(forMethod).map(a -> ai(mi, 
a)).toList();
        }
 
-       private List<AnnotationInfo<Annotation>> findFieldAnnotations(Field 
forField) {
+       private List<AnnotationInfo<Annotation>> 
findFieldRuntimeAnnotations(Field forField) {
                var fi = FieldInfo.of(forField);
                return annotationMap.find(forField).map(a -> ai(fi, 
a)).toList();
        }
 
-       private List<AnnotationInfo<Annotation>> 
findConstructorAnnotations(Constructor<?> forConstructor) {
+       private List<AnnotationInfo<Annotation>> 
findConstructorRuntimeAnnotations(Constructor<?> forConstructor) {
                var ci = ConstructorInfo.of(forConstructor);
                return annotationMap.find(forConstructor).map(a -> ai(ci, 
a)).toList();
        }
@@ -533,7 +540,7 @@ public class AnnotationProvider {
                trackCall("find(Class, ClassInfo, AnnotationTraversal...)");
                if (ENABLE_NEW_CODE)
                        return findNew(type, clazz, traversals).stream();
-               
+
                assertArgNotNull("type", type);
                assertArgNotNull("clazz", clazz);
                if (traversals.length == 0)
@@ -544,14 +551,14 @@ public class AnnotationProvider {
                        .flatMap(traversal -> {
                                if (traversal == SELF) {
                                        return concat(
-                                               
classAnnnotations.get(clazz.inner()).stream(),
+                                               
classRuntimeAnnotations.get(clazz.inner()).stream(),
                                                
clazz.getDeclaredAnnotations().stream()
                                        )
                                        .filter(a -> a.isType(type)).map(a -> 
(AnnotationInfo<A>)a);
                                } else if (traversal == PARENTS) {
                                        return 
clazz.getParentsAndInterfaces().stream().flatMap(x ->
                                                concat(
-                                                       
classAnnnotations.get(x.inner()).stream(),
+                                                       
classRuntimeAnnotations.get(x.inner()).stream(),
                                                        
x.getDeclaredAnnotations().stream()
                                                ).filter(a -> 
a.isType(type)).map(a -> (AnnotationInfo<A>)a)
                                        );
@@ -569,10 +576,42 @@ public class AnnotationProvider {
         */
        @SuppressWarnings("unchecked")
        private <A extends Annotation> List<AnnotationInfo<A>> findNew(Class<A> 
type, ClassInfo clazz, AnnotationTraversal... traversals) {
-               // TODO: Implement optimized version
-               throw new UnsupportedOperationException("New implementation not 
yet available");
+               return (List<AnnotationInfo<A>>)cache.get(type, clazz, 
traversals);
        }
 
+       /**
+        * Computes and caches the complete list of annotations for a given 
type, class, and traversal combination.
+        * This is the supplier function for the findCache.
+        */
+       @SuppressWarnings("unchecked")
+       private List findCached(Class<?> type, ElementInfo element, 
AnnotationTraversal[] traversals) {
+               var l = new ArrayList();
+               var filter = isType((Class<? extends Annotation>)type);
+
+               if (element instanceof ClassInfo ci) {
+                       if (traversals.length == 0)
+                               traversals = a(PARENTS, PACKAGE);
+                       var t = Arrays.asList(traversals);
+                       if (t.contains(SELF)) {
+                               
classRuntimeAnnotations.get(ci.inner()).stream().filter(filter).forEach(l::add);
+                               
ci.getDeclaredAnnotations().stream().filter(filter).forEach(l::add);
+                       }
+                       if (t.contains(PARENTS)) {
+                               for (var p : ci.getParentsAndInterfaces()) {
+                                       
classRuntimeAnnotations.get(p.inner()).stream().filter(filter).forEach(l::add);
+                                       
p.getDeclaredAnnotations().stream().filter(filter).forEach(l::add);
+                               }
+                       }
+                       if (t.contains(PACKAGE)) {
+                               if (ci.getPackage() != null)
+                                       
ci.getPackage().getAnnotations().stream().filter(filter).forEach(l::add);
+                       }
+               }
+
+               return l;
+       }
+
+
        /**
         * Streams all annotations from a class using configurable traversal 
options, without filtering by annotation type.
         *
@@ -607,13 +646,13 @@ public class AnnotationProvider {
                        .flatMap(traversal -> {
                                if (traversal == SELF) {
                                        return concat(
-                                               
classAnnnotations.get(clazz.inner()).stream(),
+                                               
classRuntimeAnnotations.get(clazz.inner()).stream(),
                                                
clazz.getDeclaredAnnotations().stream().map(a -> (AnnotationInfo<Annotation>)a)
                                        );
                                } else if (traversal == PARENTS) {
                                        return 
clazz.getParentsAndInterfaces().stream().flatMap(x -> {
                                                return concat(
-                                                       
classAnnnotations.get(x.inner()).stream(),
+                                                       
classRuntimeAnnotations.get(x.inner()).stream(),
                                                        
x.getDeclaredAnnotations().stream().map(a -> (AnnotationInfo<Annotation>)a)
                                                );
                                        });
@@ -736,13 +775,13 @@ public class AnnotationProvider {
                        .flatMap(traversal -> {
                                if (traversal == SELF) {
                                        return concat(
-                                               
methodAnnotations.get(method.inner()).stream(),
+                                               
methodRuntimeAnnotations.get(method.inner()).stream(),
                                                
method.getDeclaredAnnotations().stream()
                                        ).filter(a -> a.isType(type)).map(a -> 
(AnnotationInfo<A>)a);
                                } else if (traversal == MATCHING_METHODS) {
                                        return 
method.getMatchingMethods().stream().skip(1).flatMap(m ->
                                                concat(
-                                                       
methodAnnotations.get(m.inner()).stream(),
+                                                       
methodRuntimeAnnotations.get(m.inner()).stream(),
                                                        
m.getDeclaredAnnotations().stream()
                                                ).filter(a -> 
a.isType(type)).map(a -> (AnnotationInfo<A>)a)
                                        );
@@ -790,13 +829,13 @@ public class AnnotationProvider {
                        .flatMap(traversal -> {
                                if (traversal == SELF) {
                                        return concat(
-                                               
methodAnnotations.get(method.inner()).stream(),
+                                               
methodRuntimeAnnotations.get(method.inner()).stream(),
                                                
method.getDeclaredAnnotations().stream()
                                        );
                                } else if (traversal == MATCHING_METHODS) {
                                        return 
method.getMatchingMethods().stream().skip(1).flatMap(m -> {
                                                return concat(
-                                                       
methodAnnotations.get(m.inner()).stream(),
+                                                       
methodRuntimeAnnotations.get(m.inner()).stream(),
                                                        
m.getDeclaredAnnotations().stream()
                                                );
                                        });
@@ -1132,7 +1171,7 @@ public class AnnotationProvider {
                        .flatMap(traversal -> {
                                if (traversal == SELF) {
                                        return concat(
-                                               
fieldAnnotations.get(field.inner()).stream(),
+                                               
fieldRuntimeAnnotations.get(field.inner()).stream(),
                                                field.getAnnotations().stream()
                                        ).filter(a -> a.isType(type)).map(a -> 
(AnnotationInfo<A>)a);
                                }
@@ -1169,7 +1208,7 @@ public class AnnotationProvider {
                        .flatMap(traversal -> {
                                if (traversal == SELF) {
                                        return concat(
-                                               
fieldAnnotations.get(field.inner()).stream(),
+                                               
fieldRuntimeAnnotations.get(field.inner()).stream(),
                                                field.getAnnotations().stream()
                                        );
                                }
@@ -1279,7 +1318,7 @@ public class AnnotationProvider {
                        .flatMap(traversal -> {
                                if (traversal == SELF) {
                                        return concat(
-                                               
constructorAnnotations.get(constructor.inner()).stream(),
+                                               
constructorRuntimeAnnotations.get(constructor.inner()).stream(),
                                                
constructor.getDeclaredAnnotations().stream()
                                        ).filter(a -> a.isType(type)).map(a -> 
(AnnotationInfo<A>)a);
                                }
@@ -1316,7 +1355,7 @@ public class AnnotationProvider {
                        .flatMap(traversal -> {
                                if (traversal == SELF) {
                                        return concat(
-                                               
constructorAnnotations.get(constructor.inner()).stream(),
+                                               
constructorRuntimeAnnotations.get(constructor.inner()).stream(),
                                                
constructor.getDeclaredAnnotations().stream()
                                        );
                                }
@@ -1393,11 +1432,23 @@ public class AnnotationProvider {
                trackCall("has(Class, ConstructorInfo, 
AnnotationTraversal...)");
                return find(type, constructor, 
traversals).findFirst().isPresent();
        }
-       
+
        
//-----------------------------------------------------------------------------------------------------------------
        // Helper methods
        
//-----------------------------------------------------------------------------------------------------------------
 
+       /**
+        * Creates a predicate that filters annotations by type.
+        * If type is null, all annotations pass; otherwise only annotations of 
the specified type pass.
+        *
+        * @param type The annotation type to filter by, or null to accept all 
annotations.
+        * @return A predicate that tests if an annotation matches the type.
+        */
+       @SuppressWarnings({"unchecked", "rawtypes"})
+       private static Predicate isType(Class<? extends Annotation> type) {
+               return type == null ? x -> true : x -> 
((AnnotationInfo)x).isType(type);
+       }
+
        private <A extends Annotation> AnnotationInfo<A> ai(Annotatable on, A 
value) {
                return AnnotationInfo.of(on, value);
        }
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/common/collections/ReversedList_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/common/collections/ReversedList_Test.java
new file mode 100644
index 0000000000..cb520ea74b
--- /dev/null
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/common/collections/ReversedList_Test.java
@@ -0,0 +1,433 @@
+/*
+ * 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.common.collections;
+
+import static org.apache.juneau.junit.bct.BctAssertions.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.junit.jupiter.api.*;
+
+class ReversedList_Test extends TestBase {
+
+       
//====================================================================================================
+       // Basic functionality
+       
//====================================================================================================
+
+       @Test
+       void a01_basicGet() {
+               var original = List.of("A", "B", "C");
+               var reversed = new ReversedList<>(original);
+
+               assertEquals("C", reversed.get(0));
+               assertEquals("B", reversed.get(1));
+               assertEquals("A", reversed.get(2));
+               assertSize(3, reversed);
+       }
+
+       @Test
+       void a02_basicIteration() {
+               var original = List.of("A", "B", "C");
+               var reversed = new ReversedList<>(original);
+
+               var result = new ArrayList<String>();
+               for (String s : reversed) {
+                       result.add(s);
+               }
+
+               assertEquals(List.of("C", "B", "A"), result);
+       }
+
+       @Test
+       void a03_emptyList() {
+               var original = List.<String>of();
+               var reversed = new ReversedList<>(original);
+
+               assertEmpty(reversed);
+               assertFalse(reversed.iterator().hasNext());
+       }
+
+       @Test
+       void a04_singleElement() {
+               var original = List.of("A");
+               var reversed = new ReversedList<>(original);
+
+               assertSize(1, reversed);
+               assertEquals("A", reversed.get(0));
+       }
+
+       
//====================================================================================================
+       // Null handling
+       
//====================================================================================================
+
+       @Test
+       void b01_nullList_throwsException() {
+               assertThrows(IllegalArgumentException.class, () -> new 
ReversedList<>(null));
+       }
+
+       @Test
+       void b02_listWithNulls() {
+               var original = Arrays.asList("A", null, "C");
+               var reversed = new ReversedList<>(original);
+
+               assertEquals("C", reversed.get(0));
+               assertNull(reversed.get(1));
+               assertEquals("A", reversed.get(2));
+       }
+
+       
//====================================================================================================
+       // Index bounds
+       
//====================================================================================================
+
+       @Test
+       void c01_outOfBounds_negative() {
+               var original = List.of("A", "B", "C");
+               var reversed = new ReversedList<>(original);
+
+               assertThrows(IndexOutOfBoundsException.class, () -> 
reversed.get(-1));
+       }
+
+       @Test
+       void c02_outOfBounds_tooLarge() {
+               var original = List.of("A", "B", "C");
+               var reversed = new ReversedList<>(original);
+
+               assertThrows(IndexOutOfBoundsException.class, () -> 
reversed.get(3));
+       }
+
+       
//====================================================================================================
+       // Reflection of underlying list changes
+       
//====================================================================================================
+
+       @Test
+       void d01_reflectsUnderlyingChanges() {
+               var original = new ArrayList<>(Arrays.asList("A", "B", "C"));
+               var reversed = new ReversedList<>(original);
+
+               assertEquals("C", reversed.get(0));
+
+               original.add("D");
+
+               assertSize(4, reversed);
+               assertEquals("D", reversed.get(0));
+               assertEquals("C", reversed.get(1));
+       }
+
+       @Test
+       void d02_reflectsUnderlyingRemoval() {
+               var original = new ArrayList<>(Arrays.asList("A", "B", "C"));
+               var reversed = new ReversedList<>(original);
+
+               original.remove(2); // Remove "C"
+
+               assertSize(2, reversed);
+               assertEquals("B", reversed.get(0));
+               assertEquals("A", reversed.get(1));
+       }
+
+       
//====================================================================================================
+       // Read-only enforcement
+       
//====================================================================================================
+
+       @Test
+       void e01_add_throwsException() {
+               var original = List.of("A", "B", "C");
+               var reversed = new ReversedList<>(original);
+
+               assertThrows(UnsupportedOperationException.class, () -> 
reversed.add("D"));
+       }
+
+       @Test
+       void e02_addAtIndex_throwsException() {
+               var original = List.of("A", "B", "C");
+               var reversed = new ReversedList<>(original);
+
+               assertThrows(UnsupportedOperationException.class, () -> 
reversed.add(0, "D"));
+       }
+
+       @Test
+       void e03_remove_throwsException() {
+               var original = List.of("A", "B", "C");
+               var reversed = new ReversedList<>(original);
+
+               assertThrows(UnsupportedOperationException.class, () -> 
reversed.remove(0));
+       }
+
+       @Test
+       void e04_set_throwsException() {
+               var original = List.of("A", "B", "C");
+               var reversed = new ReversedList<>(original);
+
+               assertThrows(UnsupportedOperationException.class, () -> 
reversed.set(0, "D"));
+       }
+
+       @Test
+       void e05_clear_throwsException() {
+               var original = List.of("A", "B", "C");
+               var reversed = new ReversedList<>(original);
+
+               assertThrows(UnsupportedOperationException.class, () -> 
reversed.clear());
+       }
+
+       @Test
+       void e06_iteratorRemove_throwsException() {
+               var original = List.of("A", "B", "C");
+               var reversed = new ReversedList<>(original);
+
+               var it = reversed.iterator();
+               it.next();
+               assertThrows(UnsupportedOperationException.class, () -> 
it.remove());
+       }
+
+       
//====================================================================================================
+       // Iterator functionality
+       
//====================================================================================================
+
+       @Test
+       void f01_iterator_traversal() {
+               var original = List.of("A", "B", "C", "D");
+               var reversed = new ReversedList<>(original);
+
+               var it = reversed.iterator();
+               assertTrue(it.hasNext());
+               assertEquals("D", it.next());
+               assertTrue(it.hasNext());
+               assertEquals("C", it.next());
+               assertTrue(it.hasNext());
+               assertEquals("B", it.next());
+               assertTrue(it.hasNext());
+               assertEquals("A", it.next());
+               assertFalse(it.hasNext());
+       }
+
+       @Test
+       void f02_listIterator_forward() {
+               var original = List.of("A", "B", "C");
+               var reversed = new ReversedList<>(original);
+
+               var it = reversed.listIterator();
+               assertEquals("C", it.next());
+               assertEquals("B", it.next());
+               assertEquals("A", it.next());
+               assertFalse(it.hasNext());
+       }
+
+       @Test
+       void f03_listIterator_backward() {
+               var original = List.of("A", "B", "C");
+               var reversed = new ReversedList<>(original);
+
+               var it = reversed.listIterator(3);
+               assertEquals("A", it.previous());
+               assertEquals("B", it.previous());
+               assertEquals("C", it.previous());
+               assertFalse(it.hasPrevious());
+       }
+
+       @Test
+       void f04_listIterator_bidirectional() {
+               var original = List.of("A", "B", "C");
+               var reversed = new ReversedList<>(original);
+
+               var it = reversed.listIterator(1);
+               assertEquals("B", it.next());
+               assertEquals("B", it.previous());
+               assertEquals("C", it.previous());
+               assertEquals("C", it.next());
+       }
+
+       @Test
+       void f05_listIterator_indices() {
+               var original = List.of("A", "B", "C");
+               var reversed = new ReversedList<>(original);
+
+               var it = reversed.listIterator();
+               assertEquals(-1, it.previousIndex());
+               assertEquals(0, it.nextIndex());
+
+               it.next();
+               assertEquals(0, it.previousIndex());
+               assertEquals(1, it.nextIndex());
+
+               it.next();
+               assertEquals(1, it.previousIndex());
+               assertEquals(2, it.nextIndex());
+
+               it.next();
+               assertEquals(2, it.previousIndex());
+               assertEquals(3, it.nextIndex());
+       }
+
+       @Test
+       void f06_listIterator_modificationThrows() {
+               var original = List.of("A", "B", "C");
+               var reversed = new ReversedList<>(original);
+
+               var it = reversed.listIterator();
+               it.next();
+
+               assertThrows(UnsupportedOperationException.class, () -> 
it.remove());
+               assertThrows(UnsupportedOperationException.class, () -> 
it.set("X"));
+               assertThrows(UnsupportedOperationException.class, () -> 
it.add("X"));
+       }
+
+       
//====================================================================================================
+       // SubList functionality
+       
//====================================================================================================
+
+       @Test
+       void g01_subList_basic() {
+               var original = List.of("A", "B", "C", "D", "E");
+               var reversed = new ReversedList<>(original);
+
+               var subList = reversed.subList(1, 4);
+
+               assertSize(3, subList);
+               assertEquals("D", subList.get(0));
+               assertEquals("C", subList.get(1));
+               assertEquals("B", subList.get(2));
+       }
+
+       @Test
+       void g02_subList_empty() {
+               var original = List.of("A", "B", "C");
+               var reversed = new ReversedList<>(original);
+
+               var subList = reversed.subList(1, 1);
+
+               assertEmpty(subList);
+       }
+
+       @Test
+       void g03_subList_full() {
+               var original = List.of("A", "B", "C");
+               var reversed = new ReversedList<>(original);
+
+               var subList = reversed.subList(0, 3);
+
+               assertSize(3, subList);
+               assertEquals("C", subList.get(0));
+               assertEquals("B", subList.get(1));
+               assertEquals("A", subList.get(2));
+       }
+
+       @Test
+       void g04_subList_outOfBounds() {
+               var original = List.of("A", "B", "C");
+               var reversed = new ReversedList<>(original);
+
+               assertThrows(IndexOutOfBoundsException.class, () -> 
reversed.subList(-1, 2));
+               assertThrows(IndexOutOfBoundsException.class, () -> 
reversed.subList(0, 4));
+               assertThrows(IndexOutOfBoundsException.class, () -> 
reversed.subList(2, 1));
+       }
+
+       @Test
+       void g05_subList_reflectsChanges() {
+               var original = new ArrayList<>(Arrays.asList("A", "B", "C", 
"D", "E"));
+               var reversed = new ReversedList<>(original);
+               var subList = reversed.subList(1, 4);
+
+               original.set(3, "X"); // Changes "D" to "X" in original
+
+               assertEquals("X", subList.get(0));
+       }
+
+       
//====================================================================================================
+       // Contains and indexOf
+       
//====================================================================================================
+
+       @Test
+       void h01_contains() {
+               var original = List.of("A", "B", "C");
+               var reversed = new ReversedList<>(original);
+
+               assertTrue(reversed.contains("A"));
+               assertTrue(reversed.contains("B"));
+               assertTrue(reversed.contains("C"));
+               assertFalse(reversed.contains("D"));
+       }
+
+       @Test
+       void h02_indexOf() {
+               var original = List.of("A", "B", "C");
+               var reversed = new ReversedList<>(original);
+
+               assertEquals(2, reversed.indexOf("A"));
+               assertEquals(1, reversed.indexOf("B"));
+               assertEquals(0, reversed.indexOf("C"));
+               assertEquals(-1, reversed.indexOf("D"));
+       }
+
+       @Test
+       void h03_lastIndexOf() {
+               var original = List.of("A", "B", "A", "C");
+               var reversed = new ReversedList<>(original);
+
+               // Original: ["A", "B", "A", "C"]
+               // Reversed: ["C", "A", "B", "A"]
+               // First "A" in reversed is at index 1, last "A" is at index 3
+               assertEquals(3, reversed.lastIndexOf("A"));
+               assertEquals(0, reversed.lastIndexOf("C"));
+       }
+
+       
//====================================================================================================
+       // Edge cases
+       
//====================================================================================================
+
+       @Test
+       void i01_largeList() {
+               var original = new ArrayList<Integer>();
+               for (int i = 0; i < 1000; i++) {
+                       original.add(i);
+               }
+
+               var reversed = new ReversedList<>(original);
+
+               assertSize(1000, reversed);
+               assertEquals(999, reversed.get(0));
+               assertEquals(0, reversed.get(999));
+       }
+
+       @Test
+       void i02_toArray() {
+               var original = List.of("A", "B", "C");
+               var reversed = new ReversedList<>(original);
+
+               var array = reversed.toArray();
+
+               assertEquals(3, array.length);
+               assertEquals("C", array[0]);
+               assertEquals("B", array[1]);
+               assertEquals("A", array[2]);
+       }
+
+       @Test
+       void i03_toArrayTyped() {
+               var original = List.of("A", "B", "C");
+               var reversed = new ReversedList<>(original);
+
+               var array = reversed.toArray(new String[0]);
+
+               assertEquals(3, array.length);
+               assertEquals("C", array[0]);
+               assertEquals("B", array[1]);
+               assertEquals("A", array[2]);
+       }
+}
+


Reply via email to