KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > internal > events > ResourceChangeListenerList


1 /*******************************************************************************
2  * Copyright (c) 2000, 2005 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.core.internal.events;
12
13 import org.eclipse.core.resources.IResourceChangeListener;
14 import org.eclipse.core.runtime.Assert;
15
16 /**
17  * This class is used to maintain a list of listeners. It is a fairly lightweight object,
18  * occupying minimal space when no listeners are registered.
19  * <p>
20  * Note that the <code>add</code> method checks for and eliminates
21  * duplicates based on identity (not equality). Likewise, the
22  * <code>remove</code> method compares based on identity.
23  * </p>
24  * <p>
25  * This implementation is thread safe. The listener list is copied every time
26  * it is modified, so readers do not need to copy or synchronize. This optimizes
27  * for frequent reads and infrequent writes, and assumes that readers can
28  * be trusted not to modify the returned array.
29  */

30 public class ResourceChangeListenerList {
31
32     static class ListenerEntry {
33         int eventMask;
34         IResourceChangeListener listener;
35
36         ListenerEntry(IResourceChangeListener listener, int eventMask) {
37             this.listener = listener;
38             this.eventMask = eventMask;
39         }
40     }
41
42     /**
43      * The empty array singleton instance.
44      */

45     private static final ListenerEntry[] EMPTY_ARRAY = new ListenerEntry[0];
46
47     private int count1 = 0;
48     private int count2 = 0;
49     private int count4 = 0;
50     private int count8 = 0;
51     private int count16 = 0;
52
53     /**
54      * The list of listeners. Maintains invariant: listeners != null.
55      */

56     private volatile ListenerEntry[] listeners = EMPTY_ARRAY;
57
58     /**
59      * Adds the given listener to this list. Has no effect if an identical listener
60      * is already registered.
61      *
62      * @param listener the listener
63      * @param mask event types
64      */

65     public synchronized void add(IResourceChangeListener listener, int mask) {
66         Assert.isNotNull(listener);
67         if (mask == 0) {
68             remove(listener);
69             return;
70         }
71         ResourceChangeListenerList.ListenerEntry entry = new ResourceChangeListenerList.ListenerEntry(listener, mask);
72         final int oldSize = listeners.length;
73         // check for duplicates using identity
74
for (int i = 0; i < oldSize; ++i) {
75             if (listeners[i].listener == listener) {
76                 removing(listeners[i].eventMask);
77                 adding(mask);
78                 listeners[i] = entry;
79                 return;
80             }
81         }
82         adding(mask);
83         // Thread safety: copy on write to protect concurrent readers.
84
ListenerEntry[] newListeners = new ListenerEntry[oldSize + 1];
85         System.arraycopy(listeners, 0, newListeners, 0, oldSize);
86         newListeners[oldSize] = entry;
87         //atomic assignment
88
this.listeners = newListeners;
89     }
90
91     private void adding(int mask) {
92         if ((mask & 1) != 0)
93             count1++;
94         if ((mask & 2) != 0)
95             count2++;
96         if ((mask & 4) != 0)
97             count4++;
98         if ((mask & 8) != 0)
99             count8++;
100         if ((mask & 16) != 0)
101             count16++;
102     }
103
104     /**
105      * Returns an array containing all the registered listeners.
106      * The resulting array is unaffected by subsequent adds or removes.
107      * If there are no listeners registered, the result is an empty array
108      * singleton instance (no garbage is created).
109      * Use this method when notifying listeners, so that any modifications
110      * to the listener list during the notification will have no effect on the
111      * notification itself.
112      * <p>
113      * Note: Clients must not modify the returned list
114      * @return the list of registered listeners that must not be modified
115      */

116     public ListenerEntry[] getListeners() {
117         return listeners;
118     }
119
120     public boolean hasListenerFor(int event) {
121         if (event == 1)
122             return count1 > 0;
123         if (event == 2)
124             return count2 > 0;
125         if (event == 4)
126             return count4 > 0;
127         if (event == 8)
128             return count8 > 0;
129         if (event == 16)
130             return count16 > 0;
131         return false;
132     }
133
134     /**
135      * Removes the given listener from this list. Has no effect if an identical
136      * listener was not already registered.
137      *
138      * @param listener the listener to remove
139      */

140     public synchronized void remove(IResourceChangeListener listener) {
141         Assert.isNotNull(listener);
142         final int oldSize = listeners.length;
143         for (int i = 0; i < oldSize; ++i) {
144             if (listeners[i].listener == listener) {
145                 removing(listeners[i].eventMask);
146                 if (oldSize == 1) {
147                     listeners = EMPTY_ARRAY;
148                 } else {
149                     // Thread safety: create new array to avoid affecting concurrent readers
150
ListenerEntry[] newListeners = new ListenerEntry[oldSize - 1];
151                     System.arraycopy(listeners, 0, newListeners, 0, i);
152                     System.arraycopy(listeners, i + 1, newListeners, i, oldSize - i - 1);
153                     //atomic assignment to field
154
this.listeners = newListeners;
155                 }
156                 return;
157             }
158         }
159     }
160
161     private void removing(int mask) {
162         if ((mask & 1) != 0)
163             count1--;
164         if ((mask & 2) != 0)
165             count2--;
166         if ((mask & 4) != 0)
167             count4--;
168         if ((mask & 8) != 0)
169             count8--;
170         if ((mask & 16) != 0)
171             count16--;
172     }
173 }
174
Popular Tags