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