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.seq.impl; 023 024import java.util.Iterator; 025 026import org.biojava.bio.BioException; 027import org.biojava.bio.seq.Feature; 028import org.biojava.bio.seq.FeatureFilter; 029import org.biojava.bio.seq.FeatureHolder; 030import org.biojava.bio.seq.FilterUtils; 031import org.biojava.utils.ChangeEvent; 032import org.biojava.utils.ChangeListener; 033import org.biojava.utils.ChangeSupport; 034import org.biojava.utils.ChangeType; 035import org.biojava.utils.ChangeVetoException; 036 037/** 038 * FeatureHolder which lazily applies a specified filter to another FeatureHolder. 039 * This means that when you use a second filter to query the LazyFilterFeatureHolder, 040 * the underlying holder receives a single <code>filter</code> request with the 041 * two queries ANDed together. 042 * 043 * @author Thomas Down 044 * @since 1.3 045 */ 046 047public class LazyFilterFeatureHolder implements FeatureHolder { 048 private FeatureHolder featureHolder; 049 private FeatureFilter featureFilter; 050 private transient ChangeSupport changeSupport; 051 052 public LazyFilterFeatureHolder(FeatureHolder fh, 053 FeatureFilter ff) 054 { 055 this.featureHolder = fh; 056 this.featureFilter = ff; 057 } 058 059 public Iterator features() { 060 return featureHolder.filter(featureFilter, false).features(); 061 } 062 063 public int countFeatures() { 064 return featureHolder.filter(featureFilter, false).countFeatures(); 065 } 066 067 public boolean containsFeature(Feature f) { 068 if (featureFilter.accept(f)) { 069 return featureHolder.containsFeature(f); 070 } else { 071 return false; 072 } 073 } 074 075 public FeatureHolder filter(FeatureFilter ff) { 076 if (FilterUtils.areDisjoint(ff, featureFilter)) { 077 return FeatureHolder.EMPTY_FEATURE_HOLDER; 078 } 079 return featureHolder.filter(new FeatureFilter.And(ff, featureFilter)); 080 } 081 082 public FeatureHolder filter(FeatureFilter ff, boolean recurse) { 083 if (FilterUtils.areDisjoint(ff, featureFilter)) { 084 return FeatureHolder.EMPTY_FEATURE_HOLDER; 085 } 086 return featureHolder.filter(new FeatureFilter.And(ff, featureFilter), recurse); 087 } 088 089 090 public Feature createFeature(Feature.Template temp) 091 throws ChangeVetoException, BioException 092 { 093 094 Feature f= null; 095 096 synchronized (featureHolder) { 097 f = featureHolder.createFeature(temp); 098 } 099 100 return f; 101 } 102 103 public void removeFeature(Feature f) 104 throws ChangeVetoException, BioException 105 { 106 synchronized (featureHolder) { 107 featureHolder.removeFeature(f); 108 } 109 110 } 111 112 113 protected boolean hasListeners() { 114 return changeSupport != null; 115 } 116 117 protected ChangeSupport getChangeSupport() { 118 if(changeSupport != null) { 119 return changeSupport; 120 } 121 122 synchronized(this) { 123 if(changeSupport == null) { 124 changeSupport = new ChangeSupport(); 125 } 126 synchronized (featureHolder) { 127 featureHolder.addChangeListener(new LFFHChangeForwarder(), ChangeType.UNKNOWN); 128 } 129 130 } 131 132 return changeSupport; 133 } 134 135 private class LFFHChangeForwarder implements ChangeListener { 136 public void preChange(ChangeEvent cev) 137 throws ChangeVetoException 138 { 139 ChangeEvent fcev = getForwardedEvent(cev); 140 if (fcev != null) { 141 ChangeSupport cs = getChangeSupport(); 142 synchronized(cs){ 143 cs.firePreChangeEvent(fcev); 144 } 145 } 146 } 147 148 public void postChange(ChangeEvent cev) 149 { 150 ChangeEvent fcev = getForwardedEvent(cev); 151 if (fcev != null) { 152 ChangeSupport cs = getChangeSupport(); 153 synchronized(cs){ 154 cs.firePostChangeEvent(fcev); 155 } 156 } 157 } 158 } 159 160 /** 161 * Only forward events concerning features which are either accepted by 162 * our filter, or are parents of a feature which is. 163 * 164 * In future this maybe ought to look further down the event chain 165 * to reject forwarded events from uninterested features. 166 */ 167 168 private ChangeEvent getForwardedEvent(ChangeEvent cev) { 169 Object change = cev.getChange(); 170 if (! (change instanceof Feature)) { 171 change = null; 172 } 173 Object previous = cev.getPrevious(); 174 if (! (previous instanceof Feature)) { 175 previous = null; 176 } 177 boolean forward = false; 178 if (change == null && previous == null) { 179 forward = true; 180 } else { 181 forward = isInterestingFeature((Feature) previous) || 182 isInterestingFeature((Feature) change); 183 } 184 if (forward) { 185 return new ChangeEvent(this, 186 cev.getType(), 187 cev.getChange(), 188 cev.getPrevious(), 189 cev); 190 } else { 191 return null; 192 } 193 } 194 195 private boolean isInterestingFeature(Feature f) { 196 if (f == null) { 197 return false; 198 } else if (featureFilter.accept(f)) { 199 return true; 200 } else { 201 return f.filter(featureFilter).countFeatures() > 0; 202 } 203 } 204 205 public final void addChangeListener(ChangeListener cl) { 206 addChangeListener(cl, ChangeType.UNKNOWN); 207 } 208 209 public final void addChangeListener(ChangeListener cl, ChangeType ct) { 210 if (!isUnchanging(ct)) { 211 ChangeSupport cs = getChangeSupport(); 212 cs.addChangeListener(cl, ct); 213 } 214 } 215 216 public final void removeChangeListener(ChangeListener cl) { 217 removeChangeListener(cl, ChangeType.UNKNOWN); 218 } 219 220 public final void removeChangeListener(ChangeListener cl, ChangeType ct) { 221 if(hasListeners()) { 222 ChangeSupport cs = getChangeSupport(); 223 cs.removeChangeListener(cl, ct); 224 } 225 } 226 227 public boolean isUnchanging(ChangeType ct) { 228 return featureHolder.isUnchanging(ct); 229 } 230 231 public FeatureFilter getSchema() { 232 return new FeatureFilter.And(featureFilter, 233 new FeatureFilter.Or(featureHolder.getSchema(), 234 new FeatureFilter.ByAncestor(featureHolder.getSchema()))); 235 } 236}