001: /*
002: * Copyright (c) 2002-2007 JGoodies Karsten Lentzsch. All Rights Reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of JGoodies Karsten Lentzsch nor the names of
015: * its contributors may be used to endorse or promote products derived
016: * from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package com.jgoodies.binding.tests;
032:
033: import java.util.ArrayList;
034: import java.util.Arrays;
035: import java.util.Collections;
036: import java.util.List;
037:
038: import javax.swing.DefaultListModel;
039: import javax.swing.ListModel;
040: import javax.swing.event.ListDataEvent;
041: import javax.swing.event.ListDataListener;
042:
043: import junit.framework.TestCase;
044:
045: import com.jgoodies.binding.list.ArrayListModel;
046: import com.jgoodies.binding.list.IndirectListModel;
047: import com.jgoodies.binding.list.LinkedListModel;
048: import com.jgoodies.binding.list.ObservableList;
049: import com.jgoodies.binding.tests.event.ListDataReport;
050: import com.jgoodies.binding.tests.event.ListSizeConstraintChecker;
051: import com.jgoodies.binding.tests.value.ValueHolderWithOldValueNull;
052: import com.jgoodies.binding.value.ValueHolder;
053: import com.jgoodies.binding.value.ValueModel;
054:
055: /**
056: * A test case for class {@link IndirectListModel}.
057: *
058: * @author Karsten Lentzsch
059: * @version $Revision: 1.2 $
060: */
061: public final class IndirectListModelTest extends TestCase {
062:
063: private static final Object[] AN_ARRAY = { "one", "two", "three" };
064:
065: private DefaultListModel listModel;
066:
067: // Initialization *********************************************************
068:
069: @Override
070: protected void setUp() throws Exception {
071: super .setUp();
072: listModel = createListModel(AN_ARRAY);
073: }
074:
075: // Testing Constructors ***************************************************
076:
077: public void testConstructorRejectsNullListModelHolder() {
078: try {
079: new IndirectListModel<String>((ValueModel) null);
080: fail("The IndirectListModel must reject a null ListModel holder.");
081: } catch (NullPointerException e) {
082: // The expected behavior.
083: }
084: }
085:
086: public void testConstructorRejectsNonIdentityCheckingValueHolder() {
087: try {
088: new IndirectListModel<String>(new ValueHolder(listModel));
089: fail("The IndirectListModel must reject ListModel holders that have the identity check disabled.");
090: } catch (IllegalArgumentException e) {
091: // The expected behavior.
092: }
093: }
094:
095: public void testConstructorRejectsInvalidListModelHolderContent() {
096: try {
097: new IndirectListModel<String>(
098: new ValueHolder("Hello", true));
099: fail("The IndirectListModel must reject ListModel holder content other than ListModel.");
100: } catch (ClassCastException e) {
101: // The expected behavior.
102: }
103: }
104:
105: // ************************************************************************
106:
107: /**
108: * Checks that list data events from an underlying are reported
109: * by the SelectionInList.
110: */
111: public void testFiresListModelListDataEvents() {
112: ListDataReport listDataReport1 = new ListDataReport();
113: ListDataReport listDataReport2 = new ListDataReport();
114:
115: ArrayListModel<String> arrayListModel = new ArrayListModel<String>();
116: IndirectListModel<String> lmh = new IndirectListModel<String>(
117: (ListModel) arrayListModel);
118:
119: arrayListModel.addListDataListener(listDataReport1);
120: lmh.addListDataListener(listDataReport2);
121:
122: arrayListModel.add("One");
123: assertEquals("An element has been added.", listDataReport2
124: .eventCount(), 1);
125: assertEquals("An element has been added.", listDataReport2
126: .eventCountAdd(), 1);
127:
128: arrayListModel.addAll(Arrays.asList(new String[] { "two",
129: "three", "four" }));
130: assertEquals("An element block has been added.",
131: listDataReport2.eventCount(), 2);
132: assertEquals("An element block has been added.",
133: listDataReport2.eventCountAdd(), 2);
134:
135: arrayListModel.remove(0);
136: assertEquals("An element has been removed.", listDataReport2
137: .eventCount(), 3);
138: assertEquals("No element has been added.", listDataReport2
139: .eventCountAdd(), 2);
140: assertEquals("An element has been removed.", listDataReport2
141: .eventCountRemove(), 1);
142:
143: arrayListModel.set(1, "newTwo");
144: assertEquals("An element has been replaced.", listDataReport2
145: .eventCount(), 4);
146: assertEquals("No element has been added.", listDataReport2
147: .eventCountAdd(), 2);
148: assertEquals("No element has been removed.", listDataReport2
149: .eventCountRemove(), 1);
150: assertEquals("An element has been changed.", listDataReport2
151: .eventCountChange(), 1);
152:
153: // Compare the event counts of the list models listener
154: // with the SelectionInList listener.
155: assertEquals("Add event counts are equal.", listDataReport1
156: .eventCountAdd(), listDataReport2.eventCountAdd());
157: assertEquals("Remove event counts are equal.", listDataReport1
158: .eventCountRemove(), listDataReport2.eventCountRemove());
159: assertEquals("Change event counts are equal.", listDataReport1
160: .eventCountChange(), listDataReport2.eventCountChange());
161: }
162:
163: // Registering, Unregistering and Registering of the ListDataListener *****
164:
165: /**
166: * Checks and verifies that the SelectionInList registers
167: * its ListDataListener with the underlying ListModel once only.
168: * In other words: the SelectionInList doesn't register
169: * its ListDataListener multiple times.<p>
170: *
171: * Uses a list holder that checks the identity and
172: * reports an old and new value.
173: */
174: public void testSingleListDataListener() {
175: testSingleListDataListener(new ValueHolder(null, true));
176: }
177:
178: /**
179: * Checks and verifies that the SelectionInList registers
180: * its ListDataListener with the underlying ListModel once only.
181: * In other words: the SelectionInList doesn't register
182: * its ListDataListener multiple times.<p>
183: *
184: * Uses a list holder uses null as old value when reporting value changes.
185: */
186: public void testSingleListDataListenerNoOldList() {
187: testSingleListDataListener(new ValueHolderWithOldValueNull(null));
188: }
189:
190: /**
191: * Checks and verifies that the SelectionInList registers
192: * its ListDataListener with the underlying ListModel once only.
193: * In other words: the SelectionInList doesn't register
194: * its ListDataListener multiple times.
195: */
196: private void testSingleListDataListener(ValueModel listHolder) {
197: new IndirectListModel<Object>(listHolder);
198: ArrayListModel<Object> listModel1 = new ArrayListModel<Object>();
199: LinkedListModel<Object> listModel2 = new LinkedListModel<Object>();
200: listHolder.setValue(listModel1);
201: assertEquals(
202: "SelectionInList registered its ListDataListener.", 1,
203: listModel1.getListDataListeners().length);
204: listHolder.setValue(listModel1);
205: assertEquals(
206: "SelectionInList reregistered its ListDataListener.",
207: 1, listModel1.getListDataListeners().length);
208: listHolder.setValue(listModel2);
209: assertEquals(
210: "SelectionInList deregistered its ListDataListener.",
211: 0, listModel1.getListDataListeners().length);
212: assertEquals(
213: "SelectionInList registered its ListDataListener.", 1,
214: listModel2.getListDataListeners().length);
215: }
216:
217: /**
218: * Checks and verifies for a bunch of ListModel instances,
219: * whether the ListDataListener has been reregistered properly.
220: */
221: public void testReregisterListDataListener() {
222: ObservableList<Object> empty1 = new ArrayListModel<Object>();
223: ObservableList<Object> empty2 = new ArrayListModel<Object>();
224: testReregistersListDataListener(empty1, empty2);
225:
226: ObservableList<Object> empty3 = new LinkedListModel<Object>();
227: ObservableList<Object> empty4 = new LinkedListModel<Object>();
228: testReregistersListDataListener(empty3, empty4);
229:
230: ObservableList<Object> array1 = new ArrayListModel<Object>();
231: ObservableList<Object> array2 = new ArrayListModel<Object>();
232: array1.add(Boolean.TRUE);
233: array2.add(Boolean.TRUE);
234: testReregistersListDataListener(array1, array2);
235:
236: ObservableList<Object> linked1 = new LinkedListModel<Object>();
237: ObservableList<Object> linked2 = new LinkedListModel<Object>();
238: linked1.add(Boolean.TRUE);
239: linked2.add(Boolean.TRUE);
240: testReregistersListDataListener(linked1, linked2);
241: }
242:
243: /**
244: * Checks and verifies whether the ListDataListener has been
245: * reregistered properly. This will fail if the change support
246: * fails to fire a change event when the instance changes.<p>
247: *
248: * Creates a SelectionInList on list1, then changes it to list2,
249: * modifies both lists, and finally checks whether the SelectionInList
250: * has fired the correct events.
251: */
252: private void testReregistersListDataListener(
253: ObservableList<Object> list1, ObservableList<Object> list2) {
254: ListDataReport listDataReport1 = new ListDataReport();
255: ListDataReport listDataReport2 = new ListDataReport();
256: ListDataReport listDataReportSel = new ListDataReport();
257:
258: IndirectListModel<Object> lmh = new IndirectListModel<Object>(
259: (ListModel) list1);
260:
261: // Change the list model.
262: // Changes on list1 shall not affect the SelectionInList.
263: // Changes in list2 shall be the same as for the SelectionInList.
264: lmh.setListModel(list2);
265:
266: list1.addListDataListener(listDataReport1);
267: list2.addListDataListener(listDataReport2);
268: lmh.addListDataListener(listDataReportSel);
269:
270: // Modify both list models.
271: list1.add("one1");
272: list1.add("two1");
273: list1.add("three1");
274: list1.add("four1");
275: list1.remove(1);
276: list1.remove(0);
277: list1.set(0, "newOne1");
278: list1.set(1, "newTwo1");
279:
280: assertEquals("Events counted for list model 1", 8,
281: listDataReport1.eventCount());
282: assertEquals("No events counted for list model 2", 0,
283: listDataReport2.eventCount());
284: assertEquals("No events counted for the SelectionInList", 0,
285: listDataReportSel.eventCount());
286:
287: list2.add("one2");
288: list2.add("two2");
289: list2.add("three2");
290: list2.remove(1);
291: list2.set(0, "newOne2");
292:
293: assertEquals("Events counted for list model 2", 5,
294: listDataReport2.eventCount());
295: assertEquals("Events counted for the SelectionInList", 5,
296: listDataReportSel.eventCount());
297:
298: // Compare the event lists.
299: assertEquals("Events for list2 and SelectionInList differ.",
300: listDataReport2, listDataReportSel);
301: }
302:
303: // List Change Events on List Operations **********************************
304:
305: /**
306: * Tests the ListDataEvents fired during list changes.
307: * The transitions are {} -> {} -> {a, b} -> {b, c} -> {a, b, c} -> {b, c} -> {}.
308: */
309: public void testListDataEventsOnDirectListChange() {
310: List<String> list1 = Collections.emptyList();
311: List<String> list2 = Collections.emptyList();
312: List<String> list3 = Arrays.asList("a", "b");
313: List<String> list4 = Arrays.asList("b", "c");
314: List<String> list5 = Arrays.asList("a", "b", "c");
315: List<String> list6 = Collections.emptyList();
316:
317: IndirectListModel<String> listHolder = new IndirectListModel<String>(
318: list1);
319: ListDataReport report = new ListDataReport();
320: listHolder.addListDataListener(report);
321:
322: listHolder.setList(list2);
323: assertEquals("The transition {} -> {} fires no ListDataEvent.",
324: 0, report.eventCount());
325:
326: report.clearEventList();
327: listHolder.setList(list3);
328: assertEquals("The transition {} -> {a, b} fires 1 event.", 1,
329: report.eventCount());
330: assertEvent(
331: "The transition {} -> {a, b} fires an add event with interval[0, 1].",
332: ListDataEvent.INTERVAL_ADDED, 0, 1, report.lastEvent());
333:
334: report.clearEventList();
335: listHolder.setList(list4);
336: assertEquals("The transition {a, b} -> {b, c} fires 1 event.",
337: 1, report.eventCount());
338: assertEvent(
339: "The transition {a, b} -> {b, c} fires a contents changed event with interval[0, 1].",
340: ListDataEvent.CONTENTS_CHANGED, 0, 1, report
341: .lastEvent());
342:
343: report.clearEventList();
344: listHolder.setList(list5);
345: assertEquals(
346: "The transition {b, c} -> {a, b, c} fires two events.",
347: 2, report.eventCount());
348: assertEvent(
349: "The transition {b, c} -> {a, b, c} fires an add event with interval[2, 2].",
350: ListDataEvent.INTERVAL_ADDED, 2, 2, report
351: .previousEvent());
352: assertEvent(
353: "The transition {b, c} -> {a, b, c} fires a contents changed with interval[0, 1].",
354: ListDataEvent.CONTENTS_CHANGED, 0, 1, report
355: .lastEvent());
356:
357: report.clearEventList();
358: listHolder.setList(list4);
359: assertEquals(
360: "The transition {a, b, c} -> {b, c} fires two events.",
361: 2, report.eventCount());
362: assertEvent(
363: "The transition {a, b, c} -> {b, c} fires a remove event with interval[2, 2].",
364: ListDataEvent.INTERVAL_REMOVED, 2, 2, report
365: .previousEvent());
366: assertEvent(
367: "The transition {a, b, c} -> {b, c} fires a contents changed with interval[0, 1].",
368: ListDataEvent.CONTENTS_CHANGED, 0, 1, report
369: .lastEvent());
370:
371: report.clearEventList();
372: listHolder.setList(list6);
373: assertEquals("The transition {b, c} -> {} fires one event.", 1,
374: report.eventCount());
375: assertEvent(
376: "The transition {b, c} -> {} fires a remove event with interval[0, 1].",
377: ListDataEvent.INTERVAL_REMOVED, 0, 1, report
378: .lastEvent());
379: }
380:
381: /**
382: * Tests that ListDataEvents fired during list changes
383: * provide size information that are consistent with the
384: * size of the list model.
385: * The transitions are {} -> {} -> {a, b} -> {b, c} -> {a, b, c} -> {b, c} -> {}.
386: */
387: public void testListDataEventsRetainSizeConstraints() {
388: List<String> list1 = Collections.emptyList();
389: List<String> list2 = Collections.emptyList();
390: List<String> list3 = Arrays.asList(new String[] { "a", "b" });
391: List<String> list4 = Arrays.asList(new String[] { "b", "c" });
392: List<String> list5 = Arrays
393: .asList(new String[] { "a", "b", "c" });
394: List<String> list6 = Collections.emptyList();
395:
396: IndirectListModel<String> listHolder = new IndirectListModel<String>(
397: list1);
398: listHolder.addListDataListener(new ListSizeConstraintChecker(
399: listHolder.getSize()));
400:
401: listHolder.setList(list2);
402: listHolder.setList(list3);
403: listHolder.setList(list4);
404: listHolder.setList(list5);
405: listHolder.setList(list4);
406: listHolder.setList(list6);
407: }
408:
409: /**
410: * Tests the ListDataEvents fired during list changes.
411: * The transitions are {} -> {a} -> {b} -> {} on the same list.
412: */
413: public void testListDataEventsOnIndirectListChange() {
414: String element1 = "a";
415: String element2 = "b";
416: List<String> list = new ArrayList<String>();
417: ValueHolder listInModel = new ValueHolder(list, true);
418: IndirectListModel<String> listHolder = new IndirectListModel<String>(
419: listInModel);
420: ListDataReport report = new ListDataReport();
421: listHolder.addListDataListener(report);
422:
423: list.add(element1);
424: listInModel.fireValueChange(null, list);
425: assertEquals("The transition {} -> {a} fires 1 event.", 1,
426: report.eventCount());
427: assertEvent(
428: "The transition {} -> {a} fires an add event with interval[0, 0].",
429: ListDataEvent.INTERVAL_ADDED, 0, 0, report.lastEvent());
430: report.clearEventList();
431:
432: list.remove(element1);
433: list.add(element2);
434: listInModel.fireValueChange(null, list);
435: assertEquals("The transition {a} -> {b} fires 1 event.", 1,
436: report.eventCount());
437: assertEvent(
438: "The transition {a} -> {b} fires a contents changed event with interval[0, 0].",
439: ListDataEvent.CONTENTS_CHANGED, 0, 0, report
440: .lastEvent());
441: report.clearEventList();
442:
443: list.remove(element2);
444: listInModel.fireValueChange(null, list);
445: assertEquals("The transition {b} -> {} fires one event.", 1,
446: report.eventCount());
447: assertEvent(
448: "The transition {b} -> {} fires a remove event with interval[0, 0].",
449: ListDataEvent.INTERVAL_REMOVED, 0, 0, report
450: .lastEvent());
451: }
452:
453: /**
454: * Tests the ListDataEvents fired in list changes after elements
455: * have been added. Shall detect bugs where the IndirectListModel's internal
456: * listSize is unsynchronized with the list.
457: */
458: public void testListDataEventsOnListAdd() {
459: String element1 = "a";
460: String element2 = "b";
461: List<String> list = new ArrayList<String>();
462: ValueHolder listInModel = new ValueHolder(list, true);
463: IndirectListModel<String> listHolder = new IndirectListModel<String>(
464: listInModel);
465: ListDataReport report = new ListDataReport();
466: listHolder.addListDataListener(report);
467:
468: list.add(element1);
469: listHolder.fireIntervalAdded(0, 0);
470: assertEquals("The transition {} -> {a} fires 1 event.", 1,
471: report.eventCount());
472: assertEvent(
473: "The transition {} -> {a} fires an add event with interval[0, 0].",
474: ListDataEvent.INTERVAL_ADDED, 0, 0, report.lastEvent());
475: report.clearEventList();
476:
477: list.remove(element1);
478: list.add(element2);
479: listInModel.fireValueChange(null, list);
480: assertEquals("The transition {a} -> {b} fires 1 event.", 1,
481: report.eventCount());
482: assertEvent(
483: "The transition {a} -> {b} fires a contents changed event with interval[0, 0].",
484: ListDataEvent.CONTENTS_CHANGED, 0, 0, report
485: .lastEvent());
486: report.clearEventList();
487: }
488:
489: /**
490: * Tests the ListDataEvents fired in list changes after elements
491: * have been removed. Shall detect bugs where the IndirectListModel's internal
492: * listSize is unsynchronized with the list.
493: */
494: public void testListDataEventsOnListRemove() {
495: String element1 = "a";
496: String element2 = "b";
497: List<String> list = new ArrayList<String>();
498: list.add(element1);
499: ValueHolder listInModel = new ValueHolder(list, true);
500: IndirectListModel<String> listHolder = new IndirectListModel<String>(
501: listInModel);
502: ListDataReport report = new ListDataReport();
503: listHolder.addListDataListener(report);
504:
505: list.remove(0);
506: listHolder.fireIntervalRemoved(0, 0);
507: assertEquals("The transition {a} -> {} fires 1 event.", 1,
508: report.eventCount());
509: assertEvent(
510: "The transition {a} -> {} fires a remove event with interval[0, 0].",
511: ListDataEvent.INTERVAL_REMOVED, 0, 0, report
512: .lastEvent());
513: report.clearEventList();
514:
515: list.add(element2);
516: listInModel.fireValueChange(null, list);
517: assertEquals("The transition {} -> {b} fires 1 event.", 1,
518: report.eventCount());
519: assertEvent(
520: "The transition {a} -> {b} fires an add event with interval[0, 0].",
521: ListDataEvent.INTERVAL_ADDED, 0, 0, report.lastEvent());
522: report.clearEventList();
523: }
524:
525: // List Change Events on ListModel Operations *****************************
526:
527: /**
528: * Tests the ListDataEvents fired during ListModel changes.
529: * The transitions are {} -> {} -> {a, b} -> {b, c} -> {a, b, c} -> {b, c} -> {}.
530: */
531: public void testListDataEventsOnDirectListModelChange() {
532: ListModel list1 = createListModel();
533: ListModel list2 = createListModel();
534: ListModel list3 = createListModel("a", "b");
535: ListModel list4 = createListModel("b", "c");
536: ListModel list5 = createListModel("a", "b", "c");
537: ListModel list6 = createListModel();
538:
539: IndirectListModel<Object> listModelHolder = new IndirectListModel<Object>(
540: list1);
541: ListDataReport report = new ListDataReport();
542: listModelHolder.addListDataListener(report);
543:
544: listModelHolder.setListModel(list2);
545: assertEquals("The transition {} -> {} fires no ListDataEvent.",
546: 0, report.eventCount());
547:
548: report.clearEventList();
549: listModelHolder.setListModel(list3);
550: assertEquals("The transition {} -> {a, b} fires 1 add event.",
551: 1, report.eventCount());
552: assertEvent(
553: "The transition {} -> {a, b} fires an add event with interval[0, 1].",
554: ListDataEvent.INTERVAL_ADDED, 0, 1, report.lastEvent());
555:
556: report.clearEventList();
557: listModelHolder.setListModel(list4);
558: assertEquals(
559: "The transition {a, b} -> {b, c} fires 1 add event.",
560: 1, report.eventCount());
561: assertEvent(
562: "The transition {a, b} -> {b, c} fires an add event with interval[0, 1].",
563: ListDataEvent.CONTENTS_CHANGED, 0, 1, report
564: .lastEvent());
565:
566: report.clearEventList();
567: listModelHolder.setListModel(list5);
568: assertEquals(
569: "The transition {b, c} -> {a, b, c} fires two events.",
570: 2, report.eventCount());
571: assertEvent(
572: "The transition {b, c} -> {a, b, c} fires an add event with interval[2, 2].",
573: ListDataEvent.INTERVAL_ADDED, 2, 2, report
574: .previousEvent());
575: assertEvent(
576: "The transition {b, c} -> {a, b, c} fires a contents changed with interval[0, 1].",
577: ListDataEvent.CONTENTS_CHANGED, 0, 1, report
578: .lastEvent());
579:
580: report.clearEventList();
581: listModelHolder.setListModel(list4);
582: assertEquals(
583: "The transition {a, b, c} -> {b, c} fires two events.",
584: 2, report.eventCount());
585: assertEvent(
586: "The transition {a, b, c} -> {b, c} fires a remove event with interval[2, 2].",
587: ListDataEvent.INTERVAL_REMOVED, 2, 2, report
588: .previousEvent());
589: assertEvent(
590: "The transition {a, b, c} -> {b, c} fires a contents changed with interval[0, 1].",
591: ListDataEvent.CONTENTS_CHANGED, 0, 1, report
592: .lastEvent());
593:
594: report.clearEventList();
595: listModelHolder.setListModel(list6);
596: assertEquals("The transition {b, c} -> {} fires one event.", 1,
597: report.eventCount());
598: assertEvent(
599: "The transition {b, c} -> {} fires a remove event with interval[0, 1].",
600: ListDataEvent.INTERVAL_REMOVED, 0, 1, report
601: .lastEvent());
602: }
603:
604: /**
605: * Tests that ListDataEvents fired during list changes
606: * provide size information that are consistent with the
607: * size of the list model.
608: * The transitions are {} -> {} -> {a, b} -> {b, c} -> {a, b, c} -> {b, c} -> {}.
609: */
610: public void testListDataEventsRetainListModelSizeConstraints() {
611: ListModel list1 = createListModel();
612: ListModel list2 = createListModel();
613: ListModel list3 = createListModel("a", "b");
614: ListModel list4 = createListModel("b", "c");
615: ListModel list5 = createListModel("a", "b", "c");
616: ListModel list6 = createListModel();
617:
618: IndirectListModel<Object> listModelHolder = new IndirectListModel<Object>(
619: list1);
620: listModelHolder
621: .addListDataListener(new ListSizeConstraintChecker(
622: listModelHolder.getSize()));
623:
624: listModelHolder.setListModel(list2);
625: listModelHolder.setListModel(list3);
626: listModelHolder.setListModel(list4);
627: listModelHolder.setListModel(list5);
628: listModelHolder.setListModel(list4);
629: listModelHolder.setListModel(list6);
630: }
631:
632: /**
633: * Tests the ListDataEvents fired during list changes.
634: * The transitions are {} -> {a} -> {b} -> {} on the same list.
635: */
636: public void testListDataEventsOnIndirectListModelChange() {
637: String element1 = "a";
638: String element2 = "b";
639: DeadListModel list = new DeadListModel();
640: ValueHolder listModelInModel = new ValueHolder(list, true);
641: IndirectListModel<String> listModelHolder = new IndirectListModel<String>(
642: listModelInModel);
643: ListDataReport report = new ListDataReport();
644: listModelHolder.addListDataListener(report);
645:
646: list.add(element1);
647: listModelInModel.fireValueChange(null, list);
648: assertEquals("The transition {} -> {a} fires 1 event.", 1,
649: report.eventCount());
650: assertEvent(
651: "The transition {} -> {a} fires an add event with interval[0, 0].",
652: ListDataEvent.INTERVAL_ADDED, 0, 0, report.lastEvent());
653: report.clearEventList();
654:
655: list.remove(element1);
656: list.add(element2);
657: listModelInModel.fireValueChange(null, list);
658: assertEquals("The transition {a} -> {b} fires 1 event.", 1,
659: report.eventCount());
660: assertEvent(
661: "The transition {a} -> {b} fires a contents changed event with interval[0, 0].",
662: ListDataEvent.CONTENTS_CHANGED, 0, 0, report
663: .lastEvent());
664: report.clearEventList();
665:
666: list.remove(element2);
667: listModelInModel.fireValueChange(null, list);
668: assertEquals("The transition {b} -> {} fires one event.", 1,
669: report.eventCount());
670: assertEvent(
671: "The transition {b} -> {} fires a remove event with interval[0, 0].",
672: ListDataEvent.INTERVAL_REMOVED, 0, 0, report
673: .lastEvent());
674: }
675:
676: /**
677: * Tests the ListDataEvents fired in list changes after elements
678: * have been added. Shall detect bugs where the ListHolder's internal
679: * listSize is unsynchronized with the list.
680: */
681: public void testListDataEventsOnListModelAdd() {
682: String element1 = "a";
683: String element2 = "b";
684: List<String> list1 = new ArrayListModel<String>();
685: List<String> list2 = new ArrayListModel<String>();
686: list2.add(element2);
687: ValueHolder listInModel = new ValueHolder(list1, true);
688: IndirectListModel<String> listModelHolder = new IndirectListModel<String>(
689: listInModel);
690: ListDataReport report = new ListDataReport();
691: listModelHolder.addListDataListener(report);
692:
693: list1.add(element1);
694: assertEquals("The transition {} -> {a} fires 1 event.", 1,
695: report.eventCount());
696: assertEvent(
697: "The transition {} -> {a} fires an add event with interval[0, 0].",
698: ListDataEvent.INTERVAL_ADDED, 0, 0, report.lastEvent());
699: report.clearEventList();
700:
701: listInModel.setValue(list2);
702: assertEquals("The transition {a} -> {b} fires 1 event.", 1,
703: report.eventCount());
704: assertEvent(
705: "The transition {a} -> {b} fires a contents changed event with interval[0, 0].",
706: ListDataEvent.CONTENTS_CHANGED, 0, 0, report
707: .lastEvent());
708: report.clearEventList();
709: }
710:
711: /**
712: * Tests the ListDataEvents fired in list changes after elements
713: * have been removed. Shall detect bugs where the ListHolder's internal
714: * listSize is unsynchronized with the list.
715: */
716: public void testListDataEventsOnListModelRemove() {
717: String element1 = "a";
718: String element2 = "b";
719: List<String> list1 = new ArrayListModel<String>();
720: list1.add(element1);
721: List<String> list2 = new ArrayListModel<String>();
722: list2.add(element2);
723: ValueHolder listInModel = new ValueHolder(list1, true);
724: IndirectListModel<String> listHolder = new IndirectListModel<String>(
725: listInModel);
726: ListDataReport report = new ListDataReport();
727: listHolder.addListDataListener(report);
728:
729: list1.remove(0);
730: assertEquals("The transition {a} -> {} fires 1 event.", 1,
731: report.eventCount());
732: assertEvent(
733: "The transition {a} -> {} fires a remove event with interval[0, 0].",
734: ListDataEvent.INTERVAL_REMOVED, 0, 0, report
735: .lastEvent());
736: report.clearEventList();
737:
738: listInModel.setValue(list2);
739: assertEquals("The transition {} -> {b} fires 1 event.", 1,
740: report.eventCount());
741: assertEvent(
742: "The transition {} -> {b} fires an add event with interval[0, 0].",
743: ListDataEvent.INTERVAL_ADDED, 0, 0, report.lastEvent());
744: report.clearEventList();
745: }
746:
747: private void assertEvent(String description, int eventType,
748: int index0, int index1, ListDataEvent event) {
749: assertEquals("Type: " + description, eventType, event.getType());
750: assertEquals("Index0: " + description, index0, event
751: .getIndex0());
752: assertEquals("Index1: " + description, index1, event
753: .getIndex1());
754: }
755:
756: // Helper Code ************************************************************
757:
758: private DefaultListModel createListModel(Object... elements) {
759: DefaultListModel model = new DefaultListModel();
760: for (Object element : elements) {
761: model.addElement(element);
762: }
763: return model;
764: }
765:
766: /**
767: * A ListModel that wraps a List and doesn't fire ListDataEvents
768: * on List content changes.
769: */
770: private static final class DeadListModel implements ListModel {
771:
772: private final List<String> list;
773:
774: DeadListModel() {
775: this .list = new ArrayList<String>();
776: }
777:
778: public int getSize() {
779: return list.size();
780: }
781:
782: public String getElementAt(int index) {
783: return list.get(index);
784: }
785:
786: public void add(String element) {
787: list.add(element);
788: }
789:
790: public void remove(String element) {
791: list.remove(element);
792: }
793:
794: public void addListDataListener(ListDataListener l) {
795: // Ignore; this ListModel doesn't fire events.
796: }
797:
798: public void removeListDataListener(ListDataListener l) {
799: // Ignore; this ListModel doesn't fire events.
800: }
801: }
802:
803: }
|