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