001 /*
002 * Copyright 2002-2004 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://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, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016 package org.springframework.binding.value.support;
017
018 import java.util.ArrayList;
019 import java.util.Collection;
020 import java.util.Collections;
021 import java.util.Comparator;
022 import java.util.HashSet;
023 import java.util.Iterator;
024 import java.util.List;
025 import java.util.Random;
026 import java.util.Set;
027 import java.util.SortedSet;
028 import java.util.TreeSet;
029
030 import org.springframework.beans.BeanUtils;
031 import org.springframework.binding.support.TestPropertyChangeListener;
032 import org.springframework.binding.value.CommitTrigger;
033 import org.springframework.binding.value.ValueModel;
034 import org.springframework.richclient.test.SpringRichTestCase;
035
036 /**
037 * Test cases for {@link BufferedCollectionValueModel}
038 *
039 * @author oliverh
040 */
041 public class BufferedCollectionValueModelTests extends SpringRichTestCase {
042
043 private Class[] supportedIterfaces = new Class[] {Collection.class, List.class, Set.class, SortedSet.class,};
044
045 private Class[] supportedClasses = new Class[] {ArrayList.class, HashSet.class, TreeSet.class,};
046
047 public void testCreating() {
048 try {
049 getBufferedCollectionValueModel(null, null);
050 fail("NULL wrappedType should not be supported");
051 }
052 catch (IllegalArgumentException e) {
053 // expected
054 }
055 try {
056 getBufferedCollectionValueModel(null, Object.class);
057 fail("wrappedType can only be an instance Collection or an array");
058 }
059 catch (IllegalArgumentException e) {
060 // expected
061 }
062 try {
063 getBufferedCollectionValueModel(null, int[].class);
064 fail("wrappedType can not be a primitive array");
065 }
066 catch (IllegalArgumentException e) {
067 // expected
068 }
069 for (int i = 0; i < supportedIterfaces.length; i++) {
070 getBufferedCollectionValueModel(null, supportedIterfaces[i]);
071 }
072 try {
073 getBufferedCollectionValueModel(null, CustomCollectionInterface.class);
074 fail("if wrappedType is an interface it must one of the standard JDK Collection interfaces");
075 }
076 catch (IllegalArgumentException e) {
077 // expected
078 }
079 getBufferedCollectionValueModel(null, Object[].class);
080 getBufferedCollectionValueModel(null, CustomCollectionClass.class);
081 for (int i = 0; i < supportedClasses.length; i++) {
082 getBufferedCollectionValueModel(null, supportedClasses[i]);
083 }
084 }
085
086 public void testGetAfterBackingObjectChange() {
087 Object[] backingArray = getArray(1);
088 BufferedCollectionValueModel vm = getBufferedCollectionValueModel(backingArray);
089 assertHasSameStructure((ListListModel)vm.getValue(), backingArray);
090
091 backingArray = getArray(2);
092 vm.getWrappedValueModel().setValue(backingArray);
093 assertHasSameStructure((ListListModel)vm.getValue(), backingArray);
094
095 vm.getWrappedValueModel().setValue(null);
096 assertEquals("ListListModel must have no elements when backing collection is NULL",
097 ((ListListModel)vm.getValue()).size(), 0);
098
099 for (int i = 0; i < supportedClasses.length; i++) {
100 Collection backingCollection = getCollection(supportedClasses[i], i);
101 vm = getBufferedCollectionValueModel(backingCollection);
102 assertHasSameStructure((ListListModel)vm.getValue(), backingCollection);
103
104 backingCollection = getCollection(supportedClasses[i], i + 1);
105 vm.getWrappedValueModel().setValue(backingCollection);
106 assertHasSameStructure((ListListModel)vm.getValue(), backingCollection);
107
108 vm.getWrappedValueModel().setValue(null);
109 assertEquals("ListListModel must have no elements when backing collection is NULL",
110 ((ListListModel)vm.getValue()).size(), 0);
111 }
112 }
113
114 public void testCreateWithEmptyCollection() {
115 BufferedCollectionValueModel vm = new BufferedCollectionValueModel(new ValueHolder(null), Collection.class);
116 assertTrue(vm.getValue() instanceof ListListModel);
117 assertEquals(0, ((ListListModel)vm.getValue()).size());
118
119 vm = new BufferedCollectionValueModel(new ValueHolder(new ArrayList()), Collection.class);
120 assertTrue(vm.getValue() instanceof ListListModel);
121 assertEquals(0, ((ListListModel)vm.getValue()).size());
122
123 vm = new BufferedCollectionValueModel(new ValueHolder(null), Object[].class);
124 assertTrue(vm.getValue() instanceof ListListModel);
125 assertEquals(0, ((ListListModel)vm.getValue()).size());
126
127 vm = new BufferedCollectionValueModel(new ValueHolder(new Object[0]), Object[].class);
128 assertTrue(vm.getValue() instanceof ListListModel);
129 assertEquals(0, ((ListListModel)vm.getValue()).size());
130 }
131
132 public void testChangesToListListModelWithBackingArray() {
133 Object[] backingArray = getArray(100);
134 BufferedCollectionValueModel vm = getBufferedCollectionValueModel(backingArray);
135 ListListModel llm = (ListListModel)vm.getValue();
136 llm.clear();
137 assertEquals("changes to ListListModel should be not be made to backing array unless commit is called",
138 vm.getWrappedValueModel().getValue(), backingArray);
139
140 backingArray = getArray(101);
141 vm.getWrappedValueModel().setValue(backingArray);
142 Object newValue = new Double(1);
143 llm.set(1, newValue);
144 vm.commit();
145 Object[] newBackingArray = (Object[])vm.getWrappedValueModel().getValue();
146 assertNotSame("change should not have been committed back to original array", newBackingArray, backingArray);
147
148 llm.add(newValue);
149 vm.commit();
150 newBackingArray = (Object[])vm.getWrappedValueModel().getValue();
151 assertNotSame("change should not have been committed back to original array", newBackingArray, backingArray);
152 assertTrue(newBackingArray.length == backingArray.length + 1);
153 assertEquals(newBackingArray[newBackingArray.length - 1], newValue);
154
155 llm.clear();
156 vm.commit();
157 newBackingArray = (Object[])vm.getWrappedValueModel().getValue();
158 assertEquals(newBackingArray.length, 0);
159
160 vm.getWrappedValueModel().setValue(null);
161 llm.clear();
162 vm.commit();
163 assertEquals("if backingCollection is NULL then a commit of an empty LLM should also be NULL",
164 vm.getWrappedValueModel().getValue(), null);
165
166 llm.add(newValue);
167 vm.commit();
168 newBackingArray = (Object[])vm.getWrappedValueModel().getValue();
169 assertEquals(newBackingArray.length, 1);
170 assertEquals(newBackingArray[0], newValue);
171 }
172
173 public void testChangesToListListModelWithBackingCollection() {
174 for (int i = 0; i < supportedClasses.length; i++) {
175 Collection backingCollection = getCollection(supportedClasses[i], 200 + i);
176 BufferedCollectionValueModel vm = getBufferedCollectionValueModel(backingCollection);
177 ListListModel llm = (ListListModel)vm.getValue();
178 llm.clear();
179 assertEquals("changes to LLM should be not be made to backing collection unless commit is called",
180 vm.getWrappedValueModel().getValue(), backingCollection);
181
182 backingCollection = getCollection(supportedClasses[i], 201 + i);
183 vm.getWrappedValueModel().setValue(backingCollection);
184 Object newValue = new Integer(-1);
185 backingCollection.remove(newValue);
186 int orgSize = backingCollection.size();
187 llm.set(1, newValue);
188 vm.commit();
189 Collection newBackingCollection = (Collection)vm.getWrappedValueModel().getValue();
190 assertTrue("change should not have been committed back to original array",
191 !backingCollection.contains(newValue));
192 assertTrue(newBackingCollection.contains(newValue));
193 assertTrue(orgSize == newBackingCollection.size());
194
195 newValue = new Integer(-2);
196 backingCollection.remove(newValue);
197 orgSize = backingCollection.size();
198 llm.add(newValue);
199 vm.commit();
200 newBackingCollection = (Collection)vm.getWrappedValueModel().getValue();
201
202 assertTrue(newBackingCollection.contains(newValue));
203 assertTrue(newBackingCollection.size() == orgSize + 1);
204
205 llm.clear();
206 vm.commit();
207 assertEquals(((Collection)vm.getWrappedValueModel().getValue()).size(), 0);
208
209 vm.getWrappedValueModel().setValue(null);
210 llm.clear();
211 vm.commit();
212 newBackingCollection = (Collection)vm.getWrappedValueModel().getValue();
213 assertEquals("if backingCollection is NULL then a commit of an empty LLM should also be NULL",
214 newBackingCollection, null);
215
216 llm.add(newValue);
217 vm.commit();
218 newBackingCollection = (Collection)vm.getWrappedValueModel().getValue();
219 assertTrue(supportedClasses[i].isAssignableFrom(newBackingCollection.getClass()));
220 assertEquals(newBackingCollection.size(), 1);
221 assertEquals(newBackingCollection.iterator().next(), newValue);
222 }
223 }
224
225 public void testListListModelKeepsStuctureOfBackingObjectAfterCommit() {
226 Collection backingCollection = getCollection(HashSet.class, 500);
227 int origLength = backingCollection.size();
228 BufferedCollectionValueModel vm = getBufferedCollectionValueModel(backingCollection);
229 ListListModel llm = (ListListModel)vm.getValue();
230 llm.add(backingCollection.iterator().next());
231 assertTrue(llm.size() == origLength + 1);
232 vm.commit();
233 assertTrue("adding a duplicate item should not change the size of a set", llm.size() == origLength);
234 assertHasSameStructure(llm, backingCollection);
235
236 backingCollection = getCollection(TreeSet.class, 501);
237 vm = getBufferedCollectionValueModel(backingCollection);
238 llm = (ListListModel)vm.getValue();
239 Collections.reverse(llm);
240 assertTrue(((Comparable)llm.get(0)).compareTo(llm.get(1)) > 0);
241 vm.commit();
242 assertTrue("LLM should be sorted the same way as backingCollection",
243 ((Comparable)llm.get(0)).compareTo(llm.get(1)) < 0);
244 assertHasSameStructure(llm, backingCollection);
245
246 backingCollection = new TreeSet(new Comparator() {
247
248 public int compare(Object o1, Object o2) {
249 return ((Comparable)o2).compareTo(o1);
250 }
251
252 });
253 populateCollection(backingCollection, 502);
254 vm = getBufferedCollectionValueModel(backingCollection);
255 llm = (ListListModel)vm.getValue();
256 Collections.reverse(llm);
257 assertTrue(((Comparable)llm.get(0)).compareTo(llm.get(1)) < 0);
258 vm.commit();
259 assertTrue("LLM should be sorted the same way as backingCollection",
260 ((Comparable)llm.get(0)).compareTo(llm.get(1)) > 0);
261 assertHasSameStructure(llm, backingCollection);
262 }
263
264 public void testIncompatibleCollections() {
265 try {
266 getBufferedCollectionValueModel(new ArrayList(), Set.class);
267 fail("backing object must be assignable to backingCollectionClass");
268 }
269 catch (IllegalArgumentException e) {
270 // expected
271 }
272 try {
273 getBufferedCollectionValueModel(new Double[0], Integer[].class);
274 fail("backing object must be assignable to backingCollectionClass");
275 }
276 catch (IllegalArgumentException e) {
277 // expected
278 }
279 }
280
281 public void testValueChangeNotification() {
282 Object[] backingArray = getArray(100);
283 BufferedCollectionValueModel vm = getBufferedCollectionValueModel(backingArray);
284 TestPropertyChangeListener vl = new TestPropertyChangeListener(ValueModel.VALUE_PROPERTY);
285 vm.addValueChangeListener(vl);
286
287 ListListModel llm = (ListListModel)vm.getValue();
288 assertEquals(0, vl.eventCount());
289
290 vl.reset();
291 llm.add(new Integer(100));
292 assertEquals(1, vl.eventCount());
293 llm.add(1, new Integer(102));
294 assertEquals(2, vl.eventCount());
295
296 vl.reset();
297 llm.addAll(getCollection(ArrayList.class, 101));
298 assertEquals(1, vl.eventCount());
299 llm.addAll(1, getCollection(ArrayList.class, 101));
300 assertEquals(2, vl.eventCount());
301
302 vl.reset();
303 llm.remove(1);
304 assertEquals(1, vl.eventCount());
305 llm.removeAll(getCollection(ArrayList.class, 101));
306 assertEquals(2, vl.eventCount());
307
308 vl.reset();
309 llm.set(1, llm.get(1));
310 assertEquals(0, vl.eventCount());
311
312 vl.reset();
313 llm.clear();
314 assertEquals(1, vl.eventCount());
315 }
316
317 public void testRevert() {
318 CommitTrigger commitTriger = new CommitTrigger();
319
320 Collection backingCollection = getCollection(HashSet.class, 700);
321 BufferedCollectionValueModel vm = getBufferedCollectionValueModel(backingCollection);
322 vm.setCommitTrigger(commitTriger);
323 ListListModel llm = (ListListModel)vm.getValue();
324 llm.clear();
325 commitTriger.revert();
326 assertHasSameStructure(llm, backingCollection);
327 }
328
329 private void assertHasSameStructure(ListListModel c1, Object[] c2) {
330 assertEquals("collections must be the same size", c1.size(), c2.length);
331 for (int i = 0; i < c2.length; i++) {
332 assertEquals("collections must have the same items in the same order", c1.get(i), c2[i]);
333 }
334 }
335
336 private void assertHasSameStructure(ListListModel c1, Collection c2) {
337 assertEquals("collections must be the same size", c2.size(), c1.size());
338 for (Iterator i = c1.iterator(), j = c2.iterator(); i.hasNext();) {
339 assertEquals("collections must have the same items in the same order", i.next(), j.next());
340 }
341 }
342
343 private Object[] getArray(long randomSeed) {
344 Random random = new Random(randomSeed);
345 return new Number[] {new Integer(random.nextInt()), new Integer(random.nextInt()),
346 new Integer(random.nextInt())};
347 }
348
349 private Collection getCollection(Class collectionClass, long randomSeed) {
350 return populateCollection((Collection)BeanUtils.instantiateClass(collectionClass), randomSeed);
351 }
352
353 private Collection populateCollection(Collection c, long randomSeed) {
354 Random random = new Random(randomSeed);
355 c.add(new Integer(random.nextInt()));
356 c.add(new Integer(random.nextInt()));
357 c.add(new Integer(random.nextInt()));
358 return c;
359 }
360
361 private BufferedCollectionValueModel getBufferedCollectionValueModel(Object backingCollecton) {
362 return getBufferedCollectionValueModel(backingCollecton, backingCollecton.getClass());
363 }
364
365 private BufferedCollectionValueModel getBufferedCollectionValueModel(Object backingCollecton,
366 Class backingCollectionClass) {
367 ValueModel vm = new ValueHolder(backingCollecton);
368 return new BufferedCollectionValueModel(vm, backingCollectionClass);
369 }
370
371 interface CustomCollectionInterface extends Collection {
372
373 }
374
375 class CustomCollectionClass extends ArrayList implements CustomCollectionInterface {
376
377 }
378 }