1 /* 2 * Copyright (c) 2010, 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 javafx.beans.binding; 27 28 import javafx.beans.InvalidationListener; 29 import javafx.beans.Observable; 30 import javafx.beans.value.ChangeListener; 31 import javafx.collections.FXCollections; 32 import javafx.collections.ObservableList; 33 import com.sun.javafx.collections.annotations.ReturnsUnmodifiableCollection; 34 35 import com.sun.javafx.binding.BindingHelperObserver; 36 import com.sun.javafx.binding.ExpressionHelper; 37 38 /** 39 * Base class that provides most of the functionality needed to implement a 40 * {@link Binding} of a {@code double} value. 41 * <p> 42 * {@code DoubleBinding} provides a simple invalidation-scheme. An extending 43 * class can register dependencies by calling {@link #bind(Observable...)}. 44 * If One of the registered dependencies becomes invalid, this 45 * {@code DoubleBinding} is marked as invalid. With 46 * {@link #unbind(Observable...)} listening to dependencies can be stopped. 47 * <p> 48 * To provide a concrete implementation of this class, the method 49 * {@link #computeValue()} has to be implemented to calculate the value of this 50 * binding based on the current state of the dependencies. It is called when 51 * {@link #get()} is called for an invalid binding. 52 * <p> 53 * Below is a simple example of a {@code DoubleBinding} calculating the square- 54 * root of a {@link javafx.beans.value.ObservableNumberValue} {@code moo}. 55 * 56 * <pre> 57 * <code> 58 * final ObservableDoubleValue moo = ...; 59 * 60 * DoubleBinding foo = new DoubleBinding() { 61 * 62 * { 63 * super.bind(moo); 64 * } 65 * 66 * @Override 67 * protected double computeValue() { 68 * return Math.sqrt(moo.getValue()); 69 * } 70 * }; 71 * </code> 72 * </pre> 73 * 74 * Following is the same example with implementations for the optional methods 75 * {@link Binding#getDependencies()} and {@link Binding#dispose()}. 76 * 77 * <pre> 78 * <code> 79 * final ObservableDoubleValue moo = ...; 80 * 81 * DoubleBinding foo = new DoubleBinding() { 82 * 83 * { 84 * super.bind(moo); 85 * } 86 * 87 * @Override 88 * protected double computeValue() { 89 * return Math.sqrt(moo.getValue()); 90 * } 91 * 92 * @Override 93 * public ObservableList<?> getDependencies() { 94 * return FXCollections.singletonObservableList(moo); 95 * } 96 * 97 * @Override 98 * public void dispose() { 99 * super.unbind(moo); 100 * } 101 * }; 102 * </code> 103 * </pre> 104 * 105 * @see Binding 106 * @see NumberBinding 107 * @see javafx.beans.binding.DoubleExpression 108 * 109 * 110 * @since JavaFX 2.0 111 */ 112 public abstract class DoubleBinding extends DoubleExpression implements 113 NumberBinding { 114 115 private double value; 116 private boolean valid; 117 private BindingHelperObserver observer; 118 private ExpressionHelper<Number> helper = null; 119 120 @Override 121 public void addListener(InvalidationListener listener) { 122 helper = ExpressionHelper.addListener(helper, this, listener); 123 } 124 125 @Override 126 public void removeListener(InvalidationListener listener) { 127 helper = ExpressionHelper.removeListener(helper, listener); 128 } 129 130 @Override 131 public void addListener(ChangeListener<? super Number> listener) { 132 helper = ExpressionHelper.addListener(helper, this, listener); 133 } 134 135 @Override 136 public void removeListener(ChangeListener<? super Number> listener) { 137 helper = ExpressionHelper.removeListener(helper, listener); 138 } 139 140 /** 141 * Start observing the dependencies for changes. If the value of one of the 142 * dependencies changes, the binding is marked as invalid. 143 * 144 * @param dependencies 145 * the dependencies to observe 146 */ 147 protected final void bind(Observable... dependencies) { 148 if ((dependencies != null) && (dependencies.length > 0)) { 149 if (observer == null) { 150 observer = new BindingHelperObserver(this); 151 } 152 for (final Observable dep : dependencies) { 153 dep.addListener(observer); 154 } 155 } 156 } 157 158 /** 159 * Stop observing the dependencies for changes. 160 * 161 * @param dependencies 162 * the dependencies to stop observing 163 */ 164 protected final void unbind(Observable... dependencies) { 165 if (observer != null) { 166 for (final Observable dep : dependencies) { 167 dep.removeListener(observer); 168 } 169 observer = null; 170 } 171 } 172 173 /** 174 * A default implementation of {@code dispose()} that is empty. 175 */ 176 @Override 177 public void dispose() { 178 } 179 180 /** 181 * A default implementation of {@code getDependencies()} that returns an 182 * empty {@link javafx.collections.ObservableList}. 183 * 184 * @return an empty {@code ObservableList} 185 */ 186 @Override 187 @ReturnsUnmodifiableCollection 188 public ObservableList<?> getDependencies() { 189 return FXCollections.emptyObservableList(); 190 } 191 192 /** 193 * Returns the result of {@link #computeValue()}. The method 194 * {@code computeValue()} is only called if the binding is invalid. The 195 * result is cached and returned if the binding did not become invalid since 196 * the last call of {@code get()}. 197 * 198 * @return the current value 199 */ 200 @Override 201 public final double get() { 202 if (!valid) { 203 value = computeValue(); 204 valid = true; 205 } 206 return value; 207 } 208 209 /** 210 * The method onInvalidating() can be overridden by extending classes to 211 * react, if this binding becomes invalid. The default implementation is 212 * empty. 213 */ 214 protected void onInvalidating() { 215 } 216 217 @Override 218 public final void invalidate() { 219 if (valid) { 220 valid = false; 221 onInvalidating(); 222 ExpressionHelper.fireValueChangedEvent(helper); 223 } 224 } 225 226 @Override 227 public final boolean isValid() { 228 return valid; 229 } 230 231 /** 232 * Calculates the current value of this binding. 233 * <p> 234 * Classes extending {@code DoubleBinding} have to provide an implementation 235 * of {@code computeValue}. 236 * 237 * @return the current value 238 */ 239 protected abstract double computeValue(); 240 241 /** 242 * Returns a string representation of this {@code DoubleBinding} object. 243 * @return a string representation of this {@code DoubleBinding} object. 244 */ 245 @Override 246 public String toString() { 247 return valid ? "DoubleBinding [value: " + get() + "]" 248 : "DoubleBinding [invalid]"; 249 } 250 }