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 022package org.biojava.bio.gui.sequence; 023 024import java.awt.Graphics2D; 025import java.awt.event.MouseEvent; 026import java.util.HashSet; 027import java.util.List; 028import java.util.Set; 029 030import org.biojava.bio.seq.FeatureFilter; 031import org.biojava.bio.seq.FeatureHolder; 032import org.biojava.utils.AssertionFailure; 033import org.biojava.utils.ChangeEvent; 034import org.biojava.utils.ChangeListener; 035import org.biojava.utils.ChangeSupport; 036import org.biojava.utils.ChangeType; 037import org.biojava.utils.ChangeVetoException; 038import org.biojava.utils.Changeable; 039import org.biojava.utils.cache.CacheMap; 040import org.biojava.utils.cache.FixedSizeMap; 041 042/** 043 * @author Matthew Pocock 044 * @author Thomas Down 045 */ 046public class FilteringRenderer 047extends SequenceRendererWrapper { 048 public static ChangeType FILTER = new ChangeType( 049 "The filter has changed", 050 "org.biojava.bio.gui.sequence.FilteringRenderer", 051 "FILTER", 052 SequenceRenderContext.LAYOUT 053 ); 054 055 public static ChangeType RECURSE = new ChangeType( 056 "The recurse flag has changed", 057 "org.biojava.bio.gui.sequence.FilteringRenderer", 058 "RECURSE", 059 SequenceRenderContext.LAYOUT 060 ); 061 062 protected FeatureFilter filter; 063 protected boolean recurse; 064 065 protected boolean hasListeners() { 066 return super.hasListeners(); 067 } 068 069 protected ChangeSupport getChangeSupport(ChangeType ct) { 070 return super.getChangeSupport(ct); 071 } 072 073 public FilteringRenderer() { 074 filter = FeatureFilter.all; 075 recurse = false; 076 } 077 078 public FilteringRenderer( 079 SequenceRenderer renderer, 080 FeatureFilter filter, 081 boolean recurse 082 ) { 083 super(renderer); 084 try { 085 setFilter(filter); 086 setRecurse(recurse); 087 } catch (ChangeVetoException cve) { 088 throw new AssertionFailure("Assertion Failure: Should have no listeners", cve); 089 } 090 } 091 092 public void setFilter(FeatureFilter filter) 093 throws ChangeVetoException { 094 if(hasListeners()) { 095 ChangeSupport cs = getChangeSupport(FILTER); 096 synchronized(cs) { 097 ChangeEvent ce = new ChangeEvent( 098 this, FILTER, this.filter, filter 099 ); 100 cs.firePreChangeEvent(ce); 101 this.filter = filter; 102 cs.firePostChangeEvent(ce); 103 } 104 } else { 105 this.filter = filter; 106 } 107 } 108 109 public FeatureFilter getFilter() { 110 return this.filter; 111 } 112 113 public void setRecurse(boolean recurse) 114 throws ChangeVetoException { 115 if(hasListeners()) { 116 ChangeSupport cs = getChangeSupport(RECURSE); 117 synchronized(cs) { 118 ChangeEvent ce = new ChangeEvent( 119 this, RECURSE, new Boolean(recurse), new Boolean(this.recurse) 120 ); 121 cs.firePreChangeEvent(ce); 122 this.recurse = recurse; 123 cs.firePostChangeEvent(ce); 124 } 125 } else { 126 this.recurse = recurse; 127 } 128 } 129 130 public boolean getRecurse() { 131 return this.recurse; 132 } 133 134 public double getDepth(SequenceRenderContext src) { 135 return super.getDepth(getContext(src)); 136 } 137 138 public double getMinimumLeader(SequenceRenderContext src) { 139 return super.getMinimumLeader(getContext(src)); 140 } 141 142 public double getMinimumTrailer(SequenceRenderContext src) { 143 return super.getMinimumTrailer(getContext(src)); 144 } 145 146 public void paint( 147 Graphics2D g, 148 SequenceRenderContext src 149 ) { 150 super.paint(g, getContext(src)); 151 } 152 153 public SequenceViewerEvent processMouseEvent( 154 SequenceRenderContext src, 155 MouseEvent me, 156 List path 157 ) { 158 return super.processMouseEvent( 159 getContext(src), 160 me, 161 path 162 ); 163 } 164 165 private CacheMap contextCache = new FixedSizeMap(500); 166 private Set flushers = new HashSet(); 167 168 protected SequenceRenderContext getContext( 169 SequenceRenderContext src 170 ) { 171 FeatureFilter actual = new FeatureFilter.And( 172 filter, 173 new FeatureFilter.OverlapsLocation(src.getRange()) 174 ); 175 176 CtxtFilt gopher = new CtxtFilt(src, actual, recurse); 177 SequenceRenderContext subSrc = (SequenceRenderContext) contextCache.get(gopher); 178 if(subSrc == null) { 179 subSrc = new SubSequenceRenderContext( 180 src, 181 null, 182 src.getFeatures().filter(actual, recurse), 183 null 184 ); 185 contextCache.put(gopher, subSrc); 186 CacheFlusher cf = new CacheFlusher(gopher); 187 // System.err.println("Adding changelistener: " + toString()); 188 ((Changeable) src.getSymbols()).addChangeListener(cf, FeatureHolder.FEATURES); 189 flushers.add(cf); 190 } 191 192 return subSrc; 193 } 194 195 private class CacheFlusher implements ChangeListener { 196 private CtxtFilt ctxtFilt; 197 198 public CacheFlusher(CtxtFilt ctxtFilt) { 199 this.ctxtFilt = ctxtFilt; 200 } 201 202 public void preChange(ChangeEvent ce) { 203 } 204 205 public void postChange(ChangeEvent ce) { 206 contextCache.remove(ctxtFilt); 207 flushers.remove(this); 208 209 if(hasListeners()) { 210 ChangeSupport cs = getChangeSupport(SequenceRenderContext.LAYOUT); 211 synchronized(cs) { 212 ChangeEvent ce2 = new ChangeEvent( 213 FilteringRenderer.this, 214 SequenceRenderContext.LAYOUT 215 ); 216 cs.firePostChangeEvent(ce2); 217 } 218 } 219 } 220 } 221 222 private class CtxtFilt { 223 private SequenceRenderContext src; 224 private FeatureFilter filter; 225 private boolean recurse; 226 227 public CtxtFilt(SequenceRenderContext src, FeatureFilter filter, boolean recurse) { 228 this.src = src; 229 this.filter = filter; 230 this.recurse = recurse; 231 } 232 233 public boolean equals(Object o) { 234 if(! (o instanceof CtxtFilt) ) { 235 return false; 236 } 237 CtxtFilt that = (CtxtFilt) o; 238 return 239 src.equals(that.src) && 240 filter.equals(that.filter) && 241 (recurse == that.recurse); 242 } 243 244 public int hashCode() { 245 return src.hashCode() ^ filter.hashCode(); 246 } 247 } 248 249 public String toString() { 250 return "FilteringRenderer(" + filter + ", " + recurse + ", " + getRenderer() + ")"; 251 } 252}