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.search; 023 024import java.util.ArrayList; 025import java.util.HashMap; 026import java.util.Iterator; 027import java.util.List; 028import java.util.Map; 029 030import org.biojava.bio.BioException; 031import org.biojava.utils.TriState; 032import org.biojava.utils.walker.Visitor; 033import org.biojava.utils.walker.Walker; 034import org.biojava.utils.walker.WalkerFactory; 035 036public class FilteringContentHandler 037 extends SearchContentAdapter 038 implements BlastLikeSearchFilter.Node 039{ 040 // these store properties 041 private Map searchProperty = new HashMap(); 042 private Map hitProperty = new HashMap(); 043 private Map subHitProperty = new HashMap(); 044 045 // these store the filter objects 046 private List searchFilters = new ArrayList(); 047 private List hitFilters = new ArrayList(); 048 private List subHitFilters = new ArrayList(); 049 050 // these keep track of what this class has issued 051 // to its downstream filters. 052 private boolean emittedStartSearch; 053 private boolean emittedStartHit; 054 055 // these determine whether to skip the lower ranked levels 056 private boolean skipHits =false; 057 private boolean skipSubHits = false; 058 059 // these keep track of whether the test for the outcome at a certain level has 060 // been done. 061 private boolean firstSubHit = true; 062 private boolean firstHit = true; // first hit in a search 063 064 public Object getSearchProperty(Object key) { return searchProperty.get(key); } 065 public Object getHitProperty(Object key) { return hitProperty.get(key); } 066 public Object getSubHitProperty(Object key) { return subHitProperty.get(key); } 067 068 private static final WalkerFactory walkerFactory = WalkerFactory.getInstance(BlastLikeSearchFilter.class); 069 070 /** 071 * Visitor class that parses the filter tree 072 */ 073 public class FilterVisitor 074 implements Visitor 075 { 076 public void bySearchProperty(BlastLikeSearchFilter.BySearchProperty sf) 077 { 078 // register this instance 079 searchFilters.add(sf); 080 } 081 082 public void byHitProperty(BlastLikeSearchFilter.ByHitProperty sf) 083 { 084 hitFilters.add(sf); 085 } 086 087 public void bySubHitProperty(BlastLikeSearchFilter.BySubHitProperty sf) 088 { 089 subHitFilters.add(sf); 090 } 091 } 092 093 private BlastLikeSearchFilter filter; 094 private SearchContentHandler delegate; 095 096 public FilteringContentHandler(BlastLikeSearchFilter filter, SearchContentHandler delegate) 097 throws BioException 098 { 099 construct(filter); 100 setSearchContentHandler(delegate); 101 } 102 103 public FilteringContentHandler(BlastLikeSearchFilter filter) 104 throws BioException 105 { 106 construct(filter); 107 } 108 109 private void construct(BlastLikeSearchFilter filter) 110 throws BioException 111 { 112 this.filter = filter; 113 114 FilterVisitor visitor = new FilterVisitor(); 115 Walker walker = walkerFactory.getWalker(visitor); 116 walker.walk(filter, visitor); 117 } 118 119 public void setSearchContentHandler(SearchContentHandler delegate) 120 { 121 this.delegate = delegate; 122 } 123 124 public void startHeader() 125 { 126 delegate.startHeader(); 127 } 128 129 public void setDatabaseID(String id) 130 { 131 delegate.setDatabaseID(id); 132 } 133 134 public void endHeader() 135 { 136 delegate.endHeader(); 137 } 138 139 public void startSearch() 140 { 141 emittedStartSearch = false; 142 firstHit = true; 143 } 144 145 public void setQueryID(String queryID) 146 { 147 addSearchProperty(BlastLikeSearchFilter.KEY_QUERY_ID, queryID); 148 } 149 150 public void addSearchProperty(Object key, Object value) 151 { 152 searchProperty.put(key, value); 153 } 154 155 public void startHit() 156 { 157 emittedStartHit = false; 158 firstSubHit = true; 159 160 // determine if anything at the search level 161 // will cause the rest of the hits/subhits 162 // in this search to be skipped. 163 // do only for first hit in a search 164 if (firstHit) { 165 for (Iterator searchFilterI = searchFilters.iterator(); searchFilterI.hasNext();) { 166 BlastLikeSearchFilter sf = (BlastLikeSearchFilter) searchFilterI.next(); 167 sf.evaluate(this); 168 } 169 170 TriState filterStatus = filter.accept(); 171 if (filterStatus == TriState.FALSE) { 172 skipHits = true; 173 skipSubHits = true; 174 } 175 176 firstHit = false; 177 } 178 } 179 180 public void addHitProperty(Object key, Object value) 181 { 182 if (skipHits) return; 183 hitProperty.put(key, value); 184 } 185 186 public void startSubHit() 187 { 188 if (skipSubHits) return; 189 190 191 // clear outcomes that depend on the properties 192 // of this subhit 193 for (Iterator subHitFilterI = hitFilters.iterator(); subHitFilterI.hasNext();) { 194 BlastLikeSearchFilter sf = (BlastLikeSearchFilter) subHitFilterI.next(); 195 sf.reset(); 196 } 197 198 // test filter at hit level here 199 // determine if anything at the search level 200 // will cause the rest of the hits/subhits 201 // in this search to be skipped. 202 // do only at first subhit in a search. 203 if (firstSubHit) { 204 for (Iterator hitFilterI = hitFilters.iterator(); hitFilterI.hasNext();) { 205 BlastLikeSearchFilter sf = (BlastLikeSearchFilter) hitFilterI.next(); 206 sf.evaluate(this); 207 } 208 209 TriState filterStatus = filter.accept(); 210 211 if (filterStatus == TriState.FALSE) { 212 skipSubHits = true; 213 } 214 215 firstSubHit = false; 216 } 217 } 218 219 public void addSubHitProperty(Object key, Object value) 220 { 221 if (skipSubHits) return; 222 subHitProperty.put(key, value); 223 } 224 225 public void endSubHit() 226 { 227 228 // test filter at subhit level here 229 // this will be a decision on whether to emit events or not 230 if (!skipSubHits) { 231 for (Iterator subHitFilterI = subHitFilters.iterator(); subHitFilterI.hasNext();) { 232 BlastLikeSearchFilter sf = (BlastLikeSearchFilter) subHitFilterI.next(); 233 sf.evaluate(this); 234 } 235 236 // handle emitting events to delegate 237 if (filter.accept() == TriState.TRUE) { 238 if (!emittedStartSearch) { 239 delegate.startSearch(); 240 String queryId = (String) searchProperty.get(BlastLikeSearchFilter.KEY_QUERY_ID); 241 if (queryId != null) delegate.setQueryID(queryId); 242 243 // dump search properties to delegate omitting special keys 244 for (Iterator searchPropertyI = searchProperty.entrySet().iterator(); 245 searchPropertyI.hasNext(); ) { 246 Map.Entry entry = (Map.Entry) searchPropertyI.next(); 247 if (BlastLikeSearchFilter.KEY_QUERY_ID.equals(entry.getKey())) continue; 248 249 delegate.addSearchProperty(entry.getKey(), entry.getValue()); 250 } 251 252 emittedStartSearch = true; 253 } 254 255 if (!emittedStartHit) { 256 delegate.startHit(); 257 // dump hit properties to delegate. 258 for (Iterator hitPropertyI = hitProperty.entrySet().iterator(); 259 hitPropertyI.hasNext(); ) { 260 Map.Entry entry = (Map.Entry) hitPropertyI.next(); 261 262 delegate.addSearchProperty(entry.getKey(), entry.getValue()); 263 } 264 265 emittedStartHit = true; 266 } 267 268 // dump subhit properties 269 delegate.startSubHit(); 270 271 for (Iterator subHitPropertyI = subHitProperty.entrySet().iterator(); 272 subHitPropertyI.hasNext(); ) { 273 Map.Entry entry = (Map.Entry) subHitPropertyI.next(); 274 275 delegate.addSearchProperty(entry.getKey(), entry.getValue()); 276 } 277 278 delegate.endSubHit(); 279 } 280 } 281 } 282 283 public void endHit() 284 { 285 if (emittedStartHit) { 286 delegate.endHit(); 287 emittedStartHit = false; 288 } 289 } 290 291 public void endSearch() 292 { 293 if (emittedStartSearch) { 294 delegate.endSearch(); 295 emittedStartSearch = false; 296 } 297 } 298 299} 300