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 * @see GrowthList
065 * @since 3.0
066 */
067public class LazyList<E> extends AbstractSerializableListDecorator<E> {
068
069    /** Serialization version */
070    private static final long serialVersionUID = -3677737457567429713L;
071
072    /**
073     * Factory method to create a lazily instantiating list.
074     *
075     * @param <E> the type of the elements in the list
076     * @param list  the list to decorate, must not be null
077     * @param factory  the factory to use for creation, must not be null
078     * @return a new lazy list
079     * @throws NullPointerException if list or factory is null
080     * @since 4.0
081     */
082    public static <E> LazyList<E> lazyList(final List<E> list, final Factory<? extends E> factory) {
083        return new LazyList<>(list, factory);
084    }
085
086    /**
087     * Transformer method to create a lazily instantiating list.
088     *
089     * @param <E> the type of the elements in the list
090     * @param list  the list to decorate, must not be null
091     * @param transformer  the transformer to use for creation, must not be null
092     * @return a new lazy list
093     * @throws NullPointerException if list or transformer is null
094     * @since 4.4
095     */
096    public static <E> LazyList<E> lazyList(final List<E> list, final Transformer<Integer, ? extends E> transformer) {
097        return new LazyList<>(list, transformer);
098    }
099
100    /** The factory to use to lazily instantiate the objects */
101    private final Factory<? extends E> factory;
102
103    /** The transformer to use to lazily instantiate the objects */
104    private final Transformer<Integer, ? extends E> transformer;
105
106    /**
107     * Constructor that wraps (not copies).
108     *
109     * @param list  the list to decorate, must not be null
110     * @param factory  the factory to use for creation, must not be null
111     * @throws NullPointerException if list or factory is null
112     */
113    protected LazyList(final List<E> list, final Factory<? extends E> factory) {
114        super(list);
115        this.factory = Objects.requireNonNull(factory);
116        this.transformer = null;
117    }
118
119    /**
120     * Constructor that wraps (not copies).
121     *
122     * @param list  the list to decorate, must not be null
123     * @param transformer  the transformer to use for creation, must not be null
124     * @throws NullPointerException if list or transformer is null
125     */
126    protected LazyList(final List<E> list, final Transformer<Integer, ? extends E> transformer) {
127        super(list);
128        this.factory = null;
129        this.transformer = Objects.requireNonNull(transformer);
130    }
131
132    private E element(final int index) {
133        if (factory != null) {
134            return factory.create();
135        }
136        if (transformer != null) {
137            return transformer.transform(index);
138        }
139        throw new IllegalStateException("Factory and Transformer are both null!");
140    }
141
142    /**
143     * Decorate the get method to perform the lazy behavior.
144     * <p>
145     * If the requested index is greater than the current size, the list will
146     * grow to the new size and a new object will be returned from the factory
147     * or transformer. Indexes in-between the old size and the requested size
148     * are left with a placeholder that is replaced with a factory or
149     * transformer object when requested.
150     *
151     * @param index  the index to retrieve
152     * @return the element at the given index
153     */
154    @Override
155    public E get(final int index) {
156        final int size = decorated().size();
157        if (index < size) {
158            // within bounds, get the object
159            E object = decorated().get(index);
160            if (object == null) {
161                // item is a placeholder, create new one, set and return
162                object = element(index);
163                decorated().set(index, object);
164            }
165            // good and ready to go
166            return object;
167        }
168        // we have to grow the list
169        for (int i = size; i < index; i++) {
170            decorated().add(null);
171        }
172        // create our last object, set and return
173        final E object = element(index);
174        decorated().add(object);
175        return object;
176    }
177
178    @Override
179    public List<E> subList(final int fromIndex, final int toIndex) {
180        final List<E> sub = decorated().subList(fromIndex, toIndex);
181        if (factory != null) {
182            return new LazyList<>(sub, factory);
183        }
184        if (transformer != null) {
185            return new LazyList<>(sub, transformer);
186        }
187        throw new IllegalStateException("Factory and Transformer are both null!");
188    }
189
190}