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