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