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 */ 021 022 023package org.biojava.utils; 024 025import java.lang.ref.Reference; 026import java.lang.ref.ReferenceQueue; 027import java.lang.ref.WeakReference; 028import java.util.ArrayList; 029import java.util.HashMap; 030import java.util.Iterator; 031import java.util.List; 032import java.util.Map; 033 034/** 035 * implements Changeable support with a ChangeHub that 036 * stores ChangeListener by key. 037 * 038 * @author Thomas Down (original implementation) 039 * @author David Huen (refactoring) 040 * @since 1.3 041 */ 042public abstract class IndexedChangeHub implements ChangeHub 043{ 044 private ReferenceQueue refQueue; 045 private Map listeners; 046 047 public IndexedChangeHub() 048 { 049 refQueue = new ReferenceQueue(); 050 listeners = new HashMap(); 051 } 052 053 // queue cleanup 054 // the references are now safe 055 056 abstract protected boolean isMyChangeEvent(ChangeEvent cev, IndexedChangeHub.ListenerMemento lm); 057 058 public void addListener(Object key, ChangeListener listener, ChangeType ct) 059 { 060 diddleQueue(); 061 List listenerList = (List) listeners.get(key); 062 if (listenerList == null) { 063 listenerList = new ArrayList(); 064 listeners.put(key, listenerList); 065 } 066 listenerList.add(new ListenerMemento(ct, new ListenerReference(key, listener, refQueue))); 067 } 068 069 public void removeListener(Object key, ChangeListener listener, ChangeType ct) 070 { 071 List listenerList = (List) listeners.get(key); 072 if (listenerList != null) { 073 for (Iterator i = listenerList.iterator(); i.hasNext(); ) { 074 ListenerMemento lm = (ListenerMemento) i.next(); 075 if (ct == lm.type && listener.equals(lm.listener.get())) { 076 lm.listener.clear(); 077 i.remove(); 078 return; 079 } 080 } 081 } 082 } 083 084 public void firePreChange(Object key, ChangeEvent cev) 085 throws ChangeVetoException 086 { 087 List listenerList = (List) listeners.get(key); 088 if (listenerList != null) { 089 for (Iterator i = listenerList.iterator(); i.hasNext(); ) { 090 ListenerMemento lm = (ListenerMemento) i.next(); 091 if (isMyChangeEvent(cev, lm)) { 092 ChangeListener cl = (ChangeListener) lm.listener.get(); 093 if (cl != null) { 094 cl.preChange(cev); 095 } 096 } 097 } 098 } 099 100 // in the original version, there was the possibility of firing a 101 // ChangeEvent for the parent class here. This is not feasible 102 // in this implementation. The child must override this method 103 // and fire it themselves. 104 105 } 106 107 public void firePostChange(Object key, ChangeEvent cev) 108 { 109 List listenerList = (List) listeners.get(key); 110 if (listenerList != null) { 111 for (Iterator i = listenerList.iterator(); i.hasNext(); ) { 112 ListenerMemento lm = (ListenerMemento) i.next(); 113 if (isMyChangeEvent(cev, lm)) { 114 ChangeListener cl = (ChangeListener) lm.listener.get(); 115 if (cl != null) { 116 cl.postChange(cev); 117 } 118 } 119 } 120 } 121 // in the original version, there was the possibility of firing a 122 // ChangeEvent for the parent class here. This is not feasible 123 // in this implementation. The child must override this method 124 // and fire it themselves. 125 126 } 127 128 protected void diddleQueue() 129 { 130 Reference ref; 131 while ((ref = refQueue.poll()) != null) { 132 List listenerList = (List) listeners.get(((ListenerReference) ref).getKey()); 133 if (listenerList != null) { 134 for (Iterator i = listenerList.iterator(); i.hasNext(); ) { 135 ListenerMemento lm = (ListenerMemento) i.next(); 136 if (lm.listener == ref) { 137 i.remove(); 138 break; 139 } 140 } 141 } 142 } 143 } 144 145 private class ListenerReference extends WeakReference { 146 private Object key; 147 148 public ListenerReference(Object key, Object ref) { 149 super(ref); 150 this.key = key; 151 } 152 153 public ListenerReference(Object key, Object ref, ReferenceQueue queue) { 154 super(ref, queue); 155 this.key = key; 156 } 157 158 public Object getKey() { 159 return key; 160 } 161 } 162 163 protected class ListenerMemento { 164 public final ChangeType type; 165 public final Reference listener; 166 167 public ListenerMemento(ChangeType type, Reference listener) { 168 this.type = type; 169 this.listener = listener; 170 } 171 } 172 173} 174