View Javadoc

1   /*
2    * Copyright 2002-2007 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.kuali.rice.kns.util.spring;
18  
19  import java.io.Serializable;
20  import java.lang.reflect.Modifier;
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.ListIterator;
26  
27  import org.springframework.util.Assert;
28  
29  /**
30   * Simple {@link List} wrapper class that allows for elements to be
31   * automatically populated as they are requested. This is particularly
32   * useful for data binding to {@link List Lists}, allowing for elements
33   * to be created and added to the {@link List} in a "just in time" fashion.
34   *
35   * <p>Note: This class is not thread-safe. To create a thread-safe version,
36   * use the {@link java.util.Collections#synchronizedList} utility methods.
37   *
38   * <p>Inspired by <code>LazyList</code> from Commons Collections.
39   *
40   * @author Rob Harrop
41   * @author Juergen Hoeller
42   * @since 2.0
43   */
44  public class AutoPopulatingList extends Object implements List, Serializable {
45  
46  	/**
47  	 * The {@link List} that all operations are eventually delegated to.
48  	 */
49  	private final List backingList;
50  
51  	/**
52  	 * The {@link ElementFactory} to use to create new {@link List} elements
53  	 * on demand.
54  	 */
55  	private final ElementFactory elementFactory;
56  
57  
58  	
59  	/**
60  	 * Creates a new <code>AutoPopulatingList</code> that is backed by a standard
61  	 * {@link ArrayList} and adds new instances of the supplied {@link Class element Class}
62  	 * to the backing {@link List} on demand.
63  	 */
64  	public AutoPopulatingList(Class elementClass) {
65  		this(new ArrayList(), elementClass);
66  	}
67  	
68  	public AutoPopulatingList() {
69  		this(new ArrayList(), String.class);
70  	}
71  	
72  	/**
73  	 * Creates a new <code>AutoPopulatingList</code> that is backed by the supplied {@link List}
74  	 * and adds new instances of the supplied {@link Class element Class} to the backing
75  	 * {@link List} on demand.
76  	 */
77  	public AutoPopulatingList(List backingList, Class elementClass) {
78  		this(backingList, new ReflectiveElementFactory(elementClass));
79  	}
80  
81  	/**
82  	 * Creates a new <code>AutoPopulatingList</code> that is backed by a standard
83  	 * {@link ArrayList} and creates new elements on demand using the supplied {@link ElementFactory}.
84  	 */
85  	public AutoPopulatingList(ElementFactory elementFactory) {
86  		this(new ArrayList(), elementFactory);
87  	}
88  
89  	/**
90  	 * Creates a new <code>AutoPopulatingList</code> that is backed by the supplied {@link List}
91  	 * and creates new elements on demand using the supplied {@link ElementFactory}.
92  	 */
93  	public AutoPopulatingList(List backingList, ElementFactory elementFactory) {
94  		Assert.notNull(backingList, "Backing List must not be null");
95  		Assert.notNull(elementFactory, "Element factory must not be null");
96  		this.backingList = backingList;
97  		this.elementFactory = elementFactory;
98  	}
99  
100 
101 	public void add(int index, Object element) {
102 		this.backingList.add(index, element);
103 	}
104 
105 	public boolean add(Object o) {
106 		return this.backingList.add(o);
107 	}
108 
109 	public boolean addAll(Collection c) {
110 		return this.backingList.addAll(c);
111 	}
112 
113 	public boolean addAll(int index, Collection c) {
114 		return this.backingList.addAll(index, c);
115 	}
116 
117 	public void clear() {
118 		this.backingList.clear();
119 	}
120 
121 	public boolean contains(Object o) {
122 		return this.backingList.contains(o);
123 	}
124 
125 	public boolean containsAll(Collection c) {
126 		return this.backingList.containsAll(c);
127 	}
128 
129 	public boolean equals(Object o) {
130 		return this.backingList.equals(o);
131 	}
132 
133 	/**
134 	 * Get the element at the supplied index, creating it if there is
135 	 * no element at that index.
136 	 */
137 	public Object get(int index) {
138 		int backingListSize = this.backingList.size();
139 
140 		Object element = null;
141 		if (index < backingListSize) {
142 			element = this.backingList.get(index);
143 			if (element == null) {
144 				element = this.elementFactory.createElement(index);
145 				this.backingList.set(index, element);
146 			}
147 		}
148 		else {
149 			for (int x = backingListSize; x < index; x++) {
150 				this.backingList.add(null);
151 			}
152 			element = this.elementFactory.createElement(index);
153 			this.backingList.add(element);
154 		}
155 		return element;
156 	}
157 
158 	public int hashCode() {
159 		return this.backingList.hashCode();
160 	}
161 
162 	public int indexOf(Object o) {
163 		return this.backingList.indexOf(o);
164 	}
165 
166 	public boolean isEmpty() {
167 		return this.backingList.isEmpty();
168 	}
169 
170 	public Iterator iterator() {
171 		return this.backingList.iterator();
172 	}
173 
174 	public int lastIndexOf(Object o) {
175 		return this.backingList.lastIndexOf(o);
176 	}
177 
178 	public ListIterator listIterator() {
179 		return this.backingList.listIterator();
180 	}
181 
182 	public ListIterator listIterator(int index) {
183 		return this.backingList.listIterator(index);
184 	}
185 
186 	public Object remove(int index) {
187 		return this.backingList.remove(index);
188 	}
189 
190 	public boolean remove(Object o) {
191 		return this.backingList.remove(o);
192 	}
193 
194 	public boolean removeAll(Collection c) {
195 		return this.backingList.removeAll(c);
196 	}
197 
198 	public boolean retainAll(Collection c) {
199 		return this.backingList.retainAll(c);
200 	}
201 
202 	public Object set(int index, Object element) {
203 		get( index );
204 		return this.backingList.set(index, element);
205 	}
206 
207 	public int size() {
208 		return this.backingList.size();
209 	}
210 
211 	public List subList(int fromIndex, int toIndex) {
212 		return this.backingList.subList(fromIndex, toIndex);
213 	}
214 
215 	public Object[] toArray() {
216 		return this.backingList.toArray();
217 	}
218 
219 	public Object[] toArray(Object[] a) {
220 		return this.backingList.toArray(a);
221 	}
222 
223 
224 	/**
225 	 * Factory interface for creating elements for an index-based access
226 	 * data structure such as a {@link java.util.List}.
227 	 */
228 	public interface ElementFactory {
229 
230 		/**
231 		 * Create the element for the supplied index.
232 		 * @return the element object
233 		 * @throws ElementInstantiationException if the instantiation process failed
234 		 * (any exception thrown by a target constructor should be propagated as-is)
235 		 */
236 		Object createElement(int index) throws ElementInstantiationException;
237 	}
238 
239 
240 	/**
241 	 * Exception to be thrown from ElementFactory.
242 	 */
243 	public static class ElementInstantiationException extends RuntimeException {
244 
245 		public ElementInstantiationException(String msg) {
246 			super(msg);
247 		}
248 	}
249 
250 
251 	/**
252 	 * Reflective implementation of the ElementFactory interface,
253 	 * using <code>Class.newInstance()</code> on a given element class.
254 	 * @see java.lang.Class#newInstance()
255 	 */
256 	private static class ReflectiveElementFactory implements ElementFactory, Serializable {
257 
258 		private final Class elementClass;
259 
260 		public ReflectiveElementFactory(Class elementClass) {
261 			Assert.notNull(elementClass, "Element clas must not be null");
262 			Assert.isTrue(!elementClass.isInterface(), "Element class must not be an interface type");
263 			Assert.isTrue(!Modifier.isAbstract(elementClass.getModifiers()), "Element class cannot be an abstract class");
264 			this.elementClass = elementClass;
265 		}
266 
267 		public Object createElement(int index) {
268 			try {
269 				return this.elementClass.newInstance();
270 			}
271 			catch (InstantiationException ex) {
272 				throw new ElementInstantiationException("Unable to instantiate element class [" +
273 						this.elementClass.getName() + "]. Root cause is " + ex);
274 			}
275 			catch (IllegalAccessException ex) {
276 				throw new ElementInstantiationException("Cannot access element class [" +
277 						this.elementClass.getName() + "]. Root cause is " + ex);
278 			}
279 		}
280 	}
281 
282 }