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