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.io.Serializable;
025
026import org.biojava.utils.TriState;
027import org.biojava.utils.walker.WalkerFactory;
028/**
029 * A SearchContentHandler class that implements filtering
030 * in chains of SearchContentHandler instances.
031 * <p>
032 * The SearchContentHandler organise Blast-like searches
033 * as a hierarchy of search/hit/subhit.  Each search
034 * is conducted with a single query sequence.  Hits
035 * of the query sequence are reported against different
036 * target sequences.  The hit is further subdivided into
037 * one of more subhits which represent the positions
038 * within the target sequence that alignments of the query
039 * sequence were achieved against the query sequence
040 * (e.g. HSPs).
041 * <p>
042 * This implementation depends on the a well ordered use
043 * of the SearchContentHandler interface.  In particular,
044 * it requires that search/hit/subhit properties are
045 * reported immediately following the associated 
046 * startSearch/startHit/startSubHit call.  For example,
047 * search properties should not be reported following
048 * the corresponding startHit() call.
049 * <p>
050 * <u>Semantics of this interface</u><br>
051 * BlastLikeSearchFilters test different levels of the
052 * SearchContentHandler property hierarchy and each
053 * filter should be seen as being applied when a full
054 * set of events from that level is received.  So the
055 * ByHitProperty filter is applied when endHit() is
056 * called and determines whether all events received
057 * between startHit() and endHit() are to be passed on
058 * or discarded.
059 * <p>
060 * <u>Some keys used by SearchContentHandlers</u><br>
061 * <u>SearchProperties</u><br>
062 * <table border="1">
063 * <tr>
064 * <td>KEY_QUERY_ID</td>
065 * <td>String. Value from setQueryID</td>
066 * </tr>
067 * <tr>
068 * <td>queryDescription</td>
069 * <td>String. FASTA description line</td>
070 * </tr>
071 * <tr>
072 * <td>program</td>
073 * <td>String. variant of BLAST used</td>
074 * </tr>
075 * <tr>
076 * <td>version</td>
077 * <td>software version</td>
078 * </tr>
079 * </table>
080 * <p>
081 * <u>HitProperties</u><br>
082 * <table border="1" > 
083* <tr>
084 * <td>subjectId</td>
085 * <td>String.  Identity of subject (target) sequence.</td>
086 * </tr>
087 * <tr>
088 * <td>subjectSequenceLength</td>
089 * <td>String representation of integer value</td>
090 * </tr>
091 * <tr>
092 * <td>subjectDescription</td>
093 * <td>String.</td>
094 * </tr>
095 * <tr>
096 * <td></td>
097 * <td></td>
098 * </tr>
099 * </table>
100 * <p>
101 * <u>SubHitProperties</u><br>
102 * <table border="1" >
103 * <tr>
104 * <td>bitScore</td>
105 * <td>String representation of real value</td>
106 * </tr>
107 * <tr>
108 * <td>queryStrand</td>
109 * <td>plus/minus</td>
110 * </tr>
111 * <tr>
112 * <td>percentageIdentity</td>
113 * <td>String representation of real value</td>
114 * </tr>
115 * <tr>
116 * <td>querySequenceEnd</td>
117 * <td>String representation of integer value</td>
118 * </tr>
119 * <tr>
120 * <td>expectValue</td>
121 * <td>String representation of real value</td>
122 * </tr>
123 * <tr>
124 * <td>subjectStrand</td>
125 * <td>plus/minus</td>
126 * </tr>
127 * <tr>
128 * <td>subjectSequenceEnd</td>
129 * <td>String representation of integer value</td>
130 * </tr>
131 * <tr>
132 * <td>numberOfPositives</td>
133 * <td>String representation of integer value</td>
134 * </tr>
135 * <tr>
136 * <td>score</td>
137 * <td>String representation of integer value</td>
138 * </tr>
139 * <tr>
140 * <td>subjectSequence</td>
141 * <td>String representation of sequence</td>
142 * </tr>
143 * <tr>
144 * <td>alignmentSize</td>
145 * <td>String representation of integer value</td>
146 * </tr>
147 * <tr>
148 * <td>querySequenceStart</td>
149 * <td>String representation of integer value</td>
150 * </tr>
151 * <tr>
152 * <td>subjectSequenceStart</td>
153 * <td>String representation of integer value</td>
154 * </tr>
155 * <tr>
156 * <td>numberOfIdentities</td>
157 * <td>String representation of integer value</td>
158 * </tr>
159 * <tr>
160 * <td>querySequence</td>
161 * <td>String representation of sequence</td>
162 * </tr>
163 * </table>
164 *
165 *
166 * @author David Huen
167 */
168public interface BlastLikeSearchFilter
169    extends Serializable
170{
171    public interface Node
172    {
173        public Object getSearchProperty(Object key);
174        public Object getHitProperty(Object key);
175        public Object getSubHitProperty(Object key);
176    }
177
178    public static final String KEY_QUERY_ID = "___QUERY_ID___";
179
180    /**
181     * returns a TriState indicating the current outcome
182     * of evaluating this filter.  This is usually the
183     * outcome saved when evaluate(FilteringContentHandler fch) was called.
184     */
185    public TriState accept();
186
187    /**
188     * computes the outcome of this filter on the 
189     * specified node and stores it.  <b>This method
190     * is only exposed to permit it to be included
191     * in an interface.  Users should not use it.</b>
192     */
193    public void evaluate(Node fch);
194
195    /**
196     * resets the internal state of this filter including
197     * any cached evaluations. <b>This method
198     * is only exposed to permit it to be included
199     * in an interface.  Users should not use it.</b>
200     */
201    public void reset();
202
203    public abstract static class AbstractBlastLikeSearchFilter
204        implements BlastLikeSearchFilter
205    {
206        protected TriState cachedOutcome = TriState.INDETERMINATE;
207        public TriState accept() { return cachedOutcome; }
208        abstract public void evaluate(Node fch);
209        public void reset() { cachedOutcome = TriState.INDETERMINATE; }
210
211        private AbstractBlastLikeSearchFilter() {}
212    }
213
214    public static final class And
215    {
216    static { WalkerFactory.getInstance().addTypeWithParent(And.class); }
217
218        private AbstractBlastLikeSearchFilter filter0;
219        private AbstractBlastLikeSearchFilter filter1;
220
221        public And(
222            AbstractBlastLikeSearchFilter filter0,
223            AbstractBlastLikeSearchFilter filter1)
224        {
225            this.filter0 = filter0;
226            this.filter1 = filter1;
227        }
228
229        public TriState accept()
230        {
231            TriState outcome0 = filter0.accept();
232            TriState outcome1 = filter1.accept();
233
234            if ((outcome0 == TriState.FALSE) || (outcome1 == TriState.FALSE))
235                return TriState.FALSE;
236
237            // neither can be false now
238            if ((outcome0 == TriState.INDETERMINATE) || (outcome1 == TriState.INDETERMINATE))
239                return TriState.INDETERMINATE;
240
241            // neither is false nor indeterminate so it must be true!
242            return TriState.TRUE;
243        }
244    }
245
246    public static final class Or
247    {
248    static { WalkerFactory.getInstance().addTypeWithParent(Or.class); }
249
250        private AbstractBlastLikeSearchFilter filter0;
251        private AbstractBlastLikeSearchFilter filter1;
252
253        public Or(
254            AbstractBlastLikeSearchFilter filter0,
255            AbstractBlastLikeSearchFilter filter1)
256        {
257            this.filter0 = filter0;
258            this.filter1 = filter1;
259        }
260
261        public TriState accept()
262        {
263            TriState outcome0 = filter0.accept();
264            TriState outcome1 = filter1.accept();
265
266            if ((outcome0 == TriState.TRUE) || (outcome1 == TriState.TRUE))
267                return TriState.TRUE;
268
269            // neither can be false now
270            if ((outcome0 == TriState.INDETERMINATE) || (outcome1 == TriState.INDETERMINATE))
271                return TriState.INDETERMINATE;
272
273            // neither is true nor indeterminate so it must be false!
274            return TriState.FALSE;
275        }
276    }
277
278    public static final class Not
279        extends AbstractBlastLikeSearchFilter
280    {
281    static { WalkerFactory.getInstance().addTypeWithParent(Not.class); }
282
283        private AbstractBlastLikeSearchFilter filter;
284
285        public Not(AbstractBlastLikeSearchFilter filter)
286        {
287            this.filter = filter;
288        }
289
290        public TriState accept()
291        {
292            TriState outcome = filter.accept();
293
294            if (outcome == TriState.INDETERMINATE)
295                return TriState.INDETERMINATE;
296
297            if (outcome == TriState.TRUE)
298                return TriState.FALSE;
299            else
300                return TriState.TRUE;
301        }
302
303        public void evaluate(Node fch) {}
304    }
305
306    /**
307     * Applies test to the value specified by the key in search properties.
308     */
309    public static final class BySearchProperty
310        extends AbstractBlastLikeSearchFilter
311    {
312        private Object key;
313        private FilterTest test;
314        public BySearchProperty(String key, FilterTest test)
315        {
316            this.key = key;
317            this.test = test;
318        }
319
320        public void evaluate(Node fch)
321        {
322            Object propertyValue = fch.getSearchProperty(key);
323
324            cachedOutcome = ((propertyValue != null) && test.accept(propertyValue)) ? TriState.TRUE : TriState.FALSE;
325        }
326    }
327
328    /**
329     * Applies test to the value specified by the key in hit properties.
330     */
331    public static final class ByHitProperty
332        extends AbstractBlastLikeSearchFilter
333    {
334        private Object key;
335        private FilterTest test;
336        public ByHitProperty(String key, FilterTest test)
337        {
338            this.key = key;
339            this.test = test;
340        }
341
342        public void evaluate(Node fch)
343        {
344            Object propertyValue = fch.getHitProperty(key);
345
346            cachedOutcome = ((propertyValue != null) && test.accept(propertyValue)) ? TriState.TRUE : TriState.FALSE;
347        }
348    }
349
350    /**
351     * Applies test to the value specified by the key in subhit properties.
352     */
353    public static final class BySubHitProperty
354        extends AbstractBlastLikeSearchFilter
355    {
356        private Object key;
357        private FilterTest test;
358        public BySubHitProperty(String key, FilterTest test)
359        {
360            this.key = key;
361            this.test = test;
362        }
363
364        public void evaluate(Node fch)
365        {
366            Object propertyValue = fch.getSubHitProperty(key);
367            //cachedOutcome = ((propertyValue == null)
368            //    ? TriState.INDETERMINATE 
369            //    : (test.accept(propertyValue)) ? TriState.TRUE : TriState.FALSE);
370            cachedOutcome = ((propertyValue != null) && test.accept(propertyValue)) ? TriState.TRUE : TriState.FALSE;
371        }
372    }
373}
374