/* * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.javafx.collections; import com.sun.javafx.binding.ExpressionHelperBase; import javafx.beans.InvalidationListener; import javafx.collections.ListChangeListener; import com.sun.javafx.logging.PlatformLogger; import java.util.Arrays; /** */ public abstract class ListListenerHelper extends ExpressionHelperBase { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Static methods public static ListListenerHelper addListener(ListListenerHelper helper, InvalidationListener listener) { if (listener == null) { throw new NullPointerException(); } return (helper == null)? new SingleInvalidation(listener) : helper.addListener(listener); } public static ListListenerHelper removeListener(ListListenerHelper helper, InvalidationListener listener) { if (listener == null) { throw new NullPointerException(); } return (helper == null)? null : helper.removeListener(listener); } public static ListListenerHelper addListener(ListListenerHelper helper, ListChangeListener listener) { if (listener == null) { throw new NullPointerException(); } return (helper == null)? new SingleChange(listener) : helper.addListener(listener); } public static ListListenerHelper removeListener(ListListenerHelper helper, ListChangeListener listener) { if (listener == null) { throw new NullPointerException(); } return (helper == null)? null : helper.removeListener(listener); } public static void fireValueChangedEvent(ListListenerHelper helper, ListChangeListener.Change change) { if (helper != null) { change.reset(); helper.fireValueChangedEvent(change); } } public static boolean hasListeners(ListListenerHelper helper) { return helper != null; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Common implementations protected abstract ListListenerHelper addListener(InvalidationListener listener); protected abstract ListListenerHelper removeListener(InvalidationListener listener); protected abstract ListListenerHelper addListener(ListChangeListener listener); protected abstract ListListenerHelper removeListener(ListChangeListener listener); protected abstract void fireValueChangedEvent(ListChangeListener.Change change); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Implementations private static class SingleInvalidation extends ListListenerHelper { private final InvalidationListener listener; private SingleInvalidation(InvalidationListener listener) { this.listener = listener; } @Override protected ListListenerHelper addListener(InvalidationListener listener) { return new Generic(this.listener, listener); } @Override protected ListListenerHelper removeListener(InvalidationListener listener) { return (listener.equals(this.listener))? null : this; } @Override protected ListListenerHelper addListener(ListChangeListener listener) { return new Generic(this.listener, listener); } @Override protected ListListenerHelper removeListener(ListChangeListener listener) { return this; } @Override protected void fireValueChangedEvent(ListChangeListener.Change change) { try { listener.invalidated(change.getList()); } catch (Exception e) { Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e); } } } private static class SingleChange extends ListListenerHelper { private final ListChangeListener listener; private SingleChange(ListChangeListener listener) { this.listener = listener; } @Override protected ListListenerHelper addListener(InvalidationListener listener) { return new Generic(listener, this.listener); } @Override protected ListListenerHelper removeListener(InvalidationListener listener) { return this; } @Override protected ListListenerHelper addListener(ListChangeListener listener) { return new Generic(this.listener, listener); } @Override protected ListListenerHelper removeListener(ListChangeListener listener) { return (listener.equals(this.listener))? null : this; } @Override protected void fireValueChangedEvent(ListChangeListener.Change change) { try { listener.onChanged(change); } catch (Exception e) { Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e); } } } private static class Generic extends ListListenerHelper { private InvalidationListener[] invalidationListeners; private ListChangeListener[] changeListeners; private int invalidationSize; private int changeSize; private boolean locked; private Generic(InvalidationListener listener0, InvalidationListener listener1) { this.invalidationListeners = new InvalidationListener[] {listener0, listener1}; this.invalidationSize = 2; } private Generic(ListChangeListener listener0, ListChangeListener listener1) { this.changeListeners = new ListChangeListener[] {listener0, listener1}; this.changeSize = 2; } private Generic(InvalidationListener invalidationListener, ListChangeListener changeListener) { this.invalidationListeners = new InvalidationListener[] {invalidationListener}; this.invalidationSize = 1; this.changeListeners = new ListChangeListener[] {changeListener}; this.changeSize = 1; } @Override protected Generic addListener(InvalidationListener listener) { if (invalidationListeners == null) { invalidationListeners = new InvalidationListener[] {listener}; invalidationSize = 1; } else { final int oldCapacity = invalidationListeners.length; if (locked) { final int newCapacity = (invalidationSize < oldCapacity)? oldCapacity : (oldCapacity * 3)/2 + 1; invalidationListeners = Arrays.copyOf(invalidationListeners, newCapacity); } else if (invalidationSize == oldCapacity) { invalidationSize = trim(invalidationSize, invalidationListeners); if (invalidationSize == oldCapacity) { final int newCapacity = (oldCapacity * 3)/2 + 1; invalidationListeners = Arrays.copyOf(invalidationListeners, newCapacity); } } invalidationListeners[invalidationSize++] = listener; } return this; } @Override protected ListListenerHelper removeListener(InvalidationListener listener) { if (invalidationListeners != null) { for (int index = 0; index < invalidationSize; index++) { if (listener.equals(invalidationListeners[index])) { if (invalidationSize == 1) { if (changeSize == 1) { return new SingleChange(changeListeners[0]); } invalidationListeners = null; invalidationSize = 0; } else if ((invalidationSize == 2) && (changeSize == 0)) { return new SingleInvalidation(invalidationListeners[1-index]); } else { final int numMoved = invalidationSize - index - 1; final InvalidationListener[] oldListeners = invalidationListeners; if (locked) { invalidationListeners = new InvalidationListener[invalidationListeners.length]; System.arraycopy(oldListeners, 0, invalidationListeners, 0, index); } if (numMoved > 0) { System.arraycopy(oldListeners, index+1, invalidationListeners, index, numMoved); } invalidationSize--; if (!locked) { invalidationListeners[invalidationSize] = null; // Let gc do its work } } break; } } } return this; } @Override protected ListListenerHelper addListener(ListChangeListener listener) { if (changeListeners == null) { changeListeners = new ListChangeListener[] {listener}; changeSize = 1; } else { final int oldCapacity = changeListeners.length; if (locked) { final int newCapacity = (changeSize < oldCapacity)? oldCapacity : (oldCapacity * 3)/2 + 1; changeListeners = Arrays.copyOf(changeListeners, newCapacity); } else if (changeSize == oldCapacity) { changeSize = trim(changeSize, changeListeners); if (changeSize == oldCapacity) { final int newCapacity = (oldCapacity * 3)/2 + 1; changeListeners = Arrays.copyOf(changeListeners, newCapacity); } } changeListeners[changeSize++] = listener; } return this; } @Override protected ListListenerHelper removeListener(ListChangeListener listener) { if (changeListeners != null) { for (int index = 0; index < changeSize; index++) { if (listener.equals(changeListeners[index])) { if (changeSize == 1) { if (invalidationSize == 1) { return new SingleInvalidation(invalidationListeners[0]); } changeListeners = null; changeSize = 0; } else if ((changeSize == 2) && (invalidationSize == 0)) { return new SingleChange(changeListeners[1-index]); } else { final int numMoved = changeSize - index - 1; final ListChangeListener[] oldListeners = changeListeners; if (locked) { changeListeners = new ListChangeListener[changeListeners.length]; System.arraycopy(oldListeners, 0, changeListeners, 0, index); } if (numMoved > 0) { System.arraycopy(oldListeners, index+1, changeListeners, index, numMoved); } changeSize--; if (!locked) { changeListeners[changeSize] = null; // Let gc do its work } } break; } } } return this; } @Override protected void fireValueChangedEvent(ListChangeListener.Change change) { final InvalidationListener[] curInvalidationList = invalidationListeners; final int curInvalidationSize = invalidationSize; final ListChangeListener[] curChangeList = changeListeners; final int curChangeSize = changeSize; try { locked = true; for (int i = 0; i < curInvalidationSize; i++) { try { curInvalidationList[i].invalidated(change.getList()); } catch (Exception e) { Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e); } } for (int i = 0; i < curChangeSize; i++) { change.reset(); try { curChangeList[i].onChanged(change); } catch (Exception e) { Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e); } } } finally { locked = false; } } } }