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&lt;String&gt; <jv>list</jv> = <jk>new</jk> ControlledArrayList&lt;&gt;(<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&lt;String&gt; <jv>list</jv> = <jk>new</jk> ControlledArrayList&lt;&gt;(<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&lt;String&gt; <jv>config</jv> = <jk>new</jk> ControlledArrayList&lt;&gt;(<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&lt;String&gt; <jv>list</jv> = <jk>new</jk> ControlledArrayList&lt;&gt;(<jk>true</jk>);
137    *
138    *    <jc>// Create an empty modifiable list</jc>
139    *    ControlledArrayList&lt;String&gt; <jv>list2</jv> = <jk>new</jk> ControlledArrayList&lt;&gt;(<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&lt;String&gt; <jv>initial</jv> = List.of(<js>"a"</js>, <js>"b"</js>, <js>"c"</js>);
159    *    ControlledArrayList&lt;String&gt; <jv>list</jv> = <jk>new</jk> ControlledArrayList&lt;&gt;(<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&lt;String&gt; <jv>list</jv> = <jk>new</jk> ControlledArrayList&lt;&gt;(<jk>true</jk>);
214    *    <jsm>assertFalse</jsm>(<jv>list</jv>.isModifiable());  <jc>// Standard methods cannot modify</jc>
215    *
216    *    ControlledArrayList&lt;String&gt; <jv>list2</jv> = <jk>new</jk> ControlledArrayList&lt;&gt;(<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&lt;String&gt; <jv>list</jv> = <jk>new</jk> ControlledArrayList&lt;&gt;(<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&lt;String&gt; <jv>list</jv> = <jk>new</jk> ControlledArrayList&lt;&gt;(<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}