1 /*
   2  * Copyright (c) 2015, 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 javafx.beans.property;
  27 
  28 import com.sun.javafx.binding.ExpressionHelperUtility;
  29 import java.util.Arrays;
  30 import java.util.List;
  31 import javafx.beans.value.ObservableNumberValue;
  32 import javafx.beans.value.ObservableValue;
  33 import javafx.beans.value.ObservableValueBase;
  34 import static org.junit.Assert.assertEquals;
  35 import org.junit.Before;
  36 import org.junit.Test;
  37 import org.junit.runner.RunWith;
  38 import org.junit.runners.Parameterized;
  39 
  40 @RunWith(Parameterized.class)
  41 public class PropertyBaseTest<T> {
  42 
  43     @FunctionalInterface
  44     public interface PropertyFactory<T> {
  45         public Property<T> createProperty();
  46     }
  47 
  48     public static class Factory<T> {
  49 
  50         private PropertyFactory<T> propertyFactory;
  51         private PropertyFactory<T> observableFactory;
  52         private T value;
  53 
  54         public Factory(PropertyFactory<T> propertyFactory,
  55                 PropertyFactory<T> observableFactory, T value)
  56         {
  57             this.propertyFactory = propertyFactory;
  58             this.observableFactory = observableFactory;
  59             this.value = value;
  60         }
  61 
  62         public Property<T> createProperty() {
  63             return propertyFactory.createProperty();
  64         }
  65 
  66         public Property<T> createObservable() {
  67             return observableFactory.createProperty();
  68         }
  69 
  70         public T getValue() {
  71             return value;
  72         }
  73     }
  74 
  75     private static class NumberPropertyMock extends ObservableValueBase<Number>
  76             implements ObservableNumberValue, Property<Number>
  77     {
  78         private Number value = 0;
  79 
  80         @Override public int intValue()       { return value.intValue(); }
  81         @Override public long longValue()     { return value.longValue(); }
  82         @Override public float floatValue()   { return value.floatValue(); }
  83         @Override public double doubleValue() { return value.doubleValue(); }
  84         @Override public Number getValue()    { return value; }
  85         @Override public void setValue(Number value) {
  86             this.value = value;
  87             fireValueChangedEvent();
  88         }
  89 
  90         @Override public void bind(ObservableValue<? extends Number> observable) {}
  91         @Override public void unbind() {}
  92         @Override public boolean isBound() { return false; }
  93         @Override public void bindBidirectional(Property<Number> other) {}
  94         @Override public void unbindBidirectional(Property<Number> other) {}
  95 
  96         @Override public Object getBean() { return null; }
  97         @Override public String getName() { return ""; }
  98     }
  99 
 100     @Parameterized.Parameters
 101     public static List<Object[]> data() {
 102         return Arrays.asList(new Object[][] {
 103             // primitive binding
 104             // Property->Listener->Value
 105             { new Factory(() -> new SimpleBooleanProperty(), () -> new SimpleBooleanProperty(), true) },
 106             { new Factory(() -> new SimpleDoubleProperty(), () -> new SimpleDoubleProperty(), 1.0) },
 107             { new Factory(() -> new SimpleFloatProperty(), () -> new SimpleFloatProperty(), 1.0f) },
 108             { new Factory(() -> new SimpleIntegerProperty(), () -> new SimpleIntegerProperty(), 1) },
 109             { new Factory(() -> new SimpleLongProperty(), () -> new SimpleLongProperty(), 1L) },
 110             // generic with wrapper
 111             // Property->Listener->Binding->BindingHelperObserver->Value
 112             { new Factory(() -> new SimpleBooleanProperty(), () -> new SimpleObjectProperty<>(), true) },
 113             { new Factory(() -> new SimpleDoubleProperty(), () -> new SimpleObjectProperty<>(), 1.0) },
 114             { new Factory(() -> new SimpleDoubleProperty(), () -> new NumberPropertyMock(), 1.0) },
 115             { new Factory(() -> new SimpleFloatProperty(), () -> new SimpleObjectProperty<>(), 1.0f) },
 116             { new Factory(() -> new SimpleFloatProperty(), () -> new NumberPropertyMock(), 1.0f) },
 117             { new Factory(() -> new SimpleIntegerProperty(), () -> new SimpleObjectProperty<>(), 1) },
 118             { new Factory(() -> new SimpleIntegerProperty(), () -> new NumberPropertyMock(), 1) },
 119             { new Factory(() -> new SimpleLongProperty(), () -> new SimpleObjectProperty<>(), 1L) },
 120             { new Factory(() -> new SimpleLongProperty(), () -> new NumberPropertyMock(), 1L) },
 121             // generic
 122             // Property->Listener->Value
 123             { new Factory(() -> new SimpleObjectProperty(), () -> new SimpleObjectProperty<>(), new Object()) },
 124             { new Factory(() -> new SimpleStringProperty(), () -> new SimpleObjectProperty<>(), "1") },
 125             // the same as generic
 126             { new Factory(() -> new SimpleStringProperty(), () -> new SimpleStringProperty(), "1") },
 127         });
 128     }
 129 
 130     public PropertyBaseTest(Factory<T> factory) {
 131         this.factory = factory;
 132     }
 133 
 134     @Before
 135     public void setUp() {
 136         property = factory.createProperty();
 137         observable = factory.createObservable();
 138         value = factory.getValue();
 139     }
 140 
 141     private Factory<T> factory;
 142     private Property<T> property;
 143     private Property<T> observable;
 144     private T value;
 145 
 146     @Test
 147     public void testUnbindAfterInvalidation() {
 148         property.bind(observable);
 149         assertEquals(1, ExpressionHelperUtility.getInvalidationListeners(observable).size());
 150 
 151         property = null;
 152         System.gc();
 153 
 154         observable.setValue(value);
 155         assertEquals(0, ExpressionHelperUtility.getInvalidationListeners(observable).size());
 156     }
 157 
 158     @Test
 159     public void testTrimAfterGC() {
 160         Property<T> p1 = factory.createProperty();
 161         Property<T> p2 = factory.createProperty();
 162         p1.bind(observable); // creates SingleInvalidation
 163         p2.bind(observable); // creates Generic with 2 listeners
 164         assertEquals(2, ExpressionHelperUtility.getInvalidationListeners(observable).size());
 165 
 166         p1 = null;
 167         p2 = null;
 168         System.gc();
 169 
 170         property.bind(observable); // calls trim
 171         assertEquals(1, ExpressionHelperUtility.getInvalidationListeners(observable).size());
 172     }
 173 
 174     @Test
 175     public void testUnbindGenericWrapper() {
 176         property.bind(observable);
 177         assertEquals(1, ExpressionHelperUtility.getInvalidationListeners(observable).size());
 178 
 179         property.unbind(); // should unbind wrapper from observable
 180         assertEquals(0, ExpressionHelperUtility.getInvalidationListeners(observable).size());
 181     }
 182 }