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
jaroslav@601
     1
/*
jaroslav@601
     2
 * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
jaroslav@601
     3
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jaroslav@601
     4
 *
jaroslav@601
     5
 * This code is free software; you can redistribute it and/or modify it
jaroslav@601
     6
 * under the terms of the GNU General Public License version 2 only, as
jaroslav@601
     7
 * published by the Free Software Foundation.  Oracle designates this
jaroslav@601
     8
 * particular file as subject to the "Classpath" exception as provided
jaroslav@601
     9
 * by Oracle in the LICENSE file that accompanied this code.
jaroslav@601
    10
 *
jaroslav@601
    11
 * This code is distributed in the hope that it will be useful, but WITHOUT
jaroslav@601
    12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jaroslav@601
    13
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
jaroslav@601
    14
 * version 2 for more details (a copy is included in the LICENSE file that
jaroslav@601
    15
 * accompanied this code).
jaroslav@601
    16
 *
jaroslav@601
    17
 * You should have received a copy of the GNU General Public License version
jaroslav@601
    18
 * 2 along with this work; if not, write to the Free Software Foundation,
jaroslav@601
    19
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jaroslav@601
    20
 *
jaroslav@601
    21
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
jaroslav@601
    22
 * or visit www.oracle.com if you need additional information or have any
jaroslav@601
    23
 * questions.
jaroslav@601
    24
 */
jaroslav@601
    25
package java.beans;
jaroslav@601
    26
jaroslav@601
    27
import java.util.ArrayList;
jaroslav@601
    28
import java.util.Collections;
jaroslav@601
    29
import java.util.EventListener;
jaroslav@601
    30
import java.util.EventListenerProxy;
jaroslav@601
    31
import java.util.HashMap;
jaroslav@601
    32
import java.util.List;
jaroslav@601
    33
import java.util.Map;
jaroslav@601
    34
import java.util.Map.Entry;
jaroslav@601
    35
import java.util.Set;
jaroslav@604
    36
import org.apidesign.bck2brwsr.emul.lang.System;
jaroslav@601
    37
jaroslav@601
    38
/**
jaroslav@601
    39
 * This is an abstract class that provides base functionality
jaroslav@601
    40
 * for the {@link PropertyChangeSupport PropertyChangeSupport} class
jaroslav@601
    41
 * and the {@link VetoableChangeSupport VetoableChangeSupport} class.
jaroslav@601
    42
 *
jaroslav@601
    43
 * @see PropertyChangeListenerMap
jaroslav@601
    44
 * @see VetoableChangeListenerMap
jaroslav@601
    45
 *
jaroslav@601
    46
 * @author Sergey A. Malenkov
jaroslav@601
    47
 */
jaroslav@601
    48
abstract class ChangeListenerMap<L extends EventListener> {
jaroslav@601
    49
    private Map<String, L[]> map;
jaroslav@601
    50
jaroslav@601
    51
    /**
jaroslav@601
    52
     * Creates an array of listeners.
jaroslav@601
    53
     * This method can be optimized by using
jaroslav@601
    54
     * the same instance of the empty array
jaroslav@601
    55
     * when {@code length} is equal to {@code 0}.
jaroslav@601
    56
     *
jaroslav@601
    57
     * @param length  the array length
jaroslav@601
    58
     * @return        an array with specified length
jaroslav@601
    59
     */
jaroslav@601
    60
    protected abstract L[] newArray(int length);
jaroslav@601
    61
jaroslav@601
    62
    /**
jaroslav@601
    63
     * Creates a proxy listener for the specified property.
jaroslav@601
    64
     *
jaroslav@601
    65
     * @param name      the name of the property to listen on
jaroslav@601
    66
     * @param listener  the listener to process events
jaroslav@601
    67
     * @return          a proxy listener
jaroslav@601
    68
     */
jaroslav@601
    69
    protected abstract L newProxy(String name, L listener);
jaroslav@601
    70
jaroslav@601
    71
    /**
jaroslav@601
    72
     * Adds a listener to the list of listeners for the specified property.
jaroslav@601
    73
     * This listener is called as many times as it was added.
jaroslav@601
    74
     *
jaroslav@601
    75
     * @param name      the name of the property to listen on
jaroslav@601
    76
     * @param listener  the listener to process events
jaroslav@601
    77
     */
jaroslav@601
    78
    public final synchronized void add(String name, L listener) {
jaroslav@601
    79
        if (this.map == null) {
jaroslav@601
    80
            this.map = new HashMap<String, L[]>();
jaroslav@601
    81
        }
jaroslav@601
    82
        L[] array = this.map.get(name);
jaroslav@601
    83
        int size = (array != null)
jaroslav@601
    84
                ? array.length
jaroslav@601
    85
                : 0;
jaroslav@601
    86
jaroslav@601
    87
        L[] clone = newArray(size + 1);
jaroslav@601
    88
        clone[size] = listener;
jaroslav@601
    89
        if (array != null) {
jaroslav@601
    90
            System.arraycopy(array, 0, clone, 0, size);
jaroslav@601
    91
        }
jaroslav@601
    92
        this.map.put(name, clone);
jaroslav@601
    93
    }
jaroslav@601
    94
jaroslav@601
    95
    /**
jaroslav@601
    96
     * Removes a listener from the list of listeners for the specified property.
jaroslav@601
    97
     * If the listener was added more than once to the same event source,
jaroslav@601
    98
     * this listener will be notified one less time after being removed.
jaroslav@601
    99
     *
jaroslav@601
   100
     * @param name      the name of the property to listen on
jaroslav@601
   101
     * @param listener  the listener to process events
jaroslav@601
   102
     */
jaroslav@601
   103
    public final synchronized void remove(String name, L listener) {
jaroslav@601
   104
        if (this.map != null) {
jaroslav@601
   105
            L[] array = this.map.get(name);
jaroslav@601
   106
            if (array != null) {
jaroslav@601
   107
                for (int i = 0; i < array.length; i++) {
jaroslav@601
   108
                    if (listener.equals(array[i])) {
jaroslav@601
   109
                        int size = array.length - 1;
jaroslav@601
   110
                        if (size > 0) {
jaroslav@601
   111
                            L[] clone = newArray(size);
jaroslav@601
   112
                            System.arraycopy(array, 0, clone, 0, i);
jaroslav@601
   113
                            System.arraycopy(array, i + 1, clone, i, size - i);
jaroslav@601
   114
                            this.map.put(name, clone);
jaroslav@601
   115
                        }
jaroslav@601
   116
                        else {
jaroslav@601
   117
                            this.map.remove(name);
jaroslav@601
   118
                            if (this.map.isEmpty()) {
jaroslav@601
   119
                                this.map = null;
jaroslav@601
   120
                            }
jaroslav@601
   121
                        }
jaroslav@601
   122
                        break;
jaroslav@601
   123
                    }
jaroslav@601
   124
                }
jaroslav@601
   125
            }
jaroslav@601
   126
        }
jaroslav@601
   127
    }
jaroslav@601
   128
jaroslav@601
   129
    /**
jaroslav@601
   130
     * Returns the list of listeners for the specified property.
jaroslav@601
   131
     *
jaroslav@601
   132
     * @param name  the name of the property
jaroslav@601
   133
     * @return      the corresponding list of listeners
jaroslav@601
   134
     */
jaroslav@601
   135
    public final synchronized L[] get(String name) {
jaroslav@601
   136
        return (this.map != null)
jaroslav@601
   137
                ? this.map.get(name)
jaroslav@601
   138
                : null;
jaroslav@601
   139
    }
jaroslav@601
   140
jaroslav@601
   141
    /**
jaroslav@601
   142
     * Sets new list of listeners for the specified property.
jaroslav@601
   143
     *
jaroslav@601
   144
     * @param name       the name of the property
jaroslav@601
   145
     * @param listeners  new list of listeners
jaroslav@601
   146
     */
jaroslav@601
   147
    public final void set(String name, L[] listeners) {
jaroslav@601
   148
        if (listeners != null) {
jaroslav@601
   149
            if (this.map == null) {
jaroslav@601
   150
                this.map = new HashMap<String, L[]>();
jaroslav@601
   151
            }
jaroslav@601
   152
            this.map.put(name, listeners);
jaroslav@601
   153
        }
jaroslav@601
   154
        else if (this.map != null) {
jaroslav@601
   155
            this.map.remove(name);
jaroslav@601
   156
            if (this.map.isEmpty()) {
jaroslav@601
   157
                this.map = null;
jaroslav@601
   158
            }
jaroslav@601
   159
        }
jaroslav@601
   160
    }
jaroslav@601
   161
jaroslav@601
   162
    /**
jaroslav@601
   163
     * Returns all listeners in the map.
jaroslav@601
   164
     *
jaroslav@601
   165
     * @return an array of all listeners
jaroslav@601
   166
     */
jaroslav@601
   167
    public final synchronized L[] getListeners() {
jaroslav@601
   168
        if (this.map == null) {
jaroslav@601
   169
            return newArray(0);
jaroslav@601
   170
        }
jaroslav@601
   171
        List<L> list = new ArrayList<L>();
jaroslav@601
   172
jaroslav@601
   173
        L[] listeners = this.map.get(null);
jaroslav@601
   174
        if (listeners != null) {
jaroslav@601
   175
            for (L listener : listeners) {
jaroslav@601
   176
                list.add(listener);
jaroslav@601
   177
            }
jaroslav@601
   178
        }
jaroslav@601
   179
        for (Entry<String, L[]> entry : this.map.entrySet()) {
jaroslav@601
   180
            String name = entry.getKey();
jaroslav@601
   181
            if (name != null) {
jaroslav@601
   182
                for (L listener : entry.getValue()) {
jaroslav@601
   183
                    list.add(newProxy(name, listener));
jaroslav@601
   184
                }
jaroslav@601
   185
            }
jaroslav@601
   186
        }
jaroslav@601
   187
        return list.toArray(newArray(list.size()));
jaroslav@601
   188
    }
jaroslav@601
   189
jaroslav@601
   190
    /**
jaroslav@601
   191
     * Returns listeners that have been associated with the named property.
jaroslav@601
   192
     *
jaroslav@601
   193
     * @param name  the name of the property
jaroslav@601
   194
     * @return an array of listeners for the named property
jaroslav@601
   195
     */
jaroslav@601
   196
    public final L[] getListeners(String name) {
jaroslav@601
   197
        if (name != null) {
jaroslav@601
   198
            L[] listeners = get(name);
jaroslav@601
   199
            if (listeners != null) {
jaroslav@601
   200
                return listeners.clone();
jaroslav@601
   201
            }
jaroslav@601
   202
        }
jaroslav@601
   203
        return newArray(0);
jaroslav@601
   204
    }
jaroslav@601
   205
jaroslav@601
   206
    /**
jaroslav@601
   207
     * Indicates whether the map contains
jaroslav@601
   208
     * at least one listener to be notified.
jaroslav@601
   209
     *
jaroslav@601
   210
     * @param name  the name of the property
jaroslav@601
   211
     * @return      {@code true} if at least one listener exists or
jaroslav@601
   212
     *              {@code false} otherwise
jaroslav@601
   213
     */
jaroslav@601
   214
    public final synchronized boolean hasListeners(String name) {
jaroslav@601
   215
        if (this.map == null) {
jaroslav@601
   216
            return false;
jaroslav@601
   217
        }
jaroslav@601
   218
        L[] array = this.map.get(null);
jaroslav@601
   219
        return (array != null) || ((name != null) && (null != this.map.get(name)));
jaroslav@601
   220
    }
jaroslav@601
   221
jaroslav@601
   222
    /**
jaroslav@601
   223
     * Returns a set of entries from the map.
jaroslav@601
   224
     * Each entry is a pair consisted of the property name
jaroslav@601
   225
     * and the corresponding list of listeners.
jaroslav@601
   226
     *
jaroslav@601
   227
     * @return a set of entries from the map
jaroslav@601
   228
     */
jaroslav@601
   229
    public final Set<Entry<String, L[]>> getEntries() {
jaroslav@601
   230
        return (this.map != null)
jaroslav@601
   231
                ? this.map.entrySet()
jaroslav@601
   232
                : Collections.<Entry<String, L[]>>emptySet();
jaroslav@601
   233
    }
jaroslav@601
   234
jaroslav@601
   235
    /**
jaroslav@601
   236
     * Extracts a real listener from the proxy listener.
jaroslav@601
   237
     * It is necessary because default proxy class is not serializable.
jaroslav@601
   238
     *
jaroslav@601
   239
     * @return a real listener
jaroslav@601
   240
     */
jaroslav@601
   241
    public final L extract(L listener) {
jaroslav@601
   242
        while (listener instanceof EventListenerProxy) {
jaroslav@601
   243
            EventListenerProxy<L> proxy = (EventListenerProxy<L>) listener;
jaroslav@601
   244
            listener = proxy.getListener();
jaroslav@601
   245
        }
jaroslav@601
   246
        return listener;
jaroslav@601
   247
    }
jaroslav@601
   248
}