001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.util; 018 019import java.io.Serializable; 020import java.lang.reflect.InvocationTargetException; 021import java.lang.reflect.Modifier; 022import java.util.ArrayList; 023import java.util.Collection; 024import java.util.Iterator; 025import java.util.List; 026import java.util.ListIterator; 027 028import org.springframework.lang.Nullable; 029 030/** 031 * Simple {@link List} wrapper class that allows for elements to be 032 * automatically populated as they are requested. This is particularly 033 * useful for data binding to {@link List Lists}, allowing for elements 034 * to be created and added to the {@link List} in a "just in time" fashion. 035 * 036 * <p>Note: This class is not thread-safe. To create a thread-safe version, 037 * use the {@link java.util.Collections#synchronizedList} utility methods. 038 * 039 * <p>Inspired by {@code LazyList} from Commons Collections. 040 * 041 * @author Rob Harrop 042 * @author Juergen Hoeller 043 * @since 2.0 044 * @param <E> the element type 045 */ 046@SuppressWarnings("serial") 047public class AutoPopulatingList<E> implements List<E>, Serializable { 048 049 /** 050 * The {@link List} that all operations are eventually delegated to. 051 */ 052 private final List<E> backingList; 053 054 /** 055 * The {@link ElementFactory} to use to create new {@link List} elements 056 * on demand. 057 */ 058 private final ElementFactory<E> elementFactory; 059 060 061 /** 062 * Creates a new {@code AutoPopulatingList} that is backed by a standard 063 * {@link ArrayList} and adds new instances of the supplied {@link Class element Class} 064 * to the backing {@link List} on demand. 065 */ 066 public AutoPopulatingList(Class<? extends E> elementClass) { 067 this(new ArrayList<>(), elementClass); 068 } 069 070 /** 071 * Creates a new {@code AutoPopulatingList} that is backed by the supplied {@link List} 072 * and adds new instances of the supplied {@link Class element Class} to the backing 073 * {@link List} on demand. 074 */ 075 public AutoPopulatingList(List<E> backingList, Class<? extends E> elementClass) { 076 this(backingList, new ReflectiveElementFactory<>(elementClass)); 077 } 078 079 /** 080 * Creates a new {@code AutoPopulatingList} that is backed by a standard 081 * {@link ArrayList} and creates new elements on demand using the supplied {@link ElementFactory}. 082 */ 083 public AutoPopulatingList(ElementFactory<E> elementFactory) { 084 this(new ArrayList<>(), elementFactory); 085 } 086 087 /** 088 * Creates a new {@code AutoPopulatingList} that is backed by the supplied {@link List} 089 * and creates new elements on demand using the supplied {@link ElementFactory}. 090 */ 091 public AutoPopulatingList(List<E> backingList, ElementFactory<E> elementFactory) { 092 Assert.notNull(backingList, "Backing List must not be null"); 093 Assert.notNull(elementFactory, "Element factory must not be null"); 094 this.backingList = backingList; 095 this.elementFactory = elementFactory; 096 } 097 098 099 @Override 100 public void add(int index, E element) { 101 this.backingList.add(index, element); 102 } 103 104 @Override 105 public boolean add(E o) { 106 return this.backingList.add(o); 107 } 108 109 @Override 110 public boolean addAll(Collection<? extends E> c) { 111 return this.backingList.addAll(c); 112 } 113 114 @Override 115 public boolean addAll(int index, Collection<? extends E> c) { 116 return this.backingList.addAll(index, c); 117 } 118 119 @Override 120 public void clear() { 121 this.backingList.clear(); 122 } 123 124 @Override 125 public boolean contains(Object o) { 126 return this.backingList.contains(o); 127 } 128 129 @Override 130 public boolean containsAll(Collection<?> c) { 131 return this.backingList.containsAll(c); 132 } 133 134 /** 135 * Get the element at the supplied index, creating it if there is 136 * no element at that index. 137 */ 138 @Override 139 public E get(int index) { 140 int backingListSize = this.backingList.size(); 141 E element = null; 142 if (index < backingListSize) { 143 element = this.backingList.get(index); 144 if (element == null) { 145 element = this.elementFactory.createElement(index); 146 this.backingList.set(index, element); 147 } 148 } 149 else { 150 for (int x = backingListSize; x < index; x++) { 151 this.backingList.add(null); 152 } 153 element = this.elementFactory.createElement(index); 154 this.backingList.add(element); 155 } 156 return element; 157 } 158 159 @Override 160 public int indexOf(Object o) { 161 return this.backingList.indexOf(o); 162 } 163 164 @Override 165 public boolean isEmpty() { 166 return this.backingList.isEmpty(); 167 } 168 169 @Override 170 public Iterator<E> iterator() { 171 return this.backingList.iterator(); 172 } 173 174 @Override 175 public int lastIndexOf(Object o) { 176 return this.backingList.lastIndexOf(o); 177 } 178 179 @Override 180 public ListIterator<E> listIterator() { 181 return this.backingList.listIterator(); 182 } 183 184 @Override 185 public ListIterator<E> listIterator(int index) { 186 return this.backingList.listIterator(index); 187 } 188 189 @Override 190 public E remove(int index) { 191 return this.backingList.remove(index); 192 } 193 194 @Override 195 public boolean remove(Object o) { 196 return this.backingList.remove(o); 197 } 198 199 @Override 200 public boolean removeAll(Collection<?> c) { 201 return this.backingList.removeAll(c); 202 } 203 204 @Override 205 public boolean retainAll(Collection<?> c) { 206 return this.backingList.retainAll(c); 207 } 208 209 @Override 210 public E set(int index, E element) { 211 return this.backingList.set(index, element); 212 } 213 214 @Override 215 public int size() { 216 return this.backingList.size(); 217 } 218 219 @Override 220 public List<E> subList(int fromIndex, int toIndex) { 221 return this.backingList.subList(fromIndex, toIndex); 222 } 223 224 @Override 225 public Object[] toArray() { 226 return this.backingList.toArray(); 227 } 228 229 @Override 230 public <T> T[] toArray(T[] a) { 231 return this.backingList.toArray(a); 232 } 233 234 235 @Override 236 public boolean equals(@Nullable Object other) { 237 return this.backingList.equals(other); 238 } 239 240 @Override 241 public int hashCode() { 242 return this.backingList.hashCode(); 243 } 244 245 246 /** 247 * Factory interface for creating elements for an index-based access 248 * data structure such as a {@link java.util.List}. 249 * 250 * @param <E> the element type 251 */ 252 @FunctionalInterface 253 public interface ElementFactory<E> { 254 255 /** 256 * Create the element for the supplied index. 257 * @return the element object 258 * @throws ElementInstantiationException if the instantiation process failed 259 * (any exception thrown by a target constructor should be propagated as-is) 260 */ 261 E createElement(int index) throws ElementInstantiationException; 262 } 263 264 265 /** 266 * Exception to be thrown from ElementFactory. 267 */ 268 public static class ElementInstantiationException extends RuntimeException { 269 270 public ElementInstantiationException(String msg) { 271 super(msg); 272 } 273 274 public ElementInstantiationException(String message, Throwable cause) { 275 super(message, cause); 276 } 277 } 278 279 280 /** 281 * Reflective implementation of the ElementFactory interface, using 282 * {@code Class.getDeclaredConstructor().newInstance()} on a given element class. 283 */ 284 private static class ReflectiveElementFactory<E> implements ElementFactory<E>, Serializable { 285 286 private final Class<? extends E> elementClass; 287 288 public ReflectiveElementFactory(Class<? extends E> elementClass) { 289 Assert.notNull(elementClass, "Element class must not be null"); 290 Assert.isTrue(!elementClass.isInterface(), "Element class must not be an interface type"); 291 Assert.isTrue(!Modifier.isAbstract(elementClass.getModifiers()), "Element class cannot be an abstract class"); 292 this.elementClass = elementClass; 293 } 294 295 @Override 296 public E createElement(int index) { 297 try { 298 return ReflectionUtils.accessibleConstructor(this.elementClass).newInstance(); 299 } 300 catch (NoSuchMethodException ex) { 301 throw new ElementInstantiationException( 302 "No default constructor on element class: " + this.elementClass.getName(), ex); 303 } 304 catch (InstantiationException ex) { 305 throw new ElementInstantiationException( 306 "Unable to instantiate element class: " + this.elementClass.getName(), ex); 307 } 308 catch (IllegalAccessException ex) { 309 throw new ElementInstantiationException( 310 "Could not access element constructor: " + this.elementClass.getName(), ex); 311 } 312 catch (InvocationTargetException ex) { 313 throw new ElementInstantiationException( 314 "Failed to invoke element constructor: " + this.elementClass.getName(), ex.getTargetException()); 315 } 316 } 317 } 318 319}