1 /*
   2  * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package test.javafx.binding;
  27 
  28 import com.sun.javafx.collections.annotations.ReturnsUnmodifiableCollection;
  29 import test.javafx.beans.InvalidationListenerMock;
  30 import javafx.beans.Observable;
  31 import javafx.beans.binding.ListBinding;
  32 import javafx.beans.property.ReadOnlyBooleanProperty;
  33 import javafx.beans.property.ReadOnlyIntegerProperty;
  34 import test.javafx.beans.value.ChangeListenerMock;
  35 import javafx.beans.value.ObservableValueBase;
  36 import javafx.collections.FXCollections;
  37 import javafx.collections.ListChangeListener;
  38 import javafx.collections.ObservableList;
  39 import org.junit.Before;
  40 import org.junit.Test;
  41 
  42 import java.util.Collections;
  43 
  44 import static org.junit.Assert.*;
  45 import static org.junit.Assert.assertEquals;
  46 
  47 /**
  48  */
  49 public class ListBindingTest {
  50 
  51     private ObservableStub dependency1;
  52     private ObservableStub dependency2;
  53 
  54     private ListBindingImpl binding0;
  55     private ListBindingImpl binding1;
  56     private ListBindingImpl binding2;
  57 
  58     private ObservableList<Object> emptyList;
  59     private ObservableList<Object> list1;
  60     private ObservableList<Object> list2;
  61 
  62     private ListChangeListenerMock listener;
  63 
  64     @Before
  65     public void setUp() {
  66         dependency1 = new ObservableStub();
  67         dependency2 = new ObservableStub();
  68         binding0 = new ListBindingImpl();
  69         binding1 = new ListBindingImpl(dependency1);
  70         binding2 = new ListBindingImpl(dependency1, dependency2);
  71         emptyList = FXCollections.observableArrayList();
  72         list1 = FXCollections.observableArrayList(new Object());
  73         list2 = FXCollections.observableArrayList(new Object(), new Object());
  74         listener = new ListChangeListenerMock();
  75         binding0.setValue(list2);
  76         binding1.setValue(list2);
  77         binding2.setValue(list2);
  78     }
  79 
  80     @Test
  81     public void testSizeProperty() {
  82         assertEquals(binding0, binding0.sizeProperty().getBean());
  83         assertEquals(binding1, binding1.sizeProperty().getBean());
  84         assertEquals(binding2, binding2.sizeProperty().getBean());
  85 
  86         final ReadOnlyIntegerProperty size = binding1.sizeProperty();
  87         assertEquals("size", size.getName());
  88 
  89         assertEquals(2, size.get());
  90         binding1.setValue(emptyList);
  91         dependency1.fireValueChangedEvent();
  92         assertEquals(0, size.get());
  93         binding1.setValue(null);
  94         dependency1.fireValueChangedEvent();
  95         assertEquals(0, size.get());
  96         binding1.setValue(list1);
  97         dependency1.fireValueChangedEvent();
  98         assertEquals(1, size.get());
  99     }
 100 
 101     @Test
 102     public void testEmptyProperty() {
 103         assertEquals(binding0, binding0.emptyProperty().getBean());
 104         assertEquals(binding1, binding1.emptyProperty().getBean());
 105         assertEquals(binding2, binding2.emptyProperty().getBean());
 106 
 107         final ReadOnlyBooleanProperty empty = binding1.emptyProperty();
 108         assertEquals("empty", empty.getName());
 109 
 110         assertFalse(empty.get());
 111         binding1.setValue(emptyList);
 112         dependency1.fireValueChangedEvent();
 113         assertTrue(empty.get());
 114         binding1.setValue(null);
 115         dependency1.fireValueChangedEvent();
 116         assertTrue(empty.get());
 117         binding1.setValue(list1);
 118         dependency1.fireValueChangedEvent();
 119         assertFalse(empty.get());
 120     }
 121 
 122     @Test
 123     public void testNoDependency_ListChangeListener() {
 124         binding0.getValue();
 125         binding0.addListener(listener);
 126         System.gc(); // making sure we did not not overdo weak references
 127         assertEquals(true, binding0.isValid());
 128 
 129         // calling getValue()
 130         binding0.reset();
 131         binding0.getValue();
 132         assertEquals(0, binding0.getComputeValueCounter());
 133         listener.checkNotCalled();
 134         assertEquals(true, binding0.isValid());
 135     }
 136 
 137     @Test
 138     public void testSingleDependency_ListChangeListener() {
 139         binding1.getValue();
 140         binding1.addListener(listener);
 141         System.gc(); // making sure we did not not overdo weak references
 142         assertEquals(true, binding1.isValid());
 143 
 144         // fire single change event
 145         binding1.reset();
 146         listener.reset();
 147         binding1.setValue(list1);
 148         dependency1.fireValueChangedEvent();
 149         assertEquals(1, binding1.getComputeValueCounter());
 150         listener.check(list2, list1, 1);
 151         assertEquals(true, binding1.isValid());
 152 
 153         binding1.getValue();
 154         assertEquals(0, binding1.getComputeValueCounter());
 155         listener.checkNotCalled();
 156         assertEquals(true, binding1.isValid());
 157 
 158         // fire single change event with same value
 159         binding1.setValue(list1);
 160         dependency1.fireValueChangedEvent();
 161         assertEquals(1, binding1.getComputeValueCounter());
 162         listener.checkNotCalled();
 163         assertEquals(true, binding1.isValid());
 164 
 165         binding1.getValue();
 166         assertEquals(0, binding1.getComputeValueCounter());
 167         listener.checkNotCalled();
 168         assertEquals(true, binding1.isValid());
 169 
 170         // fire two change events
 171         binding1.setValue(list2);
 172         dependency1.fireValueChangedEvent();
 173         binding1.setValue(list1);
 174         dependency1.fireValueChangedEvent();
 175         assertEquals(2, binding1.getComputeValueCounter());
 176         listener.check(list2, list1, 2);
 177         assertEquals(true, binding1.isValid());
 178 
 179         binding1.getValue();
 180         assertEquals(0, binding1.getComputeValueCounter());
 181         listener.checkNotCalled();
 182         assertEquals(true, binding1.isValid());
 183 
 184         // fire two change events with same value
 185         binding1.setValue(list2);
 186         dependency1.fireValueChangedEvent();
 187         binding1.setValue(list2);
 188         dependency1.fireValueChangedEvent();
 189         assertEquals(2, binding1.getComputeValueCounter());
 190         listener.check(list1, list2, 1);
 191         assertEquals(true, binding1.isValid());
 192 
 193         binding1.getValue();
 194         assertEquals(0, binding1.getComputeValueCounter());
 195         listener.checkNotCalled();
 196         assertEquals(true, binding1.isValid());
 197     }
 198 
 199     @Test
 200     public void testChangeContent_InvalidationListener() {
 201         final InvalidationListenerMock listenerMock = new InvalidationListenerMock();
 202         binding1.get();
 203         binding1.addListener(listenerMock);
 204         assertTrue(binding1.isValid());
 205 
 206         binding1.reset();
 207         listenerMock.reset();
 208         list2.add(new Object());
 209         assertEquals(0, binding1.getComputeValueCounter());
 210         listenerMock.check(binding1, 1);
 211         assertTrue(binding1.isValid());
 212     }
 213 
 214     @Test
 215     public void testChangeContent_ChangeListener() {
 216         final ChangeListenerMock listenerMock = new ChangeListenerMock(null);
 217         binding1.get();
 218         binding1.addListener(listenerMock);
 219         assertTrue(binding1.isValid());
 220 
 221         binding1.reset();
 222         listenerMock.reset();
 223         list2.add(new Object());
 224         assertEquals(0, binding1.getComputeValueCounter());
 225         listenerMock.check(binding1, list2, list2, 1);
 226         assertTrue(binding1.isValid());
 227     }
 228 
 229     @Test
 230     public void testChangeContent_ListChangeListener() {
 231         binding1.get();
 232         binding1.addListener(listener);
 233         assertTrue(binding1.isValid());
 234 
 235         final int oldSize = list2.size();
 236         final Object newObject = new Object();
 237         binding1.reset();
 238         listener.reset();
 239         list2.add(newObject);
 240         assertEquals(0, binding1.getComputeValueCounter());
 241         listener.check(oldSize, newObject, 1);
 242         assertTrue(binding1.isValid());
 243     }
 244 
 245     public static class ObservableStub extends ObservableValueBase<Object> {
 246         @Override public void fireValueChangedEvent() {super.fireValueChangedEvent();}
 247 
 248         @Override
 249         public Object getValue() {
 250             return null;
 251         }
 252     }
 253 
 254     private static class ListBindingImpl extends ListBinding<Object> {
 255 
 256         private int computeValueCounter = 0;
 257         private ObservableList<Object> value;
 258 
 259         public void setValue(ObservableList<Object> value) {
 260             this.value = value;
 261         }
 262 
 263         public ListBindingImpl(Observable... dep) {
 264             super.bind(dep);
 265         }
 266 
 267         public int getComputeValueCounter() {
 268             final int result = computeValueCounter;
 269             reset();
 270             return result;
 271         }
 272 
 273         public void reset() {
 274             computeValueCounter = 0;
 275         }
 276 
 277         @Override
 278         public ObservableList<Object> computeValue() {
 279             computeValueCounter++;
 280             return value;
 281         }
 282 
 283         @Override @ReturnsUnmodifiableCollection
 284         public ObservableList<?> getDependencies() {
 285             fail("Should not reach here");
 286             return null;
 287         }
 288     }
 289 
 290 
 291     private class ListChangeListenerMock implements ListChangeListener<Object> {
 292         
 293         private Change<? extends Object> change;
 294         private int counter;
 295 
 296         @Override
 297         public void onChanged(Change<? extends Object> change) {
 298             this.change = change;
 299             counter++;
 300         }
 301         
 302         private void reset() {
 303             change = null;
 304             counter = 0;
 305         }
 306 
 307         private void checkNotCalled() {
 308             assertEquals(null, change);
 309             assertEquals(0, counter);
 310             reset();
 311         }
 312 
 313         private void check(ObservableList<Object> oldList, ObservableList<Object> newList, int counter) {
 314             assertTrue(change.next());
 315             assertTrue(change.wasReplaced());
 316             assertEquals(oldList, change.getRemoved());
 317             assertEquals(newList, change.getList());
 318             assertFalse(change.next());
 319             assertEquals(counter, this.counter);
 320             reset();
 321         }
 322 
 323         private void check(int pos, Object newObject, int counter) {
 324             assertTrue(change.next());
 325             assertTrue(change.wasAdded());
 326             assertEquals(pos, change.getFrom());
 327             assertEquals(Collections.singletonList(newObject), change.getAddedSubList());
 328             assertFalse(change.next());
 329             assertEquals(counter, this.counter);
 330             reset();
 331         }
 332     }
 333 }