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