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 }