| /* |
| * 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.commons.collections4.list; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| |
| /** |
| * Decorates another {@code List} to make it seamlessly grow when |
| * indices larger than the list size are used on add and set, |
| * avoiding most IndexOutOfBoundsExceptions. |
| * <p> |
| * This class avoids errors by growing when a set or add method would |
| * normally throw an IndexOutOfBoundsException. |
| * Note that IndexOutOfBoundsException IS returned for invalid negative indices. |
| * </p> |
| * <p> |
| * Trying to set or add to an index larger than the size will cause the list |
| * to grow (using {@code null} elements). Clearly, care must be taken |
| * not to use excessively large indices, as the internal list will grow to |
| * match. |
| * </p> |
| * <p> |
| * Trying to use any method other than add or set with an invalid index will |
| * call the underlying list and probably result in an IndexOutOfBoundsException. |
| * </p> |
| * <p> |
| * Take care when using this list with {@code null} values, as |
| * {@code null} is the value added when growing the list. |
| * </p> |
| * <p> |
| * All sub-lists will access the underlying list directly, and will throw |
| * IndexOutOfBoundsExceptions. |
| * </p> |
| * <p> |
| * This class differs from {@link LazyList} because here growth occurs on |
| * set and add, where {@code LazyList} grows on get. However, they |
| * can be used together by decorating twice. |
| * </p> |
| * |
| * @see LazyList |
| * @since 3.2 |
| */ |
| public class GrowthList<E> extends AbstractSerializableListDecorator<E> { |
| |
| /** Serialization version */ |
| private static final long serialVersionUID = -3620001881672L; |
| |
| /** |
| * Factory method to create a growth list. |
| * |
| * @param <E> the type of the elements in the list |
| * @param list the list to decorate, must not be null |
| * @return a new growth list |
| * @throws NullPointerException if list is null |
| * @since 4.0 |
| */ |
| public static <E> GrowthList<E> growthList(final List<E> list) { |
| return new GrowthList<>(list); |
| } |
| |
| //----------------------------------------------------------------------- |
| /** |
| * Constructor that uses an ArrayList internally. |
| */ |
| public GrowthList() { |
| super(new ArrayList<E>()); |
| } |
| |
| /** |
| * Constructor that uses an ArrayList internally. |
| * |
| * @param initialCapacity the initial capacity of the ArrayList |
| * @throws IllegalArgumentException if initial capacity is invalid |
| */ |
| public GrowthList(final int initialCapacity) { |
| super(new ArrayList<E>(initialCapacity)); |
| } |
| |
| /** |
| * Constructor that wraps (not copies). |
| * |
| * @param list the list to decorate, must not be null |
| * @throws NullPointerException if list is null |
| */ |
| protected GrowthList(final List<E> list) { |
| super(list); |
| } |
| |
| //----------------------------------------------------------------------- |
| /** |
| * Decorate the add method to perform the growth behavior. |
| * <p> |
| * If the requested index is greater than the current size, the list will |
| * grow to the new size. Indices between the old size and the requested |
| * size will be filled with {@code null}. |
| * <p> |
| * If the index is less than the current size, the value will be added to |
| * the underlying list directly. |
| * If the index is less than zero, the underlying list is called, which |
| * will probably throw an IndexOutOfBoundsException. |
| * |
| * @param index the index to add at |
| * @param element the object to add at the specified index |
| * @throws UnsupportedOperationException if the underlying list doesn't implement set |
| * @throws ClassCastException if the underlying list rejects the element |
| * @throws IllegalArgumentException if the underlying list rejects the element |
| */ |
| @Override |
| public void add(final int index, final E element) { |
| final int size = decorated().size(); |
| if (index > size) { |
| decorated().addAll(Collections.<E>nCopies(index - size, null)); |
| } |
| decorated().add(index, element); |
| } |
| |
| //----------------------------------------------------------------------- |
| /** |
| * Decorate the addAll method to perform the growth behavior. |
| * <p> |
| * If the requested index is greater than the current size, the list will |
| * grow to the new size. Indices between the old size and the requested |
| * size will be filled with {@code null}. |
| * <p> |
| * If the index is less than the current size, the values will be added to |
| * the underlying list directly. |
| * If the index is less than zero, the underlying list is called, which |
| * will probably throw an IndexOutOfBoundsException. |
| * |
| * @param index the index to add at |
| * @param coll the collection to add at the specified index |
| * @return true if the list changed |
| * @throws UnsupportedOperationException if the underlying list doesn't implement set |
| * @throws ClassCastException if the underlying list rejects the element |
| * @throws IllegalArgumentException if the underlying list rejects the element |
| */ |
| @Override |
| public boolean addAll(final int index, final Collection<? extends E> coll) { |
| final int size = decorated().size(); |
| boolean result = false; |
| if (index > size) { |
| decorated().addAll(Collections.<E>nCopies(index - size, null)); |
| result = true; |
| } |
| return decorated().addAll(index, coll) || result; |
| } |
| |
| //----------------------------------------------------------------------- |
| /** |
| * Decorate the set method to perform the growth behavior. |
| * <p> |
| * If the requested index is greater than the current size, the list will |
| * grow to the new size. Indices between the old size and the requested |
| * size will be filled with {@code null}. |
| * <p> |
| * If the index is less than the current size, the value will be set onto |
| * the underlying list directly. |
| * If the index is less than zero, the underlying list is called, which |
| * will probably throw an IndexOutOfBoundsException. |
| * |
| * @param index the index to set |
| * @param element the object to set at the specified index |
| * @return the object previously at that index |
| * @throws UnsupportedOperationException if the underlying list doesn't implement set |
| * @throws ClassCastException if the underlying list rejects the element |
| * @throws IllegalArgumentException if the underlying list rejects the element |
| */ |
| @Override |
| public E set(final int index, final E element) { |
| final int size = decorated().size(); |
| if (index >= size) { |
| decorated().addAll(Collections.<E>nCopies(index - size + 1, null)); |
| } |
| return decorated().set(index, element); |
| } |
| |
| } |