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.FloatBinding; 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.ObservableFloatValue; 38 import javafx.beans.value.ObservableNumberValue; 39 40 /** 41 * The class {@code FloatPropertyBase} is the base class for a property wrapping 42 * a {@code float} 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 FloatProperty 49 * 50 * 51 * @since JavaFX 2.0 52 */ 53 public abstract class FloatPropertyBase extends FloatProperty { 54 55 private float value; 56 private ObservableFloatValue 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 FloatPropertyBase}. 63 */ 64 public FloatPropertyBase() { 65 } 66 67 /** 68 * The constructor of the {@code FloatPropertyBase}. 69 * 70 * @param initialValue 71 * the initial value of the wrapped value 72 */ 73 public FloatPropertyBase(float 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(float)} 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 float get() { 133 valid = true; 134 return observable == null ? value : observable.get(); 135 } 136 137 /** 138 * {@inheritDoc} 139 */ 140 @Override 141 public void set(float 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 ObservableFloatValue newObservable; 170 if (rawObservable instanceof ObservableFloatValue) { 171 newObservable = (ObservableFloatValue)rawObservable; 172 } else if (rawObservable instanceof ObservableNumberValue) { 173 final ObservableNumberValue numberValue = (ObservableNumberValue)rawObservable; 174 newObservable = new ValueWrapper(rawObservable) { 175 176 @Override 177 protected float computeValue() { 178 return numberValue.floatValue(); 179 } 180 }; 181 } else { 182 newObservable = new ValueWrapper(rawObservable) { 183 184 @Override 185 protected float computeValue() { 186 final Number value = rawObservable.getValue(); 187 return (value == null)? 0.0f : value.floatValue(); 188 } 189 }; 190 } 191 192 193 if (!newObservable.equals(observable)) { 194 unbind(); 195 observable = newObservable; 196 if (listener == null) { 197 listener = new Listener(this); 198 } 199 observable.addListener(listener); 200 markInvalid(); 201 } 202 } 203 204 /** 205 * {@inheritDoc} 206 */ 207 @Override 208 public void unbind() { 209 if (observable != null) { 210 value = observable.get(); 211 observable.removeListener(listener); 212 if (observable instanceof ValueWrapper) { 213 ((ValueWrapper)observable).dispose(); 214 } 215 observable = null; 216 } 217 } 218 219 /** 220 * Returns a string representation of this {@code FloatPropertyBase} object. 221 * @return a string representation of this {@code FloatPropertyBase} object. 222 */ 223 @Override 224 public String toString() { 225 final Object bean = getBean(); 226 final String name = getName(); 227 final StringBuilder result = new StringBuilder("FloatProperty ["); 228 if (bean != null) { 229 result.append("bean: ").append(bean).append(", "); 230 } 231 if ((name != null) && (!name.equals(""))) { 232 result.append("name: ").append(name).append(", "); 233 } 234 if (isBound()) { 235 result.append("bound, "); 236 if (valid) { 237 result.append("value: ").append(get()); 238 } else { 239 result.append("invalid"); 240 } 241 } else { 242 result.append("value: ").append(get()); 243 } 244 result.append("]"); 245 return result.toString(); 246 } 247 248 private static class Listener implements InvalidationListener, WeakListener { 249 250 private final WeakReference<FloatPropertyBase> wref; 251 252 public Listener(FloatPropertyBase ref) { 253 this.wref = new WeakReference<>(ref); 254 } 255 256 @Override 257 public void invalidated(Observable observable) { 258 FloatPropertyBase ref = wref.get(); 259 if (ref == null) { 260 observable.removeListener(this); 261 } else { 262 ref.markInvalid(); 263 } 264 } 265 266 @Override 267 public boolean wasGarbageCollected() { 268 return wref.get() == null; 269 } 270 } 271 272 private abstract class ValueWrapper extends FloatBinding { 273 274 private ObservableValue<? extends Number> observable; 275 276 public ValueWrapper(ObservableValue<? extends Number> observable) { 277 this.observable = observable; 278 bind(observable); 279 } 280 281 @Override 282 public void dispose() { 283 unbind(observable); 284 } 285 } 286 }