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.collections;
018
019import java.util.*;
020import java.util.function.*;
021
022import org.apache.juneau.internal.*;
023
024/**
025 * An array list that allows you to control whether it's read-only via a constructor parameter.
026 *
027 * <p>
028 * Override methods such as {@link #overrideAdd(int, Object)} are provided that bypass the unmodifiable restriction
029 * on the list.  They allow you to manipulate the list while not exposing the ability to manipulate the list through
030 * any of the methods provided by the {@link List} interface (meaning you can pass the object around as an unmodifiable List).
031 *
032 * @param <E> The element type.
033 */
034public class ControlledArrayList<E> extends ArrayList<E> {
035
036   private static final long serialVersionUID = -1L;
037
038   private boolean unmodifiable;
039
040   /**
041    * Constructor.
042    *
043    * @param unmodifiable If <jk>true</jk>, this list cannot be modified through normal list operation methods on the {@link List} interface.
044    */
045   public ControlledArrayList(boolean unmodifiable) {
046      this.unmodifiable = unmodifiable;
047   }
048
049   /**
050    * Constructor.
051    *
052    * @param unmodifiable If <jk>true</jk>, this list cannot be modified through normal list operation methods on the {@link List} interface.
053    * @param list The initial contents of this list.
054    */
055   public ControlledArrayList(boolean unmodifiable, List<? extends E> list) {
056      super(list);
057      this.unmodifiable = unmodifiable;
058   }
059
060   //-----------------------------------------------------------------------------------------------------------------
061   // Properties
062   //-----------------------------------------------------------------------------------------------------------------
063
064
065   /**
066    * Specifies whether this bean should be unmodifiable.
067    * <p>
068    * When enabled, attempting to set any properties on this bean will cause an {@link UnsupportedOperationException}.
069    *
070    * @return This object.
071    */
072   public ControlledArrayList<E> setUnmodifiable() {
073      unmodifiable = true;
074      return this;
075   }
076
077   /**
078    * Throws an {@link UnsupportedOperationException} if the unmodifiable flag is set on this bean.
079    */
080   protected final void assertModifiable() {
081      if (unmodifiable)
082         throw new UnsupportedOperationException("List is read-only");
083   }
084
085   /**
086    * Returns <jk>true</jk> if this list is modifiable.
087    *
088    * @return <jk>true</jk> if this list is modifiable.
089    */
090   public boolean isModifiable() {
091      return ! unmodifiable;
092   }
093
094   @Override
095   public E set(int index, E element) {
096      assertModifiable();
097      return overrideSet(index, element);
098   }
099
100   /**
101    * Same as {@link #set(int, Object)} but bypasses the modifiable flag.
102    *
103    * @param index Index of the element to replace.
104    * @param element Element to be stored at the specified position.
105    * @return The element previously at the specified position.
106    */
107   public E overrideSet(int index, E element) {
108      return super.set(index, element);
109   }
110
111   @Override
112   public void add(int index, E element) {
113      assertModifiable();
114      overrideAdd(index, element);
115   }
116
117   /**
118    * Same as {@link #add(int, Object)} but bypasses the modifiable flag.
119    *
120    * @param index Index of the element to replace.
121    * @param element Element to be stored at the specified position.
122    */
123   public void overrideAdd(int index, E element) {
124      super.add(index, element);
125   }
126
127   @Override
128   public E remove(int index) {
129      assertModifiable();
130      return overrideRemove(index);
131   }
132
133   /**
134    * Same as {@link #remove(int)} but bypasses the modifiable flag.
135    *
136    * @param index Index of the element to remove.
137    * @return The element that was removed from the list.
138    */
139   public E overrideRemove(int index) {
140      return super.remove(index);
141   }
142
143   @Override
144   public boolean addAll(int index, Collection<? extends E> c) {
145      assertModifiable();
146      return overrideAddAll(index, c);
147   }
148
149   /**
150    * Same as {@link #addAll(int,Collection)} but bypasses the modifiable flag.
151    *
152    * @param index Index at which to insert the first element from the specified collection.
153    * @param c Collection containing elements to be added to this list.
154    * @return <jk>true</jk> if this list changed as a result of the call.
155    */
156   public boolean overrideAddAll(int index, Collection<? extends E> c) {
157      return super.addAll(index, c);
158   }
159
160   @Override
161   public void replaceAll(UnaryOperator<E> operator) {
162      assertModifiable();
163      overrideReplaceAll(operator);
164   }
165
166   /**
167    * Same as {@link #replaceAll(UnaryOperator)} but bypasses the modifiable flag.
168    *
169    * @param operator The operator to apply to each element.
170    */
171   public void overrideReplaceAll(UnaryOperator<E> operator) {
172      super.replaceAll(operator);
173   }
174
175   @Override
176   public void sort(Comparator<? super E> c) {
177      assertModifiable();
178      overrideSort(c);
179   }
180
181   /**
182    * Same as {@link #overrideSort(Comparator)} but bypasses the modifiable flag.
183    *
184    * @param c The Comparator used to compare list elements. A null value indicates that the elements' natural ordering should be used.
185    */
186   public void overrideSort(Comparator<? super E> c) {
187      super.sort(c);
188   }
189
190   @Override
191   public boolean add(E element) {
192      assertModifiable();
193      return overrideAdd(element);
194   }
195
196   /**
197    * Same as {@link #add(Object)} but bypasses the modifiable flag.
198    *
199    * @param element Element to be stored at the specified position.
200    * @return <jk>true</jk>.
201    */
202   public boolean overrideAdd(E element) {
203      return super.add(element);
204   }
205
206   @Override
207   public boolean remove(Object o) {
208      assertModifiable();
209      return overrideRemove(o);
210   }
211
212   /**
213    * Same as {@link #remove(Object)} but bypasses the modifiable flag.
214    *
215    * @param o Element to be removed from this list, if present.
216    * @return <jk>true</jk> if this list contained the specified element.
217    */
218   public boolean overrideRemove(Object o) {
219      return super.remove(o);
220   }
221
222   @Override
223   public boolean addAll(Collection<? extends E> c) {
224      assertModifiable();
225      return overrideAddAll(c);
226   }
227
228   /**
229    * Same as {@link #addAll(Collection)} but bypasses the modifiable flag.
230    *
231    * @param c Collection containing elements to be added to this list.
232    * @return <jk>true</jk> if this list changed as a result of the call.
233    */
234   public boolean overrideAddAll(Collection<? extends E> c) {
235      return super.addAll(c);
236   }
237
238   @Override
239   public boolean removeAll(Collection<?> coll) {
240      assertModifiable();
241      return overrideRemoveAll(coll);
242   }
243
244   /**
245    * Same as {@link #removeAll(Collection)} but bypasses the modifiable flag.
246    *
247    * @param c Collection containing elements to be removed from this list.
248    * @return <jk>true</jk> if this list changed as a result of the call.
249    */
250   public boolean overrideRemoveAll(Collection<?> c) {
251      return super.removeAll(c);
252   }
253
254   @Override
255   public boolean retainAll(Collection<?> c) {
256      assertModifiable();
257      return overrideRetainAll(c);
258   }
259
260   /**
261    * Same as {@link #retainAll(Collection)} but bypasses the modifiable flag.
262    *
263    * @param c Collection containing elements to be retained in this list.
264    * @return <jk>true</jk> if this list changed as a result of the call.
265    */
266   public boolean overrideRetainAll(Collection<?> c) {
267      return super.retainAll(c);
268   }
269
270   @Override
271   public void clear() {
272      assertModifiable();
273      overrideClear();
274   }
275
276   /**
277    * Same as {@link #clear()} but bypasses the modifiable flag.
278    */
279   public void overrideClear() {
280      super.clear();
281   }
282
283   @Override
284   public boolean removeIf(Predicate<? super E> filter) {
285      assertModifiable();
286      return overrideRemoveIf(filter);
287   }
288
289   /**
290    * Same as {@link #removeIf(Predicate)} but bypasses the modifiable flag.
291    *
292    * @param filter A predicate which returns true for elements to be removed.
293    * @return <jk>true</jk> if any elements were removed.
294    */
295   public boolean overrideRemoveIf(Predicate<? super E> filter) {
296      return super.removeIf(filter);
297   }
298
299   @Override
300   public List<E> subList(int fromIndex, int toIndex) {
301      return new ControlledArrayList<>(unmodifiable, super.subList(fromIndex, toIndex));
302   }
303
304   @Override
305   public ListIterator<E> listIterator() {
306      return listIterator(0);
307   }
308
309   @Override
310   public ListIterator<E> listIterator(final int index) {
311      if (! unmodifiable)
312         return overrideListIterator(index);
313
314      return new ListIterator<>() {
315         private final ListIterator<? extends E> i = overrideListIterator(index);
316
317         @Override
318         public boolean hasNext() {
319            return i.hasNext();
320         }
321
322         @Override
323         public E next() {
324            return i.next();
325         }
326
327         @Override
328         public boolean hasPrevious() {
329            return i.hasPrevious();
330         }
331
332         @Override
333         public E previous() {
334            return i.previous();
335         }
336
337         @Override
338         public int nextIndex() {
339            return i.nextIndex();
340         }
341
342         @Override
343         public int previousIndex() {
344            return i.previousIndex();
345         }
346
347         @Override
348         public void remove() {
349            throw new UnsupportedOperationException();
350         }
351
352         @Override
353         public void set(E e) {
354            throw new UnsupportedOperationException();
355         }
356
357         @Override
358         public void add(E e) {
359            throw new UnsupportedOperationException();
360         }
361
362         @Override
363         public void forEachRemaining(Consumer<? super E> action) {
364            i.forEachRemaining(action);
365         }
366      };
367   }
368
369   /**
370    * Same as {@link #listIterator()} but bypasses the modifiable flag.
371    *
372    * @param index Index of the first element to be returned from the list iterator.
373    * @return A list iterator over the elements in this list (in proper sequence), starting at the specified position in the list.
374    */
375   public ListIterator<E> overrideListIterator(final int index) {
376      return super.listIterator(index);
377   }
378
379   @Override
380   public Iterator<E> iterator() {
381      if (! unmodifiable)
382         return overrideIterator();
383
384      return new Iterator<>() {
385         private final Iterator<? extends E> i = overrideIterator();
386
387         @Override
388         public boolean hasNext() {
389            return i.hasNext();
390         }
391
392         @Override
393         public E next() {
394            return i.next();
395         }
396
397         @Override
398         public void remove() {
399            throw new UnsupportedOperationException();
400         }
401
402         @Override
403         public void forEachRemaining(Consumer<? super E> action) {
404            i.forEachRemaining(action);
405         }
406      };
407   }
408
409   /**
410    * Same as {@link #iterator()} but bypasses the modifiable flag.
411    *
412    * @return An iterator over the elements in this list in proper sequence.
413    */
414   public Iterator<E> overrideIterator() {
415      return super.iterator();
416   }
417}