[Biojava-l] ChangeSupport.java
Peter Kharchenko
pkharch@fas.harvard.edu
Mon, 04 Mar 2002 22:06:27 -0500
This is a multi-part message in MIME format.
--------------060508070904090205070502
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit
Biojava guys (/girls?) ,
I am using biojava for sequence alignment stats. I ran into a really
bad performance problem, which was apparently was caused by a rather
naive implementation of org.biojava.util.ChangeSupport class. Rewriting
that class sped up my application by more then factor of 10.
I am sending you a copy of my implementation. I haven't changed any of
the methods (although some constructors are rather meaningless now), so
you should be able to just swap it in.
-peter.
--------------060508070904090205070502
Content-Type: text/plain;
name="ChangeSupport.java"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="ChangeSupport.java"
/*
* BioJava development code
*
* This code may be freely distributed and modified under the
* terms of the GNU Lesser General Public Licence. This should
* be distributed with the code. If you do not have a copy,
* see:
*
* http://www.gnu.org/copyleft/lesser.html
*
* Copyright for this code is held jointly by the individual
* authors. These should be listed in @author doc comments.
*
* For more information on the BioJava project and its aims,
* or to join the biojava-l mailing list, visit the home page
* at:
*
* http://www.biojava.org/
*/
package org.biojava.utils;
import java.io.Serializable;
import java.util.*;
import java.lang.ref.*;
/**
* A utility class to provide management for informing ChangeListeners of
* ChangeEvents.
* <P>
* This is loosely modelled after the standard PropertyChangeEvent objects.
* <P>
* For an object to correctly fire these events, they must follow a broad
* outline like this:
* <code><pre>
* public void mutator(foo arg) throw ChangeVetoException {
* ChangeEvent cevt = new ChangeEvent(this, SOME_EVENT_TYPE, arg);
* synchronized(changeSupport) {
* changeSupport.firePreChangeEvent(cevt);
* // update our state using arg
* // ...
* changeSupport.firePostChangeEvent(cevt);
* }
* }
* </pre></code>
* The methods that delegate adding and removing listeners to a ChangeSupport
* must take responsibility for synchronizing on the delegate.
*
* @author Matthew Pocock
* @author Thomas Down
* @author Keith James (docs)
* @since 1.1
*/
public class ChangeSupport {
private Hashtable changeTypes;
/**
* Generate a new ChangeSupport instance.
*/
public ChangeSupport() {
changeTypes=new Hashtable();
}
/**
* Generate a new ChangeSupport instance which has room for initialSize
* listeners before it needs to grow any resources.
*
* @param initialSize the number of listeners that can be added before this
* needs to grow for the first time
*/
public ChangeSupport(int initialSize) {
this();
}
/**
* Generate a new ChangeSupport instance which has room for initialSize
* listeners before it needs to grow any resources, and which will grow by
* delta each time.
*
* @param initialSize the number of listeners that can be added before this
* needs to grow for the first time
* @param delta the number of listener slots that this will grow by each time
* it needs to
*/
public ChangeSupport(int initialSize, int delta) {
this();
}
/**
* Add a listener that will be informed of all changes.
*
* @param cl the ChangeListener to add
*/
public void addChangeListener(ChangeListener cl) {
addChangeListener(cl, ChangeType.UNKNOWN);
}
/**
* Add a listener that will be informed of changes of a given type (and it's subtypes)
*
* @param cl the ChangeListener
* @param ct the ChangeType it is to be informed of
*/
public synchronized void addChangeListener(ChangeListener cl, ChangeType ct) {
// Needed to synchronize this method in case multiple threads attempt to add a change listener at the same time.
// Richard J. Fox 05/30/2001
if (ct == null) {
throw new NestedError("Since 1.2, listeners registered for the null changetype are not meaningful. Please register a listener for ChangeType.UNKNOWN instead");
} else {
ChangeTypeListeners ctl=(ChangeTypeListeners)changeTypes.get(ct);
// if this type has not been registered before
if(ctl==null) {
ctl=new ChangeTypeListeners(ct);
changeTypes.put(ct,ctl);
}
}
}
/**
* Remove a listener that was interested in all types of changes.
*
* @param cl a ChangeListener to remove
*/
public void removeChangeListener(ChangeListener cl) {
removeChangeListener(cl, ChangeType.UNKNOWN);
}
/**
* Remove a listener that was interested in a specific types of changes.
*
* @param cl a ChangeListener to remove
* @param ct the ChangeType that it was interested in
*/
public void removeChangeListener(ChangeListener cl, ChangeType ct) {
if(ct==null) {
throw new NestedError("Since 1.2, listeners registered for the null changetype are not meaningful. Please register a listener for ChangeType.UNKNOWN instead");
} else {
ChangeTypeListeners ctl=(ChangeTypeListeners)changeTypes.get(ct);
if(ct!=null) {
ctl.listeners.remove(cl);
}
}
}
/**
* Inform the listeners that a change is about to take place using their
* firePreChangeEvent methods.
* <P>
* Listeners will be informed if they were interested in all types of event,
* or if ce.getType() is equal to the type they are registered for.
*
* @param ce the ChangeEvent to pass on
* @throws ChangeVetoException if any of the listeners veto this change
*/
public void firePreChangeEvent(ChangeEvent ce)
throws ChangeVetoException {
ChangeType ct = ce.getType();
if(ct!=null) {
ChangeTypeListeners ctl=(ChangeTypeListeners)changeTypes.get(ct);
if(ctl!=null) {
for(Iterator i=ctl.listeners.iterator();i.hasNext();) {
ChangeListener cl=(ChangeListener)i.next();
cl.preChange(ce);
}
}
}
}
/**
* Inform the listeners that a change has taken place using their
* firePostChangeEvent methods.
* <P>
* Listeners will be informed if they were interested in all types of event,
* or if ce.getType() is equal to the type they are registered for.
*
* @param ce the ChangeEvent to pass on
*/
public void firePostChangeEvent(ChangeEvent ce) {
ChangeType ct = ce.getType();
if(ct!=null) {
ChangeTypeListeners ctl=(ChangeTypeListeners)changeTypes.get(ct);
if(ctl!=null) {
for(Iterator i=ctl.listeners.iterator();i.hasNext();) {
ChangeListener cl=(ChangeListener)i.next();
cl.postChange(ce);
}
}
}
}
/** A little private class to hold a change type together with a set of listeners for that type */
private class ChangeTypeListeners {
Set listeners;
ChangeType type;
public ChangeTypeListeners(ChangeType t) {
this.type=t;
this.listeners=Collections.synchronizedSet(new HashSet());
}
}
}
--------------060508070904090205070502--