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.commons.collections4.list;
018
019import java.util.List;
020import java.util.Objects;
021
022import org.apache.commons.collections4.Factory;
023import org.apache.commons.collections4.Transformer;
024
025/**
026 * Decorates another {@code List} to create objects in the list on demand.
027 * <p>
028 * When the {@link #get(int)} method is called with an index greater than
029 * the size of the list, the list will automatically grow in size and return
030 * a new object from the specified factory or transformer. The gaps will be
031 * filled by null. If a get method call encounters a null, it will be replaced
032 * with a new object from the factory. Thus this list is unsuitable for
033 * storing null objects.
034 * </p>
035 * <p>
036 * For instance:
037 * </p>
038 *
039 * <pre>
040 * Factory&lt;Date&gt; factory = new Factory&lt;Date&gt;() {
041 *     public Date create() {
042 *         return new Date();
043 *     }
044 * }
045 * List&lt;Date&gt; lazy = LazyList.decorate(new ArrayList&lt;Date&gt;(), factory);
046 * Date date = lazy.get(3);
047 * </pre>
048 *
049 * <p>
050 * After the above code is executed, {@code date} will contain
051 * a new {@code Date} instance.  Furthermore, that {@code Date}
052 * instance is the fourth element in the list.  The first, second,
053 * and third element are all set to {@code null}.
054 * </p>
055 * <p>
056 * This class differs from {@link GrowthList} because here growth occurs on
057 * get, where {@code GrowthList} grows on set and add. However, they
058 * could easily be used together by decorating twice.
059 * </p>
060 * <p>
061 * This class is Serializable from Commons Collections 3.1.
062 * </p>
063 *
064 * @param <E> the type of the elements in the list.
065 * @see GrowthList
066 * @since 3.0
067 */
068public class LazyList<E> extends AbstractSerializableListDecorator<E> {
069
070    /** Serialization version */
071    private static final long serialVersionUID = -3677737457567429713L;
072
073    /**
074     * Factory method to create a lazily instantiating list.
075     *
076     * @param <E> the type of the elements in the list
077     * @param list  the list to decorate, must not be null
078     * @param factory  the factory to use for creation, must not be null
079     * @return a new lazy list
080     * @throws NullPointerException if list or factory is null
081     * @since 4.0
082     */
083    public static <E> LazyList<E> lazyList(final List<E> list, final Factory<? extends E> factory) {
084        return new LazyList<>(list, factory);
085    }
086
087    /**
088     * Transformer method to create a lazily instantiating list.
089     *
090     * @param <E> the type of the elements in the list
091     * @param list  the list to decorate, must not be null
092     * @param transformer  the transformer to use for creation, must not be null
093     * @return a new lazy list
094     * @throws NullPointerException if list or transformer is null
095     * @since 4.4
096     */
097    public static <E> LazyList<E> lazyList(final List<E> list, final Transformer<Integer, ? extends E> transformer) {
098        return new LazyList<>(list, transformer);
099    }
100
101    /** The factory to use to lazily instantiate the objects */
102    private final Factory<? extends E> factory;
103
104    /** The transformer to use to lazily instantiate the objects */
105    private final Transformer<Integer, ? extends E> transformer;
106
107    /**
108     * Constructor that wraps (not copies).
109     *
110     * @param list  the list to decorate, must not be null
111     * @param factory  the factory to use for creation, must not be null
112     * @throws NullPointerException if list or factory is null
113     */
114    protected LazyList(final List<E> list, final Factory<? extends E> factory) {
115        super(list);
116        this.factory = Objects.requireNonNull(factory);
117        this.transformer = null;
118    }
119
120    /**
121     * Constructor that wraps (not copies).
122     *
123     * @param list  the list to decorate, must not be null
124     * @param transformer  the transformer to use for creation, must not be null
125     * @throws NullPointerException if list or transformer is null
126     */
127    protected LazyList(final List<E> list, final Transformer<Integer, ? extends E> transformer) {
128        super(list);
129        this.factory = null;
130        this.transformer = Objects.requireNonNull(transformer);
131    }
132
133    private E element(final int index) {
134        if (factory != null) {
135            return factory.get();
136        }
137        if (transformer != null) {
138            return transformer.apply(index);
139        }
140        throw new IllegalStateException("Factory and Transformer are both null!");
141    }
142
143    /**
144     * Decorate the get method to perform the lazy behavior.
145     * <p>
146     * If the requested index is greater than the current size, the list will
147     * grow to the new size and a new object will be returned from the factory
148     * or transformer. Indexes in-between the old size and the requested size
149     * are left with a placeholder that is replaced with a factory or
150     * transformer object when requested.
151     *
152     * @param index  the index to retrieve
153     * @return the element at the given index
154     */
155    @Override
156    public E get(final int index) {
157        final int size = decorated().size();
158        if (index < size) {
159            // within bounds, get the object
160            E object = decorated().get(index);
161            if (object == null) {
162                // item is a placeholder, create new one, set and return
163                object = element(index);
164                decorated().set(index, object);
165            }
166            // good and ready to go
167            return object;
168        }
169        // we have to grow the list
170        for (int i = size; i < index; i++) {
171            decorated().add(null);
172        }
173        // create our last object, set and return
174        final E object = element(index);
175        decorated().add(object);
176        return object;
177    }
178
179    @Override
180    public List<E> subList(final int fromIndex, final int toIndex) {
181        final List<E> sub = decorated().subList(fromIndex, toIndex);
182        if (factory != null) {
183            return new LazyList<>(sub, factory);
184        }
185        if (transformer != null) {
186            return new LazyList<>(sub, transformer);
187        }
188        throw new IllegalStateException("Factory and Transformer are both null!");
189    }
190
191}