001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2015-2016 ForgeRock AS.
015 */
016
017package org.forgerock.util;
018
019// Java SE
020import java.util.Collection;
021import java.util.Iterator;
022import java.util.List;
023import java.util.ListIterator;
024
025/**
026 * A list with lazy initialization. The factory is called to initialize the list
027 * on the first call to one of this object's methods.
028 *
029 * @param <E>
030 *            The type of element contained in this list.
031 */
032public class LazyList<E> implements List<E> {
033
034    /** The list that this lazy list exposes, once initialized. */
035    private List<E> list;
036
037    /** Factory to create the instance of the list to expose. */
038    protected Factory<List<E>> factory;
039
040    /**
041     * Constructs a new lazy list. Allows factory to be set in subclass
042     * constructor.
043     */
044    protected LazyList() {
045    }
046
047    /**
048     * Constructs a new lazy list.
049     *
050     * @param factory
051     *            factory to create the list instance to expose.
052     */
053    public LazyList(Factory<List<E>> factory) {
054        this.factory = factory;
055    }
056
057    /**
058     * Performs lazy initialization of the list if not already performed, and
059     * returns the initialized list.
060     */
061    private List<E> lazy() {
062        if (list == null) {
063            synchronized (this) {
064                if (list == null) {
065                    list = factory.newInstance();
066                }
067            }
068        }
069        return list;
070    }
071
072    /**
073     * Returns the number of elements in this list.
074     */
075    @Override
076    public int size() {
077        return lazy().size();
078    }
079
080    /**
081     * Returns {@code true} if this list contains no elements.
082     */
083    @Override
084    public boolean isEmpty() {
085        return lazy().isEmpty();
086    }
087
088    /**
089     * Returns {@code true} if this list contains the specified element.
090     *
091     * @param o
092     *            the element whose presence in this list is to be tested.
093     * @return {@code true} if this list contains the specified element.
094     */
095    @Override
096    public boolean contains(Object o) {
097        return lazy().contains(o);
098    }
099
100    /**
101     * Returns an iterator over the elements in this list in proper sequence.
102     */
103    @Override
104    public Iterator<E> iterator() {
105        return lazy().iterator();
106    }
107
108    /**
109     * Returns an array containing all of the elements in this list in proper
110     * sequence (from first to last element).
111     */
112    @Override
113    public Object[] toArray() {
114        return lazy().toArray();
115    }
116
117    /**
118     * Returns an array containing all of the elements in this list in proper
119     * sequence (from first to last element); the runtime type of the returned
120     * array is that of the specified array. If the list fits in the specified
121     * array, it is returned therein. Otherwise, a new array is allocated with
122     * the runtime type of the specified array and the size of this list.
123     *
124     * @param a
125     *            the array into which the elements of this list are to be
126     *            stored.
127     * @return an array containing the elements of this list.
128     */
129    @Override
130    public <T> T[] toArray(T[] a) {
131        return lazy().toArray(a);
132    }
133
134    /**
135     * Appends the specified element to the end of this list.
136     *
137     * @param e
138     *            the element to be appended to this list.
139     * @return {@code true} if this list changed as a result of the call.
140     */
141    @Override
142    public boolean add(E e) {
143        return lazy().add(e);
144    }
145
146    /**
147     * Removes the first occurrence of the specified element from this list, if
148     * it is present.
149     *
150     * @param o
151     *            the element to be removed from this list, if present.
152     * @return true if this list contained the specified element.
153     */
154    @Override
155    public boolean remove(Object o) {
156        return lazy().remove(o);
157    }
158
159    /**
160     * Returns {@code true} if this list contains all of the elements of the
161     * specified collection.
162     *
163     * @param c
164     *            the collection to be checked for containment in this list.
165     * @return {@code true} if this list contains all of the elements of the
166     *         specified collection.
167     */
168    @Override
169    public boolean containsAll(Collection<?> c) {
170        return lazy().containsAll(c);
171    }
172
173    /**
174     * Appends all of the elements in the specified collection to the end of
175     * this list, in the order that they are returned by the specified
176     * collection's iterator.
177     *
178     * @param c
179     *            the collection containing elements to be added to this list.
180     * @return {@code true} if this list changed as a result of the call.
181     */
182    @Override
183    public boolean addAll(Collection<? extends E> c) {
184        return lazy().addAll(c);
185    }
186
187    /**
188     * Inserts all of the elements in the specified collection into this list at
189     * the specified position.
190     *
191     * @param index
192     *            the index at which to insert the first element from the
193     *            specified collection.
194     * @param c
195     *            the collection containing elements to be added to this list.
196     * @return {@code true} if this list changed as a result of the call.
197     */
198    @Override
199    public boolean addAll(int index, Collection<? extends E> c) {
200        return lazy().addAll(index, c);
201    }
202
203    /**
204     * Removes from this list all of its elements that are contained in the
205     * specified collection.
206     *
207     * @param c
208     *            the collection containing elements to be removed from this
209     *            list.
210     * @return {@code true} if this list changed as a result of the call.
211     */
212    @Override
213    public boolean removeAll(Collection<?> c) {
214        return lazy().removeAll(c);
215    }
216
217    /**
218     * Retains only the elements in this list that are contained in the
219     * specified collection.
220     *
221     * @param c
222     *            the collection containing elements to be retained in this
223     *            list.
224     * @return {@code true} if this list changed as a result of the call.
225     */
226    @Override
227    public boolean retainAll(Collection<?> c) {
228        return lazy().retainAll(c);
229    }
230
231    /**
232     * Removes all of the elements from this list.
233     */
234    @Override
235    public void clear() {
236        lazy().clear();
237    }
238
239    /**
240     * Compares the specified object with this list for equality.
241     *
242     * @param o
243     *            the object to be compared for equality with this list.
244     * @return {@code true} if the specified object is equal to this list.
245     */
246    @Override
247    public boolean equals(Object o) {
248        return lazy().equals(o);
249    }
250
251    /**
252     * Returns the hash code value for this list.
253     */
254    @Override
255    public int hashCode() {
256        return lazy().hashCode();
257    }
258
259    /**
260     * Returns the element at the specified position in this list.
261     *
262     * @param index
263     *            the index of the element to return.
264     * @return the element at the specified position in this list.
265     */
266    @Override
267    public E get(int index) {
268        return lazy().get(index);
269    }
270
271    /**
272     * Replaces the element at the specified position in this list with the
273     * specified element.
274     *
275     * @param index
276     *            the index of the element to replace.
277     * @param element
278     *            the element to be stored at the specified position.
279     * @return the element previously at the specified position.
280     */
281    @Override
282    public E set(int index, E element) {
283        return lazy().set(index, element);
284    }
285
286    /**
287     * Inserts the specified element at the specified position in this list.
288     *
289     * @param index
290     *            the index at which the specified element is to be inserted.
291     * @param element
292     *            the element to be inserted.
293     */
294    @Override
295    public void add(int index, E element) {
296        lazy().add(index, element);
297    }
298
299    /**
300     * Removes the element at the specified position in this list.
301     *
302     * @param index
303     *            the index of the element to be removed.
304     * @return the element previously at the specified position.
305     */
306    @Override
307    public E remove(int index) {
308        return lazy().remove(index);
309    }
310
311    /**
312     * Returns the index of the first occurrence of the specified element in
313     * this list, or {@code -1} if this list does not contain the element.
314     *
315     * @param o
316     *            element to search for.
317     * @return the index of the first occurrence, or {@code -1} if no such
318     *         element.
319     */
320    @Override
321    public int indexOf(Object o) {
322        return lazy().indexOf(o);
323    }
324
325    /**
326     * Returns the index of the last occurrence of the specified element in this
327     * list, or {@code -1} if this list does not contain the element.
328     *
329     * @param o
330     *            the element to search for.
331     * @return the index of the last occurrence, or {@code -1} if no such
332     *         element.
333     */
334    @Override
335    public int lastIndexOf(Object o) {
336        return lazy().lastIndexOf(o);
337    }
338
339    /**
340     * Returns a list iterator over the elements in this list (in proper
341     * sequence).
342     */
343    @Override
344    public ListIterator<E> listIterator() {
345        return lazy().listIterator();
346    }
347
348    /**
349     * Returns a list iterator over the elements in this list (in proper
350     * sequence), starting at the specified position in the list.
351     *
352     * @param index
353     *            the index of the first element to be returned from the list
354     *            iterator.
355     * @return a list iterator, starting at the specified position in the list.
356     */
357    @Override
358    public ListIterator<E> listIterator(int index) {
359        return lazy().listIterator(index);
360    }
361
362    /**
363     * Returns a view of the portion of this list between the specified
364     * fromIndex, inclusive, and toIndex, exclusive.
365     *
366     * @param fromIndex
367     *            low endpoint (inclusive) of the subList.
368     * @param toIndex
369     *            high endpoint (exclusive) of the subList.
370     * @return a view of the specified range within this list.
371     */
372    @Override
373    public List<E> subList(int fromIndex, int toIndex) {
374        return lazy().subList(fromIndex, toIndex);
375    }
376}