1 /* 2 * Copyright (c) 2011, 2014, 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 javafx.beans.InvalidationListener; 29 import javafx.beans.Observable; 30 import javafx.beans.binding.DoubleBinding; 31 import javafx.beans.value.ChangeListener; 32 import javafx.beans.value.ObservableValue; 33 34 import com.sun.javafx.binding.ExpressionHelper; 35 import java.lang.ref.WeakReference; 36 import javafx.beans.WeakListener; 37 import javafx.beans.value.ObservableDoubleValue; 38 import javafx.beans.value.ObservableNumberValue; 39 40 /** 41 * The class {@code DoublePropertyBase} is the base class for a property 42 * wrapping a {@code double} value. 43 * 44 * It provides all the functionality required for a property except for the 45 * {@link #getBean()} and {@link #getName()} methods, which must be implemented 46 * by extending classes. 47 * 48 * @see DoubleProperty 49 * 50 * 51 * @since JavaFX 2.0 52 */ 53 public abstract class DoublePropertyBase extends DoubleProperty { 54 55 private double value; 56 private ObservableDoubleValue observable = null; 57 private InvalidationListener listener = null; 58 private boolean valid = true; 59 private ExpressionHelper<Number> helper = null; 60 61 /** 62 * The constructor of the {@code DoublePropertyBase}. 63 */ 64 public DoublePropertyBase() { 65 } 66 67 /** 68 * The constructor of the {@code DoublePropertyBase}. 69 * 70 * @param initialValue 71 * the initial value of the wrapped value 72 */ 73 public DoublePropertyBase(double initialValue) { 74 this.value = initialValue; 75 } 76 77 @Override 78 public void addListener(InvalidationListener listener) { 79 helper = ExpressionHelper.addListener(helper, this, listener); 80 } 81 82 @Override 83 public void removeListener(InvalidationListener listener) { 84 helper = ExpressionHelper.removeListener(helper, listener); 85 } 86 87 @Override 88 public void addListener(ChangeListener<? super Number> listener) { 89 helper = ExpressionHelper.addListener(helper, this, listener); 90 } 91 92 @Override 93 public void removeListener(ChangeListener<? super Number> listener) { 94 helper = ExpressionHelper.removeListener(helper, listener); 95 } 96 97 /** 98 * Sends notifications to all attached 99 * {@link javafx.beans.InvalidationListener InvalidationListeners} and 100 * {@link javafx.beans.value.ChangeListener ChangeListeners}. 101 * 102 * This method is called when the value is changed, either manually by 103 * calling {@link #set(double)} or in case of a bound property, if the 104 * binding becomes invalid. 105 */ 106 protected void fireValueChangedEvent() { 107 ExpressionHelper.fireValueChangedEvent(helper); 108 } 109 110 private void markInvalid() { 111 if (valid) { 112 valid = false; 113 invalidated(); 114 fireValueChangedEvent(); 115 } 116 } 117 118 /** 119 * The method {@code invalidated()} can be overridden to receive 120 * invalidation notifications. This is the preferred option in 121 * {@code Objects} defining the property, because it requires less memory. 122 * 123 * The default implementation is empty. 124 */ 125 protected void invalidated() { 126 } 127 128 /** 129 * {@inheritDoc} 130 */ 131 @Override 132 public double get() { 133 valid = true; 134 return observable == null ? value : observable.get(); 135 } 136 137 /** 138 * {@inheritDoc} 139 */ 140 @Override 141 public void set(double newValue) { 142 if (isBound()) { 143 throw new java.lang.RuntimeException((getBean() != null && getName() != null ? 144 getBean().getClass().getSimpleName() + "." + getName() + " : ": "") + "A bound value cannot be set."); 145 } 146 if (value != newValue) { 147 value = newValue; 148 markInvalid(); 149 } 150 } 151 152 /** 153 * {@inheritDoc} 154 */ 155 @Override 156 public boolean isBound() { 157 return observable != null; 158 } 159 160 /** 161 * {@inheritDoc} 162 */ 163 @Override 164 public void bind(final ObservableValue<? extends Number> rawObservable) { 165 if (rawObservable == null) { 166 throw new NullPointerException("Cannot bind to null"); 167 } 168 169 ObservableDoubleValue newObservable; 170 if (rawObservable instanceof ObservableDoubleValue) { 171 newObservable = (ObservableDoubleValue)rawObservable; 172 } else if (rawObservable instanceof ObservableNumberValue) { 173 final ObservableNumberValue numberValue = (ObservableNumberValue)rawObservable; 174 newObservable = new DoubleBinding() { 175 { 176 super.bind(rawObservable); 177 } 178 179 @Override 180 protected double computeValue() { 181 return numberValue.doubleValue(); 182 } 183 }; 184 } else { 185 newObservable = new DoubleBinding() { 186 { 187 super.bind(rawObservable); 188 } 189 190 @Override 191 protected double computeValue() { 192 final Number value = rawObservable.getValue(); 193 return (value == null)? 0.0 : value.doubleValue(); 194 } 195 }; 196 } 197 198 if (!newObservable.equals(observable)) { 199 unbind(); 200 observable = newObservable; 201 if (listener == null) { 202 listener = new Listener(this); 203 } 204 observable.addListener(listener); 205 markInvalid(); 206 } 207 } 208 209 /** 210 * {@inheritDoc} 211 */ 212 @Override 213 public void unbind() { 214 if (observable != null) { 215 value = observable.get(); 216 observable.removeListener(listener); 217 observable = null; 218 } 219 } 220 221 /** 222 * Returns a string representation of this {@code DoublePropertyBase} object. 223 * @return a string representation of this {@code DoublePropertyBase} object. 224 */ 225 @Override 226 public String toString() { 227 final Object bean = getBean(); 228 final String name = getName(); 229 final StringBuilder result = new StringBuilder("DoubleProperty ["); 230 if (bean != null) { 231 result.append("bean: ").append(bean).append(", "); 232 } 233 if ((name != null) && (!name.equals(""))) { 234 result.append("name: ").append(name).append(", "); 235 } 236 if (isBound()) { 237 result.append("bound, "); 238 if (valid) { 239 result.append("value: ").append(get()); 240 } else { 241 result.append("invalid"); 242 } 243 } else { 244 result.append("value: ").append(get()); 245 } 246 result.append("]"); 247 return result.toString(); 248 } 249 250 private static class Listener implements InvalidationListener, WeakListener { 251 252 private final WeakReference<DoublePropertyBase> wref; 253 254 public Listener(DoublePropertyBase ref) { 255 this.wref = new WeakReference<>(ref); 256 } 257 258 @Override 259 public void invalidated(Observable observable) { 260 DoublePropertyBase ref = wref.get(); 261 if (ref == null) { 262 observable.removeListener(this); 263 } else { 264 ref.markInvalid(); 265 } 266 } 267 268 @Override 269 public boolean wasGarbageCollected() { 270 return wref.get() == null; 271 } 272 } 273 }