Java tutorial
/* * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.util; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import org.springframework.lang.Nullable; /** * Simple {@link List} wrapper class that allows for elements to be * automatically populated as they are requested. This is particularly * useful for data binding to {@link List Lists}, allowing for elements * to be created and added to the {@link List} in a "just in time" fashion. * * <p>Note: This class is not thread-safe. To create a thread-safe version, * use the {@link java.util.Collections#synchronizedList} utility methods. * * <p>Inspired by {@code LazyList} from Commons Collections. * * @author Rob Harrop * @author Juergen Hoeller * @since 2.0 * @param <E> the element type */ @SuppressWarnings("serial") public class AutoPopulatingList<E> implements List<E>, Serializable { /** * The {@link List} that all operations are eventually delegated to. */ private final List<E> backingList; /** * The {@link ElementFactory} to use to create new {@link List} elements * on demand. */ private final ElementFactory<E> elementFactory; /** * Creates a new {@code AutoPopulatingList} that is backed by a standard * {@link ArrayList} and adds new instances of the supplied {@link Class element Class} * to the backing {@link List} on demand. */ public AutoPopulatingList(Class<? extends E> elementClass) { this(new ArrayList<>(), elementClass); } /** * Creates a new {@code AutoPopulatingList} that is backed by the supplied {@link List} * and adds new instances of the supplied {@link Class element Class} to the backing * {@link List} on demand. */ public AutoPopulatingList(List<E> backingList, Class<? extends E> elementClass) { this(backingList, new ReflectiveElementFactory<>(elementClass)); } /** * Creates a new {@code AutoPopulatingList} that is backed by a standard * {@link ArrayList} and creates new elements on demand using the supplied {@link ElementFactory}. */ public AutoPopulatingList(ElementFactory<E> elementFactory) { this(new ArrayList<>(), elementFactory); } /** * Creates a new {@code AutoPopulatingList} that is backed by the supplied {@link List} * and creates new elements on demand using the supplied {@link ElementFactory}. */ public AutoPopulatingList(List<E> backingList, ElementFactory<E> elementFactory) { Assert.notNull(backingList, "Backing List must not be null"); Assert.notNull(elementFactory, "Element factory must not be null"); this.backingList = backingList; this.elementFactory = elementFactory; } @Override public void add(int index, E element) { this.backingList.add(index, element); } @Override public boolean add(E o) { return this.backingList.add(o); } @Override public boolean addAll(Collection<? extends E> c) { return this.backingList.addAll(c); } @Override public boolean addAll(int index, Collection<? extends E> c) { return this.backingList.addAll(index, c); } @Override public void clear() { this.backingList.clear(); } @Override public boolean contains(Object o) { return this.backingList.contains(o); } @Override public boolean containsAll(Collection<?> c) { return this.backingList.containsAll(c); } /** * Get the element at the supplied index, creating it if there is * no element at that index. */ @Override public E get(int index) { int backingListSize = this.backingList.size(); E element = null; if (index < backingListSize) { element = this.backingList.get(index); if (element == null) { element = this.elementFactory.createElement(index); this.backingList.set(index, element); } } else { for (int x = backingListSize; x < index; x++) { this.backingList.add(null); } element = this.elementFactory.createElement(index); this.backingList.add(element); } return element; } @Override public int indexOf(Object o) { return this.backingList.indexOf(o); } @Override public boolean isEmpty() { return this.backingList.isEmpty(); } @Override public Iterator<E> iterator() { return this.backingList.iterator(); } @Override public int lastIndexOf(Object o) { return this.backingList.lastIndexOf(o); } @Override public ListIterator<E> listIterator() { return this.backingList.listIterator(); } @Override public ListIterator<E> listIterator(int index) { return this.backingList.listIterator(index); } @Override public E remove(int index) { return this.backingList.remove(index); } @Override public boolean remove(Object o) { return this.backingList.remove(o); } @Override public boolean removeAll(Collection<?> c) { return this.backingList.removeAll(c); } @Override public boolean retainAll(Collection<?> c) { return this.backingList.retainAll(c); } @Override public E set(int index, E element) { return this.backingList.set(index, element); } @Override public int size() { return this.backingList.size(); } @Override public List<E> subList(int fromIndex, int toIndex) { return this.backingList.subList(fromIndex, toIndex); } @Override public Object[] toArray() { return this.backingList.toArray(); } @Override public <T> T[] toArray(T[] a) { return this.backingList.toArray(a); } @Override public boolean equals(@Nullable Object other) { return this.backingList.equals(other); } @Override public int hashCode() { return this.backingList.hashCode(); } /** * Factory interface for creating elements for an index-based access * data structure such as a {@link java.util.List}. * * @param <E> the element type */ @FunctionalInterface public interface ElementFactory<E> { /** * Create the element for the supplied index. * @return the element object * @throws ElementInstantiationException if the instantiation process failed * (any exception thrown by a target constructor should be propagated as-is) */ E createElement(int index) throws ElementInstantiationException; } /** * Exception to be thrown from ElementFactory. */ public static class ElementInstantiationException extends RuntimeException { public ElementInstantiationException(String msg) { super(msg); } public ElementInstantiationException(String message, Throwable cause) { super(message, cause); } } /** * Reflective implementation of the ElementFactory interface, using * {@code Class.getDeclaredConstructor().newInstance()} on a given element class. */ private static class ReflectiveElementFactory<E> implements ElementFactory<E>, Serializable { private final Class<? extends E> elementClass; public ReflectiveElementFactory(Class<? extends E> elementClass) { Assert.notNull(elementClass, "Element class must not be null"); Assert.isTrue(!elementClass.isInterface(), "Element class must not be an interface type"); Assert.isTrue(!Modifier.isAbstract(elementClass.getModifiers()), "Element class cannot be an abstract class"); this.elementClass = elementClass; } @Override public E createElement(int index) { try { return ReflectionUtils.accessibleConstructor(this.elementClass).newInstance(); } catch (NoSuchMethodException ex) { throw new ElementInstantiationException( "No default constructor on element class: " + this.elementClass.getName(), ex); } catch (InstantiationException ex) { throw new ElementInstantiationException( "Unable to instantiate element class: " + this.elementClass.getName(), ex); } catch (IllegalAccessException ex) { throw new ElementInstantiationException( "Could not access element constructor: " + this.elementClass.getName(), ex); } catch (InvocationTargetException ex) { throw new ElementInstantiationException( "Failed to invoke element constructor: " + this.elementClass.getName(), ex.getTargetException()); } } } }