    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}