rt/emul/compact/src/main/java/java/beans/ChangeListenerMap.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Tue, 26 Feb 2013 16:54:16 +0100
changeset 772 d382dacfd73f
parent 604 emul/compact/src/main/java/java/beans/ChangeListenerMap.java@3fcc279c921b
permissions -rw-r--r--
Moving modules around so the runtime is under one master pom and can be built without building other modules that are in the repository
     1 /*
     2  * Copyright (c) 2007, 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 java.beans;
    26 
    27 import java.util.ArrayList;
    28 import java.util.Collections;
    29 import java.util.EventListener;
    30 import java.util.EventListenerProxy;
    31 import java.util.HashMap;
    32 import java.util.List;
    33 import java.util.Map;
    34 import java.util.Map.Entry;
    35 import java.util.Set;
    36 import org.apidesign.bck2brwsr.emul.lang.System;
    37 
    38 /**
    39  * This is an abstract class that provides base functionality
    40  * for the {@link PropertyChangeSupport PropertyChangeSupport} class
    41  * and the {@link VetoableChangeSupport VetoableChangeSupport} class.
    42  *
    43  * @see PropertyChangeListenerMap
    44  * @see VetoableChangeListenerMap
    45  *
    46  * @author Sergey A. Malenkov
    47  */
    48 abstract class ChangeListenerMap<L extends EventListener> {
    49     private Map<String, L[]> map;
    50 
    51     /**
    52      * Creates an array of listeners.
    53      * This method can be optimized by using
    54      * the same instance of the empty array
    55      * when {@code length} is equal to {@code 0}.
    56      *
    57      * @param length  the array length
    58      * @return        an array with specified length
    59      */
    60     protected abstract L[] newArray(int length);
    61 
    62     /**
    63      * Creates a proxy listener for the specified property.
    64      *
    65      * @param name      the name of the property to listen on
    66      * @param listener  the listener to process events
    67      * @return          a proxy listener
    68      */
    69     protected abstract L newProxy(String name, L listener);
    70 
    71     /**
    72      * Adds a listener to the list of listeners for the specified property.
    73      * This listener is called as many times as it was added.
    74      *
    75      * @param name      the name of the property to listen on
    76      * @param listener  the listener to process events
    77      */
    78     public final synchronized void add(String name, L listener) {
    79         if (this.map == null) {
    80             this.map = new HashMap<String, L[]>();
    81         }
    82         L[] array = this.map.get(name);
    83         int size = (array != null)
    84                 ? array.length
    85                 : 0;
    86 
    87         L[] clone = newArray(size + 1);
    88         clone[size] = listener;
    89         if (array != null) {
    90             System.arraycopy(array, 0, clone, 0, size);
    91         }
    92         this.map.put(name, clone);
    93     }
    94 
    95     /**
    96      * Removes a listener from the list of listeners for the specified property.
    97      * If the listener was added more than once to the same event source,
    98      * this listener will be notified one less time after being removed.
    99      *
   100      * @param name      the name of the property to listen on
   101      * @param listener  the listener to process events
   102      */
   103     public final synchronized void remove(String name, L listener) {
   104         if (this.map != null) {
   105             L[] array = this.map.get(name);
   106             if (array != null) {
   107                 for (int i = 0; i < array.length; i++) {
   108                     if (listener.equals(array[i])) {
   109                         int size = array.length - 1;
   110                         if (size > 0) {
   111                             L[] clone = newArray(size);
   112                             System.arraycopy(array, 0, clone, 0, i);
   113                             System.arraycopy(array, i + 1, clone, i, size - i);
   114                             this.map.put(name, clone);
   115                         }
   116                         else {
   117                             this.map.remove(name);
   118                             if (this.map.isEmpty()) {
   119                                 this.map = null;
   120                             }
   121                         }
   122                         break;
   123                     }
   124                 }
   125             }
   126         }
   127     }
   128 
   129     /**
   130      * Returns the list of listeners for the specified property.
   131      *
   132      * @param name  the name of the property
   133      * @return      the corresponding list of listeners
   134      */
   135     public final synchronized L[] get(String name) {
   136         return (this.map != null)
   137                 ? this.map.get(name)
   138                 : null;
   139     }
   140 
   141     /**
   142      * Sets new list of listeners for the specified property.
   143      *
   144      * @param name       the name of the property
   145      * @param listeners  new list of listeners
   146      */
   147     public final void set(String name, L[] listeners) {
   148         if (listeners != null) {
   149             if (this.map == null) {
   150                 this.map = new HashMap<String, L[]>();
   151             }
   152             this.map.put(name, listeners);
   153         }
   154         else if (this.map != null) {
   155             this.map.remove(name);
   156             if (this.map.isEmpty()) {
   157                 this.map = null;
   158             }
   159         }
   160     }
   161 
   162     /**
   163      * Returns all listeners in the map.
   164      *
   165      * @return an array of all listeners
   166      */
   167     public final synchronized L[] getListeners() {
   168         if (this.map == null) {
   169             return newArray(0);
   170         }
   171         List<L> list = new ArrayList<L>();
   172 
   173         L[] listeners = this.map.get(null);
   174         if (listeners != null) {
   175             for (L listener : listeners) {
   176                 list.add(listener);
   177             }
   178         }
   179         for (Entry<String, L[]> entry : this.map.entrySet()) {
   180             String name = entry.getKey();
   181             if (name != null) {
   182                 for (L listener : entry.getValue()) {
   183                     list.add(newProxy(name, listener));
   184                 }
   185             }
   186         }
   187         return list.toArray(newArray(list.size()));
   188     }
   189 
   190     /**
   191      * Returns listeners that have been associated with the named property.
   192      *
   193      * @param name  the name of the property
   194      * @return an array of listeners for the named property
   195      */
   196     public final L[] getListeners(String name) {
   197         if (name != null) {
   198             L[] listeners = get(name);
   199             if (listeners != null) {
   200                 return listeners.clone();
   201             }
   202         }
   203         return newArray(0);
   204     }
   205 
   206     /**
   207      * Indicates whether the map contains
   208      * at least one listener to be notified.
   209      *
   210      * @param name  the name of the property
   211      * @return      {@code true} if at least one listener exists or
   212      *              {@code false} otherwise
   213      */
   214     public final synchronized boolean hasListeners(String name) {
   215         if (this.map == null) {
   216             return false;
   217         }
   218         L[] array = this.map.get(null);
   219         return (array != null) || ((name != null) && (null != this.map.get(name)));
   220     }
   221 
   222     /**
   223      * Returns a set of entries from the map.
   224      * Each entry is a pair consisted of the property name
   225      * and the corresponding list of listeners.
   226      *
   227      * @return a set of entries from the map
   228      */
   229     public final Set<Entry<String, L[]>> getEntries() {
   230         return (this.map != null)
   231                 ? this.map.entrySet()
   232                 : Collections.<Entry<String, L[]>>emptySet();
   233     }
   234 
   235     /**
   236      * Extracts a real listener from the proxy listener.
   237      * It is necessary because default proxy class is not serializable.
   238      *
   239      * @return a real listener
   240      */
   241     public final L extract(L listener) {
   242         while (listener instanceof EventListenerProxy) {
   243             EventListenerProxy<L> proxy = (EventListenerProxy<L>) listener;
   244             listener = proxy.getListener();
   245         }
   246         return listener;
   247     }
   248 }