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 }