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 d25bf0a1fe Marshall module improvements
d25bf0a1fe is described below
commit d25bf0a1fea7b9f46f96785d953e9d68aaad48b7
Author: James Bognar <[email protected]>
AuthorDate: Fri Dec 12 18:34:52 2025 -0500
Marshall module improvements
---
.../juneau/commons/collections/MultiList.java | 462 +++++++++++++++++++
.../apache/juneau/jena/RdfBeanPropertyMeta.java | 18 +-
.../java/org/apache/juneau/BeanPropertyMeta.java | 41 --
.../java/org/apache/juneau/cp/BasicFileFinder.java | 1 -
.../org/apache/juneau/xml/XmlBeanPropertyMeta.java | 17 +-
.../apache/juneau/AnnotationInheritance_Test.java | 30 --
.../juneau/commons/collections/MultiList_Test.java | 493 +++++++++++++++++++++
7 files changed, 974 insertions(+), 88 deletions(-)
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/MultiList.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/MultiList.java
new file mode 100644
index 0000000000..855775ed26
--- /dev/null
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/MultiList.java
@@ -0,0 +1,462 @@
+/*
+ * 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.collections;
+
+import static org.apache.juneau.commons.utils.AssertionUtils.*;
+
+import java.util.*;
+
+/**
+ * A composite list that presents multiple lists as a single unified list.
+ *
+ * <p>
+ * This class allows multiple lists to be viewed and accessed as if they were
merged into
+ * a single list, without actually copying the elements. Modifications made
through the iterator
+ * or list iterator affect the underlying lists.
+ *
+ * <h5 class='section'>Features:</h5>
+ * <ul class='spaced-list'>
+ * <li><b>Zero-Copy Composition:</b> No data is copied when creating a
MultiList; it simply wraps the provided lists
+ * <li><b>Transparent Access:</b> Accessing elements by index or iterating
over a MultiList seamlessly traverses all underlying lists in order
+ * <li><b>Modification Support:</b> Elements can be removed via the
iterator's {@link Iterator#remove()} method
+ * <li><b>Efficient Size Calculation:</b> The size is computed by summing
the sizes of all underlying lists
+ * <li><b>Enumeration Support:</b> Provides an {@link Enumeration} view
via {@link #enumerator()}
+ * </ul>
+ *
+ * <h5 class='section'>Usage:</h5>
+ * <p class='bjava'>
+ * <jc>// Create a MultiList from three separate lists</jc>
+ * List<String> <jv>list1</jv> = List.of(<js>"a"</js>, <js>"b"</js>);
+ * List<String> <jv>list2</jv> = List.of(<js>"c"</js>, <js>"d"</js>);
+ * List<String> <jv>list3</jv> = List.of(<js>"e"</js>, <js>"f"</js>);
+ *
+ * MultiList<String> <jv>multiList</jv> = <jk>new</jk>
MultiList<>(<jv>list1</jv>, <jv>list2</jv>, <jv>list3</jv>);
+ *
+ * <jc>// Access elements by index</jc>
+ * <jv>multiList</jv>.get(0); <jc>// Returns "a"</jc>
+ * <jv>multiList</jv>.get(3); <jc>// Returns "d"</jc>
+ * <jv>multiList</jv>.get(5); <jc>// Returns "f"</jc>
+ *
+ * <jc>// Iterate over all elements from all lists</jc>
+ * <jk>for</jk> (String <jv>element</jv> : <jv>multiList</jv>) {
+ * System.<jsf>out</jsf>.println(<jv>element</jv>); <jc>// Prints:
a, b, c, d, e, f</jc>
+ * }
+ *
+ * <jc>// Get total size across all lists</jc>
+ * <jk>int</jk> <jv>totalSize</jv> = <jv>multiList</jv>.size(); <jc>//
Returns: 6</jc>
+ *
+ * <jc>// Remove elements via iterator (affects underlying lists)</jc>
+ * Iterator<String> <jv>it</jv> = <jv>multiList</jv>.iterator();
+ * <jk>while</jk> (<jv>it</jv>.hasNext()) {
+ * <jk>if</jk> (<jv>it</jv>.next().equals(<js>"b"</js>)) {
+ * <jv>it</jv>.remove(); <jc>// Removes "b" from list1</jc>
+ * }
+ * }
+ * </p>
+ *
+ * <h5 class='section'>Behavior Notes:</h5>
+ * <ul class='spaced-list'>
+ * <li>The order of access follows the order of lists as provided in the
constructor
+ * <li>Within each list, access order follows the list's natural order
+ * <li>The underlying lists must not be <jk>null</jk>, but can be empty
+ * <li>Modifications via {@link Iterator#remove()} or {@link
ListIterator#remove()} are delegated to the underlying list's iterator
+ * <li>This class does not support {@link #add(Object)}, {@link #add(int,
Object)}, {@link #set(int, Object)}, or {@link #remove(int)} operations
+ * <li>The {@link #size()} method recomputes the sum each time it's called
(not cached)
+ * <li>The {@link #get(int)} method locates the element by traversing
lists until the correct index is found
+ * </ul>
+ *
+ * <h5 class='section'>Thread Safety:</h5>
+ * <p>
+ * This class is not inherently thread-safe. If the underlying lists are
modified concurrently
+ * during iteration or access, the behavior is undefined. Synchronization must
be handled externally if needed.
+ *
+ * <h5 class='section'>Example - Processing Multiple Data Sources:</h5>
+ * <p class='bjava'>
+ * <jc>// Combine results from database, cache, and defaults</jc>
+ * List<User> <jv>dbUsers</jv> = fetchFromDatabase();
+ * List<User> <jv>cachedUsers</jv> = getCachedUsers();
+ * List<User> <jv>defaultUsers</jv> = getDefaultUsers();
+ *
+ * MultiList<User> <jv>allUsers</jv> = <jk>new</jk>
MultiList<>(<jv>dbUsers</jv>, <jv>cachedUsers</jv>,
<jv>defaultUsers</jv>);
+ *
+ * <jc>// Process all users from all sources</jc>
+ * <jv>allUsers</jv>.forEach(user -> processUser(user));
+ *
+ * <jc>// Access specific user by index</jc>
+ * User <jv>user</jv> = <jv>allUsers</jv>.get(<jv>10</jv>);
+ * </p>
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul>
+ * <li class='link'><a class="doclink"
href="../../../../../index.html#juneau-commons">Overview > juneau-commons</a>
+ * </ul>
+ *
+ * @param <E> The element type of this list.
+ */
+public class MultiList<E> extends AbstractList<E> {
+
+ /**
+ * The underlying lists being wrapped by this MultiList.
+ * <p>
+ * These lists are accessed directly during iteration and index access
without copying.
+ */
+ final List<E>[] l;
+
+ /**
+ * Creates a new MultiList that presents the specified lists as a
single unified list.
+ *
+ * <p>
+ * The lists are stored by reference (not copied), so modifications
made through the
+ * MultiList's iterator will affect the original lists.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * List<String> <jv>list1</jv> = <jk>new</jk>
ArrayList<>(List.of(<js>"a"</js>, <js>"b"</js>));
+ * List<String> <jv>list2</jv> = <jk>new</jk>
ArrayList<>(List.of(<js>"c"</js>, <js>"d"</js>));
+ *
+ * MultiList<String> <jv>multiList</jv> = <jk>new</jk>
MultiList<>(<jv>list1</jv>, <jv>list2</jv>);
+ * <jc>// multiList now represents all elements from both
lists</jc>
+ * </p>
+ *
+ * @param c Zero or more lists to combine into this list. Must not be
<jk>null</jk>,
+ * and no individual list can be <jk>null</jk> (but lists can
be empty).
+ * @throws IllegalArgumentException if the lists array or any list
within it is <jk>null</jk>.
+ */
+ @SafeVarargs
+ public MultiList(List<E>...c) {
+ assertArgNotNull("c", c);
+ for (var cc : c)
+ assertArgNotNull("c", cc);
+ l = c;
+ }
+
+ /**
+ * Returns an {@link Enumeration} view of this list.
+ *
+ * <p>
+ * This is useful for compatibility with legacy APIs that require an
{@link Enumeration}
+ * rather than an {@link Iterator}.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * MultiList<String> <jv>multiList</jv> = <jk>new</jk>
MultiList<>(list1, list2);
+ * Enumeration<String> <jv>enumeration</jv> =
<jv>multiList</jv>.enumerator();
+ *
+ * <jk>while</jk> (<jv>enumeration</jv>.hasMoreElements()) {
+ * String <jv>element</jv> =
<jv>enumeration</jv>.nextElement();
+ * <jc>// Process element</jc>
+ * }
+ * </p>
+ *
+ * @return An {@link Enumeration} that iterates over all elements in
all underlying lists.
+ * @see #iterator()
+ */
+ public Enumeration<E> enumerator() {
+ return Collections.enumeration(this);
+ }
+
+ /**
+ * Returns the element at the specified position in this list.
+ *
+ * <p>
+ * The index is resolved by traversing the underlying lists in order
until the
+ * correct list and position within that list is found.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * List<String> <jv>list1</jv> = List.of(<js>"a"</js>,
<js>"b"</js>); <jc>// indices 0-1</jc>
+ * List<String> <jv>list2</jv> = List.of(<js>"c"</js>,
<js>"d"</js>, <js>"e"</js>); <jc>// indices 2-4</jc>
+ * MultiList<String> <jv>multiList</jv> = <jk>new</jk>
MultiList<>(<jv>list1</jv>, <jv>list2</jv>);
+ *
+ * <jv>multiList</jv>.get(0); <jc>// Returns "a"</jc>
+ * <jv>multiList</jv>.get(2); <jc>// Returns "c"</jc>
+ * <jv>multiList</jv>.get(4); <jc>// Returns "e"</jc>
+ * </p>
+ *
+ * @param index The index of the element to return.
+ * @return The element at the specified position.
+ * @throws IndexOutOfBoundsException if the index is out of range
(index < 0 || index >= size()).
+ */
+ @Override /* List */
+ public E get(int index) {
+ if (index < 0)
+ throw new IndexOutOfBoundsException("Index: " + index +
", Size: " + size());
+ var offset = 0;
+ for (var list : l) {
+ var size = list.size();
+ if (index < offset + size)
+ return list.get(index - offset);
+ offset += size;
+ }
+ throw new IndexOutOfBoundsException("Index: " + index + ",
Size: " + size());
+ }
+
+ /**
+ * Returns an iterator over all elements in all underlying lists.
+ *
+ * <p>
+ * The iterator traverses each list in the order they were provided to
the constructor.
+ * Within each list, the iteration order follows the list's natural
order.
+ *
+ * <p>
+ * The returned iterator supports the {@link Iterator#remove()}
operation, which removes
+ * the current element from its underlying list.
+ *
+ * <h5 class='section'>Behavior:</h5>
+ * <ul class='spaced-list'>
+ * <li>Elements from the first list are iterated first, then the
second, and so on
+ * <li>If a list is empty, it is skipped during iteration
+ * <li>Calling {@link Iterator#remove()} removes the element from
the underlying list
+ * <li>Calling {@link Iterator#next()} when {@link
Iterator#hasNext()} returns <jk>false</jk>
+ * throws {@link NoSuchElementException}
+ * <li>Calling {@link Iterator#remove()} before calling {@link
Iterator#next()} or calling it twice
+ * in a row may throw {@link IllegalStateException}
(behavior depends on underlying list)
+ * </ul>
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * List<String> <jv>list1</jv> = <jk>new</jk>
ArrayList<>(List.of(<js>"a"</js>, <js>"b"</js>));
+ * List<String> <jv>list2</jv> = <jk>new</jk>
ArrayList<>(List.of(<js>"c"</js>, <js>"d"</js>));
+ * MultiList<String> <jv>multiList</jv> = <jk>new</jk>
MultiList<>(<jv>list1</jv>, <jv>list2</jv>);
+ *
+ * Iterator<String> <jv>it</jv> =
<jv>multiList</jv>.iterator();
+ * <jk>while</jk> (<jv>it</jv>.hasNext()) {
+ * String <jv>element</jv> = <jv>it</jv>.next();
+ * <jk>if</jk> (<jv>element</jv>.equals(<js>"b"</js>)) {
+ * <jv>it</jv>.remove(); <jc>// Removes "b" from
list1</jc>
+ * }
+ * }
+ * </p>
+ *
+ * @return An iterator over all elements in all underlying lists.
+ */
+ @Override /* List */
+ public Iterator<E> iterator() {
+ return new Iterator<>() {
+ int i = 0;
+ Iterator<E> i2 = (l.length > 0 ? l[i++].iterator() :
null);
+
+ @Override /* Overridden from Iterator */
+ public boolean hasNext() {
+ if (i2 == null)
+ return false;
+ if (i2.hasNext())
+ return true;
+ for (var j = i; j < l.length; j++)
+ if (l[j].size() > 0)
+ return true;
+ return false;
+ }
+
+ @Override /* Overridden from Iterator */
+ public E next() {
+ if (i2 == null)
+ throw new NoSuchElementException();
+ while (! i2.hasNext()) {
+ if (i >= l.length)
+ throw new
NoSuchElementException();
+ i2 = l[i++].iterator();
+ }
+ return i2.next();
+ }
+
+ @Override /* Overridden from Iterator */
+ public void remove() {
+ if (i2 == null)
+ throw new IllegalStateException();
+ i2.remove();
+ }
+ };
+ }
+
+ /**
+ * Returns a list iterator over all elements in all underlying lists.
+ *
+ * <p>
+ * The list iterator traverses each list in the order they were
provided to the constructor.
+ * The iterator starts at the beginning of the first list.
+ *
+ * <p>
+ * The returned list iterator supports the {@link
ListIterator#remove()} operation, which removes
+ * the current element from its underlying list.
+ *
+ * <h5 class='section'>Behavior:</h5>
+ * <ul class='spaced-list'>
+ * <li>Elements from the first list are iterated first, then the
second, and so on
+ * <li>If a list is empty, it is skipped during iteration
+ * <li>Calling {@link ListIterator#remove()} removes the element
from the underlying list
+ * <li>Bidirectional navigation is supported, but may be less
efficient than forward-only iteration
+ * </ul>
+ *
+ * @return A list iterator over all elements in all underlying lists,
starting at the beginning.
+ */
+ @Override /* List */
+ public ListIterator<E> listIterator() {
+ return listIterator(0);
+ }
+
+ /**
+ * Returns a list iterator over all elements in all underlying lists,
starting at the specified position.
+ *
+ * <p>
+ * The list iterator traverses each list in the order they were
provided to the constructor.
+ * The iterator starts at the specified index.
+ *
+ * @param index The index to start the iterator at.
+ * @return A list iterator over all elements in all underlying lists,
starting at the specified index.
+ * @throws IndexOutOfBoundsException if the index is out of range
(index < 0 || index > size()).
+ */
+ @Override /* List */
+ public ListIterator<E> listIterator(int index) {
+ if (index < 0 || index > size())
+ throw new IndexOutOfBoundsException("Index: " + index +
", Size: " + size());
+ return new ListIterator<>() {
+ int currentIndex = index;
+ int listIndex = 0;
+ int offset = 0;
+ ListIterator<E> currentIterator = null;
+
+ {
+ // Initialize to the correct position
+ for (var i = 0; i < l.length; i++) {
+ var size = l[i].size();
+ if (index < offset + size) {
+ listIndex = i;
+ currentIterator =
l[i].listIterator(index - offset);
+ break;
+ }
+ offset += size;
+ }
+ if (currentIterator == null && l.length > 0) {
+ // Index is at the end, position at the
last list
+ listIndex = l.length - 1;
+ currentIterator =
l[listIndex].listIterator(l[listIndex].size());
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (currentIterator == null)
+ return false;
+ if (currentIterator.hasNext())
+ return true;
+ for (var j = listIndex + 1; j < l.length; j++)
+ if (l[j].size() > 0)
+ return true;
+ return false;
+ }
+
+ @Override
+ public E next() {
+ if (currentIterator == null)
+ throw new NoSuchElementException();
+ while (! currentIterator.hasNext()) {
+ if (listIndex + 1 >= l.length)
+ throw new
NoSuchElementException();
+ listIndex++;
+ currentIterator =
l[listIndex].listIterator();
+ }
+ currentIndex++;
+ return currentIterator.next();
+ }
+
+ @Override
+ public boolean hasPrevious() {
+ if (currentIterator == null)
+ return false;
+ if (currentIterator.hasPrevious())
+ return true;
+ for (var j = listIndex - 1; j >= 0; j--)
+ if (l[j].size() > 0)
+ return true;
+ return false;
+ }
+
+ @Override
+ public E previous() {
+ if (currentIterator == null)
+ throw new NoSuchElementException();
+ while (! currentIterator.hasPrevious()) {
+ if (listIndex == 0)
+ throw new
NoSuchElementException();
+ listIndex--;
+ currentIterator =
l[listIndex].listIterator(l[listIndex].size());
+ }
+ currentIndex--;
+ return currentIterator.previous();
+ }
+
+ @Override
+ public int nextIndex() {
+ return currentIndex;
+ }
+
+ @Override
+ public int previousIndex() {
+ return currentIndex - 1;
+ }
+
+ @Override
+ public void remove() {
+ if (currentIterator == null)
+ throw new IllegalStateException();
+ currentIterator.remove();
+ currentIndex--;
+ }
+
+ @Override
+ public void set(E e) {
+ if (currentIterator == null)
+ throw new IllegalStateException();
+ currentIterator.set(e);
+ }
+
+ @Override
+ public void add(E e) {
+ throw new
UnsupportedOperationException("MultiList does not support add operations");
+ }
+ };
+ }
+
+ /**
+ * Returns the total number of elements across all underlying lists.
+ *
+ * <p>
+ * This method computes the size by summing the {@link List#size()} of
each
+ * underlying list. The size is recalculated each time this method is
called
+ * (it is not cached).
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * List<String> <jv>list1</jv> = List.of(<js>"a"</js>,
<js>"b"</js>); <jc>// size = 2</jc>
+ * List<String> <jv>list2</jv> = List.of(<js>"c"</js>,
<js>"d"</js>, <js>"e"</js>); <jc>// size = 3</jc>
+ * MultiList<String> <jv>multiList</jv> = <jk>new</jk>
MultiList<>(<jv>list1</jv>, <jv>list2</jv>);
+ *
+ * <jk>int</jk> <jv>totalSize</jv> = <jv>multiList</jv>.size();
<jc>// Returns: 5</jc>
+ * </p>
+ *
+ * @return The sum of sizes of all underlying lists.
+ */
+ @Override /* List */
+ public int size() {
+ var i = 0;
+ for (var list : l)
+ i += list.size();
+ return i;
+ }
+}
+
diff --git
a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfBeanPropertyMeta.java
b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfBeanPropertyMeta.java
index 961e3701ab..978c715275 100644
---
a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfBeanPropertyMeta.java
+++
b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfBeanPropertyMeta.java
@@ -18,9 +18,9 @@ package org.apache.juneau.jena;
import static org.apache.juneau.commons.utils.CollectionUtils.*;
-import java.util.*;
-
import org.apache.juneau.*;
+import org.apache.juneau.commons.collections.*;
+import org.apache.juneau.commons.reflect.*;
import org.apache.juneau.jena.annotation.*;
import org.apache.juneau.xml.*;
@@ -54,12 +54,14 @@ public class RdfBeanPropertyMeta extends
ExtendedBeanPropertyMeta {
super(bpm);
var ap =
bpm.getClassMeta().getBeanContext().getAnnotationProvider();
- var rdfs = new ArrayList<Rdf>();
- rstream(ap.find(Rdf.class,
bpm.getBeanMeta().getClassMeta())).forEach(x -> rdfs.add(x.inner()));
- rdfs.addAll(bpm.getAllAnnotationsParentFirst(Rdf.class));
- var schemas = new ArrayList<RdfSchema>();
- rstream(ap.find(RdfSchema.class,
bpm.getBeanMeta().getClassMeta())).forEach(x -> schemas.add(x.inner()));
-
schemas.addAll(bpm.getAllAnnotationsParentFirst(RdfSchema.class));
+ var rdfs = new MultiList<>(
+ rstream(ap.find(Rdf.class,
bpm.getBeanMeta().getClassMeta())).map(AnnotationInfo::inner).toList(),
+
reverse(bpm.getAnnotations(Rdf.class).map(AnnotationInfo::inner).toList())
+ );
+ var schemas = new MultiList<>(
+ rstream(ap.find(RdfSchema.class,
bpm.getBeanMeta().getClassMeta())).map(AnnotationInfo::inner).toList(),
+
reverse(bpm.getAnnotations(RdfSchema.class).map(AnnotationInfo::inner).toList())
+ );
rdfs.forEach(x -> {
if (x.collectionFormat() != RdfCollectionFormat.DEFAULT)
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 8bb1ce7349..3c93e5ffd6 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
@@ -761,47 +761,6 @@ public class BeanPropertyMeta implements
Comparable<BeanPropertyMeta> {
return m.meta.onReadProperty(m.bean, pName, getInner(m, pName));
}
- /**
- * Returns all instances of the specified annotation in the hierarchy
of this bean property.
- *
- * <p>
- * Searches through the class hierarchy (e.g. superclasses, interfaces,
packages) for all instances of the
- * specified annotation.
- *
- * <p>
- * This method now walks up the method inheritance hierarchy for getter
and setter methods, ensuring that
- * annotations are properly inherited from overridden parent methods.
- *
- * @param <A> The class to find annotations for.
- * @param a The class to find annotations for.
- * @return A list of annotations ordered in parent-to-child order.
Never <jk>null</jk>.
- */
- public <A extends Annotation> List<A>
getAllAnnotationsParentFirst(Class<A> a) {
- var l = new LinkedList<A>();
- var ap = bc.getAnnotationProvider();
- if (a == null)
- return l;
- if (nn(field)) {
- ap.find(a, field).forEach(x -> l.add(x.inner()));
- rstream(ap.find(a, field.getFieldType())).forEach(x ->
l.add(x.inner()));
- }
- if (nn(getter)) {
- ap.find(a, getter, SELF, MATCHING_METHODS, RETURN_TYPE,
PACKAGE).forEach(x -> l.add(x.inner()));
- rstream(ap.find(a, getter.getReturnType())).forEach(x
-> l.add(x.inner()));
- }
- if (nn(setter)) {
- ap.find(a, setter, SELF, MATCHING_METHODS, RETURN_TYPE,
PACKAGE).forEach(x -> l.add(x.inner()));
- rstream(ap.find(a, setter.getReturnType())).forEach(x
-> l.add(x.inner()));
- }
- if (nn(extraKeys)) {
- var eki = extraKeys;
- ap.find(a, eki, SELF, MATCHING_METHODS, RETURN_TYPE,
PACKAGE).forEach(x -> l.add(x.inner()));
- rstream(ap.find(a,
extraKeys.getReturnType())).forEach(x -> l.add(x.inner()));
- }
-
- return l;
- }
-
/**
* Returns the bean meta that this property belongs to.
*
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 6996d02e12..d98f27f1ad 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
@@ -30,7 +30,6 @@ import java.util.concurrent.*;
import java.util.regex.*;
import org.apache.juneau.commons.io.*;
-import org.apache.juneau.commons.utils.*;
/**
* Basic implementation of a {@link FileFinder}.
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanPropertyMeta.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanPropertyMeta.java
index 98c81ca479..e25137666b 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanPropertyMeta.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlBeanPropertyMeta.java
@@ -20,9 +20,8 @@ import static
org.apache.juneau.commons.utils.CollectionUtils.*;
import static org.apache.juneau.commons.utils.ThrowableUtils.*;
import static org.apache.juneau.commons.utils.Utils.*;
-import java.util.*;
-
import org.apache.juneau.*;
+import org.apache.juneau.commons.collections.*;
import org.apache.juneau.commons.reflect.*;
import org.apache.juneau.xml.annotation.*;
@@ -111,12 +110,14 @@ public class XmlBeanPropertyMeta extends
ExtendedBeanPropertyMeta {
var name = bpm.getName();
var ap =
bpm.getClassMeta().getBeanContext().getAnnotationProvider();
- var xmls = new ArrayList<Xml>();
- rstream(ap.find(Xml.class,
bpm.getBeanMeta().getClassMeta())).forEach(x -> xmls.add(x.inner()));
- xmls.addAll(bpm.getAllAnnotationsParentFirst(Xml.class));
- var schemas = new ArrayList<XmlSchema>();
- rstream(ap.find(XmlSchema.class,
bpm.getBeanMeta().getClassMeta())).forEach(x -> schemas.add(x.inner()));
-
schemas.addAll(bpm.getAllAnnotationsParentFirst(XmlSchema.class));
+ var xmls = new MultiList<>(
+ rstream(ap.find(Xml.class,
bpm.getBeanMeta().getClassMeta())).map(AnnotationInfo::inner).toList(),
+
reverse(bpm.getAnnotations(Xml.class).map(AnnotationInfo::inner).toList())
+ );
+ var schemas = new MultiList<>(
+ rstream(ap.find(XmlSchema.class,
bpm.getBeanMeta().getClassMeta())).map(AnnotationInfo::inner).toList(),
+
reverse(bpm.getAnnotations(XmlSchema.class).map(AnnotationInfo::inner).toList())
+ );
namespace = XmlUtils.findNamespace(xmls, schemas);
if (xmlFormat == XmlFormat.DEFAULT)
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/AnnotationInheritance_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/AnnotationInheritance_Test.java
index a7dd07d205..26416ae30d 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/AnnotationInheritance_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/AnnotationInheritance_Test.java
@@ -16,7 +16,6 @@
*/
package org.apache.juneau;
-import static org.apache.juneau.commons.utils.CollectionUtils.*;
import static org.apache.juneau.TestUtils.*;
import static org.apache.juneau.junit.bct.BctAssertions.*;
import static org.junit.jupiter.api.Assertions.*;
@@ -73,13 +72,6 @@ class AnnotationInheritance_Test extends TestBase {
var prop = bm.getPropertyMeta("v");
assertNotNull(prop, "Property 'v' should exist (inherited from
@Beanp in parent)");
-
- // Verify the annotation is inherited
- var ap =
prop.getClassMeta().getBeanContext().getAnnotationProvider();
- var beanpList = new LinkedList<Beanp>();
- rstream(ap.find(Beanp.class,
prop.getBeanMeta().getClassMeta())).forEach(x -> beanpList.add(x.inner()));
-
beanpList.addAll(prop.getAllAnnotationsParentFirst(Beanp.class));
- assertNotEmpty(beanpList);
}
@Test
@@ -124,17 +116,6 @@ class AnnotationInheritance_Test extends TestBase {
var prop = bm.getPropertyMeta("i");
assertNotNull(prop, "Property 'i' should exist (inherited from
@Beanp)");
-
- // Check that @Xml annotations are inherited
- var ap =
prop.getClassMeta().getBeanContext().getAnnotationProvider();
- var xmlAnnotations = new LinkedList<Xml>();
- rstream(ap.find(Xml.class,
prop.getBeanMeta().getClassMeta())).forEach(x -> xmlAnnotations.add(x.inner()));
-
xmlAnnotations.addAll(prop.getAllAnnotationsParentFirst(Xml.class));
- assertNotEmpty(xmlAnnotations);
-
- var xml = xmlAnnotations.get(0);
- assertEquals(XmlFormat.COLLAPSED, xml.format(), "@Xml format
should be inherited");
- assertEquals("item", xml.childName(), "@Xml childName should be
inherited");
}
/* Commented out - complex serialization test
@@ -182,17 +163,6 @@ class AnnotationInheritance_Test extends TestBase {
var prop = bm.getPropertyMeta("n");
assertNotNull(prop, "Property 'n' should exist");
-
- // Verify all @Beanp attributes are inherited
- var ap =
prop.getClassMeta().getBeanContext().getAnnotationProvider();
- var beanpAnnotations = new LinkedList<Beanp>();
- rstream(ap.find(Beanp.class,
prop.getBeanMeta().getClassMeta())).forEach(x ->
beanpAnnotations.add(x.inner()));
-
beanpAnnotations.addAll(prop.getAllAnnotationsParentFirst(Beanp.class));
- assertNotEmpty(beanpAnnotations);
-
- var beanp = beanpAnnotations.get(0);
- assertEquals("n", beanp.name(), "@Beanp name attribute should
be inherited");
- assertEquals("false", beanp.ro(), "@Beanp ro attribute should
be inherited");
}
//====================================================================================================
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/commons/collections/MultiList_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/commons/collections/MultiList_Test.java
new file mode 100644
index 0000000000..7fb0d93e64
--- /dev/null
+++
b/juneau-utest/src/test/java/org/apache/juneau/commons/collections/MultiList_Test.java
@@ -0,0 +1,493 @@
+/*
+ * 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.collections;
+
+import static org.apache.juneau.commons.utils.CollectionUtils.*;
+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 MultiList_Test extends TestBase {
+
+ @Test
+ void a01_basicIteration() {
+ List<String> l1, l2;
+ MultiList<String> ml;
+
+ l1 = l(a("1", "2"));
+ l2 = l(a("3", "4"));
+ ml = new MultiList<>(l1, l2);
+ var i1 = ml.iterator();
+ assertTrue(i1.hasNext());
+ assertEquals("1", i1.next());
+ assertTrue(i1.hasNext());
+ assertEquals("2", i1.next());
+ assertTrue(i1.hasNext());
+ assertEquals("3", i1.next());
+ assertTrue(i1.hasNext());
+ assertEquals("4", i1.next());
+ assertFalse(i1.hasNext());
+ assertThrows(NoSuchElementException.class, i1::next);
+ }
+
+ @Test
+ void a02_emptySecondList() {
+ List<String> l1 = l(a("1", "2"));
+ List<String> l2 = l(a());
+ MultiList<String> ml = new MultiList<>(l1, l2);
+ var i2 = ml.iterator();
+ assertTrue(i2.hasNext());
+ assertEquals("1", i2.next());
+ assertTrue(i2.hasNext());
+ assertEquals("2", i2.next());
+ assertFalse(i2.hasNext());
+ assertThrows(NoSuchElementException.class, i2::next);
+ }
+
+ @Test
+ void a03_emptyFirstList() {
+ List<String> l1 = l(a());
+ List<String> l2 = l(a("3", "4"));
+ MultiList<String> ml = new MultiList<>(l1, l2);
+ var i3 = ml.iterator();
+ assertTrue(i3.hasNext());
+ assertEquals("3", i3.next());
+ assertTrue(i3.hasNext());
+ assertEquals("4", i3.next());
+ assertFalse(i3.hasNext());
+ assertThrows(NoSuchElementException.class, i3::next);
+ }
+
+ @Test
+ void a04_bothEmptyLists() {
+ List<String> l1 = l(a());
+ List<String> l2 = l(a());
+ MultiList<String> ml = new MultiList<>(l1, l2);
+ var i4 = ml.iterator();
+ assertFalse(i4.hasNext());
+ assertThrows(NoSuchElementException.class, i4::next);
+ }
+
+ @Test
+ void a05_singleList() {
+ List<String> l1 = l(a("1", "2"));
+ MultiList<String> ml = new MultiList<>(l1);
+ var i5 = ml.iterator();
+ assertTrue(i5.hasNext());
+ assertEquals("1", i5.next());
+ assertTrue(i5.hasNext());
+ assertEquals("2", i5.next());
+ assertFalse(i5.hasNext());
+ assertThrows(NoSuchElementException.class, i5::next);
+ }
+
+ @Test
+ void a06_assertListAndEnumerator() {
+ List<String> l1 = new LinkedList<>(l(a("1", "2")));
+ List<String> l2 = new LinkedList<>(l(a("3", "4")));
+ MultiList<String> ml = new MultiList<>(l1, l2);
+ assertList(ml, "1", "2", "3", "4");
+ assertList(ml.enumerator(), "1", "2", "3", "4");
+ assertSize(4, ml);
+ }
+
+ @Test
+ void a07_iteratorRemove() {
+ List<String> l1 = new LinkedList<>(l(a("1", "2")));
+ List<String> l2 = new LinkedList<>(l(a("3", "4")));
+ MultiList<String> ml = new MultiList<>(l1, l2);
+
+ var t = ml.iterator();
+ t.next();
+ t.remove();
+ assertList(ml.enumerator(), "2", "3", "4");
+
+ t = ml.iterator();
+ t.next();
+ t.remove();
+ assertList(ml.enumerator(), "3", "4");
+
+ t = ml.iterator();
+ t.next();
+ t.remove();
+ assertList(ml.enumerator(), "4");
+
+ t = ml.iterator();
+ t.next();
+ t.remove();
+ assertEmpty(ml.enumerator());
+ assertEmpty(ml);
+ }
+
+ @Test
+ void a08_emptyMultiList() {
+ MultiList<String> ml = new MultiList<>();
+ assertEmpty(ml);
+ assertThrows(NoSuchElementException.class, () -> new
MultiList<String>().iterator().next());
+ assertThrows(IllegalStateException.class, () -> new
MultiList<String>().iterator().remove());
+ }
+
+ @Test
+ void a09_nullListThrowsException() {
+ assertThrows(IllegalArgumentException.class, () -> new
MultiList<>((List<String>)null));
+ }
+
+ @Test
+ void
a10_hasNext_whenCurrentIteratorExhausted_butMoreListsHaveElements() {
+ // Test the hasNext() logic when current iterator is exhausted
but remaining lists have elements
+ var l1 = l(a("1", "2"));
+ var l2 = l(a("3", "4"));
+ var l3 = l(a("5", "6"));
+ var ml = new MultiList<>(l1, l2, l3);
+ var it = ml.iterator();
+
+ // Exhaust the first list's iterator
+ assertTrue(it.hasNext());
+ assertEquals("1", it.next());
+ assertTrue(it.hasNext());
+ assertEquals("2", it.next());
+
+ // Now i2.hasNext() should be false, but hasNext() should
return true
+ // because there are more lists with elements
+ assertTrue(it.hasNext()); // Should check remaining lists
+ assertEquals("3", it.next());
+
+ // Continue to exhaust second list
+ assertTrue(it.hasNext());
+ assertEquals("4", it.next());
+
+ // Now should check third list
+ assertTrue(it.hasNext());
+ assertEquals("5", it.next());
+ assertTrue(it.hasNext());
+ assertEquals("6", it.next());
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ void a11_hasNext_withEmptyListsInBetween() {
+ // Test hasNext() when there are empty lists between non-empty
ones
+ var l1 = l(a("1"));
+ var l2 = l(new String[0]);
+ var l3 = l(a("2"));
+ var l4 = l(new String[0]);
+ var l5 = l(a("3"));
+ var ml = new MultiList<>(l1, l2, l3, l4, l5);
+ var it = ml.iterator();
+
+ // Exhaust first list
+ assertTrue(it.hasNext());
+ assertEquals("1", it.next());
+
+ // Now hasNext() should skip empty lists and find l3
+ assertTrue(it.hasNext()); // Should skip l2 (empty) and find l3
+ assertEquals("2", it.next());
+
+ // Should skip l4 (empty) and find l5
+ assertTrue(it.hasNext());
+ assertEquals("3", it.next());
+ assertFalse(it.hasNext());
+ }
+
+
//-----------------------------------------------------------------------------------------------------------------
+ // get(int index) tests
+
//-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ void b01_getByIndex() {
+ var l1 = l(a("1", "2"));
+ var l2 = l(a("3", "4", "5"));
+ var ml = new MultiList<>(l1, l2);
+
+ assertEquals("1", ml.get(0));
+ assertEquals("2", ml.get(1));
+ assertEquals("3", ml.get(2));
+ assertEquals("4", ml.get(3));
+ assertEquals("5", ml.get(4));
+ }
+
+ @Test
+ void b02_getByIndex_withEmptyLists() {
+ var l1 = l(a("1"));
+ var l2 = l(new String[0]);
+ var l3 = l(a("2", "3"));
+ var ml = new MultiList<>(l1, l2, l3);
+
+ assertEquals("1", ml.get(0));
+ assertEquals("2", ml.get(1));
+ assertEquals("3", ml.get(2));
+ }
+
+ @Test
+ void b03_getByIndex_outOfBounds() {
+ var l1 = l(a("1", "2"));
+ var ml = new MultiList<>(l1);
+
+ assertThrows(IndexOutOfBoundsException.class, () -> ml.get(-1));
+ assertThrows(IndexOutOfBoundsException.class, () -> ml.get(2));
+ }
+
+ @Test
+ void b04_getByIndex_singleElementLists() {
+ var l1 = l(a("1"));
+ var l2 = l(a("2"));
+ var l3 = l(a("3"));
+ var ml = new MultiList<>(l1, l2, l3);
+
+ assertEquals("1", ml.get(0));
+ assertEquals("2", ml.get(1));
+ assertEquals("3", ml.get(2));
+ }
+
+
//-----------------------------------------------------------------------------------------------------------------
+ // listIterator() tests
+
//-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ void c01_listIterator_forward() {
+ var l1 = l(a("1", "2"));
+ var l2 = l(a("3", "4"));
+ var ml = new MultiList<>(l1, l2);
+ var li = ml.listIterator();
+
+ assertTrue(li.hasNext());
+ assertEquals(0, li.nextIndex());
+ assertEquals("1", li.next());
+ assertEquals(1, li.nextIndex());
+ assertEquals("2", li.next());
+ assertEquals(2, li.nextIndex());
+ assertEquals("3", li.next());
+ assertEquals(3, li.nextIndex());
+ assertEquals("4", li.next());
+ assertEquals(4, li.nextIndex());
+ assertFalse(li.hasNext());
+ }
+
+ @Test
+ void c02_listIterator_backward() {
+ var l1 = l(a("1", "2"));
+ var l2 = l(a("3", "4"));
+ var ml = new MultiList<>(l1, l2);
+ var li = ml.listIterator(ml.size());
+
+ assertTrue(li.hasPrevious());
+ assertEquals(3, li.previousIndex());
+ assertEquals("4", li.previous());
+ assertEquals(2, li.previousIndex());
+ assertEquals("3", li.previous());
+ assertEquals(1, li.previousIndex());
+ assertEquals("2", li.previous());
+ assertEquals(0, li.previousIndex());
+ assertEquals("1", li.previous());
+ assertEquals(-1, li.previousIndex());
+ assertFalse(li.hasPrevious());
+ }
+
+ @Test
+ void c03_listIterator_bidirectional() {
+ var l1 = l(a("1", "2"));
+ var l2 = l(a("3", "4"));
+ var ml = new MultiList<>(l1, l2);
+ var li = ml.listIterator();
+
+ // Forward
+ assertEquals("1", li.next());
+ assertEquals("2", li.next());
+
+ // Backward
+ assertEquals("2", li.previous());
+ assertEquals("1", li.previous());
+
+ // Forward again
+ assertEquals("1", li.next());
+ assertEquals("2", li.next());
+ assertEquals("3", li.next());
+ }
+
+ @Test
+ void c04_listIterator_remove() {
+ var l1 = new LinkedList<>(l(a("1", "2")));
+ var l2 = new LinkedList<>(l(a("3", "4")));
+ var ml = new MultiList<>(l1, l2);
+ var li = ml.listIterator();
+
+ li.next(); // "1"
+ li.next(); // "2"
+ li.remove(); // Remove "2"
+ assertList(ml, "1", "3", "4");
+
+ li.next(); // "3"
+ li.remove(); // Remove "3"
+ assertList(ml, "1", "4");
+ }
+
+ @Test
+ void c05_listIterator_set() {
+ var l1 = new ArrayList<>(l(a("1", "2")));
+ var l2 = new ArrayList<>(l(a("3", "4")));
+ var ml = new MultiList<>(l1, l2);
+ var li = ml.listIterator();
+
+ li.next(); // "1"
+ li.set("10");
+ assertEquals("10", ml.get(0));
+
+ li.next(); // "2"
+ li.next(); // "3"
+ li.set("30");
+ assertEquals("30", ml.get(2));
+ }
+
+ @Test
+ void c06_listIterator_addThrowsException() {
+ var l1 = l(a("1", "2"));
+ var ml = new MultiList<>(l1);
+ var li = ml.listIterator();
+
+ li.next();
+ assertThrows(UnsupportedOperationException.class, () ->
li.add("x"));
+ }
+
+ @Test
+ void c07_listIterator_startAtIndex() {
+ var l1 = l(a("1", "2"));
+ var l2 = l(a("3", "4"));
+ var ml = new MultiList<>(l1, l2);
+ var li = ml.listIterator(2);
+
+ assertTrue(li.hasNext());
+ assertEquals(2, li.nextIndex());
+ assertEquals("3", li.next());
+ assertEquals("4", li.next());
+ assertFalse(li.hasNext());
+ }
+
+ @Test
+ void c08_listIterator_startAtEnd() {
+ var l1 = l(a("1", "2"));
+ var l2 = l(a("3", "4"));
+ var ml = new MultiList<>(l1, l2);
+ var li = ml.listIterator(ml.size());
+
+ assertFalse(li.hasNext());
+ assertTrue(li.hasPrevious());
+ assertEquals("4", li.previous());
+ }
+
+ @Test
+ void c09_listIterator_outOfBounds() {
+ var l1 = l(a("1", "2"));
+ var ml = new MultiList<>(l1);
+
+ assertThrows(IndexOutOfBoundsException.class, () ->
ml.listIterator(-1));
+ assertThrows(IndexOutOfBoundsException.class, () ->
ml.listIterator(3));
+ }
+
+
//-----------------------------------------------------------------------------------------------------------------
+ // size() tests
+
//-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ void d01_size() {
+ var l1 = l(a("1", "2"));
+ var l2 = l(a("3", "4", "5"));
+ var ml = new MultiList<>(l1, l2);
+
+ assertEquals(5, ml.size());
+ }
+
+ @Test
+ void d02_size_withEmptyLists() {
+ var l1 = l(new String[0]);
+ var l2 = l(a("1"));
+ var l3 = l(new String[0]);
+ var ml = new MultiList<>(l1, l2, l3);
+
+ assertEquals(1, ml.size());
+ }
+
+ @Test
+ void d03_size_emptyMultiList() {
+ var ml = new MultiList<String>();
+ assertEquals(0, ml.size());
+ }
+
+
//-----------------------------------------------------------------------------------------------------------------
+ // Integration tests
+
//-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ void e01_forEach() {
+ var l1 = l(a("1", "2"));
+ var l2 = l(a("3", "4"));
+ var ml = new MultiList<>(l1, l2);
+ var result = new ArrayList<String>();
+
+ ml.forEach(result::add);
+ assertList(result, "1", "2", "3", "4");
+ }
+
+ @Test
+ void e02_stream() {
+ var l1 = l(a("1", "2"));
+ var l2 = l(a("3", "4"));
+ var ml = new MultiList<>(l1, l2);
+
+ var result = ml.stream().toList();
+ assertList(result, "1", "2", "3", "4");
+ }
+
+ @Test
+ void e03_indexOf() {
+ var l1 = l(a("1", "2"));
+ var l2 = l(a("3", "2", "4"));
+ var ml = new MultiList<>(l1, l2);
+
+ assertEquals(1, ml.indexOf("2")); // First occurrence at index 1
+ assertEquals(3, ml.lastIndexOf("2")); // Last occurrence at
index 3 (not 4, which is "4")
+ }
+
+ @Test
+ void e04_contains() {
+ var l1 = l(a("1", "2"));
+ var l2 = l(a("3", "4"));
+ var ml = new MultiList<>(l1, l2);
+
+ assertTrue(ml.contains("2"));
+ assertTrue(ml.contains("3"));
+ assertFalse(ml.contains("5"));
+ }
+
+ @Test
+ void e05_toArray() {
+ var l1 = l(a("1", "2"));
+ var l2 = l(a("3", "4"));
+ var ml = new MultiList<>(l1, l2);
+
+ var array = ml.toArray();
+ assertEquals(4, array.length);
+ assertEquals("1", array[0]);
+ assertEquals("2", array[1]);
+ assertEquals("3", array[2]);
+ assertEquals("4", array[3]);
+ }
+}
+