1 /*
   2  * Copyright (c) 2012, 201, 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.SetChangeListener;
  31 import com.sun.javafx.logging.PlatformLogger;
  32 
  33 import java.util.Arrays;
  34 
  35 /**
  36  */
  37 public abstract class SetListenerHelper<E> extends ExpressionHelperBase {
  38 
  39     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  40     // Static methods
  41 
  42     public static <E> SetListenerHelper<E> addListener(SetListenerHelper<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> SetListenerHelper<E> removeListener(SetListenerHelper<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> SetListenerHelper<E> addListener(SetListenerHelper<E> helper, SetChangeListener<? 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> SetListenerHelper<E> removeListener(SetListenerHelper<E> helper, SetChangeListener<? 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(SetListenerHelper<E> helper, SetChangeListener.Change<? extends E> change) {
  71         if (helper != null) {
  72             helper.fireValueChangedEvent(change);
  73         }
  74     }
  75 
  76     public static <E> boolean hasListeners(SetListenerHelper<E> helper) {
  77         return helper != null;
  78     }
  79 
  80     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  81     // Common implementations
  82 
  83     protected abstract SetListenerHelper<E> addListener(InvalidationListener listener);
  84     protected abstract SetListenerHelper<E> removeListener(InvalidationListener listener);
  85 
  86     protected abstract SetListenerHelper<E> addListener(SetChangeListener<? super E> listener);
  87     protected abstract SetListenerHelper<E> removeListener(SetChangeListener<? super E> listener);
  88 
  89     protected abstract void fireValueChangedEvent(SetChangeListener.Change<? extends E> change);
  90 
  91     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  92     // Implementations
  93 
  94     private static class SingleInvalidation<E> extends SetListenerHelper<E> {
  95 
  96         private final InvalidationListener listener;
  97 
  98         private SingleInvalidation(InvalidationListener listener) {
  99             this.listener = listener;
 100         }
 101 
 102         @Override
 103         protected SetListenerHelper<E> addListener(InvalidationListener listener) {
 104             return new Generic<E>(this.listener, listener);
 105         }
 106 
 107         @Override
 108         protected SetListenerHelper<E> removeListener(InvalidationListener listener) {
 109             return (listener.equals(this.listener))? null : this;
 110         }
 111 
 112         @Override
 113         protected SetListenerHelper<E> addListener(SetChangeListener<? super E> listener) {
 114             return new Generic<E>(this.listener, listener);
 115         }
 116 
 117         @Override
 118         protected SetListenerHelper<E> removeListener(SetChangeListener<? super E> listener) {
 119             return this;
 120         }
 121 
 122         @Override
 123         protected void fireValueChangedEvent(SetChangeListener.Change<? extends E> change) {
 124             try {
 125                 listener.invalidated(change.getSet());
 126             } catch (Exception e) {
 127                 Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
 128             }
 129         }
 130     }
 131 
 132     private static class SingleChange<E> extends SetListenerHelper<E> {
 133 
 134         private final SetChangeListener<? super E> listener;
 135 
 136         private SingleChange(SetChangeListener<? super E> listener) {
 137             this.listener = listener;
 138         }
 139 
 140         @Override
 141         protected SetListenerHelper<E> addListener(InvalidationListener listener) {
 142             return new Generic<E>(listener, this.listener);
 143         }
 144 
 145         @Override
 146         protected SetListenerHelper<E> removeListener(InvalidationListener listener) {
 147             return this;
 148         }
 149 
 150         @Override
 151         protected SetListenerHelper<E> addListener(SetChangeListener<? super E> listener) {
 152             return new Generic<E>(this.listener, listener);
 153         }
 154 
 155         @Override
 156         protected SetListenerHelper<E> removeListener(SetChangeListener<? super E> listener) {
 157             return (listener.equals(this.listener))? null : this;
 158         }
 159 
 160         @Override
 161         protected void fireValueChangedEvent(SetChangeListener.Change<? extends E> 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<E> extends SetListenerHelper<E> {
 171 
 172         private InvalidationListener[] invalidationListeners;
 173         private SetChangeListener<? super E>[] 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(SetChangeListener<? super E> listener0, SetChangeListener<? super E> listener1) {
 184             this.changeListeners = new SetChangeListener[] {listener0, listener1};
 185             this.changeSize = 2;
 186         }
 187 
 188         private Generic(InvalidationListener invalidationListener, SetChangeListener<? super E> changeListener) {
 189             this.invalidationListeners = new InvalidationListener[] {invalidationListener};
 190             this.invalidationSize = 1;
 191             this.changeListeners = new SetChangeListener[] {changeListener};
 192             this.changeSize = 1;
 193         }
 194 
 195         @Override
 196         protected Generic<E> 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 SetListenerHelper<E> 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<E>(changeListeners[0]);
 225                             }
 226                             invalidationListeners = null;
 227                             invalidationSize = 0;
 228                         } else if ((invalidationSize == 2) && (changeSize == 0)) {
 229                             return new SingleInvalidation<E>(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 SetListenerHelper<E> addListener(SetChangeListener<? super E> listener) {
 254             if (changeListeners == null) {
 255                 changeListeners = new SetChangeListener[] {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 SetListenerHelper<E> removeListener(SetChangeListener<? super E> 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<E>(invalidationListeners[0]);
 282                             }
 283                             changeListeners = null;
 284                             changeSize = 0;
 285                         } else if ((changeSize == 2) && (invalidationSize == 0)) {
 286                             return new SingleChange<E>(changeListeners[1-index]);
 287                         } else {
 288                             final int numMoved = changeSize - index - 1;
 289                             final SetChangeListener<? super E>[] oldListeners = changeListeners;
 290                             if (locked) {
 291                                 changeListeners = new SetChangeListener[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(SetChangeListener.Change<? extends E> change) {
 311             final InvalidationListener[] curInvalidationList = invalidationListeners;
 312             final int curInvalidationSize = invalidationSize;
 313             final SetChangeListener<? super E>[] 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.getSet());
 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 }