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.BooleanBinding;
  31 import javafx.beans.value.ChangeListener;
  32 import javafx.beans.value.ObservableBooleanValue;
  33 import javafx.beans.value.ObservableValue;
  34 
  35 import com.sun.javafx.binding.ExpressionHelper;
  36 import java.lang.ref.WeakReference;
  37 import javafx.beans.WeakListener;
  38 
  39 /**
  40  * The class {@code BooleanPropertyBase} is the base class for a property
  41  * wrapping a {@code boolean} value.
  42  *
  43  * It provides all the functionality required for a property except for the
  44  * {@link #getBean()} and {@link #getName()} methods, which must be implemented
  45  * by extending classes.
  46  *
  47  * @see BooleanProperty
  48  * @since JavaFX 2.0
  49  */
  50 public abstract class BooleanPropertyBase extends BooleanProperty {
  51 
  52     private boolean value;
  53     private ObservableBooleanValue observable = null;
  54     private InvalidationListener listener = null;
  55     private boolean valid = true;
  56     private ExpressionHelper<Boolean> helper = null;
  57 
  58     /**
  59      * The constructor of the {@code BooleanPropertyBase}.
  60      */
  61     public BooleanPropertyBase() {
  62     }
  63 
  64     /**
  65      * The constructor of the {@code BooleanPropertyBase}.
  66      *
  67      * @param initialValue
  68      *            the initial value of the wrapped value
  69      */
  70     public BooleanPropertyBase(boolean initialValue) {
  71         this.value = initialValue;
  72     }
  73 
  74     @Override
  75     public void addListener(InvalidationListener listener) {
  76         helper = ExpressionHelper.addListener(helper, this, listener);
  77     }
  78 
  79     @Override
  80     public void removeListener(InvalidationListener listener) {
  81         helper = ExpressionHelper.removeListener(helper, listener);
  82     }
  83 
  84     @Override
  85     public void addListener(ChangeListener<? super Boolean> listener) {
  86         helper = ExpressionHelper.addListener(helper, this, listener);
  87     }
  88 
  89     @Override
  90     public void removeListener(ChangeListener<? super Boolean> listener) {
  91         helper = ExpressionHelper.removeListener(helper, listener);
  92     }
  93 
  94     /**
  95      * Sends notifications to all attached
  96      * {@link javafx.beans.InvalidationListener InvalidationListeners} and
  97      * {@link javafx.beans.value.ChangeListener ChangeListeners}.
  98      *
  99      * This method is called when the value is changed, either manually by
 100      * calling {@link #set(boolean)} or in case of a bound property, if the
 101      * binding becomes invalid.
 102      */
 103     protected void fireValueChangedEvent() {
 104         ExpressionHelper.fireValueChangedEvent(helper);
 105     }
 106 
 107     private void markInvalid() {
 108         if (valid) {
 109             valid = false;
 110             invalidated();
 111             fireValueChangedEvent();
 112         }
 113     }
 114 
 115     /**
 116      * The method {@code invalidated()} can be overridden to receive
 117      * invalidation notifications. This is the preferred option in
 118      * {@code Objects} defining the property, because it requires less memory.
 119      *
 120      * The default implementation is empty.
 121      */
 122     protected void invalidated() {
 123     }
 124 
 125     /**
 126      * {@inheritDoc}
 127      */
 128     @Override
 129     public boolean get() {
 130         valid = true;
 131         return observable == null ? value : observable.get();
 132     }
 133 
 134     /**
 135      * {@inheritDoc}
 136      */
 137     @Override
 138     public void set(boolean newValue) {
 139         if (isBound()) {
 140             throw new java.lang.RuntimeException((getBean() != null && getName() != null ?
 141                     getBean().getClass().getSimpleName() + "." + getName() + " : ": "") + "A bound value cannot be set.");
 142         }
 143         if (value != newValue) {
 144             value = newValue;
 145             markInvalid();
 146         }
 147     }
 148 
 149     /**
 150      * {@inheritDoc}
 151      */
 152     @Override
 153     public boolean isBound() {
 154         return observable != null;
 155     }
 156 
 157     /**
 158      * {@inheritDoc}
 159      * Note:
 160      */
 161     @Override
 162     public void bind(final ObservableValue<? extends Boolean> rawObservable) {
 163         if (rawObservable == null) {
 164             throw new NullPointerException("Cannot bind to null");
 165         }
 166 
 167         final ObservableBooleanValue newObservable = (rawObservable instanceof ObservableBooleanValue) ? (ObservableBooleanValue) rawObservable
 168                 : new BooleanBinding() {
 169                     {
 170                         super.bind(rawObservable);
 171                     }
 172 
 173                     @Override
 174                     protected boolean computeValue() {
 175                         final Boolean value = rawObservable.getValue();
 176                         return (value == null)? false : value;
 177                     }
 178                 };
 179 
 180         if (!newObservable.equals(observable)) {
 181             unbind();
 182             observable = newObservable;
 183             if (listener == null) {
 184                 listener = new Listener(this);
 185             }
 186             observable.addListener(listener);
 187             markInvalid();
 188         }
 189     }
 190 
 191     /**
 192      * {@inheritDoc}
 193      */
 194     @Override
 195     public void unbind() {
 196         if (observable != null) {
 197             value = observable.get();
 198             observable.removeListener(listener);
 199             observable = null;
 200         }
 201     }
 202 
 203     /**
 204      * Returns a string representation of this {@code BooleanPropertyBase} object.
 205      * @return a string representation of this {@code BooleanPropertyBase} object.
 206      */
 207     @Override
 208     public String toString() {
 209         final Object bean = getBean();
 210         final String name = getName();
 211         final StringBuilder result = new StringBuilder("BooleanProperty [");
 212         if (bean != null) {
 213             result.append("bean: ").append(bean).append(", ");
 214         }
 215         if ((name != null) && (!name.equals(""))) {
 216             result.append("name: ").append(name).append(", ");
 217         }
 218         if (isBound()) {
 219             result.append("bound, ");
 220             if (valid) {
 221                 result.append("value: ").append(get());
 222             } else {
 223                 result.append("invalid");
 224             }
 225         } else {
 226             result.append("value: ").append(get());
 227         }
 228         result.append("]");
 229         return result.toString();
 230     }
 231 
 232     private static class Listener implements InvalidationListener, WeakListener {
 233 
 234         private final WeakReference<BooleanPropertyBase> wref;
 235 
 236         public Listener(BooleanPropertyBase ref) {
 237             this.wref = new WeakReference<>(ref);
 238         }
 239 
 240         @Override
 241         public void invalidated(Observable observable) {
 242             BooleanPropertyBase ref = wref.get();
 243             if (ref == null) {
 244                 observable.removeListener(this);
 245             } else {
 246                 ref.markInvalid();
 247             }
 248         }
 249 
 250         @Override
 251         public boolean wasGarbageCollected() {
 252             return wref.get() == null;
 253         }
 254     }
 255 }