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}