• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.base;
6 
7 import java.util.ArrayList;
8 import java.util.Iterator;
9 import java.util.List;
10 import java.util.NoSuchElementException;
11 
12 import javax.annotation.concurrent.NotThreadSafe;
13 
14 /**
15  * A container for a list of observers.
16  * <p/>
17  * This container can be modified during iteration without invalidating the iterator.
18  * So, it safely handles the case of an observer removing itself or other observers from the list
19  * while observers are being notified.
20  * <p/>
21  * The implementation (and the interface) is heavily influenced by the C++ ObserverList.
22  * Notable differences:
23  *   - The iterator implements NOTIFY_EXISTING_ONLY.
24  *   - The FOR_EACH_OBSERVER closure is left to the clients to implement in terms of iterator().
25  * <p/>
26  * This class is not threadsafe. Observers MUST be added, removed and will be notified on the same
27  * thread this is created.
28  *
29  * @param <E> The type of observers that this list should hold.
30  */
31 @NotThreadSafe
32 public class ObserverList<E> implements Iterable<E> {
33     /**
34      * Extended iterator interface that provides rewind functionality.
35      */
36     public interface RewindableIterator<E> extends Iterator<E> {
37         /**
38          * Rewind the iterator back to the beginning.
39          *
40          * If we need to iterate multiple times, we can avoid iterator object reallocation by using
41          * this method.
42          */
rewind()43         public void rewind();
44     }
45 
46     public final List<E> mObservers = new ArrayList<E>();
47     private int mIterationDepth = 0;
48     private int mCount = 0;
49 
ObserverList()50     public ObserverList() {}
51 
52     /**
53      * Add an observer to the list.
54      * <p/>
55      * An observer should not be added to the same list more than once. If an iteration is already
56      * in progress, this observer will be not be visible during that iteration.
57      *
58      * @return true if the observer list changed as a result of the call.
59      */
addObserver(E obs)60     public boolean addObserver(E obs) {
61         // Avoid adding null elements to the list as they may be removed on a compaction.
62         if (obs == null || mObservers.contains(obs)) {
63             return false;
64         }
65 
66         // Structurally modifying the underlying list here. This means we
67         // cannot use the underlying list's iterator to iterate over the list.
68         boolean result = mObservers.add(obs);
69         assert result == true;
70 
71         ++mCount;
72         return true;
73     }
74 
75     /**
76      * Remove an observer from the list if it is in the list.
77      *
78      * @return true if an element was removed as a result of this call.
79      */
removeObserver(E obs)80     public boolean removeObserver(E obs) {
81         if (obs == null) {
82             return false;
83         }
84 
85         int index = mObservers.indexOf(obs);
86         if (index == -1) {
87             return false;
88         }
89 
90         if (mIterationDepth == 0) {
91             // No one is iterating over the list.
92             mObservers.remove(index);
93         } else {
94             mObservers.set(index, null);
95         }
96         --mCount;
97         assert mCount >= 0;
98 
99         return true;
100     }
101 
hasObserver(E obs)102     public boolean hasObserver(E obs) {
103         return mObservers.contains(obs);
104     }
105 
clear()106     public void clear() {
107         mCount = 0;
108 
109         if (mIterationDepth == 0) {
110             mObservers.clear();
111             return;
112         }
113 
114         int size = mObservers.size();
115         for (int i = 0; i < size; i++) {
116             mObservers.set(i, null);
117         }
118     }
119 
120     @Override
iterator()121     public Iterator<E> iterator() {
122         return new ObserverListIterator();
123     }
124 
125     /**
126      * It's the same as {@link ObserverList#iterator()} but the return type is
127      * {@link RewindableIterator}. Use this iterator type if you need to use
128      * {@link RewindableIterator#rewind()}.
129      */
rewindableIterator()130     public RewindableIterator<E> rewindableIterator() {
131         return new ObserverListIterator();
132     }
133 
134     /**
135      * Returns the number of observers currently registered in the ObserverList.
136      * This is equivalent to the number of non-empty spaces in |mObservers|.
137      */
size()138     public int size() {
139         return mCount;
140     }
141 
142     /**
143      * Returns true if the ObserverList contains no observers.
144      */
isEmpty()145     public boolean isEmpty() {
146         return mCount == 0;
147     }
148 
149     /**
150      * Compact the underlying list be removing null elements.
151      * <p/>
152      * Should only be called when mIterationDepth is zero.
153      */
compact()154     private void compact() {
155         assert mIterationDepth == 0;
156         for (int i = mObservers.size() - 1; i >= 0; i--) {
157             if (mObservers.get(i) == null) {
158                 mObservers.remove(i);
159             }
160         }
161     }
162 
incrementIterationDepth()163     private void incrementIterationDepth() {
164         mIterationDepth++;
165     }
166 
decrementIterationDepthAndCompactIfNeeded()167     private void decrementIterationDepthAndCompactIfNeeded() {
168         mIterationDepth--;
169         assert mIterationDepth >= 0;
170         if (mIterationDepth == 0) compact();
171     }
172 
173     /**
174      * Returns the size of the underlying storage of the ObserverList.
175      * It will take into account the empty spaces inside |mObservers|.
176      */
capacity()177     private int capacity() {
178         return mObservers.size();
179     }
180 
getObserverAt(int index)181     private E getObserverAt(int index) {
182         return mObservers.get(index);
183     }
184 
185     private class ObserverListIterator implements RewindableIterator<E> {
186         private int mListEndMarker;
187         private int mIndex = 0;
188         private boolean mIsExhausted = false;
189 
ObserverListIterator()190         private ObserverListIterator() {
191             ObserverList.this.incrementIterationDepth();
192             mListEndMarker = ObserverList.this.capacity();
193         }
194 
195         @Override
rewind()196         public void rewind() {
197             compactListIfNeeded();
198             ObserverList.this.incrementIterationDepth();
199             mListEndMarker = ObserverList.this.capacity();
200             mIsExhausted = false;
201             mIndex = 0;
202         }
203 
204         @Override
hasNext()205         public boolean hasNext() {
206             int lookupIndex = mIndex;
207             while (lookupIndex < mListEndMarker &&
208                     ObserverList.this.getObserverAt(lookupIndex) == null) {
209                 lookupIndex++;
210             }
211             if (lookupIndex < mListEndMarker) return true;
212 
213             // We have reached the end of the list, allow for compaction.
214             compactListIfNeeded();
215             return false;
216         }
217 
218         @Override
next()219         public E next() {
220             // Advance if the current element is null.
221             while (mIndex < mListEndMarker && ObserverList.this.getObserverAt(mIndex) == null) {
222                 mIndex++;
223             }
224             if (mIndex < mListEndMarker) return ObserverList.this.getObserverAt(mIndex++);
225 
226             // We have reached the end of the list, allow for compaction.
227             compactListIfNeeded();
228             throw new NoSuchElementException();
229         }
230 
231         @Override
remove()232         public void remove() {
233             throw new UnsupportedOperationException();
234         }
235 
compactListIfNeeded()236         private void compactListIfNeeded() {
237             if (!mIsExhausted) {
238                 mIsExhausted = true;
239                 ObserverList.this.decrementIterationDepthAndCompactIfNeeded();
240             }
241         }
242     }
243 }
244