/** * Copyright 2010 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.waveprotocol.wave.model.wave; import org.waveprotocol.wave.model.util.CollectionUtils; import org.waveprotocol.wave.model.util.CopyOnWriteSet; import org.waveprotocol.wave.model.util.SimpleMap; import java.util.Map; /** * Observable collection of items. * */ public final class ObservableMapImpl<K, V> implements SimpleMap<K, V>, ObservableMap<K, V> { /** Listeners to notify of collection changes. */ private final CopyOnWriteSet<Listener<? super K, ? super V>> listeners = CopyOnWriteSet.create(); /** Items. */ private final Map<K, V> items = CollectionUtils.newHashMap(); /** * Creates an observable map. */ private ObservableMapImpl() { // nothing to do } /** * Creates an observable map. */ public static <K, V> ObservableMapImpl<K, V> create() { ObservableMapImpl<K, V> map = new ObservableMapImpl<K, V>(); return map; } @Override public V put(K id, V item) { V old = items.get(id); if (old == item) { // Already there. return old; } items.put(id, item); if (old != null) { triggerOnItemRemoved(id, old); } triggerOnItemAdded(id, item); return old; } @Override public V remove(K id) { V old = items.remove(id); if (old != null) { triggerOnItemRemoved(id, old); } return old; } @Override public void clear() { items.clear(); } @Override public V get(K key) { return items.get(key); } @Override public int size() { return items.size(); } @Override public boolean isEmpty() { return items.isEmpty(); } @Override public Iterable<K> copyKeys() { return CollectionUtils.newArrayList(items.keySet()); } // // Extra methods that are useful, but not exposed by the map interfaces. // @Override public final boolean equals(Object o) { return (this == o) || (o instanceof ObservableMapImpl && items.equals(((ObservableMapImpl<?, ?>) o).items)); } @Override public final int hashCode() { return items.hashCode(); } // // Listeners. // @Override public void addListener(Listener<? super K, ? super V> l) { listeners.add(l); } @Override public void removeListener(Listener<? super K, ? super V> l) { listeners.remove(l); } private void triggerOnItemAdded(K key, V blip) { for (Listener<? super K, ? super V> l : listeners) { l.onEntryAdded(key, blip); } } private void triggerOnItemRemoved(K key, V blip) { for (Listener<? super K, ? super V> l : listeners) { l.onEntryRemoved(key, blip); } } }