001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.commons.collections; 018 019import static org.apache.juneau.commons.utils.AssertionUtils.*; 020import static org.apache.juneau.commons.utils.ThrowableUtils.*; 021 022import java.util.*; 023import java.util.function.*; 024 025/** 026 * An {@link ArrayList} that allows you to control whether it's read-only via a constructor parameter. 027 * 028 * <p> 029 * This class provides a unique capability: it can appear as an unmodifiable list to external code (via the standard 030 * {@link List} interface methods) while still allowing internal code to modify it through special "override" methods. 031 * This is useful when you need to pass a list to code that should not modify it, but you still need to modify it 032 * internally. 033 * 034 * <h5 class='section'>Features:</h5> 035 * <ul class='spaced-list'> 036 * <li><b>Controlled Mutability:</b> Can be configured as modifiable or unmodifiable at construction time 037 * <li><b>Override Methods:</b> Special methods (e.g., {@link #overrideAdd(Object)}) bypass the unmodifiable restriction 038 * <li><b>Standard List Interface:</b> Implements all standard {@link List} methods with proper unmodifiable enforcement 039 * <li><b>Iterator Protection:</b> Iterators returned from unmodifiable lists prevent modification operations 040 * <li><b>Dynamic Control:</b> Can be made unmodifiable after construction via {@link #setUnmodifiable()} 041 * </ul> 042 * 043 * <h5 class='section'>Use Cases:</h5> 044 * <ul class='spaced-list'> 045 * <li>Passing a list to external code that should not modify it, while maintaining internal modification capability 046 * <li>Building a list internally and then "freezing" it before exposing it to clients 047 * <li>Creating a list that appears read-only to consumers but can be modified by trusted internal code 048 * <li>Implementing defensive copying patterns where the original list needs to remain mutable internally 049 * </ul> 050 * 051 * <h5 class='section'>Usage:</h5> 052 * <p class='bjava'> 053 * <jc>// Create a list that appears unmodifiable to external code</jc> 054 * ControlledArrayList<String> <jv>list</jv> = <jk>new</jk> ControlledArrayList<>(<jk>true</jk>); 055 * 056 * <jc>// Internal code can still modify using override methods</jc> 057 * <jv>list</jv>.overrideAdd(<js>"item1"</js>); 058 * <jv>list</jv>.overrideAdd(<js>"item2"</js>); 059 * 060 * <jc>// External code sees it as unmodifiable</jc> 061 * <jv>list</jv>.add(<js>"item3"</js>); <jc>// Throws UnsupportedOperationException</jc> 062 * 063 * <jc>// Pass to external code safely</jc> 064 * processList(<jv>list</jv>); <jc>// External code cannot modify it</jc> 065 * 066 * <jc>// But internal code can still modify</jc> 067 * <jv>list</jv>.overrideAdd(<js>"item3"</js>); <jc>// Works!</jc> 068 * </p> 069 * 070 * <h5 class='section'>Override Methods:</h5> 071 * <p> 072 * The "override" methods (e.g., {@link #overrideAdd(Object)}, {@link #overrideRemove(int)}) bypass the unmodifiable 073 * restriction and allow modification regardless of the list's modifiable state. These methods are intended for use 074 * by trusted internal code that needs to modify the list even when it's marked as unmodifiable. 075 * 076 * <p class='bjava'> 077 * <jc>// Create unmodifiable list</jc> 078 * ControlledArrayList<String> <jv>list</jv> = <jk>new</jk> ControlledArrayList<>(<jk>true</jk>); 079 * 080 * <jc>// Standard methods throw exceptions</jc> 081 * <jv>list</jv>.add(<js>"x"</js>); <jc>// UnsupportedOperationException</jc> 082 * <jv>list</jv>.remove(0); <jc>// UnsupportedOperationException</jc> 083 * 084 * <jc>// Override methods work</jc> 085 * <jv>list</jv>.overrideAdd(<js>"x"</js>); <jc>// OK</jc> 086 * <jv>list</jv>.overrideRemove(0); <jc>// OK</jc> 087 * </p> 088 * 089 * <h5 class='section'>Iterator Behavior:</h5> 090 * <p> 091 * When the list is unmodifiable, iterators returned by {@link #iterator()} and {@link #listIterator()} are read-only. 092 * Attempting to call {@link Iterator#remove()} or {@link ListIterator#set(Object)} on these iterators will throw 093 * {@link UnsupportedOperationException}. However, the override methods can still be used to modify the list. 094 * 095 * <h5 class='section'>Thread Safety:</h5> 096 * <p> 097 * This class is <b>not thread-safe</b>. If multiple threads access a ControlledArrayList concurrently, and at least 098 * one thread modifies the list structurally, it must be synchronized externally. The unmodifiable flag does not 099 * provide thread-safety; it only controls whether standard {@link List} interface methods can modify the list. 100 * 101 * <h5 class='section'>Example - Building and Freezing:</h5> 102 * <p class='bjava'> 103 * <jc>// Build a list internally</jc> 104 * ControlledArrayList<String> <jv>config</jv> = <jk>new</jk> ControlledArrayList<>(<jk>false</jk>); 105 * <jv>config</jv>.add(<js>"setting1"</js>); 106 * <jv>config</jv>.add(<js>"setting2"</js>); 107 * 108 * <jc>// Freeze it before exposing</jc> 109 * <jv>config</jv>.setUnmodifiable(); 110 * 111 * <jc>// Now safe to expose - external code cannot modify</jc> 112 * <jk>return</jk> <jv>config</jv>; 113 * </p> 114 * 115 * <h5 class='section'>See Also:</h5><ul> 116 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsCollections">Collections Package</a> 117 * </ul> 118 * 119 * @param <E> The element type. 120 */ 121public class ControlledArrayList<E> extends ArrayList<E> { 122 123 private static final long serialVersionUID = -1L; 124 125 private boolean unmodifiable; 126 127 /** 128 * Constructor. 129 * 130 * <p> 131 * Creates an empty list with the specified modifiability setting. 132 * 133 * <h5 class='section'>Example:</h5> 134 * <p class='bjava'> 135 * <jc>// Create an empty unmodifiable list</jc> 136 * ControlledArrayList<String> <jv>list</jv> = <jk>new</jk> ControlledArrayList<>(<jk>true</jk>); 137 * 138 * <jc>// Create an empty modifiable list</jc> 139 * ControlledArrayList<String> <jv>list2</jv> = <jk>new</jk> ControlledArrayList<>(<jk>false</jk>); 140 * </p> 141 * 142 * @param unmodifiable If <jk>true</jk>, this list cannot be modified through normal list operation methods 143 * on the {@link List} interface. Use override methods to modify when unmodifiable. 144 */ 145 public ControlledArrayList(boolean unmodifiable) { 146 this.unmodifiable = unmodifiable; 147 } 148 149 /** 150 * Constructor. 151 * 152 * <p> 153 * Creates a list with the specified initial contents and modifiability setting. 154 * 155 * <h5 class='section'>Example:</h5> 156 * <p class='bjava'> 157 * <jc>// Create an unmodifiable list with initial contents</jc> 158 * List<String> <jv>initial</jv> = List.of(<js>"a"</js>, <js>"b"</js>, <js>"c"</js>); 159 * ControlledArrayList<String> <jv>list</jv> = <jk>new</jk> ControlledArrayList<>(<jk>true</jk>, <jv>initial</jv>); 160 * 161 * <jc>// Standard methods throw exceptions</jc> 162 * <jv>list</jv>.add(<js>"d"</js>); <jc>// UnsupportedOperationException</jc> 163 * </p> 164 * 165 * @param unmodifiable If <jk>true</jk>, this list cannot be modified through normal list operation methods 166 * on the {@link List} interface. Use override methods to modify when unmodifiable. 167 * @param list The initial contents of this list. Must not be <jk>null</jk>. 168 */ 169 public ControlledArrayList(boolean unmodifiable, List<? extends E> list) { 170 super(assertArgNotNull("list", list)); 171 this.unmodifiable = unmodifiable; 172 } 173 174 @Override 175 public boolean add(E element) { 176 assertModifiable(); 177 return overrideAdd(element); 178 } 179 180 @Override 181 public void add(int index, E element) { 182 assertModifiable(); 183 overrideAdd(index, element); 184 } 185 186 @Override 187 public boolean addAll(Collection<? extends E> c) { 188 assertModifiable(); 189 return overrideAddAll(c); 190 } 191 192 @Override 193 public boolean addAll(int index, Collection<? extends E> c) { 194 assertModifiable(); 195 return overrideAddAll(index, c); 196 } 197 198 @Override 199 public void clear() { 200 assertModifiable(); 201 overrideClear(); 202 } 203 204 /** 205 * Returns <jk>true</jk> if this list is modifiable through standard {@link List} interface methods. 206 * 207 * <p> 208 * Note that even when this method returns <jk>false</jk>, the list can still be modified using 209 * the override methods (e.g., {@link #overrideAdd(Object)}). 210 * 211 * <h5 class='section'>Example:</h5> 212 * <p class='bjava'> 213 * ControlledArrayList<String> <jv>list</jv> = <jk>new</jk> ControlledArrayList<>(<jk>true</jk>); 214 * <jsm>assertFalse</jsm>(<jv>list</jv>.isModifiable()); <jc>// Standard methods cannot modify</jc> 215 * 216 * ControlledArrayList<String> <jv>list2</jv> = <jk>new</jk> ControlledArrayList<>(<jk>false</jk>); 217 * <jsm>assertTrue</jsm>(<jv>list2</jv>.isModifiable()); <jc>// Standard methods can modify</jc> 218 * </p> 219 * 220 * @return <jk>true</jk> if this list is modifiable through standard {@link List} interface methods. 221 */ 222 public boolean isModifiable() { return ! unmodifiable; } 223 224 @Override 225 public Iterator<E> iterator() { 226 if (! unmodifiable) 227 return overrideIterator(); 228 229 return new Iterator<>() { 230 private final Iterator<? extends E> i = overrideIterator(); 231 232 @Override 233 public void forEachRemaining(Consumer<? super E> action) { 234 i.forEachRemaining(action); 235 } 236 237 @Override 238 public boolean hasNext() { 239 return i.hasNext(); 240 } 241 242 @Override 243 public E next() { 244 return i.next(); 245 } 246 247 @Override 248 public void remove() { 249 throw unsupportedOp(); 250 } 251 }; 252 } 253 254 @Override 255 public ListIterator<E> listIterator() { 256 return listIterator(0); 257 } 258 259 @Override 260 public ListIterator<E> listIterator(int index) { 261 if (! unmodifiable) 262 return overrideListIterator(index); 263 264 return new ListIterator<>() { 265 private final ListIterator<? extends E> i = overrideListIterator(index); 266 267 @Override 268 public void add(E e) { 269 throw unsupportedOp(); 270 } 271 272 @Override 273 public void forEachRemaining(Consumer<? super E> action) { 274 i.forEachRemaining(action); 275 } 276 277 @Override 278 public boolean hasNext() { 279 return i.hasNext(); 280 } 281 282 @Override 283 public boolean hasPrevious() { 284 return i.hasPrevious(); 285 } 286 287 @Override 288 public E next() { 289 return i.next(); 290 } 291 292 @Override 293 public int nextIndex() { 294 return i.nextIndex(); 295 } 296 297 @Override 298 public E previous() { 299 return i.previous(); 300 } 301 302 @Override 303 public int previousIndex() { 304 return i.previousIndex(); 305 } 306 307 @Override 308 public void remove() { 309 throw unsupportedOp(); 310 } 311 312 @Override 313 public void set(E e) { 314 throw unsupportedOp(); 315 } 316 }; 317 } 318 319 /** 320 * Same as {@link #add(Object)} but bypasses the modifiable flag. 321 * 322 * <p> 323 * This method allows you to add an element to the list even when it's marked as unmodifiable. 324 * It's intended for use by trusted internal code that needs to modify the list regardless of 325 * its modifiable state. 326 * 327 * <h5 class='section'>Example:</h5> 328 * <p class='bjava'> 329 * ControlledArrayList<String> <jv>list</jv> = <jk>new</jk> ControlledArrayList<>(<jk>true</jk>); 330 * <jv>list</jv>.add(<js>"x"</js>); <jc>// Throws UnsupportedOperationException</jc> 331 * <jv>list</jv>.overrideAdd(<js>"x"</js>); <jc>// Works!</jc> 332 * </p> 333 * 334 * @param element Element to be added to this list. 335 * @return <jk>true</jk> (as specified by {@link Collection#add(Object)}). 336 */ 337 public boolean overrideAdd(E element) { 338 return super.add(element); 339 } 340 341 /** 342 * Same as {@link #add(int, Object)} but bypasses the modifiable flag. 343 * 344 * @param index Index of the element to replace. 345 * @param element Element to be stored at the specified position. 346 */ 347 public void overrideAdd(int index, E element) { 348 super.add(index, element); 349 } 350 351 /** 352 * Same as {@link #addAll(Collection)} but bypasses the modifiable flag. 353 * 354 * @param c Collection containing elements to be added to this list. 355 * @return <jk>true</jk> if this list changed as a result of the call. 356 */ 357 public boolean overrideAddAll(Collection<? extends E> c) { 358 return super.addAll(c); 359 } 360 361 /** 362 * Same as {@link #addAll(int,Collection)} but bypasses the modifiable flag. 363 * 364 * @param index Index at which to insert the first element from the specified collection. 365 * @param c Collection containing elements to be added to this list. 366 * @return <jk>true</jk> if this list changed as a result of the call. 367 */ 368 public boolean overrideAddAll(int index, Collection<? extends E> c) { 369 return super.addAll(index, c); 370 } 371 372 /** 373 * Same as {@link #clear()} but bypasses the modifiable flag. 374 */ 375 public void overrideClear() { 376 super.clear(); 377 } 378 379 /** 380 * Same as {@link #iterator()} but bypasses the modifiable flag. 381 * 382 * @return An iterator over the elements in this list in proper sequence. 383 */ 384 public Iterator<E> overrideIterator() { 385 return super.iterator(); 386 } 387 388 /** 389 * Same as {@link #listIterator()} but bypasses the modifiable flag. 390 * 391 * @param index Index of the first element to be returned from the list iterator. 392 * @return A list iterator over the elements in this list (in proper sequence), starting at the specified position in the list. 393 */ 394 public ListIterator<E> overrideListIterator(int index) { 395 return super.listIterator(index); 396 } 397 398 /** 399 * Same as {@link #remove(int)} but bypasses the modifiable flag. 400 * 401 * @param index Index of the element to remove. 402 * @return The element that was removed from the list. 403 */ 404 public E overrideRemove(int index) { 405 return super.remove(index); 406 } 407 408 /** 409 * Same as {@link #remove(Object)} but bypasses the modifiable flag. 410 * 411 * @param o Element to be removed from this list, if present. 412 * @return <jk>true</jk> if this list contained the specified element. 413 */ 414 public boolean overrideRemove(Object o) { 415 return super.remove(o); 416 } 417 418 /** 419 * Same as {@link #removeAll(Collection)} but bypasses the modifiable flag. 420 * 421 * @param c Collection containing elements to be removed from this list. 422 * @return <jk>true</jk> if this list changed as a result of the call. 423 */ 424 public boolean overrideRemoveAll(Collection<?> c) { 425 return super.removeAll(c); 426 } 427 428 /** 429 * Same as {@link #removeIf(Predicate)} but bypasses the modifiable flag. 430 * 431 * @param filter A predicate which returns true for elements to be removed. 432 * @return <jk>true</jk> if any elements were removed. 433 */ 434 public boolean overrideRemoveIf(Predicate<? super E> filter) { 435 return super.removeIf(filter); 436 } 437 438 /** 439 * Same as {@link #replaceAll(UnaryOperator)} but bypasses the modifiable flag. 440 * 441 * @param operator The operator to apply to each element. 442 */ 443 public void overrideReplaceAll(UnaryOperator<E> operator) { 444 super.replaceAll(operator); 445 } 446 447 /** 448 * Same as {@link #retainAll(Collection)} but bypasses the modifiable flag. 449 * 450 * @param c Collection containing elements to be retained in this list. 451 * @return <jk>true</jk> if this list changed as a result of the call. 452 */ 453 public boolean overrideRetainAll(Collection<?> c) { 454 return super.retainAll(c); 455 } 456 457 /** 458 * Same as {@link #set(int, Object)} but bypasses the modifiable flag. 459 * 460 * @param index Index of the element to replace. 461 * @param element Element to be stored at the specified position. 462 * @return The element previously at the specified position. 463 */ 464 public E overrideSet(int index, E element) { 465 return super.set(index, element); 466 } 467 468 /** 469 * Same as {@link #overrideSort(Comparator)} but bypasses the modifiable flag. 470 * 471 * @param c The Comparator used to compare list elements. A null value indicates that the elements' natural ordering should be used. 472 */ 473 public void overrideSort(Comparator<? super E> c) { 474 super.sort(c); 475 } 476 477 @Override 478 public E remove(int index) { 479 assertModifiable(); 480 return overrideRemove(index); 481 } 482 483 @Override 484 public boolean remove(Object o) { 485 assertModifiable(); 486 return overrideRemove(o); 487 } 488 489 @Override 490 public boolean removeAll(Collection<?> coll) { 491 assertModifiable(); 492 return overrideRemoveAll(coll); 493 } 494 495 @Override 496 public boolean removeIf(Predicate<? super E> filter) { 497 assertModifiable(); 498 return overrideRemoveIf(filter); 499 } 500 501 @Override 502 public void replaceAll(UnaryOperator<E> operator) { 503 assertModifiable(); 504 overrideReplaceAll(operator); 505 } 506 507 @Override 508 public boolean retainAll(Collection<?> c) { 509 assertModifiable(); 510 return overrideRetainAll(c); 511 } 512 513 @Override 514 public E set(int index, E element) { 515 assertModifiable(); 516 return overrideSet(index, element); 517 } 518 519 /** 520 * Makes this list unmodifiable through standard {@link List} interface methods. 521 * 522 * <p> 523 * After calling this method, all standard modification methods (e.g., {@link #add(Object)}, 524 * {@link #remove(int)}) will throw {@link UnsupportedOperationException}. However, the override 525 * methods (e.g., {@link #overrideAdd(Object)}) can still be used to modify the list. 526 * 527 * <h5 class='section'>Example:</h5> 528 * <p class='bjava'> 529 * ControlledArrayList<String> <jv>list</jv> = <jk>new</jk> ControlledArrayList<>(<jk>false</jk>); 530 * <jv>list</jv>.add(<js>"a"</js>); <jc>// Works</jc> 531 * 532 * <jv>list</jv>.setUnmodifiable(); 533 * <jv>list</jv>.add(<js>"b"</js>); <jc>// Throws UnsupportedOperationException</jc> 534 * <jv>list</jv>.overrideAdd(<js>"b"</js>); <jc>// Still works</jc> 535 * </p> 536 * 537 * @return This object for method chaining. 538 */ 539 public ControlledArrayList<E> setUnmodifiable() { 540 unmodifiable = true; 541 return this; 542 } 543 544 @Override 545 public void sort(Comparator<? super E> c) { 546 assertModifiable(); 547 overrideSort(c); 548 } 549 550 @Override 551 public List<E> subList(int fromIndex, int toIndex) { 552 return new ControlledArrayList<>(unmodifiable, super.subList(fromIndex, toIndex)); 553 } 554 555 /** 556 * Throws an {@link UnsupportedOperationException} if the unmodifiable flag is set on this list. 557 * 558 * <p> 559 * This method is called by all standard {@link List} interface modification methods to enforce 560 * the unmodifiable restriction. Override methods bypass this check. 561 * 562 * @throws UnsupportedOperationException if the list is unmodifiable. 563 */ 564 protected final void assertModifiable() { 565 if (unmodifiable) 566 throw unsupportedOp("List is read-only"); 567 } 568}