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 }