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 8599116a82 Fix Json5 collection freeze-bypass and standardize 
Unmodifiable inner-class names
8599116a82 is described below

commit 8599116a82313e7b2ef8d2b6aa4e533d4c873f6e
Author: James Bognar <[email protected]>
AuthorDate: Sat Jul 25 08:20:45 2026 -0400

    Fix Json5 collection freeze-bypass and standardize Unmodifiable inner-class 
names
    
    Json5Map/Json5List's Unmodifiable variants overrode only a subset of 
mutators,
    so a frozen instance could still be mutated (clear, putAll, putIfAbsent,
    compute*, merge, replace*, the keySet()/entrySet()/values() views, 
add(Object),
    addAll, remove(Object), iterator-remove, sort, etc.). Extended both override
    sets to the full MarshalledMap/MarshalledList "D4" surface so the entire
    mutation surface throws.
    
    Also standardized the marshall-family frozen-variant inner classes onto the
    project's nested Unmodifiable convention (X.Unmodifiable extends X), 
renaming
    the legacy UnmodifiableXxx names in Json5Map, Json5List, JsonMap, JsonList,
    MarshalledMap, and MarshalledList. Private-static, internal-only renames 
with
    no behavior or API change.
---
 .../juneau/marshall/collections/JsonList.java      |   8 +-
 .../juneau/marshall/collections/JsonMap.java       |   8 +-
 .../marshall/collections/MarshalledList.java       |   8 +-
 .../juneau/marshall/collections/MarshalledMap.java |   8 +-
 .../apache/juneau/marshall/json5/Json5List.java    | 177 ++++++++++++++++++++-
 .../org/apache/juneau/marshall/json5/Json5Map.java |  78 ++++++++-
 .../juneau/collections/MarshalledMap_Test.java     |   2 +-
 .../marshall/collections/MarshalledMap_Test.java   |   2 +-
 .../juneau/marshall/json5/Json5List_Test.java      |  47 ++++++
 .../juneau/marshall/json5/Json5Map_Test.java       |  33 ++++
 10 files changed, 345 insertions(+), 26 deletions(-)

diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/JsonList.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/JsonList.java
index 7653e35190..32810b86ec 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/JsonList.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/JsonList.java
@@ -104,13 +104,13 @@ public class JsonList extends MarshalledList {
        @SuppressWarnings({
                "java:S110" // Inner class has many fields, acceptable for 
collection implementation
        })
-       private static class UnmodifiableJsonList extends JsonList {
+       private static class Unmodifiable extends JsonList {
                private static final long serialVersionUID = 1L;
 
                @SuppressWarnings({
                        "synthetic-access" // Access to outer class members is 
intentional
                })
-               UnmodifiableJsonList(JsonList contents) {
+               Unmodifiable(JsonList contents) {
                        if (nn(contents))
                                contents.forEach(super::add);
                }
@@ -712,9 +712,9 @@ public class JsonList extends MarshalledList {
 
        @Override /* Overridden from MarshalledList */
        public JsonList unmodifiable() {
-               if (this instanceof UnmodifiableJsonList this2)
+               if (this instanceof Unmodifiable this2)
                        return this2;
-               return new UnmodifiableJsonList(this);
+               return new Unmodifiable(this);
        }
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/JsonMap.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/JsonMap.java
index f8b1fe2dd0..9ed4940e2e 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/JsonMap.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/JsonMap.java
@@ -101,13 +101,13 @@ public class JsonMap extends MarshalledMap {
                "java:S2160", // equals() / hashCode() inherited from JsonMap; 
map equality is element-based
                "java:S110" // Inheritance depth is intentional in the 
marshalled-map hierarchy.
        })
-       private static class UnmodifiableJsonMap extends JsonMap {
+       private static class Unmodifiable extends JsonMap {
                private static final long serialVersionUID = 1L;
 
                @SuppressWarnings({
                        "synthetic-access" // Access to outer class members is 
intentional
                })
-               UnmodifiableJsonMap(JsonMap contents) {
+               Unmodifiable(JsonMap contents) {
                        if (nn(contents))
                                contents.forEach(super::put);
                }
@@ -714,9 +714,9 @@ public class JsonMap extends MarshalledMap {
 
        @Override /* Overridden from MarshalledMap */
        public JsonMap unmodifiable() {
-               if (this instanceof UnmodifiableJsonMap this2)
+               if (this instanceof Unmodifiable this2)
                        return this2;
-               return new UnmodifiableJsonMap(this);
+               return new Unmodifiable(this);
        }
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/MarshalledList.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/MarshalledList.java
index d14346e83a..64d239d967 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/MarshalledList.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/MarshalledList.java
@@ -58,13 +58,13 @@ public class MarshalledList extends LinkedList<Object> {
        @SuppressWarnings({
                "java:S110" // Inner class has many fields, acceptable for 
collection implementation
        })
-       private static class UnmodifiableMarshalledList extends MarshalledList {
+       private static class Unmodifiable extends MarshalledList {
                private static final long serialVersionUID = 1L;
 
                @SuppressWarnings({
                        "synthetic-access" // Access to outer class members is 
intentional
                })
-               UnmodifiableMarshalledList(MarshalledList contents) {
+               Unmodifiable(MarshalledList contents) {
                        if (nn(contents))
                                contents.forEach(super::add);
                }
@@ -876,9 +876,9 @@ public class MarshalledList extends LinkedList<Object> {
         * @return An unmodifiable copy of this list if it's modifiable, or 
this list if it is already unmodifiable.
         */
        public MarshalledList unmodifiable() {
-               if (this instanceof UnmodifiableMarshalledList this2)
+               if (this instanceof Unmodifiable this2)
                        return this2;
-               return new UnmodifiableMarshalledList(this);
+               return new Unmodifiable(this);
        }
 
        private PathTraversal getPathTraversal() {
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/MarshalledMap.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/MarshalledMap.java
index d83138321e..a15876e611 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/MarshalledMap.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/MarshalledMap.java
@@ -63,13 +63,13 @@ public class MarshalledMap extends 
LinkedHashMap<String,Object> {
        @SuppressWarnings({
                "java:S2160" // equals() / hashCode() inherited from 
MarshalledMap; map equality is element-based
        })
-       private static class UnmodifiableMarshalledMap extends MarshalledMap {
+       private static class Unmodifiable extends MarshalledMap {
                private static final long serialVersionUID = 1L;
 
                @SuppressWarnings({
                        "synthetic-access" // Access to outer class members is 
intentional
                })
-               UnmodifiableMarshalledMap(MarshalledMap contents) {
+               Unmodifiable(MarshalledMap contents) {
                        if (nn(contents))
                                contents.forEach(super::put);
                }
@@ -1519,9 +1519,9 @@ public class MarshalledMap extends 
LinkedHashMap<String,Object> {
         * @return An unmodifiable copy of this map if it's modifiable, or this 
map if it is already unmodifiable.
         */
        public MarshalledMap unmodifiable() {
-               if (this instanceof UnmodifiableMarshalledMap this2)
+               if (this instanceof Unmodifiable this2)
                        return this2;
-               return new UnmodifiableMarshalledMap(this);
+               return new Unmodifiable(this);
        }
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5/Json5List.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5/Json5List.java
index 5f5ab6d2bf..5ad958f423 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5/Json5List.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5/Json5List.java
@@ -70,34 +70,203 @@ public class Json5List extends MarshalledList {
        @SuppressWarnings({
                "java:S110" // Inner class has many fields, acceptable for 
collection implementation
        })
-       private static class UnmodifiableJson5List extends Json5List {
+       private static class Unmodifiable extends Json5List {
                private static final long serialVersionUID = 1L;
 
                @SuppressWarnings({
                        "synthetic-access" // Access to outer class members is 
intentional
                })
-               UnmodifiableJson5List(Json5List contents) {
+               Unmodifiable(Json5List contents) {
                        if (nn(contents))
                                contents.forEach(super::add);
                }
 
+               @Override /* Overridden from List */
+               public boolean add(Object object) {
+                       throw uoroex();
+               }
+
                @Override /* Overridden from List */
                public void add(int location, Object object) {
                        throw uoroex();
                }
 
+               @Override /* Overridden from List */
+               public boolean addAll(Collection<?> c) {
+                       throw uoroex();
+               }
+
+               @Override /* Overridden from List */
+               public boolean addAll(int location, Collection<?> c) {
+                       throw uoroex();
+               }
+
+               @Override /* Overridden from Deque */
+               public void addFirst(Object object) {
+                       throw uoroex();
+               }
+
+               @Override /* Overridden from Deque */
+               public void addLast(Object object) {
+                       throw uoroex();
+               }
+
+               @Override /* Overridden from List */
+               public void clear() {
+                       throw uoroex();
+               }
+
                @Override
                public boolean isUnmodifiable() { return true; }
 
+               @Override /* Overridden from List */
+               public Iterator<Object> iterator() {
+                       return readOnlyIterator(super.iterator());
+               }
+
+               @Override /* Overridden from List */
+               public ListIterator<Object> listIterator() {
+                       return readOnlyListIterator(super.listIterator());
+               }
+
+               @Override /* Overridden from List */
+               public ListIterator<Object> listIterator(int location) {
+                       return 
readOnlyListIterator(super.listIterator(location));
+               }
+
+               @Override /* Overridden from Queue */
+               public boolean offer(Object object) {
+                       throw uoroex();
+               }
+
+               @Override /* Overridden from Deque */
+               public boolean offerFirst(Object object) {
+                       throw uoroex();
+               }
+
+               @Override /* Overridden from Deque */
+               public boolean offerLast(Object object) {
+                       throw uoroex();
+               }
+
+               @Override /* Overridden from Queue */
+               public Object poll() {
+                       throw uoroex();
+               }
+
+               @Override /* Overridden from Deque */
+               public Object pollFirst() {
+                       throw uoroex();
+               }
+
+               @Override /* Overridden from Deque */
+               public Object pollLast() {
+                       throw uoroex();
+               }
+
+               @Override /* Overridden from Deque */
+               public Object pop() {
+                       throw uoroex();
+               }
+
+               @Override /* Overridden from Deque */
+               public void push(Object object) {
+                       throw uoroex();
+               }
+
+               @Override /* Overridden from Queue */
+               public Object remove() {
+                       throw uoroex();
+               }
+
                @Override /* Overridden from List */
                public Object remove(int location) {
                        throw uoroex();
                }
 
+               @Override /* Overridden from List */
+               public boolean remove(Object object) {
+                       throw uoroex();
+               }
+
+               @Override /* Overridden from List */
+               public boolean removeAll(Collection<?> c) {
+                       throw uoroex();
+               }
+
+               @Override /* Overridden from Deque */
+               public Object removeFirst() {
+                       throw uoroex();
+               }
+
+               @Override /* Overridden from Deque */
+               public boolean removeFirstOccurrence(Object object) {
+                       throw uoroex();
+               }
+
+               @Override /* Overridden from Collection */
+               public boolean removeIf(Predicate<? super Object> filter) {
+                       throw uoroex();
+               }
+
+               @Override /* Overridden from Deque */
+               public Object removeLast() {
+                       throw uoroex();
+               }
+
+               @Override /* Overridden from Deque */
+               public boolean removeLastOccurrence(Object object) {
+                       throw uoroex();
+               }
+
+               @Override /* Overridden from List */
+               public void replaceAll(UnaryOperator<Object> operator) {
+                       throw uoroex();
+               }
+
+               @Override /* Overridden from List */
+               public boolean retainAll(Collection<?> c) {
+                       throw uoroex();
+               }
+
                @Override /* Overridden from List */
                public Object set(int location, Object object) {
                        throw uoroex();
                }
+
+               @Override /* Overridden from List */
+               public void sort(Comparator<? super Object> c) {
+                       throw uoroex();
+               }
+
+               @Override /* Overridden from List */
+               public List<Object> subList(int start, int end) {
+                       return u(new ArrayList<>(this).subList(start, end));
+               }
+
+               private static Iterator<Object> 
readOnlyIterator(Iterator<Object> it) {
+                       return new Iterator<>() {
+                               @Override public boolean hasNext() { return 
it.hasNext(); }
+                               @Override public Object next() { return 
it.next(); }
+                               @Override public void remove() { throw 
uoroex(); }
+                               @Override public void 
forEachRemaining(Consumer<? super Object> action) { 
it.forEachRemaining(action); }
+                       };
+               }
+
+               private static ListIterator<Object> 
readOnlyListIterator(ListIterator<Object> it) {
+                       return new ListIterator<>() {
+                               @Override public boolean hasNext() { return 
it.hasNext(); }
+                               @Override public Object next() { return 
it.next(); }
+                               @Override public boolean hasPrevious() { return 
it.hasPrevious(); }
+                               @Override public Object previous() { return 
it.previous(); }
+                               @Override public int nextIndex() { return 
it.nextIndex(); }
+                               @Override public int previousIndex() { return 
it.previousIndex(); }
+                               @Override public void remove() { throw 
uoroex(); }
+                               @Override public void set(Object e) { throw 
uoroex(); }
+                               @Override public void add(Object e) { throw 
uoroex(); }
+                               @Override public void 
forEachRemaining(Consumer<? super Object> action) { 
it.forEachRemaining(action); }
+                       };
+               }
        }
 
        private static final long serialVersionUID = 1L;
@@ -443,9 +612,9 @@ public class Json5List extends MarshalledList {
 
        @Override /* Overridden from MarshalledList */
        public Json5List unmodifiable() {
-               if (this instanceof UnmodifiableJson5List this2)
+               if (this instanceof Unmodifiable this2)
                        return this2;
-               return new UnmodifiableJson5List(this);
+               return new Unmodifiable(this);
        }
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5/Json5Map.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5/Json5Map.java
index 939021d246..1a63d2939d 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5/Json5Map.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5/Json5Map.java
@@ -67,29 +67,99 @@ public class Json5Map extends MarshalledMap {
                "java:S2160", // equals() / hashCode() inherited from Json5Map; 
map equality is element-based
                "java:S110"   // Inheritance depth inherited from MarshalledMap 
-> LinkedHashMap chain; intentional
        })
-       private static class UnmodifiableJson5Map extends Json5Map {
+       private static class Unmodifiable extends Json5Map {
                private static final long serialVersionUID = 1L;
 
                @SuppressWarnings({
                        "synthetic-access" // Access to outer class members is 
intentional
                })
-               UnmodifiableJson5Map(Json5Map contents) {
+               Unmodifiable(Json5Map contents) {
                        if (nn(contents))
                                contents.forEach(super::put);
                }
 
+               @Override
+               public void clear() {
+                       throw uoroex();
+               }
+
+               @Override
+               public Object compute(String key, BiFunction<? super String,? 
super Object,?> remappingFunction) {
+                       throw uoroex();
+               }
+
+               @Override
+               public Object computeIfAbsent(String key, Function<? super 
String,?> mappingFunction) {
+                       throw uoroex();
+               }
+
+               @Override
+               public Object computeIfPresent(String key, BiFunction<? super 
String,? super Object,?> remappingFunction) {
+                       throw uoroex();
+               }
+
+               @Override
+               public Set<Map.Entry<String,Object>> entrySet() {
+                       return u(super.entrySet());
+               }
+
                @Override
                public boolean isUnmodifiable() { return true; }
 
+               @Override
+               public Set<String> keySet() {
+                       return u(super.keySet());
+               }
+
+               @Override
+               public Object merge(String key, Object value, BiFunction<? 
super Object,? super Object,?> remappingFunction) {
+                       throw uoroex();
+               }
+
                @Override
                public Object put(String key, Object val) {
                        throw uoroex();
                }
 
+               @Override
+               public void putAll(Map<? extends String,?> m) {
+                       throw uoroex();
+               }
+
+               @Override
+               public Object putIfAbsent(String key, Object value) {
+                       throw uoroex();
+               }
+
                @Override
                public Object remove(Object key) {
                        throw uoroex();
                }
+
+               @Override
+               public boolean remove(Object key, Object value) {
+                       throw uoroex();
+               }
+
+               @Override
+               public Object replace(String key, Object value) {
+                       throw uoroex();
+               }
+
+               @Override
+               public boolean replace(String key, Object oldValue, Object 
newValue) {
+                       throw uoroex();
+               }
+
+               @Override
+               public void replaceAll(BiFunction<? super String,? super 
Object,?> function) {
+                       throw uoroex();
+               }
+
+               @Override
+               public Collection<Object> values() {
+                       return 
Collections.unmodifiableCollection(super.values());
+               }
        }
 
        private static final long serialVersionUID = 1L;
@@ -549,9 +619,9 @@ public class Json5Map extends MarshalledMap {
 
        @Override /* Overridden from MarshalledMap */
        public Json5Map unmodifiable() {
-               if (this instanceof UnmodifiableJson5Map this2)
+               if (this instanceof Unmodifiable this2)
                        return this2;
-               return new UnmodifiableJson5Map(this);
+               return new Unmodifiable(this);
        }
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/collections/MarshalledMap_Test.java
 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/collections/MarshalledMap_Test.java
index 4878ecbb7b..f30c9e1347 100644
--- 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/collections/MarshalledMap_Test.java
+++ 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/collections/MarshalledMap_Test.java
@@ -774,7 +774,7 @@ class MarshalledMap_Test extends TestBase {
        }
 
        @Test void b81_unmodifiableConstructorWithNull() {
-               // UnmodifiableMarshalledMap is private but reachable via 
.unmodifiable().
+               // Unmodifiable is private but reachable via .unmodifiable().
                // Construct from a null source by going through the public API.
                var m = MarshalledMap.create().unmodifiable();
                assertTrue(m.isUnmodifiable());
diff --git 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/collections/MarshalledMap_Test.java
 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/collections/MarshalledMap_Test.java
index 3d696311d1..a9a93186f4 100644
--- 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/collections/MarshalledMap_Test.java
+++ 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/collections/MarshalledMap_Test.java
@@ -768,7 +768,7 @@ class MarshalledMap_Test extends TestBase {
        }
 
        @Test void b81_unmodifiableConstructorWithNull() {
-               // UnmodifiableMarshalledMap is private but reachable via 
.unmodifiable().
+               // Unmodifiable is private but reachable via .unmodifiable().
                // Construct from a null source by going through the public API.
                var m = MarshalledMap.create().unmodifiable();
                assertTrue(m.isUnmodifiable());
diff --git 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5/Json5List_Test.java
 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5/Json5List_Test.java
index 71d03170a2..df4cdda520 100644
--- 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5/Json5List_Test.java
+++ 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5/Json5List_Test.java
@@ -20,6 +20,7 @@ import static org.apache.juneau.commons.utils.Shorts.*;
 import static org.junit.jupiter.api.Assertions.*;
 
 import java.io.*;
+import java.util.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.marshall.collections.*;
@@ -231,4 +232,50 @@ class Json5List_Test extends TestBase {
                assertEquals(3, l.size());
                assertEquals("a", l.getString(0));
        }
+
+       @SuppressWarnings({
+               "java:S5778", // Lambda intentionally calls multiple throwing 
methods to test compound failure scenarios.
+               "java:S5961"  // High assertion count is acceptable in a 
comprehensive data-driven mutator-surface test.
+       })
+       @Test void a20_unmodifiableFullMutatorSurfaceThrows() {
+               // Regression: Json5List.Unmodifiable originally overrode only 
add(int,·)/remove(int)/set(int,·), leaving
+               // every other List/Deque mutator (and iterator/listIterator 
remove) able to bypass the freeze.
+               var l = Json5List.of("a", "b", "c").unmodifiable();
+               assertThrows(UnsupportedOperationException.class, () -> 
l.add(0, "x"));
+               assertThrows(UnsupportedOperationException.class, () -> 
l.add("x"));
+               assertThrows(UnsupportedOperationException.class, () -> 
l.remove(0));
+               assertThrows(UnsupportedOperationException.class, () -> 
l.remove("a"));
+               assertThrows(UnsupportedOperationException.class, () -> 
l.set(0, "x"));
+               assertThrows(UnsupportedOperationException.class, () -> 
l.addAll(List.of("x")));
+               assertThrows(UnsupportedOperationException.class, () -> 
l.addAll(0, List.of("x")));
+               assertThrows(UnsupportedOperationException.class, () -> 
l.removeAll(List.of("a")));
+               assertThrows(UnsupportedOperationException.class, () -> 
l.retainAll(List.of("a")));
+               assertThrows(UnsupportedOperationException.class, l::clear);
+               assertThrows(UnsupportedOperationException.class, () -> 
l.addFirst("x"));
+               assertThrows(UnsupportedOperationException.class, () -> 
l.addLast("x"));
+               assertThrows(UnsupportedOperationException.class, 
l::removeFirst);
+               assertThrows(UnsupportedOperationException.class, 
l::removeLast);
+               assertThrows(UnsupportedOperationException.class, () -> 
l.removeFirstOccurrence("a"));
+               assertThrows(UnsupportedOperationException.class, () -> 
l.removeLastOccurrence("a"));
+               assertThrows(UnsupportedOperationException.class, () -> 
l.offer("x"));
+               assertThrows(UnsupportedOperationException.class, () -> 
l.offerFirst("x"));
+               assertThrows(UnsupportedOperationException.class, () -> 
l.offerLast("x"));
+               assertThrows(UnsupportedOperationException.class, l::poll);
+               assertThrows(UnsupportedOperationException.class, l::pollFirst);
+               assertThrows(UnsupportedOperationException.class, l::pollLast);
+               assertThrows(UnsupportedOperationException.class, l::pop);
+               assertThrows(UnsupportedOperationException.class, () -> 
l.push("x"));
+               assertThrows(UnsupportedOperationException.class, () -> 
l.removeIf(o -> true));
+               assertThrows(UnsupportedOperationException.class, () -> 
l.replaceAll(o -> "x"));
+               assertThrows(UnsupportedOperationException.class, () -> 
l.sort((a, b) -> 0));
+               var it = l.iterator();
+               it.next();
+               assertThrows(UnsupportedOperationException.class, it::remove);
+               var lit = l.listIterator();
+               lit.next();
+               assertThrows(UnsupportedOperationException.class, lit::remove);
+               assertThrows(UnsupportedOperationException.class, () -> 
lit.add("x"));
+               assertThrows(UnsupportedOperationException.class, () -> 
lit.set("x"));
+               assertEquals(3, l.size());
+       }
 }
diff --git 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5/Json5Map_Test.java
 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5/Json5Map_Test.java
index 0b01b3709b..27d26fcfb1 100644
--- 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5/Json5Map_Test.java
+++ 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5/Json5Map_Test.java
@@ -20,6 +20,7 @@ import static org.apache.juneau.commons.utils.Shorts.*;
 import static org.junit.jupiter.api.Assertions.*;
 
 import java.io.*;
+import java.util.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.marshall.collections.*;
@@ -235,4 +236,36 @@ class Json5Map_Test extends TestBase {
                Object inner = ((Json5Map)o).get("b");
                assertTrue(inner instanceof Json5List, "Expected Json5List for 
nested array, got " + cn(inner));
        }
+
+       @SuppressWarnings({
+               "java:S5778" // Lambda intentionally calls multiple throwing 
methods to test compound failure scenarios.
+       })
+       @Test void a23_unmodifiableFullMutatorSurfaceThrows() {
+               // Regression: Json5Map.Unmodifiable originally overrode only 
put()/remove(Object), leaving every
+               // other Map mutator (and the keySet()/entrySet()/values() 
collection views) able to bypass the freeze.
+               var m = Json5Map.of("a", 1).unmodifiable();
+               assertThrows(UnsupportedOperationException.class, () -> 
m.put("b", 2));
+               assertThrows(UnsupportedOperationException.class, () -> 
m.remove("a"));
+               assertThrows(UnsupportedOperationException.class, () -> 
m.putAll(Map.of("b", 2)));
+               assertThrows(UnsupportedOperationException.class, m::clear);
+               assertThrows(UnsupportedOperationException.class, () -> 
m.putIfAbsent("b", 2));
+               assertThrows(UnsupportedOperationException.class, () -> 
m.remove("a", 1));
+               assertThrows(UnsupportedOperationException.class, () -> 
m.replace("a", 2));
+               assertThrows(UnsupportedOperationException.class, () -> 
m.replace("a", 1, 2));
+               assertThrows(UnsupportedOperationException.class, () -> 
m.replaceAll((k, v) -> 99));
+               assertThrows(UnsupportedOperationException.class, () -> 
m.compute("a", (k, v) -> 99));
+               assertThrows(UnsupportedOperationException.class, () -> 
m.computeIfAbsent("b", k -> 99));
+               assertThrows(UnsupportedOperationException.class, () -> 
m.computeIfPresent("a", (k, v) -> 99));
+               assertThrows(UnsupportedOperationException.class, () -> 
m.merge("a", 99, (v1, v2) -> 99));
+               var entryIt = m.entrySet().iterator();
+               entryIt.next();
+               assertThrows(UnsupportedOperationException.class, 
entryIt::remove);
+               var keyIt = m.keySet().iterator();
+               keyIt.next();
+               assertThrows(UnsupportedOperationException.class, 
keyIt::remove);
+               var valIt = m.values().iterator();
+               valIt.next();
+               assertThrows(UnsupportedOperationException.class, 
valIt::remove);
+               assertEquals(1, m.size());
+       }
 }

Reply via email to