/* Copyright (C) 2006 EBI This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the itmplied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.biomart.common.utils; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Collection; import java.util.Map; import java.util.Set; /** * This class wraps an existing map, and causes {@link PropertyChangeEvent} * events to be fired whenever it changes. * <p> * Adding objects to the map will result in events where the before value is * null and the after value is the key being added. * <p> * Removing them will result in events where the before value is they key being * removed and the after value is null. * <p> * Multiple add/remove events will have both before and after values of null. * <p> * All events will have a property of {@link BeanMap#propertyName}. * * @author Richard Holland <holland@ebi.ac.uk> * @version $Revision: 1.4 $, $Date: 2007-10-31 10:32:56 $, modified by * $Author: rh4 $ * @since 0.7 */ public class BeanMap extends WeakPropertyChangeSupport implements Map { private static final long serialVersionUID = 1L; private final Map delegate; /** * The property key used in events generated by this map. */ public static final String propertyName = "MapEntry"; /** * Construct a new instance that wraps the delegate map and produces * {@link PropertyChangeEvent} events whenever the delegate map changes. * * @param delegate * the delegate map. */ public BeanMap(final Map delegate) { super(delegate); this.delegate = delegate; } /** * Construct a new instance that wraps the delegate map and produces * {@link PropertyChangeEvent} events whenever the delegate map changes. * <p> * As the delegate is a {@link BeanMap} instance, it doesn't wrap the map * directly, instead it finds out the delegate's delegate and wraps that * instead. It will also notify all listeners that are already listening to * the delegate. * * @param delegate * the delegate map. */ public BeanMap(final BeanMap delegate) { this(delegate.delegate); final PropertyChangeListener[] listeners = delegate .getPropertyChangeListeners(); for (int i = 0; i < listeners.length; i++) this.addPropertyChangeListener(listeners[i]); } public void clear() { this.delegate.clear(); this.firePropertyChange(BeanMap.propertyName, null, null); } public boolean containsKey(final Object key) { return this.delegate.containsKey(key); } public boolean containsValue(final Object value) { return this.delegate.containsValue(value); } private final PropertyChangeListener entrySetListener = new PropertyChangeListener() { public void propertyChange(final PropertyChangeEvent evt) { final Map.Entry oldValue = (Map.Entry) evt.getOldValue(); final Map.Entry newValue = (Map.Entry) evt.getNewValue(); BeanMap.this.firePropertyChange(BeanMap.propertyName, oldValue == null ? null : oldValue.getKey(), newValue == null ? null : newValue.getKey()); } }; public Set entrySet() { // Wrap the entry set in a BeanCollection. final BeanSet beanSet = new BeanSet(this.delegate.entrySet()); // Add a PropertyChangeListener to the BeanSet // which fires events as if they came from us. beanSet.addPropertyChangeListener(this.entrySetListener); // Return the wrapped entry set. return beanSet; } public Object get(final Object key) { return this.delegate.get(key); } public boolean isEmpty() { return this.delegate.isEmpty(); } private final PropertyChangeListener keySetListener = new PropertyChangeListener() { public void propertyChange(final PropertyChangeEvent evt) { BeanMap.this.firePropertyChange(BeanMap.propertyName, evt .getOldValue(), evt.getNewValue()); } }; public Set keySet() { // Wrap the entry set in a BeanCollection. final BeanSet beanSet = new BeanSet(this.delegate.keySet()); // Add a PropertyChangeListener to the BeanSet // which fires events as if they came from us. beanSet.addPropertyChangeListener(this.keySetListener); // Return the wrapped entry set. return beanSet; } public Object put(final Object arg0, final Object arg1) { final boolean already = this.delegate.containsKey(arg0); final Object result = this.delegate.put(arg0, arg1); this.firePropertyChange(BeanMap.propertyName, already ? arg0 : null, arg0); return result; } public void putAll(final Map arg0) { this.delegate.putAll(arg0); this.firePropertyChange(BeanMap.propertyName, null, null); } public Object remove(final Object key) { final Object result = this.delegate.remove(key); this.firePropertyChange(BeanMap.propertyName, key, null); return result; } public int size() { return this.delegate.size(); } public Collection values() { return this.delegate.values(); } protected Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); } public boolean equals(final Object obj) { if (obj == this) return true; else if (obj instanceof BeanMap) return this.delegate.equals(((BeanMap) obj).delegate); else if (obj instanceof Map) return this.delegate.equals(obj); else return false; } public int hashCode() { return this.delegate.hashCode(); } public String toString() { return this.delegate.toString(); } }