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 FloatBinding() { 175 { 176 super.bind(rawObservable); 177 } 178 179 @Override 180 protected float computeValue() { 181 return numberValue.floatValue(); 182 } 183 }; 184 } else { 185 newObservable = new FloatBinding() { 186 { 187 super.bind(rawObservable); 188 } 189 190 @Override 191 protected float computeValue() { 192 final Number value = rawObservable.getValue(); 193 return (value == null)? 0.0f : value.floatValue(); 194 } 195 }; 196 } 197 198 199 if (!newObservable.equals(observable)) { 200 unbind(); 201 observable = newObservable; 202 if (listener == null) { 203 listener = new Listener(this); 204 } 205 observable.addListener(listener); 206 markInvalid(); 207 } 208 } 209 210 /** 211 * {@inheritDoc} 212 */ 213 @Override 214 public void unbind() { 215 if (observable != null) { 216 value = observable.get(); 217 observable.removeListener(listener); 218 observable = null; 219 } 220 } 221 222 /** 223 * Returns a string representation of this {@code FloatPropertyBase} object. 224 * @return a string representation of this {@code FloatPropertyBase} object. 225 */ 226 @Override 227 public String toString() { 228 final Object bean = getBean(); 229 final String name = getName(); 230 final StringBuilder result = new StringBuilder("FloatProperty ["); 231 if (bean != null) { 232 result.append("bean: ").append(bean).append(", "); 233 } 234 if ((name != null) && (!name.equals(""))) { 235 result.append("name: ").append(name).append(", "); 236 } 237 if (isBound()) { 238 result.append("bound, "); 239 if (valid) { 240 result.append("value: ").append(get()); 241 } else { 242 result.append("invalid"); 243 } 244 } else { 245 result.append("value: ").append(get()); 246 } 247 result.append("]"); 248 return result.toString(); 249 } 250 251 private static class Listener implements InvalidationListener, WeakListener { 252 253 private final WeakReference<FloatPropertyBase> wref; 254 255 public Listener(FloatPropertyBase ref) { 256 this.wref = new WeakReference<>(ref); 257 } 258 259 @Override 260 public void invalidated(Observable observable) { 261 FloatPropertyBase ref = wref.get(); 262 if (ref == null) { 263 observable.removeListener(this); 264 } else { 265 ref.markInvalid(); 266 } 267 } 268 269 @Override 270 public boolean wasGarbageCollected() { 271 return wref.get() == null; 272 } 273 } 274 }