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