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 com.sun.javafx.binding; 27 28 import javafx.beans.InvalidationListener; 29 import javafx.beans.value.ChangeListener; 30 import javafx.beans.value.ObservableValue; 31 import sun.util.logging.PlatformLogger; 32 33 import java.util.Arrays; 34 35 /** 36 * A convenience class for creating implementations of {@link javafx.beans.value.ObservableValue}. 37 * It contains all of the infrastructure support for value invalidation- and 38 * change event notification. 39 * 40 * This implementation can handle adding and removing listeners while the 41 * observers are being notified, but it is not thread-safe. 42 * 43 * 44 */ 45 public abstract class ExpressionHelper<T> extends ExpressionHelperBase { 46 47 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 48 // Static methods 49 50 public static <T> ExpressionHelper<T> addListener(ExpressionHelper<T> helper, ObservableValue<T> observable, InvalidationListener listener) { 51 if ((observable == null) || (listener == null)) { 52 throw new NullPointerException(); 53 } 54 observable.getValue(); // validate observable 55 return (helper == null)? new SingleInvalidation<T>(observable, listener) : helper.addListener(listener); 56 } 57 58 public static <T> ExpressionHelper<T> removeListener(ExpressionHelper<T> helper, InvalidationListener listener) { 59 if (listener == null) { 60 throw new NullPointerException(); 61 } 62 return (helper == null)? null : helper.removeListener(listener); 63 } 64 65 public static <T> ExpressionHelper<T> addListener(ExpressionHelper<T> helper, ObservableValue<T> observable, ChangeListener<? super T> listener) { 66 if ((observable == null) || (listener == null)) { 67 throw new NullPointerException(); 68 } 69 return (helper == null)? new SingleChange<T>(observable, listener) : helper.addListener(listener); 70 } 71 72 public static <T> ExpressionHelper<T> removeListener(ExpressionHelper<T> helper, ChangeListener<? super T> listener) { 73 if (listener == null) { 74 throw new NullPointerException(); 75 } 76 return (helper == null)? null : helper.removeListener(listener); 77 } 78 79 public static <T> void fireValueChangedEvent(ExpressionHelper<T> helper) { 80 if (helper != null) { 81 helper.fireValueChangedEvent(); 82 } 83 } 84 85 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 86 // Common implementations 87 88 protected final ObservableValue<T> observable; 89 90 private ExpressionHelper(ObservableValue<T> observable) { 91 this.observable = observable; 92 } 93 94 protected abstract ExpressionHelper<T> addListener(InvalidationListener listener); 95 protected abstract ExpressionHelper<T> removeListener(InvalidationListener listener); 96 97 protected abstract ExpressionHelper<T> addListener(ChangeListener<? super T> listener); 98 protected abstract ExpressionHelper<T> removeListener(ChangeListener<? super T> listener); 99 100 protected abstract void fireValueChangedEvent(); 101 102 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 103 // Implementations 104 105 private static class SingleInvalidation<T> extends ExpressionHelper<T> { 106 107 private final InvalidationListener listener; 108 109 private SingleInvalidation(ObservableValue<T> expression, InvalidationListener listener) { 110 super(expression); 111 this.listener = listener; 112 } 113 114 @Override 115 protected ExpressionHelper<T> addListener(InvalidationListener listener) { 116 return new Generic<T>(observable, this.listener, listener); 117 } 118 119 @Override 120 protected ExpressionHelper<T> removeListener(InvalidationListener listener) { 121 return (listener.equals(this.listener))? null : this; 122 } 123 124 @Override 125 protected ExpressionHelper<T> addListener(ChangeListener<? super T> listener) { 126 return new Generic<T>(observable, this.listener, listener); 127 } 128 129 @Override 130 protected ExpressionHelper<T> removeListener(ChangeListener<? super T> listener) { 131 return this; 132 } 133 134 @Override 135 protected void fireValueChangedEvent() { 136 try { 137 listener.invalidated(observable); 138 } catch (Exception e) { 139 Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e); 140 } 141 } 142 } 143 144 private static class SingleChange<T> extends ExpressionHelper<T> { 145 146 private final ChangeListener<? super T> listener; 147 private T currentValue; 148 149 private SingleChange(ObservableValue<T> observable, ChangeListener<? super T> listener) { 150 super(observable); 151 this.listener = listener; 152 this.currentValue = observable.getValue(); 153 } 154 155 @Override 156 protected ExpressionHelper<T> addListener(InvalidationListener listener) { 157 return new Generic<T>(observable, listener, this.listener); 158 } 159 160 @Override 161 protected ExpressionHelper<T> removeListener(InvalidationListener listener) { 162 return this; 163 } 164 165 @Override 166 protected ExpressionHelper<T> addListener(ChangeListener<? super T> listener) { 167 return new Generic<T>(observable, this.listener, listener); 168 } 169 170 @Override 171 protected ExpressionHelper<T> removeListener(ChangeListener<? super T> listener) { 172 return (listener.equals(this.listener))? null : this; 173 } 174 175 @Override 176 protected void fireValueChangedEvent() { 177 final T oldValue = currentValue; 178 currentValue = observable.getValue(); 179 final boolean changed = (currentValue == null)? (oldValue != null) : !currentValue.equals(oldValue); 180 if (changed) { 181 try { 182 listener.changed(observable, oldValue, currentValue); 183 } catch (Exception e) { 184 Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e); 185 } 186 } 187 } 188 } 189 190 private static class Generic<T> extends ExpressionHelper<T> { 191 192 private InvalidationListener[] invalidationListeners; 193 private ChangeListener<? super T>[] changeListeners; 194 private int invalidationSize; 195 private int changeSize; 196 private boolean locked; 197 private T currentValue; 198 199 private Generic(ObservableValue<T> observable, InvalidationListener listener0, InvalidationListener listener1) { 200 super(observable); 201 this.invalidationListeners = new InvalidationListener[] {listener0, listener1}; 202 this.invalidationSize = 2; 203 } 204 205 private Generic(ObservableValue<T> observable, ChangeListener<? super T> listener0, ChangeListener<? super T> listener1) { 206 super(observable); 207 this.changeListeners = new ChangeListener[] {listener0, listener1}; 208 this.changeSize = 2; 209 this.currentValue = observable.getValue(); 210 } 211 212 private Generic(ObservableValue<T> observable, InvalidationListener invalidationListener, ChangeListener<? super T> changeListener) { 213 super(observable); 214 this.invalidationListeners = new InvalidationListener[] {invalidationListener}; 215 this.invalidationSize = 1; 216 this.changeListeners = new ChangeListener[] {changeListener}; 217 this.changeSize = 1; 218 this.currentValue = observable.getValue(); 219 } 220 221 @Override 222 protected Generic<T> addListener(InvalidationListener listener) { 223 if (invalidationListeners == null) { 224 invalidationListeners = new InvalidationListener[] {listener}; 225 invalidationSize = 1; 226 } else { 227 final int oldCapacity = invalidationListeners.length; 228 if (locked) { 229 final int newCapacity = (invalidationSize < oldCapacity)? oldCapacity : (oldCapacity * 3)/2 + 1; 230 invalidationListeners = Arrays.copyOf(invalidationListeners, newCapacity); 231 } else if (invalidationSize == oldCapacity) { 232 invalidationSize = trim(invalidationSize, invalidationListeners); 233 if (invalidationSize == oldCapacity) { 234 final int newCapacity = (oldCapacity * 3)/2 + 1; 235 invalidationListeners = Arrays.copyOf(invalidationListeners, newCapacity); 236 } 237 } 238 invalidationListeners[invalidationSize++] = listener; 239 } 240 return this; 241 } 242 243 @Override 244 protected ExpressionHelper<T> removeListener(InvalidationListener listener) { 245 if (invalidationListeners != null) { 246 for (int index = 0; index < invalidationSize; index++) { 247 if (listener.equals(invalidationListeners[index])) { 248 if (invalidationSize == 1) { 249 if (changeSize == 1) { 250 return new SingleChange<T>(observable, changeListeners[0]); 251 } 252 invalidationListeners = null; 253 invalidationSize = 0; 254 } else if ((invalidationSize == 2) && (changeSize == 0)) { 255 return new SingleInvalidation<T>(observable, invalidationListeners[1-index]); 256 } else { 257 final int numMoved = invalidationSize - index - 1; 258 final InvalidationListener[] oldListeners = invalidationListeners; 259 if (locked) { 260 invalidationListeners = new InvalidationListener[invalidationListeners.length]; 261 System.arraycopy(oldListeners, 0, invalidationListeners, 0, index); 262 } 263 if (numMoved > 0) { 264 System.arraycopy(oldListeners, index+1, invalidationListeners, index, numMoved); 265 } 266 invalidationSize--; 267 if (!locked) { 268 invalidationListeners[invalidationSize] = null; // Let gc do its work 269 } 270 } 271 break; 272 } 273 } 274 } 275 return this; 276 } 277 278 @Override 279 protected ExpressionHelper<T> addListener(ChangeListener<? super T> listener) { 280 if (changeListeners == null) { 281 changeListeners = new ChangeListener[] {listener}; 282 changeSize = 1; 283 } else { 284 final int oldCapacity = changeListeners.length; 285 if (locked) { 286 final int newCapacity = (changeSize < oldCapacity)? oldCapacity : (oldCapacity * 3)/2 + 1; 287 changeListeners = Arrays.copyOf(changeListeners, newCapacity); 288 } else if (changeSize == oldCapacity) { 289 changeSize = trim(changeSize, changeListeners); 290 if (changeSize == oldCapacity) { 291 final int newCapacity = (oldCapacity * 3)/2 + 1; 292 changeListeners = Arrays.copyOf(changeListeners, newCapacity); 293 } 294 } 295 changeListeners[changeSize++] = listener; 296 } 297 if (changeSize == 1) { 298 currentValue = observable.getValue(); 299 } 300 return this; 301 } 302 303 @Override 304 protected ExpressionHelper<T> removeListener(ChangeListener<? super T> listener) { 305 if (changeListeners != null) { 306 for (int index = 0; index < changeSize; index++) { 307 if (listener.equals(changeListeners[index])) { 308 if (changeSize == 1) { 309 if (invalidationSize == 1) { 310 return new SingleInvalidation<T>(observable, invalidationListeners[0]); 311 } 312 changeListeners = null; 313 changeSize = 0; 314 } else if ((changeSize == 2) && (invalidationSize == 0)) { 315 return new SingleChange<T>(observable, changeListeners[1-index]); 316 } else { 317 final int numMoved = changeSize - index - 1; 318 final ChangeListener<? super T>[] oldListeners = changeListeners; 319 if (locked) { 320 changeListeners = new ChangeListener[changeListeners.length]; 321 System.arraycopy(oldListeners, 0, changeListeners, 0, index); 322 } 323 if (numMoved > 0) { 324 System.arraycopy(oldListeners, index+1, changeListeners, index, numMoved); 325 } 326 changeSize--; 327 if (!locked) { 328 changeListeners[changeSize] = null; // Let gc do its work 329 } 330 } 331 break; 332 } 333 } 334 } 335 return this; 336 } 337 338 @Override 339 protected void fireValueChangedEvent() { 340 final InvalidationListener[] curInvalidationList = invalidationListeners; 341 final int curInvalidationSize = invalidationSize; 342 final ChangeListener<? super T>[] curChangeList = changeListeners; 343 final int curChangeSize = changeSize; 344 345 try { 346 locked = true; 347 for (int i = 0; i < curInvalidationSize; i++) { 348 try { 349 curInvalidationList[i].invalidated(observable); 350 } catch (Exception e) { 351 Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e); 352 } 353 } 354 if (curChangeSize > 0) { 355 final T oldValue = currentValue; 356 currentValue = observable.getValue(); 357 final boolean changed = (currentValue == null)? (oldValue != null) : !currentValue.equals(oldValue); 358 if (changed) { 359 for (int i = 0; i < curChangeSize; i++) { 360 try { 361 curChangeList[i].changed(observable, oldValue, currentValue); 362 } catch (Exception e) { 363 Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e); 364 } 365 } 366 } 367 } 368 } finally { 369 locked = false; 370 } 371 } 372 } 373 374 }