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