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 }