| Index: base/android/java/src/org/chromium/base/ObserverList.java
|
| diff --git a/base/android/java/src/org/chromium/base/ObserverList.java b/base/android/java/src/org/chromium/base/ObserverList.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..13a81c5c19540b1d7ae7295ac4810ea72f53c56a
|
| --- /dev/null
|
| +++ b/base/android/java/src/org/chromium/base/ObserverList.java
|
| @@ -0,0 +1,177 @@
|
| +// Copyright 2013 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +package org.chromium.base;
|
| +
|
| +import java.lang.Iterable;
|
| +import java.util.ArrayList;
|
| +import java.util.Iterator;
|
| +import java.util.List;
|
| +import java.util.NoSuchElementException;
|
| +
|
| +import javax.annotation.concurrent.NotThreadSafe;
|
| +
|
| +/**
|
| + * A container for a list of observers.
|
| + * <p/>
|
| + * This container can be modified during iteration without invalidating the iterator.
|
| + * So, it safely handles the case of an observer removing itself or other observers from the list
|
| + * while observers are being notified.
|
| + * <p/>
|
| + * The implementation (and the interface) is heavily influenced by the C++ ObserverList.
|
| + * Notable differences:
|
| + * - The iterator implements NOTIFY_EXISTING_ONLY.
|
| + * - The FOR_EACH_OBSERVER closure is left to the clients to implement in terms of iterator().
|
| + * <p/>
|
| + * This class is not threadsafe. Observers MUST be added, removed and will be notified on the same
|
| + * thread this is created.
|
| + */
|
| +@NotThreadSafe
|
| +public class ObserverList<E> implements Iterable<E> {
|
| + public final List<E> mObservers = new ArrayList<E>();
|
| + private int mIterationDepth = 0;
|
| +
|
| + public ObserverList() {}
|
| +
|
| + /**
|
| + * Add an observer to the list.
|
| + * <p/>
|
| + * An observer should not be added to the same list more than once. If an iteration is already
|
| + * in progress, this observer will be not be visible during that iteration.
|
| + */
|
| + public void addObserver(E obs) {
|
| + // Avoid adding null elements to the list as they may be removed on a compaction.
|
| + if (obs == null || mObservers.contains(obs)) {
|
| + assert false;
|
| + return;
|
| + }
|
| +
|
| + // Structurally modifying the underlying list here. This means we
|
| + // cannot use the underlying list's iterator to iterate over the list.
|
| + mObservers.add(obs);
|
| + }
|
| +
|
| + /**
|
| + * Remove an observer from the list if it is in the list.
|
| + */
|
| + public void removeObserver(E obs) {
|
| + int index = mObservers.indexOf(obs);
|
| +
|
| + if (index == -1)
|
| + return;
|
| +
|
| + if (mIterationDepth == 0) {
|
| + // No one is iterating over the list.
|
| + mObservers.remove(obs);
|
| + } else {
|
| + mObservers.set(index, null);
|
| + }
|
| + }
|
| +
|
| + public boolean hasObserver(E obs) {
|
| + return mObservers.contains(obs);
|
| + }
|
| +
|
| + public void clear() {
|
| + if (mIterationDepth == 0) {
|
| + mObservers.clear();
|
| + return;
|
| + }
|
| +
|
| + int size = mObservers.size();
|
| + for (int i = 0; i < size; i++)
|
| + mObservers.set(i, null);
|
| + }
|
| +
|
| + @Override
|
| + public Iterator<E> iterator() {
|
| + return new ObserverListIterator();
|
| + }
|
| +
|
| + /**
|
| + * Compact the underlying list be removing null elements.
|
| + * <p/>
|
| + * Should only be called when mIterationDepth is zero.
|
| + */
|
| + private void compact() {
|
| + assert mIterationDepth == 0;
|
| + // Safe to use the underlying list's iterator, as we know that no-one else
|
| + // is iterating over the list.
|
| + Iterator<E> it = mObservers.iterator();
|
| + while (it.hasNext()) {
|
| + E el = it.next();
|
| + if (el == null)
|
| + it.remove();
|
| + }
|
| + }
|
| +
|
| + private void incrementIterationDepth() {
|
| + mIterationDepth++;
|
| + }
|
| +
|
| + private void decrementIterationDepthAndCompactIfNeeded() {
|
| + mIterationDepth--;
|
| + assert mIterationDepth >= 0;
|
| + if (mIterationDepth == 0)
|
| + compact();
|
| + }
|
| +
|
| + private int getSize() {
|
| + return mObservers.size();
|
| + }
|
| +
|
| + private E getObserverAt(int index) {
|
| + return mObservers.get(index);
|
| + }
|
| +
|
| + private class ObserverListIterator implements Iterator<E> {
|
| + private final int mListEndMarker;
|
| + private int mIndex = 0;
|
| + private boolean mIsExhausted = false;
|
| +
|
| + private ObserverListIterator() {
|
| + ObserverList.this.incrementIterationDepth();
|
| + mListEndMarker = ObserverList.this.getSize();
|
| + }
|
| +
|
| + @Override
|
| + public boolean hasNext() {
|
| + int lookupIndex = mIndex;
|
| + while (lookupIndex < mListEndMarker &&
|
| + ObserverList.this.getObserverAt(lookupIndex) == null)
|
| + lookupIndex++;
|
| + if (lookupIndex < mListEndMarker)
|
| + return true;
|
| +
|
| + // We have reached the end of the list, allow for compaction.
|
| + compactListIfNeeded();
|
| + return false;
|
| + }
|
| +
|
| + @Override
|
| + public E next() {
|
| + // Advance if the current element is null.
|
| + while (mIndex < mListEndMarker && ObserverList.this.getObserverAt(mIndex) == null)
|
| + mIndex++;
|
| + if (mIndex < mListEndMarker)
|
| + return ObserverList.this.getObserverAt(mIndex++);
|
| +
|
| + // We have reached the end of the list, allow for compaction.
|
| + compactListIfNeeded();
|
| + throw new NoSuchElementException();
|
| + }
|
| +
|
| + @Override
|
| + public void remove() {
|
| + throw new UnsupportedOperationException();
|
| + }
|
| +
|
| + private void compactListIfNeeded() {
|
| + if (!mIsExhausted) {
|
| + mIsExhausted = true;
|
| + ObserverList.this.decrementIterationDepthAndCompactIfNeeded();
|
| + }
|
| + }
|
| + }
|
| +}
|
|
|