001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 */
020
021package org.biojava.utils;
022
023/**
024 * Useful base-class for objects implementing Changeable
025 *
026 * @author Matthew Pocock
027 * @author Thomas Down
028 * @author George Waldon - private lock on synchronization
029 */
030 
031public abstract class AbstractChangeable implements Changeable {
032  private transient ChangeSupport changeSupport = null;
033  private final Object changeLock = new Object();
034
035  /**
036   * Discover if we have any listeners registered.
037   * 
038   * @return true if there is at least one listener
039   * @deprecated        use hasListeners(ChangeType) if at all possible
040   */
041  protected boolean hasListeners() {
042    return changeSupport != null && changeSupport.hasListeners();
043  }
044  
045  /**
046   * Discover if we have listeners registered for a particular change type.
047   * 
048   * @param ct  the ChangeType we are interested in
049   * @return    true if there is at least one listener
050   */
051  protected boolean hasListeners(ChangeType ct)
052  {
053        return changeSupport != null && changeSupport.hasListeners(ct);
054  }
055
056  /**
057   * Called the first time a ChangeSupport object is needed.  Override this if
058   * you want to set the Unchanging set on the ChangeSupport, or if you want to
059   * install listeners on other objects when the change system is initialized.
060   *
061   * @since 1.3
062   */
063  
064  protected ChangeSupport generateChangeSupport() {
065      return new ChangeSupport();
066  }
067  
068  /**
069   * Called to retrieve the ChangeSupport for this object.
070   *
071   * <p>
072   * Your implementation of this method should have the following structure:
073   * <code><pre>
074   * ChangeSupport cs = super.getChangeSupport(ct);
075   *
076   * if(someForwarder == null && ct.isMatching(SomeInterface.SomeChangeType)) {
077   *   someForwarder = new ChangeForwarder(...
078   *
079   *   this.stateVariable.addChangeListener(someForwarder, VariableInterface.AChange);
080   * }
081   *
082   * return cs;
083   * </pre></code>
084   *
085   * It is usual for the forwarding listeners (someForwarder in this example) to
086   * be transient and lazily instantiated. Be sure to register & unregister the
087   * forwarder in the code that does the ChangeEvent handling in setter methods.
088   */
089  
090  protected ChangeSupport getChangeSupport(ChangeType ct) {
091
092    synchronized(changeLock) {
093      if(changeSupport == null) {
094        changeSupport = generateChangeSupport();
095      }
096    }
097    
098    return changeSupport;
099  }
100
101  public final void addChangeListener(ChangeListener cl) {
102    addChangeListener(cl, ChangeType.UNKNOWN);
103  }
104
105  public final void addChangeListener(ChangeListener cl, ChangeType ct) {
106    ChangeSupport cs = getChangeSupport(ct);
107    cs.addChangeListener(cl, ct);
108  }
109
110  public final void removeChangeListener(ChangeListener cl) {
111    removeChangeListener(cl, ChangeType.UNKNOWN);
112  }
113
114  public final void removeChangeListener(ChangeListener cl, ChangeType ct) {
115    if(hasListeners()) {
116      ChangeSupport cs = getChangeSupport(ct);
117      cs.removeChangeListener(cl, ct);
118    }
119  }
120  
121  public final boolean isUnchanging(ChangeType ct) {
122    ChangeSupport cs = getChangeSupport(ct);
123    return cs.isUnchanging(ct);
124  }
125}